diff options
Diffstat (limited to 'drivers/net/wireless')
598 files changed, 92435 insertions, 14731 deletions
diff --git a/drivers/net/wireless/ath/ar5523/ar5523.c b/drivers/net/wireless/ath/ar5523/ar5523.c index 156f3650c006..96dc2778022a 100644 --- a/drivers/net/wireless/ath/ar5523/ar5523.c +++ b/drivers/net/wireless/ath/ar5523/ar5523.c @@ -733,7 +733,7 @@ static void ar5523_data_tx_pkt_put(struct ar5523 *ar) { atomic_dec(&ar->tx_nr_total); if (!atomic_dec_return(&ar->tx_nr_pending)) { - del_timer(&ar->tx_wd_timer); + timer_delete(&ar->tx_wd_timer); wake_up(&ar->tx_flush_waitq); } @@ -1076,7 +1076,7 @@ static void ar5523_stop(struct ieee80211_hw *hw, bool suspend) ar5523_cmd_write(ar, WDCMSG_TARGET_STOP, NULL, 0, 0); - del_timer_sync(&ar->tx_wd_timer); + timer_delete_sync(&ar->tx_wd_timer); cancel_work_sync(&ar->tx_wd_work); cancel_work_sync(&ar->rx_refill_work); ar5523_cancel_rx_bufs(ar); diff --git a/drivers/net/wireless/ath/ath10k/ahb.c b/drivers/net/wireless/ath/ath10k/ahb.c index db9f9ebcb62d..eb8b35b6224d 100644 --- a/drivers/net/wireless/ath/ath10k/ahb.c +++ b/drivers/net/wireless/ath/ath10k/ahb.c @@ -497,7 +497,7 @@ static int ath10k_ahb_resource_init(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_BOOT, "irq: %d\n", ar_ahb->irq); - ath10k_dbg(ar, ATH10K_DBG_BOOT, "mem: 0x%pK mem_len: %lu gcc mem: 0x%pK tcsr_mem: 0x%pK\n", + ath10k_dbg(ar, ATH10K_DBG_BOOT, "mem: 0x%p mem_len: %lu gcc mem: 0x%p tcsr_mem: 0x%p\n", ar_ahb->mem, ar_ahb->mem_len, ar_ahb->gcc_mem, ar_ahb->tcsr_mem); return 0; diff --git a/drivers/net/wireless/ath/ath10k/bmi.c b/drivers/net/wireless/ath/ath10k/bmi.c index 9a4f8e815412..48efdc71d54d 100644 --- a/drivers/net/wireless/ath/ath10k/bmi.c +++ b/drivers/net/wireless/ath/ath10k/bmi.c @@ -349,7 +349,7 @@ static int ath10k_bmi_lz_data_large(struct ath10k *ar, const void *buffer, u32 l int ret; size_t buf_len; - ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%pK length %d\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "large bmi lz data buffer 0x%p length %d\n", buffer, length); if (ar->bmi.done_sent) { @@ -395,7 +395,7 @@ int ath10k_bmi_lz_data(struct ath10k *ar, const void *buffer, u32 length) u32 txlen; int ret; - ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%pK length %d\n", + ath10k_dbg(ar, ATH10K_DBG_BMI, "bmi lz data buffer 0x%p length %d\n", buffer, length); if (ar->bmi.done_sent) { @@ -461,7 +461,7 @@ int ath10k_bmi_fast_download(struct ath10k *ar, int ret; ath10k_dbg(ar, ATH10K_DBG_BMI, - "bmi fast download address 0x%x buffer 0x%pK length %d\n", + "bmi fast download address 0x%x buffer 0x%p length %d\n", address, buffer, length); ret = ath10k_bmi_lz_stream_start(ar, address); diff --git a/drivers/net/wireless/ath/ath10k/ce.c b/drivers/net/wireless/ath/ath10k/ce.c index afae4a8027f8..a89a7491a76c 100644 --- a/drivers/net/wireless/ath/ath10k/ce.c +++ b/drivers/net/wireless/ath/ath10k/ce.c @@ -80,7 +80,7 @@ static inline u32 shadow_sr_wr_ind_addr(struct ath10k *ar, static inline unsigned int ath10k_set_ring_byte(unsigned int offset, - struct ath10k_hw_ce_regs_addr_map *addr_map) + const struct ath10k_hw_ce_regs_addr_map *addr_map) { return ((offset << addr_map->lsb) & addr_map->mask); } @@ -203,7 +203,7 @@ static inline void ath10k_ce_src_ring_dmax_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; + const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ctrl_regs->addr); @@ -217,7 +217,7 @@ static inline void ath10k_ce_src_ring_byte_swap_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; + const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ctrl_regs->addr); @@ -231,7 +231,7 @@ static inline void ath10k_ce_dest_ring_byte_swap_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; + const struct ath10k_hw_ce_ctrl1 *ctrl_regs = ar->hw_ce_regs->ctrl1_regs; u32 ctrl1_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ctrl_regs->addr); @@ -313,7 +313,7 @@ static inline void ath10k_ce_src_ring_highmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; + const struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + srcr_wm->addr); ath10k_ce_write32(ar, ce_ctrl_addr + srcr_wm->addr, @@ -325,7 +325,7 @@ static inline void ath10k_ce_src_ring_lowmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; + const struct ath10k_hw_ce_dst_src_wm_regs *srcr_wm = ar->hw_ce_regs->wm_srcr; u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + srcr_wm->addr); ath10k_ce_write32(ar, ce_ctrl_addr + srcr_wm->addr, @@ -337,7 +337,7 @@ static inline void ath10k_ce_dest_ring_highmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; + const struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + dstr_wm->addr); ath10k_ce_write32(ar, ce_ctrl_addr + dstr_wm->addr, @@ -349,7 +349,7 @@ static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int n) { - struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; + const struct ath10k_hw_ce_dst_src_wm_regs *dstr_wm = ar->hw_ce_regs->wm_dstr; u32 addr = ath10k_ce_read32(ar, ce_ctrl_addr + dstr_wm->addr); ath10k_ce_write32(ar, ce_ctrl_addr + dstr_wm->addr, @@ -360,7 +360,7 @@ static inline void ath10k_ce_dest_ring_lowmark_set(struct ath10k *ar, static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar, u32 ce_ctrl_addr) { - struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; + const struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); @@ -372,7 +372,7 @@ static inline void ath10k_ce_copy_complete_inter_enable(struct ath10k *ar, static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; + const struct ath10k_hw_ce_host_ie *host_ie = ar->hw_ce_regs->host_ie; u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); @@ -384,7 +384,7 @@ static inline void ath10k_ce_copy_complete_intr_disable(struct ath10k *ar, static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; u32 host_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ar->hw_ce_regs->host_ie_addr); @@ -396,7 +396,7 @@ static inline void ath10k_ce_watermark_intr_disable(struct ath10k *ar, static inline void ath10k_ce_error_intr_disable(struct ath10k *ar, u32 ce_ctrl_addr) { - struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; + const struct ath10k_hw_ce_misc_regs *misc_regs = ar->hw_ce_regs->misc_regs; u32 misc_ie_addr = ath10k_ce_read32(ar, ce_ctrl_addr + ar->hw_ce_regs->misc_ie_addr); @@ -410,7 +410,7 @@ static inline void ath10k_ce_engine_int_status_clear(struct ath10k *ar, u32 ce_ctrl_addr, unsigned int mask) { - struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; ath10k_ce_write32(ar, ce_ctrl_addr + wm_regs->addr, mask); } @@ -1230,7 +1230,7 @@ void ath10k_ce_per_engine_service(struct ath10k *ar, unsigned int ce_id) { struct ath10k_ce *ce = ath10k_ce_priv(ar); struct ath10k_ce_pipe *ce_state = &ce->ce_states[ce_id]; - struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; + const struct ath10k_hw_ce_host_wm_regs *wm_regs = ar->hw_ce_regs->wm_regs; u32 ctrl_addr = ce_state->ctrl_addr; /* @@ -1388,7 +1388,7 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar, ath10k_ce_src_ring_highmark_set(ar, ctrl_addr, nentries); ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot init ce src ring id %d entries %d base_addr %pK\n", + "boot init ce src ring id %d entries %d base_addr %p\n", ce_id, nentries, src_ring->base_addr_owner_space); return 0; @@ -1426,7 +1426,7 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar, ath10k_ce_dest_ring_highmark_set(ar, ctrl_addr, nentries); ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot ce dest ring id %d entries %d base_addr %pK\n", + "boot ce dest ring id %d entries %d base_addr %p\n", ce_id, nentries, dest_ring->base_addr_owner_space); return 0; diff --git a/drivers/net/wireless/ath/ath10k/core.c b/drivers/net/wireless/ath/ath10k/core.c index b3294287bce1..fe3a8f4a1cc1 100644 --- a/drivers/net/wireless/ath/ath10k/core.c +++ b/drivers/net/wireless/ath/ath10k/core.c @@ -1163,9 +1163,12 @@ int ath10k_core_check_dt(struct ath10k *ar) if (!node) return -ENOENT; - of_property_read_string(node, "qcom,ath10k-calibration-variant", + of_property_read_string(node, "qcom,calibration-variant", &variant); if (!variant) + of_property_read_string(node, "qcom,ath10k-calibration-variant", + &variant); + if (!variant) return -ENODATA; if (strscpy(ar->id.bdf_ext, variant, sizeof(ar->id.bdf_ext)) < 0) @@ -1197,7 +1200,7 @@ static int ath10k_download_fw(struct ath10k *ar) } ath10k_dbg(ar, ATH10K_DBG_BOOT, - "boot uploading firmware image %pK len %d\n", + "boot uploading firmware image %p len %d\n", data, data_len); /* Check if device supports to download firmware via @@ -1823,7 +1826,7 @@ static int ath10k_download_and_run_otp(struct ath10k *ar) if (!ar->running_fw->fw_file.otp_data || !ar->running_fw->fw_file.otp_len) { - ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %pK otp_len %zd)!\n", + ath10k_warn(ar, "Not running otp, calibration will be incorrect (otp-data %p otp_len %zd)!\n", ar->running_fw->fw_file.otp_data, ar->running_fw->fw_file.otp_len); return 0; @@ -2259,7 +2262,9 @@ static int ath10k_core_pre_cal_download(struct ath10k *ar) "boot did not find a pre calibration file, try DT next: %d\n", ret); - ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data"); + ret = ath10k_download_cal_dt(ar, "qcom,pre-calibration-data"); + if (ret == -ENOENT) + ret = ath10k_download_cal_dt(ar, "qcom,ath10k-pre-calibration-data"); if (ret) { ath10k_dbg(ar, ATH10K_DBG_BOOT, "unable to load pre cal data from DT: %d\n", ret); @@ -2337,7 +2342,9 @@ static int ath10k_download_cal_data(struct ath10k *ar) "boot did not find a calibration file, try DT next: %d\n", ret); - ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data"); + ret = ath10k_download_cal_dt(ar, "qcom,calibration-data"); + if (ret == -ENOENT) + ret = ath10k_download_cal_dt(ar, "qcom,ath10k-calibration-data"); if (ret == 0) { ar->cal_mode = ATH10K_CAL_MODE_DT; goto done; diff --git a/drivers/net/wireless/ath/ath10k/debug.c b/drivers/net/wireless/ath/ath10k/debug.c index 35bfe7232e95..a0c1afeda4dd 100644 --- a/drivers/net/wireless/ath/ath10k/debug.c +++ b/drivers/net/wireless/ath/ath10k/debug.c @@ -1751,7 +1751,7 @@ void ath10k_debug_stop(struct ath10k *ar) /* Must not use _sync to avoid deadlock, we do that in * ath10k_debug_destroy(). The check for htt_stats_mask is to avoid - * warning from del_timer(). + * warning from timer_delete(). */ if (ar->debug.htt_stats_mask != 0) cancel_delayed_work(&ar->debug.htt_stats_dwork); diff --git a/drivers/net/wireless/ath/ath10k/htc.c b/drivers/net/wireless/ath/ath10k/htc.c index a6e21ce90bad..2da08dfebd3e 100644 --- a/drivers/net/wireless/ath/ath10k/htc.c +++ b/drivers/net/wireless/ath/ath10k/htc.c @@ -34,7 +34,7 @@ static struct sk_buff *ath10k_htc_build_tx_ctrl_skb(void *ar) skb_cb = ATH10K_SKB_CB(skb); memset(skb_cb, 0, sizeof(*skb_cb)); - ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %pK\n", __func__, skb); + ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: skb %p\n", __func__, skb); return skb; } @@ -54,7 +54,7 @@ void ath10k_htc_notify_tx_completion(struct ath10k_htc_ep *ep, struct ath10k *ar = ep->htc->ar; struct ath10k_htc_hdr *hdr; - ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %pK\n", __func__, + ath10k_dbg(ar, ATH10K_DBG_HTC, "%s: ep %d skb %p\n", __func__, ep->eid, skb); /* A corner case where the copy completion is reaching to host but still @@ -515,7 +515,7 @@ void ath10k_htc_rx_completion_handler(struct ath10k *ar, struct sk_buff *skb) /* zero length packet with trailer data, just drop these */ goto out; - ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %pK\n", + ath10k_dbg(ar, ATH10K_DBG_HTC, "htc rx completion ep %d skb %p\n", eid, skb); ep->ep_ops.ep_rx_complete(ar, skb); diff --git a/drivers/net/wireless/ath/ath10k/htt_rx.c b/drivers/net/wireless/ath/ath10k/htt_rx.c index 7d28ae5453cf..52981052e211 100644 --- a/drivers/net/wireless/ath/ath10k/htt_rx.c +++ b/drivers/net/wireless/ath/ath10k/htt_rx.c @@ -287,7 +287,7 @@ void ath10k_htt_rx_free(struct ath10k_htt *htt) if (htt->ar->bus_param.dev_type == ATH10K_DEV_TYPE_HL) return; - del_timer_sync(&htt->rx_ring.refill_retry_timer); + timer_delete_sync(&htt->rx_ring.refill_retry_timer); skb_queue_purge(&htt->rx_msdus_q); skb_queue_purge(&htt->rx_in_ord_compl_q); @@ -1373,7 +1373,7 @@ static void ath10k_process_rx(struct ath10k *ar, struct sk_buff *skb) } ath10k_dbg(ar, ATH10K_DBG_DATA, - "rx skb %pK len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + "rx skb %p len %u peer %pM %s %s sn %u %s%s%s%s%s%s %srate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", skb, skb->len, ieee80211_get_SA(hdr), diff --git a/drivers/net/wireless/ath/ath10k/hw.c b/drivers/net/wireless/ath/ath10k/hw.c index 8fafe096adff..84b35a22fc23 100644 --- a/drivers/net/wireless/ath/ath10k/hw.c +++ b/drivers/net/wireless/ath/ath10k/hw.c @@ -212,40 +212,40 @@ const struct ath10k_hw_regs wcn3990_regs = { .pcie_intr_fw_mask = 0x00100000, }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_ring = { .msb = 0x00000010, .lsb = 0x00000010, .mask = GENMASK(17, 17), }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_ring = { .msb = 0x00000012, .lsb = 0x00000012, .mask = GENMASK(18, 18), }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_dmax = { .msb = 0x00000000, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = { +static const struct ath10k_hw_ce_ctrl1 wcn3990_ctrl1 = { .addr = 0x00000018, .src_ring = &wcn3990_src_ring, .dst_ring = &wcn3990_dst_ring, .dmax = &wcn3990_dmax, }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_host_ie_cc = { .mask = GENMASK(0, 0), }; -static struct ath10k_hw_ce_host_ie wcn3990_host_ie = { +static const struct ath10k_hw_ce_host_ie wcn3990_host_ie = { .copy_complete = &wcn3990_host_ie_cc, }; -static struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = { +static const struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = { .dstr_lmask = 0x00000010, .dstr_hmask = 0x00000008, .srcr_lmask = 0x00000004, @@ -255,7 +255,7 @@ static struct ath10k_hw_ce_host_wm_regs wcn3990_wm_reg = { .addr = 0x00000030, }; -static struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = { +static const struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = { .axi_err = 0x00000100, .dstr_add_err = 0x00000200, .srcr_len_err = 0x00000100, @@ -266,19 +266,19 @@ static struct ath10k_hw_ce_misc_regs wcn3990_misc_reg = { .addr = 0x00000038, }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_low = { .msb = 0x00000000, .lsb = 0x00000010, .mask = GENMASK(31, 16), }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_src_wm_high = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = { +static const struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = { .addr = 0x0000004c, .low_rst = 0x00000000, .high_rst = 0x00000000, @@ -286,18 +286,18 @@ static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_src_ring = { .wm_high = &wcn3990_src_wm_high, }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_low = { .lsb = 0x00000010, .mask = GENMASK(31, 16), }; -static struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = { +static const struct ath10k_hw_ce_regs_addr_map wcn3990_dst_wm_high = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { +static const struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { .addr = 0x00000050, .low_rst = 0x00000000, .high_rst = 0x00000000, @@ -305,7 +305,7 @@ static struct ath10k_hw_ce_dst_src_wm_regs wcn3990_wm_dst_ring = { .wm_high = &wcn3990_dst_wm_high, }; -static struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = { +static const struct ath10k_hw_ce_ctrl1_upd wcn3990_ctrl1_upd = { .shift = 19, .mask = 0x00080000, .enable = 0x00000000, @@ -344,25 +344,25 @@ const struct ath10k_hw_values wcn3990_values = { .ce_desc_meta_data_lsb = 4, }; -static struct ath10k_hw_ce_regs_addr_map qcax_src_ring = { +static const struct ath10k_hw_ce_regs_addr_map qcax_src_ring = { .msb = 0x00000010, .lsb = 0x00000010, .mask = GENMASK(16, 16), }; -static struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = { +static const struct ath10k_hw_ce_regs_addr_map qcax_dst_ring = { .msb = 0x00000011, .lsb = 0x00000011, .mask = GENMASK(17, 17), }; -static struct ath10k_hw_ce_regs_addr_map qcax_dmax = { +static const struct ath10k_hw_ce_regs_addr_map qcax_dmax = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = { +static const struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = { .addr = 0x00000010, .hw_mask = 0x0007ffff, .sw_mask = 0x0007ffff, @@ -375,31 +375,31 @@ static struct ath10k_hw_ce_ctrl1 qcax_ctrl1 = { .dmax = &qcax_dmax, }; -static struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = { +static const struct ath10k_hw_ce_regs_addr_map qcax_cmd_halt_status = { .msb = 0x00000003, .lsb = 0x00000003, .mask = GENMASK(3, 3), }; -static struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = { +static const struct ath10k_hw_ce_cmd_halt qcax_cmd_halt = { .msb = 0x00000000, .mask = GENMASK(0, 0), .status_reset = 0x00000000, .status = &qcax_cmd_halt_status, }; -static struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = { +static const struct ath10k_hw_ce_regs_addr_map qcax_host_ie_cc = { .msb = 0x00000000, .lsb = 0x00000000, .mask = GENMASK(0, 0), }; -static struct ath10k_hw_ce_host_ie qcax_host_ie = { +static const struct ath10k_hw_ce_host_ie qcax_host_ie = { .copy_complete_reset = 0x00000000, .copy_complete = &qcax_host_ie_cc, }; -static struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = { +static const struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = { .dstr_lmask = 0x00000010, .dstr_hmask = 0x00000008, .srcr_lmask = 0x00000004, @@ -409,7 +409,7 @@ static struct ath10k_hw_ce_host_wm_regs qcax_wm_reg = { .addr = 0x00000030, }; -static struct ath10k_hw_ce_misc_regs qcax_misc_reg = { +static const struct ath10k_hw_ce_misc_regs qcax_misc_reg = { .axi_err = 0x00000400, .dstr_add_err = 0x00000200, .srcr_len_err = 0x00000100, @@ -420,19 +420,19 @@ static struct ath10k_hw_ce_misc_regs qcax_misc_reg = { .addr = 0x00000038, }; -static struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = { +static const struct ath10k_hw_ce_regs_addr_map qcax_src_wm_low = { .msb = 0x0000001f, .lsb = 0x00000010, .mask = GENMASK(31, 16), }; -static struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = { +static const struct ath10k_hw_ce_regs_addr_map qcax_src_wm_high = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = { +static const struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = { .addr = 0x0000004c, .low_rst = 0x00000000, .high_rst = 0x00000000, @@ -440,18 +440,18 @@ static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_src_ring = { .wm_high = &qcax_src_wm_high, }; -static struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = { +static const struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_low = { .lsb = 0x00000010, .mask = GENMASK(31, 16), }; -static struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = { +static const struct ath10k_hw_ce_regs_addr_map qcax_dst_wm_high = { .msb = 0x0000000f, .lsb = 0x00000000, .mask = GENMASK(15, 0), }; -static struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = { +static const struct ath10k_hw_ce_dst_src_wm_regs qcax_wm_dst_ring = { .addr = 0x00000050, .low_rst = 0x00000000, .high_rst = 0x00000000, diff --git a/drivers/net/wireless/ath/ath10k/hw.h b/drivers/net/wireless/ath/ath10k/hw.h index 442091c6dfd2..7ffa1fbe2874 100644 --- a/drivers/net/wireless/ath/ath10k/hw.h +++ b/drivers/net/wireless/ath/ath10k/hw.h @@ -289,19 +289,22 @@ struct ath10k_hw_ce_ctrl1 { u32 sw_wr_mask; u32 reset_mask; u32 reset; - struct ath10k_hw_ce_regs_addr_map *src_ring; - struct ath10k_hw_ce_regs_addr_map *dst_ring; - struct ath10k_hw_ce_regs_addr_map *dmax; }; + const struct ath10k_hw_ce_regs_addr_map *src_ring; + const struct ath10k_hw_ce_regs_addr_map *dst_ring; + const struct ath10k_hw_ce_regs_addr_map *dmax; +}; struct ath10k_hw_ce_cmd_halt { u32 status_reset; u32 msb; u32 mask; - struct ath10k_hw_ce_regs_addr_map *status; }; + const struct ath10k_hw_ce_regs_addr_map *status; +}; struct ath10k_hw_ce_host_ie { u32 copy_complete_reset; - struct ath10k_hw_ce_regs_addr_map *copy_complete; }; + const struct ath10k_hw_ce_regs_addr_map *copy_complete; +}; struct ath10k_hw_ce_host_wm_regs { u32 dstr_lmask; @@ -328,8 +331,9 @@ struct ath10k_hw_ce_dst_src_wm_regs { u32 addr; u32 low_rst; u32 high_rst; - struct ath10k_hw_ce_regs_addr_map *wm_low; - struct ath10k_hw_ce_regs_addr_map *wm_high; }; + const struct ath10k_hw_ce_regs_addr_map *wm_low; + const struct ath10k_hw_ce_regs_addr_map *wm_high; +}; struct ath10k_hw_ce_ctrl1_upd { u32 shift; @@ -355,14 +359,14 @@ struct ath10k_hw_ce_regs { u32 ce_rri_low; u32 ce_rri_high; u32 host_ie_addr; - struct ath10k_hw_ce_host_wm_regs *wm_regs; - struct ath10k_hw_ce_misc_regs *misc_regs; - struct ath10k_hw_ce_ctrl1 *ctrl1_regs; - struct ath10k_hw_ce_cmd_halt *cmd_halt; - struct ath10k_hw_ce_host_ie *host_ie; - struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr; - struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr; - struct ath10k_hw_ce_ctrl1_upd *upd; + const struct ath10k_hw_ce_host_wm_regs *wm_regs; + const struct ath10k_hw_ce_misc_regs *misc_regs; + const struct ath10k_hw_ce_ctrl1 *ctrl1_regs; + const struct ath10k_hw_ce_cmd_halt *cmd_halt; + const struct ath10k_hw_ce_host_ie *host_ie; + const struct ath10k_hw_ce_dst_src_wm_regs *wm_srcr; + const struct ath10k_hw_ce_dst_src_wm_regs *wm_dstr; + const struct ath10k_hw_ce_ctrl1_upd *upd; }; struct ath10k_hw_values { diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index c61b95a928da..8c7ffea0fa44 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -875,7 +875,7 @@ static void ath10k_peer_map_cleanup(struct ath10k *ar, struct ath10k_peer *peer) */ for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) { if (ar->peer_map[i] == peer) { - ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %pK idx %d)\n", + ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %p idx %d)\n", peer->addr, peer, i); ar->peer_map[i] = NULL; } @@ -4063,7 +4063,7 @@ static int ath10k_mac_tx(struct ath10k *ar, if (!noque_offchan && info->flags & IEEE80211_TX_CTL_TX_OFFCHAN) { if (!ath10k_mac_tx_frm_has_freq(ar)) { - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %pK len %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac queued offchannel skb %p len %d\n", skb, skb->len); skb_queue_tail(&ar->offchan_tx_queue, skb); @@ -4126,7 +4126,7 @@ void ath10k_offchan_tx_work(struct work_struct *work) mutex_lock(&ar->conf_mutex); - ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %pK len %d\n", + ath10k_dbg(ar, ATH10K_DBG_MAC, "mac offchannel skb %p len %d\n", skb, skb->len); hdr = (struct ieee80211_hdr *)skb->data; @@ -4181,7 +4181,7 @@ void ath10k_offchan_tx_work(struct work_struct *work) time_left = wait_for_completion_timeout(&ar->offchan_tx_completed, 3 * HZ); if (time_left == 0) - ath10k_warn(ar, "timed out waiting for offchannel skb %pK, len: %d\n", + ath10k_warn(ar, "timed out waiting for offchannel skb %p, len: %d\n", skb, skb->len); if (!peer && tmp_peer_created) { @@ -7604,7 +7604,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, * Existing station deletion. */ ath10k_dbg(ar, ATH10K_DBG_STA, - "mac vdev %d peer delete %pM sta %pK (sta gone)\n", + "mac vdev %d peer delete %pM sta %p (sta gone)\n", arvif->vdev_id, sta->addr, sta); if (sta->tdls) { @@ -7631,7 +7631,7 @@ static int ath10k_sta_state(struct ieee80211_hw *hw, continue; if (peer->sta == sta) { - ath10k_warn(ar, "found sta peer %pM (ptr %pK id %d) entry on vdev %i after it was supposedly removed\n", + ath10k_warn(ar, "found sta peer %pM (ptr %p id %d) entry on vdev %i after it was supposedly removed\n", sta->addr, peer, i, arvif->vdev_id); peer->sta = NULL; @@ -8811,7 +8811,7 @@ ath10k_mac_op_add_chanctx(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx add freq %u width %d ptr %pK\n", + "mac chanctx add freq %u width %d ptr %p\n", ctx->def.chan->center_freq, ctx->def.width, ctx); mutex_lock(&ar->conf_mutex); @@ -8835,7 +8835,7 @@ ath10k_mac_op_remove_chanctx(struct ieee80211_hw *hw, struct ath10k *ar = hw->priv; ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx remove freq %u width %d ptr %pK\n", + "mac chanctx remove freq %u width %d ptr %p\n", ctx->def.chan->center_freq, ctx->def.width, ctx); mutex_lock(&ar->conf_mutex); @@ -8900,7 +8900,7 @@ ath10k_mac_op_change_chanctx(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx change freq %u width %d ptr %pK changed %x\n", + "mac chanctx change freq %u width %d ptr %p changed %x\n", ctx->def.chan->center_freq, ctx->def.width, ctx, changed); /* This shouldn't really happen because channel switching should use @@ -8959,7 +8959,7 @@ ath10k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx assign ptr %pK vdev_id %i\n", + "mac chanctx assign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); if (WARN_ON(arvif->is_started)) { @@ -9039,7 +9039,7 @@ ath10k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, mutex_lock(&ar->conf_mutex); ath10k_dbg(ar, ATH10K_DBG_MAC, - "mac chanctx unassign ptr %pK vdev_id %i\n", + "mac chanctx unassign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); WARN_ON(!arvif->is_started); diff --git a/drivers/net/wireless/ath/ath10k/pci.c b/drivers/net/wireless/ath/ath10k/pci.c index c52a16f8078f..20ec0a6d0f71 100644 --- a/drivers/net/wireless/ath/ath10k/pci.c +++ b/drivers/net/wireless/ath/ath10k/pci.c @@ -619,7 +619,7 @@ static void ath10k_pci_sleep_sync(struct ath10k *ar) return; } - del_timer_sync(&ar_pci->ps_timer); + timer_delete_sync(&ar_pci->ps_timer); spin_lock_irqsave(&ar_pci->ps_lock, flags); WARN_ON(ar_pci->ps_wake_refcount > 0); @@ -1817,7 +1817,7 @@ static void ath10k_pci_rx_retry_sync(struct ath10k *ar) { struct ath10k_pci *ar_pci = ath10k_pci_priv(ar); - del_timer_sync(&ar_pci->rx_post_retry); + timer_delete_sync(&ar_pci->rx_post_retry); } int ath10k_pci_hif_map_service_to_pipe(struct ath10k *ar, u16 service_id, @@ -3411,7 +3411,7 @@ static int ath10k_pci_claim(struct ath10k *ar) goto err_region; } - ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot pci_mem 0x%pK\n", ar_pci->mem); + ath10k_dbg(ar, ATH10K_DBG_BOOT, "boot pci_mem 0x%p\n", ar_pci->mem); return 0; err_region: diff --git a/drivers/net/wireless/ath/ath10k/sdio.c b/drivers/net/wireless/ath/ath10k/sdio.c index 6805357ee29e..f3212eab56a1 100644 --- a/drivers/net/wireless/ath/ath10k/sdio.c +++ b/drivers/net/wireless/ath/ath10k/sdio.c @@ -1621,7 +1621,7 @@ static void ath10k_sdio_hif_power_down(struct ath10k *ar) ath10k_dbg(ar, ATH10K_DBG_BOOT, "sdio power off\n"); - del_timer_sync(&ar_sdio->sleep_timer); + timer_delete_sync(&ar_sdio->sleep_timer); ath10k_sdio_set_mbox_sleep(ar, true); /* Disable the card */ @@ -1844,7 +1844,7 @@ static int ath10k_sdio_get_htt_tx_complete(struct ath10k *ar) ret = ath10k_sdio_diag_read32(ar, addr, &val); if (ret) { ath10k_warn(ar, - "unable to read hi_acs_flags for htt tx comple : %d\n", ret); + "unable to read hi_acs_flags for htt tx complete: %d\n", ret); return ret; } diff --git a/drivers/net/wireless/ath/ath10k/snoc.c b/drivers/net/wireless/ath/ath10k/snoc.c index d436a874cd5a..866bad2db334 100644 --- a/drivers/net/wireless/ath/ath10k/snoc.c +++ b/drivers/net/wireless/ath/ath10k/snoc.c @@ -911,7 +911,7 @@ static void ath10k_snoc_buffer_cleanup(struct ath10k *ar) struct ath10k_snoc_pipe *pipe_info; int pipe_num; - del_timer_sync(&ar_snoc->rx_post_retry); + timer_delete_sync(&ar_snoc->rx_post_retry); for (pipe_num = 0; pipe_num < CE_COUNT; pipe_num++) { pipe_info = &ar_snoc->pipe_info[pipe_num]; ath10k_snoc_rx_pipe_cleanup(pipe_info); diff --git a/drivers/net/wireless/ath/ath10k/testmode.c b/drivers/net/wireless/ath/ath10k/testmode.c index 7a9b9bbcdbfc..3fcefc55b74f 100644 --- a/drivers/net/wireless/ath/ath10k/testmode.c +++ b/drivers/net/wireless/ath/ath10k/testmode.c @@ -35,7 +35,7 @@ bool ath10k_tm_event_wmi(struct ath10k *ar, u32 cmd_id, struct sk_buff *skb) int ret; ath10k_dbg(ar, ATH10K_DBG_TESTMODE, - "testmode event wmi cmd_id %d skb %pK skb->len %d\n", + "testmode event wmi cmd_id %d skb %p skb->len %d\n", cmd_id, skb, skb->len); ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", skb->data, skb->len); @@ -397,7 +397,7 @@ static int ath10k_tm_cmd_wmi(struct ath10k *ar, struct nlattr *tb[]) cmd_id = nla_get_u32(tb[ATH10K_TM_ATTR_WMI_CMDID]); ath10k_dbg(ar, ATH10K_DBG_TESTMODE, - "testmode cmd wmi cmd_id %d buf %pK buf_len %d\n", + "testmode cmd wmi cmd_id %d buf %p buf_len %d\n", cmd_id, buf, buf_len); ath10k_dbg_dump(ar, ATH10K_DBG_TESTMODE, NULL, "", buf, buf_len); diff --git a/drivers/net/wireless/ath/ath10k/txrx.c b/drivers/net/wireless/ath/ath10k/txrx.c index da3bc35e41aa..493bfb410aff 100644 --- a/drivers/net/wireless/ath/ath10k/txrx.c +++ b/drivers/net/wireless/ath/ath10k/txrx.c @@ -35,7 +35,7 @@ static void ath10k_report_offchan_tx(struct ath10k *ar, struct sk_buff *skb) complete(&ar->offchan_tx_completed); ar->offchan_tx_skb = NULL; /* just for sanity */ - ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %pK\n", skb); + ath10k_dbg(ar, ATH10K_DBG_HTT, "completed offchannel skb %p\n", skb); out: spin_unlock_bh(&ar->data_lock); } diff --git a/drivers/net/wireless/ath/ath10k/usb.c b/drivers/net/wireless/ath/ath10k/usb.c index 3b51b7f52130..1732a4f98418 100644 --- a/drivers/net/wireless/ath/ath10k/usb.c +++ b/drivers/net/wireless/ath/ath10k/usb.c @@ -131,7 +131,7 @@ static void ath10k_usb_recv_complete(struct urb *urb) int status = 0; ath10k_dbg(ar, ATH10K_DBG_USB_BULK, - "usb recv pipe %d stat %d len %d urb 0x%pK\n", + "usb recv pipe %d stat %d len %d urb 0x%p\n", pipe->logical_pipe_num, urb->status, urb->actual_length, urb); @@ -230,7 +230,7 @@ static void ath10k_usb_post_recv_transfers(struct ath10k *ar, ath10k_usb_recv_complete, urb_context); ath10k_dbg(ar, ATH10K_DBG_USB_BULK, - "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%pK\n", + "usb bulk recv submit %d 0x%x ep 0x%2.2x len %d buf 0x%p\n", recv_pipe->logical_pipe_num, recv_pipe->usb_pipe_handle, recv_pipe->ep_address, ATH10K_USB_RX_BUFFER_SIZE, urb_context->skb); diff --git a/drivers/net/wireless/ath/ath10k/wmi.c b/drivers/net/wireless/ath/ath10k/wmi.c index 5e061f7525a6..df6a24f8f8d5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.c +++ b/drivers/net/wireless/ath/ath10k/wmi.c @@ -2029,7 +2029,7 @@ ath10k_wmi_op_gen_mgmt_tx(struct ath10k *ar, struct sk_buff *msdu) ether_addr_copy(cmd->hdr.peer_macaddr.addr, ieee80211_get_DA(hdr)); memcpy(cmd->buf, msdu->data, msdu->len); - ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %pK len %d ftype %02x stype %02x\n", + ath10k_dbg(ar, ATH10K_DBG_WMI, "wmi mgmt tx skb %p len %d ftype %02x stype %02x\n", msdu, skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); trace_ath10k_tx_hdr(ar, skb->data, skb->len); @@ -2637,7 +2637,7 @@ int ath10k_wmi_event_mgmt_rx(struct ath10k *ar, struct sk_buff *skb) status->boottime_ns = ktime_get_boottime_ns(); ath10k_dbg(ar, ATH10K_DBG_MGMT, - "event mgmt rx skb %pK len %d ftype %02x stype %02x\n", + "event mgmt rx skb %p len %d ftype %02x stype %02x\n", skb, skb->len, fc & IEEE80211_FCTL_FTYPE, fc & IEEE80211_FCTL_STYPE); diff --git a/drivers/net/wireless/ath/ath11k/Makefile b/drivers/net/wireless/ath/ath11k/Makefile index 43d2d8ddcdc0..d9092414b362 100644 --- a/drivers/net/wireless/ath/ath11k/Makefile +++ b/drivers/net/wireless/ath/ath11k/Makefile @@ -27,6 +27,7 @@ ath11k-$(CONFIG_ATH11K_TRACING) += trace.o ath11k-$(CONFIG_THERMAL) += thermal.o ath11k-$(CONFIG_ATH11K_SPECTRAL) += spectral.o ath11k-$(CONFIG_PM) += wow.o +ath11k-$(CONFIG_DEV_COREDUMP) += coredump.o obj-$(CONFIG_ATH11K_AHB) += ath11k_ahb.o ath11k_ahb-y += ahb.o diff --git a/drivers/net/wireless/ath/ath11k/ahb.c b/drivers/net/wireless/ath/ath11k/ahb.c index f2fc04596d48..fde1ce43c499 100644 --- a/drivers/net/wireless/ath/ath11k/ahb.c +++ b/drivers/net/wireless/ath/ath11k/ahb.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -397,7 +397,7 @@ static void ath11k_ahb_stop(struct ath11k_base *ab) ath11k_ahb_ce_irqs_disable(ab); ath11k_ahb_sync_ce_irqs(ab); ath11k_ahb_kill_tasklets(ab); - del_timer_sync(&ab->rx_replenish_retry); + timer_delete_sync(&ab->rx_replenish_retry); ath11k_ce_cleanup_pipes(ab); } @@ -413,7 +413,7 @@ static int ath11k_ahb_power_up(struct ath11k_base *ab) return ret; } -static void ath11k_ahb_power_down(struct ath11k_base *ab) +static void ath11k_ahb_power_down(struct ath11k_base *ab, bool is_suspend) { struct ath11k_ahb *ab_ahb = ath11k_ahb_priv(ab); @@ -1280,7 +1280,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev) struct ath11k_base *ab = platform_get_drvdata(pdev); if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { - ath11k_ahb_power_down(ab); + ath11k_ahb_power_down(ab, false); ath11k_debugfs_soc_destroy(ab); ath11k_qmi_deinit_service(ab); goto qmi_fail; @@ -1290,6 +1290,7 @@ static void ath11k_ahb_remove(struct platform_device *pdev) ath11k_core_deinit(ab); qmi_fail: + ath11k_fw_destroy(ab); ath11k_ahb_free_resources(ab); } @@ -1309,6 +1310,7 @@ static void ath11k_ahb_shutdown(struct platform_device *pdev) ath11k_core_deinit(ab); free_resources: + ath11k_fw_destroy(ab); ath11k_ahb_free_resources(ab); } diff --git a/drivers/net/wireless/ath/ath11k/ce.c b/drivers/net/wireless/ath/ath11k/ce.c index e66e86bdec20..9d8efec46508 100644 --- a/drivers/net/wireless/ath/ath11k/ce.c +++ b/drivers/net/wireless/ath/ath11k/ce.c @@ -393,11 +393,10 @@ static int ath11k_ce_completed_recv_next(struct ath11k_ce_pipe *pipe, goto err; } + /* Make sure descriptor is read after the head pointer. */ + dma_rmb(); + *nbytes = ath11k_hal_ce_dst_status_get_length(desc); - if (*nbytes == 0) { - ret = -EIO; - goto err; - } *skb = pipe->dest_ring->skb[sw_index]; pipe->dest_ring->skb[sw_index] = NULL; @@ -430,8 +429,8 @@ static void ath11k_ce_recv_process_cb(struct ath11k_ce_pipe *pipe) dma_unmap_single(ab->dev, ATH11K_SKB_RXCB(skb)->paddr, max_nbytes, DMA_FROM_DEVICE); - if (unlikely(max_nbytes < nbytes)) { - ath11k_warn(ab, "rxed more than expected (nbytes %d, max %d)", + if (unlikely(max_nbytes < nbytes || nbytes == 0)) { + ath11k_warn(ab, "unexpected rx length (nbytes %d, max %d)", nbytes, max_nbytes); dev_kfree_skb_any(skb); continue; diff --git a/drivers/net/wireless/ath/ath11k/core.c b/drivers/net/wireless/ath/ath11k/core.c index c576bbba52bf..2e9f8a5e61e4 100644 --- a/drivers/net/wireless/ath/ath11k/core.c +++ b/drivers/net/wireless/ath/ath11k/core.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -907,12 +907,51 @@ static const struct ath11k_hw_params ath11k_hw_params[] = { }, }; -static inline struct ath11k_pdev *ath11k_core_get_single_pdev(struct ath11k_base *ab) -{ - WARN_ON(!ab->hw_params.single_pdev_only); - - return &ab->pdevs[0]; -} +static const struct dmi_system_id ath11k_pm_quirk_table[] = { + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21J4"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21K4"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21K6"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21K8"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21KA"), + }, + }, + { + .driver_data = (void *)ATH11K_PM_WOW, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21F9"), + }, + }, + {} +}; void ath11k_fw_stats_pdevs_free(struct list_head *head) { @@ -972,23 +1011,33 @@ bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab) return ab->hw_params.coldboot_cal_mm; } -int ath11k_core_suspend(struct ath11k_base *ab) +/* Check if we need to continue with suspend/resume operation. + * Return: + * a negative value: error happens and don't continue. + * 0: no error but don't continue. + * positive value: no error and do continue. + */ +static int ath11k_core_continue_suspend_resume(struct ath11k_base *ab) { - int ret; - struct ath11k_pdev *pdev; struct ath11k *ar; if (!ab->hw_params.supports_suspend) return -EOPNOTSUPP; /* so far single_pdev_only chips have supports_suspend as true - * and only the first pdev is valid. + * so pass 0 as a dummy pdev_id here. */ - pdev = ath11k_core_get_single_pdev(ab); - ar = pdev->ar; + ar = ab->pdevs[0].ar; if (!ar || ar->state != ATH11K_STATE_OFF) return 0; + return 1; +} + +static int ath11k_core_suspend_wow(struct ath11k_base *ab) +{ + int ret; + ret = ath11k_dp_rx_pktlog_stop(ab, true); if (ret) { ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n", @@ -996,7 +1045,10 @@ int ath11k_core_suspend(struct ath11k_base *ab) return ret; } - ret = ath11k_mac_wait_tx_complete(ar); + /* So far only single_pdev_only devices can reach here, + * so it is valid to handle the first, and the only, pdev. + */ + ret = ath11k_mac_wait_tx_complete(ab->pdevs[0].ar); if (ret) { ath11k_warn(ab, "failed to wait tx complete: %d\n", ret); return ret; @@ -1029,24 +1081,146 @@ int ath11k_core_suspend(struct ath11k_base *ab) return 0; } + +static int ath11k_core_suspend_default(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_dp_rx_pktlog_stop(ab, true); + if (ret) { + ath11k_warn(ab, "failed to stop dp rx (and timer) pktlog during suspend: %d\n", + ret); + return ret; + } + + /* So far only single_pdev_only devices can reach here, + * so it is valid to handle the first, and the only, pdev. + */ + ret = ath11k_mac_wait_tx_complete(ab->pdevs[0].ar); + if (ret) { + ath11k_warn(ab, "failed to wait tx complete: %d\n", ret); + return ret; + } + + ret = ath11k_dp_rx_pktlog_stop(ab, false); + if (ret) { + ath11k_warn(ab, "failed to stop dp rx pktlog during suspend: %d\n", + ret); + return ret; + } + + ath11k_ce_stop_shadow_timers(ab); + ath11k_dp_stop_shadow_timers(ab); + + /* PM framework skips suspend_late/resume_early callbacks + * if other devices report errors in their suspend callbacks. + * However ath11k_core_resume() would still be called because + * here we return success thus kernel put us on dpm_suspended_list. + * Since we won't go through a power down/up cycle, there is + * no chance to call complete(&ab->restart_completed) in + * ath11k_core_restart(), making ath11k_core_resume() timeout. + * So call it here to avoid this issue. This also works in case + * no error happens thus suspend_late/resume_early get called, + * because it will be reinitialized in ath11k_core_resume_early(). + */ + complete(&ab->restart_completed); + + return 0; +} + +int ath11k_core_suspend(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + if (ab->actual_pm_policy == ATH11K_PM_WOW) + return ath11k_core_suspend_wow(ab); + + return ath11k_core_suspend_default(ab); +} EXPORT_SYMBOL(ath11k_core_suspend); -int ath11k_core_resume(struct ath11k_base *ab) +int ath11k_core_suspend_late(struct ath11k_base *ab) { int ret; - struct ath11k_pdev *pdev; + + ret = ath11k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + if (ab->actual_pm_policy == ATH11K_PM_WOW) + return 0; + + ath11k_hif_irq_disable(ab); + ath11k_hif_ce_irq_disable(ab); + + ath11k_hif_power_down(ab, true); + + return 0; +} +EXPORT_SYMBOL(ath11k_core_suspend_late); + +int ath11k_core_resume_early(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + if (ab->actual_pm_policy == ATH11K_PM_WOW) + return 0; + + reinit_completion(&ab->restart_completed); + ret = ath11k_hif_power_up(ab); + if (ret) + ath11k_warn(ab, "failed to power up hif during resume: %d\n", ret); + + return ret; +} +EXPORT_SYMBOL(ath11k_core_resume_early); + +static int ath11k_core_resume_default(struct ath11k_base *ab) +{ struct ath11k *ar; + long time_left; + int ret; - if (!ab->hw_params.supports_suspend) - return -EOPNOTSUPP; + time_left = wait_for_completion_timeout(&ab->restart_completed, + ATH11K_RESET_TIMEOUT_HZ); + if (time_left == 0) { + ath11k_warn(ab, "timeout while waiting for restart complete"); + return -ETIMEDOUT; + } - /* so far signle_pdev_only chips have supports_suspend as true - * and only the first pdev is valid. + /* So far only single_pdev_only devices can reach here, + * so it is valid to handle the first, and the only, pdev. */ - pdev = ath11k_core_get_single_pdev(ab); - ar = pdev->ar; - if (!ar || ar->state != ATH11K_STATE_OFF) - return 0; + ar = ab->pdevs[0].ar; + if (ab->hw_params.current_cc_support && + ar->alpha2[0] != 0 && ar->alpha2[1] != 0) { + ret = ath11k_reg_set_cc(ar); + if (ret) { + ath11k_warn(ab, "failed to set country code during resume: %d\n", + ret); + return ret; + } + } + + ret = ath11k_dp_rx_pktlog_start(ab); + if (ret) + ath11k_warn(ab, "failed to start rx pktlog during resume: %d\n", + ret); + + return ret; +} + +static int ath11k_core_resume_wow(struct ath11k_base *ab) +{ + int ret; ret = ath11k_hif_resume(ab); if (ret) { @@ -1072,6 +1246,20 @@ int ath11k_core_resume(struct ath11k_base *ab) return 0; } + +int ath11k_core_resume(struct ath11k_base *ab) +{ + int ret; + + ret = ath11k_core_continue_suspend_resume(ab); + if (ret <= 0) + return ret; + + if (ab->actual_pm_policy == ATH11K_PM_WOW) + return ath11k_core_resume_wow(ab); + + return ath11k_core_resume_default(ab); +} EXPORT_SYMBOL(ath11k_core_resume); static void ath11k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data) @@ -1175,9 +1363,12 @@ int ath11k_core_check_dt(struct ath11k_base *ab) if (!node) return -ENOENT; - of_property_read_string(node, "qcom,ath11k-calibration-variant", + of_property_read_string(node, "qcom,calibration-variant", &variant); if (!variant) + of_property_read_string(node, "qcom,ath11k-calibration-variant", + &variant); + if (!variant) return -ENODATA; if (strscpy(ab->qmi.target.bdf_ext, variant, max_len) < 0) @@ -2047,6 +2238,7 @@ err_hal_srng_deinit: void ath11k_core_halt(struct ath11k *ar) { struct ath11k_base *ab = ar->ab; + struct list_head *pos, *n; lockdep_assert_held(&ar->conf_mutex); @@ -2056,12 +2248,18 @@ void ath11k_core_halt(struct ath11k *ar) ath11k_mac_scan_finish(ar); ath11k_mac_peer_cleanup_all(ar); cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ab->update_11d_work); rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); synchronize_rcu(); - INIT_LIST_HEAD(&ar->arvifs); + + spin_lock_bh(&ar->data_lock); + list_for_each_safe(pos, n, &ar->arvifs) + list_del_init(pos); + spin_unlock_bh(&ar->data_lock); + idr_init(&ar->txmgmt_idr); } @@ -2201,6 +2399,8 @@ static void ath11k_core_restart(struct work_struct *work) if (!ab->is_reset) ath11k_core_post_reconfigure_recovery(ab); + + complete(&ab->restart_completed); } static void ath11k_core_reset(struct work_struct *work) @@ -2257,6 +2457,7 @@ static void ath11k_core_reset(struct work_struct *work) reinit_completion(&ab->recovery_start); atomic_set(&ab->recovery_start_count, 0); + ath11k_coredump_collect(ab); ath11k_core_pre_reconfigure_recovery(ab); reinit_completion(&ab->reconfigure_complete); @@ -2270,7 +2471,7 @@ static void ath11k_core_reset(struct work_struct *work) ath11k_hif_irq_disable(ab); ath11k_hif_ce_irq_disable(ab); - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_hif_power_up(ab); ath11k_dbg(ab, ATH11K_DBG_BOOT, "reset started\n"); @@ -2320,10 +2521,62 @@ int ath11k_core_pre_init(struct ath11k_base *ab) } EXPORT_SYMBOL(ath11k_core_pre_init); +static int ath11k_core_pm_notify(struct notifier_block *nb, + unsigned long action, void *nouse) +{ + struct ath11k_base *ab = container_of(nb, struct ath11k_base, + pm_nb); + + switch (action) { + case PM_SUSPEND_PREPARE: + ab->actual_pm_policy = ab->pm_policy; + break; + case PM_HIBERNATION_PREPARE: + ab->actual_pm_policy = ATH11K_PM_DEFAULT; + break; + default: + break; + } + + return NOTIFY_OK; +} + +static int ath11k_core_pm_notifier_register(struct ath11k_base *ab) +{ + ab->pm_nb.notifier_call = ath11k_core_pm_notify; + return register_pm_notifier(&ab->pm_nb); +} + +void ath11k_core_pm_notifier_unregister(struct ath11k_base *ab) +{ + int ret; + + ret = unregister_pm_notifier(&ab->pm_nb); + if (ret) + /* just warn here, there is nothing can be done in fail case */ + ath11k_warn(ab, "failed to unregister PM notifier %d\n", ret); +} +EXPORT_SYMBOL(ath11k_core_pm_notifier_unregister); + int ath11k_core_init(struct ath11k_base *ab) { + const struct dmi_system_id *dmi_id; int ret; + dmi_id = dmi_first_match(ath11k_pm_quirk_table); + if (dmi_id) + ab->pm_policy = (kernel_ulong_t)dmi_id->driver_data; + else + ab->pm_policy = ATH11K_PM_DEFAULT; + + ath11k_dbg(ab, ATH11K_DBG_BOOT, "pm policy %u\n", ab->pm_policy); + + ret = ath11k_core_pm_notifier_register(ab); + if (ret) { + ath11k_err(ab, "failed to register PM notifier: %d\n", ret); + return ret; + } + ret = ath11k_core_soc_create(ab); if (ret) { ath11k_err(ab, "failed to create soc core: %d\n", ret); @@ -2343,10 +2596,10 @@ void ath11k_core_deinit(struct ath11k_base *ab) mutex_unlock(&ab->core_lock); - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_mac_destroy(ab); ath11k_core_soc_destroy(ab); - ath11k_fw_destroy(ab); + ath11k_core_pm_notifier_unregister(ab); } EXPORT_SYMBOL(ath11k_core_deinit); @@ -2393,9 +2646,11 @@ struct ath11k_base *ath11k_core_alloc(struct device *dev, size_t priv_size, INIT_WORK(&ab->restart_work, ath11k_core_restart); INIT_WORK(&ab->update_11d_work, ath11k_update_11d); INIT_WORK(&ab->reset_work, ath11k_core_reset); + INIT_WORK(&ab->dump_work, ath11k_coredump_upload); timer_setup(&ab->rx_replenish_retry, ath11k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); init_completion(&ab->wow.wakeup_completed); + init_completion(&ab->restart_completed); ab->dev = dev; ab->hif.bus = bus; diff --git a/drivers/net/wireless/ath/ath11k/core.h b/drivers/net/wireless/ath/ath11k/core.h index a9dc7fe7765a..339d4fca1ed5 100644 --- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_CORE_H @@ -16,6 +16,7 @@ #include <linux/rhashtable.h> #include <linux/average.h> #include <linux/firmware.h> +#include <linux/suspend.h> #include "qmi.h" #include "htc.h" @@ -32,6 +33,7 @@ #include "spectral.h" #include "wow.h" #include "fw.h" +#include "coredump.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -370,6 +372,7 @@ struct ath11k_vif { struct ieee80211_vif *vif; struct wmi_wmm_params_all_arg wmm_params; + struct wmi_wmm_params_all_arg muedca_params; struct list_head list; union { struct { @@ -685,7 +688,7 @@ struct ath11k { struct mutex conf_mutex; /* protects the radio specific data like debug stats, ppdu_stats_info stats, * vdev_stop_status info, scan data, ath11k_sta info, ath11k_vif info, - * channel context data, survey info, test mode data. + * channel context data, survey info, test mode data, channel_update_queue. */ spinlock_t data_lock; @@ -743,6 +746,9 @@ struct ath11k { struct completion bss_survey_done; struct work_struct regd_update_work; + struct work_struct channel_update_work; + /* protected with data_lock */ + struct list_head channel_update_queue; struct work_struct wmi_mgmt_tx_work; struct sk_buff_head wmi_mgmt_tx_queue; @@ -887,6 +893,11 @@ struct ath11k_msi_config { u16 hw_rev; }; +enum ath11k_pm_policy { + ATH11K_PM_DEFAULT, + ATH11K_PM_WOW, +}; + /* Master structure to hold the hw data which may be used in core module */ struct ath11k_base { enum ath11k_hw_rev hw_rev; @@ -900,6 +911,10 @@ struct ath11k_base { /* HW channel counters frequency value in hertz common to all MACs */ u32 cc_freq_hz; + struct ath11k_dump_file_data *dump_data; + size_t ath11k_coredump_len; + struct work_struct dump_work; + struct ath11k_htc htc; struct ath11k_dp dp; @@ -1041,6 +1056,8 @@ struct ath11k_base { DECLARE_BITMAP(fw_features, ATH11K_FW_FEATURE_COUNT); } fw; + struct completion restart_completed; + #ifdef CONFIG_NL80211_TESTMODE struct { u32 data_pos; @@ -1049,6 +1066,10 @@ struct ath11k_base { } testmode; #endif + enum ath11k_pm_policy pm_policy; + enum ath11k_pm_policy actual_pm_policy; + struct notifier_block pm_nb; + /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); }; @@ -1240,8 +1261,10 @@ void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd); int ath11k_core_check_dt(struct ath11k_base *ath11k); int ath11k_core_check_smbios(struct ath11k_base *ab); void ath11k_core_halt(struct ath11k *ar); +int ath11k_core_resume_early(struct ath11k_base *ab); int ath11k_core_resume(struct ath11k_base *ab); int ath11k_core_suspend(struct ath11k_base *ab); +int ath11k_core_suspend_late(struct ath11k_base *ab); void ath11k_core_pre_reconfigure_recovery(struct ath11k_base *ab); bool ath11k_core_coldboot_cal_support(struct ath11k_base *ab); @@ -1313,4 +1336,6 @@ static inline const char *ath11k_bus_str(enum ath11k_bus bus) return "unknown"; } +void ath11k_core_pm_notifier_unregister(struct ath11k_base *ab); + #endif /* _CORE_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/coredump.c b/drivers/net/wireless/ath/ath11k/coredump.c new file mode 100644 index 000000000000..b8bad358cebe --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump.c @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#include <linux/devcoredump.h> +#include "hif.h" +#include "coredump.h" +#include "debug.h" + +enum +ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type) +{ + enum ath11k_fw_crash_dump_type dump_type; + + switch (type) { + case HOST_DDR_REGION_TYPE: + dump_type = FW_CRASH_DUMP_REMOTE_MEM_DATA; + break; + case M3_DUMP_REGION_TYPE: + dump_type = FW_CRASH_DUMP_M3_DUMP; + break; + case PAGEABLE_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_PAGEABLE_DATA; + break; + case BDF_MEM_REGION_TYPE: + case CALDB_MEM_REGION_TYPE: + dump_type = FW_CRASH_DUMP_NONE; + break; + default: + dump_type = FW_CRASH_DUMP_TYPE_MAX; + break; + } + + return dump_type; +} +EXPORT_SYMBOL(ath11k_coredump_get_dump_type); + +void ath11k_coredump_upload(struct work_struct *work) +{ + struct ath11k_base *ab = container_of(work, struct ath11k_base, dump_work); + + ath11k_info(ab, "Uploading coredump\n"); + /* dev_coredumpv() takes ownership of the buffer */ + dev_coredumpv(ab->dev, ab->dump_data, ab->ath11k_coredump_len, GFP_KERNEL); + ab->dump_data = NULL; +} + +void ath11k_coredump_collect(struct ath11k_base *ab) +{ + ath11k_hif_coredump_download(ab); +} diff --git a/drivers/net/wireless/ath/ath11k/coredump.h b/drivers/net/wireless/ath/ath11k/coredump.h new file mode 100644 index 000000000000..3960d9385261 --- /dev/null +++ b/drivers/net/wireless/ath/ath11k/coredump.h @@ -0,0 +1,79 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2020 The Linux Foundation. All rights reserved. + * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef _ATH11K_COREDUMP_H_ +#define _ATH11K_COREDUMP_H_ + +#define ATH11K_FW_CRASH_DUMP_V2 2 + +enum ath11k_fw_crash_dump_type { + FW_CRASH_DUMP_PAGING_DATA, + FW_CRASH_DUMP_RDDM_DATA, + FW_CRASH_DUMP_REMOTE_MEM_DATA, + FW_CRASH_DUMP_PAGEABLE_DATA, + FW_CRASH_DUMP_M3_DUMP, + FW_CRASH_DUMP_NONE, + + /* keep last */ + FW_CRASH_DUMP_TYPE_MAX, +}; + +#define COREDUMP_TLV_HDR_SIZE 8 + +struct ath11k_tlv_dump_data { + /* see ath11k_fw_crash_dump_type above */ + __le32 type; + + /* in bytes */ + __le32 tlv_len; + + /* pad to 32-bit boundaries as needed */ + u8 tlv_data[]; +} __packed; + +struct ath11k_dump_file_data { + /* "ATH11K-FW-DUMP" */ + char df_magic[16]; + /* total dump len in bytes */ + __le32 len; + /* file dump version */ + __le32 version; + /* pci device id */ + __le32 chip_id; + /* qrtr instance id */ + __le32 qrtr_id; + /* pci domain id */ + __le32 bus_id; + guid_t guid; + /* time-of-day stamp */ + __le64 tv_sec; + /* time-of-day stamp, nano-seconds */ + __le64 tv_nsec; + /* room for growth w/out changing binary format */ + u8 unused[128]; + u8 data[]; +} __packed; + +#ifdef CONFIG_DEV_COREDUMP +enum ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type); +void ath11k_coredump_upload(struct work_struct *work); +void ath11k_coredump_collect(struct ath11k_base *ab); +#else +static inline enum +ath11k_fw_crash_dump_type ath11k_coredump_get_dump_type(int type) +{ + return FW_CRASH_DUMP_TYPE_MAX; +} + +static inline void ath11k_coredump_upload(struct work_struct *work) +{ +} + +static inline void ath11k_coredump_collect(struct ath11k_base *ab) +{ +} +#endif + +#endif diff --git a/drivers/net/wireless/ath/ath11k/dp.c b/drivers/net/wireless/ath/ath11k/dp.c index fbf666d0ecf1..3a544e5fefca 100644 --- a/drivers/net/wireless/ath/ath11k/dp.c +++ b/drivers/net/wireless/ath/ath11k/dp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <crypto/hash.h> @@ -104,14 +104,12 @@ void ath11k_dp_srng_cleanup(struct ath11k_base *ab, struct dp_srng *ring) if (!ring->vaddr_unaligned) return; - if (ring->cached) { - dma_unmap_single(ab->dev, ring->paddr_unaligned, ring->size, - DMA_FROM_DEVICE); - kfree(ring->vaddr_unaligned); - } else { + if (ring->cached) + dma_free_noncoherent(ab->dev, ring->size, ring->vaddr_unaligned, + ring->paddr_unaligned, DMA_FROM_DEVICE); + else dma_free_coherent(ab->dev, ring->size, ring->vaddr_unaligned, ring->paddr_unaligned); - } ring->vaddr_unaligned = NULL; } @@ -249,25 +247,14 @@ int ath11k_dp_srng_setup(struct ath11k_base *ab, struct dp_srng *ring, default: cached = false; } - - if (cached) { - ring->vaddr_unaligned = kzalloc(ring->size, GFP_KERNEL); - if (!ring->vaddr_unaligned) - return -ENOMEM; - - ring->paddr_unaligned = dma_map_single(ab->dev, - ring->vaddr_unaligned, - ring->size, - DMA_FROM_DEVICE); - if (dma_mapping_error(ab->dev, ring->paddr_unaligned)) { - kfree(ring->vaddr_unaligned); - ring->vaddr_unaligned = NULL; - return -ENOMEM; - } - } } - if (!cached) + if (cached) + ring->vaddr_unaligned = dma_alloc_noncoherent(ab->dev, ring->size, + &ring->paddr_unaligned, + DMA_FROM_DEVICE, + GFP_KERNEL); + else ring->vaddr_unaligned = dma_alloc_coherent(ab->dev, ring->size, &ring->paddr_unaligned, GFP_KERNEL); @@ -888,7 +875,7 @@ void ath11k_dp_pdev_free(struct ath11k_base *ab) struct ath11k *ar; int i; - del_timer_sync(&ab->mon_reap_timer); + timer_delete_sync(&ab->mon_reap_timer); for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -1183,7 +1170,7 @@ void ath11k_dp_shadow_stop_timer(struct ath11k_base *ab, if (!update_timer->init) return; - del_timer_sync(&update_timer->timer); + timer_delete_sync(&update_timer->timer); } void ath11k_dp_shadow_init_timer(struct ath11k_base *ab, diff --git a/drivers/net/wireless/ath/ath11k/dp.h b/drivers/net/wireless/ath/ath11k/dp.h index f777314db8b3..7a55afd33be8 100644 --- a/drivers/net/wireless/ath/ath11k/dp.h +++ b/drivers/net/wireless/ath/ath11k/dp.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2023, 2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_DP_H @@ -20,7 +20,6 @@ struct ath11k_ext_irq_grp; struct dp_rx_tid { u8 tid; - u32 *vaddr; dma_addr_t paddr; u32 size; u32 ba_win_sz; @@ -37,6 +36,9 @@ struct dp_rx_tid { /* Timer info related to fragments */ struct timer_list frag_timer; struct ath11k_base *ab; + u32 *vaddr_unaligned; + dma_addr_t paddr_unaligned; + u32 unaligned_size; }; #define DP_REO_DESC_FREE_THRESHOLD 64 diff --git a/drivers/net/wireless/ath/ath11k/dp_rx.c b/drivers/net/wireless/ath/ath11k/dp_rx.c index 029ecf51c9ef..ea2959305dec 100644 --- a/drivers/net/wireless/ath/ath11k/dp_rx.c +++ b/drivers/net/wireless/ath/ath11k/dp_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/ieee80211.h> @@ -675,11 +675,11 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { list_del(&cmd->list); rx_tid = &cmd->data; - if (rx_tid->vaddr) { - dma_unmap_single(ab->dev, rx_tid->paddr, - rx_tid->size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } kfree(cmd); } @@ -689,11 +689,11 @@ void ath11k_dp_reo_cmd_list_cleanup(struct ath11k_base *ab) list_del(&cmd_cache->list); dp->reo_cmd_cache_flush_count--; rx_tid = &cmd_cache->data; - if (rx_tid->vaddr) { - dma_unmap_single(ab->dev, rx_tid->paddr, - rx_tid->size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } kfree(cmd_cache); } @@ -708,11 +708,11 @@ static void ath11k_dp_reo_cmd_free(struct ath11k_dp *dp, void *ctx, if (status != HAL_REO_CMD_SUCCESS) ath11k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", rx_tid->tid, status); - if (rx_tid->vaddr) { - dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + if (rx_tid->vaddr_unaligned) { + dma_free_noncoherent(dp->ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } } @@ -749,10 +749,10 @@ static void ath11k_dp_reo_cache_flush(struct ath11k_base *ab, if (ret) { ath11k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", rx_tid->tid, ret); - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } } @@ -802,10 +802,10 @@ static void ath11k_dp_rx_tid_del_func(struct ath11k_dp *dp, void *ctx, return; free_desc: - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } void ath11k_peer_rx_tid_delete(struct ath11k *ar, @@ -831,14 +831,16 @@ void ath11k_peer_rx_tid_delete(struct ath11k *ar, if (ret != -ESHUTDOWN) ath11k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", tid, ret); - dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ar->ab->dev, rx_tid->unaligned_size, + rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; } rx_tid->paddr = 0; + rx_tid->paddr_unaligned = 0; rx_tid->size = 0; + rx_tid->unaligned_size = 0; } static int ath11k_dp_rx_link_desc_return(struct ath11k_base *ab, @@ -904,7 +906,7 @@ void ath11k_peer_frags_flush(struct ath11k *ar, struct ath11k_peer *peer) rx_tid = &peer->rx_tid[i]; spin_unlock_bh(&ar->ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ar->ab->base_lock); ath11k_dp_rx_frags_cleanup(rx_tid, true); @@ -925,7 +927,7 @@ void ath11k_peer_rx_tid_cleanup(struct ath11k *ar, struct ath11k_peer *peer) ath11k_dp_rx_frags_cleanup(rx_tid, true); spin_unlock_bh(&ar->ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ar->ab->base_lock); } } @@ -982,10 +984,9 @@ static void ath11k_dp_rx_tid_mem_free(struct ath11k_base *ab, if (!rx_tid->active) goto unlock_exit; - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_free_noncoherent(ab->dev, rx_tid->unaligned_size, rx_tid->vaddr_unaligned, + rx_tid->paddr_unaligned, DMA_BIDIRECTIONAL); + rx_tid->vaddr_unaligned = NULL; rx_tid->active = false; @@ -1000,9 +1001,8 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, struct ath11k_base *ab = ar->ab; struct ath11k_peer *peer; struct dp_rx_tid *rx_tid; - u32 hw_desc_sz; - u32 *addr_aligned; - void *vaddr; + u32 hw_desc_sz, *vaddr; + void *vaddr_unaligned; dma_addr_t paddr; int ret; @@ -1050,37 +1050,34 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, else hw_desc_sz = ath11k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); - vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); - if (!vaddr) { + rx_tid->unaligned_size = hw_desc_sz + HAL_LINK_DESC_ALIGN - 1; + vaddr_unaligned = dma_alloc_noncoherent(ab->dev, rx_tid->unaligned_size, &paddr, + DMA_BIDIRECTIONAL, GFP_ATOMIC); + if (!vaddr_unaligned) { spin_unlock_bh(&ab->base_lock); return -ENOMEM; } - addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); - - ath11k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, - ssn, pn_type); - - paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, - DMA_BIDIRECTIONAL); - - ret = dma_mapping_error(ab->dev, paddr); - if (ret) { - spin_unlock_bh(&ab->base_lock); - ath11k_warn(ab, "failed to setup dma map for peer %pM rx tid %d: %d\n", - peer_mac, tid, ret); - goto err_mem_free; - } - - rx_tid->vaddr = vaddr; - rx_tid->paddr = paddr; + rx_tid->vaddr_unaligned = vaddr_unaligned; + vaddr = PTR_ALIGN(vaddr_unaligned, HAL_LINK_DESC_ALIGN); + rx_tid->paddr_unaligned = paddr; + rx_tid->paddr = rx_tid->paddr_unaligned + ((unsigned long)vaddr - + (unsigned long)rx_tid->vaddr_unaligned); + ath11k_hal_reo_qdesc_setup(vaddr, tid, ba_win_sz, ssn, pn_type); rx_tid->size = hw_desc_sz; rx_tid->active = true; + /* After dma_alloc_noncoherent, vaddr is being modified for reo qdesc setup. + * Since these changes are not reflected in the device, driver now needs to + * explicitly call dma_sync_single_for_device. + */ + dma_sync_single_for_device(ab->dev, rx_tid->paddr, + rx_tid->size, + DMA_TO_DEVICE); spin_unlock_bh(&ab->base_lock); - ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, ba_win_sz); + ret = ath11k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, rx_tid->paddr, + tid, 1, ba_win_sz); if (ret) { ath11k_warn(ar->ab, "failed to setup rx reorder queue for peer %pM tid %d: %d\n", peer_mac, tid, ret); @@ -1088,12 +1085,6 @@ int ath11k_peer_rx_tid_setup(struct ath11k *ar, const u8 *peer_mac, int vdev_id, } return ret; - -err_mem_free: - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; - - return ret; } int ath11k_dp_rx_ampdu_start(struct ath11k *ar, @@ -2646,7 +2637,7 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id, struct ath11k *ar; struct hal_reo_dest_ring *desc; enum hal_reo_dest_ring_push_reason push_reason; - u32 cookie; + u32 cookie, info0, rx_msdu_info0, rx_mpdu_info0; int i; for (i = 0; i < MAX_RADIOS; i++) @@ -2659,11 +2650,14 @@ int ath11k_dp_process_rx(struct ath11k_base *ab, int ring_id, try_again: ath11k_hal_srng_access_begin(ab, srng); + /* Make sure descriptor is read after the head pointer. */ + dma_rmb(); + while (likely(desc = (struct hal_reo_dest_ring *)ath11k_hal_srng_dst_get_next_entry(ab, srng))) { cookie = FIELD_GET(BUFFER_ADDR_INFO1_SW_COOKIE, - desc->buf_addr_info.info1); + READ_ONCE(desc->buf_addr_info.info1)); buf_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_BUF_ID, cookie); mac_id = FIELD_GET(DP_RXDMA_BUF_COOKIE_PDEV_ID, cookie); @@ -2692,8 +2686,9 @@ try_again: num_buffs_reaped[mac_id]++; + info0 = READ_ONCE(desc->info0); push_reason = FIELD_GET(HAL_REO_DEST_RING_INFO0_PUSH_REASON, - desc->info0); + info0); if (unlikely(push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION)) { dev_kfree_skb_any(msdu); @@ -2701,18 +2696,21 @@ try_again: continue; } - rxcb->is_first_msdu = !!(desc->rx_msdu_info.info0 & + rx_msdu_info0 = READ_ONCE(desc->rx_msdu_info.info0); + rx_mpdu_info0 = READ_ONCE(desc->rx_mpdu_info.info0); + + rxcb->is_first_msdu = !!(rx_msdu_info0 & RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); - rxcb->is_last_msdu = !!(desc->rx_msdu_info.info0 & + rxcb->is_last_msdu = !!(rx_msdu_info0 & RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); - rxcb->is_continuation = !!(desc->rx_msdu_info.info0 & + rxcb->is_continuation = !!(rx_msdu_info0 & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION); rxcb->peer_id = FIELD_GET(RX_MPDU_DESC_META_DATA_PEER_ID, - desc->rx_mpdu_info.meta_data); + READ_ONCE(desc->rx_mpdu_info.meta_data)); rxcb->seq_no = FIELD_GET(RX_MPDU_DESC_INFO0_SEQ_NUM, - desc->rx_mpdu_info.info0); + rx_mpdu_info0); rxcb->tid = FIELD_GET(HAL_REO_DEST_RING_INFO0_RX_QUEUE_NUM, - desc->info0); + info0); rxcb->mac_id = mac_id; __skb_queue_tail(&msdu_list[mac_id], msdu); @@ -2831,8 +2829,6 @@ static void ath11k_dp_rx_update_peer_stats(struct ath11k_sta *arsta, rx_stats->dcm_count += ppdu_info->dcm; rx_stats->ru_alloc_cnt[ppdu_info->ru_alloc] += num_msdu; - arsta->rssi_comb = ppdu_info->rssi_comb; - BUILD_BUG_ON(ARRAY_SIZE(arsta->chain_signal) > ARRAY_SIZE(ppdu_info->rssi_chain_pri20)); @@ -3721,7 +3717,7 @@ static int ath11k_dp_rx_frag_h_mpdu(struct ath11k *ar, } spin_unlock_bh(&ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ab->base_lock); peer = ath11k_peer_find_by_id(ab, peer_id); @@ -4783,7 +4779,7 @@ u32 ath11k_dp_rx_mon_mpdu_pop(struct ath11k *ar, int mac_id, if (!msdu) { ath11k_dbg(ar->ab, ATH11K_DBG_DATA, "msdu_pop: invalid buf_id %d\n", buf_id); - break; + goto next_msdu; } rxcb = ATH11K_SKB_RXCB(msdu); if (!rxcb->unmapped) { @@ -5148,7 +5144,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, struct ath11k_mon_data *pmon = (struct ath11k_mon_data *)&dp->mon_data; const struct ath11k_hw_hal_params *hal_params; void *ring_entry; - void *mon_dst_srng; + struct hal_srng *mon_dst_srng; u32 ppdu_id; u32 rx_bufs_used; u32 ring_id; @@ -5165,6 +5161,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, spin_lock_bh(&pmon->mon_lock); + spin_lock_bh(&mon_dst_srng->lock); ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); ppdu_id = pmon->mon_ppdu_info.ppdu_id; @@ -5223,6 +5220,7 @@ static void ath11k_dp_rx_mon_dest_process(struct ath11k *ar, int mac_id, mon_dst_srng); } ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); + spin_unlock_bh(&mon_dst_srng->lock); spin_unlock_bh(&pmon->mon_lock); @@ -5410,7 +5408,7 @@ ath11k_dp_rx_full_mon_mpdu_pop(struct ath11k *ar, "full mon msdu_pop: invalid buf_id %d\n", buf_id); spin_unlock_bh(&rx_ring->idr_lock); - break; + goto next_msdu; } idr_remove(&rx_ring->bufs_idr, buf_id); spin_unlock_bh(&rx_ring->idr_lock); @@ -5612,7 +5610,7 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, struct hal_sw_mon_ring_entries *sw_mon_entries; struct ath11k_pdev_mon_stats *rx_mon_stats; struct sk_buff *head_msdu, *tail_msdu; - void *mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; + struct hal_srng *mon_dst_srng; void *ring_entry; u32 rx_bufs_used = 0, mpdu_rx_bufs_used; int quota = 0, ret; @@ -5628,6 +5626,9 @@ static int ath11k_dp_full_mon_process_rx(struct ath11k_base *ab, int mac_id, goto reap_status_ring; } + mon_dst_srng = &ar->ab->hal.srng_list[dp->rxdma_mon_dst_ring.ring_id]; + spin_lock_bh(&mon_dst_srng->lock); + ath11k_hal_srng_access_begin(ar->ab, mon_dst_srng); while ((ring_entry = ath11k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) { head_msdu = NULL; @@ -5671,6 +5672,7 @@ next_entry: } ath11k_hal_srng_access_end(ar->ab, mon_dst_srng); + spin_unlock_bh(&mon_dst_srng->lock); spin_unlock_bh(&pmon->mon_lock); if (rx_bufs_used) { @@ -5786,7 +5788,7 @@ int ath11k_dp_rx_pktlog_stop(struct ath11k_base *ab, bool stop_timer) int ret; if (stop_timer) - del_timer_sync(&ab->mon_reap_timer); + timer_delete_sync(&ab->mon_reap_timer); /* reap all the monitor related rings */ ret = ath11k_dp_purge_mon_ring(ab); diff --git a/drivers/net/wireless/ath/ath11k/fw.c b/drivers/net/wireless/ath/ath11k/fw.c index 4e36292a79db..cbbd8e57119f 100644 --- a/drivers/net/wireless/ath/ath11k/fw.c +++ b/drivers/net/wireless/ath/ath11k/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -166,3 +166,4 @@ void ath11k_fw_destroy(struct ath11k_base *ab) { release_firmware(ab->fw.fw); } +EXPORT_SYMBOL(ath11k_fw_destroy); diff --git a/drivers/net/wireless/ath/ath11k/hal.c b/drivers/net/wireless/ath/ath11k/hal.c index 61f4b6dd5380..8cb1505a5a0c 100644 --- a/drivers/net/wireless/ath/ath11k/hal.c +++ b/drivers/net/wireless/ath/ath11k/hal.c @@ -599,7 +599,7 @@ u32 ath11k_hal_ce_dst_status_get_length(void *buf) struct hal_ce_srng_dst_status_desc *desc = buf; u32 len; - len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, desc->flags); + len = FIELD_GET(HAL_CE_DST_STATUS_DESC_FLAGS_LEN, READ_ONCE(desc->flags)); desc->flags &= ~HAL_CE_DST_STATUS_DESC_FLAGS_LEN; return len; @@ -829,7 +829,7 @@ void ath11k_hal_srng_access_begin(struct ath11k_base *ab, struct hal_srng *srng) srng->u.src_ring.cached_tp = *(volatile u32 *)srng->u.src_ring.tp_addr; } else { - srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr; + srng->u.dst_ring.cached_hp = READ_ONCE(*srng->u.dst_ring.hp_addr); /* Try to prefetch the next descriptor in the ring */ if (srng->flags & HAL_SRNG_FLAGS_CACHED) diff --git a/drivers/net/wireless/ath/ath11k/hif.h b/drivers/net/wireless/ath/ath11k/hif.h index 674ff772b181..cd9c4b838246 100644 --- a/drivers/net/wireless/ath/ath11k/hif.h +++ b/drivers/net/wireless/ath/ath11k/hif.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _HIF_H_ @@ -18,7 +18,7 @@ struct ath11k_hif_ops { int (*start)(struct ath11k_base *ab); void (*stop)(struct ath11k_base *ab); int (*power_up)(struct ath11k_base *ab); - void (*power_down)(struct ath11k_base *ab); + void (*power_down)(struct ath11k_base *ab, bool is_suspend); int (*suspend)(struct ath11k_base *ab); int (*resume)(struct ath11k_base *ab); int (*map_service_to_pipe)(struct ath11k_base *ab, u16 service_id, @@ -31,6 +31,7 @@ struct ath11k_hif_ops { void (*ce_irq_enable)(struct ath11k_base *ab); void (*ce_irq_disable)(struct ath11k_base *ab); void (*get_ce_msi_idx)(struct ath11k_base *ab, u32 ce_id, u32 *msi_idx); + void (*coredump_download)(struct ath11k_base *ab); }; static inline void ath11k_hif_ce_irq_enable(struct ath11k_base *ab) @@ -67,12 +68,18 @@ static inline void ath11k_hif_irq_disable(struct ath11k_base *ab) static inline int ath11k_hif_power_up(struct ath11k_base *ab) { + if (!ab->hif.ops->power_up) + return -EOPNOTSUPP; + return ab->hif.ops->power_up(ab); } -static inline void ath11k_hif_power_down(struct ath11k_base *ab) +static inline void ath11k_hif_power_down(struct ath11k_base *ab, bool is_suspend) { - ab->hif.ops->power_down(ab); + if (!ab->hif.ops->power_down) + return; + + ab->hif.ops->power_down(ab, is_suspend); } static inline int ath11k_hif_suspend(struct ath11k_base *ab) @@ -146,4 +153,10 @@ static inline void ath11k_get_ce_msi_idx(struct ath11k_base *ab, u32 ce_id, *msi_data_idx = ce_id; } +static inline void ath11k_hif_coredump_download(struct ath11k_base *ab) +{ + if (ab->hif.ops->coredump_download) + ab->hif.ops->coredump_download(ab); +} + #endif /* _HIF_H_ */ diff --git a/drivers/net/wireless/ath/ath11k/mac.c b/drivers/net/wireless/ath/ath11k/mac.c index 1556392f7ad4..08d7b136851f 100644 --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <net/mac80211.h> @@ -1529,17 +1529,29 @@ static int ath11k_mac_set_vif_params(struct ath11k_vif *arvif, return ret; } -static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) +static struct ath11k_vif *ath11k_mac_get_tx_arvif(struct ath11k_vif *arvif) +{ + struct ieee80211_bss_conf *link_conf, *tx_bss_conf; + + lockdep_assert_wiphy(arvif->ar->hw->wiphy); + + link_conf = &arvif->vif->bss_conf; + tx_bss_conf = wiphy_dereference(arvif->ar->hw->wiphy, link_conf->tx_bss_conf); + if (tx_bss_conf) + return ath11k_vif_to_arvif(tx_bss_conf->vif); + + return NULL; +} + +static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif, + struct ath11k_vif *tx_arvif) { - struct ath11k_vif *tx_arvif; struct ieee80211_ema_beacons *beacons; int ret = 0; bool nontx_vif_params_set = false; u32 params = 0; u8 i = 0; - tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif); - beacons = ieee80211_beacon_get_template_ema_list(tx_arvif->ar->hw, tx_arvif->vif, 0); if (!beacons || !beacons->cnt) { @@ -1585,25 +1597,22 @@ static int ath11k_mac_setup_bcn_tmpl_ema(struct ath11k_vif *arvif) return ret; } -static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif) +static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif, + struct ath11k_vif *tx_arvif) { struct ath11k *ar = arvif->ar; struct ath11k_base *ab = ar->ab; - struct ath11k_vif *tx_arvif = arvif; struct ieee80211_hw *hw = ar->hw; struct ieee80211_vif *vif = arvif->vif; struct ieee80211_mutable_offsets offs = {}; struct sk_buff *bcn; int ret; - if (vif->mbssid_tx_vif) { - tx_arvif = ath11k_vif_to_arvif(vif->mbssid_tx_vif); - if (tx_arvif != arvif) { - ar = tx_arvif->ar; - ab = ar->ab; - hw = ar->hw; - vif = tx_arvif->vif; - } + if (tx_arvif != arvif) { + ar = tx_arvif->ar; + ab = ar->ab; + hw = ar->hw; + vif = tx_arvif->vif; } bcn = ieee80211_beacon_get_template(hw, vif, &offs, 0); @@ -1632,6 +1641,7 @@ static int ath11k_mac_setup_bcn_tmpl_mbssid(struct ath11k_vif *arvif) static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) { struct ieee80211_vif *vif = arvif->vif; + struct ath11k_vif *tx_arvif; if (arvif->vdev_type != WMI_VDEV_TYPE_AP) return 0; @@ -1639,14 +1649,18 @@ static int ath11k_mac_setup_bcn_tmpl(struct ath11k_vif *arvif) /* Target does not expect beacon templates for the already up * non-transmitting interfaces, and results in a crash if sent. */ - if (vif->mbssid_tx_vif && - arvif != ath11k_vif_to_arvif(vif->mbssid_tx_vif) && arvif->is_up) - return 0; + tx_arvif = ath11k_mac_get_tx_arvif(arvif); + if (tx_arvif) { + if (arvif != tx_arvif && arvif->is_up) + return 0; - if (vif->bss_conf.ema_ap && vif->mbssid_tx_vif) - return ath11k_mac_setup_bcn_tmpl_ema(arvif); + if (vif->bss_conf.ema_ap) + return ath11k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif); + } else { + tx_arvif = arvif; + } - return ath11k_mac_setup_bcn_tmpl_mbssid(arvif); + return ath11k_mac_setup_bcn_tmpl_mbssid(arvif, tx_arvif); } void ath11k_mac_bcn_tx_event(struct ath11k_vif *arvif) @@ -1674,7 +1688,7 @@ static void ath11k_control_beaconing(struct ath11k_vif *arvif, struct ieee80211_bss_conf *info) { struct ath11k *ar = arvif->ar; - struct ath11k_vif *tx_arvif = NULL; + struct ath11k_vif *tx_arvif; int ret = 0; lockdep_assert_held(&arvif->ar->conf_mutex); @@ -1701,9 +1715,7 @@ static void ath11k_control_beaconing(struct ath11k_vif *arvif, ether_addr_copy(arvif->bssid, info->bssid); - if (arvif->vif->mbssid_tx_vif) - tx_arvif = ath11k_vif_to_arvif(arvif->vif->mbssid_tx_vif); - + tx_arvif = ath11k_mac_get_tx_arvif(arvif); ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, arvif->bssid, tx_arvif ? tx_arvif->bssid : NULL, @@ -5204,6 +5216,45 @@ exit: return ret; } +static int ath11k_mac_op_conf_tx_mu_edca(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct ath11k_vif *arvif = ath11k_vif_to_arvif(vif); + struct ath11k *ar = hw->priv; + struct wmi_wmm_params_arg *p; + int ret; + + switch (ac) { + case IEEE80211_AC_VO: + p = &arvif->muedca_params.ac_vo; + break; + case IEEE80211_AC_VI: + p = &arvif->muedca_params.ac_vi; + break; + case IEEE80211_AC_BE: + p = &arvif->muedca_params.ac_be; + break; + case IEEE80211_AC_BK: + p = &arvif->muedca_params.ac_bk; + break; + default: + ath11k_warn(ar->ab, "error ac: %d", ac); + return -EINVAL; + } + + p->cwmin = u8_get_bits(params->mu_edca_param_rec.ecw_min_max, GENMASK(3, 0)); + p->cwmax = u8_get_bits(params->mu_edca_param_rec.ecw_min_max, GENMASK(7, 4)); + p->aifs = u8_get_bits(params->mu_edca_param_rec.aifsn, GENMASK(3, 0)); + p->txop = params->mu_edca_param_rec.mu_edca_timer; + + ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, + &arvif->muedca_params, + WMI_WMM_PARAM_TYPE_11AX_MU_EDCA); + return ret; +} + static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned int link_id, u16 ac, @@ -5242,12 +5293,22 @@ static int ath11k_mac_op_conf_tx(struct ieee80211_hw *hw, p->txop = params->txop; ret = ath11k_wmi_send_wmm_update_cmd_tlv(ar, arvif->vdev_id, - &arvif->wmm_params); + &arvif->wmm_params, + WMI_WMM_PARAM_TYPE_LEGACY); if (ret) { ath11k_warn(ar->ab, "failed to set wmm params: %d\n", ret); goto exit; } + if (params->mu_edca) { + ret = ath11k_mac_op_conf_tx_mu_edca(hw, vif, link_id, ac, + params); + if (ret) { + ath11k_warn(ar->ab, "failed to set mu_edca params: %d\n", ret); + goto exit; + } + } + ret = ath11k_conf_tx_uapsd(ar, vif, ac, params->uapsd); if (ret) @@ -5336,8 +5397,6 @@ static int ath11k_mac_set_txbf_conf(struct ath11k_vif *arvif) if (vht_cap & (IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE)) { nsts = vht_cap & IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; nsts >>= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; - if (nsts > (ar->num_rx_chains - 1)) - nsts = ar->num_rx_chains - 1; value |= SM(nsts, WMI_TXBF_STS_CAP_OFFSET); } @@ -5421,9 +5480,6 @@ static void ath11k_set_vht_txbf_cap(struct ath11k *ar, u32 *vht_cap) /* Enable Beamformee STS Field only if SU BF is enabled */ if (subfee) { - if (nsts > (ar->num_rx_chains - 1)) - nsts = ar->num_rx_chains - 1; - nsts <<= IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT; nsts &= IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK; *vht_cap |= nsts; @@ -6288,6 +6344,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) { struct ath11k *ar = hw->priv; struct htt_ppdu_stats_info *ppdu_stats, *tmp; + struct scan_chan_list_params *params; int ret; ath11k_mac_drain_tx(ar); @@ -6303,6 +6360,7 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) mutex_unlock(&ar->conf_mutex); cancel_delayed_work_sync(&ar->scan.timeout); + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ar->ab->update_11d_work); @@ -6312,10 +6370,19 @@ static void ath11k_mac_op_stop(struct ieee80211_hw *hw, bool suspend) } spin_lock_bh(&ar->data_lock); + list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { list_del(&ppdu_stats->list); kfree(ppdu_stats); } + + while ((params = list_first_entry_or_null(&ar->channel_update_queue, + struct scan_chan_list_params, + list))) { + list_del(¶ms->list); + kfree(params); + } + spin_unlock_bh(&ar->data_lock); rcu_assign_pointer(ar->ab->pdevs_active[ar->pdev_idx], NULL); @@ -6330,23 +6397,20 @@ static int ath11k_mac_setup_vdev_params_mbssid(struct ath11k_vif *arvif, { struct ath11k *ar = arvif->ar; struct ath11k_vif *tx_arvif; - struct ieee80211_vif *tx_vif; *tx_vdev_id = 0; - tx_vif = arvif->vif->mbssid_tx_vif; - if (!tx_vif) { + tx_arvif = ath11k_mac_get_tx_arvif(arvif); + if (!tx_arvif) { *flags = WMI_HOST_VDEV_FLAGS_NON_MBSSID_AP; return 0; } - tx_arvif = ath11k_vif_to_arvif(tx_vif); - if (arvif->vif->bss_conf.nontransmitted) { - if (ar->hw->wiphy != ieee80211_vif_to_wdev(tx_vif)->wiphy) + if (ar->hw->wiphy != tx_arvif->ar->hw->wiphy) return -EINVAL; *flags = WMI_HOST_VDEV_FLAGS_NON_TRANSMIT_AP; - *tx_vdev_id = ath11k_vif_to_arvif(tx_vif)->vdev_id; + *tx_vdev_id = tx_arvif->vdev_id; } else if (tx_arvif == arvif) { *flags = WMI_HOST_VDEV_FLAGS_TRANSMIT_AP; } else { @@ -7306,8 +7370,7 @@ ath11k_mac_update_vif_chan(struct ath11k *ar, int n_vifs) { struct ath11k_base *ab = ar->ab; - struct ath11k_vif *arvif, *tx_arvif = NULL; - struct ieee80211_vif *mbssid_tx_vif; + struct ath11k_vif *arvif, *tx_arvif; int ret; int i; bool monitor_vif = false; @@ -7361,10 +7424,7 @@ ath11k_mac_update_vif_chan(struct ath11k *ar, ath11k_warn(ab, "failed to update bcn tmpl during csa: %d\n", ret); - mbssid_tx_vif = arvif->vif->mbssid_tx_vif; - if (mbssid_tx_vif) - tx_arvif = ath11k_vif_to_arvif(mbssid_tx_vif); - + tx_arvif = ath11k_mac_get_tx_arvif(arvif); ret = ath11k_wmi_vdev_up(arvif->ar, arvif->vdev_id, arvif->aid, arvif->bssid, tx_arvif ? tx_arvif->bssid : NULL, @@ -9912,12 +9972,17 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar) struct ath11k_base *ab = ar->ab; struct ieee80211_iface_combination *combinations; struct ieee80211_iface_limit *limits; - int n_limits; + int n_limits, n_combos; bool p2p; p2p = ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_P2P_DEVICE); - combinations = kzalloc(sizeof(*combinations), GFP_KERNEL); + if (ab->hw_params.support_dual_stations) + n_combos = 2; + else + n_combos = 1; + + combinations = kcalloc(n_combos, sizeof(*combinations), GFP_KERNEL); if (!combinations) return -ENOMEM; @@ -9932,7 +9997,9 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar) return -ENOMEM; } + limits[0].max = 1; limits[0].types |= BIT(NL80211_IFTYPE_STATION); + limits[1].max = 16; limits[1].types |= BIT(NL80211_IFTYPE_AP); if (IS_ENABLED(CONFIG_MAC80211_MESH) && ab->hw_params.interface_modes & BIT(NL80211_IFTYPE_MESH_POINT)) @@ -9942,25 +10009,24 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar) combinations[0].n_limits = n_limits; combinations[0].beacon_int_infra_match = true; combinations[0].beacon_int_min_gcd = 100; + combinations[0].max_interfaces = 16; + combinations[0].num_different_channels = 1; + combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80) | + BIT(NL80211_CHAN_WIDTH_80P80) | + BIT(NL80211_CHAN_WIDTH_160); if (ab->hw_params.support_dual_stations) { limits[0].max = 2; - limits[1].max = 1; - - combinations[0].max_interfaces = ab->hw_params.num_vdevs; - combinations[0].num_different_channels = 2; - } else { - limits[0].max = 1; - limits[1].max = 16; - combinations[0].max_interfaces = 16; - combinations[0].num_different_channels = 1; - combinations[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80) | - BIT(NL80211_CHAN_WIDTH_80P80) | - BIT(NL80211_CHAN_WIDTH_160); + combinations[1].limits = limits; + combinations[1].n_limits = n_limits; + combinations[1].beacon_int_infra_match = true; + combinations[1].beacon_int_min_gcd = 100; + combinations[1].max_interfaces = ab->hw_params.num_vdevs; + combinations[1].num_different_channels = 2; } if (p2p) { @@ -9971,7 +10037,7 @@ static int ath11k_mac_setup_iface_combinations(struct ath11k *ar) } ar->hw->wiphy->iface_combinations = combinations; - ar->hw->wiphy->n_iface_combinations = 1; + ar->hw->wiphy->n_iface_combinations = n_combos; return 0; } @@ -10019,6 +10085,7 @@ static const struct wiphy_iftype_ext_capab ath11k_iftypes_ext_capa[] = { static void __ath11k_mac_unregister(struct ath11k *ar) { + cancel_work_sync(&ar->channel_update_work); cancel_work_sync(&ar->regd_update_work); ieee80211_unregister_hw(ar->hw); @@ -10418,6 +10485,8 @@ int ath11k_mac_allocate(struct ath11k_base *ab) init_completion(&ar->thermal.wmi_sync); INIT_DELAYED_WORK(&ar->scan.timeout, ath11k_scan_timeout_work); + INIT_WORK(&ar->channel_update_work, ath11k_regd_update_chan_list_work); + INIT_LIST_HEAD(&ar->channel_update_queue); INIT_WORK(&ar->regd_update_work, ath11k_regd_update_work); INIT_WORK(&ar->wmi_mgmt_tx_work, ath11k_mgmt_over_wmi_tx_work); diff --git a/drivers/net/wireless/ath/ath11k/mhi.c b/drivers/net/wireless/ath/ath11k/mhi.c index 6e45f464a429..acd76e9392d3 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.c +++ b/drivers/net/wireless/ath/ath11k/mhi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/msi.h> @@ -454,9 +454,17 @@ int ath11k_mhi_start(struct ath11k_pci *ab_pci) return 0; } -void ath11k_mhi_stop(struct ath11k_pci *ab_pci) +void ath11k_mhi_stop(struct ath11k_pci *ab_pci, bool is_suspend) { - mhi_power_down(ab_pci->mhi_ctrl, true); + /* During suspend we need to use mhi_power_down_keep_dev() + * workaround, otherwise ath11k_core_resume() will timeout + * during resume. + */ + if (is_suspend) + mhi_power_down_keep_dev(ab_pci->mhi_ctrl, true); + else + mhi_power_down(ab_pci->mhi_ctrl, true); + mhi_unprepare_after_power_down(ab_pci->mhi_ctrl); } @@ -491,3 +499,8 @@ int ath11k_mhi_resume(struct ath11k_pci *ab_pci) return 0; } + +void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic) +{ + mhi_download_rddm_image(mhi_ctrl, in_panic); +} diff --git a/drivers/net/wireless/ath/ath11k/mhi.h b/drivers/net/wireless/ath/ath11k/mhi.h index a682aad52fc5..5c5c2b03c81f 100644 --- a/drivers/net/wireless/ath/ath11k/mhi.h +++ b/drivers/net/wireless/ath/ath11k/mhi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH11K_MHI_H #define _ATH11K_MHI_H @@ -18,7 +18,7 @@ #define MHICTRL_RESET_MASK 0x2 int ath11k_mhi_start(struct ath11k_pci *ar_pci); -void ath11k_mhi_stop(struct ath11k_pci *ar_pci); +void ath11k_mhi_stop(struct ath11k_pci *ar_pci, bool is_suspend); int ath11k_mhi_register(struct ath11k_pci *ar_pci); void ath11k_mhi_unregister(struct ath11k_pci *ar_pci); void ath11k_mhi_set_mhictrl_reset(struct ath11k_base *ab); @@ -26,5 +26,6 @@ void ath11k_mhi_clear_vector(struct ath11k_base *ab); int ath11k_mhi_suspend(struct ath11k_pci *ar_pci); int ath11k_mhi_resume(struct ath11k_pci *ar_pci); +void ath11k_mhi_coredump(struct mhi_controller *mhi_ctrl, bool in_panic); #endif diff --git a/drivers/net/wireless/ath/ath11k/pci.c b/drivers/net/wireless/ath/ath11k/pci.c index b93f04973ad7..78444f8ea153 100644 --- a/drivers/net/wireless/ath/ath11k/pci.c +++ b/drivers/net/wireless/ath/ath11k/pci.c @@ -1,13 +1,15 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> #include <linux/msi.h> #include <linux/pci.h> #include <linux/of.h> +#include <linux/time.h> +#include <linux/vmalloc.h> #include "pci.h" #include "core.h" @@ -610,6 +612,187 @@ static void ath11k_pci_aspm_restore(struct ath11k_pci *ab_pci) PCI_EXP_LNKCTL_ASPMC); } +#ifdef CONFIG_DEV_COREDUMP +static int ath11k_pci_coredump_calculate_size(struct ath11k_base *ab, u32 *dump_seg_sz) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct ath11k_tlv_dump_data *dump_tlv; + enum ath11k_fw_crash_dump_type mem_type; + u32 len = 0, rddm_tlv_sz = 0, paging_tlv_sz = 0; + struct ath11k_dump_file_data *file_data; + int i; + + rddm_img = mhi_ctrl->rddm_image; + if (!rddm_img) { + ath11k_err(ab, "No RDDM dump found\n"); + return 0; + } + + fw_img = mhi_ctrl->fbc_image; + + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + paging_tlv_sz += fw_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA] = paging_tlv_sz; + + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + rddm_tlv_sz += rddm_img->mhi_buf[i].len; + } + dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA] = rddm_tlv_sz; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath11k_coredump_get_dump_type(ab->qmi.target_mem[i].type); + + if (mem_type == FW_CRASH_DUMP_NONE) + continue; + + if (mem_type == FW_CRASH_DUMP_TYPE_MAX) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "target mem region type %d not supported", + ab->qmi.target_mem[i].type); + continue; + } + + if (!ab->qmi.target_mem[i].anyaddr) + continue; + + dump_seg_sz[mem_type] += ab->qmi.target_mem[i].size; + } + + for (i = 0; i < FW_CRASH_DUMP_TYPE_MAX; i++) { + if (!dump_seg_sz[i]) + continue; + + len += sizeof(*dump_tlv) + dump_seg_sz[i]; + } + + if (len) + len += sizeof(*file_data); + + return len; +} + +static void ath11k_pci_coredump_download(struct ath11k_base *ab) +{ + struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); + struct mhi_controller *mhi_ctrl = ab_pci->mhi_ctrl; + struct image_info *rddm_img, *fw_img; + struct timespec64 timestamp; + int i, len, mem_idx; + enum ath11k_fw_crash_dump_type mem_type; + struct ath11k_dump_file_data *file_data; + struct ath11k_tlv_dump_data *dump_tlv; + size_t hdr_len = sizeof(*file_data); + void *buf; + u32 dump_seg_sz[FW_CRASH_DUMP_TYPE_MAX] = { 0 }; + + ath11k_mhi_coredump(mhi_ctrl, false); + + len = ath11k_pci_coredump_calculate_size(ab, dump_seg_sz); + if (!len) { + ath11k_warn(ab, "No crash dump data found for devcoredump"); + return; + } + + rddm_img = mhi_ctrl->rddm_image; + fw_img = mhi_ctrl->fbc_image; + + /* dev_coredumpv() requires vmalloc data */ + buf = vzalloc(len); + if (!buf) + return; + + ab->dump_data = buf; + ab->ath11k_coredump_len = len; + file_data = ab->dump_data; + strscpy(file_data->df_magic, "ATH11K-FW-DUMP", sizeof(file_data->df_magic)); + file_data->len = cpu_to_le32(len); + file_data->version = cpu_to_le32(ATH11K_FW_CRASH_DUMP_V2); + file_data->chip_id = cpu_to_le32(ab_pci->dev_id); + file_data->qrtr_id = cpu_to_le32(ab_pci->ab->qmi.service_ins_id); + file_data->bus_id = cpu_to_le32(pci_domain_nr(ab_pci->pdev->bus)); + guid_gen(&file_data->guid); + ktime_get_real_ts64(×tamp); + file_data->tv_sec = cpu_to_le64(timestamp.tv_sec); + file_data->tv_nsec = cpu_to_le64(timestamp.tv_nsec); + buf += hdr_len; + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_PAGING_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_PAGING_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < fw_img->entries ; i++) { + if (!fw_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)fw_img->mhi_buf[i].buf, + fw_img->mhi_buf[i].len); + buf += fw_img->mhi_buf[i].len; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(FW_CRASH_DUMP_RDDM_DATA); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[FW_CRASH_DUMP_RDDM_DATA]); + buf += COREDUMP_TLV_HDR_SIZE; + + /* append all segments together as they are all part of a single contiguous + * block of memory + */ + for (i = 0; i < rddm_img->entries; i++) { + if (!rddm_img->mhi_buf[i].buf) + continue; + + memcpy_fromio(buf, (void const __iomem *)rddm_img->mhi_buf[i].buf, + rddm_img->mhi_buf[i].len); + buf += rddm_img->mhi_buf[i].len; + } + + mem_idx = FW_CRASH_DUMP_REMOTE_MEM_DATA; + for (; mem_idx < FW_CRASH_DUMP_TYPE_MAX; mem_idx++) { + if (mem_idx == FW_CRASH_DUMP_NONE) + continue; + + for (i = 0; i < ab->qmi.mem_seg_count; i++) { + mem_type = ath11k_coredump_get_dump_type + (ab->qmi.target_mem[i].type); + + if (mem_type != mem_idx) + continue; + + if (!ab->qmi.target_mem[i].anyaddr) { + ath11k_dbg(ab, ATH11K_DBG_PCI, + "Skipping mem region type %d", + ab->qmi.target_mem[i].type); + continue; + } + + dump_tlv = buf; + dump_tlv->type = cpu_to_le32(mem_idx); + dump_tlv->tlv_len = cpu_to_le32(dump_seg_sz[mem_idx]); + buf += COREDUMP_TLV_HDR_SIZE; + + memcpy_fromio(buf, ab->qmi.target_mem[i].iaddr, + ab->qmi.target_mem[i].size); + + buf += ab->qmi.target_mem[i].size; + } + } + + queue_work(ab->workqueue, &ab->dump_work); +} +#endif + static int ath11k_pci_power_up(struct ath11k_base *ab) { struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); @@ -638,7 +821,7 @@ static int ath11k_pci_power_up(struct ath11k_base *ab) return 0; } -static void ath11k_pci_power_down(struct ath11k_base *ab) +static void ath11k_pci_power_down(struct ath11k_base *ab, bool is_suspend) { struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); @@ -649,7 +832,7 @@ static void ath11k_pci_power_down(struct ath11k_base *ab) ath11k_pci_msi_disable(ab_pci); - ath11k_mhi_stop(ab_pci); + ath11k_mhi_stop(ab_pci, is_suspend); clear_bit(ATH11K_FLAG_DEVICE_INIT_DONE, &ab->dev_flags); ath11k_pci_sw_reset(ab_pci->ab, false); } @@ -713,6 +896,9 @@ static const struct ath11k_hif_ops ath11k_pci_hif_ops = { .ce_irq_enable = ath11k_pci_hif_ce_irq_enable, .ce_irq_disable = ath11k_pci_hif_ce_irq_disable, .get_ce_msi_idx = ath11k_pcic_get_ce_msi_idx, +#ifdef CONFIG_DEV_COREDUMP + .coredump_download = ath11k_pci_coredump_download, +#endif }; static void ath11k_pci_read_hw_version(struct ath11k_base *ab, u32 *major, u32 *minor) @@ -735,7 +921,7 @@ static int ath11k_pci_set_irq_affinity_hint(struct ath11k_pci *ab_pci, if (test_bit(ATH11K_FLAG_MULTI_MSI_VECTORS, &ab_pci->ab->dev_flags)) return 0; - return irq_set_affinity_hint(ab_pci->pdev->irq, m); + return irq_set_affinity_and_hint(ab_pci->pdev->irq, m); } static int ath11k_pci_probe(struct pci_dev *pdev, @@ -743,7 +929,7 @@ static int ath11k_pci_probe(struct pci_dev *pdev, { struct ath11k_base *ab; struct ath11k_pci *ab_pci; - u32 soc_hw_version_major, soc_hw_version_minor, addr; + u32 soc_hw_version_major, soc_hw_version_minor; int ret; u32 sub_version; @@ -769,8 +955,7 @@ static int ath11k_pci_probe(struct pci_dev *pdev, * from DT. If memory is reserved from DT for FW, ath11k driver need not * allocate memory. */ - ret = of_property_read_u32(ab->dev->of_node, "memory-region", &addr); - if (!ret) + if (of_property_present(ab->dev->of_node, "memory-region")) set_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags); ret = ath11k_pci_claim(ab_pci, pdev); @@ -939,6 +1124,8 @@ unsupported_wcn6855_soc: return 0; err_free_irq: + /* __free_irq() expects the caller to have cleared the affinity hint */ + ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); ath11k_pcic_free_irq(ab); err_ce_free: @@ -973,17 +1160,21 @@ static void ath11k_pci_remove(struct pci_dev *pdev) ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); if (test_bit(ATH11K_FLAG_QMI_FAIL, &ab->dev_flags)) { - ath11k_pci_power_down(ab); + ath11k_pci_power_down(ab, false); ath11k_debugfs_soc_destroy(ab); ath11k_qmi_deinit_service(ab); + ath11k_core_pm_notifier_unregister(ab); goto qmi_fail; } set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->reset_work); + cancel_work_sync(&ab->dump_work); ath11k_core_deinit(ab); qmi_fail: + ath11k_fw_destroy(ab); ath11k_mhi_unregister(ab_pci); ath11k_pcic_free_irq(ab); @@ -1001,7 +1192,7 @@ static void ath11k_pci_shutdown(struct pci_dev *pdev) struct ath11k_pci *ab_pci = ath11k_pci_priv(ab); ath11k_pci_set_irq_affinity_hint(ab_pci, NULL); - ath11k_pci_power_down(ab); + ath11k_pci_power_down(ab, false); } static __maybe_unused int ath11k_pci_pm_suspend(struct device *dev) @@ -1038,9 +1229,39 @@ static __maybe_unused int ath11k_pci_pm_resume(struct device *dev) return ret; } -static SIMPLE_DEV_PM_OPS(ath11k_pci_pm_ops, - ath11k_pci_pm_suspend, - ath11k_pci_pm_resume); +static __maybe_unused int ath11k_pci_pm_suspend_late(struct device *dev) +{ + struct ath11k_base *ab = dev_get_drvdata(dev); + int ret; + + ret = ath11k_core_suspend_late(ab); + if (ret) + ath11k_warn(ab, "failed to late suspend core: %d\n", ret); + + /* Similar to ath11k_pci_pm_suspend(), we return success here + * even error happens, to allow system suspend/hibernation survive. + */ + return 0; +} + +static __maybe_unused int ath11k_pci_pm_resume_early(struct device *dev) +{ + struct ath11k_base *ab = dev_get_drvdata(dev); + int ret; + + ret = ath11k_core_resume_early(ab); + if (ret) + ath11k_warn(ab, "failed to early resume core: %d\n", ret); + + return ret; +} + +static const struct dev_pm_ops __maybe_unused ath11k_pci_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend, + ath11k_pci_pm_resume) + SET_LATE_SYSTEM_SLEEP_PM_OPS(ath11k_pci_pm_suspend_late, + ath11k_pci_pm_resume_early) +}; static struct pci_driver ath11k_pci_driver = { .name = "ath11k_pci", diff --git a/drivers/net/wireless/ath/ath11k/qmi.c b/drivers/net/wireless/ath/ath11k/qmi.c index 5759fc521316..2782f4723e41 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.c +++ b/drivers/net/wireless/ath/ath11k/qmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/elf.h> @@ -1957,13 +1957,15 @@ static void ath11k_qmi_free_target_mem_chunk(struct ath11k_base *ab) int i; for (i = 0; i < ab->qmi.mem_seg_count; i++) { - if ((ab->hw_params.fixed_mem_region || - test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) && - ab->qmi.target_mem[i].iaddr) - iounmap(ab->qmi.target_mem[i].iaddr); + if (!ab->qmi.target_mem[i].anyaddr) + continue; - if (!ab->qmi.target_mem[i].vaddr) + if (ab->hw_params.fixed_mem_region || + test_bit(ATH11K_FLAG_FIXED_MEM_RGN, &ab->dev_flags)) { + iounmap(ab->qmi.target_mem[i].iaddr); + ab->qmi.target_mem[i].iaddr = NULL; continue; + } dma_free_coherent(ab->dev, ab->qmi.target_mem[i].prev_size, @@ -1991,6 +1993,15 @@ static int ath11k_qmi_alloc_target_mem_chunk(struct ath11k_base *ab) chunk->prev_size == chunk->size) continue; + if (ab->qmi.mem_seg_count <= ATH11K_QMI_FW_MEM_REQ_SEGMENT_CNT) { + ath11k_dbg(ab, ATH11K_DBG_QMI, + "size/type mismatch (current %d %u) (prev %d %u), try later with small size\n", + chunk->size, chunk->type, + chunk->prev_size, chunk->prev_type); + ab->qmi.target_mem_delayed = true; + return 0; + } + /* cannot reuse the existing chunk */ dma_free_coherent(ab->dev, chunk->prev_size, chunk->vaddr, chunk->paddr); @@ -2070,7 +2081,7 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) break; case BDF_MEM_REGION_TYPE: ab->qmi.target_mem[idx].paddr = ab->hw_params.bdf_addr; - ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].iaddr = NULL; ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; idx++; @@ -2093,10 +2104,11 @@ static int ath11k_qmi_assign_target_mem_chunk(struct ath11k_base *ab) } else { ab->qmi.target_mem[idx].paddr = ATH11K_QMI_CALDB_ADDRESS; + ab->qmi.target_mem[idx].iaddr = NULL; } } else { ab->qmi.target_mem[idx].paddr = 0; - ab->qmi.target_mem[idx].vaddr = NULL; + ab->qmi.target_mem[idx].iaddr = NULL; } ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; @@ -2884,7 +2896,7 @@ int ath11k_qmi_fwreset_from_cold_boot(struct ath11k_base *ab) } /* reset the firmware */ - ath11k_hif_power_down(ab); + ath11k_hif_power_down(ab, false); ath11k_hif_power_up(ab); ath11k_dbg(ab, ATH11K_DBG_QMI, "exit wait for cold boot done\n"); return 0; diff --git a/drivers/net/wireless/ath/ath11k/qmi.h b/drivers/net/wireless/ath/ath11k/qmi.h index 7e06d100af57..7968ab122b65 100644 --- a/drivers/net/wireless/ath/ath11k/qmi.h +++ b/drivers/net/wireless/ath/ath11k/qmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_QMI_H @@ -102,8 +102,11 @@ struct target_mem_chunk { u32 prev_size; u32 prev_type; dma_addr_t paddr; - u32 *vaddr; - void __iomem *iaddr; + union { + u32 *vaddr; + void __iomem *iaddr; + void *anyaddr; + }; }; struct target_info { @@ -154,6 +157,7 @@ struct ath11k_qmi { #define BDF_MEM_REGION_TYPE 0x2 #define M3_DUMP_REGION_TYPE 0x3 #define CALDB_MEM_REGION_TYPE 0x4 +#define PAGEABLE_MEM_REGION_TYPE 0x9 struct qmi_wlanfw_host_cap_req_msg_v01 { u8 num_clients_valid; diff --git a/drivers/net/wireless/ath/ath11k/reg.c b/drivers/net/wireless/ath/ath11k/reg.c index b0f289784dd3..d62a2014315a 100644 --- a/drivers/net/wireless/ath/ath11k/reg.c +++ b/drivers/net/wireless/ath/ath11k/reg.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/rtnetlink.h> @@ -55,6 +55,19 @@ ath11k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) ath11k_dbg(ar->ab, ATH11K_DBG_REG, "Regulatory Notification received for %s\n", wiphy_name(wiphy)); + if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "driver initiated regd update\n"); + if (ar->state != ATH11K_STATE_ON) + return; + + ret = ath11k_reg_update_chan_list(ar, true); + if (ret) + ath11k_warn(ar->ab, "failed to update channel list: %d\n", ret); + + return; + } + /* Currently supporting only General User Hints. Cell base user * hints to be handled later. * Hints from other sources like Core, Beacons are not expected for @@ -111,32 +124,7 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) struct channel_param *ch; enum nl80211_band band; int num_channels = 0; - int i, ret, left; - - if (wait && ar->state_11d != ATH11K_11D_IDLE) { - left = wait_for_completion_timeout(&ar->completed_11d_scan, - ATH11K_SCAN_TIMEOUT_HZ); - if (!left) { - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "failed to receive 11d scan complete: timed out\n"); - ar->state_11d = ATH11K_11D_IDLE; - } - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "11d scan wait left time %d\n", left); - } - - if (wait && - (ar->scan.state == ATH11K_SCAN_STARTING || - ar->scan.state == ATH11K_SCAN_RUNNING)) { - left = wait_for_completion_timeout(&ar->scan.completed, - ATH11K_SCAN_TIMEOUT_HZ); - if (!left) - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "failed to receive hw scan complete: timed out\n"); - - ath11k_dbg(ar->ab, ATH11K_DBG_REG, - "hw scan wait left time %d\n", left); - } + int i, ret = 0; if (ar->state == ATH11K_STATE_RESTARTING) return 0; @@ -218,6 +206,16 @@ int ath11k_reg_update_chan_list(struct ath11k *ar, bool wait) } } + if (wait) { + spin_lock_bh(&ar->data_lock); + list_add_tail(¶ms->list, &ar->channel_update_queue); + spin_unlock_bh(&ar->data_lock); + + queue_work(ar->ab->workqueue, &ar->channel_update_work); + + return 0; + } + ret = ath11k_wmi_send_scan_chan_list_cmd(ar, params); kfree(params); @@ -293,12 +291,6 @@ int ath11k_regd_update(struct ath11k *ar) if (ret) goto err; - if (ar->state == ATH11K_STATE_ON) { - ret = ath11k_reg_update_chan_list(ar, true); - if (ret) - goto err; - } - return 0; err: ath11k_warn(ab, "failed to perform regd update : %d\n", ret); @@ -804,6 +796,54 @@ ret: return new_regd; } +void ath11k_regd_update_chan_list_work(struct work_struct *work) +{ + struct ath11k *ar = container_of(work, struct ath11k, + channel_update_work); + struct scan_chan_list_params *params; + struct list_head local_update_list; + int left; + + INIT_LIST_HEAD(&local_update_list); + + spin_lock_bh(&ar->data_lock); + list_splice_tail_init(&ar->channel_update_queue, &local_update_list); + spin_unlock_bh(&ar->data_lock); + + while ((params = list_first_entry_or_null(&local_update_list, + struct scan_chan_list_params, + list))) { + if (ar->state_11d != ATH11K_11D_IDLE) { + left = wait_for_completion_timeout(&ar->completed_11d_scan, + ATH11K_SCAN_TIMEOUT_HZ); + if (!left) { + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "failed to receive 11d scan complete: timed out\n"); + ar->state_11d = ATH11K_11D_IDLE; + } + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "reg 11d scan wait left time %d\n", left); + } + + if ((ar->scan.state == ATH11K_SCAN_STARTING || + ar->scan.state == ATH11K_SCAN_RUNNING)) { + left = wait_for_completion_timeout(&ar->scan.completed, + ATH11K_SCAN_TIMEOUT_HZ); + if (!left) + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "failed to receive hw scan complete: timed out\n"); + + ath11k_dbg(ar->ab, ATH11K_DBG_REG, + "reg hw scan wait left time %d\n", left); + } + + ath11k_wmi_send_scan_chan_list_cmd(ar, params); + list_del(¶ms->list); + kfree(params); + } +} + static bool ath11k_reg_is_world_alpha(char *alpha) { if (alpha[0] == '0' && alpha[1] == '0') @@ -977,6 +1017,7 @@ void ath11k_regd_update_work(struct work_struct *work) void ath11k_reg_init(struct ath11k *ar) { ar->hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; + ar->hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER; ar->hw->wiphy->reg_notifier = ath11k_reg_notifier; } diff --git a/drivers/net/wireless/ath/ath11k/reg.h b/drivers/net/wireless/ath/ath11k/reg.h index 263ea9061948..72b483594015 100644 --- a/drivers/net/wireless/ath/ath11k/reg.h +++ b/drivers/net/wireless/ath/ath11k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_REG_H @@ -33,6 +33,7 @@ void ath11k_reg_init(struct ath11k *ar); void ath11k_reg_reset_info(struct cur_regulatory_info *reg_info); void ath11k_reg_free(struct ath11k_base *ab); void ath11k_regd_update_work(struct work_struct *work); +void ath11k_regd_update_chan_list_work(struct work_struct *work); struct ieee80211_regdomain * ath11k_reg_build_regd(struct ath11k_base *ab, struct cur_regulatory_info *reg_info, bool intersect, diff --git a/drivers/net/wireless/ath/ath11k/testmode.c b/drivers/net/wireless/ath/ath11k/testmode.c index 302d66092b97..a9751ea2a0b7 100644 --- a/drivers/net/wireless/ath/ath11k/testmode.c +++ b/drivers/net/wireless/ath/ath11k/testmode.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "testmode.h" @@ -10,18 +10,18 @@ #include "wmi.h" #include "hw.h" #include "core.h" -#include "testmode_i.h" +#include "../testmode_i.h" #define ATH11K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0) #define ATH11K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4) -static const struct nla_policy ath11k_tm_policy[ATH11K_TM_ATTR_MAX + 1] = { - [ATH11K_TM_ATTR_CMD] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_DATA] = { .type = NLA_BINARY, - .len = ATH11K_TM_DATA_MAX_LEN }, - [ATH11K_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, - [ATH11K_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, +static const struct nla_policy ath11k_tm_policy[ATH_TM_ATTR_MAX + 1] = { + [ATH_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH_TM_DATA_MAX_LEN }, + [ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, }; static struct ath11k *ath11k_tm_get_ar(struct ath11k_base *ab) @@ -73,9 +73,9 @@ static void ath11k_tm_wmi_event_unsegmented(struct ath11k_base *ab, u32 cmd_id, goto out; } - if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, ATH11K_TM_CMD_WMI) || - nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) || - nla_put(nl_skb, ATH11K_TM_ATTR_DATA, skb->len, skb->data)) { + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) { ath11k_warn(ab, "failed to populate testmode unsegmented event\n"); kfree_skb(nl_skb); goto out; @@ -107,7 +107,7 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, u32 pdev_id; ath11k_dbg(ab, ATH11K_DBG_TESTMODE, - "event wmi cmd_id %d ftm event msg %pK datalen %d\n", + "event wmi cmd_id %d ftm event msg %p datalen %d\n", cmd_id, ftm_msg, length); ath11k_dbg_dump(ab, ATH11K_DBG_TESTMODE, NULL, "", ftm_msg, length); pdev_id = DP_HW2SW_MACID(ftm_msg->seg_hdr.pdev_id); @@ -140,7 +140,7 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, data_pos = ab->testmode.data_pos; - if ((data_pos + datalen) > ATH11K_FTM_EVENT_MAX_BUF_LENGTH) { + if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) { ath11k_warn(ab, "Invalid ftm event length at %d: %d\n", data_pos, datalen); ret = -EINVAL; @@ -172,10 +172,10 @@ static int ath11k_tm_process_event(struct ath11k_base *ab, u32 cmd_id, goto out; } - if (nla_put_u32(nl_skb, ATH11K_TM_ATTR_CMD, - ATH11K_TM_CMD_WMI_FTM) || - nla_put_u32(nl_skb, ATH11K_TM_ATTR_WMI_CMDID, cmd_id) || - nla_put(nl_skb, ATH11K_TM_ATTR_DATA, data_pos, + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, + ATH_TM_CMD_WMI_FTM) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos, &ab->testmode.eventdata[0])) { ath11k_warn(ab, "failed to populate segmented testmode event"); kfree_skb(nl_skb); @@ -235,23 +235,23 @@ static int ath11k_tm_cmd_get_version(struct ath11k *ar, struct nlattr *tb[]) ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, "cmd get version_major %d version_minor %d\n", - ATH11K_TESTMODE_VERSION_MAJOR, - ATH11K_TESTMODE_VERSION_MINOR); + ATH_TESTMODE_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MINOR); skb = cfg80211_testmode_alloc_reply_skb(ar->hw->wiphy, nla_total_size(sizeof(u32))); if (!skb) return -ENOMEM; - ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MAJOR, - ATH11K_TESTMODE_VERSION_MAJOR); + ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MAJOR); if (ret) { kfree_skb(skb); return ret; } - ret = nla_put_u32(skb, ATH11K_TM_ATTR_VERSION_MINOR, - ATH11K_TESTMODE_VERSION_MINOR); + ret = nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR, + ATH_TESTMODE_VERSION_MINOR); if (ret) { kfree_skb(skb); return ret; @@ -277,7 +277,7 @@ static int ath11k_tm_cmd_testmode_start(struct ath11k *ar, struct nlattr *tb[]) goto err; } - ar->ab->testmode.eventdata = kzalloc(ATH11K_FTM_EVENT_MAX_BUF_LENGTH, + ar->ab->testmode.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, GFP_KERNEL); if (!ar->ab->testmode.eventdata) { ret = -ENOMEM; @@ -310,25 +310,25 @@ static int ath11k_tm_cmd_wmi(struct ath11k *ar, struct nlattr *tb[], mutex_lock(&ar->conf_mutex); - if (!tb[ATH11K_TM_ATTR_DATA]) { + if (!tb[ATH_TM_ATTR_DATA]) { ret = -EINVAL; goto out; } - if (!tb[ATH11K_TM_ATTR_WMI_CMDID]) { + if (!tb[ATH_TM_ATTR_WMI_CMDID]) { ret = -EINVAL; goto out; } - buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); - buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); if (!buf_len) { ath11k_warn(ar->ab, "No data present in testmode wmi command\n"); ret = -EINVAL; goto out; } - cmd_id = nla_get_u32(tb[ATH11K_TM_ATTR_WMI_CMDID]); + cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]); /* Make sure that the buffer length is long enough to * hold TLV and pdev/vdev id. @@ -409,13 +409,13 @@ static int ath11k_tm_cmd_wmi_ftm(struct ath11k *ar, struct nlattr *tb[]) goto out; } - if (!tb[ATH11K_TM_ATTR_DATA]) { + if (!tb[ATH_TM_ATTR_DATA]) { ret = -EINVAL; goto out; } - buf = nla_data(tb[ATH11K_TM_ATTR_DATA]); - buf_len = nla_len(tb[ATH11K_TM_ATTR_DATA]); + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); cmd_id = WMI_PDEV_UTF_CMDID; ath11k_dbg(ar->ab, ATH11K_DBG_TESTMODE, @@ -476,25 +476,25 @@ int ath11k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void *data, int len) { struct ath11k *ar = hw->priv; - struct nlattr *tb[ATH11K_TM_ATTR_MAX + 1]; + struct nlattr *tb[ATH_TM_ATTR_MAX + 1]; int ret; - ret = nla_parse(tb, ATH11K_TM_ATTR_MAX, data, len, ath11k_tm_policy, + ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath11k_tm_policy, NULL); if (ret) return ret; - if (!tb[ATH11K_TM_ATTR_CMD]) + if (!tb[ATH_TM_ATTR_CMD]) return -EINVAL; - switch (nla_get_u32(tb[ATH11K_TM_ATTR_CMD])) { - case ATH11K_TM_CMD_GET_VERSION: + switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) { + case ATH_TM_CMD_GET_VERSION: return ath11k_tm_cmd_get_version(ar, tb); - case ATH11K_TM_CMD_WMI: + case ATH_TM_CMD_WMI: return ath11k_tm_cmd_wmi(ar, tb, vif); - case ATH11K_TM_CMD_TESTMODE_START: + case ATH_TM_CMD_TESTMODE_START: return ath11k_tm_cmd_testmode_start(ar, tb); - case ATH11K_TM_CMD_WMI_FTM: + case ATH_TM_CMD_WMI_FTM: return ath11k_tm_cmd_wmi_ftm(ar, tb); default: return -EOPNOTSUPP; diff --git a/drivers/net/wireless/ath/ath11k/wmi.c b/drivers/net/wireless/ath/ath11k/wmi.c index 87abfa547529..d7f852bebf4a 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/skbuff.h> #include <linux/ctype.h> @@ -2662,7 +2662,8 @@ int ath11k_wmi_send_scan_chan_list_cmd(struct ath11k *ar, } int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, - struct wmi_wmm_params_all_arg *param) + struct wmi_wmm_params_all_arg *param, + enum wmi_wmm_params_type wmm_param_type) { struct ath11k_pdev_wmi *wmi = ar->wmi; struct wmi_vdev_set_wmm_params_cmd *cmd; @@ -2681,7 +2682,7 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); cmd->vdev_id = vdev_id; - cmd->wmm_param_type = 0; + cmd->wmm_param_type = wmm_param_type; for (ac = 0; ac < WME_NUM_AC; ac++) { switch (ac) { @@ -2714,8 +2715,8 @@ int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, wmm_param->no_ack = wmi_wmm_arg->no_ack; ath11k_dbg(ar->ab, ATH11K_DBG_WMI, - "wmm set ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n", - ac, wmm_param->aifs, wmm_param->cwmin, + "wmm set type %d ac %d aifs %d cwmin %d cwmax %d txop %d acm %d no_ack %d\n", + wmm_param_type, ac, wmm_param->aifs, wmm_param->cwmin, wmm_param->cwmax, wmm_param->txoplimit, wmm_param->acm, wmm_param->no_ack); } diff --git a/drivers/net/wireless/ath/ath11k/wmi.h b/drivers/net/wireless/ath/ath11k/wmi.h index 8982b909c821..9fcffaa2f383 100644 --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH11K_WMI_H @@ -3817,6 +3817,7 @@ struct wmi_stop_scan_cmd { }; struct scan_chan_list_params { + struct list_head list; u32 pdev_id; u16 nallchans; struct channel_param ch_param[]; @@ -6346,6 +6347,11 @@ enum wmi_sta_keepalive_method { #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 +enum wmi_wmm_params_type { + WMI_WMM_PARAM_TYPE_LEGACY = 0, + WMI_WMM_PARAM_TYPE_11AX_MU_EDCA = 1, +}; + const void **ath11k_wmi_tlv_parse_alloc(struct ath11k_base *ab, struct sk_buff *skb, gfp_t gfp); int ath11k_wmi_cmd_send(struct ath11k_pdev_wmi *wmi, struct sk_buff *skb, @@ -6402,7 +6408,8 @@ int ath11k_wmi_send_scan_start_cmd(struct ath11k *ar, int ath11k_wmi_send_scan_stop_cmd(struct ath11k *ar, struct scan_cancel_param *param); int ath11k_wmi_send_wmm_update_cmd_tlv(struct ath11k *ar, u32 vdev_id, - struct wmi_wmm_params_all_arg *param); + struct wmi_wmm_params_all_arg *param, + enum wmi_wmm_params_type wmm_param_type); int ath11k_wmi_pdev_suspend(struct ath11k *ar, u32 suspend_opt, u32 pdev_id); int ath11k_wmi_pdev_resume(struct ath11k *ar, u32 pdev_id); diff --git a/drivers/net/wireless/ath/ath12k/Kconfig b/drivers/net/wireless/ath/ath12k/Kconfig index 52a1bb19e3da..b3b15e1eb282 100644 --- a/drivers/net/wireless/ath/ath12k/Kconfig +++ b/drivers/net/wireless/ath/ath12k/Kconfig @@ -15,6 +15,14 @@ config ATH12K If you choose to build a module, it'll be called ath12k. +config ATH12K_AHB + bool "QTI ath12k AHB support" + depends on ATH12K && REMOTEPROC + select QCOM_MDT_LOADER + select QCOM_SCM + help + Enable support for Ath12k AHB bus chipsets, example IPQ5332. + config ATH12K_DEBUG bool "ath12k debugging" depends on ATH12K diff --git a/drivers/net/wireless/ath/ath12k/Makefile b/drivers/net/wireless/ath/ath12k/Makefile index b5bb3e2599cd..d95ee525a6cd 100644 --- a/drivers/net/wireless/ath/ath12k/Makefile +++ b/drivers/net/wireless/ath/ath12k/Makefile @@ -23,11 +23,13 @@ ath12k-y += core.o \ fw.o \ p2p.o -ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o +ath12k-$(CONFIG_ATH12K_AHB) += ahb.o +ath12k-$(CONFIG_ATH12K_DEBUGFS) += debugfs.o debugfs_htt_stats.o debugfs_sta.o ath12k-$(CONFIG_ACPI) += acpi.o ath12k-$(CONFIG_ATH12K_TRACING) += trace.o ath12k-$(CONFIG_PM) += wow.o ath12k-$(CONFIG_ATH12K_COREDUMP) += coredump.o +ath12k-$(CONFIG_NL80211_TESTMODE) += testmode.o # for tracing framework to find trace.h CFLAGS_trace.o := -I$(src) diff --git a/drivers/net/wireless/ath/ath12k/acpi.c b/drivers/net/wireless/ath/ath12k/acpi.c index 0555d35aab47..d81367ce6929 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.c +++ b/drivers/net/wireless/ath/ath12k/acpi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -12,7 +12,7 @@ static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func) { union acpi_object *obj; acpi_handle root_handle; - int ret; + int ret, i; root_handle = ACPI_HANDLE(ab->dev); if (!root_handle) { @@ -29,9 +29,48 @@ static int ath12k_acpi_dsm_get_data(struct ath12k_base *ab, int func) } if (obj->type == ACPI_TYPE_INTEGER) { - ab->acpi.func_bit = obj->integer.value; + switch (func) { + case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS: + ab->acpi.func_bit = obj->integer.value; + break; + case ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG: + ab->acpi.bit_flag = obj->integer.value; + break; + } + } else if (obj->type == ACPI_TYPE_STRING) { + switch (func) { + case ATH12K_ACPI_DSM_FUNC_BDF_EXT: + if (obj->string.length <= ATH12K_ACPI_BDF_ANCHOR_STRING_LEN || + obj->string.length > ATH12K_ACPI_BDF_MAX_LEN || + memcmp(obj->string.pointer, ATH12K_ACPI_BDF_ANCHOR_STRING, + ATH12K_ACPI_BDF_ANCHOR_STRING_LEN)) { + ath12k_warn(ab, "invalid ACPI DSM BDF size: %d\n", + obj->string.length); + ret = -EINVAL; + goto out; + } + + memcpy(ab->acpi.bdf_string, obj->string.pointer, + obj->buffer.length); + + break; + } } else if (obj->type == ACPI_TYPE_BUFFER) { switch (func) { + case ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS: + if (obj->buffer.length < ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE || + obj->buffer.length > ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE) { + ath12k_warn(ab, "invalid ACPI DSM func size: %d\n", + obj->buffer.length); + ret = -EINVAL; + goto out; + } + + ab->acpi.func_bit = 0; + for (i = 0; i < obj->buffer.length; i++) + ab->acpi.func_bit += obj->buffer.pointer[i] << (i * 8); + + break; case ATH12K_ACPI_DSM_FUNC_TAS_CFG: if (obj->buffer.length != ATH12K_ACPI_DSM_TAS_CFG_SIZE) { ath12k_warn(ab, "invalid ACPI DSM TAS config size: %d\n", @@ -247,24 +286,118 @@ static int ath12k_acpi_set_tas_params(struct ath12k_base *ab) return 0; } +bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab) +{ + return ab->acpi.acpi_disable_rfkill; +} + +bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab) +{ + return ab->acpi.acpi_disable_11be; +} + +void ath12k_acpi_set_dsm_func(struct ath12k_base *ab) +{ + int ret; + u8 *buf; + + if (!ab->hw_params->acpi_guid) + /* not supported with this hardware */ + return; + + if (ab->acpi.acpi_tas_enable) { + ret = ath12k_acpi_set_tas_params(ab); + if (ret) { + ath12k_warn(ab, "failed to send ACPI TAS parameters: %d\n", ret); + return; + } + } + + if (ab->acpi.acpi_bios_sar_enable) { + ret = ath12k_acpi_set_bios_sar_params(ab); + if (ret) { + ath12k_warn(ab, "failed to send ACPI BIOS SAR: %d\n", ret); + return; + } + } + + if (ab->acpi.acpi_cca_enable) { + buf = ab->acpi.cca_data + ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET; + ret = ath12k_wmi_set_bios_cmd(ab, + WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE, + buf, + ATH12K_ACPI_CCA_THR_OFFSET_LEN); + if (ret) { + ath12k_warn(ab, "failed to set ACPI DSM CCA threshold: %d\n", + ret); + return; + } + } + + if (ab->acpi.acpi_band_edge_enable) { + ret = ath12k_wmi_set_bios_cmd(ab, + WMI_BIOS_PARAM_TYPE_BANDEDGE, + ab->acpi.band_edge_power, + sizeof(ab->acpi.band_edge_power)); + if (ret) { + ath12k_warn(ab, + "failed to set ACPI DSM band edge channel power: %d\n", + ret); + return; + } + } +} + int ath12k_acpi_start(struct ath12k_base *ab) { acpi_status status; - u8 *buf; int ret; + ab->acpi.acpi_tas_enable = false; + ab->acpi.acpi_disable_11be = false; + ab->acpi.acpi_disable_rfkill = false; + ab->acpi.acpi_bios_sar_enable = false; + ab->acpi.acpi_cca_enable = false; + ab->acpi.acpi_band_edge_enable = false; + ab->acpi.acpi_enable_bdf = false; + ab->acpi.bdf_string[0] = '\0'; + if (!ab->hw_params->acpi_guid) /* not supported with this hardware */ return 0; - ab->acpi.acpi_tas_enable = false; - ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS); if (ret) { ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to get ACPI DSM data: %d\n", ret); return ret; } + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG); + if (ret) { + ath12k_warn(ab, "failed to get ACPI DISABLE FLAG: %d\n", ret); + return ret; + } + + if (ATH12K_ACPI_CHEK_BIT_VALID(ab->acpi, + ATH12K_ACPI_DSM_DISABLE_11BE_BIT)) + ab->acpi.acpi_disable_11be = true; + + if (!ATH12K_ACPI_CHEK_BIT_VALID(ab->acpi, + ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT)) + ab->acpi.acpi_disable_rfkill = true; + } + + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_BDF_EXT)) { + ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_BDF_EXT); + if (ret || ab->acpi.bdf_string[0] == '\0') { + ath12k_warn(ab, "failed to get ACPI BDF EXT: %d\n", ret); + return ret; + } + + ab->acpi.acpi_enable_bdf = true; + } + if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_TAS_CFG)) { ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_TAS_CFG); if (ret) { @@ -308,20 +441,6 @@ int ath12k_acpi_start(struct ath12k_base *ab) ab->acpi.acpi_bios_sar_enable = true; } - if (ab->acpi.acpi_tas_enable) { - ret = ath12k_acpi_set_tas_params(ab); - if (ret) { - ath12k_warn(ab, "failed to send ACPI parameters: %d\n", ret); - return ret; - } - } - - if (ab->acpi.acpi_bios_sar_enable) { - ret = ath12k_acpi_set_bios_sar_params(ab); - if (ret) - return ret; - } - if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, ATH12K_ACPI_FUNC_BIT_CCA)) { ret = ath12k_acpi_dsm_get_data(ab, ATH12K_ACPI_DSM_FUNC_INDEX_CCA); if (ret) { @@ -332,18 +451,8 @@ int ath12k_acpi_start(struct ath12k_base *ab) if (ab->acpi.cca_data[0] == ATH12K_ACPI_CCA_THR_VERSION && ab->acpi.cca_data[ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET] == - ATH12K_ACPI_CCA_THR_ENABLE_FLAG) { - buf = ab->acpi.cca_data + ATH12K_ACPI_CCA_THR_OFFSET_DATA_OFFSET; - ret = ath12k_wmi_set_bios_cmd(ab, - WMI_BIOS_PARAM_CCA_THRESHOLD_TYPE, - buf, - ATH12K_ACPI_CCA_THR_OFFSET_LEN); - if (ret) { - ath12k_warn(ab, "failed to set ACPI DSM CCA threshold: %d\n", - ret); - return ret; - } - } + ATH12K_ACPI_CCA_THR_ENABLE_FLAG) + ab->acpi.acpi_cca_enable = true; } if (ATH12K_ACPI_FUNC_BIT_VALID(ab->acpi, @@ -356,18 +465,8 @@ int ath12k_acpi_start(struct ath12k_base *ab) } if (ab->acpi.band_edge_power[0] == ATH12K_ACPI_BAND_EDGE_VERSION && - ab->acpi.band_edge_power[1] == ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG) { - ret = ath12k_wmi_set_bios_cmd(ab, - WMI_BIOS_PARAM_TYPE_BANDEDGE, - ab->acpi.band_edge_power, - sizeof(ab->acpi.band_edge_power)); - if (ret) { - ath12k_warn(ab, - "failed to set ACPI DSM band edge channel power: %d\n", - ret); - return ret; - } - } + ab->acpi.band_edge_power[1] == ATH12K_ACPI_BAND_EDGE_ENABLE_FLAG) + ab->acpi.acpi_band_edge_enable = true; } status = acpi_install_notify_handler(ACPI_HANDLE(ab->dev), @@ -383,6 +482,21 @@ int ath12k_acpi_start(struct ath12k_base *ab) return 0; } +int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab) +{ + size_t max_len = sizeof(ab->qmi.target.bdf_ext); + + if (!ab->acpi.acpi_enable_bdf) + return -ENODATA; + + if (strscpy(ab->qmi.target.bdf_ext, ab->acpi.bdf_string + 4, max_len) < 0) + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "acpi bdf variant longer than the buffer (variant: %s)\n", + ab->acpi.bdf_string); + + return 0; +} + void ath12k_acpi_stop(struct ath12k_base *ab) { if (!ab->acpi.started) diff --git a/drivers/net/wireless/ath/ath12k/acpi.h b/drivers/net/wireless/ath/ath12k/acpi.h index 39e003d86a48..3a26fea6af1a 100644 --- a/drivers/net/wireless/ath/ath12k/acpi.h +++ b/drivers/net/wireless/ath/ath12k/acpi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_ACPI_H #define ATH12K_ACPI_H @@ -9,6 +9,8 @@ #include <linux/acpi.h> #define ATH12K_ACPI_DSM_FUNC_SUPPORT_FUNCS 0 +#define ATH12K_ACPI_DSM_FUNC_DISABLE_FLAG 2 +#define ATH12K_ACPI_DSM_FUNC_BDF_EXT 3 #define ATH12K_ACPI_DSM_FUNC_BIOS_SAR 4 #define ATH12K_ACPI_DSM_FUNC_GEO_OFFSET 5 #define ATH12K_ACPI_DSM_FUNC_INDEX_CCA 6 @@ -16,6 +18,8 @@ #define ATH12K_ACPI_DSM_FUNC_TAS_DATA 9 #define ATH12K_ACPI_DSM_FUNC_INDEX_BAND_EDGE 10 +#define ATH12K_ACPI_FUNC_BIT_DISABLE_FLAG BIT(1) +#define ATH12K_ACPI_FUNC_BIT_BDF_EXT BIT(2) #define ATH12K_ACPI_FUNC_BIT_BIOS_SAR BIT(3) #define ATH12K_ACPI_FUNC_BIT_GEO_OFFSET BIT(4) #define ATH12K_ACPI_FUNC_BIT_CCA BIT(5) @@ -25,6 +29,7 @@ #define ATH12K_ACPI_NOTIFY_EVENT 0x86 #define ATH12K_ACPI_FUNC_BIT_VALID(_acdata, _func) (((_acdata).func_bit) & (_func)) +#define ATH12K_ACPI_CHEK_BIT_VALID(_acdata, _func) (((_acdata).bit_flag) & (_func)) #define ATH12K_ACPI_TAS_DATA_VERSION 0x1 #define ATH12K_ACPI_TAS_DATA_ENABLE 0x1 @@ -48,6 +53,16 @@ #define ATH12K_ACPI_DSM_BAND_EDGE_DATA_SIZE 100 #define ATH12K_ACPI_DSM_TAS_CFG_SIZE 108 +#define ATH12K_ACPI_DSM_FUNC_MIN_BITMAP_SIZE 1 +#define ATH12K_ACPI_DSM_FUNC_MAX_BITMAP_SIZE 4 + +#define ATH12K_ACPI_DSM_DISABLE_11BE_BIT BIT(0) +#define ATH12K_ACPI_DSM_DISABLE_RFKILL_BIT BIT(2) + +#define ATH12K_ACPI_BDF_ANCHOR_STRING_LEN 3 +#define ATH12K_ACPI_BDF_ANCHOR_STRING "BDF" +#define ATH12K_ACPI_BDF_MAX_LEN 100 + #define ATH12K_ACPI_DSM_GEO_OFFSET_DATA_SIZE (ATH12K_ACPI_GEO_OFFSET_DATA_OFFSET + \ ATH12K_ACPI_BIOS_SAR_GEO_OFFSET_LEN) #define ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE (ATH12K_ACPI_POWER_LIMIT_DATA_OFFSET + \ @@ -59,6 +74,10 @@ int ath12k_acpi_start(struct ath12k_base *ab); void ath12k_acpi_stop(struct ath12k_base *ab); +bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab); +bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab); +void ath12k_acpi_set_dsm_func(struct ath12k_base *ab); +int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab); #else @@ -71,6 +90,25 @@ static inline void ath12k_acpi_stop(struct ath12k_base *ab) { } +static inline bool ath12k_acpi_get_disable_rfkill(struct ath12k_base *ab) +{ + return false; +} + +static inline bool ath12k_acpi_get_disable_11be(struct ath12k_base *ab) +{ + return false; +} + +static inline void ath12k_acpi_set_dsm_func(struct ath12k_base *ab) +{ +} + +static inline int ath12k_acpi_check_bdf_variant_name(struct ath12k_base *ab) +{ + return 0; +} + #endif /* CONFIG_ACPI */ #endif /* ATH12K_ACPI_H */ diff --git a/drivers/net/wireless/ath/ath12k/ahb.c b/drivers/net/wireless/ath/ath12k/ahb.c new file mode 100644 index 000000000000..8d1a86e420a4 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/ahb.c @@ -0,0 +1,1155 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/dma-mapping.h> +#include <linux/firmware/qcom/qcom_scm.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/remoteproc.h> +#include <linux/soc/qcom/mdt_loader.h> +#include <linux/soc/qcom/smem_state.h> +#include "ahb.h" +#include "debug.h" +#include "hif.h" + +static const struct of_device_id ath12k_ahb_of_match[] = { + { .compatible = "qcom,ipq5332-wifi", + .data = (void *)ATH12K_HW_IPQ5332_HW10, + }, + { } +}; + +MODULE_DEVICE_TABLE(of, ath12k_ahb_of_match); + +#define ATH12K_IRQ_CE0_OFFSET 4 +#define ATH12K_MAX_UPDS 1 +#define ATH12K_UPD_IRQ_WRD_LEN 18 +static const char ath12k_userpd_irq[][9] = {"spawn", + "ready", + "stop-ack"}; + +static const char *irq_name[ATH12K_IRQ_NUM_MAX] = { + "misc-pulse1", + "misc-latch", + "sw-exception", + "watchdog", + "ce0", + "ce1", + "ce2", + "ce3", + "ce4", + "ce5", + "ce6", + "ce7", + "ce8", + "ce9", + "ce10", + "ce11", + "host2wbm-desc-feed", + "host2reo-re-injection", + "host2reo-command", + "host2rxdma-monitor-ring3", + "host2rxdma-monitor-ring2", + "host2rxdma-monitor-ring1", + "reo2ost-exception", + "wbm2host-rx-release", + "reo2host-status", + "reo2host-destination-ring4", + "reo2host-destination-ring3", + "reo2host-destination-ring2", + "reo2host-destination-ring1", + "rxdma2host-monitor-destination-mac3", + "rxdma2host-monitor-destination-mac2", + "rxdma2host-monitor-destination-mac1", + "ppdu-end-interrupts-mac3", + "ppdu-end-interrupts-mac2", + "ppdu-end-interrupts-mac1", + "rxdma2host-monitor-status-ring-mac3", + "rxdma2host-monitor-status-ring-mac2", + "rxdma2host-monitor-status-ring-mac1", + "host2rxdma-host-buf-ring-mac3", + "host2rxdma-host-buf-ring-mac2", + "host2rxdma-host-buf-ring-mac1", + "rxdma2host-destination-ring-mac3", + "rxdma2host-destination-ring-mac2", + "rxdma2host-destination-ring-mac1", + "host2tcl-input-ring4", + "host2tcl-input-ring3", + "host2tcl-input-ring2", + "host2tcl-input-ring1", + "wbm2host-tx-completions-ring4", + "wbm2host-tx-completions-ring3", + "wbm2host-tx-completions-ring2", + "wbm2host-tx-completions-ring1", + "tcl2host-status-ring", +}; + +enum ext_irq_num { + host2wbm_desc_feed = 16, + host2reo_re_injection, + host2reo_command, + host2rxdma_monitor_ring3, + host2rxdma_monitor_ring2, + host2rxdma_monitor_ring1, + reo2host_exception, + wbm2host_rx_release, + reo2host_status, + reo2host_destination_ring4, + reo2host_destination_ring3, + reo2host_destination_ring2, + reo2host_destination_ring1, + rxdma2host_monitor_destination_mac3, + rxdma2host_monitor_destination_mac2, + rxdma2host_monitor_destination_mac1, + ppdu_end_interrupts_mac3, + ppdu_end_interrupts_mac2, + ppdu_end_interrupts_mac1, + rxdma2host_monitor_status_ring_mac3, + rxdma2host_monitor_status_ring_mac2, + rxdma2host_monitor_status_ring_mac1, + host2rxdma_host_buf_ring_mac3, + host2rxdma_host_buf_ring_mac2, + host2rxdma_host_buf_ring_mac1, + rxdma2host_destination_ring_mac3, + rxdma2host_destination_ring_mac2, + rxdma2host_destination_ring_mac1, + host2tcl_input_ring4, + host2tcl_input_ring3, + host2tcl_input_ring2, + host2tcl_input_ring1, + wbm2host_tx_completions_ring4, + wbm2host_tx_completions_ring3, + wbm2host_tx_completions_ring2, + wbm2host_tx_completions_ring1, + tcl2host_status_ring, +}; + +static u32 ath12k_ahb_read32(struct ath12k_base *ab, u32 offset) +{ + if (ab->ce_remap && offset < HAL_SEQ_WCSS_CMEM_OFFSET) + return ioread32(ab->mem_ce + offset); + return ioread32(ab->mem + offset); +} + +static void ath12k_ahb_write32(struct ath12k_base *ab, u32 offset, + u32 value) +{ + if (ab->ce_remap && offset < HAL_SEQ_WCSS_CMEM_OFFSET) + iowrite32(value, ab->mem_ce + offset); + else + iowrite32(value, ab->mem + offset); +} + +static void ath12k_ahb_cancel_workqueue(struct ath12k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params->ce_count; i++) { + struct ath12k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + cancel_work_sync(&ce_pipe->intr_wq); + } +} + +static void ath12k_ahb_ext_grp_disable(struct ath12k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + disable_irq_nosync(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void __ath12k_ahb_ext_irq_disable(struct ath12k_base *ab) +{ + int i; + + for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + ath12k_ahb_ext_grp_disable(irq_grp); + if (irq_grp->napi_enabled) { + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + irq_grp->napi_enabled = false; + } + } +} + +static void ath12k_ahb_ext_grp_enable(struct ath12k_ext_irq_grp *irq_grp) +{ + int i; + + for (i = 0; i < irq_grp->num_irq; i++) + enable_irq(irq_grp->ab->irq_num[irq_grp->irqs[i]]); +} + +static void ath12k_ahb_setbit32(struct ath12k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath12k_ahb_read32(ab, offset); + ath12k_ahb_write32(ab, offset, val | BIT(bit)); +} + +static void ath12k_ahb_clearbit32(struct ath12k_base *ab, u8 bit, u32 offset) +{ + u32 val; + + val = ath12k_ahb_read32(ab, offset); + ath12k_ahb_write32(ab, offset, val & ~BIT(bit)); +} + +static void ath12k_ahb_ce_irq_enable(struct ath12k_base *ab, u16 ce_id) +{ + const struct ce_attr *ce_attr; + const struct ce_ie_addr *ce_ie_addr = ab->hw_params->ce_ie_addr; + u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; + + ie1_reg_addr = ce_ie_addr->ie1_reg_addr; + ie2_reg_addr = ce_ie_addr->ie2_reg_addr; + ie3_reg_addr = ce_ie_addr->ie3_reg_addr; + + ce_attr = &ab->hw_params->host_ce_config[ce_id]; + if (ce_attr->src_nentries) + ath12k_ahb_setbit32(ab, ce_id, ie1_reg_addr); + + if (ce_attr->dest_nentries) { + ath12k_ahb_setbit32(ab, ce_id, ie2_reg_addr); + ath12k_ahb_setbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + ie3_reg_addr); + } +} + +static void ath12k_ahb_ce_irq_disable(struct ath12k_base *ab, u16 ce_id) +{ + const struct ce_attr *ce_attr; + const struct ce_ie_addr *ce_ie_addr = ab->hw_params->ce_ie_addr; + u32 ie1_reg_addr, ie2_reg_addr, ie3_reg_addr; + + ie1_reg_addr = ce_ie_addr->ie1_reg_addr; + ie2_reg_addr = ce_ie_addr->ie2_reg_addr; + ie3_reg_addr = ce_ie_addr->ie3_reg_addr; + + ce_attr = &ab->hw_params->host_ce_config[ce_id]; + if (ce_attr->src_nentries) + ath12k_ahb_clearbit32(ab, ce_id, ie1_reg_addr); + + if (ce_attr->dest_nentries) { + ath12k_ahb_clearbit32(ab, ce_id, ie2_reg_addr); + ath12k_ahb_clearbit32(ab, ce_id + CE_HOST_IE_3_SHIFT, + ie3_reg_addr); + } +} + +static void ath12k_ahb_sync_ce_irqs(struct ath12k_base *ab) +{ + int i; + int irq_idx; + + for (i = 0; i < ab->hw_params->ce_count; i++) { + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH12K_IRQ_CE0_OFFSET + i; + synchronize_irq(ab->irq_num[irq_idx]); + } +} + +static void ath12k_ahb_sync_ext_irqs(struct ath12k_base *ab) +{ + int i, j; + int irq_idx; + + for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; + + for (j = 0; j < irq_grp->num_irq; j++) { + irq_idx = irq_grp->irqs[j]; + synchronize_irq(ab->irq_num[irq_idx]); + } + } +} + +static void ath12k_ahb_ce_irqs_enable(struct ath12k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params->ce_count; i++) { + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + ath12k_ahb_ce_irq_enable(ab, i); + } +} + +static void ath12k_ahb_ce_irqs_disable(struct ath12k_base *ab) +{ + int i; + + for (i = 0; i < ab->hw_params->ce_count; i++) { + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + ath12k_ahb_ce_irq_disable(ab, i); + } +} + +static int ath12k_ahb_start(struct ath12k_base *ab) +{ + ath12k_ahb_ce_irqs_enable(ab); + ath12k_ce_rx_post_buf(ab); + + return 0; +} + +static void ath12k_ahb_ext_irq_enable(struct ath12k_base *ab) +{ + struct ath12k_ext_irq_grp *irq_grp; + int i; + + for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + irq_grp = &ab->ext_irq_grp[i]; + if (!irq_grp->napi_enabled) { + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } + ath12k_ahb_ext_grp_enable(irq_grp); + } +} + +static void ath12k_ahb_ext_irq_disable(struct ath12k_base *ab) +{ + __ath12k_ahb_ext_irq_disable(ab); + ath12k_ahb_sync_ext_irqs(ab); +} + +static void ath12k_ahb_stop(struct ath12k_base *ab) +{ + if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) + ath12k_ahb_ce_irqs_disable(ab); + ath12k_ahb_sync_ce_irqs(ab); + ath12k_ahb_cancel_workqueue(ab); + timer_delete_sync(&ab->rx_replenish_retry); + ath12k_ce_cleanup_pipes(ab); +} + +static int ath12k_ahb_power_up(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + char fw_name[ATH12K_USERPD_FW_NAME_LEN]; + char fw2_name[ATH12K_USERPD_FW_NAME_LEN]; + struct device *dev = ab->dev; + const struct firmware *fw, *fw2; + struct reserved_mem *rmem = NULL; + unsigned long time_left; + phys_addr_t mem_phys; + void *mem_region; + size_t mem_size; + u32 pasid; + int ret; + + rmem = ath12k_core_get_reserved_mem(ab, 0); + if (!rmem) + return -ENODEV; + + mem_phys = rmem->base; + mem_size = rmem->size; + mem_region = devm_memremap(dev, mem_phys, mem_size, MEMREMAP_WC); + if (IS_ERR(mem_region)) { + ath12k_err(ab, "unable to map memory region: %pa+%pa\n", + &rmem->base, &rmem->size); + return PTR_ERR(mem_region); + } + + snprintf(fw_name, sizeof(fw_name), "%s/%s/%s%d%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, ATH12K_AHB_FW_PREFIX, ab_ahb->userpd_id, + ATH12K_AHB_FW_SUFFIX); + + ret = request_firmware(&fw, fw_name, dev); + if (ret < 0) { + ath12k_err(ab, "request_firmware failed\n"); + return ret; + } + + ath12k_dbg(ab, ATH12K_DBG_AHB, "Booting fw image %s, size %zd\n", fw_name, + fw->size); + + if (!fw->size) { + ath12k_err(ab, "Invalid firmware size\n"); + ret = -EINVAL; + goto err_fw; + } + + pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) | + ATH12K_AHB_UPD_SWID; + + /* Load FW image to a reserved memory location */ + ret = qcom_mdt_load(dev, fw, fw_name, pasid, mem_region, mem_phys, mem_size, + &mem_phys); + if (ret) { + ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); + goto err_fw; + } + + snprintf(fw2_name, sizeof(fw2_name), "%s/%s/%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, ATH12K_AHB_FW2); + + ret = request_firmware(&fw2, fw2_name, dev); + if (ret < 0) { + ath12k_err(ab, "request_firmware failed\n"); + goto err_fw; + } + + ath12k_dbg(ab, ATH12K_DBG_AHB, "Booting fw image %s, size %zd\n", fw2_name, + fw2->size); + + if (!fw2->size) { + ath12k_err(ab, "Invalid firmware size\n"); + ret = -EINVAL; + goto err_fw2; + } + + ret = qcom_mdt_load_no_init(dev, fw2, fw2_name, pasid, mem_region, mem_phys, + mem_size, &mem_phys); + if (ret) { + ath12k_err(ab, "Failed to load MDT segments: %d\n", ret); + goto err_fw2; + } + + /* Authenticate FW image using peripheral ID */ + ret = qcom_scm_pas_auth_and_reset(pasid); + if (ret) { + ath12k_err(ab, "failed to boot the remote processor %d\n", ret); + goto err_fw2; + } + + /* Instruct Q6 to spawn userPD thread */ + ret = qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit), + BIT(ab_ahb->spawn_bit)); + if (ret) { + ath12k_err(ab, "Failed to update spawn state %d\n", ret); + goto err_fw2; + } + + time_left = wait_for_completion_timeout(&ab_ahb->userpd_spawned, + ATH12K_USERPD_SPAWN_TIMEOUT); + if (!time_left) { + ath12k_err(ab, "UserPD spawn wait timed out\n"); + ret = -ETIMEDOUT; + goto err_fw2; + } + + time_left = wait_for_completion_timeout(&ab_ahb->userpd_ready, + ATH12K_USERPD_READY_TIMEOUT); + if (!time_left) { + ath12k_err(ab, "UserPD ready wait timed out\n"); + ret = -ETIMEDOUT; + goto err_fw2; + } + + qcom_smem_state_update_bits(ab_ahb->spawn_state, BIT(ab_ahb->spawn_bit), 0); + + ath12k_dbg(ab, ATH12K_DBG_AHB, "UserPD%d is now UP\n", ab_ahb->userpd_id); + +err_fw2: + release_firmware(fw2); +err_fw: + release_firmware(fw); + return ret; +} + +static void ath12k_ahb_power_down(struct ath12k_base *ab, bool is_suspend) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + unsigned long time_left; + u32 pasid; + int ret; + + qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), + BIT(ab_ahb->stop_bit)); + + time_left = wait_for_completion_timeout(&ab_ahb->userpd_stopped, + ATH12K_USERPD_STOP_TIMEOUT); + if (!time_left) { + ath12k_err(ab, "UserPD stop wait timed out\n"); + return; + } + + qcom_smem_state_update_bits(ab_ahb->stop_state, BIT(ab_ahb->stop_bit), 0); + + pasid = (u32_encode_bits(ab_ahb->userpd_id, ATH12K_USERPD_ID_MASK)) | + ATH12K_AHB_UPD_SWID; + /* Release the firmware */ + ret = qcom_scm_pas_shutdown(pasid); + if (ret) + ath12k_err(ab, "scm pas shutdown failed for userPD%d: %d\n", + ab_ahb->userpd_id, ret); +} + +static void ath12k_ahb_init_qmi_ce_config(struct ath12k_base *ab) +{ + struct ath12k_qmi_ce_cfg *cfg = &ab->qmi.ce_cfg; + + cfg->tgt_ce_len = ab->hw_params->target_ce_count; + cfg->tgt_ce = ab->hw_params->target_ce_config; + cfg->svc_to_ce_map_len = ab->hw_params->svc_to_ce_map_len; + cfg->svc_to_ce_map = ab->hw_params->svc_to_ce_map; + ab->qmi.service_ins_id = ab->hw_params->qmi_service_ins_id; +} + +static void ath12k_ahb_ce_workqueue(struct work_struct *work) +{ + struct ath12k_ce_pipe *ce_pipe = from_work(ce_pipe, work, intr_wq); + + ath12k_ce_per_engine_service(ce_pipe->ab, ce_pipe->pipe_num); + + ath12k_ahb_ce_irq_enable(ce_pipe->ab, ce_pipe->pipe_num); +} + +static irqreturn_t ath12k_ahb_ce_interrupt_handler(int irq, void *arg) +{ + struct ath12k_ce_pipe *ce_pipe = arg; + + /* last interrupt received for this CE */ + ce_pipe->timestamp = jiffies; + + ath12k_ahb_ce_irq_disable(ce_pipe->ab, ce_pipe->pipe_num); + + queue_work(system_bh_wq, &ce_pipe->intr_wq); + + return IRQ_HANDLED; +} + +static int ath12k_ahb_ext_grp_napi_poll(struct napi_struct *napi, int budget) +{ + struct ath12k_ext_irq_grp *irq_grp = container_of(napi, + struct ath12k_ext_irq_grp, + napi); + struct ath12k_base *ab = irq_grp->ab; + int work_done; + + work_done = ath12k_dp_service_srng(ab, irq_grp, budget); + if (work_done < budget) { + napi_complete_done(napi, work_done); + ath12k_ahb_ext_grp_enable(irq_grp); + } + + if (work_done > budget) + work_done = budget; + + return work_done; +} + +static irqreturn_t ath12k_ahb_ext_interrupt_handler(int irq, void *arg) +{ + struct ath12k_ext_irq_grp *irq_grp = arg; + + /* last interrupt received for this group */ + irq_grp->timestamp = jiffies; + + ath12k_ahb_ext_grp_disable(irq_grp); + + napi_schedule(&irq_grp->napi); + + return IRQ_HANDLED; +} + +static int ath12k_ahb_config_ext_irq(struct ath12k_base *ab) +{ + const struct ath12k_hw_ring_mask *ring_mask; + struct ath12k_ext_irq_grp *irq_grp; + const struct hal_ops *hal_ops; + int i, j, irq, irq_idx, ret; + u32 num_irq; + + ring_mask = ab->hw_params->ring_mask; + hal_ops = ab->hw_params->hal_ops; + for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { + irq_grp = &ab->ext_irq_grp[i]; + num_irq = 0; + + irq_grp->ab = ab; + irq_grp->grp_id = i; + + irq_grp->napi_ndev = alloc_netdev_dummy(0); + if (!irq_grp->napi_ndev) + return -ENOMEM; + + netif_napi_add(irq_grp->napi_ndev, &irq_grp->napi, + ath12k_ahb_ext_grp_napi_poll); + + for (j = 0; j < ATH12K_EXT_IRQ_NUM_MAX; j++) { + /* For TX ring, ensure that the ring mask and the + * tcl_to_wbm_rbm_map point to the same ring number. + */ + if (ring_mask->tx[i] & + BIT(hal_ops->tcl_to_wbm_rbm_map[j].wbm_ring_num)) { + irq_grp->irqs[num_irq++] = + wbm2host_tx_completions_ring1 - j; + } + + if (ring_mask->rx[i] & BIT(j)) { + irq_grp->irqs[num_irq++] = + reo2host_destination_ring1 - j; + } + + if (ring_mask->rx_err[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_exception; + + if (ring_mask->rx_wbm_rel[i] & BIT(j)) + irq_grp->irqs[num_irq++] = wbm2host_rx_release; + + if (ring_mask->reo_status[i] & BIT(j)) + irq_grp->irqs[num_irq++] = reo2host_status; + + if (ring_mask->rx_mon_dest[i] & BIT(j)) + irq_grp->irqs[num_irq++] = + rxdma2host_monitor_destination_mac1; + } + + irq_grp->num_irq = num_irq; + + for (j = 0; j < irq_grp->num_irq; j++) { + irq_idx = irq_grp->irqs[j]; + + irq = platform_get_irq_byname(ab->pdev, + irq_name[irq_idx]); + ab->irq_num[irq_idx] = irq; + irq_set_status_flags(irq, IRQ_NOAUTOEN | IRQ_DISABLE_UNLAZY); + ret = devm_request_irq(ab->dev, irq, + ath12k_ahb_ext_interrupt_handler, + IRQF_TRIGGER_RISING, + irq_name[irq_idx], irq_grp); + if (ret) + ath12k_warn(ab, "failed request_irq for %d\n", irq); + } + } + + return 0; +} + +static int ath12k_ahb_config_irq(struct ath12k_base *ab) +{ + int irq, irq_idx, i; + int ret; + + /* Configure CE irqs */ + for (i = 0; i < ab->hw_params->ce_count; i++) { + struct ath12k_ce_pipe *ce_pipe = &ab->ce.ce_pipe[i]; + + if (ath12k_ce_get_attr_flags(ab, i) & CE_ATTR_DIS_INTR) + continue; + + irq_idx = ATH12K_IRQ_CE0_OFFSET + i; + + INIT_WORK(&ce_pipe->intr_wq, ath12k_ahb_ce_workqueue); + irq = platform_get_irq_byname(ab->pdev, irq_name[irq_idx]); + ret = devm_request_irq(ab->dev, irq, ath12k_ahb_ce_interrupt_handler, + IRQF_TRIGGER_RISING, irq_name[irq_idx], + ce_pipe); + if (ret) + return ret; + + ab->irq_num[irq_idx] = irq; + } + + /* Configure external interrupts */ + ret = ath12k_ahb_config_ext_irq(ab); + + return ret; +} + +static int ath12k_ahb_map_service_to_pipe(struct ath12k_base *ab, u16 service_id, + u8 *ul_pipe, u8 *dl_pipe) +{ + const struct service_to_pipe *entry; + bool ul_set = false, dl_set = false; + u32 pipedir; + int i; + + for (i = 0; i < ab->hw_params->svc_to_ce_map_len; i++) { + entry = &ab->hw_params->svc_to_ce_map[i]; + + if (__le32_to_cpu(entry->service_id) != service_id) + continue; + + pipedir = __le32_to_cpu(entry->pipedir); + if (pipedir == PIPEDIR_IN || pipedir == PIPEDIR_INOUT) { + WARN_ON(dl_set); + *dl_pipe = __le32_to_cpu(entry->pipenum); + dl_set = true; + } + + if (pipedir == PIPEDIR_OUT || pipedir == PIPEDIR_INOUT) { + WARN_ON(ul_set); + *ul_pipe = __le32_to_cpu(entry->pipenum); + ul_set = true; + } + } + + if (WARN_ON(!ul_set || !dl_set)) + return -ENOENT; + + return 0; +} + +static const struct ath12k_hif_ops ath12k_ahb_hif_ops_ipq5332 = { + .start = ath12k_ahb_start, + .stop = ath12k_ahb_stop, + .read32 = ath12k_ahb_read32, + .write32 = ath12k_ahb_write32, + .irq_enable = ath12k_ahb_ext_irq_enable, + .irq_disable = ath12k_ahb_ext_irq_disable, + .map_service_to_pipe = ath12k_ahb_map_service_to_pipe, + .power_up = ath12k_ahb_power_up, + .power_down = ath12k_ahb_power_down, +}; + +static irqreturn_t ath12k_userpd_irq_handler(int irq, void *data) +{ + struct ath12k_base *ab = data; + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_SPAWN_IRQ]) { + complete(&ab_ahb->userpd_spawned); + } else if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_READY_IRQ]) { + complete(&ab_ahb->userpd_ready); + } else if (irq == ab_ahb->userpd_irq_num[ATH12K_USERPD_STOP_ACK_IRQ]) { + complete(&ab_ahb->userpd_stopped); + } else { + ath12k_err(ab, "Invalid userpd interrupt\n"); + return IRQ_NONE; + } + + return IRQ_HANDLED; +} + +static int ath12k_ahb_config_rproc_irq(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + int i, ret; + char *upd_irq_name; + + for (i = 0; i < ATH12K_USERPD_MAX_IRQ; i++) { + ab_ahb->userpd_irq_num[i] = platform_get_irq_byname(ab->pdev, + ath12k_userpd_irq[i]); + if (ab_ahb->userpd_irq_num[i] < 0) + return ab_ahb->userpd_irq_num[i]; + + upd_irq_name = devm_kzalloc(&ab->pdev->dev, ATH12K_UPD_IRQ_WRD_LEN, + GFP_KERNEL); + if (!upd_irq_name) + return -ENOMEM; + + scnprintf(upd_irq_name, ATH12K_UPD_IRQ_WRD_LEN, "UserPD%u-%s", + ab_ahb->userpd_id, ath12k_userpd_irq[i]); + ret = devm_request_threaded_irq(&ab->pdev->dev, ab_ahb->userpd_irq_num[i], + NULL, ath12k_userpd_irq_handler, + IRQF_TRIGGER_RISING | IRQF_ONESHOT, + upd_irq_name, ab); + if (ret) + return dev_err_probe(&ab->pdev->dev, ret, + "Request %s irq failed: %d\n", + ath12k_userpd_irq[i], ret); + } + + ab_ahb->spawn_state = devm_qcom_smem_state_get(&ab->pdev->dev, "spawn", + &ab_ahb->spawn_bit); + if (IS_ERR(ab_ahb->spawn_state)) + return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->spawn_state), + "Failed to acquire spawn state\n"); + + ab_ahb->stop_state = devm_qcom_smem_state_get(&ab->pdev->dev, "stop", + &ab_ahb->stop_bit); + if (IS_ERR(ab_ahb->stop_state)) + return dev_err_probe(&ab->pdev->dev, PTR_ERR(ab_ahb->stop_state), + "Failed to acquire stop state\n"); + + init_completion(&ab_ahb->userpd_spawned); + init_completion(&ab_ahb->userpd_ready); + init_completion(&ab_ahb->userpd_stopped); + return 0; +} + +static int ath12k_ahb_root_pd_state_notifier(struct notifier_block *nb, + const unsigned long event, void *data) +{ + struct ath12k_ahb *ab_ahb = container_of(nb, struct ath12k_ahb, root_pd_nb); + struct ath12k_base *ab = ab_ahb->ab; + + if (event == ATH12K_RPROC_AFTER_POWERUP) { + ath12k_dbg(ab, ATH12K_DBG_AHB, "Root PD is UP\n"); + complete(&ab_ahb->rootpd_ready); + } + + return 0; +} + +static int ath12k_ahb_register_rproc_notifier(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + ab_ahb->root_pd_nb.notifier_call = ath12k_ahb_root_pd_state_notifier; + init_completion(&ab_ahb->rootpd_ready); + + ab_ahb->root_pd_notifier = qcom_register_ssr_notifier(ab_ahb->tgt_rproc->name, + &ab_ahb->root_pd_nb); + if (IS_ERR(ab_ahb->root_pd_notifier)) + return PTR_ERR(ab_ahb->root_pd_notifier); + + return 0; +} + +static void ath12k_ahb_unregister_rproc_notifier(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + if (!ab_ahb->root_pd_notifier) { + ath12k_err(ab, "Rproc notifier not registered\n"); + return; + } + + qcom_unregister_ssr_notifier(ab_ahb->root_pd_notifier, + &ab_ahb->root_pd_nb); + ab_ahb->root_pd_notifier = NULL; +} + +static int ath12k_ahb_get_rproc(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct device *dev = ab->dev; + struct device_node *np; + struct rproc *prproc; + + np = of_parse_phandle(dev->of_node, "qcom,rproc", 0); + if (!np) { + ath12k_err(ab, "failed to get q6_rproc handle\n"); + return -ENOENT; + } + + prproc = rproc_get_by_phandle(np->phandle); + of_node_put(np); + if (!prproc) + return dev_err_probe(&ab->pdev->dev, -EPROBE_DEFER, + "failed to get rproc\n"); + + ab_ahb->tgt_rproc = prproc; + + return 0; +} + +static int ath12k_ahb_boot_root_pd(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + unsigned long time_left; + int ret; + + ret = rproc_boot(ab_ahb->tgt_rproc); + if (ret < 0) { + ath12k_err(ab, "RootPD boot failed\n"); + return ret; + } + + time_left = wait_for_completion_timeout(&ab_ahb->rootpd_ready, + ATH12K_ROOTPD_READY_TIMEOUT); + if (!time_left) { + ath12k_err(ab, "RootPD ready wait timed out\n"); + return -ETIMEDOUT; + } + + return 0; +} + +static int ath12k_ahb_configure_rproc(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + int ret; + + ret = ath12k_ahb_get_rproc(ab); + if (ret < 0) + return ret; + + ret = ath12k_ahb_register_rproc_notifier(ab); + if (ret < 0) { + ret = dev_err_probe(&ab->pdev->dev, ret, + "failed to register rproc notifier\n"); + goto err_put_rproc; + } + + if (ab_ahb->tgt_rproc->state != RPROC_RUNNING) { + ret = ath12k_ahb_boot_root_pd(ab); + if (ret < 0) { + ath12k_err(ab, "failed to boot the remote processor Q6\n"); + goto err_unreg_notifier; + } + } + + return ath12k_ahb_config_rproc_irq(ab); + +err_unreg_notifier: + ath12k_ahb_unregister_rproc_notifier(ab); + +err_put_rproc: + rproc_put(ab_ahb->tgt_rproc); + return ret; +} + +static void ath12k_ahb_deconfigure_rproc(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + ath12k_ahb_unregister_rproc_notifier(ab); + rproc_put(ab_ahb->tgt_rproc); +} + +static int ath12k_ahb_resource_init(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + struct platform_device *pdev = ab->pdev; + struct resource *mem_res; + int ret; + + ab->mem = devm_platform_get_and_ioremap_resource(pdev, 0, &mem_res); + if (IS_ERR(ab->mem)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(ab->mem), "ioremap error\n"); + goto out; + } + + ab->mem_len = resource_size(mem_res); + + if (ab->hw_params->ce_remap) { + const struct ce_remap *ce_remap = ab->hw_params->ce_remap; + /* CE register space is moved out of WCSS and the space is not + * contiguous, hence remapping the CE registers to a new space + * for accessing them. + */ + ab->mem_ce = ioremap(ce_remap->base, ce_remap->size); + if (!ab->mem_ce) { + dev_err(&pdev->dev, "ce ioremap error\n"); + ret = -ENOMEM; + goto err_mem_unmap; + } + ab->ce_remap = true; + ab->ce_remap_base_addr = HAL_IPQ5332_CE_WFSS_REG_BASE; + } + + ab_ahb->xo_clk = devm_clk_get(ab->dev, "xo"); + if (IS_ERR(ab_ahb->xo_clk)) { + ret = dev_err_probe(&pdev->dev, PTR_ERR(ab_ahb->xo_clk), + "failed to get xo clock\n"); + goto err_mem_ce_unmap; + } + + ret = clk_prepare_enable(ab_ahb->xo_clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable gcc_xo_clk: %d\n", ret); + goto err_clock_deinit; + } + + return 0; + +err_clock_deinit: + devm_clk_put(ab->dev, ab_ahb->xo_clk); + +err_mem_ce_unmap: + ab_ahb->xo_clk = NULL; + if (ab->hw_params->ce_remap) + iounmap(ab->mem_ce); + +err_mem_unmap: + ab->mem_ce = NULL; + devm_iounmap(ab->dev, ab->mem); + +out: + ab->mem = NULL; + return ret; +} + +static void ath12k_ahb_resource_deinit(struct ath12k_base *ab) +{ + struct ath12k_ahb *ab_ahb = ath12k_ab_to_ahb(ab); + + if (ab->mem) + devm_iounmap(ab->dev, ab->mem); + + if (ab->mem_ce) + iounmap(ab->mem_ce); + + ab->mem = NULL; + ab->mem_ce = NULL; + + clk_disable_unprepare(ab_ahb->xo_clk); + devm_clk_put(ab->dev, ab_ahb->xo_clk); + ab_ahb->xo_clk = NULL; +} + +static int ath12k_ahb_probe(struct platform_device *pdev) +{ + struct ath12k_base *ab; + const struct ath12k_hif_ops *hif_ops; + struct ath12k_ahb *ab_ahb; + enum ath12k_hw_rev hw_rev; + u32 addr, userpd_id; + int ret; + + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "Failed to set 32-bit coherent dma\n"); + return ret; + } + + ab = ath12k_core_alloc(&pdev->dev, sizeof(struct ath12k_ahb), + ATH12K_BUS_AHB); + if (!ab) + return -ENOMEM; + + hw_rev = (enum ath12k_hw_rev)(kernel_ulong_t)of_device_get_match_data(&pdev->dev); + switch (hw_rev) { + case ATH12K_HW_IPQ5332_HW10: + hif_ops = &ath12k_ahb_hif_ops_ipq5332; + userpd_id = ATH12K_IPQ5332_USERPD_ID; + break; + default: + ret = -EOPNOTSUPP; + goto err_core_free; + } + + ab->hif.ops = hif_ops; + ab->pdev = pdev; + ab->hw_rev = hw_rev; + platform_set_drvdata(pdev, ab); + ab_ahb = ath12k_ab_to_ahb(ab); + ab_ahb->ab = ab; + ab_ahb->userpd_id = userpd_id; + + /* Set fixed_mem_region to true for platforms that support fixed memory + * reservation from DT. If memory is reserved from DT for FW, ath12k driver + * need not to allocate memory. + */ + if (!of_property_read_u32(ab->dev->of_node, "memory-region", &addr)) + set_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags); + + ret = ath12k_core_pre_init(ab); + if (ret) + goto err_core_free; + + ret = ath12k_ahb_resource_init(ab); + if (ret) + goto err_core_free; + + ret = ath12k_hal_srng_init(ab); + if (ret) + goto err_resource_deinit; + + ret = ath12k_ce_alloc_pipes(ab); + if (ret) { + ath12k_err(ab, "failed to allocate ce pipes: %d\n", ret); + goto err_hal_srng_deinit; + } + + ath12k_ahb_init_qmi_ce_config(ab); + + ret = ath12k_ahb_configure_rproc(ab); + if (ret) + goto err_ce_free; + + ret = ath12k_ahb_config_irq(ab); + if (ret) { + ath12k_err(ab, "failed to configure irq: %d\n", ret); + goto err_rproc_deconfigure; + } + + ret = ath12k_core_init(ab); + if (ret) { + ath12k_err(ab, "failed to init core: %d\n", ret); + goto err_rproc_deconfigure; + } + + return 0; + +err_rproc_deconfigure: + ath12k_ahb_deconfigure_rproc(ab); + +err_ce_free: + ath12k_ce_free_pipes(ab); + +err_hal_srng_deinit: + ath12k_hal_srng_deinit(ab); + +err_resource_deinit: + ath12k_ahb_resource_deinit(ab); + +err_core_free: + ath12k_core_free(ab); + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static void ath12k_ahb_remove_prepare(struct ath12k_base *ab) +{ + unsigned long left; + + if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) { + left = wait_for_completion_timeout(&ab->driver_recovery, + ATH12K_AHB_RECOVERY_TIMEOUT); + if (!left) + ath12k_warn(ab, "failed to receive recovery response completion\n"); + } + + set_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags); + cancel_work_sync(&ab->restart_work); + cancel_work_sync(&ab->qmi.event_work); +} + +static void ath12k_ahb_free_resources(struct ath12k_base *ab) +{ + struct platform_device *pdev = ab->pdev; + + ath12k_hal_srng_deinit(ab); + ath12k_ce_free_pipes(ab); + ath12k_ahb_resource_deinit(ab); + ath12k_ahb_deconfigure_rproc(ab); + ath12k_core_free(ab); + platform_set_drvdata(pdev, NULL); +} + +static void ath12k_ahb_remove(struct platform_device *pdev) +{ + struct ath12k_base *ab = platform_get_drvdata(pdev); + + if (test_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags)) { + ath12k_ahb_power_down(ab, false); + goto qmi_fail; + } + + ath12k_ahb_remove_prepare(ab); + ath12k_core_hw_group_cleanup(ab->ag); +qmi_fail: + ath12k_core_deinit(ab); + ath12k_ahb_free_resources(ab); +} + +static struct platform_driver ath12k_ahb_driver = { + .driver = { + .name = "ath12k_ahb", + .of_match_table = ath12k_ahb_of_match, + }, + .probe = ath12k_ahb_probe, + .remove = ath12k_ahb_remove, +}; + +int ath12k_ahb_init(void) +{ + return platform_driver_register(&ath12k_ahb_driver); +} + +void ath12k_ahb_exit(void) +{ + platform_driver_unregister(&ath12k_ahb_driver); +} diff --git a/drivers/net/wireless/ath/ath12k/ahb.h b/drivers/net/wireless/ath/ath12k/ahb.h new file mode 100644 index 000000000000..d56244b20a6a --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/ahb.h @@ -0,0 +1,80 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2022-2025, Qualcomm Innovation Center, Inc. All rights reserved. + */ +#ifndef ATH12K_AHB_H +#define ATH12K_AHB_H + +#include <linux/clk.h> +#include <linux/remoteproc/qcom_rproc.h> +#include "core.h" + +#define ATH12K_AHB_RECOVERY_TIMEOUT (3 * HZ) + +#define ATH12K_AHB_SMP2P_SMEM_MSG GENMASK(15, 0) +#define ATH12K_AHB_SMP2P_SMEM_SEQ_NO GENMASK(31, 16) +#define ATH12K_AHB_SMP2P_SMEM_VALUE_MASK 0xFFFFFFFF +#define ATH12K_PCI_CE_WAKE_IRQ 2 +#define ATH12K_PCI_IRQ_CE0_OFFSET 3 +#define ATH12K_ROOTPD_READY_TIMEOUT (5 * HZ) +#define ATH12K_RPROC_AFTER_POWERUP QCOM_SSR_AFTER_POWERUP +#define ATH12K_AHB_FW_PREFIX "q6_fw" +#define ATH12K_AHB_FW_SUFFIX ".mdt" +#define ATH12K_AHB_FW2 "iu_fw.mdt" +#define ATH12K_AHB_UPD_SWID 0x12 +#define ATH12K_USERPD_SPAWN_TIMEOUT (5 * HZ) +#define ATH12K_USERPD_READY_TIMEOUT (10 * HZ) +#define ATH12K_USERPD_STOP_TIMEOUT (5 * HZ) +#define ATH12K_USERPD_ID_MASK GENMASK(9, 8) +#define ATH12K_USERPD_FW_NAME_LEN 35 + +enum ath12k_ahb_smp2p_msg_id { + ATH12K_AHB_POWER_SAVE_ENTER = 1, + ATH12K_AHB_POWER_SAVE_EXIT, +}; + +enum ath12k_ahb_userpd_irq { + ATH12K_USERPD_SPAWN_IRQ, + ATH12K_USERPD_READY_IRQ, + ATH12K_USERPD_STOP_ACK_IRQ, + ATH12K_USERPD_MAX_IRQ, +}; + +struct ath12k_base; + +struct ath12k_ahb { + struct ath12k_base *ab; + struct rproc *tgt_rproc; + struct clk *xo_clk; + struct completion rootpd_ready; + struct notifier_block root_pd_nb; + void *root_pd_notifier; + struct qcom_smem_state *spawn_state; + struct qcom_smem_state *stop_state; + struct completion userpd_spawned; + struct completion userpd_ready; + struct completion userpd_stopped; + u32 userpd_id; + u32 spawn_bit; + u32 stop_bit; + int userpd_irq_num[ATH12K_USERPD_MAX_IRQ]; +}; + +static inline struct ath12k_ahb *ath12k_ab_to_ahb(struct ath12k_base *ab) +{ + return (struct ath12k_ahb *)ab->drv_priv; +} + +#ifdef CONFIG_ATH12K_AHB +int ath12k_ahb_init(void); +void ath12k_ahb_exit(void); +#else +static inline int ath12k_ahb_init(void) +{ + return 0; +} + +static inline void ath12k_ahb_exit(void) {}; +#endif +#endif diff --git a/drivers/net/wireless/ath/ath12k/ce.c b/drivers/net/wireless/ath/ath12k/ce.c index be0d669d31fc..47821a3b060b 100644 --- a/drivers/net/wireless/ath/ath12k/ce.c +++ b/drivers/net/wireless/ath/ath12k/ce.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "dp_rx.h" @@ -219,6 +219,96 @@ const struct ce_attr ath12k_host_ce_config_wcn7850[] = { }; +const struct ce_attr ath12k_host_ce_config_ipq5332[] = { + /* CE0: host->target HTC control and raw streams */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 16, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + /* CE1: target->host HTT + HTC control */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath12k_htc_rx_completion_handler, + }, + /* CE2: target->host WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 128, + .recv_cb = ath12k_htc_rx_completion_handler, + }, + /* CE3: host->target WMI */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 32, + .src_sz_max = 2048, + .dest_nentries = 0, + }, + /* CE4: host->target HTT */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 2048, + .src_sz_max = 256, + .dest_nentries = 0, + }, + /* CE5: target -> host PKTLOG */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 512, + .recv_cb = ath12k_dp_htt_htc_t2h_msg_handler, + }, + /* CE6: Target autonomous HIF_memcpy */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + /* CE7: CV Prefetch */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + /* CE8: Target HIF memcpy (Generic HIF memcypy) */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + /* CE9: WMI logging/CFR/Spectral/Radar */ + { + .flags = CE_ATTR_FLAGS, + .src_nentries = 0, + .src_sz_max = 2048, + .dest_nentries = 128, + }, + /* CE10: Unused */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, + /* CE11: Unused */ + { + .flags = CE_ATTR_FLAGS | CE_ATTR_DIS_INTR, + .src_nentries = 0, + .src_sz_max = 0, + .dest_nentries = 0, + }, +}; + static int ath12k_ce_rx_buf_enqueue_pipe(struct ath12k_ce_pipe *pipe, struct sk_buff *skb, dma_addr_t paddr) { @@ -343,11 +433,10 @@ static int ath12k_ce_completed_recv_next(struct ath12k_ce_pipe *pipe, goto err; } + /* Make sure descriptor is read after the head pointer. */ + dma_rmb(); + *nbytes = ath12k_hal_ce_dst_status_get_length(desc); - if (*nbytes == 0) { - ret = -EIO; - goto err; - } *skb = pipe->dest_ring->skb[sw_index]; pipe->dest_ring->skb[sw_index] = NULL; @@ -380,8 +469,8 @@ static void ath12k_ce_recv_process_cb(struct ath12k_ce_pipe *pipe) dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(skb)->paddr, max_nbytes, DMA_FROM_DEVICE); - if (unlikely(max_nbytes < nbytes)) { - ath12k_warn(ab, "rxed more than expected (nbytes %d, max %d)", + if (unlikely(max_nbytes < nbytes || nbytes == 0)) { + ath12k_warn(ab, "unexpected rx length (nbytes %d, max %d)", nbytes, max_nbytes); dev_kfree_skb_any(skb); continue; diff --git a/drivers/net/wireless/ath/ath12k/ce.h b/drivers/net/wireless/ath/ath12k/ce.h index 1a14b9fb86b8..57f75899ee03 100644 --- a/drivers/net/wireless/ath/ath12k/ce.h +++ b/drivers/net/wireless/ath/ath12k/ce.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_CE_H @@ -39,8 +39,8 @@ #define PIPEDIR_INOUT_H2H 4 /* bidirectional, host to host */ /* CE address/mask */ -#define CE_HOST_IE_ADDRESS 0x00A1803C -#define CE_HOST_IE_2_ADDRESS 0x00A18040 +#define CE_HOST_IE_ADDRESS 0x75804C +#define CE_HOST_IE_2_ADDRESS 0x758050 #define CE_HOST_IE_3_ADDRESS CE_HOST_IE_ADDRESS #define CE_HOST_IE_3_SHIFT 0xC @@ -76,6 +76,17 @@ struct ce_pipe_config { __le32 reserved; }; +struct ce_ie_addr { + u32 ie1_reg_addr; + u32 ie2_reg_addr; + u32 ie3_reg_addr; +}; + +struct ce_remap { + u32 base; + u32 size; +}; + struct ce_attr { /* CE_ATTR_* values */ unsigned int flags; @@ -164,6 +175,7 @@ struct ath12k_ce { extern const struct ce_attr ath12k_host_ce_config_qcn9274[]; extern const struct ce_attr ath12k_host_ce_config_wcn7850[]; +extern const struct ce_attr ath12k_host_ce_config_ipq5332[]; void ath12k_ce_cleanup_pipes(struct ath12k_base *ab); void ath12k_ce_rx_replenish_retry(struct timer_list *t); diff --git a/drivers/net/wireless/ath/ath12k/core.c b/drivers/net/wireless/ath/ath12k/core.c index 0606116d6b9c..31d851d8e688 100644 --- a/drivers/net/wireless/ath/ath12k/core.c +++ b/drivers/net/wireless/ath/ath12k/core.c @@ -10,19 +10,26 @@ #include <linux/firmware.h> #include <linux/of.h> #include <linux/of_graph.h> +#include "ahb.h" #include "core.h" #include "dp_tx.h" #include "dp_rx.h" #include "debug.h" -#include "hif.h" -#include "fw.h" #include "debugfs.h" +#include "fw.h" +#include "hif.h" +#include "pci.h" #include "wow.h" +static int ahb_err, pci_err; unsigned int ath12k_debug_mask; module_param_named(debug_mask, ath12k_debug_mask, uint, 0644); MODULE_PARM_DESC(debug_mask, "Debugging mask"); +bool ath12k_ftm_mode; +module_param_named(ftm_mode, ath12k_ftm_mode, bool, 0444); +MODULE_PARM_DESC(ftm_mode, "Boots up in factory test mode"); + /* protected with ath12k_hw_group_mutex */ static struct list_head ath12k_hw_group_list = LIST_HEAD_INIT(ath12k_hw_group_list); @@ -36,6 +43,9 @@ static int ath12k_core_rfkill_config(struct ath12k_base *ab) if (!(ab->target_caps.sys_cap_info & WMI_SYS_CAP_INFO_RFKILL)) return 0; + if (ath12k_acpi_get_disable_rfkill(ab)) + return 0; + for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -173,7 +183,7 @@ EXPORT_SYMBOL(ath12k_core_resume); static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, size_t name_len, bool with_variant, - bool bus_type_mode) + bool bus_type_mode, bool with_default) { /* strlen(',variant=') + strlen(ab->qmi.target.bdf_ext) */ char variant[9 + ATH12K_QMI_BDF_EXT_STR_LENGTH] = { 0 }; @@ -204,7 +214,9 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, "bus=%s,qmi-chip-id=%d,qmi-board-id=%d%s", ath12k_bus_str(ab->hif.bus), ab->qmi.target.chip_id, - ab->qmi.target.board_id, variant); + with_default ? + ATH12K_BOARD_ID_DEFAULT : ab->qmi.target.board_id, + variant); break; } @@ -216,19 +228,19 @@ static int __ath12k_core_create_board_name(struct ath12k_base *ab, char *name, static int ath12k_core_create_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, true, false); + return __ath12k_core_create_board_name(ab, name, name_len, true, false, false); } static int ath12k_core_create_fallback_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, false, false); + return __ath12k_core_create_board_name(ab, name, name_len, false, false, true); } static int ath12k_core_create_bus_type_board_name(struct ath12k_base *ab, char *name, size_t name_len) { - return __ath12k_core_create_board_name(ab, name, name_len, false, true); + return __ath12k_core_create_board_name(ab, name, name_len, false, true, true); } const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, @@ -603,9 +615,74 @@ u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab) return TARGET_NUM_TIDS(SINGLE); } +struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab, + int index) +{ + struct device *dev = ab->dev; + struct reserved_mem *rmem; + struct device_node *node; + + node = of_parse_phandle(dev->of_node, "memory-region", index); + if (!node) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "failed to parse memory-region for index %d\n", index); + return NULL; + } + + rmem = of_reserved_mem_lookup(node); + of_node_put(node); + if (!rmem) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "unable to get memory-region for index %d\n", index); + return NULL; + } + + return rmem; +} + +static inline +void ath12k_core_to_group_ref_get(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ab->ag; + + lockdep_assert_held(&ag->mutex); + + if (ab->hw_group_ref) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "core already attached to group %d\n", + ag->id); + return; + } + + ab->hw_group_ref = true; + ag->num_started++; + + ath12k_dbg(ab, ATH12K_DBG_BOOT, "core attached to group %d, num_started %d\n", + ag->id, ag->num_started); +} + +static inline +void ath12k_core_to_group_ref_put(struct ath12k_base *ab) +{ + struct ath12k_hw_group *ag = ab->ag; + + lockdep_assert_held(&ag->mutex); + + if (!ab->hw_group_ref) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, "core already de-attached from group %d\n", + ag->id); + return; + } + + ab->hw_group_ref = false; + ag->num_started--; + + ath12k_dbg(ab, ATH12K_DBG_BOOT, "core de-attached from group %d, num_started %d\n", + ag->id, ag->num_started); +} + static void ath12k_core_stop(struct ath12k_base *ab) { - ath12k_core_stopped(ab); + ath12k_core_to_group_ref_put(ab); if (!test_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags)) ath12k_qmi_firmware_stop(ab); @@ -620,7 +697,7 @@ static void ath12k_core_stop(struct ath12k_base *ab) /* De-Init of components as needed */ } -static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data) +static void ath12k_core_check_cc_code_bdfext(const struct dmi_header *hdr, void *data) { struct ath12k_base *ab = data; const char *magic = ATH12K_SMBIOS_BDF_EXT_MAGIC; @@ -642,6 +719,28 @@ static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data) return; } + spin_lock_bh(&ab->base_lock); + + switch (smbios->country_code_flag) { + case ATH12K_SMBIOS_CC_ISO: + ab->new_alpha2[0] = u16_get_bits(smbios->cc_code >> 8, 0xff); + ab->new_alpha2[1] = u16_get_bits(smbios->cc_code, 0xff); + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot smbios cc_code %c%c\n", + ab->new_alpha2[0], ab->new_alpha2[1]); + break; + case ATH12K_SMBIOS_CC_WW: + ab->new_alpha2[0] = '0'; + ab->new_alpha2[1] = '0'; + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot smbios worldwide regdomain\n"); + break; + default: + ath12k_dbg(ab, ATH12K_DBG_BOOT, "boot ignore smbios country code setting %d\n", + smbios->country_code_flag); + break; + } + + spin_unlock_bh(&ab->base_lock); + if (!smbios->bdf_enabled) { ath12k_dbg(ab, ATH12K_DBG_BOOT, "bdf variant name not found.\n"); return; @@ -681,7 +780,7 @@ static void ath12k_core_check_bdfext(const struct dmi_header *hdr, void *data) int ath12k_core_check_smbios(struct ath12k_base *ab) { ab->qmi.target.bdf_ext[0] = '\0'; - dmi_walk(ath12k_core_check_bdfext, ab); + dmi_walk(ath12k_core_check_cc_code_bdfext, ab); if (ab->qmi.target.bdf_ext[0] == '\0') return -ENODATA; @@ -693,6 +792,11 @@ static int ath12k_core_soc_create(struct ath12k_base *ab) { int ret; + if (ath12k_ftm_mode) { + ab->fw_mode = ATH12K_FIRMWARE_MODE_FTM; + ath12k_info(ab, "Booting in ftm mode\n"); + } + ret = ath12k_qmi_init_service(ab); if (ret) { ath12k_err(ab, "failed to initialize qmi :%d\n", ret); @@ -707,6 +811,8 @@ static int ath12k_core_soc_create(struct ath12k_base *ab) goto err_qmi_deinit; } + ath12k_debugfs_pdev_create(ab); + return 0; err_qmi_deinit: @@ -741,8 +847,7 @@ static void ath12k_core_pdev_destroy(struct ath12k_base *ab) ath12k_dp_pdev_free(ab); } -static int ath12k_core_start(struct ath12k_base *ab, - enum ath12k_firmware_mode mode) +static int ath12k_core_start(struct ath12k_base *ab) { int ret; @@ -836,14 +941,10 @@ static int ath12k_core_start(struct ath12k_base *ab, goto err_reo_cleanup; } - ret = ath12k_acpi_start(ab); - if (ret) - /* ACPI is optional so continue in case of an error */ - ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", ret); + ath12k_acpi_set_dsm_func(ab); - if (!test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) - /* Indicate the core start in the appropriate group */ - ath12k_core_started(ab); + /* Indicate the core start in the appropriate group */ + ath12k_core_to_group_ref_get(ab); return 0; @@ -881,16 +982,50 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag) ab = ag->ab[i]; if (!ab) continue; + + clear_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); + ath12k_core_device_cleanup(ab); } ath12k_mac_destroy(ag); } +u8 ath12k_get_num_partner_link(struct ath12k *ar) +{ + struct ath12k_base *partner_ab, *ab = ar->ab; + struct ath12k_hw_group *ag = ab->ag; + struct ath12k_pdev *pdev; + u8 num_link = 0; + int i, j; + + lockdep_assert_held(&ag->mutex); + + for (i = 0; i < ag->num_devices; i++) { + partner_ab = ag->ab[i]; + + for (j = 0; j < partner_ab->num_radios; j++) { + pdev = &partner_ab->pdevs[j]; + + /* Avoid the self link */ + if (ar == pdev->ar) + continue; + + num_link++; + } + } + + return num_link; +} + static int __ath12k_mac_mlo_ready(struct ath12k *ar) { + u8 num_link = ath12k_get_num_partner_link(ar); int ret; + if (num_link == 0) + return 0; + ret = ath12k_wmi_mlo_ready(ar); if (ret) { ath12k_err(ar->ab, "MLO ready failed for pdev %d: %d\n", @@ -920,19 +1055,18 @@ int ath12k_mac_mlo_ready(struct ath12k_hw_group *ag) ar = &ah->radio[j]; ret = __ath12k_mac_mlo_ready(ar); if (ret) - goto out; + return ret; } } -out: - return ret; + return 0; } static int ath12k_core_mlo_setup(struct ath12k_hw_group *ag) { int ret, i; - if (!ag->mlo_capable || ag->num_devices == 1) + if (!ag->mlo_capable) return 0; ret = ath12k_mac_mlo_setup(ag); @@ -986,6 +1120,8 @@ core_pdev_create: mutex_lock(&ab->core_lock); + set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); + ret = ath12k_core_pdev_create(ab); if (ret) { ath12k_err(ab, "failed to create pdev core %d\n", ret); @@ -1044,6 +1180,59 @@ bool ath12k_core_hw_group_start_ready(struct ath12k_hw_group *ag) return (ag->num_started == ag->num_devices); } +static void ath12k_fw_stats_pdevs_free(struct list_head *head) +{ + struct ath12k_fw_stats_pdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +void ath12k_fw_stats_bcn_free(struct list_head *head) +{ + struct ath12k_fw_stats_bcn *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +static void ath12k_fw_stats_vdevs_free(struct list_head *head) +{ + struct ath12k_fw_stats_vdev *i, *tmp; + + list_for_each_entry_safe(i, tmp, head, list) { + list_del(&i->list); + kfree(i); + } +} + +void ath12k_fw_stats_init(struct ath12k *ar) +{ + INIT_LIST_HEAD(&ar->fw_stats.vdevs); + INIT_LIST_HEAD(&ar->fw_stats.pdevs); + INIT_LIST_HEAD(&ar->fw_stats.bcn); + init_completion(&ar->fw_stats_complete); +} + +void ath12k_fw_stats_free(struct ath12k_fw_stats *stats) +{ + ath12k_fw_stats_pdevs_free(&stats->pdevs); + ath12k_fw_stats_vdevs_free(&stats->vdevs); + ath12k_fw_stats_bcn_free(&stats->bcn); +} + +void ath12k_fw_stats_reset(struct ath12k *ar) +{ + spin_lock_bh(&ar->data_lock); + ar->fw_stats.fw_stats_done = false; + ath12k_fw_stats_free(&ar->fw_stats); + spin_unlock_bh(&ar->data_lock); +} + static void ath12k_core_trigger_partner(struct ath12k_base *ab) { struct ath12k_hw_group *ag = ab->ag; @@ -1068,7 +1257,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab) struct ath12k_hw_group *ag = ath12k_ab_to_ag(ab); int ret, i; - ret = ath12k_core_start_firmware(ab, ATH12K_FIRMWARE_MODE_NORMAL); + ret = ath12k_core_start_firmware(ab, ab->fw_mode); if (ret) { ath12k_err(ab, "failed to start firmware: %d\n", ret); return ret; @@ -1089,7 +1278,7 @@ int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab) mutex_lock(&ag->mutex); mutex_lock(&ab->core_lock); - ret = ath12k_core_start(ab, ATH12K_FIRMWARE_MODE_NORMAL); + ret = ath12k_core_start(ab); if (ret) { ath12k_err(ab, "failed to start core: %d\n", ret); goto err_dp_free; @@ -1122,16 +1311,18 @@ err_core_stop: ath12k_core_stop(ab); mutex_unlock(&ab->core_lock); } + mutex_unlock(&ag->mutex); goto exit; err_dp_free: ath12k_dp_free(ab); mutex_unlock(&ab->core_lock); + mutex_unlock(&ag->mutex); + err_firmware_stop: ath12k_qmi_firmware_stop(ab); exit: - mutex_unlock(&ag->mutex); return ret; } @@ -1204,6 +1395,7 @@ static void ath12k_rfkill_work(struct work_struct *work) void ath12k_core_halt(struct ath12k *ar) { + struct list_head *pos, *n; struct ath12k_base *ab = ar->ab; lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); @@ -1216,10 +1408,16 @@ void ath12k_core_halt(struct ath12k *ar) cancel_delayed_work_sync(&ar->scan.timeout); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ab->rfkill_work); + cancel_work_sync(&ab->update_11d_work); rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx], NULL); synchronize_rcu(); - INIT_LIST_HEAD(&ar->arvifs); + + spin_lock_bh(&ar->data_lock); + list_for_each_safe(pos, n, &ar->arvifs) + list_del_init(pos); + spin_unlock_bh(&ar->data_lock); + idr_init(&ar->txmgmt_idr); } @@ -1239,17 +1437,32 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) for (i = 0; i < ag->num_hw; i++) { ah = ath12k_ag_to_ah(ag, i); - if (!ah || ah->state == ATH12K_HW_STATE_OFF) + if (!ah || ah->state == ATH12K_HW_STATE_OFF || + ah->state == ATH12K_HW_STATE_TM) continue; + wiphy_lock(ah->hw->wiphy); + + /* If queue 0 is stopped, it is safe to assume that all + * other queues are stopped by driver via + * ieee80211_stop_queues() below. This means, there is + * no need to stop it again and hence continue + */ + if (ieee80211_queue_stopped(ah->hw, 0)) { + wiphy_unlock(ah->hw->wiphy); + continue; + } + ieee80211_stop_queues(ah->hw); for (j = 0; j < ah->num_radio; j++) { ar = &ah->radio[j]; ath12k_mac_drain_tx(ar); + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); complete(&ar->scan.started); - complete(&ar->scan.completed); + complete_all(&ar->scan.completed); complete(&ar->scan.on_channel); complete(&ar->peer_assoc_done); complete(&ar->peer_delete_done); @@ -1263,13 +1476,47 @@ static void ath12k_core_pre_reconfigure_recovery(struct ath12k_base *ab) ath12k_mac_tx_mgmt_pending_free, ar); idr_destroy(&ar->txmgmt_idr); wake_up(&ar->txmgmt_empty_waitq); + + ar->monitor_vdev_id = -1; + ar->monitor_vdev_created = false; + ar->monitor_started = false; } + + wiphy_unlock(ah->hw->wiphy); } wake_up(&ab->wmi_ab.tx_credits_wq); wake_up(&ab->peer_mapping_wq); } +static void ath12k_update_11d(struct work_struct *work) +{ + struct ath12k_base *ab = container_of(work, struct ath12k_base, update_11d_work); + struct ath12k *ar; + struct ath12k_pdev *pdev; + struct wmi_set_current_country_arg arg = {}; + int ret, i; + + spin_lock_bh(&ab->base_lock); + memcpy(&arg.alpha2, &ab->new_alpha2, 2); + spin_unlock_bh(&ab->base_lock); + + ath12k_dbg(ab, ATH12K_DBG_WMI, "update 11d new cc %c%c\n", + arg.alpha2[0], arg.alpha2[1]); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + + memcpy(&ar->alpha2, &arg.alpha2, 2); + ret = ath12k_wmi_send_set_current_country_cmd(ar, &arg); + if (ret) + ath12k_warn(ar->ab, + "pdev id %d failed set current country code: %d\n", + i, ret); + } +} + static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) { struct ath12k_hw_group *ag = ab->ag; @@ -1309,6 +1556,9 @@ static void ath12k_core_post_reconfigure_recovery(struct ath12k_base *ab) ath12k_warn(ab, "device is wedged, will not restart hw %d\n", i); break; + case ATH12K_HW_STATE_TM: + ath12k_warn(ab, "fw mode reset done radio %d\n", i); + break; } mutex_unlock(&ah->hw_mutex); @@ -1340,19 +1590,30 @@ static void ath12k_core_restart(struct work_struct *work) ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset success\n"); } + mutex_lock(&ag->mutex); + + if (!ath12k_core_hw_group_start_ready(ag)) { + mutex_unlock(&ag->mutex); + goto exit_restart; + } + for (i = 0; i < ag->num_hw; i++) { - ah = ath12k_ag_to_ah(ab->ag, i); + ah = ath12k_ag_to_ah(ag, i); ieee80211_restart_hw(ah->hw); } + + mutex_unlock(&ag->mutex); } +exit_restart: complete(&ab->restart_completed); } static void ath12k_core_reset(struct work_struct *work) { struct ath12k_base *ab = container_of(work, struct ath12k_base, reset_work); - int reset_count, fail_cont_count; + struct ath12k_hw_group *ag = ab->ag; + int reset_count, fail_cont_count, i; long time_left; if (!(test_bit(ATH12K_FLAG_QMI_FW_READY_COMPLETE, &ab->dev_flags))) { @@ -1411,9 +1672,34 @@ static void ath12k_core_reset(struct work_struct *work) ath12k_hif_ce_irq_disable(ab); ath12k_hif_power_down(ab, false); - ath12k_hif_power_up(ab); - ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n"); + /* prepare for power up */ + ab->qmi.num_radios = U8_MAX; + + mutex_lock(&ag->mutex); + ath12k_core_to_group_ref_put(ab); + + if (ag->num_started > 0) { + ath12k_dbg(ab, ATH12K_DBG_BOOT, + "waiting for %d partner device(s) to reset\n", + ag->num_started); + mutex_unlock(&ag->mutex); + return; + } + + /* Prepare MLO global memory region for power up */ + ath12k_qmi_reset_mlo_mem(ag); + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + ath12k_hif_power_up(ab); + ath12k_dbg(ab, ATH12K_DBG_BOOT, "reset started\n"); + } + + mutex_unlock(&ag->mutex); } int ath12k_core_pre_init(struct ath12k_base *ab) @@ -1550,9 +1836,9 @@ static int ath12k_core_get_wsi_info(struct ath12k_hw_group *ag, of_node_put(next_rx_endpoint); device_count++; - if (device_count > ATH12K_MAX_SOCS) { + if (device_count > ATH12K_MAX_DEVICES) { ath12k_warn(ab, "device count in DT %d is more than limit %d\n", - device_count, ATH12K_MAX_SOCS); + device_count, ATH12K_MAX_DEVICES); of_node_put(next_wsi_dev); return -EINVAL; } @@ -1602,6 +1888,9 @@ static struct ath12k_hw_group *ath12k_core_hw_group_assign(struct ath12k_base *a lockdep_assert_held(&ath12k_hw_group_mutex); + if (ath12k_ftm_mode) + goto invalid_group; + /* The grouping of multiple devices will be done based on device tree file. * The platforms that do not have any valid group information would have * each device to be part of its own invalid group. @@ -1725,7 +2014,7 @@ static void ath12k_core_hw_group_destroy(struct ath12k_hw_group *ag) } } -static void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag) +void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag) { struct ath12k_base *ab; int i; @@ -1789,29 +2078,26 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag) struct ath12k_base *ab; int i; + if (ath12k_ftm_mode) + return; + lockdep_assert_held(&ag->mutex); - /* If more than one devices are grouped, then inter MLO - * functionality can work still independent of whether internally - * each device supports single_chip_mlo or not. - * Only when there is one device, then it depends whether the - * device can support intra chip MLO or not - */ - if (ag->num_devices > 1) { - ag->mlo_capable = true; - } else { + if (ag->num_devices == 1) { ab = ag->ab[0]; - ag->mlo_capable = ab->single_chip_mlo_supp; - - /* WCN chipsets does not advertise in firmware features - * hence skip checking - */ - if (ab->hw_params->def_num_link) + /* QCN9274 firmware uses firmware IE for MLO advertisement */ + if (ab->fw.fw_features_valid) { + ag->mlo_capable = + ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MLO); return; - } + } - if (!ag->mlo_capable) + /* while WCN7850 firmware uses QMI single_chip_mlo_support bit */ + ag->mlo_capable = ab->single_chip_mlo_support; return; + } + + ag->mlo_capable = true; for (i = 0; i < ag->num_devices; i++) { ab = ag->ab[i]; @@ -1821,7 +2107,7 @@ void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag) /* even if 1 device's firmware feature indicates MLO * unsupported, make MLO unsupported for the whole group */ - if (!test_bit(ATH12K_FW_FEATURE_MLO, ab->fw.fw_features)) { + if (!ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MLO)) { ag->mlo_capable = false; return; } @@ -1874,10 +2160,9 @@ err: void ath12k_core_deinit(struct ath12k_base *ab) { - ath12k_core_panic_notifier_unregister(ab); - ath12k_core_hw_group_cleanup(ab->ag); ath12k_core_hw_group_destroy(ab->ag); ath12k_core_hw_group_unassign(ab); + ath12k_core_panic_notifier_unregister(ab); } void ath12k_core_free(struct ath12k_base *ab) @@ -1918,6 +2203,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, INIT_WORK(&ab->reset_work, ath12k_core_reset); INIT_WORK(&ab->rfkill_work, ath12k_rfkill_work); INIT_WORK(&ab->dump_work, ath12k_coredump_upload); + INIT_WORK(&ab->update_11d_work, ath12k_update_11d); timer_setup(&ab->rx_replenish_retry, ath12k_ce_rx_replenish_retry, 0); init_completion(&ab->htc_suspend); @@ -1927,7 +2213,7 @@ struct ath12k_base *ath12k_core_alloc(struct device *dev, size_t priv_size, ab->dev = dev; ab->hif.bus = bus; ab->qmi.num_radios = U8_MAX; - ab->single_chip_mlo_supp = false; + ab->single_chip_mlo_support = false; /* Device index used to identify the devices in a group. * @@ -1948,5 +2234,31 @@ err_sc_free: return NULL; } -MODULE_DESCRIPTION("Core module for Qualcomm Atheros 802.11be wireless LAN cards."); +static int ath12k_init(void) +{ + ahb_err = ath12k_ahb_init(); + if (ahb_err) + pr_warn("Failed to initialize ath12k AHB device: %d\n", ahb_err); + + pci_err = ath12k_pci_init(); + if (pci_err) + pr_warn("Failed to initialize ath12k PCI device: %d\n", pci_err); + + /* If both failed, return one of the failures (arbitrary) */ + return ahb_err && pci_err ? ahb_err : 0; +} + +static void ath12k_exit(void) +{ + if (!pci_err) + ath12k_pci_exit(); + + if (!ahb_err) + ath12k_ahb_exit(); +} + +module_init(ath12k_init); +module_exit(ath12k_exit); + +MODULE_DESCRIPTION("Driver support for Qualcomm Technologies 802.11be WLAN devices"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath12k/core.h b/drivers/net/wireless/ath/ath12k/core.h index ee595794a7ae..941db6e49d6e 100644 --- a/drivers/net/wireless/ath/ath12k/core.h +++ b/drivers/net/wireless/ath/ath12k/core.h @@ -14,7 +14,10 @@ #include <linux/dmi.h> #include <linux/ctype.h> #include <linux/firmware.h> +#include <linux/of_reserved_mem.h> #include <linux/panic_notifier.h> +#include <linux/average.h> +#include <linux/of.h> #include "qmi.h" #include "htc.h" #include "wmi.h" @@ -52,8 +55,6 @@ #define ATH12K_INVALID_HW_MAC_ID 0xFF #define ATH12K_CONNECTION_LOSS_HZ (3 * HZ) -#define ATH12K_RX_RATE_TABLE_NUM 320 -#define ATH12K_RX_RATE_TABLE_11AX_NUM 576 #define ATH12K_MON_TIMER_INTERVAL 10 #define ATH12K_RESET_TIMEOUT_HZ (20 * HZ) @@ -63,8 +64,8 @@ #define ATH12K_RECONFIGURE_TIMEOUT_HZ (10 * HZ) #define ATH12K_RECOVER_START_TIMEOUT_HZ (20 * HZ) -#define ATH12K_MAX_SOCS 3 -#define ATH12K_GROUP_MAX_RADIO (ATH12K_MAX_SOCS * MAX_RADIOS) +#define ATH12K_MAX_DEVICES 3 +#define ATH12K_GROUP_MAX_RADIO (ATH12K_MAX_DEVICES * MAX_RADIOS) #define ATH12K_INVALID_GROUP_ID 0xFF #define ATH12K_INVALID_DEVICE_ID 0xFF @@ -87,6 +88,7 @@ enum wme_ac { #define ATH12K_HT_MCS_MAX 7 #define ATH12K_VHT_MCS_MAX 9 #define ATH12K_HE_MCS_MAX 11 +#define ATH12K_EHT_MCS_MAX 15 enum ath12k_crypt_mode { /* Only use hardware crypto engine */ @@ -141,12 +143,14 @@ struct ath12k_skb_rxcb { u8 is_frag; u8 tid; u16 peer_id; + bool is_end_of_ppdu; }; enum ath12k_hw_rev { ATH12K_HW_QCN9274_HW10, ATH12K_HW_QCN9274_HW20, - ATH12K_HW_WCN7850_HW20 + ATH12K_HW_WCN7850_HW20, + ATH12K_HW_IPQ5332_HW10, }; enum ath12k_firmware_mode { @@ -159,6 +163,7 @@ enum ath12k_firmware_mode { #define ATH12K_IRQ_NUM_MAX 57 #define ATH12K_EXT_IRQ_NUM_MAX 16 +#define ATH12K_MAX_TCL_RING_NUM 3 struct ath12k_ext_irq_grp { struct ath12k_base *ab; @@ -166,13 +171,39 @@ struct ath12k_ext_irq_grp { u32 num_irq; u32 grp_id; u64 timestamp; + bool napi_enabled; struct napi_struct napi; struct net_device *napi_ndev; }; +enum ath12k_smbios_cc_type { + /* disable country code setting from SMBIOS */ + ATH12K_SMBIOS_CC_DISABLE = 0, + + /* set country code by ANSI country name, based on ISO3166-1 alpha2 */ + ATH12K_SMBIOS_CC_ISO = 1, + + /* worldwide regdomain */ + ATH12K_SMBIOS_CC_WW = 2, +}; + struct ath12k_smbios_bdf { struct dmi_header hdr; - u32 padding; + u8 features_disabled; + + /* enum ath12k_smbios_cc_type */ + u8 country_code_flag; + + /* To set specific country, you need to set country code + * flag=ATH12K_SMBIOS_CC_ISO first, then if country is United + * States, then country code value = 0x5553 ("US",'U' = 0x55, 'S'= + * 0x53). To set country to INDONESIA, then country code value = + * 0x4944 ("IN", 'I'=0x49, 'D'=0x44). If country code flag = + * ATH12K_SMBIOS_CC_WW, then you can use worldwide regulatory + * setting. + */ + u16 cc_code; + u8 bdf_enabled; u8 bdf_ext[]; } __packed; @@ -217,6 +248,12 @@ enum ath12k_scan_state { ATH12K_SCAN_ABORTING, }; +enum ath12k_11d_state { + ATH12K_11D_IDLE, + ATH12K_11D_PREPARING, + ATH12K_11D_RUNNING, +}; + enum ath12k_hw_group_flags { ATH12K_GROUP_FLAG_REGISTERED, ATH12K_GROUP_FLAG_UNREGISTER, @@ -235,6 +272,8 @@ enum ath12k_dev_flags { ATH12K_FLAG_CE_IRQ_ENABLED, ATH12K_FLAG_EXT_IRQ_ENABLED, ATH12K_FLAG_QMI_FW_READY_COMPLETE, + ATH12K_FLAG_FTM_SEGMENTED, + ATH12K_FLAG_FIXED_MEM_REGION, }; struct ath12k_tx_conf { @@ -292,12 +331,20 @@ struct ath12k_link_vif { int txpower; bool rsnie_present; bool wpaie_present; - struct ieee80211_chanctx_conf chanctx; u8 vdev_stats_id; u32 punct_bitmap; u8 link_id; struct ath12k_vif *ahvif; struct ath12k_rekey_data rekey_data; + struct ath12k_link_stats link_stats; + spinlock_t link_stats_lock; /* Protects updates to link_stats */ + + u8 current_cntdown_counter; + + /* only used in station mode */ + bool is_sta_assoc_link; + + struct ath12k_reg_tpc_power_info reg_tpc_info; }; struct ath12k_vif { @@ -327,6 +374,7 @@ struct ath12k_vif { u32 key_cipher; u8 tx_encap_type; bool ps; + atomic_t mcbc_gsn; struct ath12k_link_vif deflink; struct ath12k_link_vif __rcu *link[ATH12K_NUM_MAX_LINKS]; @@ -354,20 +402,22 @@ struct ath12k_vif_iter { #define HAL_RX_MAX_MCS_HT 31 #define HAL_RX_MAX_MCS_VHT 9 #define HAL_RX_MAX_MCS_HE 11 +#define HAL_RX_MAX_MCS_BE 15 #define HAL_RX_MAX_NSS 8 #define HAL_RX_MAX_NUM_LEGACY_RATES 12 -#define ATH12K_RX_RATE_TABLE_11AX_NUM 576 -#define ATH12K_RX_RATE_TABLE_NUM 320 + +#define ATH12K_SCAN_TIMEOUT_HZ (20 * HZ) struct ath12k_rx_peer_rate_stats { u64 ht_mcs_count[HAL_RX_MAX_MCS_HT + 1]; u64 vht_mcs_count[HAL_RX_MAX_MCS_VHT + 1]; u64 he_mcs_count[HAL_RX_MAX_MCS_HE + 1]; + u64 be_mcs_count[HAL_RX_MAX_MCS_BE + 1]; u64 nss_count[HAL_RX_MAX_NSS]; u64 bw_count[HAL_RX_BW_MAX]; u64 gi_count[HAL_RX_GI_MAX]; u64 legacy_count[HAL_RX_MAX_NUM_LEGACY_RATES]; - u64 rx_rate[ATH12K_RX_RATE_TABLE_11AX_NUM]; + u64 rx_rate[HAL_RX_BW_MAX][HAL_RX_GI_MAX][HAL_RX_MAX_NSS][HAL_RX_MAX_MCS_HT + 1]; }; struct ath12k_rx_peer_stats { @@ -477,6 +527,8 @@ struct ath12k_wbm_tx_stats { u64 wbm_tx_comp_stats[HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX]; }; +DECLARE_EWMA(avg_rssi, 10, 8) + struct ath12k_link_sta { struct ath12k_link_vif *arvif; struct ath12k_sta *ahsta; @@ -496,10 +548,13 @@ struct ath12k_link_sta { u64 rx_duration; u64 tx_duration; u8 rssi_comb; + struct ewma_avg_rssi avg_rssi; u8 link_id; struct ath12k_rx_peer_stats *rx_stats; struct ath12k_wbm_tx_stats *wbm_tx_stats; u32 bw_prev; + u32 peer_nss; + s8 rssi_beacon; /* For now the assoc link will be considered primary */ bool is_assoc_link; @@ -508,6 +563,12 @@ struct ath12k_link_sta { u8 link_idx; }; +struct ath12k_reoq_buf { + void *vaddr; + dma_addr_t paddr_aligned; + u32 size; +}; + struct ath12k_sta { struct ath12k_vif *ahvif; enum hal_pn_type pn_type; @@ -520,13 +581,25 @@ struct ath12k_sta { u8 num_peer; enum ieee80211_sta_state state; + + struct ath12k_reoq_buf reoq_bufs[IEEE80211_NUM_TIDS + 1]; }; -#define ATH12K_MIN_5G_FREQ 4150 -#define ATH12K_MIN_6G_FREQ 5925 -#define ATH12K_MAX_6G_FREQ 7115 +#define ATH12K_HALF_20MHZ_BW 10 +#define ATH12K_2GHZ_MIN_CENTER 2412 +#define ATH12K_2GHZ_MAX_CENTER 2484 +#define ATH12K_5GHZ_MIN_CENTER 4900 +#define ATH12K_5GHZ_MAX_CENTER 5920 +#define ATH12K_6GHZ_MIN_CENTER 5935 +#define ATH12K_6GHZ_MAX_CENTER 7115 +#define ATH12K_MIN_2GHZ_FREQ (ATH12K_2GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW - 1) +#define ATH12K_MAX_2GHZ_FREQ (ATH12K_2GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW + 1) +#define ATH12K_MIN_5GHZ_FREQ (ATH12K_5GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW) +#define ATH12K_MAX_5GHZ_FREQ (ATH12K_5GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW) +#define ATH12K_MIN_6GHZ_FREQ (ATH12K_6GHZ_MIN_CENTER - ATH12K_HALF_20MHZ_BW) +#define ATH12K_MAX_6GHZ_FREQ (ATH12K_6GHZ_MAX_CENTER + ATH12K_HALF_20MHZ_BW) #define ATH12K_NUM_CHANS 101 -#define ATH12K_MAX_5G_CHAN 173 +#define ATH12K_MAX_5GHZ_CHAN 173 enum ath12k_hw_state { ATH12K_HW_STATE_OFF, @@ -534,18 +607,26 @@ enum ath12k_hw_state { ATH12K_HW_STATE_RESTARTING, ATH12K_HW_STATE_RESTARTED, ATH12K_HW_STATE_WEDGED, + ATH12K_HW_STATE_TM, /* Add other states as required */ }; /* Antenna noise floor */ #define ATH12K_DEFAULT_NOISE_FLOOR -95 +struct ath12k_ftm_event_obj { + u32 data_pos; + u32 expected_seq; + u8 *eventdata; +}; + struct ath12k_fw_stats { u32 pdev_id; u32 stats_id; struct list_head pdevs; struct list_head vdevs; struct list_head bcn; + bool fw_stats_done; }; struct ath12k_dbg_htt_stats { @@ -559,6 +640,12 @@ struct ath12k_debug { struct dentry *debugfs_pdev; struct dentry *debugfs_pdev_symlink; struct ath12k_dbg_htt_stats htt_stats; + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type; + bool tpc_request; + struct completion tpc_complete; + struct wmi_tpc_stats_arg *tpc_stats; + u32 rx_filter; + bool extd_rx_stats; }; struct ath12k_per_peer_tx_stats { @@ -703,7 +790,6 @@ struct ath12k { #endif bool dfs_block_radar_events; - bool monitor_conf_enabled; bool monitor_vdev_created; bool monitor_started; int monitor_vdev_id; @@ -712,8 +798,22 @@ struct ath12k { bool nlo_enabled; + /* Protected by wiphy::mtx lock. */ + u32 vdev_id_11d_scan; + struct completion completed_11d_scan; + enum ath12k_11d_state state_11d; + u8 alpha2[REG_ALPHA2_LEN]; + bool regdom_set_by_user; + + struct completion fw_stats_complete; + struct completion mlo_setup_done; u32 mlo_setup_status; + u8 ftm_msgref; + struct ath12k_fw_stats fw_stats; + unsigned long last_tx_power_update; + + s8 max_allowed_tx_power; }; struct ath12k_hw { @@ -805,7 +905,7 @@ struct ath12k_board_data { size_t len; }; -struct ath12k_soc_dp_tx_err_stats { +struct ath12k_device_dp_tx_err_stats { /* TCL Ring Descriptor unavailable */ u32 desc_na[DP_TCL_NUM_RING_MAX]; /* Other failures during dp_tx due to mem allocation failure @@ -814,13 +914,25 @@ struct ath12k_soc_dp_tx_err_stats { atomic_t misc_fail; }; -struct ath12k_soc_dp_stats { +struct ath12k_device_dp_stats { u32 err_ring_pkts; u32 invalid_rbm; u32 rxdma_error[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX]; u32 reo_error[HAL_REO_DEST_RING_ERROR_CODE_MAX]; u32 hal_reo_error[DP_REO_DST_RING_MAX]; - struct ath12k_soc_dp_tx_err_stats tx_err; + struct ath12k_device_dp_tx_err_stats tx_err; + u32 reo_rx[DP_REO_DST_RING_MAX][ATH12K_MAX_DEVICES]; + u32 rx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX][ATH12K_MAX_DEVICES]; + u32 tqm_rel_reason[MAX_TQM_RELEASE_REASON]; + u32 fw_tx_status[MAX_FW_TX_STATUS]; + u32 tx_wbm_rel_source[HAL_WBM_REL_SRC_MODULE_MAX]; + u32 tx_enqueued[DP_TCL_NUM_RING_MAX]; + u32 tx_completed[DP_TCL_NUM_RING_MAX]; +}; + +struct ath12k_reg_freq { + u32 start_freq; + u32 end_freq; }; struct ath12k_mlo_memory { @@ -844,7 +956,7 @@ struct ath12k_hw_group { u8 num_probed; u8 num_started; unsigned long flags; - struct ath12k_base *ab[ATH12K_MAX_SOCS]; + struct ath12k_base *ab[ATH12K_MAX_DEVICES]; /* protects access to this struct */ struct mutex mutex; @@ -858,7 +970,7 @@ struct ath12k_hw_group { struct ath12k_hw *ah[ATH12K_GROUP_MAX_RADIO]; u8 num_hw; bool mlo_capable; - struct device_node *wsi_node[ATH12K_MAX_SOCS]; + struct device_node *wsi_node[ATH12K_MAX_DEVICES]; struct ath12k_mlo_memory mlo_mem; struct ath12k_hw_link hw_links[ATH12K_GROUP_MAX_RADIO]; bool hw_link_id_init_done; @@ -894,6 +1006,10 @@ struct ath12k_base { void __iomem *mem; unsigned long mem_len; + void __iomem *mem_ce; + u32 ce_remap_base_addr; + bool ce_remap; + struct { enum ath12k_bus bus; const struct ath12k_hif_ops *ops; @@ -962,9 +1078,11 @@ struct ath12k_base { */ struct ieee80211_regdomain *new_regd[MAX_RADIOS]; + struct ath12k_reg_info *reg_info[MAX_RADIOS]; + /* Current DFS Regulatory */ enum ath12k_dfs_region dfs_region; - struct ath12k_soc_dp_stats soc_stats; + struct ath12k_device_dp_stats device_stats; #ifdef CONFIG_ATH12K_DEBUGFS struct dentry *debugfs_soc; #endif @@ -982,6 +1100,8 @@ struct ath12k_base { /* continuous recovery fail count */ atomic_t fail_cont_count; unsigned long reset_fail_timeout; + struct work_struct update_11d_work; + u8 new_alpha2[2]; struct { /* protected by data_lock */ u32 fw_crash_counter; @@ -991,8 +1111,6 @@ struct ath12k_base { struct ath12k_dbring_cap *db_caps; u32 num_db_cap; - struct timer_list mon_reap_timer; - struct completion htc_suspend; u64 fw_soc_drop_count; @@ -1022,13 +1140,11 @@ struct ath12k_base { size_t m3_len; DECLARE_BITMAP(fw_features, ATH12K_FW_FEATURE_COUNT); + bool fw_features_valid; } fw; const struct hal_rx_ops *hal_rx_ops; - /* Denotes the whether MLO is possible within the chip */ - bool single_chip_mlo_supp; - struct completion restart_completed; #ifdef CONFIG_ACPI @@ -1038,6 +1154,13 @@ struct ath12k_base { u32 func_bit; bool acpi_tas_enable; bool acpi_bios_sar_enable; + bool acpi_disable_11be; + bool acpi_disable_rfkill; + bool acpi_cca_enable; + bool acpi_band_edge_enable; + bool acpi_enable_bdf; + u32 bit_flag; + char bdf_string[ATH12K_ACPI_BDF_MAX_LEN]; u8 tas_cfg[ATH12K_ACPI_DSM_TAS_CFG_SIZE]; u8 tas_sar_power_table[ATH12K_ACPI_DSM_TAS_DATA_SIZE]; u8 bios_sar_data[ATH12K_ACPI_DSM_BIOS_SAR_DATA_SIZE]; @@ -1052,6 +1175,16 @@ struct ath12k_base { struct ath12k_hw_group *ag; struct ath12k_wsi_info wsi_info; + enum ath12k_firmware_mode fw_mode; + struct ath12k_ftm_event_obj ftm_event_obj; + bool hw_group_ref; + + /* Denote whether MLO is possible within the device */ + bool single_chip_mlo_support; + + struct ath12k_reg_freq reg_freq_2ghz; + struct ath12k_reg_freq reg_freq_5ghz; + struct ath12k_reg_freq reg_freq_6ghz; /* must be last */ u8 drv_priv[] __aligned(sizeof(void *)); @@ -1062,7 +1195,95 @@ struct ath12k_pdev_map { u8 pdev_idx; }; +struct ath12k_fw_stats_vdev { + struct list_head list; + + u32 vdev_id; + u32 beacon_snr; + u32 data_snr; + u32 num_tx_frames[WLAN_MAX_AC]; + u32 num_rx_frames; + u32 num_tx_frames_retries[WLAN_MAX_AC]; + u32 num_tx_frames_failures[WLAN_MAX_AC]; + u32 num_rts_fail; + u32 num_rts_success; + u32 num_rx_err; + u32 num_rx_discard; + u32 num_tx_not_acked; + u32 tx_rate_history[MAX_TX_RATE_VALUES]; + u32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +}; + +struct ath12k_fw_stats_bcn { + struct list_head list; + + u32 vdev_id; + u32 tx_bcn_succ_cnt; + u32 tx_bcn_outage_cnt; +}; + +struct ath12k_fw_stats_pdev { + struct list_head list; + + /* PDEV stats */ + s32 ch_noise_floor; + u32 tx_frame_count; + u32 rx_frame_count; + u32 rx_clear_count; + u32 cycle_count; + u32 phy_err_count; + u32 chan_tx_power; + u32 ack_rx_bad; + u32 rts_bad; + u32 rts_good; + u32 fcs_bad; + u32 no_beacons; + u32 mib_int_count; + + /* PDEV TX stats */ + s32 comp_queued; + s32 comp_delivered; + s32 msdu_enqued; + s32 mpdu_enqued; + s32 wmm_drop; + s32 local_enqued; + s32 local_freed; + s32 hw_queued; + s32 hw_reaped; + s32 underrun; + s32 tx_abort; + s32 mpdus_requed; + u32 tx_ko; + u32 data_rc; + u32 self_triggers; + u32 sw_retry_failure; + u32 illgl_rate_phy_err; + u32 pdev_cont_xretry; + u32 pdev_tx_timeout; + u32 pdev_resets; + u32 stateless_tid_alloc_failure; + u32 phy_underrun; + u32 txop_ovf; + + /* PDEV RX stats */ + s32 mid_ppdu_route_change; + s32 status_rcvd; + s32 r0_frags; + s32 r1_frags; + s32 r2_frags; + s32 r3_frags; + s32 htt_msdus; + s32 htt_mpdus; + s32 loc_msdus; + s32 loc_mpdus; + s32 oversize_amsdu; + s32 phy_errs; + s32 phy_err_drop; + s32 mpdu_errs; +}; + int ath12k_core_qmi_firmware_ready(struct ath12k_base *ab); +void ath12k_core_hw_group_cleanup(struct ath12k_hw_group *ag); int ath12k_core_pre_init(struct ath12k_base *ab); int ath12k_core_init(struct ath12k_base *ath12k); void ath12k_core_deinit(struct ath12k_base *ath12k); @@ -1084,6 +1305,7 @@ int ath12k_core_resume(struct ath12k_base *ab); int ath12k_core_suspend(struct ath12k_base *ab); int ath12k_core_suspend_late(struct ath12k_base *ab); void ath12k_core_hw_group_unassign(struct ath12k_base *ab); +u8 ath12k_get_num_partner_link(struct ath12k *ar); const struct firmware *ath12k_core_firmware_request(struct ath12k_base *ab, const char *filename); @@ -1092,6 +1314,12 @@ u32 ath12k_core_get_max_peers_per_radio(struct ath12k_base *ab); u32 ath12k_core_get_max_num_tids(struct ath12k_base *ab); void ath12k_core_hw_group_set_mlo_capable(struct ath12k_hw_group *ag); +void ath12k_fw_stats_init(struct ath12k *ar); +void ath12k_fw_stats_bcn_free(struct list_head *head); +void ath12k_fw_stats_free(struct ath12k_fw_stats *stats); +void ath12k_fw_stats_reset(struct ath12k *ar); +struct reserved_mem *ath12k_core_get_reserved_mem(struct ath12k_base *ab, + int index); static inline const char *ath12k_scan_state_str(enum ath12k_scan_state state) { @@ -1152,8 +1380,16 @@ static inline void ath12k_core_create_firmware_path(struct ath12k_base *ab, const char *filename, void *buf, size_t buf_len) { - snprintf(buf, buf_len, "%s/%s/%s", ATH12K_FW_DIR, - ab->hw_params->fw.dir, filename); + const char *fw_name = NULL; + + of_property_read_string(ab->dev->of_node, "firmware-name", &fw_name); + + if (fw_name && strncmp(filename, "board", 5)) + snprintf(buf, buf_len, "%s/%s/%s/%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, fw_name, filename); + else + snprintf(buf, buf_len, "%s/%s/%s", ATH12K_FW_DIR, + ab->hw_params->fw.dir, filename); } static inline const char *ath12k_bus_str(enum ath12k_bus bus) @@ -1161,6 +1397,8 @@ static inline const char *ath12k_bus_str(enum ath12k_bus bus) switch (bus) { case ATH12K_BUS_PCI: return "pci"; + case ATH12K_BUS_AHB: + return "ahb"; } return "unknown"; @@ -1210,20 +1448,6 @@ static inline struct ath12k_hw_group *ath12k_ab_to_ag(struct ath12k_base *ab) return ab->ag; } -static inline void ath12k_core_started(struct ath12k_base *ab) -{ - lockdep_assert_held(&ab->ag->mutex); - - ab->ag->num_started++; -} - -static inline void ath12k_core_stopped(struct ath12k_base *ab) -{ - lockdep_assert_held(&ab->ag->mutex); - - ab->ag->num_started--; -} - static inline struct ath12k_base *ath12k_ag_to_ab(struct ath12k_hw_group *ag, u8 device_id) { diff --git a/drivers/net/wireless/ath/ath12k/debug.c b/drivers/net/wireless/ath/ath12k/debug.c index ff6eaeafa092..5ce100cd9a9d 100644 --- a/drivers/net/wireless/ath/ath12k/debug.c +++ b/drivers/net/wireless/ath/ath12k/debug.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/vmalloc.h> @@ -63,8 +63,10 @@ void __ath12k_dbg(struct ath12k_base *ab, enum ath12k_debug_mask mask, vaf.fmt = fmt; vaf.va = &args; - if (ath12k_debug_mask & mask) + if (likely(ab)) dev_printk(KERN_DEBUG, ab->dev, "%pV", &vaf); + else + printk(KERN_DEBUG "ath12k: %pV", &vaf); /* TODO: trace log */ diff --git a/drivers/net/wireless/ath/ath12k/debug.h b/drivers/net/wireless/ath/ath12k/debug.h index 90e801136bc6..48916e4e1f60 100644 --- a/drivers/net/wireless/ath/ath12k/debug.h +++ b/drivers/net/wireless/ath/ath12k/debug.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH12K_DEBUG_H_ @@ -37,6 +37,7 @@ __printf(2, 3) void __ath12k_warn(struct device *dev, const char *fmt, ...); #define ath12k_hw_warn(ah, fmt, ...) __ath12k_warn((ah)->dev, fmt, ##__VA_ARGS__) extern unsigned int ath12k_debug_mask; +extern bool ath12k_ftm_mode; #ifdef CONFIG_ATH12K_DEBUG __printf(3, 4) void __ath12k_dbg(struct ath12k_base *ab, @@ -61,11 +62,14 @@ static inline void ath12k_dbg_dump(struct ath12k_base *ab, } #endif /* CONFIG_ATH12K_DEBUG */ -#define ath12k_dbg(ar, dbg_mask, fmt, ...) \ +#define ath12k_dbg(ab, dbg_mask, fmt, ...) \ do { \ typeof(dbg_mask) mask = (dbg_mask); \ if (ath12k_debug_mask & mask) \ - __ath12k_dbg(ar, mask, fmt, ##__VA_ARGS__); \ + __ath12k_dbg(ab, mask, fmt, ##__VA_ARGS__); \ } while (0) +#define ath12k_generic_dbg(dbg_mask, fmt, ...) \ + ath12k_dbg(NULL, dbg_mask, fmt, ##__VA_ARGS__) + #endif /* _ATH12K_DEBUG_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/debugfs.c b/drivers/net/wireless/ath/ath12k/debugfs.c index d4b32d1a431c..dd624d73b8b2 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.c +++ b/drivers/net/wireless/ath/ath12k/debugfs.c @@ -1,10 +1,12 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" +#include "dp_tx.h" +#include "debug.h" #include "debugfs.h" #include "debugfs_htt_stats.h" @@ -31,6 +33,1187 @@ static const struct file_operations fops_simulate_radar = { .open = simple_open }; +static ssize_t ath12k_read_simulate_fw_crash(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char buf[] = + "To simulate firmware crash write one of the keywords to this file:\n" + "`assert` - send WMI_FORCE_FW_HANG_CMDID to firmware to cause assert.\n"; + + return simple_read_from_buffer(user_buf, count, ppos, buf, strlen(buf)); +} + +static ssize_t +ath12k_write_simulate_fw_crash(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k_base *ab = file->private_data; + struct ath12k_pdev *pdev; + struct ath12k *ar = NULL; + char buf[32] = {0}; + int i, ret; + ssize_t rc; + + /* filter partial writes and invalid commands */ + if (*ppos != 0 || count >= sizeof(buf) || count == 0) + return -EINVAL; + + rc = simple_write_to_buffer(buf, sizeof(buf) - 1, ppos, user_buf, count); + if (rc < 0) + return rc; + + /* drop the possible '\n' from the end */ + if (buf[*ppos - 1] == '\n') + buf[*ppos - 1] = '\0'; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + if (ar) + break; + } + + if (!ar) + return -ENETDOWN; + + if (!strcmp(buf, "assert")) { + ath12k_info(ab, "simulating firmware assert crash\n"); + ret = ath12k_wmi_force_fw_hang_cmd(ar, + ATH12K_WMI_FW_HANG_ASSERT_TYPE, + ATH12K_WMI_FW_HANG_DELAY); + } else { + return -EINVAL; + } + + if (ret) { + ath12k_warn(ab, "failed to simulate firmware crash: %d\n", ret); + return ret; + } + + return count; +} + +static const struct file_operations fops_simulate_fw_crash = { + .read = ath12k_read_simulate_fw_crash, + .write = ath12k_write_simulate_fw_crash, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath12k_write_tpc_stats_type(struct file *file, + const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + u8 type; + int ret; + + ret = kstrtou8_from_user(user_buf, count, 0, &type); + if (ret) + return ret; + + if (type >= WMI_HALPHY_PDEV_TX_STATS_MAX) + return -EINVAL; + + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_stats_type = type; + spin_unlock_bh(&ar->data_lock); + + return count; +} + +static int ath12k_debug_tpc_stats_request(struct ath12k *ar) +{ + enum wmi_halphy_ctrl_path_stats_id tpc_stats_sub_id; + struct ath12k_base *ab = ar->ab; + int ret; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + reinit_completion(&ar->debug.tpc_complete); + + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_request = true; + tpc_stats_sub_id = ar->debug.tpc_stats_type; + spin_unlock_bh(&ar->data_lock); + + ret = ath12k_wmi_send_tpc_stats_request(ar, tpc_stats_sub_id); + if (ret) { + ath12k_warn(ab, "failed to request pdev tpc stats: %d\n", ret); + spin_lock_bh(&ar->data_lock); + ar->debug.tpc_request = false; + spin_unlock_bh(&ar->data_lock); + return ret; + } + + return 0; +} + +static int ath12k_get_tpc_ctl_mode_idx(struct wmi_tpc_stats_arg *tpc_stats, + enum wmi_tpc_pream_bw pream_bw, int *mode_idx) +{ + u32 chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq); + u8 band; + + band = ((chan_freq > ATH12K_MIN_6GHZ_FREQ) ? NL80211_BAND_6GHZ : + ((chan_freq > ATH12K_MIN_5GHZ_FREQ) ? NL80211_BAND_5GHZ : + NL80211_BAND_2GHZ)); + + if (band == NL80211_BAND_5GHZ || band == NL80211_BAND_6GHZ) { + switch (pream_bw) { + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_VHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_EHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HT40: + case WMI_TPC_PREAM_VHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_EHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_VHT80: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT60: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20; + break; + case WMI_TPC_PREAM_HE80: + case WMI_TPC_PREAM_EHT80: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_VHT160: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT120: + case WMI_TPC_PREAM_EHT140: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20; + break; + case WMI_TPC_PREAM_HE160: + case WMI_TPC_PREAM_EHT160: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ; + break; + case WMI_TPC_PREAM_EHT200: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120; + break; + case WMI_TPC_PREAM_EHT240: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80; + break; + case WMI_TPC_PREAM_EHT280: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40; + break; + case WMI_TPC_PREAM_EHT320: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ; + break; + default: + /* for 5GHZ and 6GHZ, default case will be for OFDM */ + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ; + break; + } + } else { + switch (pream_bw) { + case WMI_TPC_PREAM_OFDM: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ; + break; + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_VHT20: + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_EHT20: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ; + break; + case WMI_TPC_PREAM_HT40: + case WMI_TPC_PREAM_VHT40: + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_EHT40: + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ; + break; + default: + /* for 2GHZ, default case will be CCK */ + *mode_idx = ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ; + break; + } + } + + return 0; +} + +static s16 ath12k_tpc_get_rate(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + u32 rate_idx, u32 num_chains, u32 rate_code, + enum wmi_tpc_pream_bw pream_bw, + enum wmi_halphy_ctrl_path_stats_id type, + u32 eht_rate_idx) +{ + u32 tot_nss, tot_modes, txbf_on_off, index_offset1, index_offset2, index_offset3; + u8 chain_idx, stm_idx, num_streams; + bool is_mu, txbf_enabled = 0; + s8 rates_ctl_min, tpc_ctl; + s16 rates, tpc, reg_pwr; + u16 rate1, rate2; + int mode, ret; + + num_streams = 1 + ATH12K_HW_NSS(rate_code); + chain_idx = num_chains - 1; + stm_idx = num_streams - 1; + mode = -1; + + ret = ath12k_get_tpc_ctl_mode_idx(tpc_stats, pream_bw, &mode); + if (ret) { + ath12k_warn(ar->ab, "Invalid mode index received\n"); + tpc = TPC_INVAL; + goto out; + } + + if (num_chains < num_streams) { + tpc = TPC_INVAL; + goto out; + } + + if (le32_to_cpu(tpc_stats->tpc_config.num_tx_chain) <= 1) { + tpc = TPC_INVAL; + goto out; + } + + if (type == WMI_HALPHY_PDEV_TX_SUTXBF_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) + txbf_enabled = 1; + + if (type == WMI_HALPHY_PDEV_TX_MU_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) { + is_mu = true; + } else { + is_mu = false; + } + + /* Below is the min calculation of ctl array, rates array and + * regulator power table. tpc is minimum of all 3 + */ + if (pream_bw >= WMI_TPC_PREAM_EHT20 && pream_bw <= WMI_TPC_PREAM_EHT320) { + rate2 = tpc_stats->rates_array2.rate_array[eht_rate_idx]; + if (is_mu) + rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_MU); + else + rates = u32_get_bits(rate2, ATH12K_TPC_RATE_ARRAY_SU); + } else { + rate1 = tpc_stats->rates_array1.rate_array[rate_idx]; + if (is_mu) + rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_MU); + else + rates = u32_get_bits(rate1, ATH12K_TPC_RATE_ARRAY_SU); + } + + if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) { + tot_nss = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d1); + tot_modes = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d2); + txbf_on_off = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.d3); + index_offset1 = txbf_on_off * tot_modes * tot_nss; + index_offset2 = tot_modes * tot_nss; + index_offset3 = tot_nss; + + tpc_ctl = *(tpc_stats->ctl_array.ctl_pwr_table + + chain_idx * index_offset1 + txbf_enabled * index_offset2 + + mode * index_offset3 + stm_idx); + } else { + tpc_ctl = TPC_MAX; + ath12k_warn(ar->ab, + "ctl array for tpc stats not received from fw\n"); + } + + rates_ctl_min = min_t(s16, rates, tpc_ctl); + + reg_pwr = tpc_stats->max_reg_allowed_power.reg_pwr_array[chain_idx]; + + if (reg_pwr < 0) + reg_pwr = TPC_INVAL; + + tpc = min_t(s16, rates_ctl_min, reg_pwr); + + /* MODULATION_LIMIT is the maximum power limit,tpc should not exceed + * modulation limit even if min tpc of all three array is greater + * modulation limit + */ + tpc = min_t(s16, tpc, MODULATION_LIMIT); + +out: + return tpc; +} + +static u16 ath12k_get_ratecode(u16 pream_idx, u16 nss, u16 mcs_rate) +{ + u16 mode_type = ~0; + + /* Below assignments are just for printing purpose only */ + switch (pream_idx) { + case WMI_TPC_PREAM_CCK: + mode_type = WMI_RATE_PREAMBLE_CCK; + break; + case WMI_TPC_PREAM_OFDM: + mode_type = WMI_RATE_PREAMBLE_OFDM; + break; + case WMI_TPC_PREAM_HT20: + case WMI_TPC_PREAM_HT40: + mode_type = WMI_RATE_PREAMBLE_HT; + break; + case WMI_TPC_PREAM_VHT20: + case WMI_TPC_PREAM_VHT40: + case WMI_TPC_PREAM_VHT80: + case WMI_TPC_PREAM_VHT160: + mode_type = WMI_RATE_PREAMBLE_VHT; + break; + case WMI_TPC_PREAM_HE20: + case WMI_TPC_PREAM_HE40: + case WMI_TPC_PREAM_HE80: + case WMI_TPC_PREAM_HE160: + mode_type = WMI_RATE_PREAMBLE_HE; + break; + case WMI_TPC_PREAM_EHT20: + case WMI_TPC_PREAM_EHT40: + case WMI_TPC_PREAM_EHT60: + case WMI_TPC_PREAM_EHT80: + case WMI_TPC_PREAM_EHT120: + case WMI_TPC_PREAM_EHT140: + case WMI_TPC_PREAM_EHT160: + case WMI_TPC_PREAM_EHT200: + case WMI_TPC_PREAM_EHT240: + case WMI_TPC_PREAM_EHT280: + case WMI_TPC_PREAM_EHT320: + mode_type = WMI_RATE_PREAMBLE_EHT; + if (mcs_rate == 0 || mcs_rate == 1) + mcs_rate += 14; + else + mcs_rate -= 2; + break; + default: + return mode_type; + } + return ((mode_type << 8) | ((nss & 0x7) << 5) | (mcs_rate & 0x1F)); +} + +static bool ath12k_he_supports_extra_mcs(struct ath12k *ar, int freq) +{ + struct ath12k_pdev_cap *cap = &ar->pdev->cap; + struct ath12k_band_cap *cap_band; + bool extra_mcs_supported; + + if (freq <= ATH12K_2GHZ_MAX_FREQUENCY) + cap_band = &cap->band[NL80211_BAND_2GHZ]; + else if (freq <= ATH12K_5GHZ_MAX_FREQUENCY) + cap_band = &cap->band[NL80211_BAND_5GHZ]; + else + cap_band = &cap->band[NL80211_BAND_6GHZ]; + + extra_mcs_supported = u32_get_bits(cap_band->he_cap_info[1], + HE_EXTRA_MCS_SUPPORT); + return extra_mcs_supported; +} + +static int ath12k_tpc_fill_pream(struct ath12k *ar, char *buf, int buf_len, int len, + enum wmi_tpc_pream_bw pream_bw, u32 max_rix, + int max_nss, int max_rates, int pream_type, + enum wmi_halphy_ctrl_path_stats_id tpc_type, + int rate_idx, int eht_rate_idx) +{ + struct wmi_tpc_stats_arg *tpc_stats = ar->debug.tpc_stats; + int nss, rates, chains; + u8 active_tx_chains; + u16 rate_code; + s16 tpc; + + static const char *const pream_str[] = { + [WMI_TPC_PREAM_CCK] = "CCK", + [WMI_TPC_PREAM_OFDM] = "OFDM", + [WMI_TPC_PREAM_HT20] = "HT20", + [WMI_TPC_PREAM_HT40] = "HT40", + [WMI_TPC_PREAM_VHT20] = "VHT20", + [WMI_TPC_PREAM_VHT40] = "VHT40", + [WMI_TPC_PREAM_VHT80] = "VHT80", + [WMI_TPC_PREAM_VHT160] = "VHT160", + [WMI_TPC_PREAM_HE20] = "HE20", + [WMI_TPC_PREAM_HE40] = "HE40", + [WMI_TPC_PREAM_HE80] = "HE80", + [WMI_TPC_PREAM_HE160] = "HE160", + [WMI_TPC_PREAM_EHT20] = "EHT20", + [WMI_TPC_PREAM_EHT40] = "EHT40", + [WMI_TPC_PREAM_EHT60] = "EHT60", + [WMI_TPC_PREAM_EHT80] = "EHT80", + [WMI_TPC_PREAM_EHT120] = "EHT120", + [WMI_TPC_PREAM_EHT140] = "EHT140", + [WMI_TPC_PREAM_EHT160] = "EHT160", + [WMI_TPC_PREAM_EHT200] = "EHT200", + [WMI_TPC_PREAM_EHT240] = "EHT240", + [WMI_TPC_PREAM_EHT280] = "EHT280", + [WMI_TPC_PREAM_EHT320] = "EHT320"}; + + active_tx_chains = ar->num_tx_chains; + + for (nss = 0; nss < max_nss; nss++) { + for (rates = 0; rates < max_rates; rates++, rate_idx++, max_rix++) { + /* FW send extra MCS(10&11) for VHT and HE rates, + * this is not used. Hence skipping it here + */ + if (pream_type == WMI_RATE_PREAMBLE_VHT && + rates > ATH12K_VHT_MCS_MAX) + continue; + + if (pream_type == WMI_RATE_PREAMBLE_HE && + rates > ATH12K_HE_MCS_MAX) + continue; + + if (pream_type == WMI_RATE_PREAMBLE_EHT && + rates > ATH12K_EHT_MCS_MAX) + continue; + + rate_code = ath12k_get_ratecode(pream_bw, nss, rates); + len += scnprintf(buf + len, buf_len - len, + "%d\t %s\t 0x%03x\t", max_rix, + pream_str[pream_bw], rate_code); + + for (chains = 0; chains < active_tx_chains; chains++) { + if (nss > chains) { + len += scnprintf(buf + len, + buf_len - len, + "\t%s", "NA"); + } else { + tpc = ath12k_tpc_get_rate(ar, tpc_stats, + rate_idx, chains + 1, + rate_code, pream_bw, + tpc_type, + eht_rate_idx); + + if (tpc == TPC_INVAL) { + len += scnprintf(buf + len, + buf_len - len, "\tNA"); + } else { + len += scnprintf(buf + len, + buf_len - len, "\t%d", + tpc); + } + } + } + len += scnprintf(buf + len, buf_len - len, "\n"); + + if (pream_type == WMI_RATE_PREAMBLE_EHT) + /*For fetching the next eht rates pwr from rates array2*/ + ++eht_rate_idx; + } + } + + return len; +} + +static int ath12k_tpc_stats_print(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + char *buf, size_t len, + enum wmi_halphy_ctrl_path_stats_id type) +{ + u32 eht_idx = 0, pream_idx = 0, rate_pream_idx = 0, total_rates = 0, max_rix = 0; + u32 chan_freq, num_tx_chain, caps, i, j = 1; + size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE; + u8 nss, active_tx_chains; + bool he_ext_mcs; + static const char *const type_str[WMI_HALPHY_PDEV_TX_STATS_MAX] = { + [WMI_HALPHY_PDEV_TX_SU_STATS] = "SU", + [WMI_HALPHY_PDEV_TX_SUTXBF_STATS] = "SU WITH TXBF", + [WMI_HALPHY_PDEV_TX_MU_STATS] = "MU", + [WMI_HALPHY_PDEV_TX_MUTXBF_STATS] = "MU WITH TXBF"}; + + u8 max_rates[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = ATH12K_CCK_RATES, + [WMI_TPC_PREAM_OFDM] = ATH12K_OFDM_RATES, + [WMI_TPC_PREAM_HT20] = ATH12K_HT_RATES, + [WMI_TPC_PREAM_HT40] = ATH12K_HT_RATES, + [WMI_TPC_PREAM_VHT20] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT40] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT80] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_VHT160] = ATH12K_VHT_RATES, + [WMI_TPC_PREAM_HE20] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE40] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE80] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_HE160] = ATH12K_HE_RATES, + [WMI_TPC_PREAM_EHT20] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT40] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT60] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT80] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT120] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT140] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT160] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT200] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT240] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT280] = ATH12K_EHT_RATES, + [WMI_TPC_PREAM_EHT320] = ATH12K_EHT_RATES}; + static const u8 max_nss[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = ATH12K_NSS_1, + [WMI_TPC_PREAM_OFDM] = ATH12K_NSS_1, + [WMI_TPC_PREAM_HT20] = ATH12K_NSS_4, + [WMI_TPC_PREAM_HT40] = ATH12K_NSS_4, + [WMI_TPC_PREAM_VHT20] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT40] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT80] = ATH12K_NSS_8, + [WMI_TPC_PREAM_VHT160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_HE20] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE40] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE80] = ATH12K_NSS_8, + [WMI_TPC_PREAM_HE160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT20] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT40] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT60] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT80] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT120] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT140] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT160] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT200] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT240] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT280] = ATH12K_NSS_4, + [WMI_TPC_PREAM_EHT320] = ATH12K_NSS_4}; + + u16 rate_idx[WMI_TPC_PREAM_MAX] = {}, eht_rate_idx[WMI_TPC_PREAM_MAX] = {}; + static const u8 pream_type[WMI_TPC_PREAM_MAX] = { + [WMI_TPC_PREAM_CCK] = WMI_RATE_PREAMBLE_CCK, + [WMI_TPC_PREAM_OFDM] = WMI_RATE_PREAMBLE_OFDM, + [WMI_TPC_PREAM_HT20] = WMI_RATE_PREAMBLE_HT, + [WMI_TPC_PREAM_HT40] = WMI_RATE_PREAMBLE_HT, + [WMI_TPC_PREAM_VHT20] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT40] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT80] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_VHT160] = WMI_RATE_PREAMBLE_VHT, + [WMI_TPC_PREAM_HE20] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE40] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE80] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_HE160] = WMI_RATE_PREAMBLE_HE, + [WMI_TPC_PREAM_EHT20] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT40] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT60] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT80] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT120] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT140] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT160] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT200] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT240] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT280] = WMI_RATE_PREAMBLE_EHT, + [WMI_TPC_PREAM_EHT320] = WMI_RATE_PREAMBLE_EHT}; + + chan_freq = le32_to_cpu(tpc_stats->tpc_config.chan_freq); + num_tx_chain = le32_to_cpu(tpc_stats->tpc_config.num_tx_chain); + caps = le32_to_cpu(tpc_stats->tpc_config.caps); + + active_tx_chains = ar->num_tx_chains; + he_ext_mcs = ath12k_he_supports_extra_mcs(ar, chan_freq); + + /* mcs 12&13 is sent by FW for certain HWs in rate array, skipping it as + * it is not supported + */ + if (he_ext_mcs) { + for (i = WMI_TPC_PREAM_HE20; i <= WMI_TPC_PREAM_HE160; ++i) + max_rates[i] = ATH12K_HE_RATES; + } + + if (type == WMI_HALPHY_PDEV_TX_MU_STATS || + type == WMI_HALPHY_PDEV_TX_MUTXBF_STATS) { + pream_idx = WMI_TPC_PREAM_VHT20; + + for (i = WMI_TPC_PREAM_CCK; i <= WMI_TPC_PREAM_HT40; ++i) + max_rix += max_nss[i] * max_rates[i]; + } + /* Enumerate all the rate indices */ + for (i = rate_pream_idx + 1; i < WMI_TPC_PREAM_MAX; i++) { + nss = (max_nss[i - 1] < num_tx_chain ? + max_nss[i - 1] : num_tx_chain); + + rate_idx[i] = rate_idx[i - 1] + max_rates[i - 1] * nss; + + if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) { + eht_rate_idx[j] = eht_rate_idx[j - 1] + max_rates[i] * nss; + ++j; + } + } + + for (i = 0; i < WMI_TPC_PREAM_MAX; i++) { + nss = (max_nss[i] < num_tx_chain ? + max_nss[i] : num_tx_chain); + total_rates += max_rates[i] * nss; + } + + len += scnprintf(buf + len, buf_len - len, + "No.of rates-%d\n", total_rates); + + len += scnprintf(buf + len, buf_len - len, + "**************** %s ****************\n", + type_str[type]); + len += scnprintf(buf + len, buf_len - len, + "\t\t\t\tTPC values for Active chains\n"); + len += scnprintf(buf + len, buf_len - len, + "Rate idx Preamble Rate code"); + + for (i = 1; i <= active_tx_chains; ++i) { + len += scnprintf(buf + len, buf_len - len, + "\t%d-Chain", i); + } + + len += scnprintf(buf + len, buf_len - len, "\n"); + for (i = pream_idx; i < WMI_TPC_PREAM_MAX; i++) { + if (chan_freq <= 2483) { + if (i == WMI_TPC_PREAM_VHT80 || + i == WMI_TPC_PREAM_VHT160 || + i == WMI_TPC_PREAM_HE80 || + i == WMI_TPC_PREAM_HE160 || + (i >= WMI_TPC_PREAM_EHT60 && + i <= WMI_TPC_PREAM_EHT320)) { + max_rix += max_nss[i] * max_rates[i]; + continue; + } + } else { + if (i == WMI_TPC_PREAM_CCK) { + max_rix += max_rates[i]; + continue; + } + } + + nss = (max_nss[i] < ar->num_tx_chains ? max_nss[i] : ar->num_tx_chains); + + if (!(caps & + (1 << ATH12K_TPC_STATS_SUPPORT_BE_PUNC))) { + if (i == WMI_TPC_PREAM_EHT60 || i == WMI_TPC_PREAM_EHT120 || + i == WMI_TPC_PREAM_EHT140 || i == WMI_TPC_PREAM_EHT200 || + i == WMI_TPC_PREAM_EHT240 || i == WMI_TPC_PREAM_EHT280) { + max_rix += max_nss[i] * max_rates[i]; + continue; + } + } + + len = ath12k_tpc_fill_pream(ar, buf, buf_len, len, i, max_rix, nss, + max_rates[i], pream_type[i], + type, rate_idx[i], eht_rate_idx[eht_idx]); + + if (pream_type[i] == WMI_RATE_PREAMBLE_EHT) + /*For fetch the next index eht rates from rates array2*/ + ++eht_idx; + + max_rix += max_nss[i] * max_rates[i]; + } + return len; +} + +static void ath12k_tpc_stats_fill(struct ath12k *ar, + struct wmi_tpc_stats_arg *tpc_stats, + char *buf) +{ + size_t buf_len = ATH12K_TPC_STATS_BUF_SIZE; + struct wmi_tpc_config_params *tpc; + size_t len = 0; + + if (!tpc_stats) { + ath12k_warn(ar->ab, "failed to find tpc stats\n"); + return; + } + + spin_lock_bh(&ar->data_lock); + + tpc = &tpc_stats->tpc_config; + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, + "*************** TPC config **************\n"); + len += scnprintf(buf + len, buf_len - len, + "* powers are in 0.25 dBm steps\n"); + len += scnprintf(buf + len, buf_len - len, + "reg domain-%d\t\tchan freq-%d\n", + tpc->reg_domain, tpc->chan_freq); + len += scnprintf(buf + len, buf_len - len, + "power limit-%d\t\tmax reg-domain Power-%d\n", + le32_to_cpu(tpc->twice_max_reg_power) / 2, tpc->power_limit); + len += scnprintf(buf + len, buf_len - len, + "No.of tx chain-%d\t", + ar->num_tx_chains); + + ath12k_tpc_stats_print(ar, tpc_stats, buf, len, + ar->debug.tpc_stats_type); + + spin_unlock_bh(&ar->data_lock); +} + +static int ath12k_open_tpc_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah->state != ATH12K_HW_STATE_ON) { + ath12k_warn(ar->ab, "Interface not up\n"); + return -ENETDOWN; + } + + void *buf __free(kfree) = kzalloc(ATH12K_TPC_STATS_BUF_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ath12k_debug_tpc_stats_request(ar); + if (ret) { + ath12k_warn(ar->ab, "failed to request tpc stats: %d\n", + ret); + return ret; + } + + if (!wait_for_completion_timeout(&ar->debug.tpc_complete, TPC_STATS_WAIT_TIME)) { + spin_lock_bh(&ar->data_lock); + ath12k_wmi_free_tpc_stats_mem(ar); + ar->debug.tpc_request = false; + spin_unlock_bh(&ar->data_lock); + return -ETIMEDOUT; + } + + ath12k_tpc_stats_fill(ar, ar->debug.tpc_stats, buf); + file->private_data = no_free_ptr(buf); + + spin_lock_bh(&ar->data_lock); + ath12k_wmi_free_tpc_stats_mem(ar); + spin_unlock_bh(&ar->data_lock); + + return 0; +} + +static ssize_t ath12k_read_tpc_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static int ath12k_release_tpc_stats(struct inode *inode, + struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static const struct file_operations fops_tpc_stats = { + .open = ath12k_open_tpc_stats, + .release = ath12k_release_tpc_stats, + .read = ath12k_read_tpc_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static const struct file_operations fops_tpc_stats_type = { + .write = ath12k_write_tpc_stats_type, + .open = simple_open, + .llseek = default_llseek, +}; + +static ssize_t ath12k_write_extd_rx_stats(struct file *file, + const char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + struct htt_rx_ring_tlv_filter tlv_filter = {0}; + u32 ring_id, rx_filter = 0; + bool enable; + int ret, i; + + if (kstrtobool_from_user(ubuf, count, &enable)) + return -EINVAL; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + + if (!ar->ab->hw_params->rxdma1_enable) { + ret = count; + goto exit; + } + + if (ar->ah->state != ATH12K_HW_STATE_ON) { + ret = -ENETDOWN; + goto exit; + } + + if (enable == ar->debug.extd_rx_stats) { + ret = count; + goto exit; + } + + if (enable) { + rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE; + rx_filter |= HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO; + + tlv_filter.rx_filter = rx_filter; + tlv_filter.pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0; + tlv_filter.pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1; + tlv_filter.pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2; + tlv_filter.pkt_filter_flags3 = HTT_RX_FP_CTRL_FILTER_FLASG3 | + HTT_RX_FP_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath12k_mac_mon_status_filter_default; + } + + ar->debug.rx_filter = tlv_filter.rx_filter; + + for (i = 0; i < ar->ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_warn(ar->ab, "failed to set rx filter for monitor status ring\n"); + goto exit; + } + } + + ar->debug.extd_rx_stats = !!enable; + ret = count; +exit: + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + return ret; +} + +static ssize_t ath12k_read_extd_rx_stats(struct file *file, + char __user *ubuf, + size_t count, loff_t *ppos) +{ + struct ath12k *ar = file->private_data; + char buf[32]; + int len = 0; + + wiphy_lock(ath12k_ar_to_hw(ar)->wiphy); + len = scnprintf(buf, sizeof(buf) - len, "%d\n", + ar->debug.extd_rx_stats); + wiphy_unlock(ath12k_ar_to_hw(ar)->wiphy); + + return simple_read_from_buffer(ubuf, count, ppos, buf, len); +} + +static const struct file_operations fops_extd_rx_stats = { + .read = ath12k_read_extd_rx_stats, + .write = ath12k_write_extd_rx_stats, + .open = simple_open, +}; + +static int ath12k_open_link_stats(struct inode *inode, struct file *file) +{ + struct ath12k_vif *ahvif = inode->i_private; + size_t len = 0, buf_len = (PAGE_SIZE * 2); + struct ath12k_link_stats linkstat; + struct ath12k_link_vif *arvif; + unsigned long links_map; + struct wiphy *wiphy; + int link_id, i; + char *buf; + + if (!ahvif) + return -EINVAL; + + buf = kzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + wiphy = ahvif->ah->hw->wiphy; + wiphy_lock(wiphy); + + links_map = ahvif->links_map; + for_each_set_bit(link_id, &links_map, + IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = rcu_dereference_protected(ahvif->link[link_id], + lockdep_is_held(&wiphy->mtx)); + + spin_lock_bh(&arvif->link_stats_lock); + linkstat = arvif->link_stats; + spin_unlock_bh(&arvif->link_stats_lock); + + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Unicast Frames Enqueued = %d\n", + link_id, linkstat.tx_enqueued); + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Broadcast Frames Enqueued = %d\n", + link_id, linkstat.tx_bcast_mcast); + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Frames Completed = %d\n", + link_id, linkstat.tx_completed); + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Frames Dropped = %d\n", + link_id, linkstat.tx_dropped); + + len += scnprintf(buf + len, buf_len - len, + "link[%d] Tx Frame descriptor Encap Type = ", + link_id); + + len += scnprintf(buf + len, buf_len - len, + " raw:%d", + linkstat.tx_encap_type[0]); + + len += scnprintf(buf + len, buf_len - len, + " native_wifi:%d", + linkstat.tx_encap_type[1]); + + len += scnprintf(buf + len, buf_len - len, + " ethernet:%d", + linkstat.tx_encap_type[2]); + + len += scnprintf(buf + len, buf_len - len, + "\nlink[%d] Tx Frame descriptor Encrypt Type = ", + link_id); + + for (i = 0; i < HAL_ENCRYPT_TYPE_MAX; i++) { + len += scnprintf(buf + len, buf_len - len, + " %d:%d", i, + linkstat.tx_encrypt_type[i]); + } + len += scnprintf(buf + len, buf_len - len, + "\nlink[%d] Tx Frame descriptor Type = buffer:%d extension:%d\n", + link_id, linkstat.tx_desc_type[0], + linkstat.tx_desc_type[1]); + + len += scnprintf(buf + len, buf_len - len, + "------------------------------------------------------\n"); + } + + wiphy_unlock(wiphy); + + file->private_data = buf; + + return 0; +} + +static int ath12k_release_link_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static ssize_t ath12k_read_link_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations ath12k_fops_link_stats = { + .open = ath12k_open_link_stats, + .release = ath12k_release_link_stats, + .read = ath12k_read_link_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + + debugfs_create_file("link_stats", 0400, vif->debugfs_dir, ahvif, + &ath12k_fops_link_stats); +} + +static ssize_t ath12k_debugfs_dump_device_dp_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ath12k_base *ab = file->private_data; + struct ath12k_device_dp_stats *device_stats = &ab->device_stats; + int len = 0, i, j, ret; + struct ath12k *ar; + const int size = 4096; + static const char *rxdma_err[HAL_REO_ENTR_RING_RXDMA_ECODE_MAX] = { + [HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR] = "Overflow", + [HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR] = "MPDU len", + [HAL_REO_ENTR_RING_RXDMA_ECODE_FCS_ERR] = "FCS", + [HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR] = "Decrypt", + [HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR] = "TKIP MIC", + [HAL_REO_ENTR_RING_RXDMA_ECODE_UNECRYPTED_ERR] = "Unencrypt", + [HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LEN_ERR] = "MSDU len", + [HAL_REO_ENTR_RING_RXDMA_ECODE_MSDU_LIMIT_ERR] = "MSDU limit", + [HAL_REO_ENTR_RING_RXDMA_ECODE_WIFI_PARSE_ERR] = "WiFi parse", + [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_PARSE_ERR] = "AMSDU parse", + [HAL_REO_ENTR_RING_RXDMA_ECODE_SA_TIMEOUT_ERR] = "SA timeout", + [HAL_REO_ENTR_RING_RXDMA_ECODE_DA_TIMEOUT_ERR] = "DA timeout", + [HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR] = "Flow timeout", + [HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR] = "Flush req", + [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_FRAG_ERR] = "AMSDU frag", + [HAL_REO_ENTR_RING_RXDMA_ECODE_MULTICAST_ECHO_ERR] = "Multicast echo", + [HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_MISMATCH_ERR] = "AMSDU mismatch", + [HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR] = "Unauth WDS", + [HAL_REO_ENTR_RING_RXDMA_ECODE_GRPCAST_AMSDU_WDS_ERR] = "AMSDU or WDS"}; + + static const char *reo_err[HAL_REO_DEST_RING_ERROR_CODE_MAX] = { + [HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO] = "Desc addr zero", + [HAL_REO_DEST_RING_ERROR_CODE_DESC_INVALID] = "Desc inval", + [HAL_REO_DEST_RING_ERROR_CODE_AMPDU_IN_NON_BA] = "AMPDU in non BA", + [HAL_REO_DEST_RING_ERROR_CODE_NON_BA_DUPLICATE] = "Non BA dup", + [HAL_REO_DEST_RING_ERROR_CODE_BA_DUPLICATE] = "BA dup", + [HAL_REO_DEST_RING_ERROR_CODE_FRAME_2K_JUMP] = "Frame 2k jump", + [HAL_REO_DEST_RING_ERROR_CODE_BAR_2K_JUMP] = "BAR 2k jump", + [HAL_REO_DEST_RING_ERROR_CODE_FRAME_OOR] = "Frame OOR", + [HAL_REO_DEST_RING_ERROR_CODE_BAR_OOR] = "BAR OOR", + [HAL_REO_DEST_RING_ERROR_CODE_NO_BA_SESSION] = "No BA session", + [HAL_REO_DEST_RING_ERROR_CODE_FRAME_SN_EQUALS_SSN] = "Frame SN equal SSN", + [HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED] = "PN check fail", + [HAL_REO_DEST_RING_ERROR_CODE_2K_ERR_FLAG_SET] = "2k err", + [HAL_REO_DEST_RING_ERROR_CODE_PN_ERR_FLAG_SET] = "PN err", + [HAL_REO_DEST_RING_ERROR_CODE_DESC_BLOCKED] = "Desc blocked"}; + + static const char *wbm_rel_src[HAL_WBM_REL_SRC_MODULE_MAX] = { + [HAL_WBM_REL_SRC_MODULE_TQM] = "TQM", + [HAL_WBM_REL_SRC_MODULE_RXDMA] = "Rxdma", + [HAL_WBM_REL_SRC_MODULE_REO] = "Reo", + [HAL_WBM_REL_SRC_MODULE_FW] = "FW", + [HAL_WBM_REL_SRC_MODULE_SW] = "SW"}; + + char *buf __free(kfree) = kzalloc(size, GFP_KERNEL); + + if (!buf) + return -ENOMEM; + + len += scnprintf(buf + len, size - len, "DEVICE RX STATS:\n\n"); + len += scnprintf(buf + len, size - len, "err ring pkts: %u\n", + device_stats->err_ring_pkts); + len += scnprintf(buf + len, size - len, "Invalid RBM: %u\n\n", + device_stats->invalid_rbm); + len += scnprintf(buf + len, size - len, "RXDMA errors:\n"); + + for (i = 0; i < HAL_REO_ENTR_RING_RXDMA_ECODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + rxdma_err[i], device_stats->rxdma_error[i]); + + len += scnprintf(buf + len, size - len, "\nREO errors:\n"); + + for (i = 0; i < HAL_REO_DEST_RING_ERROR_CODE_MAX; i++) + len += scnprintf(buf + len, size - len, "%s: %u\n", + reo_err[i], device_stats->reo_error[i]); + + len += scnprintf(buf + len, size - len, "\nHAL REO errors:\n"); + + for (i = 0; i < DP_REO_DST_RING_MAX; i++) + len += scnprintf(buf + len, size - len, + "ring%d: %u\n", i, + device_stats->hal_reo_error[i]); + + len += scnprintf(buf + len, size - len, "\nDEVICE TX STATS:\n"); + len += scnprintf(buf + len, size - len, "\nTCL Ring Full Failures:\n"); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + len += scnprintf(buf + len, size - len, "ring%d: %u\n", + i, device_stats->tx_err.desc_na[i]); + + len += scnprintf(buf + len, size - len, + "\nMisc Transmit Failures: %d\n", + atomic_read(&device_stats->tx_err.misc_fail)); + + len += scnprintf(buf + len, size - len, "\ntx_wbm_rel_source:"); + + for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++) + len += scnprintf(buf + len, size - len, " %d:%u", + i, device_stats->tx_wbm_rel_source[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\ntqm_rel_reason:"); + + for (i = 0; i < MAX_TQM_RELEASE_REASON; i++) + len += scnprintf(buf + len, size - len, " %d:%u", + i, device_stats->tqm_rel_reason[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\nfw_tx_status:"); + + for (i = 0; i < MAX_FW_TX_STATUS; i++) + len += scnprintf(buf + len, size - len, " %d:%u", + i, device_stats->fw_tx_status[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\ntx_enqueued:"); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + len += scnprintf(buf + len, size - len, " %d:%u", i, + device_stats->tx_enqueued[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\ntx_completed:"); + + for (i = 0; i < DP_TCL_NUM_RING_MAX; i++) + len += scnprintf(buf + len, size - len, " %d:%u", + i, device_stats->tx_completed[i]); + + len += scnprintf(buf + len, size - len, "\n"); + + for (i = 0; i < ab->num_radios; i++) { + ar = ath12k_mac_get_ar_by_pdev_id(ab, DP_SW2HW_MACID(i)); + if (ar) { + len += scnprintf(buf + len, size - len, + "\nradio%d tx_pending: %u\n", i, + atomic_read(&ar->dp.num_tx_pending)); + } + } + + len += scnprintf(buf + len, size - len, "\nREO Rx Received:\n"); + + for (i = 0; i < DP_REO_DST_RING_MAX; i++) { + len += scnprintf(buf + len, size - len, "Ring%d:", i + 1); + + for (j = 0; j < ATH12K_MAX_DEVICES; j++) { + len += scnprintf(buf + len, size - len, + "\t%d:%u", j, + device_stats->reo_rx[i][j]); + } + + len += scnprintf(buf + len, size - len, "\n"); + } + + len += scnprintf(buf + len, size - len, "\nRx WBM REL SRC Errors:\n"); + + for (i = 0; i < HAL_WBM_REL_SRC_MODULE_MAX; i++) { + len += scnprintf(buf + len, size - len, "%s:", wbm_rel_src[i]); + + for (j = 0; j < ATH12K_MAX_DEVICES; j++) { + len += scnprintf(buf + len, + size - len, + "\t%d:%u", j, + device_stats->rx_wbm_rel_source[i][j]); + } + + len += scnprintf(buf + len, size - len, "\n"); + } + + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); + + return ret; +} + +static const struct file_operations fops_device_dp_stats = { + .read = ath12k_debugfs_dump_device_dp_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_pdev_create(struct ath12k_base *ab) +{ + debugfs_create_file("simulate_fw_crash", 0600, ab->debugfs_soc, ab, + &fops_simulate_fw_crash); + + debugfs_create_file("device_dp_stats", 0400, ab->debugfs_soc, ab, + &fops_device_dp_stats); +} + void ath12k_debugfs_soc_create(struct ath12k_base *ab) { bool dput_needed; @@ -68,6 +1251,278 @@ void ath12k_debugfs_soc_destroy(struct ath12k_base *ab) */ } +void +ath12k_debugfs_fw_stats_process(struct ath12k *ar, + struct ath12k_fw_stats *stats) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_pdev *pdev; + bool is_end; + static unsigned int num_vdev, num_bcn; + size_t total_vdevs_started = 0; + int i; + + if (stats->stats_id == WMI_REQUEST_VDEV_STAT) { + if (list_empty(&stats->vdevs)) { + ath12k_warn(ab, "empty vdev stats"); + return; + } + /* FW sends all the active VDEV stats irrespective of PDEV, + * hence limit until the count of all VDEVs started + */ + rcu_read_lock(); + for (i = 0; i < ab->num_radios; i++) { + pdev = rcu_dereference(ab->pdevs_active[i]); + if (pdev && pdev->ar) + total_vdevs_started += pdev->ar->num_started_vdevs; + } + rcu_read_unlock(); + + is_end = ((++num_vdev) == total_vdevs_started); + + list_splice_tail_init(&stats->vdevs, + &ar->fw_stats.vdevs); + + if (is_end) { + ar->fw_stats.fw_stats_done = true; + num_vdev = 0; + } + return; + } + if (stats->stats_id == WMI_REQUEST_BCN_STAT) { + if (list_empty(&stats->bcn)) { + ath12k_warn(ab, "empty beacon stats"); + return; + } + /* Mark end until we reached the count of all started VDEVs + * within the PDEV + */ + is_end = ((++num_bcn) == ar->num_started_vdevs); + + list_splice_tail_init(&stats->bcn, + &ar->fw_stats.bcn); + + if (is_end) { + ar->fw_stats.fw_stats_done = true; + num_bcn = 0; + } + } +} + +static int ath12k_open_vdev_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_fw_stats_req_params param; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (!ah) + return -ENETDOWN; + + if (ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + /* VDEV stats is always sent for all active VDEVs from FW */ + param.vdev_id = 0; + param.stats_id = WMI_REQUEST_VDEV_STAT; + + ret = ath12k_mac_get_fw_stats(ar, ¶m); + if (ret) { + ath12k_warn(ar->ab, "failed to request fw vdev stats: %d\n", ret); + return ret; + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_vdev_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_vdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_vdev_stats = { + .open = ath12k_open_vdev_stats, + .release = ath12k_release_vdev_stats, + .read = ath12k_read_vdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath12k_open_bcn_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_link_vif *arvif; + struct ath12k_fw_stats_req_params param; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah && ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + param.stats_id = WMI_REQUEST_BCN_STAT; + + /* loop all active VDEVs for bcn stats */ + list_for_each_entry(arvif, &ar->arvifs, list) { + if (!arvif->is_up) + continue; + + param.vdev_id = arvif->vdev_id; + ret = ath12k_mac_get_fw_stats(ar, ¶m); + if (ret) { + ath12k_warn(ar->ab, "failed to request fw bcn stats: %d\n", ret); + return ret; + } + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + /* since beacon stats request is looped for all active VDEVs, saved fw + * stats is not freed for each request until done for all active VDEVs + */ + spin_lock_bh(&ar->data_lock); + ath12k_fw_stats_bcn_free(&ar->fw_stats.bcn); + spin_unlock_bh(&ar->data_lock); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_bcn_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_bcn_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_bcn_stats = { + .open = ath12k_open_bcn_stats, + .release = ath12k_release_bcn_stats, + .read = ath12k_read_bcn_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static int ath12k_open_pdev_stats(struct inode *inode, struct file *file) +{ + struct ath12k *ar = inode->i_private; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + struct ath12k_base *ab = ar->ab; + struct ath12k_fw_stats_req_params param; + int ret; + + guard(wiphy)(ath12k_ar_to_hw(ar)->wiphy); + + if (ah && ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + void *buf __free(kfree) = kzalloc(ATH12K_FW_STATS_BUF_SIZE, GFP_ATOMIC); + if (!buf) + return -ENOMEM; + + param.pdev_id = ath12k_mac_get_target_pdev_id(ar); + param.vdev_id = 0; + param.stats_id = WMI_REQUEST_PDEV_STAT; + + ret = ath12k_mac_get_fw_stats(ar, ¶m); + if (ret) { + ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret); + return ret; + } + + ath12k_wmi_fw_stats_dump(ar, &ar->fw_stats, param.stats_id, + buf); + + file->private_data = no_free_ptr(buf); + + return 0; +} + +static int ath12k_release_pdev_stats(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + + return 0; +} + +static ssize_t ath12k_read_pdev_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + const char *buf = file->private_data; + size_t len = strlen(buf); + + return simple_read_from_buffer(user_buf, count, ppos, buf, len); +} + +static const struct file_operations fops_pdev_stats = { + .open = ath12k_open_pdev_stats, + .release = ath12k_release_pdev_stats, + .read = ath12k_read_pdev_stats, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static +void ath12k_debugfs_fw_stats_register(struct ath12k *ar) +{ + struct dentry *fwstats_dir = debugfs_create_dir("fw_stats", + ar->debug.debugfs_pdev); + + /* all stats debugfs files created are under "fw_stats" directory + * created per PDEV + */ + debugfs_create_file("vdev_stats", 0600, fwstats_dir, ar, + &fops_vdev_stats); + debugfs_create_file("beacon_stats", 0600, fwstats_dir, ar, + &fops_bcn_stats); + debugfs_create_file("pdev_stats", 0600, fwstats_dir, ar, + &fops_pdev_stats); + + ath12k_fw_stats_init(ar); +} + void ath12k_debugfs_register(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; @@ -91,7 +1546,18 @@ void ath12k_debugfs_register(struct ath12k *ar) &fops_simulate_radar); } + debugfs_create_file("tpc_stats", 0400, ar->debug.debugfs_pdev, ar, + &fops_tpc_stats); + debugfs_create_file("tpc_stats_type", 0200, ar->debug.debugfs_pdev, + ar, &fops_tpc_stats_type); + init_completion(&ar->debug.tpc_complete); + ath12k_debugfs_htt_stats_register(ar); + ath12k_debugfs_fw_stats_register(ar); + + debugfs_create_file("ext_rx_stats", 0644, + ar->debug.debugfs_pdev, ar, + &fops_extd_rx_stats); } void ath12k_debugfs_unregister(struct ath12k *ar) diff --git a/drivers/net/wireless/ath/ath12k/debugfs.h b/drivers/net/wireless/ath/ath12k/debugfs.h index 8d64ba03aa9a..ebef7dace344 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs.h +++ b/drivers/net/wireless/ath/ath12k/debugfs.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef _ATH12K_DEBUGFS_H_ @@ -12,6 +12,103 @@ void ath12k_debugfs_soc_create(struct ath12k_base *ab); void ath12k_debugfs_soc_destroy(struct ath12k_base *ab); void ath12k_debugfs_register(struct ath12k *ar); void ath12k_debugfs_unregister(struct ath12k *ar); +void ath12k_debugfs_fw_stats_process(struct ath12k *ar, + struct ath12k_fw_stats *stats); +void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void ath12k_debugfs_pdev_create(struct ath12k_base *ab); + +static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar) +{ + return ar->debug.extd_rx_stats; +} + +static inline int ath12k_debugfs_rx_filter(struct ath12k *ar) +{ + return ar->debug.rx_filter; +} + +#define ATH12K_CCK_RATES 4 +#define ATH12K_OFDM_RATES 8 +#define ATH12K_HT_RATES 8 +#define ATH12K_VHT_RATES 12 +#define ATH12K_HE_RATES 12 +#define ATH12K_HE_RATES_WITH_EXTRA_MCS 14 +#define ATH12K_EHT_RATES 16 +#define HE_EXTRA_MCS_SUPPORT GENMASK(31, 16) +#define ATH12K_NSS_1 1 +#define ATH12K_NSS_4 4 +#define ATH12K_NSS_8 8 +#define ATH12K_HW_NSS(_rcode) (((_rcode) >> 5) & 0x7) +#define TPC_STATS_WAIT_TIME (1 * HZ) +#define MAX_TPC_PREAM_STR_LEN 7 +#define TPC_INVAL -128 +#define TPC_MAX 127 +#define TPC_STATS_WAIT_TIME (1 * HZ) +#define TPC_STATS_TOT_ROW 700 +#define TPC_STATS_TOT_COLUMN 100 +#define MODULATION_LIMIT 126 + +#define ATH12K_TPC_STATS_BUF_SIZE (TPC_STATS_TOT_ROW * TPC_STATS_TOT_COLUMN) + +enum wmi_tpc_pream_bw { + WMI_TPC_PREAM_CCK, + WMI_TPC_PREAM_OFDM, + WMI_TPC_PREAM_HT20, + WMI_TPC_PREAM_HT40, + WMI_TPC_PREAM_VHT20, + WMI_TPC_PREAM_VHT40, + WMI_TPC_PREAM_VHT80, + WMI_TPC_PREAM_VHT160, + WMI_TPC_PREAM_HE20, + WMI_TPC_PREAM_HE40, + WMI_TPC_PREAM_HE80, + WMI_TPC_PREAM_HE160, + WMI_TPC_PREAM_EHT20, + WMI_TPC_PREAM_EHT40, + WMI_TPC_PREAM_EHT60, + WMI_TPC_PREAM_EHT80, + WMI_TPC_PREAM_EHT120, + WMI_TPC_PREAM_EHT140, + WMI_TPC_PREAM_EHT160, + WMI_TPC_PREAM_EHT200, + WMI_TPC_PREAM_EHT240, + WMI_TPC_PREAM_EHT280, + WMI_TPC_PREAM_EHT320, + WMI_TPC_PREAM_MAX +}; + +enum ath12k_debug_tpc_stats_ctl_mode { + ATH12K_TPC_STATS_CTL_MODE_LEGACY_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT_VHT20_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT20_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT_VHT40_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT40_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_VHT80_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT80_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_VHT160_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT160_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_HE_EHT320_5GHZ_6GHZ, + ATH12K_TPC_STATS_CTL_MODE_CCK_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_LEGACY_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT20_2GHZ, + ATH12K_TPC_STATS_CTL_MODE_HT40_2GHZ, + + ATH12K_TPC_STATS_CTL_MODE_EHT80_SU_PUNC20 = 23, + ATH12K_TPC_STATS_CTL_MODE_EHT160_SU_PUNC20, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC40, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC80, + ATH12K_TPC_STATS_CTL_MODE_EHT320_SU_PUNC120 +}; + +enum ath12k_debug_tpc_stats_support_modes { + ATH12K_TPC_STATS_SUPPORT_160 = 0, + ATH12K_TPC_STATS_SUPPORT_320, + ATH12K_TPC_STATS_SUPPORT_AX, + ATH12K_TPC_STATS_SUPPORT_AX_EXTRA_MCS, + ATH12K_TPC_STATS_SUPPORT_BE, + ATH12K_TPC_STATS_SUPPORT_BE_PUNC, +}; #else static inline void ath12k_debugfs_soc_create(struct ath12k_base *ab) { @@ -29,6 +126,29 @@ static inline void ath12k_debugfs_unregister(struct ath12k *ar) { } +static inline void ath12k_debugfs_fw_stats_process(struct ath12k *ar, + struct ath12k_fw_stats *stats) +{ +} + +static inline bool ath12k_debugfs_is_extd_rx_stats_enabled(struct ath12k *ar) +{ + return false; +} + +static inline int ath12k_debugfs_rx_filter(struct ath12k *ar) +{ + return 0; +} + +static inline void ath12k_debugfs_op_vif_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ +} + +static inline void ath12k_debugfs_pdev_create(struct ath12k_base *ab) +{ +} #endif /* CONFIG_ATH12K_DEBUGFS */ #endif /* _ATH12K_DEBUGFS_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c index 41e4ef2ef3af..aeaf970339d4 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/vmalloc.h> @@ -48,6 +48,34 @@ print_array_to_buf(u8 *buf, u32 offset, const char *header, footer); } +static u32 +print_array_to_buf_s8(u8 *buf, u32 offset, const char *header, u32 stats_index, + const s8 *array, u32 array_len, const char *footer) +{ + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + int index = 0; + u8 i; + + if (header) + index += scnprintf(buf + offset, buf_len - offset, "%s = ", header); + + for (i = 0; i < array_len; i++) { + index += scnprintf(buf + offset + index, (buf_len - offset) - index, + " %u:%d,", stats_index++, array[i]); + } + + index--; + if ((offset + index) < buf_len) + buf[offset + index] = '\0'; + + if (footer) { + index += scnprintf(buf + offset + index, (buf_len - offset) - index, + "%s", footer); + } + + return index; +} + static const char *ath12k_htt_ax_tx_rx_ru_size_to_str(u8 ru_size) { switch (ru_size) { @@ -2512,6 +2540,268 @@ ath12k_htt_print_pdev_stats_cca_counters_tlv(const void *tag_buf, u16 tag_len, } static void +ath12k_htt_print_tx_sounding_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_sounding_stats_tlv *htt_stats_buf = tag_buf; + const __le32 *cbf_20, *cbf_40, *cbf_80, *cbf_160, *cbf_320; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 tx_sounding_mode; + u8 i, u; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + cbf_20 = htt_stats_buf->cbf_20; + cbf_40 = htt_stats_buf->cbf_40; + cbf_80 = htt_stats_buf->cbf_80; + cbf_160 = htt_stats_buf->cbf_160; + cbf_320 = htt_stats_buf->cbf_320; + tx_sounding_mode = le32_to_cpu(htt_stats_buf->tx_sounding_mode); + + if (tx_sounding_mode == ATH12K_HTT_TX_AC_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "HTT_TX_AC_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ac_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "160MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_AX_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nHTT_TX_AX_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "ax_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "160MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_BE_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nHTT_TX_BE_SOUNDING_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_20 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_20[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_20[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_40 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_40[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_40[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_80 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_80[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_80[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_160 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_160[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_160[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, + "be_cbf_320 = IBF: %u, SU_SIFS: %u, SU_RBO: %u, ", + le32_to_cpu(cbf_320[ATH12K_HTT_IMPL_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_SUSIFS_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_SURBO_STEER_STATS])); + len += scnprintf(buf + len, buf_len - len, "MU_SIFS: %u, MU_RBO: %u\n", + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_MUSIFS_STEER_STATS]), + le32_to_cpu(cbf_320[ATH12K_HTT_EXPL_MURBO_STEER_STATS])); + for (u = 0, i = 0; u < ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS; u++) { + len += scnprintf(buf + len, buf_len - len, + "Sounding User_%u = 20MHz: %u, ", u, + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "40MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, "80MHz: %u, ", + le32_to_cpu(htt_stats_buf->sounding[i++])); + len += scnprintf(buf + len, buf_len - len, + "160MHz: %u, 320MHz: %u\n", + le32_to_cpu(htt_stats_buf->sounding[i++]), + le32_to_cpu(htt_stats_buf->sounding_320[u])); + } + } else if (tx_sounding_mode == ATH12K_HTT_TX_CMN_SOUNDING_MODE) { + len += scnprintf(buf + len, buf_len - len, + "\nCV UPLOAD HANDLER STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "cv_nc_mismatch_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_nc_mismatch_err)); + len += scnprintf(buf + len, buf_len - len, "cv_fcs_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_fcs_err)); + len += scnprintf(buf + len, buf_len - len, "cv_frag_idx_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_frag_idx_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_invalid_peer_id = %u\n", + le32_to_cpu(htt_stats_buf->cv_invalid_peer_id)); + len += scnprintf(buf + len, buf_len - len, "cv_no_txbf_setup = %u\n", + le32_to_cpu(htt_stats_buf->cv_no_txbf_setup)); + len += scnprintf(buf + len, buf_len - len, "cv_expiry_in_update = %u\n", + le32_to_cpu(htt_stats_buf->cv_expiry_in_update)); + len += scnprintf(buf + len, buf_len - len, "cv_pkt_bw_exceed = %u\n", + le32_to_cpu(htt_stats_buf->cv_pkt_bw_exceed)); + len += scnprintf(buf + len, buf_len - len, "cv_dma_not_done_err = %u\n", + le32_to_cpu(htt_stats_buf->cv_dma_not_done_err)); + len += scnprintf(buf + len, buf_len - len, "cv_update_failed = %u\n", + le32_to_cpu(htt_stats_buf->cv_update_failed)); + len += scnprintf(buf + len, buf_len - len, "cv_dma_timeout_error = %u\n", + le32_to_cpu(htt_stats_buf->cv_dma_timeout_error)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_ibf_uploads = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_ibf_uploads)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_ebf_uploads = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_ebf_uploads)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_received = %u\n", + le32_to_cpu(htt_stats_buf->cv_buf_received)); + len += scnprintf(buf + len, buf_len - len, "cv_buf_fed_back = %u\n\n", + le32_to_cpu(htt_stats_buf->cv_buf_fed_back)); + + len += scnprintf(buf + len, buf_len - len, "CV QUERY STATS:\n"); + len += scnprintf(buf + len, buf_len - len, "cv_total_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_query)); + len += scnprintf(buf + len, buf_len - len, + "cv_total_pattern_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_pattern_query)); + len += scnprintf(buf + len, buf_len - len, "cv_total_bw_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_bw_query)); + len += scnprintf(buf + len, buf_len - len, "cv_invalid_bw_coding = %u\n", + le32_to_cpu(htt_stats_buf->cv_invalid_bw_coding)); + len += scnprintf(buf + len, buf_len - len, "cv_forced_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_forced_sounding)); + len += scnprintf(buf + len, buf_len - len, + "cv_standalone_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_standalone_sounding)); + len += scnprintf(buf + len, buf_len - len, "cv_nc_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_nc_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_fb_type_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_fb_type_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_ofdma_bw_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_ofdma_bw_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_bw_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_bw_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_pattern_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_pattern_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_preamble_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_preamble_mismatch)); + len += scnprintf(buf + len, buf_len - len, "cv_nr_mismatch = %u\n", + le32_to_cpu(htt_stats_buf->cv_nr_mismatch)); + len += scnprintf(buf + len, buf_len - len, + "cv_in_use_cnt_exceeded = %u\n", + le32_to_cpu(htt_stats_buf->cv_in_use_cnt_exceeded)); + len += scnprintf(buf + len, buf_len - len, "cv_ntbr_sounding = %u\n", + le32_to_cpu(htt_stats_buf->cv_ntbr_sounding)); + len += scnprintf(buf + len, buf_len - len, + "cv_found_upload_in_progress = %u\n", + le32_to_cpu(htt_stats_buf->cv_found_upload_in_progress)); + len += scnprintf(buf + len, buf_len - len, + "cv_expired_during_query = %u\n", + le32_to_cpu(htt_stats_buf->cv_expired_during_query)); + len += scnprintf(buf + len, buf_len - len, "cv_found = %u\n", + le32_to_cpu(htt_stats_buf->cv_found)); + len += scnprintf(buf + len, buf_len - len, "cv_not_found = %u\n", + le32_to_cpu(htt_stats_buf->cv_not_found)); + len += scnprintf(buf + len, buf_len - len, "cv_total_query_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_total_query_ibf)); + len += scnprintf(buf + len, buf_len - len, "cv_found_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_found_ibf)); + len += scnprintf(buf + len, buf_len - len, "cv_not_found_ibf = %u\n", + le32_to_cpu(htt_stats_buf->cv_not_found_ibf)); + len += scnprintf(buf + len, buf_len - len, + "cv_expired_during_query_ibf = %u\n\n", + le32_to_cpu(htt_stats_buf->cv_expired_during_query_ibf)); + } + + stats_req->buf_len = len; +} + +static void ath12k_htt_print_pdev_obss_pd_stats_tlv(const void *tag_buf, u16 tag_len, struct debug_htt_stats_req *stats_req) { @@ -2577,6 +2867,428 @@ ath12k_htt_print_pdev_obss_pd_stats_tlv(const void *tag_buf, u16 tag_len, } static void +ath12k_htt_print_latency_prof_ctx_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_ctx_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_LATENCY_CTX_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "duration = %u\n", + le32_to_cpu(htt_stats_buf->duration)); + len += scnprintf(buf + len, buf_len - len, "tx_msdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tx_msdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "tx_mpdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->tx_mpdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_msdu_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rx_msdu_cnt)); + len += scnprintf(buf + len, buf_len - len, "rx_mpdu_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->rx_mpdu_cnt)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_latency_prof_cnt(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_cnt_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_LATENCY_CNT_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "prof_enable_cnt = %u\n\n", + le32_to_cpu(htt_stats_buf->prof_enable_cnt)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_latency_prof_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_latency_prof_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + if (le32_to_cpu(htt_stats_buf->print_header) == 1) { + len += scnprintf(buf + len, buf_len - len, + "HTT_STATS_LATENCY_PROF_TLV:\n"); + } + + len += scnprintf(buf + len, buf_len - len, "Latency name = %s\n", + htt_stats_buf->latency_prof_name); + len += scnprintf(buf + len, buf_len - len, "count = %u\n", + le32_to_cpu(htt_stats_buf->cnt)); + len += scnprintf(buf + len, buf_len - len, "minimum = %u\n", + le32_to_cpu(htt_stats_buf->min)); + len += scnprintf(buf + len, buf_len - len, "maximum = %u\n", + le32_to_cpu(htt_stats_buf->max)); + len += scnprintf(buf + len, buf_len - len, "last = %u\n", + le32_to_cpu(htt_stats_buf->last)); + len += scnprintf(buf + len, buf_len - len, "total = %u\n", + le32_to_cpu(htt_stats_buf->tot)); + len += scnprintf(buf + len, buf_len - len, "average = %u\n", + le32_to_cpu(htt_stats_buf->avg)); + len += scnprintf(buf + len, buf_len - len, "histogram interval = %u\n", + le32_to_cpu(htt_stats_buf->hist_intvl)); + len += print_array_to_buf(buf, len, "histogram", htt_stats_buf->hist, + ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST, "\n\n"); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_ul_ofdma_trigger_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 mac_id; + u8 j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_TRIGGER_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_ofdma)); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs", + htt_stats_buf->ul_ofdma_rx_mcs, + ATH12K_HTT_RX_NUM_MCS_CNTRS, "\n"); + for (j = 0; j < ATH12K_HTT_RX_NUM_GI_CNTRS; j++) { + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_gi[%u]", j); + len += print_array_to_buf(buf, len, "", + htt_stats_buf->ul_ofdma_rx_gi[j], + ATH12K_HTT_RX_NUM_MCS_CNTRS, "\n"); + } + + len += print_array_to_buf_index(buf, len, "ul_ofdma_rx_nss", 1, + htt_stats_buf->ul_ofdma_rx_nss, + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_bw", + htt_stats_buf->ul_ofdma_rx_bw, + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES; j++) { + len += scnprintf(buf + len, buf_len - len, j == 0 ? + "half_ul_ofdma_rx_bw" : + "quarter_ul_ofdma_rx_bw"); + len += print_array_to_buf(buf, len, "", htt_stats_buf->red_bw[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_ldpc)); + + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ru_size_ppdu = "); + for (j = 0; j < ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS; j++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_htt_ax_tx_rx_ru_size_to_str(j), + le32_to_cpu(htt_stats_buf->data_ru_size_ppdu[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, + "rx_ulofdma_non_data_ru_size_ppdu = "); + for (j = 0; j < ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS; j++) + len += scnprintf(buf + len, buf_len - len, " %s:%u ", + ath12k_htt_ax_tx_rx_ru_size_to_str(j), + le32_to_cpu(htt_stats_buf->non_data_ru_size_ppdu[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "rx_rssi_track_sta_aid", + htt_stats_buf->uplink_sta_aid, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_target_rssi", + htt_stats_buf->uplink_sta_target_rssi, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_fd_rssi", + htt_stats_buf->uplink_sta_fd_rssi, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += print_array_to_buf(buf, len, "rx_sta_power_headroom", + htt_stats_buf->uplink_sta_power_headroom, + ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK, "\n"); + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_basic_trigger_rx_qos_null_only = %u\n\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_bsc_trig_rx_qos_null_only)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_ul_ofdma_user_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_ul_ofdma_user_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 user_index; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + user_index = __le32_to_cpu(htt_stats_buf->user_index); + + if (!user_index) + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_OFDMA_USER_STAS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_non_data_ppdu_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_non_data_ppdu)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_ppdu_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_data_ppdu)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_ok_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_mpdu_ok)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_mpdu_fail_%u = %u\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_mpdu_fail)); + len += scnprintf(buf + len, buf_len - len, + "rx_ulofdma_non_data_nusers_%u = %u\n", user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_non_data_nusers)); + len += scnprintf(buf + len, buf_len - len, "rx_ulofdma_data_nusers_%u = %u\n\n", + user_index, + le32_to_cpu(htt_stats_buf->rx_ulofdma_data_nusers)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_ul_mumimo_trig_stats(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv *htt_stats_buf = tag_buf; + char str_buf[ATH12K_HTT_MAX_STRING_LEN] = {0}; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + u32 mac_id; + u16 index; + u8 i, j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id = __le32_to_cpu(htt_stats_buf->mac_id__word); + + len += scnprintf(buf + len, buf_len - len, + "HTT_RX_PDEV_UL_MUMIMO_TRIG_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_mumimo)); + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (i = 0; i < ATH12K_HTT_RX_NUM_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i, + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_mcs[i])); + + for (i = 0; i < ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i + ATH12K_HTT_RX_NUM_MCS_CNTRS, + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_mcs_ext[i])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_mcs = %s\n", str_buf); + + for (j = 0; j < ATH12K_HTT_RX_NUM_GI_CNTRS; j++) { + index = 0; + memset(&str_buf[index], 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (i = 0; i < ATH12K_HTT_RX_NUM_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i, + le32_to_cpu(htt_stats_buf->ul_rx_gi[j][i])); + + for (i = 0; i < ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS; i++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", i + ATH12K_HTT_RX_NUM_MCS_CNTRS, + le32_to_cpu(htt_stats_buf->ul_gi_ext[j][i])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, + "ul_mumimo_rx_gi_%u = %s\n", j, str_buf); + } + + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + len += print_array_to_buf_index(buf, len, "ul_mumimo_rx_nss", 1, + htt_stats_buf->ul_mumimo_rx_nss, + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + + len += print_array_to_buf(buf, len, "ul_mumimo_rx_bw", + htt_stats_buf->ul_mumimo_rx_bw, + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + for (i = 0; i < ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES; i++) { + index = 0; + memset(str_buf, 0x0, ATH12K_HTT_MAX_STRING_LEN); + for (j = 0; j < ATH12K_HTT_RX_NUM_BW_CNTRS; j++) + index += scnprintf(&str_buf[index], + ATH12K_HTT_MAX_STRING_LEN - index, + " %u:%u,", j, + le32_to_cpu(htt_stats_buf->red_bw[i][j])); + str_buf[--index] = '\0'; + len += scnprintf(buf + len, buf_len - len, "%s = %s\n", + i == 0 ? "half_ul_mumimo_rx_bw" : + "quarter_ul_mumimo_rx_bw", str_buf); + } + + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_mumimo_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_mumimo_rx_ldpc)); + + for (j = 0; j < ATH12K_HTT_RX_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_rssi_in_dbm: chain%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->ul_rssi[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_target_rssi: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->tgt_rssi[j], + ATH12K_HTT_RX_NUM_BW_CNTRS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_mumimo_fd_rssi: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->fd[j], + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_UL_MUMIMO_USER_STATS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ulmumimo_pilot_evm_db_mean: user_%u ", j); + len += print_array_to_buf_s8(buf, len, "", 0, + htt_stats_buf->db[j], + ATH12K_HTT_RX_NUM_SPATIAL_STREAMS, "\n"); + } + + len += scnprintf(buf + len, buf_len - len, + "ul_mumimo_basic_trigger_rx_qos_null_only = %u\n\n", + le32_to_cpu(htt_stats_buf->mumimo_bsc_trig_rx_qos_null_only)); + + stats_req->buf_len = len; +} + +static void +ath12k_htt_print_rx_fse_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_fse_stats_tlv *htt_stats_buf = tag_buf; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u32 len = stats_req->buf_len; + u8 *buf = stats_req->buf; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_STATS_RX_FSE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "=== Software RX FSE STATS ===\n"); + len += scnprintf(buf + len, buf_len - len, "Enable count = %u\n", + le32_to_cpu(htt_stats_buf->fse_enable_cnt)); + len += scnprintf(buf + len, buf_len - len, "Disable count = %u\n", + le32_to_cpu(htt_stats_buf->fse_disable_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache invalidate entry count = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_invalidate_entry_cnt)); + len += scnprintf(buf + len, buf_len - len, "Full cache invalidate count = %u\n", + le32_to_cpu(htt_stats_buf->fse_full_cache_invalidate_cnt)); + + len += scnprintf(buf + len, buf_len - len, "\n=== Hardware RX FSE STATS ===\n"); + len += scnprintf(buf + len, buf_len - len, "Cache hits count = %u\n", + le32_to_cpu(htt_stats_buf->fse_num_cache_hits_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache no. of searches = %u\n", + le32_to_cpu(htt_stats_buf->fse_num_searches_cnt)); + len += scnprintf(buf + len, buf_len - len, "Cache occupancy peak count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-16] = %u [17-32] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[33-48] = %u [49-64] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[65-80] = %u [81-96] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[5]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[6])); + len += scnprintf(buf + len, buf_len - len, "[97-112] = %u [113-127] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[7]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[8])); + len += scnprintf(buf + len, buf_len - len, "[128] = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_peak_cnt[9])); + len += scnprintf(buf + len, buf_len - len, "Cache occupancy current count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-16] = %u [17-32] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[33-48] = %u [49-64] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[65-80] = %u [81-96] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[5]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[6])); + len += scnprintf(buf + len, buf_len - len, "[97-112] = %u [113-127] = %u ", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[7]), + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[8])); + len += scnprintf(buf + len, buf_len - len, "[128] = %u\n", + le32_to_cpu(htt_stats_buf->fse_cache_occupancy_curr_cnt[9])); + len += scnprintf(buf + len, buf_len - len, "Cache search square count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-50] = %u [51-100] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[101-200] = %u [201-255] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[3]), + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[4])); + len += scnprintf(buf + len, buf_len - len, "[256] = %u\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_square_cnt[5])); + len += scnprintf(buf + len, buf_len - len, "Cache search peak pending count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-2] = %u [3-4] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[Greater/Equal to 5] = %u\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_peak_cnt[3])); + len += scnprintf(buf + len, buf_len - len, "Cache search tot pending count:\n"); + len += scnprintf(buf + len, buf_len - len, "[0] = %u [1-2] = %u [3-4] = %u ", + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[0]), + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[1]), + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[2])); + len += scnprintf(buf + len, buf_len - len, "[Greater/Equal to 5] = %u\n\n", + le32_to_cpu(htt_stats_buf->fse_search_stat_pending_cnt[3])); + + stats_req->buf_len = len; +} + +static void ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(const void *tag_buf, u16 tag_len, struct debug_htt_stats_req *stats_req) { @@ -3812,6 +4524,497 @@ ath12k_htt_print_pdev_mbssid_ctrl_frame_stats_tlv(const void *tag_buf, u16 tag_l stats_req->buf_len = len; } +static inline void +ath12k_htt_print_tx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_tx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i, j; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id_word); + + len += scnprintf(buf + len, buf_len - len, "HTT_TX_PDEV_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ac_mu_mimo_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "ofdma_tx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ofdma_tx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rts_cnt)); + len += scnprintf(buf + len, buf_len - len, "rts_success = %u\n", + le32_to_cpu(htt_stats_buf->rts_success)); + len += scnprintf(buf + len, buf_len - len, "ack_rssi = %u\n", + le32_to_cpu(htt_stats_buf->ack_rssi)); + len += scnprintf(buf + len, buf_len - len, + "Legacy CCK Rates: 1 Mbps: %u, 2 Mbps: %u, 5.5 Mbps: %u, 12 Mbps: %u\n", + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[0]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[1]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[2]), + le32_to_cpu(htt_stats_buf->tx_legacy_cck_rate[3])); + len += scnprintf(buf + len, buf_len - len, + "Legacy OFDM Rates: 6 Mbps: %u, 9 Mbps: %u, 12 Mbps: %u, 18 Mbps: %u\n" + " 24 Mbps: %u, 36 Mbps: %u, 48 Mbps: %u, 54 Mbps: %u\n", + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[0]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[1]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[2]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[3]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[4]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[5]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[6]), + le32_to_cpu(htt_stats_buf->tx_legacy_ofdm_rate[7])); + len += scnprintf(buf + len, buf_len - len, "HE LTF: 1x: %u, 2x: %u, 4x: %u\n", + le32_to_cpu(htt_stats_buf->tx_he_ltf[1]), + le32_to_cpu(htt_stats_buf->tx_he_ltf[2]), + le32_to_cpu(htt_stats_buf->tx_he_ltf[3])); + + len += print_array_to_buf(buf, len, "tx_mcs", htt_stats_buf->tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_mcs_ext[j])); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS + + ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_mcs_ext_2[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "ax_mu_mimo_tx_mcs", + htt_stats_buf->ax_mu_mimo_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "ofdma_tx_mcs", + htt_stats_buf->ofdma_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ofdma_tx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ac_mu_mimo_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ac_mu_mimo_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ax_mu_mimo_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ax_mu_mimo_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += scnprintf(buf + len, buf_len - len, "ofdma_tx_nss ="); + for (j = 1; j <= ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) + len += scnprintf(buf + len, buf_len - len, " %u:%u,", + j, le32_to_cpu(htt_stats_buf->ofdma_tx_nss[j - 1])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + len += print_array_to_buf(buf, len, "tx_bw", htt_stats_buf->tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, NULL); + len += scnprintf(buf + len, buf_len - len, ", %u:%u\n", + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_bw_320mhz)); + + len += print_array_to_buf(buf, len, "tx_stbc", + htt_stats_buf->tx_stbc, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_stbc_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ac_mu_mimo_tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ac_mu_mimo_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ax_mu_mimo_tx_gi[%u] =", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->ax_mimo_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ax_tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, (buf_len - len), + "ofdma_tx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, htt_stats_buf->ofdma_tx_gi[j], + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + NULL); + for (i = 0; i < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; i++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + i + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->ofd_tx_gi_ext[j][i])); + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + len += print_array_to_buf(buf, len, "tx_su_mcs", htt_stats_buf->tx_su_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "tx_mu_mcs", htt_stats_buf->tx_mu_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_tx_mcs", + htt_stats_buf->ac_mu_mimo_tx_mcs, + ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ac_mu_mimo_tx_bw", + htt_stats_buf->ac_mu_mimo_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ax_mu_mimo_tx_bw", + htt_stats_buf->ax_mu_mimo_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ofdma_tx_bw", + htt_stats_buf->ofdma_tx_bw, + ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "tx_pream", htt_stats_buf->tx_pream, + ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + len += print_array_to_buf(buf, len, "tx_dcm", htt_stats_buf->tx_dcm, + ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS, "\n"); + + stats_req->buf_len = len; +} + +static inline void +ath12k_htt_print_rx_pdev_rate_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_rate_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 i, j; + u32 mac_id_word; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + mac_id_word = le32_to_cpu(htt_stats_buf->mac_id_word); + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "mac_id = %u\n", + u32_get_bits(mac_id_word, ATH12K_HTT_STATS_MAC_ID)); + len += scnprintf(buf + len, buf_len - len, "nsts = %u\n", + le32_to_cpu(htt_stats_buf->nsts)); + len += scnprintf(buf + len, buf_len - len, "rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->rx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "rts_cnt = %u\n", + le32_to_cpu(htt_stats_buf->rts_cnt)); + len += scnprintf(buf + len, buf_len - len, "rssi_mgmt = %u\n", + le32_to_cpu(htt_stats_buf->rssi_mgmt)); + len += scnprintf(buf + len, buf_len - len, "rssi_data = %u\n", + le32_to_cpu(htt_stats_buf->rssi_data)); + len += scnprintf(buf + len, buf_len - len, "rssi_comb = %u\n", + le32_to_cpu(htt_stats_buf->rssi_comb)); + len += scnprintf(buf + len, buf_len - len, "rssi_in_dbm = %d\n", + le32_to_cpu(htt_stats_buf->rssi_in_dbm)); + len += scnprintf(buf + len, buf_len - len, "rx_evm_nss_count = %u\n", + le32_to_cpu(htt_stats_buf->nss_count)); + len += scnprintf(buf + len, buf_len - len, "rx_evm_pilot_count = %u\n", + le32_to_cpu(htt_stats_buf->pilot_count)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_su_ext = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_su_ext)); + len += scnprintf(buf + len, buf_len - len, "rx_11ac_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ac_mumimo)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_mumimo = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_mumimo)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ofdma)); + len += scnprintf(buf + len, buf_len - len, "txbf = %u\n", + le32_to_cpu(htt_stats_buf->txbf)); + len += scnprintf(buf + len, buf_len - len, "rx_su_ndpa = %u\n", + le32_to_cpu(htt_stats_buf->rx_su_ndpa)); + len += scnprintf(buf + len, buf_len - len, "rx_mu_ndpa = %u\n", + le32_to_cpu(htt_stats_buf->rx_mu_ndpa)); + len += scnprintf(buf + len, buf_len - len, "rx_br_poll = %u\n", + le32_to_cpu(htt_stats_buf->rx_br_poll)); + len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_low = %u\n", + le32_to_cpu(htt_stats_buf->rx_active_dur_us_low)); + len += scnprintf(buf + len, buf_len - len, "rx_active_dur_us_high = %u\n", + le32_to_cpu(htt_stats_buf->rx_active_dur_us_high)); + len += scnprintf(buf + len, buf_len - len, "rx_11ax_ul_ofdma = %u\n", + le32_to_cpu(htt_stats_buf->rx_11ax_ul_ofdma)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_stbc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_stbc)); + len += scnprintf(buf + len, buf_len - len, "ul_ofdma_rx_ldpc = %u\n", + le32_to_cpu(htt_stats_buf->ul_ofdma_rx_ldpc)); + len += scnprintf(buf + len, buf_len - len, "per_chain_rssi_pkt_type = %#x\n", + le32_to_cpu(htt_stats_buf->per_chain_rssi_pkt_type)); + + len += print_array_to_buf(buf, len, "rx_nss", htt_stats_buf->rx_nss, + ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "rx_dcm", htt_stats_buf->rx_dcm, + ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_stbc", htt_stats_buf->rx_stbc, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_bw", htt_stats_buf->rx_bw, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_pream", htt_stats_buf->rx_pream, + ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_su_txbf_mcs", + htt_stats_buf->rx_11ax_su_txbf_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_mu_txbf_mcs", + htt_stats_buf->rx_11ax_mu_txbf_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_legacy_cck_rate", + htt_stats_buf->rx_legacy_cck_rate, + ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS, "\n"); + len += print_array_to_buf(buf, len, "rx_legacy_ofdm_rate", + htt_stats_buf->rx_legacy_ofdm_rate, + ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs", + htt_stats_buf->ul_ofdma_rx_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_nss", + htt_stats_buf->ul_ofdma_rx_nss, + ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_bw", + htt_stats_buf->ul_ofdma_rx_bw, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_non_data_ppdu", + htt_stats_buf->rx_ulofdma_non_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_data_ppdu", + htt_stats_buf->rx_ulofdma_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_mpdu_ok", + htt_stats_buf->rx_ulofdma_mpdu_ok, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_mpdu_fail", + htt_stats_buf->rx_ulofdma_mpdu_fail, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_non_data_nusers", + htt_stats_buf->rx_ulofdma_non_data_nusers, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulofdma_data_nusers", + htt_stats_buf->rx_ulofdma_data_nusers, + ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_mcs", + htt_stats_buf->rx_11ax_dl_ofdma_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_ru", + htt_stats_buf->rx_11ax_dl_ofdma_ru, + ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_non_data_ppdu", + htt_stats_buf->rx_ulmumimo_non_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_data_ppdu", + htt_stats_buf->rx_ulmumimo_data_ppdu, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_mpdu_ok", + htt_stats_buf->rx_ulmumimo_mpdu_ok, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + len += print_array_to_buf(buf, len, "rx_ulmumimo_mpdu_fail", + htt_stats_buf->rx_ulmumimo_mpdu_fail, + ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER, "\n"); + + len += print_array_to_buf(buf, len, "rx_mcs", + htt_stats_buf->rx_mcs, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, NULL); + for (j = 0; j < ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + le32_to_cpu(htt_stats_buf->rx_mcs_ext[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "pilot_evm_db[%u] =", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_pil_evm_db[j], + ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS, + "\n"); + } + + len += scnprintf(buf + len, buf_len - len, "pilot_evm_db_mean ="); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", i, + le32_to_cpu(htt_stats_buf->rx_pilot_evm_db_mean[i])); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rssi_chain_in_db[%u] = ", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u: %d,", i, + htt_stats_buf->rssi_chain_in_db[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_gi[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_rx_gi[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ul_ofdma_rx_gi[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_ul_fd_rssi: nss[%u] = ", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", + i, htt_stats_buf->rx_ul_fd_rssi[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_per_chain_rssi_in_dbm[%u] =", j); + for (i = 0; i < ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS; i++) + len += scnprintf(buf + len, + buf_len - len, + " %u:%d,", + i, + htt_stats_buf->rx_per_chain_rssi_in_dbm[j][i]); + len--; + len += scnprintf(buf + len, buf_len - len, "\n"); + } + + stats_req->buf_len = len; +} + +static inline void +ath12k_htt_print_rx_pdev_rate_ext_stats_tlv(const void *tag_buf, u16 tag_len, + struct debug_htt_stats_req *stats_req) +{ + const struct ath12k_htt_rx_pdev_rate_ext_stats_tlv *htt_stats_buf = tag_buf; + u8 *buf = stats_req->buf; + u32 len = stats_req->buf_len; + u32 buf_len = ATH12K_HTT_STATS_BUF_SIZE; + u8 j; + + if (tag_len < sizeof(*htt_stats_buf)) + return; + + len += scnprintf(buf + len, buf_len - len, "HTT_RX_PDEV_RATE_EXT_STATS_TLV:\n"); + len += scnprintf(buf + len, buf_len - len, "rssi_mgmt_in_dbm = %d\n", + le32_to_cpu(htt_stats_buf->rssi_mgmt_in_dbm)); + + len += print_array_to_buf(buf, len, "rx_stbc_ext", + htt_stats_buf->rx_stbc_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "ul_ofdma_rx_mcs_ext", + htt_stats_buf->ul_ofdma_rx_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_su_txbf_mcs_ext", + htt_stats_buf->rx_11ax_su_txbf_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_mu_txbf_mcs_ext", + htt_stats_buf->rx_11ax_mu_txbf_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_11ax_dl_ofdma_mcs_ext", + htt_stats_buf->rx_11ax_dl_ofdma_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, "\n"); + len += print_array_to_buf(buf, len, "rx_bw_ext", + htt_stats_buf->rx_bw_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS, "\n"); + len += print_array_to_buf(buf, len, "rx_su_punctured_mode", + htt_stats_buf->rx_su_punctured_mode, + ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS, + "\n"); + + len += print_array_to_buf(buf, len, "rx_mcs_ext", + htt_stats_buf->rx_mcs_ext, + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + NULL); + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS; j++) + len += scnprintf(buf + len, buf_len - len, ", %u:%u", + j + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + le32_to_cpu(htt_stats_buf->rx_mcs_ext_2[j])); + len += scnprintf(buf + len, buf_len - len, "\n"); + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "rx_gi_ext[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->rx_gi_ext[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + "\n"); + } + + for (j = 0; j < ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS; j++) { + len += scnprintf(buf + len, buf_len - len, + "ul_ofdma_rx_gi_ext[%u] = ", j); + len += print_array_to_buf(buf, len, NULL, + htt_stats_buf->ul_ofdma_rx_gi_ext[j], + ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT, + "\n"); + } + + stats_req->buf_len = len; +} + static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, u16 tag, u16 len, const void *tag_buf, void *user_data) @@ -3982,9 +5185,33 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, case HTT_STATS_PDEV_CCA_COUNTERS_TAG: ath12k_htt_print_pdev_stats_cca_counters_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_TX_SOUNDING_STATS_TAG: + ath12k_htt_print_tx_sounding_stats_tlv(tag_buf, len, stats_req); + break; case HTT_STATS_PDEV_OBSS_PD_TAG: ath12k_htt_print_pdev_obss_pd_stats_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_LATENCY_CTX_TAG: + ath12k_htt_print_latency_prof_ctx_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_LATENCY_CNT_TAG: + ath12k_htt_print_latency_prof_cnt(tag_buf, len, stats_req); + break; + case HTT_STATS_LATENCY_PROF_STATS_TAG: + ath12k_htt_print_latency_prof_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG: + ath12k_htt_print_ul_ofdma_trigger_stats(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG: + ath12k_htt_print_ul_ofdma_user_stats(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG: + ath12k_htt_print_ul_mumimo_trig_stats(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_FSE_STATS_TAG: + ath12k_htt_print_rx_fse_stats_tlv(tag_buf, len, stats_req); + break; case HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG: ath12k_htt_print_pdev_tx_rate_txbf_stats_tlv(tag_buf, len, stats_req); break; @@ -4047,6 +5274,15 @@ static int ath12k_dbg_htt_ext_stats_parse(struct ath12k_base *ab, ath12k_htt_print_pdev_mbssid_ctrl_frame_stats_tlv(tag_buf, len, stats_req); break; + case HTT_STATS_TX_PDEV_RATE_STATS_TAG: + ath12k_htt_print_tx_pdev_rate_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_RATE_STATS_TAG: + ath12k_htt_print_rx_pdev_rate_stats_tlv(tag_buf, len, stats_req); + break; + case HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG: + ath12k_htt_print_rx_pdev_rate_ext_stats_tlv(tag_buf, len, stats_req); + break; default: break; } @@ -4141,6 +5377,9 @@ static ssize_t ath12k_write_htt_stats_type(struct file *file, const int size = 32; int num_args; + if (count > size) + return -EINVAL; + char *buf __free(kfree) = kzalloc(size, GFP_KERNEL); if (!buf) return -ENOMEM; diff --git a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h index 4b59976fbc35..c2a02cf8a38b 100644 --- a/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h +++ b/drivers/net/wireless/ath/ath12k/debugfs_htt_stats.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef DEBUG_HTT_STATS_H @@ -123,30 +123,38 @@ struct ath12k_htt_extd_stats_msg { /* htt_dbg_ext_stats_type */ enum ath12k_dbg_htt_ext_stats_type { - ATH12K_DBG_HTT_EXT_STATS_RESET = 0, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, - ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, - ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, - ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, - ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, - ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, - ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, - ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, - ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, - ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, - ATH12K_DBG_HTT_EXT_STATS_DLPAGER_STATS = 36, - ATH12K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37, - ATH12K_DBG_HTT_EXT_VDEVS_TXRX_STATS = 38, - ATH12K_DBG_HTT_EXT_PDEV_PER_STATS = 40, - ATH12K_DBG_HTT_EXT_AST_ENTRIES = 41, - ATH12K_DBG_HTT_EXT_STATS_SOC_ERROR = 45, - ATH12K_DBG_HTT_DBG_PDEV_PUNCTURE_STATS = 46, - ATH12K_DBG_HTT_EXT_STATS_PDEV_SCHED_ALGO = 49, - ATH12K_DBG_HTT_EXT_STATS_MANDATORY_MUOFDMA = 51, - ATH12K_DGB_HTT_EXT_STATS_PDEV_MBSSID_CTRL_FRAME = 54, + ATH12K_DBG_HTT_EXT_STATS_RESET = 0, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX = 1, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_SCHED = 4, + ATH12K_DBG_HTT_EXT_STATS_PDEV_ERROR = 5, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TQM = 6, + ATH12K_DBG_HTT_EXT_STATS_TX_DE_INFO = 8, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE = 9, + ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE = 10, + ATH12K_DBG_HTT_EXT_STATS_TX_SELFGEN_INFO = 12, + ATH12K_DBG_HTT_EXT_STATS_SRNG_INFO = 15, + ATH12K_DBG_HTT_EXT_STATS_SFM_INFO = 16, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_MU = 17, + ATH12K_DBG_HTT_EXT_STATS_PDEV_CCA_STATS = 19, + ATH12K_DBG_HTT_EXT_STATS_TX_SOUNDING_INFO = 22, + ATH12K_DBG_HTT_EXT_STATS_PDEV_OBSS_PD_STATS = 23, + ATH12K_DBG_HTT_EXT_STATS_LATENCY_PROF_STATS = 25, + ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_TRIG_STATS = 26, + ATH12K_DBG_HTT_EXT_STATS_PDEV_UL_MUMIMO_TRIG_STATS = 27, + ATH12K_DBG_HTT_EXT_STATS_FSE_RX = 28, + ATH12K_DBG_HTT_EXT_STATS_PDEV_RX_RATE_EXT = 30, + ATH12K_DBG_HTT_EXT_STATS_PDEV_TX_RATE_TXBF = 31, + ATH12K_DBG_HTT_EXT_STATS_TXBF_OFDMA = 32, + ATH12K_DBG_HTT_EXT_STATS_DLPAGER_STATS = 36, + ATH12K_DBG_HTT_EXT_PHY_COUNTERS_AND_PHY_STATS = 37, + ATH12K_DBG_HTT_EXT_VDEVS_TXRX_STATS = 38, + ATH12K_DBG_HTT_EXT_PDEV_PER_STATS = 40, + ATH12K_DBG_HTT_EXT_AST_ENTRIES = 41, + ATH12K_DBG_HTT_EXT_STATS_SOC_ERROR = 45, + ATH12K_DBG_HTT_DBG_PDEV_PUNCTURE_STATS = 46, + ATH12K_DBG_HTT_EXT_STATS_PDEV_SCHED_ALGO = 49, + ATH12K_DBG_HTT_EXT_STATS_MANDATORY_MUOFDMA = 51, + ATH12K_DGB_HTT_EXT_STATS_PDEV_MBSSID_CTRL_FRAME = 54, /* keep this last */ ATH12K_DBG_HTT_NUM_EXT_STATS, @@ -173,6 +181,8 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_TX_PDEV_MU_MIMO_STATS_TAG = 25, HTT_STATS_SFM_CMN_TAG = 26, HTT_STATS_SRING_STATS_TAG = 27, + HTT_STATS_TX_PDEV_RATE_STATS_TAG = 34, + HTT_STATS_RX_PDEV_RATE_STATS_TAG = 35, HTT_STATS_TX_PDEV_SCHEDULER_TXQ_STATS_TAG = 36, HTT_STATS_TX_SCHED_CMN_TAG = 37, HTT_STATS_SCHED_TXQ_CMD_POSTED_TAG = 39, @@ -195,12 +205,21 @@ enum ath12k_dbg_htt_tlv_tag { HTT_STATS_PDEV_CCA_STAT_CUMULATIVE_TAG = 72, HTT_STATS_PDEV_CCA_COUNTERS_TAG = 73, HTT_STATS_TX_PDEV_MPDU_STATS_TAG = 74, + HTT_STATS_TX_SOUNDING_STATS_TAG = 80, HTT_STATS_SCHED_TXQ_SCHED_ORDER_SU_TAG = 86, HTT_STATS_SCHED_TXQ_SCHED_INELIGIBILITY_TAG = 87, HTT_STATS_PDEV_OBSS_PD_TAG = 88, HTT_STATS_HW_WAR_TAG = 89, + HTT_STATS_LATENCY_PROF_STATS_TAG = 91, + HTT_STATS_LATENCY_CTX_TAG = 92, + HTT_STATS_LATENCY_CNT_TAG = 93, + HTT_STATS_RX_PDEV_UL_TRIG_STATS_TAG = 94, + HTT_STATS_RX_PDEV_UL_OFDMA_USER_STATS_TAG = 95, + HTT_STATS_RX_PDEV_UL_MUMIMO_TRIG_STATS_TAG = 97, + HTT_STATS_RX_FSE_STATS_TAG = 98, HTT_STATS_SCHED_TXQ_SUPERCYCLE_TRIGGER_TAG = 100, HTT_STATS_PDEV_CTRL_PATH_TX_STATS_TAG = 102, + HTT_STATS_RX_PDEV_RATE_EXT_STATS_TAG = 103, HTT_STATS_PDEV_TX_RATE_TXBF_STATS_TAG = 108, HTT_STATS_TX_SELFGEN_AC_SCHED_STATUS_STATS_TAG = 111, HTT_STATS_TX_SELFGEN_AX_SCHED_STATUS_STATS_TAG = 112, @@ -387,6 +406,182 @@ struct ath12k_htt_tx_pdev_mu_ppdu_dist_stats_tlv { __le32 num_ppdu_posted_per_burst[ATH12K_HTT_STATS_MU_PPDU_PER_BURST_WORDS]; } __packed; +#define ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES 7 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_LTF 4 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS 2 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS 2 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_11AX_TRIGGER_TYPES 6 + +struct ath12k_htt_tx_pdev_rate_stats_tlv { + __le32 mac_id_word; + __le32 tx_ldpc; + __le32 rts_cnt; + __le32 ack_rssi; + __le32 tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_su_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_mu_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 tx_stbc[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_pream[ATH12K_HTT_TX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + __le32 tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 tx_dcm[ATH12K_HTT_TX_PDEV_STATS_NUM_DCM_COUNTERS]; + __le32 rts_success; + __le32 tx_legacy_cck_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + __le32 tx_legacy_ofdm_rate[ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + __le32 ac_mu_mimo_tx_ldpc; + __le32 ax_mu_mimo_tx_ldpc; + __le32 ofdma_tx_ldpc; + __le32 tx_he_ltf[ATH12K_HTT_TX_PDEV_STATS_NUM_LTF]; + __le32 ac_mu_mimo_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ax_mu_mimo_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ofdma_tx_mcs[ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ac_mu_mimo_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ax_mu_mimo_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ofdma_tx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ac_mu_mimo_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ax_mu_mimo_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ofdma_tx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ac_mu_mimo_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ax_mimo_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ofdma_tx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 trigger_type_11ax[ATH12K_HTT_TX_PDEV_STATS_NUM_11AX_TRIGGER_TYPES]; + __le32 tx_11ax_su_ext; + __le32 tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_stbc_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ax_mu_mimo_tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ofdma_tx_mcs_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ax_tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 ofd_tx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; + __le32 tx_mcs_ext_2[ATH12K_HTT_TX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 tx_bw_320mhz; +}; + +#define ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS 12 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS 5 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES 7 +#define ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER 8 +#define ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS 16 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS 6 +#define ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER 8 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS 2 + +struct ath12k_htt_rx_pdev_rate_stats_tlv { + __le32 mac_id_word; + __le32 nsts; + __le32 rx_ldpc; + __le32 rts_cnt; + __le32 rssi_mgmt; + __le32 rssi_data; + __le32 rssi_comb; + __le32 rx_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_nss[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 rx_dcm[ATH12K_HTT_RX_PDEV_STATS_NUM_DCM_COUNTERS]; + __le32 rx_stbc[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_bw[ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_pream[ATH12K_HTT_RX_PDEV_STATS_NUM_PREAMBLE_TYPES]; + u8 rssi_chain_in_db[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_gi[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rssi_in_dbm; + __le32 rx_11ax_su_ext; + __le32 rx_11ac_mumimo; + __le32 rx_11ax_mumimo; + __le32 rx_11ax_ofdma; + __le32 txbf; + __le32 rx_legacy_cck_rate[ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_CCK_STATS]; + __le32 rx_legacy_ofdm_rate[ATH12K_HTT_RX_PDEV_STATS_NUM_LEGACY_OFDM_STATS]; + __le32 rx_active_dur_us_low; + __le32 rx_active_dur_us_high; + __le32 rx_11ax_ul_ofdma; + __le32 ul_ofdma_rx_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ul_ofdma_rx_gi[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 ul_ofdma_rx_nss[ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + __le32 ul_ofdma_rx_bw[ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 ul_ofdma_rx_stbc; + __le32 ul_ofdma_rx_ldpc; + __le32 rx_ulofdma_non_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_mpdu_ok[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_mpdu_fail[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 nss_count; + __le32 pilot_count; + __le32 rx_pil_evm_db[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_RXEVM_MAX_PILOTS_NSS]; + __le32 rx_pilot_evm_db_mean[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS]; + s8 rx_ul_fd_rssi[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 per_chain_rssi_pkt_type; + s8 rx_per_chain_rssi_in_dbm[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_COUNTERS]; + __le32 rx_su_ndpa; + __le32 rx_11ax_su_txbf_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_mu_ndpa; + __le32 rx_11ax_mu_txbf_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_br_poll; + __le32 rx_11ax_dl_ofdma_mcs[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS]; + __le32 rx_11ax_dl_ofdma_ru[ATH12K_HTT_RX_PDEV_STATS_NUM_RU_SIZE_COUNTERS]; + __le32 rx_ulmumimo_non_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_data_ppdu[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_mpdu_ok[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulmumimo_mpdu_fail[ATH12K_HTT_RX_PDEV_MAX_ULMUMIMO_NUM_USER]; + __le32 rx_ulofdma_non_data_nusers[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_ulofdma_data_nusers[ATH12K_HTT_RX_PDEV_MAX_OFDMA_NUM_USER]; + __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA_MCS_COUNTERS]; +}; + +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS 4 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT 14 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS 2 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS 5 +#define ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS 5 + +struct ath12k_htt_rx_pdev_rate_ext_stats_tlv { + u8 rssi_chain_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS]; + s8 rx_per_chain_rssi_ext_in_dbm[ATH12K_HTT_RX_PDEV_STATS_NUM_SPATIAL_STREAMS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT_COUNTERS]; + __le32 rssi_mcast_in_dbm; + __le32 rssi_mgmt_in_dbm; + __le32 rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_stbc_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_gi_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 ul_ofdma_rx_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 ul_ofdma_rx_gi_ext[ATH12K_HTT_TX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_su_txbf_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_mu_txbf_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_11ax_dl_ofdma_mcs_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_MCS_COUNTERS_EXT]; + __le32 rx_mcs_ext_2[ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 rx_bw_ext[ATH12K_HTT_RX_PDEV_STATS_NUM_BW_EXT2_COUNTERS]; + __le32 rx_gi_ext_2[ATH12K_HTT_RX_PDEV_STATS_NUM_GI_COUNTERS] + [ATH12K_HTT_RX_PDEV_STATS_NUM_EXTRA2_MCS_COUNTERS]; + __le32 rx_su_punctured_mode[ATH12K_HTT_RX_PDEV_STATS_NUM_PUNCTURED_MODE_COUNTERS]; +}; + #define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_MAC_ID GENMASK(7, 0) #define ATH12K_HTT_TX_PDEV_STATS_SCHED_PER_TXQ_ID GENMASK(15, 8) @@ -1058,6 +1253,82 @@ struct ath12k_htt_pdev_cca_stats_hist_v1_tlv { __le32 collection_interval; } __packed; +#define ATH12K_HTT_TX_CV_CORR_MAX_NUM_COLUMNS 8 +#define ATH12K_HTT_TX_NUM_AC_MUMIMO_USER_STATS 4 +#define ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS 8 +#define ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS 4 +#define ATH12K_HTT_TX_NUM_MCS_CNTRS 12 +#define ATH12K_HTT_TX_NUM_EXTRA_MCS_CNTRS 2 + +#define ATH12K_HTT_TX_NUM_OF_SOUNDING_STATS_WORDS \ + (ATH12K_HTT_TX_PDEV_STATS_NUM_BW_COUNTERS * \ + ATH12K_HTT_TX_NUM_AX_MUMIMO_USER_STATS) + +enum ath12k_htt_txbf_sound_steer_modes { + ATH12K_HTT_IMPL_STEER_STATS = 0, + ATH12K_HTT_EXPL_SUSIFS_STEER_STATS = 1, + ATH12K_HTT_EXPL_SURBO_STEER_STATS = 2, + ATH12K_HTT_EXPL_MUSIFS_STEER_STATS = 3, + ATH12K_HTT_EXPL_MURBO_STEER_STATS = 4, + ATH12K_HTT_TXBF_MAX_NUM_OF_MODES = 5 +}; + +enum ath12k_htt_stats_sounding_tx_mode { + ATH12K_HTT_TX_AC_SOUNDING_MODE = 0, + ATH12K_HTT_TX_AX_SOUNDING_MODE = 1, + ATH12K_HTT_TX_BE_SOUNDING_MODE = 2, + ATH12K_HTT_TX_CMN_SOUNDING_MODE = 3, +}; + +struct ath12k_htt_tx_sounding_stats_tlv { + __le32 tx_sounding_mode; + __le32 cbf_20[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_40[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_80[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cbf_160[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 sounding[ATH12K_HTT_TX_NUM_OF_SOUNDING_STATS_WORDS]; + __le32 cv_nc_mismatch_err; + __le32 cv_fcs_err; + __le32 cv_frag_idx_mismatch; + __le32 cv_invalid_peer_id; + __le32 cv_no_txbf_setup; + __le32 cv_expiry_in_update; + __le32 cv_pkt_bw_exceed; + __le32 cv_dma_not_done_err; + __le32 cv_update_failed; + __le32 cv_total_query; + __le32 cv_total_pattern_query; + __le32 cv_total_bw_query; + __le32 cv_invalid_bw_coding; + __le32 cv_forced_sounding; + __le32 cv_standalone_sounding; + __le32 cv_nc_mismatch; + __le32 cv_fb_type_mismatch; + __le32 cv_ofdma_bw_mismatch; + __le32 cv_bw_mismatch; + __le32 cv_pattern_mismatch; + __le32 cv_preamble_mismatch; + __le32 cv_nr_mismatch; + __le32 cv_in_use_cnt_exceeded; + __le32 cv_found; + __le32 cv_not_found; + __le32 sounding_320[ATH12K_HTT_TX_NUM_BE_MUMIMO_USER_STATS]; + __le32 cbf_320[ATH12K_HTT_TXBF_MAX_NUM_OF_MODES]; + __le32 cv_ntbr_sounding; + __le32 cv_found_upload_in_progress; + __le32 cv_expired_during_query; + __le32 cv_dma_timeout_error; + __le32 cv_buf_ibf_uploads; + __le32 cv_buf_ebf_uploads; + __le32 cv_buf_received; + __le32 cv_buf_fed_back; + __le32 cv_total_query_ibf; + __le32 cv_found_ibf; + __le32 cv_not_found_ibf; + __le32 cv_expired_during_query_ibf; +} __packed; + struct ath12k_htt_pdev_obss_pd_stats_tlv { __le32 num_obss_tx_ppdu_success; __le32 num_obss_tx_ppdu_failure; @@ -1080,6 +1351,127 @@ struct ath12k_htt_pdev_obss_pd_stats_tlv { __le32 num_sr_ppdu_abort_flush_cnt; } __packed; +#define ATH12K_HTT_STATS_MAX_PROF_STATS_NAME_LEN 32 +#define ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST 3 +#define ATH12K_HTT_INTERRUPTS_LATENCY_PROFILE_MAX_HIST 3 + +struct ath12k_htt_latency_prof_stats_tlv { + __le32 print_header; + s8 latency_prof_name[ATH12K_HTT_STATS_MAX_PROF_STATS_NAME_LEN]; + __le32 cnt; + __le32 min; + __le32 max; + __le32 last; + __le32 tot; + __le32 avg; + __le32 hist_intvl; + __le32 hist[ATH12K_HTT_LATENCY_PROFILE_NUM_MAX_HIST]; +} __packed; + +struct ath12k_htt_latency_prof_ctx_tlv { + __le32 duration; + __le32 tx_msdu_cnt; + __le32 tx_mpdu_cnt; + __le32 tx_ppdu_cnt; + __le32 rx_msdu_cnt; + __le32 rx_mpdu_cnt; +} __packed; + +struct ath12k_htt_latency_prof_cnt_tlv { + __le32 prof_enable_cnt; +} __packed; + +#define ATH12K_HTT_RX_NUM_MCS_CNTRS 12 +#define ATH12K_HTT_RX_NUM_GI_CNTRS 4 +#define ATH12K_HTT_RX_NUM_SPATIAL_STREAMS 8 +#define ATH12K_HTT_RX_NUM_BW_CNTRS 4 +#define ATH12K_HTT_RX_NUM_RU_SIZE_CNTRS 6 +#define ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS 7 +#define ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK 5 +#define ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES 2 +#define ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS 2 + +enum ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE { + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_52, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_106, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_242, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_484, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996, + ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996x2, + ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS, +}; + +struct ath12k_htt_rx_pdev_ul_ofdma_user_stats_tlv { + __le32 user_index; + __le32 rx_ulofdma_non_data_ppdu; + __le32 rx_ulofdma_data_ppdu; + __le32 rx_ulofdma_mpdu_ok; + __le32 rx_ulofdma_mpdu_fail; + __le32 rx_ulofdma_non_data_nusers; + __le32 rx_ulofdma_data_nusers; +} __packed; + +struct ath12k_htt_rx_pdev_ul_trigger_stats_tlv { + __le32 mac_id__word; + __le32 rx_11ax_ul_ofdma; + __le32 ul_ofdma_rx_mcs[ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_ofdma_rx_gi[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_ofdma_rx_nss[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 ul_ofdma_rx_bw[ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_ofdma_rx_stbc; + __le32 ul_ofdma_rx_ldpc; + __le32 data_ru_size_ppdu[ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS]; + __le32 non_data_ru_size_ppdu[ATH12K_HTT_RX_NUM_RU_SIZE_160MHZ_CNTRS]; + __le32 uplink_sta_aid[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_target_rssi[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_fd_rssi[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 uplink_sta_power_headroom[ATH12K_HTT_RX_UL_MAX_UPLINK_RSSI_TRACK]; + __le32 red_bw[ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_ofdma_bsc_trig_rx_qos_null_only; +} __packed; + +#define ATH12K_HTT_TX_UL_MUMIMO_USER_STATS 8 + +struct ath12k_htt_rx_ul_mumimo_trig_stats_tlv { + __le32 mac_id__word; + __le32 rx_11ax_ul_mumimo; + __le32 ul_mumimo_rx_mcs[ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_rx_gi[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_MCS_CNTRS]; + __le32 ul_mumimo_rx_nss[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 ul_mumimo_rx_bw[ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 ul_mumimo_rx_stbc; + __le32 ul_mumimo_rx_ldpc; + __le32 ul_mumimo_rx_mcs_ext[ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS]; + __le32 ul_gi_ext[ATH12K_HTT_RX_NUM_GI_CNTRS][ATH12K_HTT_RX_NUM_EXTRA_MCS_CNTRS]; + s8 ul_rssi[ATH12K_HTT_RX_NUM_SPATIAL_STREAMS][ATH12K_HTT_RX_NUM_BW_CNTRS]; + s8 tgt_rssi[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_BW_CNTRS]; + s8 fd[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + s8 db[ATH12K_HTT_TX_UL_MUMIMO_USER_STATS][ATH12K_HTT_RX_NUM_SPATIAL_STREAMS]; + __le32 red_bw[ATH12K_HTT_RX_NUM_REDUCED_CHAN_TYPES][ATH12K_HTT_RX_NUM_BW_CNTRS]; + __le32 mumimo_bsc_trig_rx_qos_null_only; +} __packed; + +#define ATH12K_HTT_RX_NUM_MAX_PEAK_OCCUPANCY_INDEX 10 +#define ATH12K_HTT_RX_NUM_MAX_CURR_OCCUPANCY_INDEX 10 +#define ATH12K_HTT_RX_NUM_SQUARE_INDEX 6 +#define ATH12K_HTT_RX_NUM_MAX_PEAK_SEARCH_INDEX 4 +#define ATH12K_HTT_RX_NUM_MAX_PENDING_SEARCH_INDEX 4 + +struct ath12k_htt_rx_fse_stats_tlv { + __le32 fse_enable_cnt; + __le32 fse_disable_cnt; + __le32 fse_cache_invalidate_entry_cnt; + __le32 fse_full_cache_invalidate_cnt; + __le32 fse_num_cache_hits_cnt; + __le32 fse_num_searches_cnt; + __le32 fse_cache_occupancy_peak_cnt[ATH12K_HTT_RX_NUM_MAX_PEAK_OCCUPANCY_INDEX]; + __le32 fse_cache_occupancy_curr_cnt[ATH12K_HTT_RX_NUM_MAX_CURR_OCCUPANCY_INDEX]; + __le32 fse_search_stat_square_cnt[ATH12K_HTT_RX_NUM_SQUARE_INDEX]; + __le32 fse_search_stat_peak_cnt[ATH12K_HTT_RX_NUM_MAX_PEAK_SEARCH_INDEX]; + __le32 fse_search_stat_pending_cnt[ATH12K_HTT_RX_NUM_MAX_PENDING_SEARCH_INDEX]; +} __packed; + #define ATH12K_HTT_TX_BF_RATE_STATS_NUM_MCS_COUNTERS 14 #define ATH12K_HTT_TX_PDEV_STATS_NUM_LEGACY_OFDM_STATS 8 #define ATH12K_HTT_TX_PDEV_STATS_NUM_SPATIAL_STREAMS 8 @@ -1417,17 +1809,6 @@ enum ATH12K_HTT_RC_MODE { ATH12K_HTT_RC_MODE_2D_COUNT }; -enum ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE { - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_26, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_52, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_106, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_242, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_484, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996, - ATH12K_HTT_TX_RX_PDEV_STATS_AX_RU_SIZE_996x2, - ATH12K_HTT_TX_RX_PDEV_STATS_NUM_AX_RU_SIZE_CNTRS -}; - enum ath12k_htt_stats_rc_mode { ATH12K_HTT_STATS_RC_MODE_DLSU = 0, ATH12K_HTT_STATS_RC_MODE_DLMUMIMO = 1, diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.c b/drivers/net/wireless/ath/ath12k/debugfs_sta.c new file mode 100644 index 000000000000..5bd2bf4c9dac --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.c @@ -0,0 +1,337 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include <linux/vmalloc.h> + +#include "debugfs_sta.h" +#include "core.h" +#include "peer.h" +#include "debug.h" +#include "debugfs_htt_stats.h" +#include "debugfs.h" + +static +u32 ath12k_dbg_sta_dump_rate_stats(u8 *buf, u32 offset, const int size, + bool he_rates_avail, + const struct ath12k_rx_peer_rate_stats *stats) +{ + static const char *legacy_rate_str[HAL_RX_MAX_NUM_LEGACY_RATES] = { + "1 Mbps", "2 Mbps", "5.5 Mbps", "6 Mbps", + "9 Mbps", "11 Mbps", "12 Mbps", "18 Mbps", + "24 Mbps", "36 Mbps", "48 Mbps", "54 Mbps"}; + u8 max_bw = HAL_RX_BW_MAX, max_gi = HAL_RX_GI_MAX, max_mcs = HAL_RX_MAX_NSS; + int mcs = 0, bw = 0, nss = 0, gi = 0, bw_num = 0; + u32 i, len = offset, max = max_bw * max_gi * max_mcs; + bool found; + + len += scnprintf(buf + len, size - len, "\nEHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_BE; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->be_mcs_count[i], + (i + 1) % 8 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nHE stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_HE; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->he_mcs_count[i], + (i + 1) % 6 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nVHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_VHT; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->vht_mcs_count[i], + (i + 1) % 5 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nHT stats:\n"); + for (i = 0; i <= HAL_RX_MAX_MCS_HT; i++) + len += scnprintf(buf + len, size - len, + "MCS %d: %llu%s", i, stats->ht_mcs_count[i], + (i + 1) % 8 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nLegacy stats:\n"); + for (i = 0; i < HAL_RX_MAX_NUM_LEGACY_RATES; i++) + len += scnprintf(buf + len, size - len, + "%s: %llu%s", legacy_rate_str[i], + stats->legacy_count[i], + (i + 1) % 4 ? "\t" : "\n"); + + len += scnprintf(buf + len, size - len, "\nNSS stats:\n"); + for (i = 0; i < HAL_RX_MAX_NSS; i++) + len += scnprintf(buf + len, size - len, + "%dx%d: %llu ", i + 1, i + 1, + stats->nss_count[i]); + + len += scnprintf(buf + len, size - len, + "\n\nGI: 0.8 us %llu 0.4 us %llu 1.6 us %llu 3.2 us %llu\n", + stats->gi_count[0], + stats->gi_count[1], + stats->gi_count[2], + stats->gi_count[3]); + + len += scnprintf(buf + len, size - len, + "BW: 20 MHz %llu 40 MHz %llu 80 MHz %llu 160 MHz %llu 320 MHz %llu\n", + stats->bw_count[0], + stats->bw_count[1], + stats->bw_count[2], + stats->bw_count[3], + stats->bw_count[4]); + + for (i = 0; i < max; i++) { + found = false; + + for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) { + if (stats->rx_rate[bw][gi][nss][mcs]) { + found = true; + break; + } + } + + if (!found) + goto skip_report; + + switch (bw) { + case HAL_RX_BW_20MHZ: + bw_num = 20; + break; + case HAL_RX_BW_40MHZ: + bw_num = 40; + break; + case HAL_RX_BW_80MHZ: + bw_num = 80; + break; + case HAL_RX_BW_160MHZ: + bw_num = 160; + break; + case HAL_RX_BW_320MHZ: + bw_num = 320; + break; + } + + len += scnprintf(buf + len, size - len, "\n%d Mhz gi %d us %dx%d : ", + bw_num, gi, nss + 1, nss + 1); + + for (mcs = 0; mcs <= HAL_RX_MAX_MCS_HT; mcs++) { + if (stats->rx_rate[bw][gi][nss][mcs]) + len += scnprintf(buf + len, size - len, + " %d:%llu", mcs, + stats->rx_rate[bw][gi][nss][mcs]); + } + +skip_report: + if (nss++ >= max_mcs - 1) { + nss = 0; + if (gi++ >= max_gi - 1) { + gi = 0; + if (bw < max_bw - 1) + bw++; + } + } + } + + len += scnprintf(buf + len, size - len, "\n"); + + return len - offset; +} + +static ssize_t ath12k_dbg_sta_dump_rx_stats(struct file *file, + char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_link_sta *link_sta = file->private_data; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta); + const int size = ATH12K_STA_RX_STATS_BUF_SIZE; + struct ath12k_hw *ah = ahsta->ahvif->ah; + struct ath12k_rx_peer_stats *rx_stats; + struct ath12k_link_sta *arsta; + u8 link_id = link_sta->link_id; + int len = 0, i, ret = 0; + bool he_rates_avail; + struct ath12k *ar; + + wiphy_lock(ah->hw->wiphy); + + if (!(BIT(link_id) & ahsta->links_map)) { + wiphy_unlock(ah->hw->wiphy); + return -ENOENT; + } + + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arsta || !arsta->arvif->ar) { + wiphy_unlock(ah->hw->wiphy); + return -ENOENT; + } + + ar = arsta->arvif->ar; + + u8 *buf __free(kfree) = kzalloc(size, GFP_KERNEL); + if (!buf) { + ret = -ENOENT; + goto out; + } + + spin_lock_bh(&ar->ab->base_lock); + + rx_stats = arsta->rx_stats; + if (!rx_stats) { + ret = -ENOENT; + goto unlock; + } + + len += scnprintf(buf + len, size - len, "RX peer stats:\n\n"); + len += scnprintf(buf + len, size - len, "Num of MSDUs: %llu\n", + rx_stats->num_msdu); + len += scnprintf(buf + len, size - len, "Num of MSDUs with TCP L4: %llu\n", + rx_stats->tcp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs with UDP L4: %llu\n", + rx_stats->udp_msdu_count); + len += scnprintf(buf + len, size - len, "Num of other MSDUs: %llu\n", + rx_stats->other_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs part of AMPDU: %llu\n", + rx_stats->ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs not part of AMPDU: %llu\n", + rx_stats->non_ampdu_msdu_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs using STBC: %llu\n", + rx_stats->stbc_count); + len += scnprintf(buf + len, size - len, "Num of MSDUs beamformed: %llu\n", + rx_stats->beamformed_count); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS ok: %llu\n", + rx_stats->num_mpdu_fcs_ok); + len += scnprintf(buf + len, size - len, "Num of MPDUs with FCS error: %llu\n", + rx_stats->num_mpdu_fcs_err); + + he_rates_avail = (rx_stats->pream_cnt[HAL_RX_PREAMBLE_11AX] > 1) ? true : false; + + len += scnprintf(buf + len, size - len, + "preamble: 11A %llu 11B %llu 11N %llu 11AC %llu 11AX %llu 11BE %llu\n", + rx_stats->pream_cnt[0], rx_stats->pream_cnt[1], + rx_stats->pream_cnt[2], rx_stats->pream_cnt[3], + rx_stats->pream_cnt[4], rx_stats->pream_cnt[6]); + len += scnprintf(buf + len, size - len, + "reception type: SU %llu MU_MIMO %llu MU_OFDMA %llu MU_OFDMA_MIMO %llu\n", + rx_stats->reception_type[0], rx_stats->reception_type[1], + rx_stats->reception_type[2], rx_stats->reception_type[3]); + + len += scnprintf(buf + len, size - len, "TID(0-15) Legacy TID(16):"); + for (i = 0; i <= IEEE80211_NUM_TIDS; i++) + len += scnprintf(buf + len, size - len, "%llu ", rx_stats->tid_count[i]); + + len += scnprintf(buf + len, size - len, "\nRX Duration:%llu\n", + rx_stats->rx_duration); + + len += scnprintf(buf + len, size - len, + "\nDCM: %llu\nRU26: %llu\nRU52: %llu\nRU106: %llu\nRU242: %llu\nRU484: %llu\nRU996: %llu\nRU996x2: %llu\n", + rx_stats->dcm_count, rx_stats->ru_alloc_cnt[0], + rx_stats->ru_alloc_cnt[1], rx_stats->ru_alloc_cnt[2], + rx_stats->ru_alloc_cnt[3], rx_stats->ru_alloc_cnt[4], + rx_stats->ru_alloc_cnt[5], rx_stats->ru_alloc_cnt[6]); + + len += scnprintf(buf + len, size - len, "\nRX success packet stats:\n"); + len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail, + &rx_stats->pkt_stats); + + len += scnprintf(buf + len, size - len, "\n"); + + len += scnprintf(buf + len, size - len, "\nRX success byte stats:\n"); + len += ath12k_dbg_sta_dump_rate_stats(buf, len, size, he_rates_avail, + &rx_stats->byte_stats); + +unlock: + spin_unlock_bh(&ar->ab->base_lock); + + if (len) + ret = simple_read_from_buffer(user_buf, count, ppos, buf, len); +out: + wiphy_unlock(ah->hw->wiphy); + return ret; +} + +static const struct file_operations fops_rx_stats = { + .read = ath12k_dbg_sta_dump_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +static ssize_t ath12k_dbg_sta_reset_rx_stats(struct file *file, + const char __user *buf, + size_t count, loff_t *ppos) +{ + struct ieee80211_link_sta *link_sta = file->private_data; + struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(link_sta->sta); + struct ath12k_hw *ah = ahsta->ahvif->ah; + struct ath12k_rx_peer_stats *rx_stats; + struct ath12k_link_sta *arsta; + u8 link_id = link_sta->link_id; + struct ath12k *ar; + bool reset; + int ret; + + ret = kstrtobool_from_user(buf, count, &reset); + if (ret) + return ret; + + if (!reset) + return -EINVAL; + + wiphy_lock(ah->hw->wiphy); + + if (!(BIT(link_id) & ahsta->links_map)) { + ret = -ENOENT; + goto out; + } + + arsta = wiphy_dereference(ah->hw->wiphy, ahsta->link[link_id]); + if (!arsta || !arsta->arvif->ar) { + ret = -ENOENT; + goto out; + } + + ar = arsta->arvif->ar; + + spin_lock_bh(&ar->ab->base_lock); + + rx_stats = arsta->rx_stats; + if (!rx_stats) { + spin_unlock_bh(&ar->ab->base_lock); + ret = -ENOENT; + goto out; + } + + memset(rx_stats, 0, sizeof(*rx_stats)); + spin_unlock_bh(&ar->ab->base_lock); + + ret = count; +out: + wiphy_unlock(ah->hw->wiphy); + return ret; +} + +static const struct file_operations fops_reset_rx_stats = { + .write = ath12k_dbg_sta_reset_rx_stats, + .open = simple_open, + .owner = THIS_MODULE, + .llseek = default_llseek, +}; + +void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir) +{ + struct ath12k *ar; + + lockdep_assert_wiphy(hw->wiphy); + + ar = ath12k_get_ar_by_vif(hw, vif, link_sta->link_id); + if (!ar) + return; + + if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) { + debugfs_create_file("rx_stats", 0400, dir, link_sta, + &fops_rx_stats); + debugfs_create_file("reset_rx_stats", 0200, dir, link_sta, + &fops_reset_rx_stats); + } +} diff --git a/drivers/net/wireless/ath/ath12k/debugfs_sta.h b/drivers/net/wireless/ath/ath12k/debugfs_sta.h new file mode 100644 index 000000000000..8de924f4d7d5 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/debugfs_sta.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#ifndef _ATH12K_DEBUGFS_STA_H_ +#define _ATH12K_DEBUGFS_STA_H_ + +#include <net/mac80211.h> + +#include "core.h" + +#define ATH12K_STA_RX_STATS_BUF_SIZE (1024 * 16) + +#ifdef CONFIG_ATH12K_DEBUGFS + +void ath12k_debugfs_link_sta_op_add(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir); + +#endif /* CONFIG_ATH12K_DEBUGFS */ + +#endif /* _ATH12K_DEBUGFS_STA_H_ */ diff --git a/drivers/net/wireless/ath/ath12k/dp.c b/drivers/net/wireless/ath/ath12k/dp.c index 9e5a4e75f2f6..6317c6d4c043 100644 --- a/drivers/net/wireless/ath/ath12k/dp.c +++ b/drivers/net/wireless/ath/ath12k/dp.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <crypto/hash.h> @@ -168,6 +168,8 @@ static int ath12k_dp_srng_calculate_msi_group(struct ath12k_base *ab, grp_mask = &ab->hw_params->ring_mask->reo_status[0]; break; case HAL_RXDMA_MONITOR_STATUS: + grp_mask = &ab->hw_params->ring_mask->rx_mon_status[0]; + break; case HAL_RXDMA_MONITOR_DST: grp_mask = &ab->hw_params->ring_mask->rx_mon_dest[0]; break; @@ -274,12 +276,17 @@ int ath12k_dp_srng_setup(struct ath12k_base *ab, struct dp_srng *ring, break; case HAL_RXDMA_BUF: case HAL_RXDMA_MONITOR_BUF: - case HAL_RXDMA_MONITOR_STATUS: params.low_threshold = num_entries >> 3; params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; params.intr_batch_cntr_thres_entries = 0; params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; break; + case HAL_RXDMA_MONITOR_STATUS: + params.low_threshold = num_entries >> 3; + params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; + params.intr_batch_cntr_thres_entries = 1; + params.intr_timer_thres_us = HAL_SRNG_INT_TIMER_THRESHOLD_RX; + break; case HAL_TX_MONITOR_DST: params.low_threshold = DP_TX_MONITOR_BUF_SIZE_MAX >> 3; params.flags |= HAL_SRNG_FLAGS_LOW_THRESH_INTR_EN; @@ -354,7 +361,10 @@ u32 ath12k_dp_tx_get_vdev_bank_config(struct ath12k_base *ab, u32_encode_bits(0, HAL_TX_BANK_CONFIG_EPD); /* only valid if idx_lookup_override is not set in tcl_data_cmd */ - bank_config |= u32_encode_bits(0, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN); + if (ahvif->vdev_type == WMI_VDEV_TYPE_STA) + bank_config |= u32_encode_bits(1, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN); + else + bank_config |= u32_encode_bits(0, HAL_TX_BANK_CONFIG_INDEX_LOOKUP_EN); bank_config |= u32_encode_bits(arvif->hal_addr_search_flags & HAL_TX_ADDRX_EN, HAL_TX_BANK_CONFIG_ADDRX_EN) | @@ -919,6 +929,25 @@ int ath12k_dp_service_srng(struct ath12k_base *ab, goto done; } + if (ab->hw_params->ring_mask->rx_mon_status[grp_id]) { + ring_mask = ab->hw_params->ring_mask->rx_mon_status[grp_id]; + for (i = 0; i < ab->num_radios; i++) { + for (j = 0; j < ab->hw_params->num_rxdma_per_pdev; j++) { + int id = i * ab->hw_params->num_rxdma_per_pdev + j; + + if (ring_mask & BIT(id)) { + work_done = + ath12k_dp_mon_process_ring(ab, id, napi, budget, + 0); + budget -= work_done; + tot_work_done += work_done; + if (budget <= 0) + goto done; + } + } + } + } + if (ab->hw_params->ring_mask->rx_mon_dest[grp_id]) { monitor_mode = ATH12K_DP_RX_MONITOR_MODE; ring_mask = ab->hw_params->ring_mask->rx_mon_dest[grp_id]; @@ -982,11 +1011,6 @@ void ath12k_dp_pdev_free(struct ath12k_base *ab) { int i; - if (!ab->mon_reap_timer.function) - return; - - del_timer_sync(&ab->mon_reap_timer); - for (i = 0; i < ab->num_radios; i++) ath12k_dp_rx_pdev_free(ab, i); } @@ -1024,27 +1048,6 @@ void ath12k_dp_hal_rx_desc_init(struct ath12k_base *ab) ab->hal_rx_ops->rx_desc_get_desc_size(); } -static void ath12k_dp_service_mon_ring(struct timer_list *t) -{ - struct ath12k_base *ab = from_timer(ab, t, mon_reap_timer); - int i; - - for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) - ath12k_dp_mon_process_ring(ab, i, NULL, DP_MON_SERVICE_BUDGET, - ATH12K_DP_RX_MONITOR_MODE); - - mod_timer(&ab->mon_reap_timer, jiffies + - msecs_to_jiffies(ATH12K_MON_TIMER_INTERVAL)); -} - -static void ath12k_dp_mon_reap_timer_init(struct ath12k_base *ab) -{ - if (ab->hw_params->rxdma1_enable) - return; - - timer_setup(&ab->mon_reap_timer, ath12k_dp_service_mon_ring, 0); -} - int ath12k_dp_pdev_alloc(struct ath12k_base *ab) { struct ath12k *ar; @@ -1055,8 +1058,6 @@ int ath12k_dp_pdev_alloc(struct ath12k_base *ab) if (ret) goto out; - ath12k_dp_mon_reap_timer_init(ab); - /* TODO: Per-pdev rx ring unlike tx ring which is mapped to different AC's */ for (i = 0; i < ab->num_radios; i++) { ar = ab->pdevs[i].ar; @@ -1107,11 +1108,8 @@ static void ath12k_dp_update_vdev_search(struct ath12k_link_vif *arvif) { switch (arvif->ahvif->vdev_type) { case WMI_VDEV_TYPE_STA: - /* TODO: Verify the search type and flags since ast hash - * is not part of peer mapv3 - */ arvif->hal_addr_search_flags = HAL_TX_ADDRY_EN; - arvif->search_type = HAL_TX_ADDR_SEARCH_DEFAULT; + arvif->search_type = HAL_TX_ADDR_SEARCH_INDEX; break; case WMI_VDEV_TYPE_AP: case WMI_VDEV_TYPE_IBSS: @@ -1206,11 +1204,19 @@ static void ath12k_dp_cc_cleanup(struct ath12k_base *ab) if (!skb) continue; + skb_cb = ATH12K_SKB_CB(skb); + if (skb_cb->paddr_ext_desc) { + dma_unmap_single(ab->dev, + skb_cb->paddr_ext_desc, + tx_desc_info->skb_ext_desc->len, + DMA_TO_DEVICE); + dev_kfree_skb_any(tx_desc_info->skb_ext_desc); + } + /* if we are unregistering, hw would've been destroyed and * ar is no longer valid. */ if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) { - skb_cb = ATH12K_SKB_CB(skb); ar = skb_cb->ar; if (atomic_dec_and_test(&ar->dp.num_tx_pending)) @@ -1261,22 +1267,24 @@ static void ath12k_dp_reoq_lut_cleanup(struct ath12k_base *ab) if (!ab->hw_params->reoq_lut_support) return; - if (dp->reoq_lut.vaddr) { + if (dp->reoq_lut.vaddr_unaligned) { ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE0(ab), 0); - dma_free_coherent(ab->dev, DP_REOQ_LUT_SIZE, - dp->reoq_lut.vaddr, dp->reoq_lut.paddr); - dp->reoq_lut.vaddr = NULL; + dma_free_coherent(ab->dev, dp->reoq_lut.size, + dp->reoq_lut.vaddr_unaligned, + dp->reoq_lut.paddr_unaligned); + dp->reoq_lut.vaddr_unaligned = NULL; } - if (dp->ml_reoq_lut.vaddr) { + if (dp->ml_reoq_lut.vaddr_unaligned) { ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE1(ab), 0); - dma_free_coherent(ab->dev, DP_REOQ_LUT_SIZE, - dp->ml_reoq_lut.vaddr, dp->ml_reoq_lut.paddr); - dp->ml_reoq_lut.vaddr = NULL; + dma_free_coherent(ab->dev, dp->ml_reoq_lut.size, + dp->ml_reoq_lut.vaddr_unaligned, + dp->ml_reoq_lut.paddr_unaligned); + dp->ml_reoq_lut.vaddr_unaligned = NULL; } } @@ -1315,6 +1323,9 @@ void ath12k_dp_cc_config(struct ath12k_base *ab) u32 wbm_base = HAL_SEQ_WCSS_UMAC_WBM_REG; u32 val = 0; + if (ath12k_ftm_mode) + return; + ath12k_hif_write32(ab, reo_base + HAL_REO1_SW_COOKIE_CFG0(ab), cmem_base); val |= u32_encode_bits(ATH12K_CMEM_ADDR_MSB, @@ -1605,39 +1616,67 @@ free: return ret; } +static int ath12k_dp_alloc_reoq_lut(struct ath12k_base *ab, + struct ath12k_reo_q_addr_lut *lut) +{ + lut->size = DP_REOQ_LUT_SIZE + HAL_REO_QLUT_ADDR_ALIGN - 1; + lut->vaddr_unaligned = dma_alloc_coherent(ab->dev, lut->size, + &lut->paddr_unaligned, + GFP_KERNEL | __GFP_ZERO); + if (!lut->vaddr_unaligned) + return -ENOMEM; + + lut->vaddr = PTR_ALIGN(lut->vaddr_unaligned, HAL_REO_QLUT_ADDR_ALIGN); + lut->paddr = lut->paddr_unaligned + + ((unsigned long)lut->vaddr - (unsigned long)lut->vaddr_unaligned); + return 0; +} + static int ath12k_dp_reoq_lut_setup(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; + u32 val; + int ret; if (!ab->hw_params->reoq_lut_support) return 0; - dp->reoq_lut.vaddr = dma_alloc_coherent(ab->dev, - DP_REOQ_LUT_SIZE, - &dp->reoq_lut.paddr, - GFP_KERNEL | __GFP_ZERO); - if (!dp->reoq_lut.vaddr) { + ret = ath12k_dp_alloc_reoq_lut(ab, &dp->reoq_lut); + if (ret) { ath12k_warn(ab, "failed to allocate memory for reoq table"); - return -ENOMEM; + return ret; } - dp->ml_reoq_lut.vaddr = dma_alloc_coherent(ab->dev, - DP_REOQ_LUT_SIZE, - &dp->ml_reoq_lut.paddr, - GFP_KERNEL | __GFP_ZERO); - if (!dp->ml_reoq_lut.vaddr) { + ret = ath12k_dp_alloc_reoq_lut(ab, &dp->ml_reoq_lut); + if (ret) { ath12k_warn(ab, "failed to allocate memory for ML reoq table"); - dma_free_coherent(ab->dev, DP_REOQ_LUT_SIZE, - dp->reoq_lut.vaddr, dp->reoq_lut.paddr); - dp->reoq_lut.vaddr = NULL; - return -ENOMEM; + dma_free_coherent(ab->dev, dp->reoq_lut.size, + dp->reoq_lut.vaddr_unaligned, + dp->reoq_lut.paddr_unaligned); + dp->reoq_lut.vaddr_unaligned = NULL; + return ret; } + /* Bits in the register have address [39:8] LUT base address to be + * allocated such that LSBs are assumed to be zero. Also, current + * design supports paddr up to 4 GB max hence it fits in 32 bit + * register only + */ + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE0(ab), - dp->reoq_lut.paddr); + dp->reoq_lut.paddr >> 8); + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_LUT_BASE1(ab), dp->ml_reoq_lut.paddr >> 8); + val = ath12k_hif_read32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_ADDR(ab)); + + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_ADDR(ab), + val | HAL_REO_QDESC_ADDR_READ_LUT_ENABLE); + + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO1_QDESC_MAX_PEERID(ab), + HAL_REO_QDESC_MAX_PEERID); + return 0; } diff --git a/drivers/net/wireless/ath/ath12k/dp.h b/drivers/net/wireless/ath/ath12k/dp.h index 7ac3143de016..a353333f83b6 100644 --- a/drivers/net/wireless/ath/ath12k/dp.h +++ b/drivers/net/wireless/ath/ath12k/dp.h @@ -1,12 +1,13 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_H #define ATH12K_DP_H +#include "hal_desc.h" #include "hal_rx.h" #include "hw.h" @@ -69,6 +70,16 @@ struct ath12k_pdev_mon_stats { u32 dest_mpdu_drop; u32 dup_mon_linkdesc_cnt; u32 dup_mon_buf_cnt; + u32 dest_mon_stuck; + u32 dest_mon_not_reaped; +}; + +enum dp_mon_status_buf_state { + DP_MON_STATUS_MATCH, + DP_MON_STATUS_NO_DMA, + DP_MON_STATUS_LAG, + DP_MON_STATUS_LEAD, + DP_MON_STATUS_REPLINISH, }; struct dp_link_desc_bank { @@ -106,6 +117,8 @@ struct dp_mon_mpdu { struct list_head list; struct sk_buff *head; struct sk_buff *tail; + u32 err_bitmap; + u8 decap_format; }; #define DP_MON_MAX_STATUS_BUF 32 @@ -118,14 +131,16 @@ struct ath12k_mon_data { u32 mon_last_buf_cookie; u64 mon_last_linkdesc_paddr; u16 chan_noise_floor; + u32 err_bitmap; + u8 decap_format; struct ath12k_pdev_mon_stats rx_mon_stats; + enum dp_mon_status_buf_state buf_state; /* lock for monitor data */ spinlock_t mon_lock; struct sk_buff_head rx_status_q; struct dp_mon_mpdu *mon_mpdu; struct list_head dp_rx_mon_mpdu_list; - struct sk_buff *dest_skb_q[DP_MON_MAX_STATUS_BUF]; struct dp_mon_tx_ppdu_info *tx_prot_ppdu_info; struct dp_mon_tx_ppdu_info *tx_data_ppdu_info; }; @@ -176,7 +191,7 @@ struct ath12k_pdev_dp { #define DP_RXDMA_ERR_DST_RING_SIZE 1024 #define DP_RXDMA_MON_STATUS_RING_SIZE 1024 #define DP_RXDMA_MONITOR_BUF_RING_SIZE 4096 -#define DP_RXDMA_MONITOR_DST_RING_SIZE 2048 +#define DP_RXDMA_MONITOR_DST_RING_SIZE 8092 #define DP_RXDMA_MONITOR_DESC_RING_SIZE 4096 #define DP_TX_MONITOR_BUF_RING_SIZE 4096 #define DP_TX_MONITOR_DEST_RING_SIZE 2048 @@ -189,6 +204,14 @@ struct ath12k_pdev_dp { #define DP_RX_BUFFER_SIZE_LITE 1024 #define DP_RX_BUFFER_ALIGN_SIZE 128 +#define RX_MON_STATUS_BASE_BUF_SIZE 2048 +#define RX_MON_STATUS_BUF_ALIGN 128 +#define RX_MON_STATUS_BUF_RESERVATION 128 +#define RX_MON_STATUS_BUF_SIZE (RX_MON_STATUS_BASE_BUF_SIZE - \ + (RX_MON_STATUS_BUF_RESERVATION + \ + RX_MON_STATUS_BUF_ALIGN + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)))) + #define DP_RXDMA_BUF_COOKIE_BUF_ID GENMASK(17, 0) #define DP_RXDMA_BUF_COOKIE_PDEV_ID GENMASK(19, 18) @@ -264,6 +287,9 @@ struct ath12k_pdev_dp { /* Invalid TX Bank ID value */ #define DP_INVALID_BANK_ID -1 +#define MAX_TQM_RELEASE_REASON 15 +#define MAX_FW_TX_STATUS 7 + struct ath12k_dp_tx_bank_profile { u8 is_configured; u32 num_users; @@ -294,11 +320,18 @@ struct ath12k_rx_desc_info { struct ath12k_tx_desc_info { struct list_head list; struct sk_buff *skb; + struct sk_buff *skb_ext_desc; u32 desc_id; /* Cookie */ u8 mac_id; u8 pool_id; }; +struct ath12k_tx_desc_params { + struct sk_buff *skb; + struct sk_buff *skb_ext_desc; + u8 mac_id; +}; + struct ath12k_spt_info { dma_addr_t paddr; u64 *vaddr; @@ -310,12 +343,26 @@ struct ath12k_reo_queue_ref { } __packed; struct ath12k_reo_q_addr_lut { - dma_addr_t paddr; + u32 *vaddr_unaligned; u32 *vaddr; + dma_addr_t paddr_unaligned; + dma_addr_t paddr; + u32 size; +}; + +struct ath12k_link_stats { + u32 tx_enqueued; + u32 tx_completed; + u32 tx_bcast_mcast; + u32 tx_dropped; + u32 tx_encap_type[HAL_TCL_ENCAP_TYPE_MAX]; + u32 tx_encrypt_type[HAL_ENCRYPT_TYPE_MAX]; + u32 tx_desc_type[HAL_TCL_DESC_TYPE_MAX]; }; struct ath12k_dp { struct ath12k_base *ab; + u32 mon_dest_ring_stuck_cnt; u8 num_bank_profiles; /* protects the access and update of bank_profiles */ spinlock_t tx_bank_lock; @@ -368,22 +415,30 @@ struct ath12k_dp { struct dp_srng rxdma_err_dst_ring[MAX_RXDMA_PER_PDEV]; struct dp_rxdma_mon_ring rxdma_mon_buf_ring; struct dp_rxdma_mon_ring tx_mon_buf_ring; + struct dp_rxdma_mon_ring rx_mon_status_refill_ring[MAX_RXDMA_PER_PDEV]; struct ath12k_reo_q_addr_lut reoq_lut; struct ath12k_reo_q_addr_lut ml_reoq_lut; }; /* HTT definitions */ +#define HTT_TAG_TCL_METADATA_VERSION 5 -#define HTT_TCL_META_DATA_TYPE BIT(0) -#define HTT_TCL_META_DATA_VALID_HTT BIT(1) +#define HTT_TCL_META_DATA_TYPE GENMASK(1, 0) +#define HTT_TCL_META_DATA_VALID_HTT BIT(2) /* vdev meta data */ -#define HTT_TCL_META_DATA_VDEV_ID GENMASK(9, 2) -#define HTT_TCL_META_DATA_PDEV_ID GENMASK(11, 10) -#define HTT_TCL_META_DATA_HOST_INSPECTED BIT(12) +#define HTT_TCL_META_DATA_VDEV_ID GENMASK(10, 3) +#define HTT_TCL_META_DATA_PDEV_ID GENMASK(12, 11) +#define HTT_TCL_META_DATA_HOST_INSPECTED_MISSION BIT(13) /* peer meta data */ -#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 2) +#define HTT_TCL_META_DATA_PEER_ID GENMASK(15, 3) + +/* Global sequence number */ +#define HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM 3 +#define HTT_TCL_META_DATA_GLOBAL_SEQ_HOST_INSPECTED BIT(2) +#define HTT_TCL_META_DATA_GLOBAL_SEQ_NUM GENMASK(14, 3) +#define HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID 128 /* HTT tx completion is overlaid in wbm_release_ring */ #define HTT_TX_WBM_COMP_INFO0_STATUS GENMASK(16, 13) @@ -414,9 +469,15 @@ enum htt_h2t_msg_type { }; #define HTT_VER_REQ_INFO_MSG_ID GENMASK(7, 0) +#define HTT_OPTION_TCL_METADATA_VER_V2 2 +#define HTT_OPTION_TAG GENMASK(7, 0) +#define HTT_OPTION_LEN GENMASK(15, 8) +#define HTT_OPTION_VALUE GENMASK(31, 16) +#define HTT_TCL_METADATA_VER_SZ 4 struct htt_ver_req_cmd { __le32 ver_reg_info; + __le32 tcl_metadata_version; } __packed; enum htt_srng_ring_type { @@ -434,8 +495,11 @@ enum htt_srng_ring_id { HTT_HOST1_TO_FW_RXBUF_RING, HTT_HOST2_TO_FW_RXBUF_RING, HTT_RXDMA_NON_MONITOR_DEST_RING, + HTT_RXDMA_HOST_BUF_RING2, HTT_TX_MON_HOST2MON_BUF_RING, HTT_TX_MON_MON2HOST_DEST_RING, + HTT_RX_MON_HOST2MON_BUF_RING, + HTT_RX_MON_MON2HOST_DEST_RING, }; /* host -> target HTT_SRING_SETUP message @@ -767,8 +831,22 @@ enum htt_stats_internal_ppdu_frametype { #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_RING_ID GENMASK(23, 16) #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_SS BIT(24) #define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS BIT(25) -#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) -#define HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID BIT(26) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID BIT(26) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL BIT(27) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON BIT(28) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE GENMASK(15, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT GENMASK(18, 16) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL GENMASK(21, 19) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA GENMASK(24, 22) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD GENMASK(9, 0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE BIT(17) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE BIT(18) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE BIT(19) + +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET BIT(0) +#define HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET GENMASK(14, 1) #define HTT_RX_RING_SELECTION_CFG_RX_PACKET_OFFSET GENMASK(15, 0) #define HTT_RX_RING_SELECTION_CFG_RX_HEADER_OFFSET GENMASK(31, 16) @@ -797,6 +875,7 @@ enum htt_rx_filter_tlv_flags { HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS = BIT(10), HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT = BIT(11), HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE = BIT(12), + HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO = BIT(13), }; enum htt_rx_mgmt_pkt_filter_tlv_flags0 { @@ -1085,6 +1164,21 @@ enum htt_rx_data_pkt_filter_tlv_flasg3 { HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ HTT_RX_FILTER_TLV_FLAGS_ATTENTION) +#define HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING \ + (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_RX_PACKET | \ + HTT_RX_FILTER_TLV_FLAGS_MSDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_MPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PACKET_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PER_MSDU_HEADER | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_USER_STATS_EXT | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | \ + HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO) + /* msdu start. mpdu end, attention, rx hdr tlv's are not subscribed */ #define HTT_RX_TLV_FLAGS_RXDMA_RING \ (HTT_RX_FILTER_TLV_FLAGS_MPDU_START | \ @@ -1113,6 +1207,10 @@ struct htt_rx_ring_selection_cfg_cmd { __le32 info3; } __packed; +#define HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE 32 +#define HTT_RX_RING_DEFAULT_DMA_LENGTH 0x7 +#define HTT_RX_RING_PKT_TLV_OFFSET 0x1 + struct htt_rx_ring_tlv_filter { u32 rx_filter; /* see htt_rx_filter_tlv_flags */ u32 pkt_filter_flags0; /* MGMT */ @@ -1130,6 +1228,17 @@ struct htt_rx_ring_tlv_filter { u16 rx_mpdu_start_wmask; u16 rx_mpdu_end_wmask; u32 rx_msdu_end_wmask; + u32 conf_len_ctrl; + u32 conf_len_mgmt; + u32 conf_len_data; + u16 rx_drop_threshold; + bool enable_log_mgmt_type; + bool enable_log_ctrl_type; + bool enable_log_data_type; + bool enable_rx_tlv_offset; + u16 rx_tlv_offset; + bool drop_threshold_valid; + bool rxmon_disable; }; #define HTT_STATS_FRAME_CTRL_TYPE_MGMT 0x0 @@ -1247,6 +1356,8 @@ struct htt_t2h_version_conf_msg { #define HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16 GENMASK(15, 0) #define HTT_T2H_PEER_MAP_INFO1_HW_PEER_ID GENMASK(31, 16) #define HTT_T2H_PEER_MAP_INFO2_AST_HASH_VAL GENMASK(15, 0) +#define HTT_T2H_PEER_MAP3_INFO2_HW_PEER_ID GENMASK(15, 0) +#define HTT_T2H_PEER_MAP3_INFO2_AST_HASH_VAL GENMASK(31, 16) #define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_M BIT(16) #define HTT_T2H_PEER_MAP_INFO2_NEXT_HOP_S 16 diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.c b/drivers/net/wireless/ath/ath12k/dp_mon.c index 5a21961cfd46..28cadc4167f7 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.c +++ b/drivers/net/wireless/ath/ath12k/dp_mon.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "dp_mon.h" @@ -10,6 +10,12 @@ #include "dp_tx.h" #include "peer.h" +#define ATH12K_LE32_DEC_ENC(value, dec_bits, enc_bits) \ + u32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) + +#define ATH12K_LE64_DEC_ENC(value, dec_bits, enc_bits) \ + u32_encode_bits(le64_get_bits(value, dec_bits), enc_bits) + static void ath12k_dp_mon_rx_handle_ofdma_info(const struct hal_rx_ppdu_end_user_stats *ppdu_end_user, struct hal_rx_user_status *rx_user_status) @@ -75,7 +81,7 @@ ath12k_dp_mon_rx_populate_mu_user_info(const struct hal_rx_ppdu_end_user_stats * static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vht_sig, struct hal_rx_mon_ppdu_info *ppdu_info) { - u32 nsts, group_id, info0, info1; + u32 nsts, info0, info1; u8 gi_setting; info0 = __le32_to_cpu(vht_sig->info0); @@ -103,12 +109,8 @@ static void ath12k_dp_mon_parse_vht_sig_a(const struct hal_rx_vht_sig_a_info *vh ppdu_info->bw = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_BW); ppdu_info->beamformed = u32_get_bits(info1, HAL_RX_VHT_SIG_A_INFO_INFO1_BEAMFORMED); - group_id = u32_get_bits(info0, HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID); - if (group_id == 0 || group_id == 63) - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; - else - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; - ppdu_info->vht_flag_values5 = group_id; + ppdu_info->vht_flag_values5 = u32_get_bits(info0, + HAL_RX_VHT_SIG_A_INFO_INFO0_GROUP_ID); ppdu_info->vht_flag_values3[0] = (((ppdu_info->mcs) << 4) | ppdu_info->nss); ppdu_info->vht_flag_values2 = ppdu_info->bw; @@ -128,7 +130,6 @@ static void ath12k_dp_mon_parse_ht_sig(const struct hal_rx_ht_sig_info *ht_sig, ppdu_info->ldpc = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_FEC_CODING); ppdu_info->gi = u32_get_bits(info1, HAL_RX_HT_SIG_INFO_INFO1_GI); ppdu_info->nss = (ppdu_info->mcs >> 3); - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, @@ -160,7 +161,6 @@ static void ath12k_dp_mon_parse_l_sig_b(const struct hal_rx_lsig_b_info *lsigb, ppdu_info->rate = rate; ppdu_info->cck_flag = 1; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } static void ath12k_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga, @@ -200,7 +200,6 @@ static void ath12k_dp_mon_parse_l_sig_a(const struct hal_rx_lsig_a_info *lsiga, } ppdu_info->rate = rate; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; } static void @@ -237,7 +236,6 @@ ath12k_dp_mon_parse_he_sig_b2_ofdma(const struct hal_rx_he_sig_b2_ofdma_info *of ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_NSTS); ppdu_info->beamformed = u32_get_bits(info0, HAL_RX_HE_SIG_B2_OFDMA_INFO_INFO0_STA_TXBF); - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; } static void @@ -277,7 +275,6 @@ ath12k_dp_mon_parse_he_sig_b1_mu(const struct hal_rx_he_sig_b1_mu_info *he_sig_b HAL_RX_HE_SIG_B1_MU_INFO_INFO0_RU_ALLOCATION); ppdu_info->ru_alloc = ath12k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); ppdu_info->he_RU[0] = ru_tones; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; } static void @@ -411,7 +408,6 @@ ath12k_dp_mon_parse_he_sig_mu(const struct hal_rx_he_sig_a_mu_dl_info *he_sig_a_ ppdu_info->is_stbc = info1 & HAL_RX_HE_SIG_A_MU_DL_INFO1_STBC; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; } static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info *he_sig_a, @@ -559,17 +555,921 @@ static void ath12k_dp_mon_parse_he_sig_su(const struct hal_rx_he_sig_a_su_info * dcm = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_DCM); ppdu_info->nss = u32_get_bits(info0, HAL_RX_HE_SIG_A_SU_INFO_INFO0_NSTS); ppdu_info->dcm = dcm; - ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_cmn(const struct hal_mon_usig_cmn *cmn, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + u32 common; + + ppdu_info->u_sig_info.bw = le32_get_bits(cmn->info0, + HAL_RX_USIG_CMN_INFO0_BW); + ppdu_info->u_sig_info.ul_dl = le32_get_bits(cmn->info0, + HAL_RX_USIG_CMN_INFO0_UL_DL); + + common = __le32_to_cpu(ppdu_info->u_sig_info.usig.common); + common |= IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_PHY_VERSION, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER) | + u32_encode_bits(ppdu_info->u_sig_info.bw, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW) | + u32_encode_bits(ppdu_info->u_sig_info.ul_dl, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL) | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_BSS_COLOR, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR) | + ATH12K_LE32_DEC_ENC(cmn->info0, + HAL_RX_USIG_CMN_INFO0_TXOP, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + ppdu_info->u_sig_info.usig.common = cpu_to_le32(common); + + switch (ppdu_info->u_sig_info.bw) { + default: + fallthrough; + case HAL_EHT_BW_20: + ppdu_info->bw = HAL_RX_BW_20MHZ; + break; + case HAL_EHT_BW_40: + ppdu_info->bw = HAL_RX_BW_40MHZ; + break; + case HAL_EHT_BW_80: + ppdu_info->bw = HAL_RX_BW_80MHZ; + break; + case HAL_EHT_BW_160: + ppdu_info->bw = HAL_RX_BW_160MHZ; + break; + case HAL_EHT_BW_320_1: + case HAL_EHT_BW_320_2: + ppdu_info->bw = HAL_RX_BW_320MHZ; + break; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_tb(const struct hal_mon_usig_tb *usig_tb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct ieee80211_radiotap_eht_usig *usig = &ppdu_info->u_sig_info.usig; + enum ieee80211_radiotap_eht_usig_tb spatial_reuse1, spatial_reuse2; + u32 common, value, mask; + + spatial_reuse1 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1; + spatial_reuse2 = IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2; + + common = __le32_to_cpu(usig->common); + value = __le32_to_cpu(usig->value); + mask = __le32_to_cpu(usig->mask); + + ppdu_info->u_sig_info.ppdu_type_comp_mode = + le32_get_bits(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE); + + common |= ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + value |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD | + u32_encode_bits(ppdu_info->u_sig_info.ppdu_type_comp_mode, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE) | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1, + spatial_reuse1) | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_2, + spatial_reuse2) | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC) | + ATH12K_LE32_DEC_ENC(usig_tb->info0, + HAL_RX_USIG_TB_INFO0_TAIL, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL); + + mask |= IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE | + spatial_reuse1 | spatial_reuse2 | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC | + IEEE80211_RADIOTAP_EHT_USIG2_TB_B20_B25_TAIL; + + usig->common = cpu_to_le32(common); + usig->value = cpu_to_le32(value); + usig->mask = cpu_to_le32(mask); +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_mu(const struct hal_mon_usig_mu *usig_mu, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct ieee80211_radiotap_eht_usig *usig = &ppdu_info->u_sig_info.usig; + enum ieee80211_radiotap_eht_usig_mu sig_symb, punc; + u32 common, value, mask; + + sig_symb = IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS; + punc = IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO; + + common = __le32_to_cpu(usig->common); + value = __le32_to_cpu(usig->value); + mask = __le32_to_cpu(usig->mask); + + ppdu_info->u_sig_info.ppdu_type_comp_mode = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE); + ppdu_info->u_sig_info.eht_sig_mcs = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS); + ppdu_info->u_sig_info.num_eht_sig_sym = + le32_get_bits(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM); + + common |= ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + value |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE | + u32_encode_bits(ppdu_info->u_sig_info.ppdu_type_comp_mode, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE) | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO, + punc) | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE | + u32_encode_bits(ppdu_info->u_sig_info.eht_sig_mcs, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS) | + u32_encode_bits(ppdu_info->u_sig_info.num_eht_sig_sym, + sig_symb) | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC) | + ATH12K_LE32_DEC_ENC(usig_mu->info0, + HAL_RX_USIG_MU_INFO0_TAIL, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL); + + mask |= IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD | + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE | + punc | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS | + sig_symb | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC | + IEEE80211_RADIOTAP_EHT_USIG2_MU_B20_B25_TAIL; + + usig->common = cpu_to_le32(common); + usig->value = cpu_to_le32(value); + usig->mask = cpu_to_le32(mask); +} + +static void +ath12k_dp_mon_hal_rx_parse_u_sig_hdr(const struct hal_mon_usig_hdr *usig, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + u8 comp_mode; + + ppdu_info->eht_usig = true; + + ath12k_dp_mon_hal_rx_parse_u_sig_cmn(&usig->cmn, ppdu_info); + + comp_mode = le32_get_bits(usig->non_cmn.mu.info0, + HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE); + + if (comp_mode == 0 && ppdu_info->u_sig_info.ul_dl) + ath12k_dp_mon_hal_rx_parse_u_sig_tb(&usig->non_cmn.tb, ppdu_info); + else + ath12k_dp_mon_hal_rx_parse_u_sig_mu(&usig->non_cmn.mu, ppdu_info); +} + +static void +ath12k_dp_mon_hal_aggr_tlv(struct hal_rx_mon_ppdu_info *ppdu_info, + u16 tlv_len, const void *tlv_data) +{ + if (tlv_len <= HAL_RX_MON_MAX_AGGR_SIZE - ppdu_info->tlv_aggr.cur_len) { + memcpy(ppdu_info->tlv_aggr.buf + ppdu_info->tlv_aggr.cur_len, + tlv_data, tlv_len); + ppdu_info->tlv_aggr.cur_len += tlv_len; + } +} + +static inline bool +ath12k_dp_mon_hal_rx_is_frame_type_ndp(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == 1 && + usig_info->eht_sig_mcs == 0 && + usig_info->num_eht_sig_sym == 0) + return true; + + return false; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_non_ofdma(const struct hal_rx_u_sig_info *usig_info) +{ + u32 ppdu_type_comp_mode = usig_info->ppdu_type_comp_mode; + u32 ul_dl = usig_info->ul_dl; + + if ((ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_MIMO && ul_dl == 0) || + (ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_OFDMA && ul_dl == 0) || + (ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_MU_MIMO && ul_dl == 1)) + return true; + + return false; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_ofdma(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == 0 && usig_info->ul_dl == 0) + return true; + + return false; +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_ndp(const struct hal_eht_sig_ndp_cmn_eb *eht_sig_ndp, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE | + IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF | + IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S | + IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S | + IEEE80211_RADIOTAP_EHT_KNOWN_DISREGARD_S | + IEEE80211_RADIOTAP_EHT_KNOWN_CRC1 | + IEEE80211_RADIOTAP_EHT_KNOWN_TAIL1; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[0]); + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + /* GI and LTF size are separately indicated in radiotap header + * and hence will be parsed from other TLV + */ + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_CRC, + IEEE80211_RADIOTAP_EHT_DATA0_CRC1_O); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_DISREGARD, + IEEE80211_RADIOTAP_EHT_DATA0_DISREGARD_S); + eht->data[0] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[7]); + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_NSS, + IEEE80211_RADIOTAP_EHT_DATA7_NSS_S); + + data |= ATH12K_LE32_DEC_ENC(eht_sig_ndp->info0, + HAL_RX_EHT_SIG_NDP_CMN_INFO0_BEAMFORMED, + IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S); + eht->data[7] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_usig_overflow(const struct hal_eht_sig_usig_overflow *ovflow, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE | + IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF | + IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM | + IEEE80211_RADIOTAP_EHT_KNOWN_DISREGARD_O; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[0]); + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_SPATIAL_REUSE, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + + /* GI and LTF size are separately indicated in radiotap header + * and hence will be parsed from other TLV + */ + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_NUM_LTF_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_LDPC_EXTA_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_PRE_FEC_PAD_FACTOR, + IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISAMBIGUITY, + IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM); + + data |= ATH12K_LE32_DEC_ENC(ovflow->info0, + HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISREGARD, + IEEE80211_RADIOTAP_EHT_DATA0_DISREGARD_O); + eht->data[0] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_non_ofdma_users(const struct hal_eht_sig_non_ofdma_cmn_eb *eb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[7]); + data |= ATH12K_LE32_DEC_ENC(eb->info0, + HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_USERS, + IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS); + eht->data[7] = cpu_to_le32(data); +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_mumimo_user(const struct hal_eht_sig_mu_mimo *user, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_eht_info *eht_info = &ppdu_info->eht_info; + u32 user_idx; + + if (eht_info->num_user_info >= ARRAY_SIZE(eht_info->user_info)) + return; + + user_idx = eht_info->num_user_info++; + + eht_info->user_info[user_idx] |= + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_SPATIAL_CONFIG_KNOWN_M | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_STA_ID, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS, + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_SPATIAL_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_SPATIAL_CONFIG_M); + + ppdu_info->mcs = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS); +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(const struct hal_eht_sig_non_mu_mimo *user, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_eht_info *eht_info = &ppdu_info->eht_info; + u32 user_idx; + + if (eht_info->num_user_info >= ARRAY_SIZE(eht_info->user_info)) + return; + + user_idx = eht_info->num_user_info++; + + eht_info->user_info[user_idx] |= + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_STA_ID, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CODING, + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS, + IEEE80211_RADIOTAP_EHT_USER_INFO_MCS) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS, + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O) | + ATH12K_LE32_DEC_ENC(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_BEAMFORMED, + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); + + ppdu_info->mcs = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS); + + ppdu_info->nss = le32_get_bits(user->info0, + HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS) + 1; +} + +static inline bool +ath12k_dp_mon_hal_rx_is_mu_mimo_user(const struct hal_rx_u_sig_info *usig_info) +{ + if (usig_info->ppdu_type_comp_mode == HAL_RX_RECEPTION_TYPE_SU && + usig_info->ul_dl == 1) + return true; + + return false; +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_non_ofdma(const void *tlv, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_non_ofdma_cmn_eb *eb = tlv; + + ath12k_dp_mon_hal_rx_parse_usig_overflow(tlv, ppdu_info); + ath12k_dp_mon_hal_rx_parse_non_ofdma_users(eb, ppdu_info); + + if (ath12k_dp_mon_hal_rx_is_mu_mimo_user(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_mumimo_user(&eb->user_field.mu_mimo, + ppdu_info); + else + ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(&eb->user_field.n_mu_mimo, + ppdu_info); +} + +static void +ath12k_dp_mon_hal_rx_parse_ru_allocation(const struct hal_eht_sig_ofdma_cmn_eb *eb, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_ofdma_cmn_eb1 *ofdma_cmn_eb1 = &eb->eb1; + const struct hal_eht_sig_ofdma_cmn_eb2 *ofdma_cmn_eb2 = &eb->eb2; + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + enum ieee80211_radiotap_eht_data ru_123, ru_124, ru_125, ru_126; + enum ieee80211_radiotap_eht_data ru_121, ru_122, ru_112, ru_111; + u32 data; + + ru_123 = IEEE80211_RADIOTAP_EHT_DATA4_RU_ALLOC_CC_1_2_3; + ru_124 = IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_4; + ru_125 = IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_5; + ru_126 = IEEE80211_RADIOTAP_EHT_DATA6_RU_ALLOC_CC_1_2_6; + ru_121 = IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_1; + ru_122 = IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_2; + ru_112 = IEEE80211_RADIOTAP_EHT_DATA2_RU_ALLOC_CC_1_1_2; + ru_111 = IEEE80211_RADIOTAP_EHT_DATA1_RU_ALLOC_CC_1_1_1; + + switch (ppdu_info->u_sig_info.bw) { + case HAL_EHT_BW_320_2: + case HAL_EHT_BW_320_1: + data = __le32_to_cpu(eht->data[4]); + /* CC1 2::3 */ + data |= IEEE80211_RADIOTAP_EHT_DATA4_RU_ALLOC_CC_1_2_3_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_3, + ru_123); + eht->data[4] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[5]); + /* CC1 2::4 */ + data |= IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_4_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4, + ru_124); + + /* CC1 2::5 */ + data |= IEEE80211_RADIOTAP_EHT_DATA5_RU_ALLOC_CC_1_2_5_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5, + ru_125); + eht->data[5] = cpu_to_le32(data); + + data = __le32_to_cpu(eht->data[6]); + /* CC1 2::6 */ + data |= IEEE80211_RADIOTAP_EHT_DATA6_RU_ALLOC_CC_1_2_6_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6, + ru_126); + eht->data[6] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_160: + data = __le32_to_cpu(eht->data[3]); + /* CC1 2::1 */ + data |= IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_1_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_1, + ru_121); + /* CC1 2::2 */ + data |= IEEE80211_RADIOTAP_EHT_DATA3_RU_ALLOC_CC_1_2_2_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb2->info0, + HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_2, + ru_122); + eht->data[3] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_80: + data = __le32_to_cpu(eht->data[2]); + /* CC1 1::2 */ + data |= IEEE80211_RADIOTAP_EHT_DATA2_RU_ALLOC_CC_1_1_2_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb1->info0, + HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_2, + ru_112); + eht->data[2] = cpu_to_le32(data); + + fallthrough; + case HAL_EHT_BW_40: + fallthrough; + case HAL_EHT_BW_20: + data = __le32_to_cpu(eht->data[1]); + /* CC1 1::1 */ + data |= IEEE80211_RADIOTAP_EHT_DATA1_RU_ALLOC_CC_1_1_1_KNOWN | + ATH12K_LE64_DEC_ENC(ofdma_cmn_eb1->info0, + HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_1, + ru_111); + eht->data[1] = cpu_to_le32(data); + break; + default: + break; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(const void *tlv, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + const struct hal_eht_sig_ofdma_cmn_eb *ofdma = tlv; + + ath12k_dp_mon_hal_rx_parse_usig_overflow(tlv, ppdu_info); + ath12k_dp_mon_hal_rx_parse_ru_allocation(ofdma, ppdu_info); + + ath12k_dp_mon_hal_rx_parse_eht_non_mumimo_user(&ofdma->user_field.n_mu_mimo, + ppdu_info); +} + +static void +ath12k_dp_mon_parse_eht_sig_hdr(struct hal_rx_mon_ppdu_info *ppdu_info, + const void *tlv_data) +{ + ppdu_info->is_eht = true; + + if (ath12k_dp_mon_hal_rx_is_frame_type_ndp(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_ndp(tlv_data, ppdu_info); + else if (ath12k_dp_mon_hal_rx_is_non_ofdma(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_non_ofdma(tlv_data, ppdu_info); + else if (ath12k_dp_mon_hal_rx_is_ofdma(&ppdu_info->u_sig_info)) + ath12k_dp_mon_hal_rx_parse_eht_sig_ofdma(tlv_data, ppdu_info); +} + +static inline enum ath12k_eht_ru_size +hal_rx_mon_hal_ru_size_to_ath12k_ru_size(u32 hal_ru_size) +{ + switch (hal_ru_size) { + case HAL_EHT_RU_26: + return ATH12K_EHT_RU_26; + case HAL_EHT_RU_52: + return ATH12K_EHT_RU_52; + case HAL_EHT_RU_78: + return ATH12K_EHT_RU_52_26; + case HAL_EHT_RU_106: + return ATH12K_EHT_RU_106; + case HAL_EHT_RU_132: + return ATH12K_EHT_RU_106_26; + case HAL_EHT_RU_242: + return ATH12K_EHT_RU_242; + case HAL_EHT_RU_484: + return ATH12K_EHT_RU_484; + case HAL_EHT_RU_726: + return ATH12K_EHT_RU_484_242; + case HAL_EHT_RU_996: + return ATH12K_EHT_RU_996; + case HAL_EHT_RU_996x2: + return ATH12K_EHT_RU_996x2; + case HAL_EHT_RU_996x3: + return ATH12K_EHT_RU_996x3; + case HAL_EHT_RU_996x4: + return ATH12K_EHT_RU_996x4; + case HAL_EHT_RU_NONE: + return ATH12K_EHT_RU_INVALID; + case HAL_EHT_RU_996_484: + return ATH12K_EHT_RU_996_484; + case HAL_EHT_RU_996x2_484: + return ATH12K_EHT_RU_996x2_484; + case HAL_EHT_RU_996x3_484: + return ATH12K_EHT_RU_996x3_484; + case HAL_EHT_RU_996_484_242: + return ATH12K_EHT_RU_996_484_242; + default: + return ATH12K_EHT_RU_INVALID; + } +} + +static inline u32 +hal_rx_ul_ofdma_ru_size_to_width(enum ath12k_eht_ru_size ru_size) +{ + switch (ru_size) { + case ATH12K_EHT_RU_26: + return RU_26; + case ATH12K_EHT_RU_52: + return RU_52; + case ATH12K_EHT_RU_52_26: + return RU_52_26; + case ATH12K_EHT_RU_106: + return RU_106; + case ATH12K_EHT_RU_106_26: + return RU_106_26; + case ATH12K_EHT_RU_242: + return RU_242; + case ATH12K_EHT_RU_484: + return RU_484; + case ATH12K_EHT_RU_484_242: + return RU_484_242; + case ATH12K_EHT_RU_996: + return RU_996; + case ATH12K_EHT_RU_996_484: + return RU_996_484; + case ATH12K_EHT_RU_996_484_242: + return RU_996_484_242; + case ATH12K_EHT_RU_996x2: + return RU_2X996; + case ATH12K_EHT_RU_996x2_484: + return RU_2X996_484; + case ATH12K_EHT_RU_996x3: + return RU_3X996; + case ATH12K_EHT_RU_996x3_484: + return RU_3X996_484; + case ATH12K_EHT_RU_996x4: + return RU_4X996; + default: + return RU_INVALID; + } +} + +static void +ath12k_dp_mon_hal_rx_parse_user_info(const struct hal_receive_user_info *rx_usr_info, + u16 user_id, + struct hal_rx_mon_ppdu_info *ppdu_info) +{ + struct hal_rx_user_status *mon_rx_user_status = NULL; + struct hal_rx_radiotap_eht *eht = &ppdu_info->eht_info.eht; + enum ath12k_eht_ru_size rtap_ru_size = ATH12K_EHT_RU_INVALID; + u32 ru_width, reception_type, ru_index = HAL_EHT_RU_INVALID; + u32 ru_type_80_0, ru_start_index_80_0; + u32 ru_type_80_1, ru_start_index_80_1; + u32 ru_type_80_2, ru_start_index_80_2; + u32 ru_type_80_3, ru_start_index_80_3; + u32 ru_size = 0, num_80mhz_with_ru = 0; + u64 ru_index_320mhz = 0; + u32 ru_index_per80mhz; + + reception_type = le32_get_bits(rx_usr_info->info0, + HAL_RX_USR_INFO0_RECEPTION_TYPE); + + switch (reception_type) { + case HAL_RECEPTION_TYPE_SU: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_SU; + break; + case HAL_RECEPTION_TYPE_DL_MU_MIMO: + case HAL_RECEPTION_TYPE_UL_MU_MIMO: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_MIMO; + break; + case HAL_RECEPTION_TYPE_DL_MU_OFMA: + case HAL_RECEPTION_TYPE_UL_MU_OFDMA: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA; + break; + case HAL_RECEPTION_TYPE_DL_MU_OFDMA_MIMO: + case HAL_RECEPTION_TYPE_UL_MU_OFDMA_MIMO: + ppdu_info->reception_type = HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO; + } + + ppdu_info->is_stbc = le32_get_bits(rx_usr_info->info0, HAL_RX_USR_INFO0_STBC); + ppdu_info->ldpc = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_LDPC); + ppdu_info->dcm = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_STA_DCM); + ppdu_info->bw = le32_get_bits(rx_usr_info->info1, HAL_RX_USR_INFO1_RX_BW); + ppdu_info->mcs = le32_get_bits(rx_usr_info->info1, HAL_RX_USR_INFO1_MCS); + ppdu_info->nss = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_NSS) + 1; + + if (user_id < HAL_MAX_UL_MU_USERS) { + mon_rx_user_status = &ppdu_info->userstats[user_id]; + mon_rx_user_status->mcs = ppdu_info->mcs; + mon_rx_user_status->nss = ppdu_info->nss; + } + + if (!(ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_MIMO || + ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA || + ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_MU_OFDMA_MIMO)) + return; + + /* RU allocation present only for OFDMA reception */ + ru_type_80_0 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_0); + ru_start_index_80_0 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_0); + if (ru_type_80_0 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_0; + ru_index_per80mhz = ru_start_index_80_0; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_0, 0, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_1 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_1); + ru_start_index_80_1 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_1); + if (ru_type_80_1 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_1; + ru_index_per80mhz = ru_start_index_80_1; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_1, 1, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_2 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_2); + ru_start_index_80_2 = le32_get_bits(rx_usr_info->info3, + HAL_RX_USR_INFO3_RU_START_IDX_80_2); + if (ru_type_80_2 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_2; + ru_index_per80mhz = ru_start_index_80_2; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_2, 2, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + ru_type_80_3 = le32_get_bits(rx_usr_info->info2, HAL_RX_USR_INFO2_RU_TYPE_80_3); + ru_start_index_80_3 = le32_get_bits(rx_usr_info->info2, + HAL_RX_USR_INFO3_RU_START_IDX_80_3); + if (ru_type_80_3 != HAL_EHT_RU_NONE) { + ru_size += ru_type_80_3; + ru_index_per80mhz = ru_start_index_80_3; + ru_index = ru_index_per80mhz; + ru_index_320mhz |= HAL_RU_PER80(ru_type_80_3, 3, ru_index_per80mhz); + num_80mhz_with_ru++; + } + + if (num_80mhz_with_ru > 1) { + /* Calculate the MRU index */ + switch (ru_index_320mhz) { + case HAL_EHT_RU_996_484_0: + case HAL_EHT_RU_996x2_484_0: + case HAL_EHT_RU_996x3_484_0: + ru_index = 0; + break; + case HAL_EHT_RU_996_484_1: + case HAL_EHT_RU_996x2_484_1: + case HAL_EHT_RU_996x3_484_1: + ru_index = 1; + break; + case HAL_EHT_RU_996_484_2: + case HAL_EHT_RU_996x2_484_2: + case HAL_EHT_RU_996x3_484_2: + ru_index = 2; + break; + case HAL_EHT_RU_996_484_3: + case HAL_EHT_RU_996x2_484_3: + case HAL_EHT_RU_996x3_484_3: + ru_index = 3; + break; + case HAL_EHT_RU_996_484_4: + case HAL_EHT_RU_996x2_484_4: + case HAL_EHT_RU_996x3_484_4: + ru_index = 4; + break; + case HAL_EHT_RU_996_484_5: + case HAL_EHT_RU_996x2_484_5: + case HAL_EHT_RU_996x3_484_5: + ru_index = 5; + break; + case HAL_EHT_RU_996_484_6: + case HAL_EHT_RU_996x2_484_6: + case HAL_EHT_RU_996x3_484_6: + ru_index = 6; + break; + case HAL_EHT_RU_996_484_7: + case HAL_EHT_RU_996x2_484_7: + case HAL_EHT_RU_996x3_484_7: + ru_index = 7; + break; + case HAL_EHT_RU_996x2_484_8: + ru_index = 8; + break; + case HAL_EHT_RU_996x2_484_9: + ru_index = 9; + break; + case HAL_EHT_RU_996x2_484_10: + ru_index = 10; + break; + case HAL_EHT_RU_996x2_484_11: + ru_index = 11; + break; + default: + ru_index = HAL_EHT_RU_INVALID; + break; + } + + ru_size += 4; + } + + rtap_ru_size = hal_rx_mon_hal_ru_size_to_ath12k_ru_size(ru_size); + if (rtap_ru_size != ATH12K_EHT_RU_INVALID) { + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_RU_MRU_SIZE_OM; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[1]); + data |= u32_encode_bits(rtap_ru_size, + IEEE80211_RADIOTAP_EHT_DATA1_RU_SIZE); + eht->data[1] = cpu_to_le32(data); + } + + if (ru_index != HAL_EHT_RU_INVALID) { + u32 known, data; + + known = __le32_to_cpu(eht->known); + known |= IEEE80211_RADIOTAP_EHT_KNOWN_RU_MRU_INDEX_OM; + eht->known = cpu_to_le32(known); + + data = __le32_to_cpu(eht->data[1]); + data |= u32_encode_bits(rtap_ru_size, + IEEE80211_RADIOTAP_EHT_DATA1_RU_INDEX); + eht->data[1] = cpu_to_le32(data); + } + + if (mon_rx_user_status && ru_index != HAL_EHT_RU_INVALID && + rtap_ru_size != ATH12K_EHT_RU_INVALID) { + mon_rx_user_status->ul_ofdma_ru_start_index = ru_index; + mon_rx_user_status->ul_ofdma_ru_size = rtap_ru_size; + + ru_width = hal_rx_ul_ofdma_ru_size_to_width(rtap_ru_size); + + mon_rx_user_status->ul_ofdma_ru_width = ru_width; + mon_rx_user_status->ofdma_info_valid = 1; + } +} + +static void ath12k_dp_mon_parse_rx_msdu_end_err(u32 info, u32 *errmap) +{ + if (info & RX_MSDU_END_INFO13_FCS_ERR) + *errmap |= HAL_RX_MPDU_ERR_FCS; + + if (info & RX_MSDU_END_INFO13_DECRYPT_ERR) + *errmap |= HAL_RX_MPDU_ERR_DECRYPT; + + if (info & RX_MSDU_END_INFO13_TKIP_MIC_ERR) + *errmap |= HAL_RX_MPDU_ERR_TKIP_MIC; + + if (info & RX_MSDU_END_INFO13_A_MSDU_ERROR) + *errmap |= HAL_RX_MPDU_ERR_AMSDU_ERR; + + if (info & RX_MSDU_END_INFO13_OVERFLOW_ERR) + *errmap |= HAL_RX_MPDU_ERR_OVERFLOW; + + if (info & RX_MSDU_END_INFO13_MSDU_LEN_ERR) + *errmap |= HAL_RX_MPDU_ERR_MSDU_LEN; + + if (info & RX_MSDU_END_INFO13_MPDU_LEN_ERR) + *errmap |= HAL_RX_MPDU_ERR_MPDU_LEN; +} + +static void +ath12k_dp_mon_parse_status_msdu_end(struct ath12k_mon_data *pmon, + const struct hal_rx_msdu_end *msdu_end) +{ + ath12k_dp_mon_parse_rx_msdu_end_err(__le32_to_cpu(msdu_end->info2), + &pmon->err_bitmap); + pmon->decap_format = le32_get_bits(msdu_end->info1, + RX_MSDU_END_INFO11_DECAP_FORMAT); } static enum hal_rx_mon_status -ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, +ath12k_dp_mon_rx_parse_status_tlv(struct ath12k *ar, struct ath12k_mon_data *pmon, - u32 tlv_tag, const void *tlv_data, - u32 userid) + const struct hal_tlv_64_hdr *tlv) { struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; - u32 info[7]; + const void *tlv_data = tlv->value; + u32 info[7], userid; + u16 tlv_tag, tlv_len; + + tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG); + tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN); + userid = le64_get_bits(tlv->tl, HAL_TLV_64_USR_ID); + + if (ppdu_info->tlv_aggr.in_progress && ppdu_info->tlv_aggr.tlv_tag != tlv_tag) { + ath12k_dp_mon_parse_eht_sig_hdr(ppdu_info, ppdu_info->tlv_aggr.buf); + + ppdu_info->tlv_aggr.in_progress = false; + ppdu_info->tlv_aggr.cur_len = 0; + } switch (tlv_tag) { case HAL_RX_PPDU_START: { @@ -638,6 +1538,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, ppdu_info->num_mpdu_fcs_err = u32_get_bits(info[0], HAL_RX_PPDU_END_USER_STATS_INFO0_MPDU_CNT_FCS_ERR); + ppdu_info->peer_id = + u32_get_bits(info[0], HAL_RX_PPDU_END_USER_STATS_INFO0_PEER_ID); + switch (ppdu_info->preamble_type) { case HAL_RX_PREAMBLE_11N: ppdu_info->ht_flags = 1; @@ -648,6 +1551,9 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, case HAL_RX_PREAMBLE_11AX: ppdu_info->he_flags = 1; break; + case HAL_RX_PREAMBLE_11BE: + ppdu_info->is_eht = true; + break; default: break; } @@ -655,6 +1561,11 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, if (userid < HAL_MAX_UL_MU_USERS) { struct hal_rx_user_status *rxuser_stats = &ppdu_info->userstats[userid]; + + if (ppdu_info->num_mpdu_fcs_ok > 1 || + ppdu_info->num_mpdu_fcs_err > 1) + ppdu_info->userstats[userid].ampdu_present = true; + ppdu_info->num_users += 1; ath12k_dp_mon_rx_handle_ofdma_info(eu_stats, rxuser_stats); @@ -730,6 +1641,17 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, HAL_RX_PHYRX_RSSI_LEGACY_INFO_INFO0_RX_BW); break; } + case HAL_PHYRX_OTHER_RECEIVE_INFO: { + const struct hal_phyrx_common_user_info *cmn_usr_info = tlv_data; + + ppdu_info->gi = le32_get_bits(cmn_usr_info->info0, + HAL_RX_PHY_CMN_USER_INFO0_GI); + break; + } + case HAL_RX_PPDU_START_USER_INFO: + ath12k_dp_mon_hal_rx_parse_user_info(tlv_data, userid, ppdu_info); + break; + case HAL_RXPCU_PPDU_END_INFO: { const struct hal_rx_ppdu_end_duration *ppdu_rx_duration = tlv_data; @@ -743,7 +1665,6 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, } case HAL_RX_MPDU_START: { const struct hal_rx_mpdu_start *mpdu_start = tlv_data; - struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; u16 peer_id; info[1] = __le32_to_cpu(mpdu_start->info1); @@ -756,68 +1677,39 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, if (userid < HAL_MAX_UL_MU_USERS) { info[0] = __le32_to_cpu(mpdu_start->info0); ppdu_info->userid = userid; - ppdu_info->ampdu_id[userid] = - u32_get_bits(info[0], HAL_RX_MPDU_START_INFO1_PEERID); + ppdu_info->userstats[userid].ampdu_id = + u32_get_bits(info[0], HAL_RX_MPDU_START_INFO0_PPDU_ID); } - mon_mpdu = kzalloc(sizeof(*mon_mpdu), GFP_ATOMIC); - if (!mon_mpdu) - return HAL_RX_MON_STATUS_PPDU_NOT_DONE; - - break; + return HAL_RX_MON_STATUS_MPDU_START; } case HAL_RX_MSDU_START: /* TODO: add msdu start parsing logic */ break; - case HAL_MON_BUF_ADDR: { - struct dp_rxdma_mon_ring *buf_ring = &ab->dp.rxdma_mon_buf_ring; - const struct dp_mon_packet_info *packet_info = tlv_data; - int buf_id = u32_get_bits(packet_info->cookie, - DP_RXDMA_BUF_COOKIE_BUF_ID); - struct sk_buff *msdu; - struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; - struct ath12k_skb_rxcb *rxcb; - - spin_lock_bh(&buf_ring->idr_lock); - msdu = idr_remove(&buf_ring->bufs_idr, buf_id); - spin_unlock_bh(&buf_ring->idr_lock); - - if (unlikely(!msdu)) { - ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", - buf_id); - return HAL_RX_MON_STATUS_PPDU_NOT_DONE; + case HAL_MON_BUF_ADDR: + return HAL_RX_MON_STATUS_BUF_ADDR; + case HAL_RX_MSDU_END: + ath12k_dp_mon_parse_status_msdu_end(pmon, tlv_data); + return HAL_RX_MON_STATUS_MSDU_END; + case HAL_RX_MPDU_END: + return HAL_RX_MON_STATUS_MPDU_END; + case HAL_PHYRX_GENERIC_U_SIG: + ath12k_dp_mon_hal_rx_parse_u_sig_hdr(tlv_data, ppdu_info); + break; + case HAL_PHYRX_GENERIC_EHT_SIG: + /* Handle the case where aggregation is in progress + * or the current TLV is one of the TLVs which should be + * aggregated + */ + if (!ppdu_info->tlv_aggr.in_progress) { + ppdu_info->tlv_aggr.in_progress = true; + ppdu_info->tlv_aggr.tlv_tag = tlv_tag; + ppdu_info->tlv_aggr.cur_len = 0; } - rxcb = ATH12K_SKB_RXCB(msdu); - dma_unmap_single(ab->dev, rxcb->paddr, - msdu->len + skb_tailroom(msdu), - DMA_FROM_DEVICE); - - if (mon_mpdu->tail) - mon_mpdu->tail->next = msdu; - else - mon_mpdu->tail = msdu; - - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); + ppdu_info->is_eht = true; - break; - } - case HAL_RX_MSDU_END: { - const struct rx_msdu_end_qcn9274 *msdu_end = tlv_data; - bool is_first_msdu_in_mpdu; - u16 msdu_end_info; - - msdu_end_info = __le16_to_cpu(msdu_end->info5); - is_first_msdu_in_mpdu = u32_get_bits(msdu_end_info, - RX_MSDU_END_INFO5_FIRST_MSDU); - if (is_first_msdu_in_mpdu) { - pmon->mon_mpdu->head = pmon->mon_mpdu->tail; - pmon->mon_mpdu->tail = NULL; - } - break; - } - case HAL_RX_MPDU_END: - list_add_tail(&pmon->mon_mpdu->list, &pmon->dp_rx_mon_mpdu_list); + ath12k_dp_mon_hal_aggr_tlv(ppdu_info, tlv_len, tlv_data); break; case HAL_DUMMY: return HAL_RX_MON_STATUS_BUF_DONE; @@ -831,45 +1723,295 @@ ath12k_dp_mon_rx_parse_status_tlv(struct ath12k_base *ab, return HAL_RX_MON_STATUS_PPDU_NOT_DONE; } +static void +ath12k_dp_mon_fill_rx_stats_info(struct ath12k *ar, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct ieee80211_rx_status *rx_status) +{ + u32 center_freq = ppdu_info->freq; + + rx_status->freq = center_freq; + rx_status->bw = ath12k_mac_bw_to_mac80211_bw(ppdu_info->bw); + rx_status->nss = ppdu_info->nss; + rx_status->rate_idx = 0; + rx_status->encoding = RX_ENC_LEGACY; + rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; + + if (center_freq >= ATH12K_MIN_6GHZ_FREQ && + center_freq <= ATH12K_MAX_6GHZ_FREQ) { + rx_status->band = NL80211_BAND_6GHZ; + } else if (center_freq >= ATH12K_MIN_2GHZ_FREQ && + center_freq <= ATH12K_MAX_2GHZ_FREQ) { + rx_status->band = NL80211_BAND_2GHZ; + } else if (center_freq >= ATH12K_MIN_5GHZ_FREQ && + center_freq <= ATH12K_MAX_5GHZ_FREQ) { + rx_status->band = NL80211_BAND_5GHZ; + } else { + rx_status->band = NUM_NL80211_BANDS; + } +} + +static struct sk_buff +*ath12k_dp_rx_alloc_mon_status_buf(struct ath12k_base *ab, + struct dp_rxdma_mon_ring *rx_ring, + int *buf_id) +{ + struct sk_buff *skb; + dma_addr_t paddr; + + skb = dev_alloc_skb(RX_MON_STATUS_BUF_SIZE); + + if (!skb) + goto fail_alloc_skb; + + if (!IS_ALIGNED((unsigned long)skb->data, + RX_MON_STATUS_BUF_ALIGN)) { + skb_pull(skb, PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (unlikely(dma_mapping_error(ab->dev, paddr))) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + *buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max, GFP_ATOMIC); + spin_unlock_bh(&rx_ring->idr_lock); + if (*buf_id < 0) + goto fail_dma_unmap; + + ATH12K_SKB_RXCB(skb)->paddr = paddr; + return skb; + +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); +fail_free_skb: + dev_kfree_skb_any(skb); +fail_alloc_skb: + return NULL; +} + +static enum dp_mon_status_buf_state +ath12k_dp_rx_mon_buf_done(struct ath12k_base *ab, struct hal_srng *srng, + struct dp_rxdma_mon_ring *rx_ring) +{ + struct ath12k_skb_rxcb *rxcb; + struct hal_tlv_64_hdr *tlv; + struct sk_buff *skb; + void *status_desc; + dma_addr_t paddr; + u32 cookie; + int buf_id; + u8 rbm; + + status_desc = ath12k_hal_srng_src_next_peek(ab, srng); + if (!status_desc) + return DP_MON_STATUS_NO_DMA; + + ath12k_hal_rx_buf_addr_info_get(status_desc, &paddr, &cookie, &rbm); + + buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + if (!skb) + return DP_MON_STATUS_NO_DMA; + + rxcb = ATH12K_SKB_RXCB(skb); + dma_sync_single_for_cpu(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + tlv = (struct hal_tlv_64_hdr *)skb->data; + if (le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG) != HAL_RX_STATUS_BUFFER_DONE) + return DP_MON_STATUS_NO_DMA; + + return DP_MON_STATUS_REPLINISH; +} + +static u32 ath12k_dp_mon_comp_ppduid(u32 msdu_ppdu_id, u32 *ppdu_id) +{ + u32 ret = 0; + + if ((*ppdu_id < msdu_ppdu_id) && + ((msdu_ppdu_id - *ppdu_id) < DP_NOT_PPDU_ID_WRAP_AROUND)) { + /* Hold on mon dest ring, and reap mon status ring. */ + *ppdu_id = msdu_ppdu_id; + ret = msdu_ppdu_id; + } else if ((*ppdu_id > msdu_ppdu_id) && + ((*ppdu_id - msdu_ppdu_id) > DP_NOT_PPDU_ID_WRAP_AROUND)) { + /* PPDU ID has exceeded the maximum value and will + * restart from 0. + */ + *ppdu_id = msdu_ppdu_id; + ret = msdu_ppdu_id; + } + return ret; +} + +static +void ath12k_dp_mon_next_link_desc_get(struct hal_rx_msdu_link *msdu_link, + dma_addr_t *paddr, u32 *sw_cookie, u8 *rbm, + struct ath12k_buffer_addr **pp_buf_addr_info) +{ + struct ath12k_buffer_addr *buf_addr_info; + + buf_addr_info = &msdu_link->buf_addr_info; + + ath12k_hal_rx_buf_addr_info_get(buf_addr_info, paddr, sw_cookie, rbm); + + *pp_buf_addr_info = buf_addr_info; +} + +static void +ath12k_dp_mon_fill_rx_rate(struct ath12k *ar, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_supported_band *sband; + enum rx_msdu_start_pkt_type pkt_type; + u8 rate_mcs, nss, sgi; + bool is_cck; + + pkt_type = ppdu_info->preamble_type; + rate_mcs = ppdu_info->rate; + nss = ppdu_info->nss; + sgi = ppdu_info->gi; + + switch (pkt_type) { + case RX_MSDU_START_PKT_TYPE_11A: + case RX_MSDU_START_PKT_TYPE_11B: + is_cck = (pkt_type == RX_MSDU_START_PKT_TYPE_11B); + if (rx_status->band < NUM_NL80211_BANDS) { + sband = &ar->mac.sbands[rx_status->band]; + rx_status->rate_idx = ath12k_mac_hw_rate_to_idx(sband, rate_mcs, + is_cck); + } + break; + case RX_MSDU_START_PKT_TYPE_11N: + rx_status->encoding = RX_ENC_HT; + if (rate_mcs > ATH12K_HT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in HT mode %d\n", + rate_mcs); + break; + } + rx_status->rate_idx = rate_mcs + (8 * (nss - 1)); + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + break; + case RX_MSDU_START_PKT_TYPE_11AC: + rx_status->encoding = RX_ENC_VHT; + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH12K_VHT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in VHT mode %d\n", + rate_mcs); + break; + } + if (sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + break; + case RX_MSDU_START_PKT_TYPE_11AX: + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH12K_HE_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in HE mode %d\n", + rate_mcs); + break; + } + rx_status->encoding = RX_ENC_HE; + rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi); + break; + case RX_MSDU_START_PKT_TYPE_11BE: + rx_status->rate_idx = rate_mcs; + if (rate_mcs > ATH12K_EHT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in EHT mode %d\n", + rate_mcs); + break; + } + rx_status->encoding = RX_ENC_EHT; + rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi); + break; + default: + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "monitor receives invalid preamble type %d", + pkt_type); + break; + } +} + static void ath12k_dp_mon_rx_msdus_set_payload(struct ath12k *ar, struct sk_buff *head_msdu, struct sk_buff *tail_msdu) { - u32 rx_pkt_offset, l2_hdr_offset; + u32 rx_pkt_offset, l2_hdr_offset, total_offset; rx_pkt_offset = ar->ab->hal.hal_desc_sz; l2_hdr_offset = ath12k_dp_rx_h_l3pad(ar->ab, (struct hal_rx_desc *)tail_msdu->data); - skb_pull(head_msdu, rx_pkt_offset + l2_hdr_offset); + + if (ar->ab->hw_params->rxdma1_enable) + total_offset = ATH12K_MON_RX_PKT_OFFSET; + else + total_offset = rx_pkt_offset + l2_hdr_offset; + + skb_pull(head_msdu, total_offset); } static struct sk_buff * -ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id, - struct sk_buff *head_msdu, struct sk_buff *tail_msdu, - struct ieee80211_rx_status *rxs, bool *fcs_err) +ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, + struct dp_mon_mpdu *mon_mpdu, + struct hal_rx_mon_ppdu_info *ppdu_info, + struct ieee80211_rx_status *rxs) { struct ath12k_base *ab = ar->ab; struct sk_buff *msdu, *mpdu_buf, *prev_buf, *head_frag_list; - struct hal_rx_desc *rx_desc, *tail_rx_desc; - u8 *hdr_desc, *dest, decap_format; + struct sk_buff *head_msdu, *tail_msdu; + struct hal_rx_desc *rx_desc; + u8 *hdr_desc, *dest, decap_format = mon_mpdu->decap_format; struct ieee80211_hdr_3addr *wh; - u32 err_bitmap, frag_list_sum_len = 0; + struct ieee80211_channel *channel; + u32 frag_list_sum_len = 0; + u8 channel_num = ppdu_info->chan_num; mpdu_buf = NULL; + head_msdu = mon_mpdu->head; + tail_msdu = mon_mpdu->tail; - if (!head_msdu) + if (!head_msdu || !tail_msdu) goto err_merge_fail; - rx_desc = (struct hal_rx_desc *)head_msdu->data; - tail_rx_desc = (struct hal_rx_desc *)tail_msdu->data; + ath12k_dp_mon_fill_rx_stats_info(ar, ppdu_info, rxs); - err_bitmap = ath12k_dp_rx_h_mpdu_err(ab, tail_rx_desc); - if (err_bitmap & HAL_RX_MPDU_ERR_FCS) - *fcs_err = true; + if (unlikely(rxs->band == NUM_NL80211_BANDS || + !ath12k_ar_to_hw(ar)->wiphy->bands[rxs->band])) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "sband is NULL for status band %d channel_num %d center_freq %d pdev_id %d\n", + rxs->band, channel_num, ppdu_info->freq, ar->pdev_idx); - decap_format = ath12k_dp_rx_h_decap_type(ab, tail_rx_desc); + spin_lock_bh(&ar->data_lock); + channel = ar->rx_channel; + if (channel) { + rxs->band = channel->band; + channel_num = + ieee80211_frequency_to_channel(channel->center_freq); + } + spin_unlock_bh(&ar->data_lock); + } - ath12k_dp_rx_h_ppdu(ar, tail_rx_desc, rxs); + if (rxs->band < NUM_NL80211_BANDS) + rxs->freq = ieee80211_channel_to_frequency(channel_num, + rxs->band); + + ath12k_dp_mon_fill_rx_rate(ar, ppdu_info, rxs); if (decap_format == DP_RX_DECAP_TYPE_RAW) { ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu); @@ -879,7 +2021,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id, head_frag_list = NULL; while (msdu) { - ath12k_dp_mon_rx_msdus_set_payload(ar, msdu, tail_msdu); + ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu); if (!head_frag_list) head_frag_list = msdu; @@ -891,7 +2033,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id, prev_buf->next = NULL; - skb_trim(prev_buf, prev_buf->len - HAL_RX_FCS_LEN); + skb_trim(prev_buf, prev_buf->len); if (head_frag_list) { skb_shinfo(head_msdu)->frag_list = head_frag_list; head_msdu->data_len = frag_list_sum_len; @@ -914,7 +2056,7 @@ ath12k_dp_mon_rx_merg_msdus(struct ath12k *ar, u32 mac_id, msdu = head_msdu; while (msdu) { - ath12k_dp_mon_rx_msdus_set_payload(ar, msdu, tail_msdu); + ath12k_dp_mon_rx_msdus_set_payload(ar, head_msdu, tail_msdu); if (qos_pkt) { dest = skb_push(msdu, sizeof(__le16)); if (!dest) @@ -1005,18 +2147,70 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar, { struct ieee80211_supported_band *sband; u8 *ptr = NULL; - u16 ampdu_id = ppduinfo->ampdu_id[ppduinfo->userid]; rxs->flag |= RX_FLAG_MACTIME_START; rxs->signal = ppduinfo->rssi_comb + ATH12K_DEFAULT_NOISE_FLOOR; rxs->nss = ppduinfo->nss + 1; - if (ampdu_id) { + if (ppduinfo->userstats[ppduinfo->userid].ampdu_present) { rxs->flag |= RX_FLAG_AMPDU_DETAILS; - rxs->ampdu_reference = ampdu_id; + rxs->ampdu_reference = ppduinfo->userstats[ppduinfo->userid].ampdu_id; } - if (ppduinfo->he_mu_flags) { + if (ppduinfo->is_eht || ppduinfo->eht_usig) { + struct ieee80211_radiotap_tlv *tlv; + struct ieee80211_radiotap_eht *eht; + struct ieee80211_radiotap_eht_usig *usig; + u16 len = 0, i, eht_len, usig_len; + u8 user; + + if (ppduinfo->is_eht) { + eht_len = struct_size(eht, + user_info, + ppduinfo->eht_info.num_user_info); + len += sizeof(*tlv) + eht_len; + } + + if (ppduinfo->eht_usig) { + usig_len = sizeof(*usig); + len += sizeof(*tlv) + usig_len; + } + + rxs->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; + rxs->encoding = RX_ENC_EHT; + + skb_reset_mac_header(mon_skb); + + tlv = skb_push(mon_skb, len); + + if (ppduinfo->is_eht) { + tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT); + tlv->len = cpu_to_le16(eht_len); + + eht = (struct ieee80211_radiotap_eht *)tlv->data; + eht->known = ppduinfo->eht_info.eht.known; + + for (i = 0; + i < ARRAY_SIZE(eht->data) && + i < ARRAY_SIZE(ppduinfo->eht_info.eht.data); + i++) + eht->data[i] = ppduinfo->eht_info.eht.data[i]; + + for (user = 0; user < ppduinfo->eht_info.num_user_info; user++) + put_unaligned_le32(ppduinfo->eht_info.user_info[user], + &eht->user_info[user]); + + tlv = (struct ieee80211_radiotap_tlv *)&tlv->data[eht_len]; + } + + if (ppduinfo->eht_usig) { + tlv->type = cpu_to_le16(IEEE80211_RADIOTAP_EHT_USIG); + tlv->len = cpu_to_le16(usig_len); + + usig = (struct ieee80211_radiotap_eht_usig *)tlv->data; + *usig = ppduinfo->u_sig_info.usig; + } + } else if (ppduinfo->he_mu_flags) { rxs->flag |= RX_FLAG_RADIOTAP_HE_MU; rxs->encoding = RX_ENC_HE; ptr = skb_push(mon_skb, sizeof(struct ieee80211_radiotap_he_mu)); @@ -1045,7 +2239,8 @@ static void ath12k_dp_mon_update_radiotap(struct ath12k *ar, static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *napi, struct sk_buff *msdu, - struct ieee80211_rx_status *status) + struct ieee80211_rx_status *status, + u8 decap) { static const struct ieee80211_radiotap_he known = { .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | @@ -1057,10 +2252,12 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct struct ieee80211_sta *pubsta = NULL; struct ath12k_peer *peer; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - u8 decap = DP_RX_DECAP_TYPE_RAW; + struct ath12k_dp_rx_info rx_info; bool is_mcbc = rxcb->is_mcbc; bool is_eapol_tkip = rxcb->is_eapol; + status->link_valid = 0; + if ((status->encoding == RX_ENC_HE) && !(status->flag & RX_FLAG_RADIOTAP_HE) && !(status->flag & RX_FLAG_SKIP_MONITOR)) { he = skb_push(msdu, sizeof(known)); @@ -1068,10 +2265,9 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct status->flag |= RX_FLAG_RADIOTAP_HE; } - if (!(status->flag & RX_FLAG_ONLY_MONITOR)) - decap = ath12k_dp_rx_h_decap_type(ar->ab, rxcb->rx_desc); spin_lock_bh(&ar->ab->base_lock); - peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu); + rx_info.addr2_present = false; + peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu, &rx_info); if (peer && peer->sta) { pubsta = peer->sta; if (pubsta->valid_links) { @@ -1125,26 +2321,24 @@ static void ath12k_dp_mon_rx_deliver_msdu(struct ath12k *ar, struct napi_struct ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi); } -static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, - struct sk_buff *head_msdu, struct sk_buff *tail_msdu, +static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, + struct dp_mon_mpdu *mon_mpdu, struct hal_rx_mon_ppdu_info *ppduinfo, struct napi_struct *napi) { struct ath12k_pdev_dp *dp = &ar->dp; struct sk_buff *mon_skb, *skb_next, *header; struct ieee80211_rx_status *rxs = &dp->rx_status; - bool fcs_err = false; + u8 decap = DP_RX_DECAP_TYPE_RAW; - mon_skb = ath12k_dp_mon_rx_merg_msdus(ar, mac_id, - head_msdu, tail_msdu, - rxs, &fcs_err); + mon_skb = ath12k_dp_mon_rx_merg_msdus(ar, mon_mpdu, ppduinfo, rxs); if (!mon_skb) goto mon_deliver_fail; header = mon_skb; rxs->flag = 0; - if (fcs_err) + if (mon_mpdu->err_bitmap & HAL_RX_MPDU_ERR_FCS) rxs->flag = RX_FLAG_FAILED_FCS_CRC; do { @@ -1161,8 +2355,12 @@ static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, rxs->flag |= RX_FLAG_ALLOW_SAME_PN; } rxs->flag |= RX_FLAG_ONLY_MONITOR; + + if (!(rxs->flag & RX_FLAG_ONLY_MONITOR)) + decap = mon_mpdu->decap_format; + ath12k_dp_mon_update_radiotap(ar, ppduinfo, mon_skb, rxs); - ath12k_dp_mon_rx_deliver_msdu(ar, napi, mon_skb, rxs); + ath12k_dp_mon_rx_deliver_msdu(ar, napi, mon_skb, rxs, decap); mon_skb = skb_next; } while (mon_skb); rxs->flag = 0; @@ -1170,7 +2368,7 @@ static int ath12k_dp_mon_rx_deliver(struct ath12k *ar, u32 mac_id, return 0; mon_deliver_fail: - mon_skb = head_msdu; + mon_skb = mon_mpdu->head; while (mon_skb) { skb_next = mon_skb->next; dev_kfree_skb_any(mon_skb); @@ -1179,25 +2377,146 @@ mon_deliver_fail: return -EINVAL; } +static int ath12k_dp_pkt_set_pktlen(struct sk_buff *skb, u32 len) +{ + if (skb->len > len) { + skb_trim(skb, len); + } else { + if (skb_tailroom(skb) < len - skb->len) { + if ((pskb_expand_head(skb, 0, + len - skb->len - skb_tailroom(skb), + GFP_ATOMIC))) { + return -ENOMEM; + } + } + skb_put(skb, (len - skb->len)); + } + + return 0; +} + +/* Hardware fill buffer with 128 bytes aligned. So need to reap it + * with 128 bytes aligned. + */ +#define RXDMA_DATA_DMA_BLOCK_SIZE 128 + +static void +ath12k_dp_mon_get_buf_len(struct hal_rx_msdu_desc_info *info, + bool *is_frag, u32 *total_len, + u32 *frag_len, u32 *msdu_cnt) +{ + if (info->msdu_flags & RX_MSDU_DESC_INFO0_MSDU_CONTINUATION) { + *is_frag = true; + *frag_len = (RX_MON_STATUS_BASE_BUF_SIZE - + sizeof(struct hal_rx_desc)) & + ~(RXDMA_DATA_DMA_BLOCK_SIZE - 1); + *total_len += *frag_len; + } else { + if (*is_frag) + *frag_len = info->msdu_len - *total_len; + else + *frag_len = info->msdu_len; + + *msdu_cnt -= 1; + } +} + +static int +ath12k_dp_mon_parse_status_buf(struct ath12k *ar, + struct ath12k_mon_data *pmon, + const struct dp_mon_packet_info *packet_info) +{ + struct ath12k_base *ab = ar->ab; + struct dp_rxdma_mon_ring *buf_ring = &ab->dp.rxdma_mon_buf_ring; + struct sk_buff *msdu; + int buf_id; + u32 offset; + + buf_id = u32_get_bits(packet_info->cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); + + spin_lock_bh(&buf_ring->idr_lock); + msdu = idr_remove(&buf_ring->bufs_idr, buf_id); + spin_unlock_bh(&buf_ring->idr_lock); + + if (unlikely(!msdu)) { + ath12k_warn(ab, "mon dest desc with inval buf_id %d\n", buf_id); + return 0; + } + + dma_unmap_single(ab->dev, ATH12K_SKB_RXCB(msdu)->paddr, + msdu->len + skb_tailroom(msdu), + DMA_FROM_DEVICE); + + offset = packet_info->dma_length + ATH12K_MON_RX_DOT11_OFFSET; + if (ath12k_dp_pkt_set_pktlen(msdu, offset)) { + dev_kfree_skb_any(msdu); + goto dest_replenish; + } + + if (!pmon->mon_mpdu->head) + pmon->mon_mpdu->head = msdu; + else + pmon->mon_mpdu->tail->next = msdu; + + pmon->mon_mpdu->tail = msdu; + +dest_replenish: + ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); + + return 0; +} + +static int +ath12k_dp_mon_parse_rx_dest_tlv(struct ath12k *ar, + struct ath12k_mon_data *pmon, + enum hal_rx_mon_status hal_status, + const void *tlv_data) +{ + switch (hal_status) { + case HAL_RX_MON_STATUS_MPDU_START: + if (WARN_ON_ONCE(pmon->mon_mpdu)) + break; + + pmon->mon_mpdu = kzalloc(sizeof(*pmon->mon_mpdu), GFP_ATOMIC); + if (!pmon->mon_mpdu) + return -ENOMEM; + break; + case HAL_RX_MON_STATUS_BUF_ADDR: + return ath12k_dp_mon_parse_status_buf(ar, pmon, tlv_data); + case HAL_RX_MON_STATUS_MPDU_END: + /* If no MSDU then free empty MPDU */ + if (pmon->mon_mpdu->tail) { + pmon->mon_mpdu->tail->next = NULL; + list_add_tail(&pmon->mon_mpdu->list, &pmon->dp_rx_mon_mpdu_list); + } else { + kfree(pmon->mon_mpdu); + } + pmon->mon_mpdu = NULL; + break; + case HAL_RX_MON_STATUS_MSDU_END: + pmon->mon_mpdu->decap_format = pmon->decap_format; + pmon->mon_mpdu->err_bitmap = pmon->err_bitmap; + break; + default: + break; + } + + return 0; +} + static enum hal_rx_mon_status -ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon, +ath12k_dp_mon_parse_rx_dest(struct ath12k *ar, struct ath12k_mon_data *pmon, struct sk_buff *skb) { - struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; struct hal_tlv_64_hdr *tlv; + struct ath12k_skb_rxcb *rxcb; enum hal_rx_mon_status hal_status; - u32 tlv_userid; u16 tlv_tag, tlv_len; u8 *ptr = skb->data; - memset(ppdu_info, 0, sizeof(struct hal_rx_mon_ppdu_info)); - do { tlv = (struct hal_tlv_64_hdr *)ptr; tlv_tag = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_TAG); - tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN); - tlv_userid = le64_get_bits(tlv->tl, HAL_TLV_64_USR_ID); - ptr += sizeof(*tlv); /* The actual length of PPDU_END is the combined length of many PHY * TLVs that follow. Skip the TLV header and @@ -1207,16 +2526,30 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon if (tlv_tag == HAL_RX_PPDU_END) tlv_len = sizeof(struct hal_rx_rxpcu_classification_overview); + else + tlv_len = le64_get_bits(tlv->tl, HAL_TLV_64_HDR_LEN); - hal_status = ath12k_dp_mon_rx_parse_status_tlv(ab, pmon, - tlv_tag, ptr, tlv_userid); - ptr += tlv_len; + hal_status = ath12k_dp_mon_rx_parse_status_tlv(ar, pmon, tlv); + + if (ar->monitor_started && ar->ab->hw_params->rxdma1_enable && + ath12k_dp_mon_parse_rx_dest_tlv(ar, pmon, hal_status, tlv->value)) + return HAL_RX_MON_STATUS_PPDU_DONE; + + ptr += sizeof(*tlv) + tlv_len; ptr = PTR_ALIGN(ptr, HAL_TLV_64_ALIGN); - if ((ptr - skb->data) >= DP_RX_BUFFER_SIZE) + if ((ptr - skb->data) > skb->len) break; - } while (hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE); + } while ((hal_status == HAL_RX_MON_STATUS_PPDU_NOT_DONE) || + (hal_status == HAL_RX_MON_STATUS_BUF_ADDR) || + (hal_status == HAL_RX_MON_STATUS_MPDU_START) || + (hal_status == HAL_RX_MON_STATUS_MPDU_END) || + (hal_status == HAL_RX_MON_STATUS_MSDU_END)); + + rxcb = ATH12K_SKB_RXCB(skb); + if (rxcb->is_end_of_ppdu) + hal_status = HAL_RX_MON_STATUS_PPDU_DONE; return hal_status; } @@ -1224,31 +2557,27 @@ ath12k_dp_mon_parse_rx_dest(struct ath12k_base *ab, struct ath12k_mon_data *pmon enum hal_rx_mon_status ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi) { - struct ath12k_base *ab = ar->ab; struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; struct dp_mon_mpdu *tmp; struct dp_mon_mpdu *mon_mpdu = pmon->mon_mpdu; - struct sk_buff *head_msdu, *tail_msdu; - enum hal_rx_mon_status hal_status = HAL_RX_MON_STATUS_BUF_DONE; + enum hal_rx_mon_status hal_status; - ath12k_dp_mon_parse_rx_dest(ab, pmon, skb); + hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); + if (hal_status != HAL_RX_MON_STATUS_PPDU_DONE) + return hal_status; list_for_each_entry_safe(mon_mpdu, tmp, &pmon->dp_rx_mon_mpdu_list, list) { list_del(&mon_mpdu->list); - head_msdu = mon_mpdu->head; - tail_msdu = mon_mpdu->tail; - if (head_msdu && tail_msdu) { - ath12k_dp_mon_rx_deliver(ar, mac_id, head_msdu, - tail_msdu, ppdu_info, napi); - } + if (mon_mpdu->head && mon_mpdu->tail) + ath12k_dp_mon_rx_deliver(ar, mon_mpdu, ppdu_info, napi); kfree(mon_mpdu); } + return hal_status; } @@ -1327,6 +2656,94 @@ fail_alloc_skb: return -ENOMEM; } +int ath12k_dp_mon_status_bufs_replenish(struct ath12k_base *ab, + struct dp_rxdma_mon_ring *rx_ring, + int req_entries) +{ + enum hal_rx_buf_return_buf_manager mgr = + ab->hw_params->hal_params->rx_buf_rbm; + int num_free, num_remain, buf_id; + struct ath12k_buffer_addr *desc; + struct hal_srng *srng; + struct sk_buff *skb; + dma_addr_t paddr; + u32 cookie; + + req_entries = min(req_entries, rx_ring->bufs_max); + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath12k_hal_srng_access_begin(ab, srng); + + num_free = ath12k_hal_srng_src_num_free(ab, srng, true); + if (!req_entries && (num_free > (rx_ring->bufs_max * 3) / 4)) + req_entries = num_free; + + req_entries = min(num_free, req_entries); + num_remain = req_entries; + + while (num_remain > 0) { + skb = dev_alloc_skb(RX_MON_STATUS_BUF_SIZE); + if (!skb) + break; + + if (!IS_ALIGNED((unsigned long)skb->data, + RX_MON_STATUS_BUF_ALIGN)) { + skb_pull(skb, + PTR_ALIGN(skb->data, RX_MON_STATUS_BUF_ALIGN) - + skb->data); + } + + paddr = dma_map_single(ab->dev, skb->data, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + if (dma_mapping_error(ab->dev, paddr)) + goto fail_free_skb; + + spin_lock_bh(&rx_ring->idr_lock); + buf_id = idr_alloc(&rx_ring->bufs_idr, skb, 0, + rx_ring->bufs_max * 3, GFP_ATOMIC); + spin_unlock_bh(&rx_ring->idr_lock); + if (buf_id < 0) + goto fail_dma_unmap; + cookie = u32_encode_bits(buf_id, DP_RXDMA_BUF_COOKIE_BUF_ID); + + desc = ath12k_hal_srng_src_get_next_entry(ab, srng); + if (!desc) + goto fail_buf_unassign; + + ATH12K_SKB_RXCB(skb)->paddr = paddr; + + num_remain--; + + ath12k_hal_rx_buf_addr_info_set(desc, paddr, cookie, mgr); + } + + ath12k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; + +fail_buf_unassign: + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); +fail_dma_unmap: + dma_unmap_single(ab->dev, paddr, skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); +fail_free_skb: + dev_kfree_skb_any(skb); + + ath12k_hal_srng_access_end(ab, srng); + + spin_unlock_bh(&srng->lock); + + return req_entries - num_remain; +} + static struct dp_mon_tx_ppdu_info * ath12k_dp_mon_tx_get_ppdu_info(struct ath12k_mon_data *pmon, unsigned int ppdu_id, @@ -1924,21 +3341,18 @@ ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag, } static void -ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id, +ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, struct napi_struct *napi, struct dp_mon_tx_ppdu_info *tx_ppdu_info) { struct dp_mon_mpdu *tmp, *mon_mpdu; - struct sk_buff *head_msdu, *tail_msdu; list_for_each_entry_safe(mon_mpdu, tmp, &tx_ppdu_info->dp_tx_mon_mpdu_list, list) { list_del(&mon_mpdu->list); - head_msdu = mon_mpdu->head; - tail_msdu = mon_mpdu->tail; - if (head_msdu) - ath12k_dp_mon_rx_deliver(ar, mac_id, head_msdu, tail_msdu, + if (mon_mpdu->head) + ath12k_dp_mon_rx_deliver(ar, mon_mpdu, &tx_ppdu_info->rx_status, napi); kfree(mon_mpdu); @@ -1948,7 +3362,6 @@ ath12k_dp_mon_tx_process_ppdu_info(struct ath12k *ar, int mac_id, enum hal_rx_mon_status ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi, u32 ppdu_id) @@ -1995,155 +3408,43 @@ ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, break; } while (tlv_status != DP_MON_TX_FES_STATUS_END); - ath12k_dp_mon_tx_process_ppdu_info(ar, mac_id, napi, tx_data_ppdu_info); - ath12k_dp_mon_tx_process_ppdu_info(ar, mac_id, napi, tx_prot_ppdu_info); + ath12k_dp_mon_tx_process_ppdu_info(ar, napi, tx_data_ppdu_info); + ath12k_dp_mon_tx_process_ppdu_info(ar, napi, tx_prot_ppdu_info); return tlv_status; } -int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, int *budget, - enum dp_monitor_mode monitor_mode, - struct napi_struct *napi) -{ - struct hal_mon_dest_desc *mon_dst_desc; - struct ath12k_pdev_dp *pdev_dp = &ar->dp; - struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&pdev_dp->mon_data; - struct ath12k_base *ab = ar->ab; - struct ath12k_dp *dp = &ab->dp; - struct sk_buff *skb; - struct ath12k_skb_rxcb *rxcb; - struct dp_srng *mon_dst_ring; - struct hal_srng *srng; - struct dp_rxdma_mon_ring *buf_ring; - u64 cookie; - u32 ppdu_id; - int num_buffs_reaped = 0, srng_id, buf_id; - u8 dest_idx = 0, i; - bool end_of_ppdu; - struct hal_rx_mon_ppdu_info *ppdu_info; - struct ath12k_peer *peer = NULL; - - ppdu_info = &pmon->mon_ppdu_info; - memset(ppdu_info, 0, sizeof(*ppdu_info)); - ppdu_info->peer_id = HAL_INVALID_PEERID; - - srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id); - - if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) { - mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id]; - buf_ring = &dp->rxdma_mon_buf_ring; - } else { - return 0; - } - - srng = &ab->hal.srng_list[mon_dst_ring->ring_id]; - - spin_lock_bh(&srng->lock); - ath12k_hal_srng_access_begin(ab, srng); - - while (likely(*budget)) { - *budget -= 1; - mon_dst_desc = ath12k_hal_srng_dst_peek(ab, srng); - if (unlikely(!mon_dst_desc)) - break; - - cookie = le32_to_cpu(mon_dst_desc->cookie); - buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); - - spin_lock_bh(&buf_ring->idr_lock); - skb = idr_remove(&buf_ring->bufs_idr, buf_id); - spin_unlock_bh(&buf_ring->idr_lock); - - if (unlikely(!skb)) { - ath12k_warn(ab, "monitor destination with invalid buf_id %d\n", - buf_id); - goto move_next; - } - - rxcb = ATH12K_SKB_RXCB(skb); - dma_unmap_single(ab->dev, rxcb->paddr, - skb->len + skb_tailroom(skb), - DMA_FROM_DEVICE); - - pmon->dest_skb_q[dest_idx] = skb; - dest_idx++; - ppdu_id = le32_to_cpu(mon_dst_desc->ppdu_id); - end_of_ppdu = le32_get_bits(mon_dst_desc->info0, - HAL_MON_DEST_INFO0_END_OF_PPDU); - if (!end_of_ppdu) - continue; - - for (i = 0; i < dest_idx; i++) { - skb = pmon->dest_skb_q[i]; - - if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) - ath12k_dp_mon_rx_parse_mon_status(ar, pmon, mac_id, - skb, napi); - else - ath12k_dp_mon_tx_parse_mon_status(ar, pmon, mac_id, - skb, napi, ppdu_id); - - peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); - - if (!peer || !peer->sta) { - ath12k_dbg(ab, ATH12K_DBG_DATA, - "failed to find the peer with peer_id %d\n", - ppdu_info->peer_id); - dev_kfree_skb_any(skb); - continue; - } - - dev_kfree_skb_any(skb); - pmon->dest_skb_q[i] = NULL; - } - - dest_idx = 0; -move_next: - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - ath12k_hal_srng_src_get_next_entry(ab, srng); - num_buffs_reaped++; - } - - ath12k_hal_srng_access_end(ab, srng); - spin_unlock_bh(&srng->lock); - - return num_buffs_reaped; -} - static void ath12k_dp_mon_rx_update_peer_rate_table_stats(struct ath12k_rx_peer_stats *rx_stats, struct hal_rx_mon_ppdu_info *ppdu_info, struct hal_rx_user_status *user_stats, u32 num_msdu) { - u32 rate_idx = 0; + struct ath12k_rx_peer_rate_stats *stats; u32 mcs_idx = (user_stats) ? user_stats->mcs : ppdu_info->mcs; u32 nss_idx = (user_stats) ? user_stats->nss - 1 : ppdu_info->nss - 1; u32 bw_idx = ppdu_info->bw; u32 gi_idx = ppdu_info->gi; + u32 len; - if ((mcs_idx > HAL_RX_MAX_MCS_HE) || (nss_idx >= HAL_RX_MAX_NSS) || - (bw_idx >= HAL_RX_BW_MAX) || (gi_idx >= HAL_RX_GI_MAX)) { + if (mcs_idx > HAL_RX_MAX_MCS_HT || nss_idx >= HAL_RX_MAX_NSS || + bw_idx >= HAL_RX_BW_MAX || gi_idx >= HAL_RX_GI_MAX) { return; } - if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11N || - ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AC) { - rate_idx = mcs_idx * 8 + 8 * 10 * nss_idx; - rate_idx += bw_idx * 2 + gi_idx; - } else if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX) { + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11AX || + ppdu_info->preamble_type == HAL_RX_PREAMBLE_11BE) gi_idx = ath12k_he_gi_to_nl80211_he_gi(ppdu_info->gi); - rate_idx = mcs_idx * 12 + 12 * 12 * nss_idx; - rate_idx += bw_idx * 3 + gi_idx; - } else { - return; - } - rx_stats->pkt_stats.rx_rate[rate_idx] += num_msdu; + rx_stats->pkt_stats.rx_rate[bw_idx][gi_idx][nss_idx][mcs_idx] += num_msdu; + stats = &rx_stats->byte_stats; + if (user_stats) - rx_stats->byte_stats.rx_rate[rate_idx] += user_stats->mpdu_ok_byte_count; + len = user_stats->mpdu_ok_byte_count; else - rx_stats->byte_stats.rx_rate[rate_idx] += ppdu_info->mpdu_len; + len = ppdu_info->mpdu_len; + + stats->rx_rate[bw_idx][gi_idx][nss_idx][mcs_idx] += len; } static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar, @@ -2153,11 +3454,11 @@ static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar, struct ath12k_rx_peer_stats *rx_stats = arsta->rx_stats; u32 num_msdu; + arsta->rssi_comb = ppdu_info->rssi_comb; + ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb); if (!rx_stats) return; - arsta->rssi_comb = ppdu_info->rssi_comb; - num_msdu = ppdu_info->tcp_msdu_count + ppdu_info->tcp_ack_msdu_count + ppdu_info->udp_msdu_count + ppdu_info->other_msdu_count; @@ -2229,6 +3530,12 @@ static void ath12k_dp_mon_rx_update_peer_su_stats(struct ath12k *ar, rx_stats->byte_stats.he_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; } + if (ppdu_info->preamble_type == HAL_RX_PREAMBLE_11BE && + ppdu_info->mcs <= HAL_RX_MAX_MCS_BE) { + rx_stats->pkt_stats.be_mcs_count[ppdu_info->mcs] += num_msdu; + rx_stats->byte_stats.be_mcs_count[ppdu_info->mcs] += ppdu_info->mpdu_len; + } + if ((ppdu_info->preamble_type == HAL_RX_PREAMBLE_11A || ppdu_info->preamble_type == HAL_RX_PREAMBLE_11B) && ppdu_info->rate < HAL_RX_LEGACY_RATE_INVALID) { @@ -2323,13 +3630,12 @@ ath12k_dp_mon_rx_update_user_stats(struct ath12k *ar, ahsta = ath12k_sta_to_ahsta(peer->sta); arsta = &ahsta->deflink; + arsta->rssi_comb = ppdu_info->rssi_comb; + ewma_avg_rssi_add(&arsta->avg_rssi, ppdu_info->rssi_comb); rx_stats = arsta->rx_stats; - if (!rx_stats) return; - arsta->rssi_comb = ppdu_info->rssi_comb; - num_msdu = user_stats->tcp_msdu_count + user_stats->tcp_ack_msdu_count + user_stats->udp_msdu_count + user_stats->other_msdu_count; @@ -2415,8 +3721,15 @@ ath12k_dp_mon_rx_update_peer_mu_stats(struct ath12k *ar, ath12k_dp_mon_rx_update_user_stats(ar, ppdu_info, i); } -int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, - struct napi_struct *napi, int *budget) +static void +ath12k_dp_mon_rx_memset_ppdu_info(struct hal_rx_mon_ppdu_info *ppdu_info) +{ + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; +} + +int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, + struct napi_struct *napi) { struct ath12k_base *ab = ar->ab; struct ath12k_pdev_dp *pdev_dp = &ar->dp; @@ -2432,13 +3745,14 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, struct ath12k_sta *ahsta = NULL; struct ath12k_link_sta *arsta; struct ath12k_peer *peer; + struct sk_buff_head skb_list; u64 cookie; int num_buffs_reaped = 0, srng_id, buf_id; - u8 dest_idx = 0, i; - bool end_of_ppdu; - u32 hal_status; + u32 hal_status, end_offset, info0, end_reason; + u8 pdev_idx = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, ar->pdev_idx); - srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id); + __skb_queue_head_init(&skb_list); + srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, pdev_idx); mon_dst_ring = &pdev_dp->rxdma_mon_dst_ring[srng_id]; buf_ring = &dp->rxdma_mon_buf_ring; @@ -2451,6 +3765,15 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, mon_dst_desc = ath12k_hal_srng_dst_peek(ab, srng); if (unlikely(!mon_dst_desc)) break; + + /* In case of empty descriptor, the cookie in the ring descriptor + * is invalid. Therefore, this entry is skipped, and ring processing + * continues. + */ + info0 = le32_to_cpu(mon_dst_desc->info0); + if (u32_get_bits(info0, HAL_MON_DEST_INFO0_EMPTY_DESC)) + goto move_next; + cookie = le32_to_cpu(mon_dst_desc->cookie); buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); @@ -2468,63 +3791,583 @@ int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, dma_unmap_single(ab->dev, rxcb->paddr, skb->len + skb_tailroom(skb), DMA_FROM_DEVICE); - pmon->dest_skb_q[dest_idx] = skb; - dest_idx++; - end_of_ppdu = le32_get_bits(mon_dst_desc->info0, - HAL_MON_DEST_INFO0_END_OF_PPDU); - if (!end_of_ppdu) + + end_reason = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_REASON); + + /* HAL_MON_FLUSH_DETECTED implies that an rx flush received at the end of + * rx PPDU and HAL_MON_PPDU_TRUNCATED implies that the PPDU got + * truncated due to a system level error. In both the cases, buffer data + * can be discarded + */ + if ((end_reason == HAL_MON_FLUSH_DETECTED) || + (end_reason == HAL_MON_PPDU_TRUNCATED)) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "Monitor dest descriptor end reason %d", end_reason); + dev_kfree_skb_any(skb); + goto move_next; + } + + /* Calculate the budget when the ring descriptor with the + * HAL_MON_END_OF_PPDU to ensure that one PPDU worth of data is always + * reaped. This helps to efficiently utilize the NAPI budget. + */ + if (end_reason == HAL_MON_END_OF_PPDU) { + *budget -= 1; + rxcb->is_end_of_ppdu = true; + } + + end_offset = u32_get_bits(info0, HAL_MON_DEST_INFO0_END_OFFSET); + if (likely(end_offset <= DP_RX_BUFFER_SIZE)) { + skb_put(skb, end_offset); + } else { + ath12k_warn(ab, + "invalid offset on mon stats destination %u\n", + end_offset); + skb_put(skb, DP_RX_BUFFER_SIZE); + } + + __skb_queue_tail(&skb_list, skb); + +move_next: + ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); + ath12k_hal_srng_dst_get_next_entry(ab, srng); + num_buffs_reaped++; + } + + ath12k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + if (!num_buffs_reaped) + return 0; + + /* In some cases, one PPDU worth of data can be spread across multiple NAPI + * schedules, To avoid losing existing parsed ppdu_info information, skip + * the memset of the ppdu_info structure and continue processing it. + */ + if (!ppdu_info->ppdu_continuation) + ath12k_dp_mon_rx_memset_ppdu_info(ppdu_info); + + while ((skb = __skb_dequeue(&skb_list))) { + hal_status = ath12k_dp_mon_rx_parse_mon_status(ar, pmon, skb, napi); + if (hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { + ppdu_info->ppdu_continuation = true; + dev_kfree_skb_any(skb); continue; + } + + if (ppdu_info->peer_id == HAL_INVALID_PEERID) + goto free_skb; + + rcu_read_lock(); + spin_lock_bh(&ab->base_lock); + peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); + if (!peer || !peer->sta) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "failed to find the peer with monitor peer_id %d\n", + ppdu_info->peer_id); + goto next_skb; + } + + if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { + ahsta = ath12k_sta_to_ahsta(peer->sta); + arsta = &ahsta->deflink; + ath12k_dp_mon_rx_update_peer_su_stats(ar, arsta, + ppdu_info); + } else if ((ppdu_info->fc_valid) && + (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { + ath12k_dp_mon_rx_process_ulofdma(ppdu_info); + ath12k_dp_mon_rx_update_peer_mu_stats(ar, ppdu_info); + } + +next_skb: + spin_unlock_bh(&ab->base_lock); + rcu_read_unlock(); +free_skb: + dev_kfree_skb_any(skb); + ath12k_dp_mon_rx_memset_ppdu_info(ppdu_info); + } - for (i = 0; i < dest_idx; i++) { - skb = pmon->dest_skb_q[i]; - hal_status = ath12k_dp_mon_parse_rx_dest(ab, pmon, skb); + return num_buffs_reaped; +} + +static int ath12k_dp_rx_reap_mon_status_ring(struct ath12k_base *ab, int mac_id, + int *budget, struct sk_buff_head *skb_list) +{ + const struct ath12k_hw_hal_params *hal_params; + int buf_id, srng_id, num_buffs_reaped = 0; + enum dp_mon_status_buf_state reap_status; + struct dp_rxdma_mon_ring *rx_ring; + struct ath12k_mon_data *pmon; + struct ath12k_skb_rxcb *rxcb; + struct hal_tlv_64_hdr *tlv; + void *rx_mon_status_desc; + struct hal_srng *srng; + struct ath12k_dp *dp; + struct sk_buff *skb; + struct ath12k *ar; + dma_addr_t paddr; + u32 cookie; + u8 rbm; + + ar = ab->pdevs[ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id)].ar; + dp = &ab->dp; + pmon = &ar->dp.mon_data; + srng_id = ath12k_hw_mac_id_to_srng_id(ab->hw_params, mac_id); + rx_ring = &dp->rx_mon_status_refill_ring[srng_id]; + + srng = &ab->hal.srng_list[rx_ring->refill_buf_ring.ring_id]; + + spin_lock_bh(&srng->lock); + + ath12k_hal_srng_access_begin(ab, srng); + + while (*budget) { + *budget -= 1; + rx_mon_status_desc = ath12k_hal_srng_src_peek(ab, srng); + if (!rx_mon_status_desc) { + pmon->buf_state = DP_MON_STATUS_REPLINISH; + break; + } + ath12k_hal_rx_buf_addr_info_get(rx_mon_status_desc, &paddr, + &cookie, &rbm); + if (paddr) { + buf_id = u32_get_bits(cookie, DP_RXDMA_BUF_COOKIE_BUF_ID); + + spin_lock_bh(&rx_ring->idr_lock); + skb = idr_find(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + if (!skb) { + ath12k_warn(ab, "rx monitor status with invalid buf_id %d\n", + buf_id); + pmon->buf_state = DP_MON_STATUS_REPLINISH; + goto move_next; + } + + rxcb = ATH12K_SKB_RXCB(skb); + + dma_sync_single_for_cpu(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + tlv = (struct hal_tlv_64_hdr *)skb->data; + if (le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG) != + HAL_RX_STATUS_BUFFER_DONE) { + pmon->buf_state = DP_MON_STATUS_NO_DMA; + ath12k_warn(ab, + "mon status DONE not set %llx, buf_id %d\n", + le64_get_bits(tlv->tl, HAL_TLV_HDR_TAG), + buf_id); + /* RxDMA status done bit might not be set even + * though tp is moved by HW. + */ + + /* If done status is missing: + * 1. As per MAC team's suggestion, + * when HP + 1 entry is peeked and if DMA + * is not done and if HP + 2 entry's DMA done + * is set. skip HP + 1 entry and + * start processing in next interrupt. + * 2. If HP + 2 entry's DMA done is not set, + * poll onto HP + 1 entry DMA done to be set. + * Check status for same buffer for next time + * dp_rx_mon_status_srng_process + */ + reap_status = ath12k_dp_rx_mon_buf_done(ab, srng, + rx_ring); + if (reap_status == DP_MON_STATUS_NO_DMA) + continue; + + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); - if (ppdu_info->peer_id == HAL_INVALID_PEERID || - hal_status != HAL_RX_MON_STATUS_PPDU_DONE) { dev_kfree_skb_any(skb); - continue; + pmon->buf_state = DP_MON_STATUS_REPLINISH; + goto move_next; } - rcu_read_lock(); - spin_lock_bh(&ab->base_lock); - peer = ath12k_peer_find_by_id(ab, ppdu_info->peer_id); - if (!peer || !peer->sta) { - ath12k_dbg(ab, ATH12K_DBG_DATA, - "failed to find the peer with peer_id %d\n", - ppdu_info->peer_id); - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); + spin_lock_bh(&rx_ring->idr_lock); + idr_remove(&rx_ring->bufs_idr, buf_id); + spin_unlock_bh(&rx_ring->idr_lock); + + dma_unmap_single(ab->dev, rxcb->paddr, + skb->len + skb_tailroom(skb), + DMA_FROM_DEVICE); + + if (ath12k_dp_pkt_set_pktlen(skb, RX_MON_STATUS_BUF_SIZE)) { dev_kfree_skb_any(skb); + goto move_next; + } + __skb_queue_tail(skb_list, skb); + } else { + pmon->buf_state = DP_MON_STATUS_REPLINISH; + } +move_next: + skb = ath12k_dp_rx_alloc_mon_status_buf(ab, rx_ring, + &buf_id); + + if (!skb) { + ath12k_warn(ab, "failed to alloc buffer for status ring\n"); + hal_params = ab->hw_params->hal_params; + ath12k_hal_rx_buf_addr_info_set(rx_mon_status_desc, 0, 0, + hal_params->rx_buf_rbm); + num_buffs_reaped++; + break; + } + rxcb = ATH12K_SKB_RXCB(skb); + + cookie = u32_encode_bits(mac_id, DP_RXDMA_BUF_COOKIE_PDEV_ID) | + u32_encode_bits(buf_id, DP_RXDMA_BUF_COOKIE_BUF_ID); + + ath12k_hal_rx_buf_addr_info_set(rx_mon_status_desc, rxcb->paddr, + cookie, + ab->hw_params->hal_params->rx_buf_rbm); + ath12k_hal_srng_src_get_next_entry(ab, srng); + num_buffs_reaped++; + } + ath12k_hal_srng_access_end(ab, srng); + spin_unlock_bh(&srng->lock); + + return num_buffs_reaped; +} + +static u32 +ath12k_dp_rx_mon_mpdu_pop(struct ath12k *ar, int mac_id, + void *ring_entry, struct sk_buff **head_msdu, + struct sk_buff **tail_msdu, + struct list_head *used_list, + u32 *npackets, u32 *ppdu_id) +{ + struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data; + struct ath12k_buffer_addr *p_buf_addr_info, *p_last_buf_addr_info; + u32 msdu_ppdu_id = 0, msdu_cnt = 0, total_len = 0, frag_len = 0; + u32 rx_buf_size, rx_pkt_offset, sw_cookie; + bool is_frag, is_first_msdu, drop_mpdu = false; + struct hal_reo_entrance_ring *ent_desc = + (struct hal_reo_entrance_ring *)ring_entry; + u32 rx_bufs_used = 0, i = 0, desc_bank = 0; + struct hal_rx_desc *rx_desc, *tail_rx_desc; + struct hal_rx_msdu_link *msdu_link_desc; + struct sk_buff *msdu = NULL, *last = NULL; + struct ath12k_rx_desc_info *desc_info; + struct ath12k_buffer_addr buf_info; + struct hal_rx_msdu_list msdu_list; + struct ath12k_skb_rxcb *rxcb; + u16 num_msdus = 0; + dma_addr_t paddr; + u8 rbm; + + ath12k_hal_rx_reo_ent_buf_paddr_get(ring_entry, &paddr, + &sw_cookie, + &p_last_buf_addr_info, &rbm, + &msdu_cnt); + + spin_lock_bh(&pmon->mon_lock); + + if (le32_get_bits(ent_desc->info1, + HAL_REO_ENTR_RING_INFO1_RXDMA_PUSH_REASON) == + HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED) { + u8 rxdma_err = le32_get_bits(ent_desc->info1, + HAL_REO_ENTR_RING_INFO1_RXDMA_ERROR_CODE); + if (rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR || + rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_MPDU_LEN_ERR || + rxdma_err == HAL_REO_ENTR_RING_RXDMA_ECODE_OVERFLOW_ERR) { + drop_mpdu = true; + pmon->rx_mon_stats.dest_mpdu_drop++; + } + } + + is_frag = false; + is_first_msdu = true; + rx_pkt_offset = sizeof(struct hal_rx_desc); + + do { + if (pmon->mon_last_linkdesc_paddr == paddr) { + pmon->rx_mon_stats.dup_mon_linkdesc_cnt++; + spin_unlock_bh(&pmon->mon_lock); + return rx_bufs_used; + } + + desc_bank = u32_get_bits(sw_cookie, DP_LINK_DESC_BANK_MASK); + msdu_link_desc = + ar->ab->dp.link_desc_banks[desc_bank].vaddr + + (paddr - ar->ab->dp.link_desc_banks[desc_bank].paddr); + + ath12k_hal_rx_msdu_list_get(ar, msdu_link_desc, &msdu_list, + &num_msdus); + desc_info = ath12k_dp_get_rx_desc(ar->ab, + msdu_list.sw_cookie[num_msdus - 1]); + tail_rx_desc = (struct hal_rx_desc *)(desc_info->skb)->data; + + for (i = 0; i < num_msdus; i++) { + u32 l2_hdr_offset; + + if (pmon->mon_last_buf_cookie == msdu_list.sw_cookie[i]) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "i %d last_cookie %d is same\n", + i, pmon->mon_last_buf_cookie); + drop_mpdu = true; + pmon->rx_mon_stats.dup_mon_buf_cnt++; continue; } - if (ppdu_info->reception_type == HAL_RX_RECEPTION_TYPE_SU) { - ahsta = ath12k_sta_to_ahsta(peer->sta); - arsta = &ahsta->deflink; - ath12k_dp_mon_rx_update_peer_su_stats(ar, arsta, - ppdu_info); - } else if ((ppdu_info->fc_valid) && - (ppdu_info->ast_index != HAL_AST_IDX_INVALID)) { - ath12k_dp_mon_rx_process_ulofdma(ppdu_info); - ath12k_dp_mon_rx_update_peer_mu_stats(ar, ppdu_info); + desc_info = + ath12k_dp_get_rx_desc(ar->ab, msdu_list.sw_cookie[i]); + msdu = desc_info->skb; + + if (!msdu) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "msdu_pop: invalid msdu (%d/%d)\n", + i + 1, num_msdus); + goto next_msdu; + } + rxcb = ATH12K_SKB_RXCB(msdu); + if (rxcb->paddr != msdu_list.paddr[i]) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "i %d paddr %lx != %lx\n", + i, (unsigned long)rxcb->paddr, + (unsigned long)msdu_list.paddr[i]); + drop_mpdu = true; + continue; + } + if (!rxcb->unmapped) { + dma_unmap_single(ar->ab->dev, rxcb->paddr, + msdu->len + + skb_tailroom(msdu), + DMA_FROM_DEVICE); + rxcb->unmapped = 1; + } + if (drop_mpdu) { + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "i %d drop msdu %p *ppdu_id %x\n", + i, msdu, *ppdu_id); + dev_kfree_skb_any(msdu); + msdu = NULL; + goto next_msdu; } - spin_unlock_bh(&ab->base_lock); - rcu_read_unlock(); - dev_kfree_skb_any(skb); - memset(ppdu_info, 0, sizeof(*ppdu_info)); - ppdu_info->peer_id = HAL_INVALID_PEERID; + rx_desc = (struct hal_rx_desc *)msdu->data; + l2_hdr_offset = ath12k_dp_rx_h_l3pad(ar->ab, tail_rx_desc); + if (is_first_msdu) { + if (!ath12k_dp_rxdesc_mpdu_valid(ar->ab, rx_desc)) { + drop_mpdu = true; + dev_kfree_skb_any(msdu); + msdu = NULL; + pmon->mon_last_linkdesc_paddr = paddr; + goto next_msdu; + } + msdu_ppdu_id = + ath12k_dp_rxdesc_get_ppduid(ar->ab, rx_desc); + + if (ath12k_dp_mon_comp_ppduid(msdu_ppdu_id, + ppdu_id)) { + spin_unlock_bh(&pmon->mon_lock); + return rx_bufs_used; + } + pmon->mon_last_linkdesc_paddr = paddr; + is_first_msdu = false; + } + ath12k_dp_mon_get_buf_len(&msdu_list.msdu_info[i], + &is_frag, &total_len, + &frag_len, &msdu_cnt); + rx_buf_size = rx_pkt_offset + l2_hdr_offset + frag_len; + + if (ath12k_dp_pkt_set_pktlen(msdu, rx_buf_size)) { + dev_kfree_skb_any(msdu); + goto next_msdu; + } + + if (!(*head_msdu)) + *head_msdu = msdu; + else if (last) + last->next = msdu; + + last = msdu; +next_msdu: + pmon->mon_last_buf_cookie = msdu_list.sw_cookie[i]; + rx_bufs_used++; + desc_info->skb = NULL; + list_add_tail(&desc_info->list, used_list); } - dest_idx = 0; -move_next: - ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); - ath12k_hal_srng_src_get_next_entry(ab, srng); - num_buffs_reaped++; + ath12k_hal_rx_buf_addr_info_set(&buf_info, paddr, sw_cookie, rbm); + + ath12k_dp_mon_next_link_desc_get(msdu_link_desc, &paddr, + &sw_cookie, &rbm, + &p_buf_addr_info); + + ath12k_dp_rx_link_desc_return(ar->ab, &buf_info, + HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + + p_last_buf_addr_info = p_buf_addr_info; + + } while (paddr && msdu_cnt); + + spin_unlock_bh(&pmon->mon_lock); + + if (last) + last->next = NULL; + + *tail_msdu = msdu; + + if (msdu_cnt == 0) + *npackets = 1; + + return rx_bufs_used; +} + +/* The destination ring processing is stuck if the destination is not + * moving while status ring moves 16 PPDU. The destination ring processing + * skips this destination ring PPDU as a workaround. + */ +#define MON_DEST_RING_STUCK_MAX_CNT 16 + +static void ath12k_dp_rx_mon_dest_process(struct ath12k *ar, int mac_id, + u32 quota, struct napi_struct *napi) +{ + struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data; + struct ath12k_pdev_mon_stats *rx_mon_stats; + u32 ppdu_id, rx_bufs_used = 0, ring_id; + u32 mpdu_rx_bufs_used, npackets = 0; + struct ath12k_dp *dp = &ar->ab->dp; + struct ath12k_base *ab = ar->ab; + void *ring_entry, *mon_dst_srng; + struct dp_mon_mpdu *tmp_mpdu; + LIST_HEAD(rx_desc_used_list); + struct hal_srng *srng; + + ring_id = dp->rxdma_err_dst_ring[mac_id].ring_id; + srng = &ab->hal.srng_list[ring_id]; + + mon_dst_srng = &ab->hal.srng_list[ring_id]; + + spin_lock_bh(&srng->lock); + + ath12k_hal_srng_access_begin(ab, mon_dst_srng); + + ppdu_id = pmon->mon_ppdu_info.ppdu_id; + rx_mon_stats = &pmon->rx_mon_stats; + + while ((ring_entry = ath12k_hal_srng_dst_peek(ar->ab, mon_dst_srng))) { + struct sk_buff *head_msdu, *tail_msdu; + + head_msdu = NULL; + tail_msdu = NULL; + + mpdu_rx_bufs_used = ath12k_dp_rx_mon_mpdu_pop(ar, mac_id, ring_entry, + &head_msdu, &tail_msdu, + &rx_desc_used_list, + &npackets, &ppdu_id); + + rx_bufs_used += mpdu_rx_bufs_used; + + if (mpdu_rx_bufs_used) { + dp->mon_dest_ring_stuck_cnt = 0; + } else { + dp->mon_dest_ring_stuck_cnt++; + rx_mon_stats->dest_mon_not_reaped++; + } + + if (dp->mon_dest_ring_stuck_cnt > MON_DEST_RING_STUCK_MAX_CNT) { + rx_mon_stats->dest_mon_stuck++; + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "status ring ppdu_id=%d dest ring ppdu_id=%d mon_dest_ring_stuck_cnt=%d dest_mon_not_reaped=%u dest_mon_stuck=%u\n", + pmon->mon_ppdu_info.ppdu_id, ppdu_id, + dp->mon_dest_ring_stuck_cnt, + rx_mon_stats->dest_mon_not_reaped, + rx_mon_stats->dest_mon_stuck); + spin_lock_bh(&pmon->mon_lock); + pmon->mon_ppdu_info.ppdu_id = ppdu_id; + spin_unlock_bh(&pmon->mon_lock); + continue; + } + + if (ppdu_id != pmon->mon_ppdu_info.ppdu_id) { + spin_lock_bh(&pmon->mon_lock); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + spin_unlock_bh(&pmon->mon_lock); + ath12k_dbg(ar->ab, ATH12K_DBG_DATA, + "dest_rx: new ppdu_id %x != status ppdu_id %x dest_mon_not_reaped = %u dest_mon_stuck = %u\n", + ppdu_id, pmon->mon_ppdu_info.ppdu_id, + rx_mon_stats->dest_mon_not_reaped, + rx_mon_stats->dest_mon_stuck); + break; + } + + if (head_msdu && tail_msdu) { + tmp_mpdu = kzalloc(sizeof(*tmp_mpdu), GFP_ATOMIC); + if (!tmp_mpdu) + break; + + tmp_mpdu->head = head_msdu; + tmp_mpdu->tail = tail_msdu; + tmp_mpdu->err_bitmap = pmon->err_bitmap; + tmp_mpdu->decap_format = pmon->decap_format; + ath12k_dp_mon_rx_deliver(ar, tmp_mpdu, + &pmon->mon_ppdu_info, napi); + rx_mon_stats->dest_mpdu_done++; + kfree(tmp_mpdu); + } + + ring_entry = ath12k_hal_srng_dst_get_next_entry(ar->ab, + mon_dst_srng); } + ath12k_hal_srng_access_end(ar->ab, mon_dst_srng); - ath12k_hal_srng_access_end(ab, srng); spin_unlock_bh(&srng->lock); + + if (rx_bufs_used) { + rx_mon_stats->dest_ppdu_done++; + ath12k_dp_rx_bufs_replenish(ar->ab, + &dp->rx_refill_buf_ring, + &rx_desc_used_list, + rx_bufs_used); + } +} + +static int +__ath12k_dp_mon_process_ring(struct ath12k *ar, int mac_id, + struct napi_struct *napi, int *budget) +{ + struct ath12k_mon_data *pmon = (struct ath12k_mon_data *)&ar->dp.mon_data; + struct ath12k_pdev_mon_stats *rx_mon_stats = &pmon->rx_mon_stats; + struct hal_rx_mon_ppdu_info *ppdu_info = &pmon->mon_ppdu_info; + enum hal_rx_mon_status hal_status; + struct sk_buff_head skb_list; + int num_buffs_reaped; + struct sk_buff *skb; + + __skb_queue_head_init(&skb_list); + + num_buffs_reaped = ath12k_dp_rx_reap_mon_status_ring(ar->ab, mac_id, + budget, &skb_list); + if (!num_buffs_reaped) + goto exit; + + while ((skb = __skb_dequeue(&skb_list))) { + memset(ppdu_info, 0, sizeof(*ppdu_info)); + ppdu_info->peer_id = HAL_INVALID_PEERID; + + hal_status = ath12k_dp_mon_parse_rx_dest(ar, pmon, skb); + + if (ar->monitor_started && + pmon->mon_ppdu_status == DP_PPDU_STATUS_START && + hal_status == HAL_TLV_STATUS_PPDU_DONE) { + rx_mon_stats->status_ppdu_done++; + pmon->mon_ppdu_status = DP_PPDU_STATUS_DONE; + ath12k_dp_rx_mon_dest_process(ar, mac_id, *budget, napi); + pmon->mon_ppdu_status = DP_PPDU_STATUS_START; + } + + dev_kfree_skb_any(skb); + } + +exit: return num_buffs_reaped; } @@ -2535,11 +4378,14 @@ int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id, struct ath12k *ar = ath12k_ab_to_ar(ab, mac_id); int num_buffs_reaped = 0; - if (!ar->monitor_started) - ath12k_dp_mon_rx_process_stats(ar, mac_id, napi, &budget); - else - num_buffs_reaped = ath12k_dp_mon_srng_process(ar, mac_id, &budget, - monitor_mode, napi); + if (ab->hw_params->rxdma1_enable) { + if (monitor_mode == ATH12K_DP_RX_MONITOR_MODE) + num_buffs_reaped = ath12k_dp_mon_srng_process(ar, &budget, napi); + } else { + if (ar->monitor_started) + num_buffs_reaped = + __ath12k_dp_mon_process_ring(ar, mac_id, napi, &budget); + } return num_buffs_reaped; } diff --git a/drivers/net/wireless/ath/ath12k/dp_mon.h b/drivers/net/wireless/ath/ath12k/dp_mon.h index fb9e9c176ce5..e25595cbdcf3 100644 --- a/drivers/net/wireless/ath/ath12k/dp_mon.h +++ b/drivers/net/wireless/ath/ath12k/dp_mon.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_MON_H @@ -9,6 +9,9 @@ #include "core.h" +#define ATH12K_MON_RX_DOT11_OFFSET 5 +#define ATH12K_MON_RX_PKT_OFFSET 8 + enum dp_monitor_mode { ATH12K_DP_TX_MONITOR_MODE, ATH12K_DP_RX_MONITOR_MODE @@ -77,14 +80,14 @@ struct dp_mon_tx_ppdu_info { enum hal_rx_mon_status ath12k_dp_mon_rx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, + struct sk_buff *skb, struct napi_struct *napi); int ath12k_dp_mon_buf_replenish(struct ath12k_base *ab, struct dp_rxdma_mon_ring *buf_ring, int req_entries); -int ath12k_dp_mon_srng_process(struct ath12k *ar, int mac_id, - int *budget, enum dp_monitor_mode monitor_mode, - struct napi_struct *napi); +int ath12k_dp_mon_status_bufs_replenish(struct ath12k_base *ab, + struct dp_rxdma_mon_ring *rx_ring, + int req_entries); int ath12k_dp_mon_process_ring(struct ath12k_base *ab, int mac_id, struct napi_struct *napi, int budget, enum dp_monitor_mode monitor_mode); @@ -96,11 +99,9 @@ ath12k_dp_mon_tx_status_get_num_user(u16 tlv_tag, enum hal_rx_mon_status ath12k_dp_mon_tx_parse_mon_status(struct ath12k *ar, struct ath12k_mon_data *pmon, - int mac_id, struct sk_buff *skb, struct napi_struct *napi, u32 ppdu_id); void ath12k_dp_mon_rx_process_ulofdma(struct hal_rx_mon_ppdu_info *ppdu_info); -int ath12k_dp_mon_rx_process_stats(struct ath12k *ar, int mac_id, - struct napi_struct *napi, int *budget); +int ath12k_dp_mon_srng_process(struct ath12k *ar, int *budget, struct napi_struct *napi); #endif diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.c b/drivers/net/wireless/ath/ath12k/dp_rx.c index dad35bfd83f6..7e1a8f29c7b6 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.c +++ b/drivers/net/wireless/ath/ath12k/dp_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/ieee80211.h> @@ -194,6 +194,22 @@ static void ath12k_dp_rxdesc_set_msdu_len(struct ath12k_base *ab, ab->hal_rx_ops->rx_desc_set_msdu_len(desc, len); } +u32 ath12k_dp_rxdesc_get_ppduid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc) +{ + return ab->hal_rx_ops->rx_desc_get_mpdu_ppdu_id(rx_desc); +} + +bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc) +{ + u32 tlv_tag; + + tlv_tag = ab->hal_rx_ops->rx_desc_get_mpdu_start_tag(rx_desc); + + return tlv_tag == HAL_RX_MPDU_START; +} + static bool ath12k_dp_rx_h_is_da_mcbc(struct ath12k_base *ab, struct hal_rx_desc *desc) { @@ -228,12 +244,6 @@ static void ath12k_dp_rx_desc_get_crypto_header(struct ath12k_base *ab, ab->hal_rx_ops->rx_desc_get_crypto_header(desc, crypto_hdr, enctype); } -static u16 ath12k_dp_rxdesc_get_mpdu_frame_ctrl(struct ath12k_base *ab, - struct hal_rx_desc *desc) -{ - return ab->hal_rx_ops->rx_desc_get_mpdu_frame_ctl(desc); -} - static inline u8 ath12k_dp_rx_get_msdu_src_link(struct ath12k_base *ab, struct hal_rx_desc *desc) { @@ -420,9 +430,17 @@ static int ath12k_dp_rxdma_mon_buf_ring_free(struct ath12k_base *ab, static int ath12k_dp_rxdma_buf_free(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; + int i; ath12k_dp_rxdma_mon_buf_ring_free(ab, &dp->rxdma_mon_buf_ring); + if (ab->hw_params->rxdma1_enable) + return 0; + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) + ath12k_dp_rxdma_mon_buf_ring_free(ab, + &dp->rx_mon_status_refill_ring[i]); + return 0; } @@ -436,7 +454,12 @@ static int ath12k_dp_rxdma_mon_ring_buf_setup(struct ath12k_base *ab, ath12k_hal_srng_get_entrysize(ab, ringtype); rx_ring->bufs_max = num_entries; - ath12k_dp_mon_buf_replenish(ab, rx_ring, num_entries); + + if (ringtype == HAL_RXDMA_MONITOR_STATUS) + ath12k_dp_mon_status_bufs_replenish(ab, rx_ring, + num_entries); + else + ath12k_dp_mon_buf_replenish(ab, rx_ring, num_entries); return 0; } @@ -457,7 +480,8 @@ static int ath12k_dp_rxdma_ring_buf_setup(struct ath12k_base *ab, static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; - int ret; + struct dp_rxdma_mon_ring *mon_ring; + int ret, i; ret = ath12k_dp_rxdma_ring_buf_setup(ab, &dp->rx_refill_buf_ring); if (ret) { @@ -470,9 +494,19 @@ static int ath12k_dp_rxdma_buf_setup(struct ath12k_base *ab) ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab, &dp->rxdma_mon_buf_ring, HAL_RXDMA_MONITOR_BUF); - if (ret) { + if (ret) ath12k_warn(ab, "failed to setup HAL_RXDMA_MONITOR_BUF\n"); + return ret; + } + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + mon_ring = &dp->rx_mon_status_refill_ring[i]; + ret = ath12k_dp_rxdma_mon_ring_buf_setup(ab, mon_ring, + HAL_RXDMA_MONITOR_STATUS); + if (ret) { + ath12k_warn(ab, + "failed to setup HAL_RXDMA_MONITOR_STATUS\n"); return ret; } } @@ -556,9 +590,9 @@ void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab) spin_lock_bh(&dp->reo_cmd_lock); list_for_each_entry_safe(cmd, tmp, &dp->reo_cmd_list, list) { list_del(&cmd->list); - dma_unmap_single(ab->dev, cmd->data.paddr, - cmd->data.size, DMA_BIDIRECTIONAL); - kfree(cmd->data.vaddr); + dma_unmap_single(ab->dev, cmd->data.qbuf.paddr_aligned, + cmd->data.qbuf.size, DMA_BIDIRECTIONAL); + kfree(cmd->data.qbuf.vaddr); kfree(cmd); } @@ -566,9 +600,9 @@ void ath12k_dp_rx_reo_cmd_list_cleanup(struct ath12k_base *ab) &dp->reo_cmd_cache_flush_list, list) { list_del(&cmd_cache->list); dp->reo_cmd_cache_flush_count--; - dma_unmap_single(ab->dev, cmd_cache->data.paddr, - cmd_cache->data.size, DMA_BIDIRECTIONAL); - kfree(cmd_cache->data.vaddr); + dma_unmap_single(ab->dev, cmd_cache->data.qbuf.paddr_aligned, + cmd_cache->data.qbuf.size, DMA_BIDIRECTIONAL); + kfree(cmd_cache->data.qbuf.vaddr); kfree(cmd_cache); } spin_unlock_bh(&dp->reo_cmd_lock); @@ -583,10 +617,10 @@ static void ath12k_dp_reo_cmd_free(struct ath12k_dp *dp, void *ctx, ath12k_warn(dp->ab, "failed to flush rx tid hw desc, tid %d status %d\n", rx_tid->tid, status); - dma_unmap_single(dp->ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(dp->ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } static int ath12k_dp_reo_cmd_send(struct ath12k_base *ab, struct ath12k_dp_rx_tid *rx_tid, @@ -641,13 +675,13 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, unsigned long tot_desc_sz, desc_sz; int ret; - tot_desc_sz = rx_tid->size; + tot_desc_sz = rx_tid->qbuf.size; desc_sz = ath12k_hal_reo_qdesc_size(0, HAL_DESC_REO_NON_QOS_TID); while (tot_desc_sz > desc_sz) { tot_desc_sz -= desc_sz; - cmd.addr_lo = lower_32_bits(rx_tid->paddr + tot_desc_sz); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned + tot_desc_sz); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_FLUSH_CACHE, &cmd, NULL); @@ -658,8 +692,8 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, } memset(&cmd, 0, sizeof(cmd)); - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_FLUSH_CACHE, @@ -667,10 +701,10 @@ static void ath12k_dp_reo_cache_flush(struct ath12k_base *ab, if (ret) { ath12k_err(ab, "failed to send HAL_REO_CMD_FLUSH_CACHE cmd, tid %d (%d)\n", rx_tid->tid, ret); - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } } @@ -729,10 +763,10 @@ static void ath12k_dp_rx_tid_del_func(struct ath12k_dp *dp, void *ctx, return; free_desc: - dma_unmap_single(ab->dev, rx_tid->paddr, rx_tid->size, + dma_unmap_single(ab->dev, rx_tid->qbuf.paddr_aligned, rx_tid->qbuf.size, DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } static void ath12k_peer_rx_tid_qref_setup(struct ath12k_base *ab, u16 peer_id, u16 tid, @@ -762,6 +796,7 @@ static void ath12k_peer_rx_tid_qref_setup(struct ath12k_base *ab, u16 peer_id, u qref->info1 = u32_encode_bits(upper_32_bits(paddr), BUFFER_ADDR_INFO1_ADDR) | u32_encode_bits(tid, DP_REO_QREF_NUM); + ath12k_hal_reo_shared_qaddr_cache_clear(ab); } static void ath12k_peer_rx_tid_qref_reset(struct ath12k_base *ab, u16 peer_id, u16 tid) @@ -801,8 +836,8 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar, return; cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.upd0 = HAL_REO_CMD_UPD0_VLD; ret = ath12k_dp_reo_cmd_send(ar->ab, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, @@ -810,10 +845,10 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar, if (ret) { ath12k_err(ar->ab, "failed to send HAL_REO_CMD_UPDATE_RX_QUEUE cmd, tid %d (%d)\n", tid, ret); - dma_unmap_single(ar->ab->dev, rx_tid->paddr, rx_tid->size, - DMA_BIDIRECTIONAL); - kfree(rx_tid->vaddr); - rx_tid->vaddr = NULL; + dma_unmap_single(ar->ab->dev, rx_tid->qbuf.paddr_aligned, + rx_tid->qbuf.size, DMA_BIDIRECTIONAL); + kfree(rx_tid->qbuf.vaddr); + rx_tid->qbuf.vaddr = NULL; } if (peer->mlo) @@ -824,15 +859,10 @@ void ath12k_dp_rx_peer_tid_delete(struct ath12k *ar, rx_tid->active = false; } -/* TODO: it's strange (and ugly) that struct hal_reo_dest_ring is converted - * to struct hal_wbm_release_ring, I couldn't figure out the logic behind - * that. - */ -static int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab, - struct hal_reo_dest_ring *ring, - enum hal_wbm_rel_bm_act action) +int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab, + struct ath12k_buffer_addr *buf_addr_info, + enum hal_wbm_rel_bm_act action) { - struct hal_wbm_release_ring *link_desc = (struct hal_wbm_release_ring *)ring; struct hal_wbm_release_ring *desc; struct ath12k_dp *dp = &ab->dp; struct hal_srng *srng; @@ -850,7 +880,7 @@ static int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab, goto exit; } - ath12k_hal_rx_msdu_link_desc_set(ab, desc, link_desc, action); + ath12k_hal_rx_msdu_link_desc_set(ab, desc, buf_addr_info, action); exit: ath12k_hal_srng_access_end(ab, srng); @@ -863,14 +893,17 @@ exit: static void ath12k_dp_rx_frags_cleanup(struct ath12k_dp_rx_tid *rx_tid, bool rel_link_desc) { + struct ath12k_buffer_addr *buf_addr_info; struct ath12k_base *ab = rx_tid->ab; lockdep_assert_held(&ab->base_lock); if (rx_tid->dst_ring_desc) { - if (rel_link_desc) - ath12k_dp_rx_link_desc_return(ab, rx_tid->dst_ring_desc, + if (rel_link_desc) { + buf_addr_info = &rx_tid->dst_ring_desc->buf_addr_info; + ath12k_dp_rx_link_desc_return(ab, buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); + } kfree(rx_tid->dst_ring_desc); rx_tid->dst_ring_desc = NULL; } @@ -895,7 +928,7 @@ void ath12k_dp_rx_peer_tid_cleanup(struct ath12k *ar, struct ath12k_peer *peer) ath12k_dp_rx_frags_cleanup(rx_tid, true); spin_unlock_bh(&ar->ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ar->ab->base_lock); } } @@ -909,8 +942,8 @@ static int ath12k_peer_rx_tid_reo_update(struct ath12k *ar, struct ath12k_hal_reo_cmd cmd = {0}; int ret; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); cmd.flag = HAL_REO_CMD_FLG_NEED_STATUS; cmd.upd0 = HAL_REO_CMD_UPD0_BA_WINDOW_SIZE; cmd.ba_window_size = ba_win_sz; @@ -934,18 +967,67 @@ static int ath12k_peer_rx_tid_reo_update(struct ath12k *ar, return 0; } +static int ath12k_dp_rx_assign_reoq(struct ath12k_base *ab, + struct ath12k_sta *ahsta, + struct ath12k_dp_rx_tid *rx_tid, + u16 ssn, enum hal_pn_type pn_type) +{ + u32 ba_win_sz = rx_tid->ba_win_sz; + struct ath12k_reoq_buf *buf; + void *vaddr, *vaddr_aligned; + dma_addr_t paddr_aligned; + u8 tid = rx_tid->tid; + u32 hw_desc_sz; + int ret; + + buf = &ahsta->reoq_bufs[tid]; + if (!buf->vaddr) { + /* TODO: Optimize the memory allocation for qos tid based on + * the actual BA window size in REO tid update path. + */ + if (tid == HAL_DESC_REO_NON_QOS_TID) + hw_desc_sz = ath12k_hal_reo_qdesc_size(ba_win_sz, tid); + else + hw_desc_sz = ath12k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); + + vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); + if (!vaddr) + return -ENOMEM; + + vaddr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); + + ath12k_hal_reo_qdesc_setup(vaddr_aligned, tid, ba_win_sz, + ssn, pn_type); + + paddr_aligned = dma_map_single(ab->dev, vaddr_aligned, hw_desc_sz, + DMA_BIDIRECTIONAL); + ret = dma_mapping_error(ab->dev, paddr_aligned); + if (ret) { + kfree(vaddr); + return ret; + } + + buf->vaddr = vaddr; + buf->paddr_aligned = paddr_aligned; + buf->size = hw_desc_sz; + } + + rx_tid->qbuf = *buf; + rx_tid->active = true; + + return 0; +} + int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_id, u8 tid, u32 ba_win_sz, u16 ssn, enum hal_pn_type pn_type) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = &ab->dp; - struct hal_rx_reo_queue *addr_aligned; struct ath12k_peer *peer; + struct ath12k_sta *ahsta; struct ath12k_dp_rx_tid *rx_tid; - u32 hw_desc_sz; - void *vaddr; - dma_addr_t paddr; + dma_addr_t paddr_aligned; int ret; spin_lock_bh(&ab->base_lock); @@ -957,7 +1039,8 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ return -ENOENT; } - if (!peer->primary_link) { + if (ab->hw_params->dp_primary_link_only && + !peer->primary_link) { spin_unlock_bh(&ab->base_lock); return 0; } @@ -977,9 +1060,9 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ } rx_tid = &peer->rx_tid[tid]; + paddr_aligned = rx_tid->qbuf.paddr_aligned; /* Update the tid queue if it is already setup */ if (rx_tid->active) { - paddr = rx_tid->paddr; ret = ath12k_peer_rx_tid_reo_update(ar, peer, rx_tid, ba_win_sz, ssn, true); spin_unlock_bh(&ab->base_lock); @@ -991,8 +1074,8 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ if (!ab->hw_params->reoq_lut_support) { ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, - ba_win_sz); + paddr_aligned, tid, + 1, ba_win_sz); if (ret) { ath12k_warn(ab, "failed to setup peer rx reorder queuefor tid %d: %d\n", tid, ret); @@ -1007,61 +1090,34 @@ int ath12k_dp_rx_peer_tid_setup(struct ath12k *ar, const u8 *peer_mac, int vdev_ rx_tid->ba_win_sz = ba_win_sz; - /* TODO: Optimize the memory allocation for qos tid based on - * the actual BA window size in REO tid update path. - */ - if (tid == HAL_DESC_REO_NON_QOS_TID) - hw_desc_sz = ath12k_hal_reo_qdesc_size(ba_win_sz, tid); - else - hw_desc_sz = ath12k_hal_reo_qdesc_size(DP_BA_WIN_SZ_MAX, tid); - - vaddr = kzalloc(hw_desc_sz + HAL_LINK_DESC_ALIGN - 1, GFP_ATOMIC); - if (!vaddr) { - spin_unlock_bh(&ab->base_lock); - return -ENOMEM; - } - - addr_aligned = PTR_ALIGN(vaddr, HAL_LINK_DESC_ALIGN); - - ath12k_hal_reo_qdesc_setup(addr_aligned, tid, ba_win_sz, - ssn, pn_type); - - paddr = dma_map_single(ab->dev, addr_aligned, hw_desc_sz, - DMA_BIDIRECTIONAL); - - ret = dma_mapping_error(ab->dev, paddr); + ahsta = ath12k_sta_to_ahsta(peer->sta); + ret = ath12k_dp_rx_assign_reoq(ab, ahsta, rx_tid, ssn, pn_type); if (ret) { spin_unlock_bh(&ab->base_lock); - goto err_mem_free; + ath12k_warn(ab, "failed to assign reoq buf for rx tid %u\n", tid); + return ret; } - rx_tid->vaddr = vaddr; - rx_tid->paddr = paddr; - rx_tid->size = hw_desc_sz; - rx_tid->active = true; - if (ab->hw_params->reoq_lut_support) { /* Update the REO queue LUT at the corresponding peer id * and tid with qaddr. */ if (peer->mlo) - ath12k_peer_rx_tid_qref_setup(ab, peer->ml_id, tid, paddr); + ath12k_peer_rx_tid_qref_setup(ab, peer->ml_id, tid, + paddr_aligned); else - ath12k_peer_rx_tid_qref_setup(ab, peer->peer_id, tid, paddr); + ath12k_peer_rx_tid_qref_setup(ab, peer->peer_id, tid, + paddr_aligned); spin_unlock_bh(&ab->base_lock); } else { spin_unlock_bh(&ab->base_lock); ret = ath12k_wmi_peer_rx_reorder_queue_setup(ar, vdev_id, peer_mac, - paddr, tid, 1, ba_win_sz); + paddr_aligned, tid, 1, + ba_win_sz); } return ret; - -err_mem_free: - kfree(vaddr); - - return ret; } int ath12k_dp_rx_ampdu_start(struct ath12k *ar, @@ -1196,8 +1252,8 @@ int ath12k_dp_rx_peer_pn_replay_config(struct ath12k_link_vif *arvif, rx_tid = &peer->rx_tid[tid]; if (!rx_tid->active) continue; - cmd.addr_lo = lower_32_bits(rx_tid->paddr); - cmd.addr_hi = upper_32_bits(rx_tid->paddr); + cmd.addr_lo = lower_32_bits(rx_tid->qbuf.paddr_aligned); + cmd.addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); ret = ath12k_dp_reo_cmd_send(ab, rx_tid, HAL_REO_CMD_UPDATE_RX_QUEUE, &cmd, NULL); @@ -1784,8 +1840,12 @@ void ath12k_dp_htt_htc_t2h_msg_handler(struct ath12k_base *ab, HTT_T2H_PEER_MAP_INFO1_MAC_ADDR_H16); ath12k_dp_get_mac_addr(le32_to_cpu(resp->peer_map_ev.mac_addr_l32), peer_mac_h16, mac_addr); + ast_hash = le32_get_bits(resp->peer_map_ev.info2, + HTT_T2H_PEER_MAP3_INFO2_AST_HASH_VAL); + hw_peer_id = le32_get_bits(resp->peer_map_ev.info2, + HTT_T2H_PEER_MAP3_INFO2_HW_PEER_ID); ath12k_peer_map_event(ab, vdev_id, peer_id, mac_addr, ast_hash, - peer_id); + hw_peer_id); break; case HTT_T2H_MSG_TYPE_PEER_UNMAP: case HTT_T2H_MSG_TYPE_PEER_UNMAP2: @@ -1823,6 +1883,7 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, struct hal_rx_desc *ldesc; int space_extra, rem_len, buf_len; u32 hal_rx_desc_sz = ar->ab->hal.hal_desc_sz; + bool is_continuation; /* As the msdu is spread across multiple rx buffers, * find the offset to the start of msdu for computing @@ -1871,7 +1932,8 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, rem_len = msdu_len - buf_first_len; while ((skb = __skb_dequeue(msdu_list)) != NULL && rem_len > 0) { rxcb = ATH12K_SKB_RXCB(skb); - if (rxcb->is_continuation) + is_continuation = rxcb->is_continuation; + if (is_continuation) buf_len = DP_RX_BUFFER_SIZE - hal_rx_desc_sz; else buf_len = rem_len; @@ -1889,7 +1951,7 @@ static int ath12k_dp_rx_msdu_coalesce(struct ath12k *ar, dev_kfree_skb_any(skb); rem_len -= buf_len; - if (!rxcb->is_continuation) + if (!is_continuation) break; } @@ -1914,21 +1976,14 @@ static struct sk_buff *ath12k_dp_rx_get_msdu_last_buf(struct sk_buff_head *msdu_ return NULL; } -static void ath12k_dp_rx_h_csum_offload(struct ath12k *ar, struct sk_buff *msdu) +static void ath12k_dp_rx_h_csum_offload(struct sk_buff *msdu, + struct ath12k_dp_rx_info *rx_info) { - struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - struct ath12k_base *ab = ar->ab; - bool ip_csum_fail, l4_csum_fail; - - ip_csum_fail = ath12k_dp_rx_h_ip_cksum_fail(ab, rxcb->rx_desc); - l4_csum_fail = ath12k_dp_rx_h_l4_cksum_fail(ab, rxcb->rx_desc); - - msdu->ip_summed = (ip_csum_fail || l4_csum_fail) ? - CHECKSUM_NONE : CHECKSUM_UNNECESSARY; + msdu->ip_summed = (rx_info->ip_csum_fail || rx_info->l4_csum_fail) ? + CHECKSUM_NONE : CHECKSUM_UNNECESSARY; } -static int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar, - enum hal_encrypt_type enctype) +int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar, enum hal_encrypt_type enctype) { switch (enctype) { case HAL_ENCRYPT_TYPE_OPEN: @@ -2122,10 +2177,13 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k *ar, struct hal_rx_desc *rx_desc = rxcb->rx_desc; struct ath12k_base *ab = ar->ab; size_t hdr_len, crypto_len; - struct ieee80211_hdr *hdr; - u16 qos_ctl; - __le16 fc; - u8 *crypto_hdr; + struct ieee80211_hdr hdr; + __le16 qos_ctl; + u8 *crypto_hdr, mesh_ctrl; + + ath12k_dp_rx_desc_get_dot11_hdr(ab, rx_desc, &hdr); + hdr_len = ieee80211_hdrlen(hdr.frame_control); + mesh_ctrl = ath12k_dp_rx_h_mesh_ctl_present(ab, rx_desc); if (!(status->flag & RX_FLAG_IV_STRIPPED)) { crypto_len = ath12k_dp_rx_crypto_param_len(ar, enctype); @@ -2133,27 +2191,21 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k *ar, ath12k_dp_rx_desc_get_crypto_header(ab, rx_desc, crypto_hdr, enctype); } - fc = cpu_to_le16(ath12k_dp_rxdesc_get_mpdu_frame_ctrl(ab, rx_desc)); - hdr_len = ieee80211_hdrlen(fc); skb_push(msdu, hdr_len); - hdr = (struct ieee80211_hdr *)msdu->data; - hdr->frame_control = fc; - - /* Get wifi header from rx_desc */ - ath12k_dp_rx_desc_get_dot11_hdr(ab, rx_desc, hdr); + memcpy(msdu->data, &hdr, min(hdr_len, sizeof(hdr))); if (rxcb->is_mcbc) status->flag &= ~RX_FLAG_PN_VALIDATED; /* Add QOS header */ - if (ieee80211_is_data_qos(hdr->frame_control)) { - qos_ctl = rxcb->tid; - if (ath12k_dp_rx_h_mesh_ctl_present(ab, rx_desc)) - qos_ctl |= IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT; + if (ieee80211_is_data_qos(hdr.frame_control)) { + struct ieee80211_hdr *qos_ptr = (struct ieee80211_hdr *)msdu->data; - /* TODO: Add other QoS ctl fields when required */ - memcpy(msdu->data + (hdr_len - IEEE80211_QOS_CTL_LEN), - &qos_ctl, IEEE80211_QOS_CTL_LEN); + qos_ctl = cpu_to_le16(rxcb->tid & IEEE80211_QOS_CTL_TID_MASK); + if (mesh_ctrl) + qos_ctl |= cpu_to_le16(IEEE80211_QOS_CTL_MESH_CONTROL_PRESENT); + + memcpy(ieee80211_get_qos_ctl(qos_ptr), &qos_ctl, IEEE80211_QOS_CTL_LEN); } } @@ -2229,10 +2281,10 @@ static void ath12k_dp_rx_h_undecap(struct ath12k *ar, struct sk_buff *msdu, } struct ath12k_peer * -ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu) +ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu, + struct ath12k_dp_rx_info *rx_info) { struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); - struct hal_rx_desc *rx_desc = rxcb->rx_desc; struct ath12k_peer *peer = NULL; lockdep_assert_held(&ab->base_lock); @@ -2243,40 +2295,41 @@ ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu) if (peer) return peer; - if (!rx_desc || !(ath12k_dp_rxdesc_mac_addr2_valid(ab, rx_desc))) - return NULL; + if (rx_info->addr2_present) + peer = ath12k_peer_find_by_addr(ab, rx_info->addr2); - peer = ath12k_peer_find_by_addr(ab, - ath12k_dp_rxdesc_get_mpdu_start_addr2(ab, - rx_desc)); return peer; } static void ath12k_dp_rx_h_mpdu(struct ath12k *ar, struct sk_buff *msdu, struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status) + struct ath12k_dp_rx_info *rx_info) { - bool fill_crypto_hdr; struct ath12k_base *ab = ar->ab; struct ath12k_skb_rxcb *rxcb; enum hal_encrypt_type enctype; bool is_decrypted = false; struct ieee80211_hdr *hdr; struct ath12k_peer *peer; + struct ieee80211_rx_status *rx_status = rx_info->rx_status; u32 err_bitmap; /* PN for multicast packets will be checked in mac80211 */ rxcb = ATH12K_SKB_RXCB(msdu); - fill_crypto_hdr = ath12k_dp_rx_h_is_da_mcbc(ar->ab, rx_desc); - rxcb->is_mcbc = fill_crypto_hdr; + rxcb->is_mcbc = rx_info->is_mcbc; if (rxcb->is_mcbc) - rxcb->peer_id = ath12k_dp_rx_h_peer_id(ar->ab, rx_desc); + rxcb->peer_id = rx_info->peer_id; spin_lock_bh(&ar->ab->base_lock); - peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu); + peer = ath12k_dp_rx_h_find_peer(ar->ab, msdu, rx_info); if (peer) { + /* resetting mcbc bit because mcbc packets are unicast + * packets only for AP as STA sends unicast packets. + */ + rxcb->is_mcbc = rxcb->is_mcbc && !peer->ucast_ra_only; + if (rxcb->is_mcbc) enctype = peer->sec_type_grp; else @@ -2305,7 +2358,7 @@ static void ath12k_dp_rx_h_mpdu(struct ath12k *ar, if (is_decrypted) { rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MMIC_STRIPPED; - if (fill_crypto_hdr) + if (rx_info->is_mcbc) rx_status->flag |= RX_FLAG_MIC_STRIPPED | RX_FLAG_ICV_STRIPPED; else @@ -2313,37 +2366,28 @@ static void ath12k_dp_rx_h_mpdu(struct ath12k *ar, RX_FLAG_PN_VALIDATED; } - ath12k_dp_rx_h_csum_offload(ar, msdu); + ath12k_dp_rx_h_csum_offload(msdu, rx_info); ath12k_dp_rx_h_undecap(ar, msdu, rx_desc, enctype, rx_status, is_decrypted); - if (!is_decrypted || fill_crypto_hdr) + if (!is_decrypted || rx_info->is_mcbc) return; - if (ath12k_dp_rx_h_decap_type(ar->ab, rx_desc) != - DP_RX_DECAP_TYPE_ETHERNET2_DIX) { + if (rx_info->decap_type != DP_RX_DECAP_TYPE_ETHERNET2_DIX) { hdr = (void *)msdu->data; hdr->frame_control &= ~__cpu_to_le16(IEEE80211_FCTL_PROTECTED); } } -static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status) +static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info) { - struct ath12k_base *ab = ar->ab; struct ieee80211_supported_band *sband; - enum rx_msdu_start_pkt_type pkt_type; - u8 bw; - u8 rate_mcs, nss; - u8 sgi; + struct ieee80211_rx_status *rx_status = rx_info->rx_status; + enum rx_msdu_start_pkt_type pkt_type = rx_info->pkt_type; + u8 bw = rx_info->bw, sgi = rx_info->sgi; + u8 rate_mcs = rx_info->rate_mcs, nss = rx_info->nss; bool is_cck; - pkt_type = ath12k_dp_rx_h_pkt_type(ab, rx_desc); - bw = ath12k_dp_rx_h_rx_bw(ab, rx_desc); - rate_mcs = ath12k_dp_rx_h_rate_mcs(ab, rx_desc); - nss = ath12k_dp_rx_h_nss(ab, rx_desc); - sgi = ath12k_dp_rx_h_sgi(ab, rx_desc); - switch (pkt_type) { case RX_MSDU_START_PKT_TYPE_11A: case RX_MSDU_START_PKT_TYPE_11B: @@ -2392,13 +2436,55 @@ static void ath12k_dp_rx_h_rate(struct ath12k *ar, struct hal_rx_desc *rx_desc, rx_status->he_gi = ath12k_he_gi_to_nl80211_he_gi(sgi); rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw); break; + case RX_MSDU_START_PKT_TYPE_11BE: + rx_status->rate_idx = rate_mcs; + + if (rate_mcs > ATH12K_EHT_MCS_MAX) { + ath12k_warn(ar->ab, + "Received with invalid mcs in EHT mode %d\n", + rate_mcs); + break; + } + + rx_status->encoding = RX_ENC_EHT; + rx_status->nss = nss; + rx_status->eht.gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(sgi); + rx_status->bw = ath12k_mac_bw_to_mac80211_bw(bw); + break; + default: + break; } } -void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status) +void ath12k_dp_rx_h_fetch_info(struct ath12k_base *ab, struct hal_rx_desc *rx_desc, + struct ath12k_dp_rx_info *rx_info) { - struct ath12k_base *ab = ar->ab; + rx_info->ip_csum_fail = ath12k_dp_rx_h_ip_cksum_fail(ab, rx_desc); + rx_info->l4_csum_fail = ath12k_dp_rx_h_l4_cksum_fail(ab, rx_desc); + rx_info->is_mcbc = ath12k_dp_rx_h_is_da_mcbc(ab, rx_desc); + rx_info->decap_type = ath12k_dp_rx_h_decap_type(ab, rx_desc); + rx_info->pkt_type = ath12k_dp_rx_h_pkt_type(ab, rx_desc); + rx_info->sgi = ath12k_dp_rx_h_sgi(ab, rx_desc); + rx_info->rate_mcs = ath12k_dp_rx_h_rate_mcs(ab, rx_desc); + rx_info->bw = ath12k_dp_rx_h_rx_bw(ab, rx_desc); + rx_info->nss = ath12k_dp_rx_h_nss(ab, rx_desc); + rx_info->tid = ath12k_dp_rx_h_tid(ab, rx_desc); + rx_info->peer_id = ath12k_dp_rx_h_peer_id(ab, rx_desc); + rx_info->phy_meta_data = ath12k_dp_rx_h_freq(ab, rx_desc); + + if (ath12k_dp_rxdesc_mac_addr2_valid(ab, rx_desc)) { + ether_addr_copy(rx_info->addr2, + ath12k_dp_rxdesc_get_mpdu_start_addr2(ab, rx_desc)); + rx_info->addr2_present = true; + } + + ath12k_dbg_dump(ab, ATH12K_DBG_DATA, NULL, "rx_desc: ", + rx_desc, sizeof(*rx_desc)); +} + +void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info) +{ + struct ieee80211_rx_status *rx_status = rx_info->rx_status; u8 channel_num; u32 center_freq, meta_data; struct ieee80211_channel *channel; @@ -2412,12 +2498,12 @@ void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; - meta_data = ath12k_dp_rx_h_freq(ab, rx_desc); + meta_data = rx_info->phy_meta_data; channel_num = meta_data; center_freq = meta_data >> 16; - if (center_freq >= ATH12K_MIN_6G_FREQ && - center_freq <= ATH12K_MAX_6G_FREQ) { + if (center_freq >= ATH12K_MIN_6GHZ_FREQ && + center_freq <= ATH12K_MAX_6GHZ_FREQ) { rx_status->band = NL80211_BAND_6GHZ; rx_status->freq = center_freq; } else if (channel_num >= 1 && channel_num <= 14) { @@ -2433,20 +2519,18 @@ void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, ieee80211_frequency_to_channel(channel->center_freq); } spin_unlock_bh(&ar->data_lock); - ath12k_dbg_dump(ar->ab, ATH12K_DBG_DATA, NULL, "rx_desc: ", - rx_desc, sizeof(*rx_desc)); } if (rx_status->band != NL80211_BAND_6GHZ) rx_status->freq = ieee80211_channel_to_frequency(channel_num, rx_status->band); - ath12k_dp_rx_h_rate(ar, rx_desc, rx_status); + ath12k_dp_rx_h_rate(ar, rx_info); } static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *napi, struct sk_buff *msdu, - struct ieee80211_rx_status *status) + struct ath12k_dp_rx_info *rx_info) { struct ath12k_base *ab = ar->ab; static const struct ieee80211_radiotap_he known = { @@ -2459,6 +2543,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap struct ieee80211_sta *pubsta; struct ath12k_peer *peer; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); + struct ieee80211_rx_status *status = rx_info->rx_status; u8 decap = DP_RX_DECAP_TYPE_RAW; bool is_mcbc = rxcb->is_mcbc; bool is_eapol = rxcb->is_eapol; @@ -2471,10 +2556,10 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap } if (!(status->flag & RX_FLAG_ONLY_MONITOR)) - decap = ath12k_dp_rx_h_decap_type(ab, rxcb->rx_desc); + decap = rx_info->decap_type; spin_lock_bh(&ab->base_lock); - peer = ath12k_dp_rx_h_find_peer(ab, msdu); + peer = ath12k_dp_rx_h_find_peer(ab, msdu, rx_info); pubsta = peer ? peer->sta : NULL; @@ -2486,7 +2571,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap spin_unlock_bh(&ab->base_lock); ath12k_dbg(ab, ATH12K_DBG_DATA, - "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", + "rx skb %p len %u peer %pM %d %s sn %u %s%s%s%s%s%s%s%s%s%s rate_idx %u vht_nss %u freq %u band %u flag 0x%x fcs-err %i mic-err %i amsdu-more %i\n", msdu, msdu->len, peer ? peer->addr : NULL, @@ -2497,6 +2582,7 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap (status->encoding == RX_ENC_HT) ? "ht" : "", (status->encoding == RX_ENC_VHT) ? "vht" : "", (status->encoding == RX_ENC_HE) ? "he" : "", + (status->encoding == RX_ENC_EHT) ? "eht" : "", (status->bw == RATE_INFO_BW_40) ? "40" : "", (status->bw == RATE_INFO_BW_80) ? "80" : "", (status->bw == RATE_INFO_BW_160) ? "160" : "", @@ -2530,10 +2616,33 @@ static void ath12k_dp_rx_deliver_msdu(struct ath12k *ar, struct napi_struct *nap ieee80211_rx_napi(ath12k_ar_to_hw(ar), pubsta, msdu, napi); } +static bool ath12k_dp_rx_check_nwifi_hdr_len_valid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc, + struct sk_buff *msdu) +{ + struct ieee80211_hdr *hdr; + u8 decap_type; + u32 hdr_len; + + decap_type = ath12k_dp_rx_h_decap_type(ab, rx_desc); + if (decap_type != DP_RX_DECAP_TYPE_NATIVE_WIFI) + return true; + + hdr = (struct ieee80211_hdr *)msdu->data; + hdr_len = ieee80211_hdrlen(hdr->frame_control); + + if ((likely(hdr_len <= DP_MAX_NWIFI_HDR_LEN))) + return true; + + ab->device_stats.invalid_rbm++; + WARN_ON_ONCE(1); + return false; +} + static int ath12k_dp_rx_process_msdu(struct ath12k *ar, struct sk_buff *msdu, struct sk_buff_head *msdu_list, - struct ieee80211_rx_status *rx_status) + struct ath12k_dp_rx_info *rx_info) { struct ath12k_base *ab = ar->ab; struct hal_rx_desc *rx_desc, *lrx_desc; @@ -2588,10 +2697,16 @@ static int ath12k_dp_rx_process_msdu(struct ath12k *ar, } } - ath12k_dp_rx_h_ppdu(ar, rx_desc, rx_status); - ath12k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_status); + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu))) { + ret = -EINVAL; + goto free_out; + } + + ath12k_dp_rx_h_fetch_info(ab, rx_desc, rx_info); + ath12k_dp_rx_h_ppdu(ar, rx_info); + ath12k_dp_rx_h_mpdu(ar, msdu, rx_desc, rx_info); - rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; + rx_info->rx_status->flag |= RX_FLAG_SKIP_MONITOR | RX_FLAG_DUP_VALIDATED; return 0; @@ -2611,12 +2726,16 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab, struct ath12k *ar; struct ath12k_hw_link *hw_links = ag->hw_links; struct ath12k_base *partner_ab; + struct ath12k_dp_rx_info rx_info; u8 hw_link_id, pdev_id; int ret; if (skb_queue_empty(msdu_list)) return; + rx_info.addr2_present = false; + rx_info.rx_status = &rx_status; + rcu_read_lock(); while ((msdu = __skb_dequeue(msdu_list))) { @@ -2637,7 +2756,7 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab, continue; } - ret = ath12k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_status); + ret = ath12k_dp_rx_process_msdu(ar, msdu, msdu_list, &rx_info); if (ret) { ath12k_dbg(ab, ATH12K_DBG_DATA, "Unable to process msdu %d", ret); @@ -2645,7 +2764,7 @@ static void ath12k_dp_rx_process_received_packets(struct ath12k_base *ab, continue; } - ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_status); + ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_info); } rcu_read_unlock(); @@ -2678,9 +2797,9 @@ int ath12k_dp_rx_process(struct ath12k_base *ab, int ring_id, struct napi_struct *napi, int budget) { struct ath12k_hw_group *ag = ab->ag; - struct list_head rx_desc_used_list[ATH12K_MAX_SOCS]; + struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES]; struct ath12k_hw_link *hw_links = ag->hw_links; - int num_buffs_reaped[ATH12K_MAX_SOCS] = {}; + int num_buffs_reaped[ATH12K_MAX_DEVICES] = {}; struct ath12k_rx_desc_info *desc_info; struct ath12k_dp *dp = &ab->dp; struct dp_rxdma_ring *rx_ring = &dp->rx_refill_buf_ring; @@ -2697,7 +2816,7 @@ int ath12k_dp_rx_process(struct ath12k_base *ab, int ring_id, __skb_queue_head_init(&msdu_list); - for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) INIT_LIST_HEAD(&rx_desc_used_list[device_id]); srng = &ab->hal.srng_list[dp->reo_dst_ring[ring_id].ring_id]; @@ -2758,13 +2877,14 @@ try_again: DMA_FROM_DEVICE); num_buffs_reaped[device_id]++; + ab->device_stats.reo_rx[ring_id][ab->device_id]++; push_reason = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_PUSH_REASON); if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { dev_kfree_skb_any(msdu); - ab->soc_stats.hal_reo_error[ring_id]++; + ab->device_stats.hal_reo_error[ring_id]++; continue; } @@ -2814,7 +2934,7 @@ try_again: if (!total_msdu_reaped) goto exit; - for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) { + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) { if (!num_buffs_reaped[device_id]) continue; @@ -2938,6 +3058,7 @@ static int ath12k_dp_rx_h_verify_tkip_mic(struct ath12k *ar, struct ath12k_peer struct ieee80211_rx_status *rxs = IEEE80211_SKB_RXCB(msdu); struct ieee80211_key_conf *key_conf; struct ieee80211_hdr *hdr; + struct ath12k_dp_rx_info rx_info; u8 mic[IEEE80211_CCMP_MIC_LEN]; int head_len, tail_len, ret; size_t data_len; @@ -2948,6 +3069,9 @@ static int ath12k_dp_rx_h_verify_tkip_mic(struct ath12k *ar, struct ath12k_peer if (ath12k_dp_rx_h_enctype(ab, rx_desc) != HAL_ENCRYPT_TYPE_TKIP_MIC) return 0; + rx_info.addr2_present = false; + rx_info.rx_status = rxs; + hdr = (struct ieee80211_hdr *)(msdu->data + hal_rx_desc_sz); hdr_len = ieee80211_hdrlen(hdr->frame_control); head_len = hdr_len + hal_rx_desc_sz + IEEE80211_TKIP_IV_LEN; @@ -2974,11 +3098,16 @@ mic_fail: (ATH12K_SKB_RXCB(msdu))->is_first_msdu = true; (ATH12K_SKB_RXCB(msdu))->is_last_msdu = true; + ath12k_dp_rx_h_fetch_info(ab, rx_desc, &rx_info); + rxs->flag |= RX_FLAG_MMIC_ERROR | RX_FLAG_MMIC_STRIPPED | RX_FLAG_IV_STRIPPED | RX_FLAG_DECRYPTED; skb_pull(msdu, hal_rx_desc_sz); - ath12k_dp_rx_h_ppdu(ar, rx_desc, rxs); + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, rx_desc, msdu))) + return -EINVAL; + + ath12k_dp_rx_h_ppdu(ar, &rx_info); ath12k_dp_rx_h_undecap(ar, msdu, rx_desc, HAL_ENCRYPT_TYPE_TKIP_MIC, rxs, true); ieee80211_rx(ath12k_ar_to_hw(ar), msdu); @@ -3193,8 +3322,15 @@ static int ath12k_dp_rx_h_defrag_reo_reinject(struct ath12k *ar, reo_ent_ring->rx_mpdu_info.peer_meta_data = reo_dest_ring->rx_mpdu_info.peer_meta_data; - reo_ent_ring->queue_addr_lo = cpu_to_le32(lower_32_bits(rx_tid->paddr)); - queue_addr_hi = upper_32_bits(rx_tid->paddr); + if (ab->hw_params->reoq_lut_support) { + reo_ent_ring->queue_addr_lo = reo_dest_ring->rx_mpdu_info.peer_meta_data; + queue_addr_hi = 0; + } else { + reo_ent_ring->queue_addr_lo = + cpu_to_le32(lower_32_bits(rx_tid->qbuf.paddr_aligned)); + queue_addr_hi = upper_32_bits(rx_tid->qbuf.paddr_aligned); + } + reo_ent_ring->info0 = le32_encode_bits(queue_addr_hi, HAL_REO_ENTR_RING_INFO0_QUEUE_ADDR_HI) | le32_encode_bits(dst_ind, @@ -3390,7 +3526,7 @@ static int ath12k_dp_rx_frag_h_mpdu(struct ath12k *ar, goto out_unlock; } } else { - ath12k_dp_rx_link_desc_return(ab, ring_desc, + ath12k_dp_rx_link_desc_return(ab, &ring_desc->buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); } @@ -3402,7 +3538,7 @@ static int ath12k_dp_rx_frag_h_mpdu(struct ath12k *ar, } spin_unlock_bh(&ab->base_lock); - del_timer_sync(&rx_tid->frag_timer); + timer_delete_sync(&rx_tid->frag_timer); spin_lock_bh(&ab->base_lock); peer = ath12k_peer_find_by_id(ab, peer_id); @@ -3503,7 +3639,7 @@ ath12k_dp_process_rx_err_buf(struct ath12k *ar, struct hal_reo_dest_ring *desc, if (ath12k_dp_rx_frag_h_mpdu(ar, msdu, desc)) { dev_kfree_skb_any(msdu); - ath12k_dp_rx_link_desc_return(ar->ab, desc, + ath12k_dp_rx_link_desc_return(ar->ab, &desc->buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); } exit: @@ -3515,9 +3651,9 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, int budget) { struct ath12k_hw_group *ag = ab->ag; - struct list_head rx_desc_used_list[ATH12K_MAX_SOCS]; + struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES]; u32 msdu_cookies[HAL_NUM_RX_MSDUS_PER_LINK_DESC]; - int num_buffs_reaped[ATH12K_MAX_SOCS] = {}; + int num_buffs_reaped[ATH12K_MAX_DEVICES] = {}; struct dp_link_desc_bank *link_desc_banks; enum hal_rx_buf_return_buf_manager rbm; struct hal_rx_msdu_link *link_desc_va; @@ -3539,7 +3675,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, tot_n_bufs_reaped = 0; quota = budget; - for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) INIT_LIST_HEAD(&rx_desc_used_list[device_id]); reo_except = &ab->dp.reo_except_ring; @@ -3553,7 +3689,7 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, while (budget && (reo_desc = ath12k_hal_srng_dst_get_next_entry(ab, srng))) { drop = false; - ab->soc_stats.err_ring_pkts++; + ab->device_stats.err_ring_pkts++; ret = ath12k_hal_desc_reo_parse_err(ab, reo_desc, &paddr, &desc_bank); @@ -3580,9 +3716,10 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, if (rbm != partner_ab->dp.idle_link_rbm && rbm != HAL_RX_BUF_RBM_SW3_BM && rbm != partner_ab->hw_params->hal_params->rx_buf_rbm) { - ab->soc_stats.invalid_rbm++; + ab->device_stats.invalid_rbm++; ath12k_warn(ab, "invalid return buffer manager %d\n", rbm); - ath12k_dp_rx_link_desc_return(partner_ab, reo_desc, + ath12k_dp_rx_link_desc_return(partner_ab, + &reo_desc->buf_addr_info, HAL_WBM_REL_BM_ACT_REL_MSDU); continue; } @@ -3600,7 +3737,8 @@ int ath12k_dp_rx_process_err(struct ath12k_base *ab, struct napi_struct *napi, drop = true; /* Return the link desc back to wbm idle list */ - ath12k_dp_rx_link_desc_return(partner_ab, reo_desc, + ath12k_dp_rx_link_desc_return(partner_ab, + &reo_desc->buf_addr_info, HAL_WBM_REL_BM_ACT_PUT_IN_IDLE); } @@ -3627,7 +3765,7 @@ exit: spin_unlock_bh(&srng->lock); - for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) { + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) { if (!num_buffs_reaped[device_id]) continue; @@ -3667,7 +3805,7 @@ static void ath12k_dp_rx_null_q_desc_sg_drop(struct ath12k *ar, } static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu, - struct ieee80211_rx_status *status, + struct ath12k_dp_rx_info *rx_info, struct sk_buff_head *msdu_list) { struct ath12k_base *ab = ar->ab; @@ -3720,11 +3858,14 @@ static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu, skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); } - ath12k_dp_rx_h_ppdu(ar, desc, status); + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu))) + return -EINVAL; - ath12k_dp_rx_h_mpdu(ar, msdu, desc, status); + ath12k_dp_rx_h_fetch_info(ab, desc, rx_info); + ath12k_dp_rx_h_ppdu(ar, rx_info); + ath12k_dp_rx_h_mpdu(ar, msdu, desc, rx_info); - rxcb->tid = ath12k_dp_rx_h_tid(ab, desc); + rxcb->tid = rx_info->tid; /* Please note that caller will having the access to msdu and completing * rx with mac80211. Need not worry about cleaning up amsdu_list. @@ -3734,17 +3875,17 @@ static int ath12k_dp_rx_h_null_q_desc(struct ath12k *ar, struct sk_buff *msdu, } static bool ath12k_dp_rx_h_reo_err(struct ath12k *ar, struct sk_buff *msdu, - struct ieee80211_rx_status *status, + struct ath12k_dp_rx_info *rx_info, struct sk_buff_head *msdu_list) { struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); bool drop = false; - ar->ab->soc_stats.reo_error[rxcb->err_code]++; + ar->ab->device_stats.reo_error[rxcb->err_code]++; switch (rxcb->err_code) { case HAL_REO_DEST_RING_ERROR_CODE_DESC_ADDR_ZERO: - if (ath12k_dp_rx_h_null_q_desc(ar, msdu, status, msdu_list)) + if (ath12k_dp_rx_h_null_q_desc(ar, msdu, rx_info, msdu_list)) drop = true; break; case HAL_REO_DEST_RING_ERROR_CODE_PN_CHECK_FAILED: @@ -3764,8 +3905,8 @@ static bool ath12k_dp_rx_h_reo_err(struct ath12k *ar, struct sk_buff *msdu, return drop; } -static void ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu, - struct ieee80211_rx_status *status) +static bool ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu, + struct ath12k_dp_rx_info *rx_info) { struct ath12k_base *ab = ar->ab; u16 msdu_len; @@ -3779,20 +3920,33 @@ static void ath12k_dp_rx_h_tkip_mic_err(struct ath12k *ar, struct sk_buff *msdu, l3pad_bytes = ath12k_dp_rx_h_l3pad(ab, desc); msdu_len = ath12k_dp_rx_h_msdu_len(ab, desc); + + if ((hal_rx_desc_sz + l3pad_bytes + msdu_len) > DP_RX_BUFFER_SIZE) { + ath12k_dbg(ab, ATH12K_DBG_DATA, + "invalid msdu len in tkip mic err %u\n", msdu_len); + ath12k_dbg_dump(ab, ATH12K_DBG_DATA, NULL, "", desc, + sizeof(*desc)); + return true; + } + skb_put(msdu, hal_rx_desc_sz + l3pad_bytes + msdu_len); skb_pull(msdu, hal_rx_desc_sz + l3pad_bytes); - ath12k_dp_rx_h_ppdu(ar, desc, status); + if (unlikely(!ath12k_dp_rx_check_nwifi_hdr_len_valid(ab, desc, msdu))) + return true; + + ath12k_dp_rx_h_ppdu(ar, rx_info); - status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | - RX_FLAG_DECRYPTED); + rx_info->rx_status->flag |= (RX_FLAG_MMIC_STRIPPED | RX_FLAG_MMIC_ERROR | + RX_FLAG_DECRYPTED); ath12k_dp_rx_h_undecap(ar, msdu, desc, - HAL_ENCRYPT_TYPE_TKIP_MIC, status, false); + HAL_ENCRYPT_TYPE_TKIP_MIC, rx_info->rx_status, false); + return false; } static bool ath12k_dp_rx_h_rxdma_err(struct ath12k *ar, struct sk_buff *msdu, - struct ieee80211_rx_status *status) + struct ath12k_dp_rx_info *rx_info) { struct ath12k_base *ab = ar->ab; struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); @@ -3800,14 +3954,15 @@ static bool ath12k_dp_rx_h_rxdma_err(struct ath12k *ar, struct sk_buff *msdu, bool drop = false; u32 err_bitmap; - ar->ab->soc_stats.rxdma_error[rxcb->err_code]++; + ar->ab->device_stats.rxdma_error[rxcb->err_code]++; switch (rxcb->err_code) { case HAL_REO_ENTR_RING_RXDMA_ECODE_DECRYPT_ERR: case HAL_REO_ENTR_RING_RXDMA_ECODE_TKIP_MIC_ERR: err_bitmap = ath12k_dp_rx_h_mpdu_err(ab, rx_desc); if (err_bitmap & HAL_RX_MPDU_ERR_TKIP_MIC) { - ath12k_dp_rx_h_tkip_mic_err(ar, msdu, status); + ath12k_dp_rx_h_fetch_info(ab, rx_desc, rx_info); + drop = ath12k_dp_rx_h_tkip_mic_err(ar, msdu, rx_info); break; } fallthrough; @@ -3829,14 +3984,18 @@ static void ath12k_dp_rx_wbm_err(struct ath12k *ar, { struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu); struct ieee80211_rx_status rxs = {0}; + struct ath12k_dp_rx_info rx_info; bool drop = true; + rx_info.addr2_present = false; + rx_info.rx_status = &rxs; + switch (rxcb->err_rel_src) { case HAL_WBM_REL_SRC_MODULE_REO: - drop = ath12k_dp_rx_h_reo_err(ar, msdu, &rxs, msdu_list); + drop = ath12k_dp_rx_h_reo_err(ar, msdu, &rx_info, msdu_list); break; case HAL_WBM_REL_SRC_MODULE_RXDMA: - drop = ath12k_dp_rx_h_rxdma_err(ar, msdu, &rxs); + drop = ath12k_dp_rx_h_rxdma_err(ar, msdu, &rx_info); break; default: /* msdu will get freed */ @@ -3848,13 +4007,13 @@ static void ath12k_dp_rx_wbm_err(struct ath12k *ar, return; } - ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rxs); + ath12k_dp_rx_deliver_msdu(ar, napi, msdu, &rx_info); } int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, struct napi_struct *napi, int budget) { - struct list_head rx_desc_used_list[ATH12K_MAX_SOCS]; + struct list_head rx_desc_used_list[ATH12K_MAX_DEVICES]; struct ath12k_hw_group *ag = ab->ag; struct ath12k *ar; struct ath12k_dp *dp = &ab->dp; @@ -3865,9 +4024,10 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, struct sk_buff_head msdu_list, scatter_msdu_list; struct ath12k_skb_rxcb *rxcb; void *rx_desc; - int num_buffs_reaped[ATH12K_MAX_SOCS] = {}; + int num_buffs_reaped[ATH12K_MAX_DEVICES] = {}; int total_num_buffs_reaped = 0; struct ath12k_rx_desc_info *desc_info; + struct ath12k_device_dp_stats *device_stats = &ab->device_stats; struct ath12k_hw_link *hw_links = ag->hw_links; struct ath12k_base *partner_ab; u8 hw_link_id, device_id; @@ -3877,7 +4037,7 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, __skb_queue_head_init(&msdu_list); __skb_queue_head_init(&scatter_msdu_list); - for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) INIT_LIST_HEAD(&rx_desc_used_list[device_id]); srng = &ab->hal.srng_list[dp->rx_rel_ring.ring_id]; @@ -4001,7 +4161,7 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, if (!total_num_buffs_reaped) goto done; - for (device_id = 0; device_id < ATH12K_MAX_SOCS; device_id++) { + for (device_id = 0; device_id < ATH12K_MAX_DEVICES; device_id++) { if (!num_buffs_reaped[device_id]) continue; @@ -4032,7 +4192,7 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, hw_links[hw_link_id].pdev_idx); ar = partner_ab->pdevs[pdev_id].ar; - if (!ar || !rcu_dereference(ar->ab->pdevs_active[hw_link_id])) { + if (!ar || !rcu_dereference(ar->ab->pdevs_active[pdev_id])) { dev_kfree_skb_any(msdu); continue; } @@ -4041,6 +4201,12 @@ int ath12k_dp_rx_process_wbm_err(struct ath12k_base *ab, dev_kfree_skb_any(msdu); continue; } + + if (rxcb->err_rel_src < HAL_WBM_REL_SRC_MODULE_MAX) { + device_id = ar->ab->device_id; + device_stats->rx_wbm_rel_source[rxcb->err_rel_src][device_id]++; + } + ath12k_dp_rx_wbm_err(ar, napi, msdu, &msdu_list); } rcu_read_unlock(); @@ -4130,6 +4296,7 @@ void ath12k_dp_rx_process_reo_status(struct ath12k_base *ab) void ath12k_dp_rx_free(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; + struct dp_srng *srng; int i; ath12k_dp_srng_cleanup(ab, &dp->rx_refill_buf_ring.refill_buf_ring); @@ -4137,6 +4304,10 @@ void ath12k_dp_rx_free(struct ath12k_base *ab) for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { if (ab->hw_params->rx_mac_buf_ring) ath12k_dp_srng_cleanup(ab, &dp->rx_mac_buf_ring[i]); + if (!ab->hw_params->rxdma1_enable) { + srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring; + ath12k_dp_srng_cleanup(ab, srng); + } } for (i = 0; i < ab->hw_params->num_rxdma_dst_ring; i++) @@ -4285,6 +4456,19 @@ int ath12k_dp_rx_htt_setup(struct ath12k_base *ab) ret); return ret; } + } else { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = + dp->rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + ret = ath12k_dp_tx_htt_srng_setup(ab, ring_id, i, + HAL_RXDMA_MONITOR_STATUS); + if (ret) { + ath12k_warn(ab, + "failed to configure mon_status_refill_ring%d %d\n", + i, ret); + return ret; + } + } } ret = ab->hw_params->hw_ops->rxdma_ring_sel_config(ab); @@ -4299,6 +4483,7 @@ int ath12k_dp_rx_htt_setup(struct ath12k_base *ab) int ath12k_dp_rx_alloc(struct ath12k_base *ab) { struct ath12k_dp *dp = &ab->dp; + struct dp_srng *srng; int i, ret; idr_init(&dp->rxdma_mon_buf_ring.bufs_idr); @@ -4346,6 +4531,23 @@ int ath12k_dp_rx_alloc(struct ath12k_base *ab) ath12k_warn(ab, "failed to setup HAL_RXDMA_MONITOR_BUF\n"); return ret; } + } else { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + idr_init(&dp->rx_mon_status_refill_ring[i].bufs_idr); + spin_lock_init(&dp->rx_mon_status_refill_ring[i].idr_lock); + } + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + srng = &dp->rx_mon_status_refill_ring[i].refill_buf_ring; + ret = ath12k_dp_srng_setup(ab, srng, + HAL_RXDMA_MONITOR_STATUS, 0, i, + DP_RXDMA_MON_STATUS_RING_SIZE); + if (ret) { + ath12k_warn(ab, "failed to setup mon status ring %d\n", + i); + return ret; + } + } } ret = ath12k_dp_rxdma_buf_setup(ab); @@ -4416,15 +4618,15 @@ int ath12k_dp_rx_pdev_mon_attach(struct ath12k *ar) return ret; } - /* if rxdma1_enable is false, no need to setup - * rxdma_mon_desc_ring. - */ - if (!ar->ab->hw_params->rxdma1_enable) - return 0; - pmon->mon_last_linkdesc_paddr = 0; pmon->mon_last_buf_cookie = DP_RX_DESC_COOKIE_MAX + 1; spin_lock_init(&pmon->mon_lock); + if (!ar->ab->hw_params->rxdma1_enable) + return 0; + + INIT_LIST_HEAD(&pmon->dp_rx_mon_mpdu_list); + pmon->mon_mpdu = NULL; + return 0; } diff --git a/drivers/net/wireless/ath/ath12k/dp_rx.h b/drivers/net/wireless/ath/ath12k/dp_rx.h index 1ce82088c954..e971a314bd2d 100644 --- a/drivers/net/wireless/ath/ath12k/dp_rx.h +++ b/drivers/net/wireless/ath/ath12k/dp_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_RX_H #define ATH12K_DP_RX_H @@ -14,11 +14,9 @@ struct ath12k_dp_rx_tid { u8 tid; - u32 *vaddr; - dma_addr_t paddr; - u32 size; u32 ba_win_sz; bool active; + struct ath12k_reoq_buf qbuf; /* Info related to rx fragments */ u32 cur_sn; @@ -65,6 +63,24 @@ struct ath12k_dp_rx_rfc1042_hdr { __be16 snap_type; } __packed; +struct ath12k_dp_rx_info { + struct ieee80211_rx_status *rx_status; + u32 phy_meta_data; + u16 peer_id; + u8 decap_type; + u8 pkt_type; + u8 sgi; + u8 rate_mcs; + u8 bw; + u8 nss; + u8 addr2[ETH_ALEN]; + u8 tid; + bool ip_csum_fail; + bool l4_csum_fail; + bool is_mcbc; + bool addr2_present; +}; + static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi) { u32 ret = 0; @@ -79,6 +95,9 @@ static inline u32 ath12k_he_gi_to_nl80211_he_gi(u8 sgi) case RX_MSDU_START_SGI_3_2_US: ret = NL80211_RATE_INFO_HE_GI_3_2; break; + default: + ret = NL80211_RATE_INFO_HE_GI_0_8; + break; } return ret; @@ -128,16 +147,13 @@ int ath12k_dp_rx_peer_frag_setup(struct ath12k *ar, const u8 *peer_mac, int vdev u8 ath12k_dp_rx_h_l3pad(struct ath12k_base *ab, struct hal_rx_desc *desc); struct ath12k_peer * -ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu); +ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu, + struct ath12k_dp_rx_info *rx_info); u8 ath12k_dp_rx_h_decap_type(struct ath12k_base *ab, struct hal_rx_desc *desc); u32 ath12k_dp_rx_h_mpdu_err(struct ath12k_base *ab, struct hal_rx_desc *desc); -void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct hal_rx_desc *rx_desc, - struct ieee80211_rx_status *rx_status); -struct ath12k_peer * -ath12k_dp_rx_h_find_peer(struct ath12k_base *ab, struct sk_buff *msdu); - +void ath12k_dp_rx_h_ppdu(struct ath12k *ar, struct ath12k_dp_rx_info *rx_info); int ath12k_dp_rxdma_ring_sel_config_qcn9274(struct ath12k_base *ab); int ath12k_dp_rxdma_ring_sel_config_wcn7850(struct ath12k_base *ab); @@ -145,4 +161,17 @@ int ath12k_dp_htt_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len, int (*iter)(struct ath12k_base *ar, u16 tag, u16 len, const void *ptr, void *data), void *data); +void ath12k_dp_rx_h_fetch_info(struct ath12k_base *ab, struct hal_rx_desc *rx_desc, + struct ath12k_dp_rx_info *rx_info); + +int ath12k_dp_rx_crypto_mic_len(struct ath12k *ar, enum hal_encrypt_type enctype); +u32 ath12k_dp_rxdesc_get_ppduid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc); +bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc); +int ath12k_dp_rx_link_desc_return(struct ath12k_base *ab, + struct ath12k_buffer_addr *buf_addr_info, + enum hal_wbm_rel_bm_act action); +bool ath12k_dp_rxdesc_mpdu_valid(struct ath12k_base *ab, + struct hal_rx_desc *rx_desc); #endif /* ATH12K_DP_RX_H */ diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.c b/drivers/net/wireless/ath/ath12k/dp_tx.c index a8d341a6df01..b6816b6c2c04 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.c +++ b/drivers/net/wireless/ath/ath12k/dp_tx.c @@ -1,13 +1,16 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" #include "dp_tx.h" #include "debug.h" +#include "debugfs.h" #include "hw.h" +#include "peer.h" +#include "mac.h" static enum hal_tcl_encap_type ath12k_dp_tx_get_encap_type(struct ath12k_link_vif *arvif, struct sk_buff *skb) @@ -81,6 +84,7 @@ static void ath12k_dp_tx_release_txbuf(struct ath12k_dp *dp, u8 pool_id) { spin_lock_bh(&dp->tx_desc_lock[pool_id]); + tx_desc->skb_ext_desc = NULL; list_move_tail(&tx_desc->list, &dp->tx_desc_free_list[pool_id]); spin_unlock_bh(&dp->tx_desc_lock[pool_id]); } @@ -117,7 +121,7 @@ static void ath12k_hal_tx_cmd_ext_desc_setup(struct ath12k_base *ab, le32_encode_bits(ti->data_len, HAL_TX_MSDU_EXT_INFO1_BUF_LEN); - tcl_ext_cmd->info1 = le32_encode_bits(1, HAL_TX_MSDU_EXT_INFO1_EXTN_OVERRIDE) | + tcl_ext_cmd->info1 |= le32_encode_bits(1, HAL_TX_MSDU_EXT_INFO1_EXTN_OVERRIDE) | le32_encode_bits(ti->encap_type, HAL_TX_MSDU_EXT_INFO1_ENCAP_TYPE) | le32_encode_bits(ti->encrypt_type, @@ -217,7 +221,8 @@ out: } int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, - struct sk_buff *skb) + struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, + bool is_mcast) { struct ath12k_base *ab = ar->ab; struct ath12k_dp *dp = &ab->dp; @@ -227,7 +232,7 @@ int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb); struct hal_tcl_data_cmd *hal_tcl_desc; struct hal_tx_msdu_ext_desc *msg; - struct sk_buff *skb_ext_desc; + struct sk_buff *skb_ext_desc = NULL; struct hal_srng *tcl_ring; struct ieee80211_hdr *hdr = (void *)skb->data; struct ath12k_vif *ahvif = arvif->ahvif; @@ -290,13 +295,27 @@ tcl_ring_sel: msdu_ext_desc = true; } + if (gsn_valid) { + /* Reset and Initialize meta_data_flags with Global Sequence + * Number (GSN) info. + */ + ti.meta_data_flags = + u32_encode_bits(HTT_TCL_META_DATA_TYPE_GLOBAL_SEQ_NUM, + HTT_TCL_META_DATA_TYPE) | + u32_encode_bits(mcbc_gsn, HTT_TCL_META_DATA_GLOBAL_SEQ_NUM); + } + ti.encap_type = ath12k_dp_tx_get_encap_type(arvif, skb); ti.addr_search_flags = arvif->hal_addr_search_flags; ti.search_type = arvif->search_type; ti.type = HAL_TCL_DESC_TYPE_BUFFER; ti.pkt_offset = 0; ti.lmac_id = ar->lmac_id; + ti.vdev_id = arvif->vdev_id; + if (gsn_valid) + ti.vdev_id += HTT_TX_MLO_MCAST_HOST_REINJECT_BASE_VDEV_ID; + ti.bss_ast_hash = arvif->ast_hash; ti.bss_ast_idx = arvif->ast_idx; ti.dscp_tid_tbl_idx = 0; @@ -331,7 +350,7 @@ tcl_ring_sel: default: /* TODO: Take care of other encap modes as well */ ret = -EINVAL; - atomic_inc(&ab->soc_stats.tx_err.misc_fail); + atomic_inc(&ab->device_stats.tx_err.misc_fail); goto fail_remove_tx_buf; } @@ -354,7 +373,7 @@ tcl_ring_sel: map: ti.paddr = dma_map_single(ab->dev, skb->data, skb->len, DMA_TO_DEVICE); if (dma_mapping_error(ab->dev, ti.paddr)) { - atomic_inc(&ab->soc_stats.tx_err.misc_fail); + atomic_inc(&ab->device_stats.tx_err.misc_fail); ath12k_warn(ab, "failed to DMA map data Tx buffer\n"); ret = -ENOMEM; goto fail_remove_tx_buf; @@ -368,6 +387,7 @@ map: add_htt_metadata = true; msdu_ext_desc = true; ti.flags0 |= u32_encode_bits(1, HAL_TCL_DATA_CMD_INFO2_TO_FW); + ti.meta_data_flags |= HTT_TCL_META_DATA_VALID_HTT; ti.encap_type = HAL_TCL_ENCAP_TYPE_RAW; ti.encrypt_type = HAL_ENCRYPT_TYPE_OPEN; } @@ -398,22 +418,21 @@ map: if (ret < 0) { ath12k_dbg(ab, ATH12K_DBG_DP_TX, "Failed to add HTT meta data, dropping packet\n"); - goto fail_unmap_dma; + goto fail_free_ext_skb; } } ti.paddr = dma_map_single(ab->dev, skb_ext_desc->data, skb_ext_desc->len, DMA_TO_DEVICE); ret = dma_mapping_error(ab->dev, ti.paddr); - if (ret) { - kfree_skb(skb_ext_desc); - goto fail_unmap_dma; - } + if (ret) + goto fail_free_ext_skb; ti.data_len = skb_ext_desc->len; ti.type = HAL_TCL_DESC_TYPE_EXT_DESC; skb_cb->paddr_ext_desc = ti.paddr; + tx_desc->skb_ext_desc = skb_ext_desc; } hal_ring_id = tx_ring->tcl_data_ring.ring_id; @@ -429,7 +448,7 @@ map: * desc because the desc is directly enqueued onto hw queue. */ ath12k_hal_srng_access_end(ab, tcl_ring); - ab->soc_stats.tx_err.desc_na[ti.ring_id]++; + ab->device_stats.tx_err.desc_na[ti.ring_id]++; spin_unlock_bh(&tcl_ring->lock); ret = -ENOMEM; @@ -444,9 +463,22 @@ map: ring_selector++; } - goto fail_unmap_dma; + goto fail_unmap_dma_ext; } + spin_lock_bh(&arvif->link_stats_lock); + arvif->link_stats.tx_encap_type[ti.encap_type]++; + arvif->link_stats.tx_encrypt_type[ti.encrypt_type]++; + arvif->link_stats.tx_desc_type[ti.type]++; + + if (is_mcast) + arvif->link_stats.tx_bcast_mcast++; + else + arvif->link_stats.tx_enqueued++; + spin_unlock_bh(&arvif->link_stats_lock); + + ab->device_stats.tx_enqueued[ti.ring_id]++; + ath12k_hal_tx_cmd_desc_setup(ab, hal_tcl_desc, &ti); ath12k_hal_srng_access_end(ab, tcl_ring); @@ -460,16 +492,24 @@ map: return 0; -fail_unmap_dma: - dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); - +fail_unmap_dma_ext: if (skb_cb->paddr_ext_desc) dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), + skb_ext_desc->len, DMA_TO_DEVICE); +fail_free_ext_skb: + kfree_skb(skb_ext_desc); + +fail_unmap_dma: + dma_unmap_single(ab->dev, ti.paddr, ti.data_len, DMA_TO_DEVICE); fail_remove_tx_buf: ath12k_dp_tx_release_txbuf(dp, tx_desc, pool_id); + + spin_lock_bh(&arvif->link_stats_lock); + arvif->link_stats.tx_dropped++; + spin_unlock_bh(&arvif->link_stats_lock); + if (tcl_ring_retry) goto tcl_ring_sel; @@ -477,20 +517,23 @@ fail_remove_tx_buf: } static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab, - struct sk_buff *msdu, u8 mac_id, - struct dp_tx_ring *tx_ring) + struct dp_tx_ring *tx_ring, + struct ath12k_tx_desc_params *desc_params) { struct ath12k *ar; + struct sk_buff *msdu = desc_params->skb; struct ath12k_skb_cb *skb_cb; - u8 pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); + u8 pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, desc_params->mac_id); skb_cb = ATH12K_SKB_CB(msdu); ar = ab->pdevs[pdev_id].ar; dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } ieee80211_free_txskb(ar->ah->hw, msdu); @@ -500,26 +543,46 @@ static void ath12k_dp_tx_free_txbuf(struct ath12k_base *ab, static void ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, - struct sk_buff *msdu, + struct ath12k_tx_desc_params *desc_params, struct dp_tx_ring *tx_ring, struct ath12k_dp_htt_wbm_tx_status *ts) { struct ieee80211_tx_info *info; + struct ath12k_link_vif *arvif; struct ath12k_skb_cb *skb_cb; + struct ieee80211_vif *vif; + struct ath12k_vif *ahvif; struct ath12k *ar; + struct sk_buff *msdu = desc_params->skb; skb_cb = ATH12K_SKB_CB(msdu); info = IEEE80211_SKB_CB(msdu); ar = skb_cb->ar; + ab->device_stats.tx_completed[tx_ring->tcl_data_ring_id]++; if (atomic_dec_and_test(&ar->dp.num_tx_pending)) wake_up(&ar->dp.tx_empty_waitq); dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } + + vif = skb_cb->vif; + if (vif) { + ahvif = ath12k_vif_to_ahvif(vif); + rcu_read_lock(); + arvif = rcu_dereference(ahvif->link[skb_cb->link_id]); + if (arvif) { + spin_lock_bh(&arvif->link_stats_lock); + arvif->link_stats.tx_completed++; + spin_unlock_bh(&arvif->link_stats_lock); + } + rcu_read_unlock(); + } memset(&info->status, 0, sizeof(info->status)); @@ -542,10 +605,9 @@ ath12k_dp_tx_htt_tx_complete_buf(struct ath12k_base *ab, } static void -ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, - void *desc, u8 mac_id, - struct sk_buff *msdu, - struct dp_tx_ring *tx_ring) +ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, void *desc, + struct dp_tx_ring *tx_ring, + struct ath12k_tx_desc_params *desc_params) { struct htt_tx_wbm_completion *status_desc; struct ath12k_dp_htt_wbm_tx_status ts = {0}; @@ -555,19 +617,21 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, wbm_status = le32_get_bits(status_desc->info0, HTT_TX_WBM_COMP_INFO0_STATUS); + ab->device_stats.fw_tx_status[wbm_status]++; switch (wbm_status) { case HAL_WBM_REL_HTT_TX_COMP_STATUS_OK: - case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: - case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: ts.acked = (wbm_status == HAL_WBM_REL_HTT_TX_COMP_STATUS_OK); ts.ack_rssi = le32_get_bits(status_desc->info2, HTT_TX_WBM_COMP_INFO2_ACK_RSSI); - ath12k_dp_tx_htt_tx_complete_buf(ab, msdu, tx_ring, &ts); + ath12k_dp_tx_htt_tx_complete_buf(ab, desc_params, tx_ring, &ts); break; + case HAL_WBM_REL_HTT_TX_COMP_STATUS_DROP: + case HAL_WBM_REL_HTT_TX_COMP_STATUS_TTL: case HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ: case HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT: - ath12k_dp_tx_free_txbuf(ab, msdu, mac_id, tx_ring); + case HAL_WBM_REL_HTT_TX_COMP_STATUS_VDEVID_MISMATCH: + ath12k_dp_tx_free_txbuf(ab, tx_ring, desc_params); break; case HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY: /* This event is to be handled only when the driver decides to @@ -575,19 +639,142 @@ ath12k_dp_tx_process_htt_tx_complete(struct ath12k_base *ab, */ break; default: - ath12k_warn(ab, "Unknown htt tx status %d\n", wbm_status); + ath12k_warn(ab, "Unknown htt wbm tx status %d\n", wbm_status); break; } } +static void ath12k_dp_tx_update_txcompl(struct ath12k *ar, struct hal_tx_status *ts) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_peer *peer; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; + struct rate_info txrate = {0}; + u16 rate, ru_tones; + u8 rate_idx = 0; + int ret; + + spin_lock_bh(&ab->base_lock); + peer = ath12k_peer_find_by_id(ab, ts->peer_id); + if (!peer || !peer->sta) { + ath12k_dbg(ab, ATH12K_DBG_DP_TX, + "failed to find the peer by id %u\n", ts->peer_id); + spin_unlock_bh(&ab->base_lock); + return; + } + sta = peer->sta; + ahsta = ath12k_sta_to_ahsta(sta); + arsta = &ahsta->deflink; + + /* This is to prefer choose the real NSS value arsta->last_txrate.nss, + * if it is invalid, then choose the NSS value while assoc. + */ + if (arsta->last_txrate.nss) + txrate.nss = arsta->last_txrate.nss; + else + txrate.nss = arsta->peer_nss; + spin_unlock_bh(&ab->base_lock); + + switch (ts->pkt_type) { + case HAL_TX_RATE_STATS_PKT_TYPE_11A: + case HAL_TX_RATE_STATS_PKT_TYPE_11B: + ret = ath12k_mac_hw_ratecode_to_legacy_rate(ts->mcs, + ts->pkt_type, + &rate_idx, + &rate); + if (ret < 0) { + ath12k_warn(ab, "Invalid tx legacy rate %d\n", ret); + return; + } + + txrate.legacy = rate; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11N: + if (ts->mcs > ATH12K_HT_MCS_MAX) { + ath12k_warn(ab, "Invalid HT mcs index %d\n", ts->mcs); + return; + } + + if (txrate.nss != 0) + txrate.mcs = ts->mcs + 8 * (txrate.nss - 1); + + txrate.flags = RATE_INFO_FLAGS_MCS; + + if (ts->sgi) + txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11AC: + if (ts->mcs > ATH12K_VHT_MCS_MAX) { + ath12k_warn(ab, "Invalid VHT mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_VHT_MCS; + + if (ts->sgi) + txrate.flags |= RATE_INFO_FLAGS_SHORT_GI; + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11AX: + if (ts->mcs > ATH12K_HE_MCS_MAX) { + ath12k_warn(ab, "Invalid HE mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_HE_MCS; + txrate.he_gi = ath12k_he_gi_to_nl80211_he_gi(ts->sgi); + break; + case HAL_TX_RATE_STATS_PKT_TYPE_11BE: + if (ts->mcs > ATH12K_EHT_MCS_MAX) { + ath12k_warn(ab, "Invalid EHT mcs index %d\n", ts->mcs); + return; + } + + txrate.mcs = ts->mcs; + txrate.flags = RATE_INFO_FLAGS_EHT_MCS; + txrate.eht_gi = ath12k_mac_eht_gi_to_nl80211_eht_gi(ts->sgi); + break; + default: + ath12k_warn(ab, "Invalid tx pkt type: %d\n", ts->pkt_type); + return; + } + + txrate.bw = ath12k_mac_bw_to_mac80211_bw(ts->bw); + + if (ts->ofdma && ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11AX) { + txrate.bw = RATE_INFO_BW_HE_RU; + ru_tones = ath12k_mac_he_convert_tones_to_ru_tones(ts->tones); + txrate.he_ru_alloc = + ath12k_he_ru_tones_to_nl80211_he_ru_alloc(ru_tones); + } + + if (ts->ofdma && ts->pkt_type == HAL_TX_RATE_STATS_PKT_TYPE_11BE) { + txrate.bw = RATE_INFO_BW_EHT_RU; + txrate.eht_ru_alloc = + ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(ts->tones); + } + + spin_lock_bh(&ab->base_lock); + arsta->txrate = txrate; + spin_unlock_bh(&ab->base_lock); +} + static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, - struct sk_buff *msdu, - struct hal_tx_status *ts) + struct ath12k_tx_desc_params *desc_params, + struct hal_tx_status *ts, + int ring) { struct ath12k_base *ab = ar->ab; struct ath12k_hw *ah = ar->ah; struct ieee80211_tx_info *info; + struct ath12k_link_vif *arvif; struct ath12k_skb_cb *skb_cb; + struct ieee80211_vif *vif; + struct ath12k_vif *ahvif; + struct sk_buff *msdu = desc_params->skb; if (WARN_ON_ONCE(ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_TQM)) { /* Must not happen */ @@ -595,11 +782,14 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, } skb_cb = ATH12K_SKB_CB(msdu); + ab->device_stats.tx_completed[ring]++; dma_unmap_single(ab->dev, skb_cb->paddr, msdu->len, DMA_TO_DEVICE); - if (skb_cb->paddr_ext_desc) + if (skb_cb->paddr_ext_desc) { dma_unmap_single(ab->dev, skb_cb->paddr_ext_desc, - sizeof(struct hal_tx_msdu_ext_desc), DMA_TO_DEVICE); + desc_params->skb_ext_desc->len, DMA_TO_DEVICE); + dev_kfree_skb_any(desc_params->skb_ext_desc); + } rcu_read_lock(); @@ -613,6 +803,17 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, goto exit; } + vif = skb_cb->vif; + if (vif) { + ahvif = ath12k_vif_to_ahvif(vif); + arvif = rcu_dereference(ahvif->link[skb_cb->link_id]); + if (arvif) { + spin_lock_bh(&arvif->link_stats_lock); + arvif->link_stats.tx_completed++; + spin_unlock_bh(&arvif->link_stats_lock); + } + } + info = IEEE80211_SKB_CB(msdu); memset(&info->status, 0, sizeof(info->status)); @@ -658,6 +859,8 @@ static void ath12k_dp_tx_complete_msdu(struct ath12k *ar, * Might end up reporting it out-of-band from HTT stats. */ + ath12k_dp_tx_update_txcompl(ar, ts); + ieee80211_tx_status_skb(ath12k_ar_to_hw(ar), msdu); exit: @@ -668,6 +871,8 @@ static void ath12k_dp_tx_status_parse(struct ath12k_base *ab, struct hal_wbm_completion_ring_tx *desc, struct hal_tx_status *ts) { + u32 info0 = le32_to_cpu(desc->rate_stats.info0); + ts->buf_rel_source = le32_get_bits(desc->info0, HAL_WBM_COMPL_TX_INFO0_REL_SRC_MODULE); if (ts->buf_rel_source != HAL_WBM_REL_SRC_MODULE_FW && @@ -682,10 +887,17 @@ static void ath12k_dp_tx_status_parse(struct ath12k_base *ab, ts->ppdu_id = le32_get_bits(desc->info1, HAL_WBM_COMPL_TX_INFO1_TQM_STATUS_NUMBER); - if (le32_to_cpu(desc->rate_stats.info0) & HAL_TX_RATE_STATS_INFO0_VALID) - ts->rate_stats = le32_to_cpu(desc->rate_stats.info0); - else - ts->rate_stats = 0; + + ts->peer_id = le32_get_bits(desc->info3, HAL_WBM_COMPL_TX_INFO3_PEER_ID); + + if (info0 & HAL_TX_RATE_STATS_INFO0_VALID) { + ts->pkt_type = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_PKT_TYPE); + ts->mcs = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_MCS); + ts->sgi = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_SGI); + ts->bw = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_BW); + ts->tones = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_TONES_IN_RU); + ts->ofdma = u32_get_bits(info0, HAL_TX_RATE_STATS_INFO0_OFDMA_TX); + } } void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) @@ -695,12 +907,14 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) int hal_ring_id = dp->tx_ring[ring_id].tcl_comp_ring.ring_id; struct hal_srng *status_ring = &ab->hal.srng_list[hal_ring_id]; struct ath12k_tx_desc_info *tx_desc = NULL; - struct sk_buff *msdu; struct hal_tx_status ts = { 0 }; + struct ath12k_tx_desc_params desc_params; struct dp_tx_ring *tx_ring = &dp->tx_ring[ring_id]; struct hal_wbm_release_ring *desc; - u8 mac_id, pdev_id; + u8 pdev_id; u64 desc_va; + enum hal_wbm_rel_src_module buf_rel_source; + enum hal_wbm_tqm_rel_reason rel_status; spin_lock_bh(&status_ring->lock); @@ -753,28 +967,37 @@ void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id) continue; } - msdu = tx_desc->skb; - mac_id = tx_desc->mac_id; + desc_params.mac_id = tx_desc->mac_id; + desc_params.skb = tx_desc->skb; + desc_params.skb_ext_desc = tx_desc->skb_ext_desc; + + /* Find the HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE value */ + buf_rel_source = le32_get_bits(tx_status->info0, + HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE); + ab->device_stats.tx_wbm_rel_source[buf_rel_source]++; + + rel_status = le32_get_bits(tx_status->info0, + HAL_WBM_COMPL_TX_INFO0_TQM_RELEASE_REASON); + ab->device_stats.tqm_rel_reason[rel_status]++; /* Release descriptor as soon as extracting necessary info * to reduce contention */ ath12k_dp_tx_release_txbuf(dp, tx_desc, tx_desc->pool_id); if (ts.buf_rel_source == HAL_WBM_REL_SRC_MODULE_FW) { - ath12k_dp_tx_process_htt_tx_complete(ab, - (void *)tx_status, - mac_id, msdu, - tx_ring); + ath12k_dp_tx_process_htt_tx_complete(ab, (void *)tx_status, + tx_ring, &desc_params); continue; } - pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, mac_id); + pdev_id = ath12k_hw_mac_id_to_pdev_id(ab->hw_params, desc_params.mac_id); ar = ab->pdevs[pdev_id].ar; if (atomic_dec_and_test(&ar->dp.num_tx_pending)) wake_up(&ar->dp.tx_empty_waitq); - ath12k_dp_tx_complete_msdu(ar, msdu, &ts); + ath12k_dp_tx_complete_msdu(ar, &desc_params, &ts, + tx_ring->tcl_data_ring_id); } } @@ -814,7 +1037,7 @@ ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab, *htt_ring_type = HTT_HW_TO_SW_RING; break; case HAL_RXDMA_MONITOR_BUF: - *htt_ring_id = HTT_RXDMA_MONITOR_BUF_RING; + *htt_ring_id = HTT_RX_MON_HOST2MON_BUF_RING; *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_MONITOR_STATUS: @@ -822,7 +1045,7 @@ ath12k_dp_tx_get_ring_id_type(struct ath12k_base *ab, *htt_ring_type = HTT_SW_TO_HW_RING; break; case HAL_RXDMA_MONITOR_DST: - *htt_ring_id = HTT_RXDMA_MONITOR_DEST_RING; + *htt_ring_id = HTT_RX_MON_MON2HOST_DEST_RING; *htt_ring_type = HTT_HW_TO_SW_RING; break; case HAL_RXDMA_MONITOR_DESC: @@ -971,7 +1194,14 @@ int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab) skb_put(skb, len); cmd = (struct htt_ver_req_cmd *)skb->data; cmd->ver_reg_info = le32_encode_bits(HTT_H2T_MSG_TYPE_VERSION_REQ, - HTT_VER_REQ_INFO_MSG_ID); + HTT_OPTION_TAG); + + cmd->tcl_metadata_version = le32_encode_bits(HTT_TAG_TCL_METADATA_VERSION, + HTT_OPTION_TAG) | + le32_encode_bits(HTT_TCL_METADATA_VER_SZ, + HTT_OPTION_LEN) | + le32_encode_bits(HTT_OPTION_TCL_METADATA_VER_V2, + HTT_OPTION_VALUE); ret = ath12k_htc_send(&ab->htc, dp->eid, skb); if (ret) { @@ -1077,15 +1307,46 @@ int ath12k_dp_tx_htt_rx_filter_setup(struct ath12k_base *ab, u32 ring_id, cmd->info0 |= le32_encode_bits(!!(params.flags & HAL_SRNG_FLAGS_DATA_TLV_SWAP), HTT_RX_RING_SELECTION_CFG_CMD_INFO0_PS); cmd->info0 |= le32_encode_bits(tlv_filter->offset_valid, - HTT_RX_RING_SELECTION_CFG_CMD_OFFSET_VALID); + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_OFFSET_VALID); + cmd->info0 |= + le32_encode_bits(tlv_filter->drop_threshold_valid, + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_DROP_THRES_VAL); + cmd->info0 |= le32_encode_bits(!tlv_filter->rxmon_disable, + HTT_RX_RING_SELECTION_CFG_CMD_INFO0_EN_RXMON); + cmd->info1 = le32_encode_bits(rx_buf_size, HTT_RX_RING_SELECTION_CFG_CMD_INFO1_BUF_SIZE); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_mgmt, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_MGMT); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_ctrl, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_CTRL); + cmd->info1 |= le32_encode_bits(tlv_filter->conf_len_data, + HTT_RX_RING_SELECTION_CFG_CMD_INFO1_CONF_LEN_DATA); cmd->pkt_type_en_flags0 = cpu_to_le32(tlv_filter->pkt_filter_flags0); cmd->pkt_type_en_flags1 = cpu_to_le32(tlv_filter->pkt_filter_flags1); cmd->pkt_type_en_flags2 = cpu_to_le32(tlv_filter->pkt_filter_flags2); cmd->pkt_type_en_flags3 = cpu_to_le32(tlv_filter->pkt_filter_flags3); cmd->rx_filter_tlv = cpu_to_le32(tlv_filter->rx_filter); + cmd->info2 = le32_encode_bits(tlv_filter->rx_drop_threshold, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_DROP_THRESHOLD); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_mgmt_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_MGMT_TYPE); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_ctrl_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_CTRL_TYPE); + cmd->info2 |= + le32_encode_bits(tlv_filter->enable_log_data_type, + HTT_RX_RING_SELECTION_CFG_CMD_INFO2_EN_LOG_DATA_TYPE); + + cmd->info3 = + le32_encode_bits(tlv_filter->enable_rx_tlv_offset, + HTT_RX_RING_SELECTION_CFG_CMD_INFO3_EN_TLV_PKT_OFFSET); + cmd->info3 |= + le32_encode_bits(tlv_filter->rx_tlv_offset, + HTT_RX_RING_SELECTION_CFG_CMD_INFO3_PKT_TLV_OFFSET); + if (tlv_filter->offset_valid) { cmd->rx_packet_offset = le32_encode_bits(tlv_filter->rx_packet_offset, @@ -1210,15 +1471,28 @@ int ath12k_dp_tx_htt_monitor_mode_ring_config(struct ath12k *ar, bool reset) int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset) { struct ath12k_base *ab = ar->ab; - struct ath12k_dp *dp = &ab->dp; struct htt_rx_ring_tlv_filter tlv_filter = {0}; - int ret, ring_id; + int ret, ring_id, i; - ring_id = dp->rxdma_mon_buf_ring.refill_buf_ring.ring_id; tlv_filter.offset_valid = false; if (!reset) { - tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_BUF_RING; + tlv_filter.rx_filter = HTT_RX_MON_FILTER_TLV_FLAGS_MON_DEST_RING; + + tlv_filter.drop_threshold_valid = true; + tlv_filter.rx_drop_threshold = HTT_RX_RING_TLV_DROP_THRESHOLD_VALUE; + + tlv_filter.enable_log_mgmt_type = true; + tlv_filter.enable_log_ctrl_type = true; + tlv_filter.enable_log_data_type = true; + + tlv_filter.conf_len_ctrl = HTT_RX_RING_DEFAULT_DMA_LENGTH; + tlv_filter.conf_len_mgmt = HTT_RX_RING_DEFAULT_DMA_LENGTH; + tlv_filter.conf_len_data = HTT_RX_RING_DEFAULT_DMA_LENGTH; + + tlv_filter.enable_rx_tlv_offset = true; + tlv_filter.rx_tlv_offset = HTT_RX_RING_PKT_TLV_OFFSET; + tlv_filter.pkt_filter_flags0 = HTT_RX_MON_FP_MGMT_FILTER_FLAGS0 | HTT_RX_MON_MO_MGMT_FILTER_FLAGS0; @@ -1233,16 +1507,64 @@ int ath12k_dp_tx_htt_rx_monitor_mode_ring_config(struct ath12k *ar, bool reset) HTT_RX_MON_MO_CTRL_FILTER_FLASG3 | HTT_RX_MON_FP_DATA_FILTER_FLASG3 | HTT_RX_MON_MO_DATA_FILTER_FLASG3; + } else { + tlv_filter = ath12k_mac_mon_status_filter_default; + + if (ath12k_debugfs_is_extd_rx_stats_enabled(ar)) + tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar); } if (ab->hw_params->rxdma1_enable) { - ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, 0, - HAL_RXDMA_MONITOR_BUF, - DP_RXDMA_REFILL_RING_SIZE, + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_err(ab, + "failed to setup filter for monitor buf %d\n", + ret); + return ret; + } + } + return 0; + } + + if (!reset) { + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ab->dp.rx_mac_buf_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ar->ab, ring_id, + i, + HAL_RXDMA_BUF, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_err(ab, + "failed to setup filter for mon rx buf %d\n", + ret); + return ret; + } + } + } + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ab->dp.rx_mon_status_refill_ring[i].refill_buf_ring.ring_id; + if (!reset) { + tlv_filter.rx_filter = + HTT_RX_MON_FILTER_TLV_FLAGS_MON_STATUS_RING; + } + + ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id, + i, + HAL_RXDMA_MONITOR_STATUS, + RX_MON_STATUS_BUF_SIZE, &tlv_filter); if (ret) { ath12k_err(ab, - "failed to setup filter for monitor buf %d\n", ret); + "failed to setup filter for mon status buf %d\n", + ret); return ret; } } diff --git a/drivers/net/wireless/ath/ath12k/dp_tx.h b/drivers/net/wireless/ath/ath12k/dp_tx.h index 46dce23501f3..10acdcf1fa8f 100644 --- a/drivers/net/wireless/ath/ath12k/dp_tx.h +++ b/drivers/net/wireless/ath/ath12k/dp_tx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_DP_TX_H @@ -17,7 +17,8 @@ struct ath12k_dp_htt_wbm_tx_status { int ath12k_dp_tx_htt_h2t_ver_req_msg(struct ath12k_base *ab); int ath12k_dp_tx(struct ath12k *ar, struct ath12k_link_vif *arvif, - struct sk_buff *skb); + struct sk_buff *skb, bool gsn_valid, int mcbc_gsn, + bool is_mcast); void ath12k_dp_tx_completion_handler(struct ath12k_base *ab, int ring_id); int ath12k_dp_tx_htt_h2t_ppdu_stats_req(struct ath12k *ar, u32 mask); diff --git a/drivers/net/wireless/ath/ath12k/fw.c b/drivers/net/wireless/ath/ath12k/fw.c index 5be4b2d4a19d..5ac497f80cad 100644 --- a/drivers/net/wireless/ath/ath12k/fw.c +++ b/drivers/net/wireless/ath/ath12k/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -99,6 +99,8 @@ static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab, __set_bit(i, ab->fw.fw_features); } + ab->fw.fw_features_valid = true; + ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "features", "", ab->fw.fw_features, sizeof(ab->fw.fw_features)); @@ -169,3 +171,8 @@ void ath12k_fw_unmap(struct ath12k_base *ab) release_firmware(ab->fw.fw); memset(&ab->fw, 0, sizeof(ab->fw)); } + +bool ath12k_fw_feature_supported(struct ath12k_base *ab, enum ath12k_fw_features feat) +{ + return ab->fw.fw_features_valid && test_bit(feat, ab->fw.fw_features); +} diff --git a/drivers/net/wireless/ath/ath12k/fw.h b/drivers/net/wireless/ath/ath12k/fw.h index 273c003eff3b..7afaefed5086 100644 --- a/drivers/net/wireless/ath/ath12k/fw.h +++ b/drivers/net/wireless/ath/ath12k/fw.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_FW_H @@ -32,5 +32,6 @@ enum ath12k_fw_features { void ath12k_fw_map(struct ath12k_base *ab); void ath12k_fw_unmap(struct ath12k_base *ab); +bool ath12k_fw_feature_supported(struct ath12k_base *ab, enum ath12k_fw_features feat); #endif /* ATH12K_FW_H */ diff --git a/drivers/net/wireless/ath/ath12k/hal.c b/drivers/net/wireless/ath/ath12k/hal.c index cd59ff8e6c7b..a301898e5849 100644 --- a/drivers/net/wireless/ath/ath12k/hal.c +++ b/drivers/net/wireless/ath/ath12k/hal.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/dma-mapping.h> #include "hal_tx.h" @@ -154,7 +154,14 @@ static const struct hal_srng_config hw_srng_config_template[] = { .ring_dir = HAL_SRNG_DIR_SRC, .max_size = HAL_RXDMA_RING_MAX_SIZE_BE, }, - [HAL_RXDMA_MONITOR_STATUS] = { 0, }, + [HAL_RXDMA_MONITOR_STATUS] = { + .start_ring_id = HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, + .max_rings = 1, + .entry_size = sizeof(struct hal_wbm_buffer_ring) >> 2, + .mac_type = ATH12K_HAL_SRNG_PMAC, + .ring_dir = HAL_SRNG_DIR_SRC, + .max_size = HAL_RXDMA_RING_MAX_SIZE_BE, + }, [HAL_RXDMA_MONITOR_DESC] = { 0, }, [HAL_RXDMA_DIR_BUF] = { .start_ring_id = HAL_SRNG_RING_ID_RXDMA_DIR_BUF, @@ -449,8 +456,8 @@ static u8 *ath12k_hw_qcn9274_rx_desc_mpdu_start_addr2(struct hal_rx_desc *desc) static bool ath12k_hw_qcn9274_rx_desc_is_da_mcbc(struct hal_rx_desc *desc) { - return __le32_to_cpu(desc->u.qcn9274.mpdu_start.info6) & - RX_MPDU_START_INFO6_MCAST_BCAST; + return __le16_to_cpu(desc->u.qcn9274.msdu_end.info5) & + RX_MSDU_END_INFO5_DA_IS_MCBC; } static void ath12k_hw_qcn9274_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, @@ -511,11 +518,6 @@ static void ath12k_hw_qcn9274_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9274.mpdu_start.pn[1]); } -static u16 ath12k_hw_qcn9274_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc) -{ - return __le16_to_cpu(desc->u.qcn9274.mpdu_start.frame_ctrl); -} - static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab) { struct ath12k_hal *hal = &ab->hal; @@ -552,9 +554,9 @@ static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab) s->reg_start[1] = HAL_SEQ_WCSS_UMAC_REO_REG + HAL_REO_STATUS_HP; s = &hal->srng_config[HAL_TCL_DATA]; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB(ab); s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP; - s->reg_size[0] = HAL_TCL2_RING_BASE_LSB - HAL_TCL1_RING_BASE_LSB; + s->reg_size[0] = HAL_TCL2_RING_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab); s->reg_size[1] = HAL_TCL2_RING_HP - HAL_TCL1_RING_HP; s = &hal->srng_config[HAL_TCL_CMD]; @@ -566,29 +568,29 @@ static int ath12k_hal_srng_create_config_qcn9274(struct ath12k_base *ab) s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL_STATUS_RING_HP; s = &hal->srng_config[HAL_CE_SRC]; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - - HAL_SEQ_WCSS_UMAC_CE0_SRC_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - - HAL_SEQ_WCSS_UMAC_CE0_SRC_REG; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); s = &hal->srng_config[HAL_CE_DST]; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); s = &hal->srng_config[HAL_CE_DST_STATUS]; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_STATUS_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); s = &hal->srng_config[HAL_WBM_IDLE_LINK]; s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_BASE_LSB(ab); @@ -736,7 +738,6 @@ const struct hal_rx_ops hal_rx_qcn9274_ops = { .rx_desc_is_da_mcbc = ath12k_hw_qcn9274_rx_desc_is_da_mcbc, .rx_desc_get_dot11_hdr = ath12k_hw_qcn9274_rx_desc_get_dot11_hdr, .rx_desc_get_crypto_header = ath12k_hw_qcn9274_rx_desc_get_crypto_hdr, - .rx_desc_get_mpdu_frame_ctl = ath12k_hw_qcn9274_rx_desc_get_mpdu_frame_ctl, .dp_rx_h_msdu_done = ath12k_hw_qcn9274_dp_rx_h_msdu_done, .dp_rx_h_l4_cksum_fail = ath12k_hw_qcn9274_dp_rx_h_l4_cksum_fail, .dp_rx_h_ip_cksum_fail = ath12k_hw_qcn9274_dp_rx_h_ip_cksum_fail, @@ -908,8 +909,8 @@ static u8 *ath12k_hw_qcn9274_compact_rx_desc_mpdu_start_addr2(struct hal_rx_desc static bool ath12k_hw_qcn9274_compact_rx_desc_is_da_mcbc(struct hal_rx_desc *desc) { - return __le32_to_cpu(desc->u.qcn9274_compact.mpdu_start.info6) & - RX_MPDU_START_INFO6_MCAST_BCAST; + return __le16_to_cpu(desc->u.qcn9274_compact.msdu_end.info5) & + RX_MSDU_END_INFO5_DA_IS_MCBC; } static void ath12k_hw_qcn9274_compact_rx_desc_get_dot11_hdr(struct hal_rx_desc *desc, @@ -975,11 +976,6 @@ ath12k_hw_qcn9274_compact_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.qcn9274_compact.mpdu_start.pn[1]); } -static u16 ath12k_hw_qcn9274_compact_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc) -{ - return __le16_to_cpu(desc->u.qcn9274_compact.mpdu_start.frame_ctrl); -} - static bool ath12k_hw_qcn9274_compact_dp_rx_h_msdu_done(struct hal_rx_desc *desc) { return !!le32_get_bits(desc->u.qcn9274_compact.msdu_end.info14, @@ -1080,8 +1076,6 @@ const struct hal_rx_ops hal_rx_qcn9274_compact_ops = { .rx_desc_is_da_mcbc = ath12k_hw_qcn9274_compact_rx_desc_is_da_mcbc, .rx_desc_get_dot11_hdr = ath12k_hw_qcn9274_compact_rx_desc_get_dot11_hdr, .rx_desc_get_crypto_header = ath12k_hw_qcn9274_compact_rx_desc_get_crypto_hdr, - .rx_desc_get_mpdu_frame_ctl = - ath12k_hw_qcn9274_compact_rx_desc_get_mpdu_frame_ctl, .dp_rx_h_msdu_done = ath12k_hw_qcn9274_compact_dp_rx_h_msdu_done, .dp_rx_h_l4_cksum_fail = ath12k_hw_qcn9274_compact_dp_rx_h_l4_cksum_fail, .dp_rx_h_ip_cksum_fail = ath12k_hw_qcn9274_compact_dp_rx_h_ip_cksum_fail, @@ -1330,11 +1324,6 @@ static void ath12k_hw_wcn7850_rx_desc_get_crypto_hdr(struct hal_rx_desc *desc, crypto_hdr[7] = HAL_RX_MPDU_INFO_PN_GET_BYTE2(desc->u.wcn7850.mpdu_start.pn[1]); } -static u16 ath12k_hw_wcn7850_rx_desc_get_mpdu_frame_ctl(struct hal_rx_desc *desc) -{ - return __le16_to_cpu(desc->u.wcn7850.mpdu_start.frame_ctrl); -} - static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab) { struct ath12k_hal *hal = &ab->hal; @@ -1371,9 +1360,9 @@ static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab) s = &hal->srng_config[HAL_TCL_DATA]; s->max_rings = 5; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_BASE_LSB(ab); s->reg_start[1] = HAL_SEQ_WCSS_UMAC_TCL_REG + HAL_TCL1_RING_HP; - s->reg_size[0] = HAL_TCL2_RING_BASE_LSB - HAL_TCL1_RING_BASE_LSB; + s->reg_size[0] = HAL_TCL2_RING_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB(ab); s->reg_size[1] = HAL_TCL2_RING_HP - HAL_TCL1_RING_HP; s = &hal->srng_config[HAL_TCL_CMD]; @@ -1386,31 +1375,31 @@ static int ath12k_hal_srng_create_config_wcn7850(struct ath12k_base *ab) s = &hal->srng_config[HAL_CE_SRC]; s->max_rings = 12; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG + HAL_CE_DST_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - - HAL_SEQ_WCSS_UMAC_CE0_SRC_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG - - HAL_SEQ_WCSS_UMAC_CE0_SRC_REG; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab); s = &hal->srng_config[HAL_CE_DST]; s->max_rings = 12; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_BASE_LSB; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); s = &hal->srng_config[HAL_CE_DST_STATUS]; s->max_rings = 12; - s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + + s->reg_start[0] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_BASE_LSB; - s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG + HAL_CE_DST_STATUS_RING_HP; - s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; - s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG - - HAL_SEQ_WCSS_UMAC_CE0_DST_REG; + s->reg_start[1] = HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) + HAL_CE_DST_STATUS_RING_HP; + s->reg_size[0] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); + s->reg_size[1] = HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) - + HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab); s = &hal->srng_config[HAL_WBM_IDLE_LINK]; s->reg_start[0] = HAL_SEQ_WCSS_UMAC_WBM_REG + HAL_WBM_IDLE_LINK_RING_BASE_LSB(ab); @@ -1555,7 +1544,6 @@ const struct hal_rx_ops hal_rx_wcn7850_ops = { .rx_desc_is_da_mcbc = ath12k_hw_wcn7850_rx_desc_is_da_mcbc, .rx_desc_get_dot11_hdr = ath12k_hw_wcn7850_rx_desc_get_dot11_hdr, .rx_desc_get_crypto_header = ath12k_hw_wcn7850_rx_desc_get_crypto_hdr, - .rx_desc_get_mpdu_frame_ctl = ath12k_hw_wcn7850_rx_desc_get_mpdu_frame_ctl, .dp_rx_h_msdu_done = ath12k_hw_wcn7850_dp_rx_h_msdu_done, .dp_rx_h_l4_cksum_fail = ath12k_hw_wcn7850_dp_rx_h_l4_cksum_fail, .dp_rx_h_ip_cksum_fail = ath12k_hw_wcn7850_dp_rx_h_ip_cksum_fail, @@ -1756,7 +1744,7 @@ static void ath12k_hal_srng_src_hw_init(struct ath12k_base *ab, HAL_TCL1_RING_BASE_MSB_RING_BASE_ADDR_MSB) | u32_encode_bits((srng->entry_size * srng->num_entries), HAL_TCL1_RING_BASE_MSB_RING_SIZE); - ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET, val); + ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_BASE_MSB_OFFSET(ab), val); val = u32_encode_bits(srng->entry_size, HAL_REO1_RING_ID_ENTRY_SIZE); ath12k_hif_write32(ab, reg_base + HAL_TCL1_RING_ID_OFFSET(ab), val); @@ -1962,7 +1950,7 @@ u32 ath12k_hal_ce_dst_status_get_length(struct hal_ce_srng_dst_status_desc *desc { u32 len; - len = le32_get_bits(desc->flags, HAL_CE_DST_STATUS_DESC_FLAGS_LEN); + len = le32_get_bits(READ_ONCE(desc->flags), HAL_CE_DST_STATUS_DESC_FLAGS_LEN); desc->flags &= ~cpu_to_le32(HAL_CE_DST_STATUS_DESC_FLAGS_LEN); return len; @@ -2054,6 +2042,24 @@ int ath12k_hal_srng_src_num_free(struct ath12k_base *ab, struct hal_srng *srng, return ((srng->ring_size - hp + tp) / srng->entry_size) - 1; } +void *ath12k_hal_srng_src_next_peek(struct ath12k_base *ab, + struct hal_srng *srng) +{ + void *desc; + u32 next_hp; + + lockdep_assert_held(&srng->lock); + + next_hp = (srng->u.src_ring.hp + srng->entry_size) % srng->ring_size; + + if (next_hp == srng->u.src_ring.cached_tp) + return NULL; + + desc = srng->ring_base_vaddr + next_hp; + + return desc; +} + void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng) { @@ -2087,6 +2093,17 @@ void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab, return desc; } +void *ath12k_hal_srng_src_peek(struct ath12k_base *ab, struct hal_srng *srng) +{ + lockdep_assert_held(&srng->lock); + + if (((srng->u.src_ring.hp + srng->entry_size) % srng->ring_size) == + srng->u.src_ring.cached_tp) + return NULL; + + return srng->ring_base_vaddr + srng->u.src_ring.hp; +} + void *ath12k_hal_srng_src_reap_next(struct ath12k_base *ab, struct hal_srng *srng) { @@ -2132,7 +2149,7 @@ void ath12k_hal_srng_access_begin(struct ath12k_base *ab, struct hal_srng *srng) srng->u.src_ring.cached_tp = *(volatile u32 *)srng->u.src_ring.tp_addr; else - srng->u.dst_ring.cached_hp = *srng->u.dst_ring.hp_addr; + srng->u.dst_ring.cached_hp = READ_ONCE(*srng->u.dst_ring.hp_addr); } /* Update cached ring head/tail pointers to HW. ath12k_hal_srng_access_begin() diff --git a/drivers/net/wireless/ath/ath12k/hal.h b/drivers/net/wireless/ath/ath12k/hal.h index 94e2e8735958..0ee9c6b26dab 100644 --- a/drivers/net/wireless/ath/ath12k/hal.h +++ b/drivers/net/wireless/ath/ath12k/hal.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_HAL_H @@ -11,6 +11,7 @@ #include "rx_desc.h" struct ath12k_base; +#define HAL_CE_REMAP_REG_BASE (ab->ce_remap_base_addr) #define HAL_LINK_DESC_SIZE (32 << 2) #define HAL_LINK_DESC_ALIGN 128 @@ -21,6 +22,7 @@ struct ath12k_base; #define HAL_MAX_AVAIL_BLK_RES 3 #define HAL_RING_BASE_ALIGN 8 +#define HAL_REO_QLUT_ADDR_ALIGN 256 #define HAL_WBM_IDLE_SCATTER_BUF_SIZE_MAX 32704 /* TODO: Check with hw team on the supported scatter buf size */ @@ -39,15 +41,21 @@ struct ath12k_base; #define HAL_OFFSET_FROM_HP_TO_TP 4 #define HAL_SHADOW_REG(x) (HAL_SHADOW_BASE_ADDR + (4 * (x))) +#define HAL_REO_QDESC_MAX_PEERID 8191 /* WCSS Relative address */ +#define HAL_SEQ_WCSS_CMEM_OFFSET 0x00100000 #define HAL_SEQ_WCSS_UMAC_OFFSET 0x00a00000 #define HAL_SEQ_WCSS_UMAC_REO_REG 0x00a38000 #define HAL_SEQ_WCSS_UMAC_TCL_REG 0x00a44000 -#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG 0x01b80000 -#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG 0x01b81000 -#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG 0x01b82000 -#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG 0x01b83000 +#define HAL_SEQ_WCSS_UMAC_CE0_SRC_REG(ab) \ + ((ab)->hw_params->regs->hal_umac_ce0_src_reg_base) +#define HAL_SEQ_WCSS_UMAC_CE0_DST_REG(ab) \ + ((ab)->hw_params->regs->hal_umac_ce0_dest_reg_base) +#define HAL_SEQ_WCSS_UMAC_CE1_SRC_REG(ab) \ + ((ab)->hw_params->regs->hal_umac_ce1_src_reg_base) +#define HAL_SEQ_WCSS_UMAC_CE1_DST_REG(ab) \ + ((ab)->hw_params->regs->hal_umac_ce1_dest_reg_base) #define HAL_SEQ_WCSS_UMAC_WBM_REG 0x00a34000 #define HAL_CE_WFSS_CE_REG_BASE 0x01b80000 @@ -57,8 +65,10 @@ struct ath12k_base; /* SW2TCL(x) R0 ring configuration address */ #define HAL_TCL1_RING_CMN_CTRL_REG 0x00000020 #define HAL_TCL1_RING_DSCP_TID_MAP 0x00000240 -#define HAL_TCL1_RING_BASE_LSB 0x00000900 -#define HAL_TCL1_RING_BASE_MSB 0x00000904 +#define HAL_TCL1_RING_BASE_LSB(ab) \ + ((ab)->hw_params->regs->hal_tcl1_ring_base_lsb) +#define HAL_TCL1_RING_BASE_MSB(ab) \ + ((ab)->hw_params->regs->hal_tcl1_ring_base_msb) #define HAL_TCL1_RING_ID(ab) ((ab)->hw_params->regs->hal_tcl1_ring_id) #define HAL_TCL1_RING_MISC(ab) \ ((ab)->hw_params->regs->hal_tcl1_ring_misc) @@ -76,30 +86,31 @@ struct ath12k_base; ((ab)->hw_params->regs->hal_tcl1_ring_msi1_base_msb) #define HAL_TCL1_RING_MSI1_DATA(ab) \ ((ab)->hw_params->regs->hal_tcl1_ring_msi1_data) -#define HAL_TCL2_RING_BASE_LSB 0x00000978 +#define HAL_TCL2_RING_BASE_LSB(ab) \ + ((ab)->hw_params->regs->hal_tcl2_ring_base_lsb) #define HAL_TCL_RING_BASE_LSB(ab) \ ((ab)->hw_params->regs->hal_tcl_ring_base_lsb) -#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab) \ - (HAL_TCL1_RING_MSI1_BASE_LSB(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab) \ - (HAL_TCL1_RING_MSI1_BASE_MSB(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MSI1_DATA_OFFSET(ab) \ - (HAL_TCL1_RING_MSI1_DATA(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_BASE_MSB_OFFSET \ - (HAL_TCL1_RING_BASE_MSB - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_ID_OFFSET(ab) \ - (HAL_TCL1_RING_ID(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab) \ - (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab) \ - (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab) \ - (HAL_TCL1_RING_TP_ADDR_LSB(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab) \ - (HAL_TCL1_RING_TP_ADDR_MSB(ab) - HAL_TCL1_RING_BASE_LSB) -#define HAL_TCL1_RING_MISC_OFFSET(ab) \ - (HAL_TCL1_RING_MISC(ab) - HAL_TCL1_RING_BASE_LSB) +#define HAL_TCL1_RING_MSI1_BASE_LSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_MSI1_BASE_LSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_MSI1_BASE_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_MSI1_BASE_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_MSI1_DATA_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_MSI1_DATA(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_BASE_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_BASE_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_ID_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_ID(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX0_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX0(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_CONSR_INT_SETUP_IX1_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_CONSUMER_INT_SETUP_IX1(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_TP_ADDR_LSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_TP_ADDR_LSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_TP_ADDR_MSB_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_TP_ADDR_MSB(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) +#define HAL_TCL1_RING_MISC_OFFSET(ab) ({ typeof(ab) _ab = (ab); \ + (HAL_TCL1_RING_MISC(_ab) - HAL_TCL1_RING_BASE_LSB(_ab)); }) /* SW2TCL(x) R2 ring pointers (head/tail) address */ #define HAL_TCL1_RING_HP 0x00002000 @@ -132,6 +143,8 @@ struct ath12k_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_QDESC_ADDR(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_addr) +#define HAL_REO1_QDESC_MAX_PEERID(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_max_peerid) #define HAL_REO1_SW_COOKIE_CFG0(ab) ((ab)->hw_params->regs->hal_reo1_sw_cookie_cfg0) #define HAL_REO1_SW_COOKIE_CFG1(ab) ((ab)->hw_params->regs->hal_reo1_sw_cookie_cfg1) #define HAL_REO1_QDESC_LUT_BASE0(ab) ((ab)->hw_params->regs->hal_reo1_qdesc_lut_base0) @@ -319,6 +332,8 @@ struct ath12k_base; #define HAL_REO1_SW_COOKIE_CFG_ALIGN BIT(18) #define HAL_REO1_SW_COOKIE_CFG_ENABLE BIT(19) #define HAL_REO1_SW_COOKIE_CFG_GLOBAL_ENABLE BIT(20) +#define HAL_REO_QDESC_ADDR_READ_LUT_ENABLE BIT(7) +#define HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY BIT(6) /* CE ring bit field mask and shift */ #define HAL_CE_DST_R0_DEST_CTRL_MAX_LEN GENMASK(15, 0) @@ -365,6 +380,9 @@ struct ath12k_base; * ath12k_hal_rx_desc_get_err(). */ +#define HAL_IPQ5332_CE_WFSS_REG_BASE 0x740000 +#define HAL_IPQ5332_CE_SIZE 0x100000 + enum hal_srng_ring_id { HAL_SRNG_RING_ID_REO2SW0 = 0, HAL_SRNG_RING_ID_REO2SW1, @@ -480,6 +498,7 @@ enum hal_srng_ring_id { HAL_SRNG_RING_ID_WMAC1_SW2RXMON_BUF0 = HAL_SRNG_RING_ID_PMAC1_ID_START, + HAL_SRNG_RING_ID_WMAC1_SW2RXDMA1_STATBUF, HAL_SRNG_RING_ID_WMAC1_RXDMA2SW0, HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, HAL_SRNG_RING_ID_WMAC1_RXMON2SW0 = HAL_SRNG_RING_ID_WMAC1_RXDMA2SW1, @@ -1068,7 +1087,6 @@ struct hal_rx_ops { bool (*rx_desc_is_da_mcbc)(struct hal_rx_desc *desc); void (*rx_desc_get_dot11_hdr)(struct hal_rx_desc *desc, struct ieee80211_hdr *hdr); - u16 (*rx_desc_get_mpdu_frame_ctl)(struct hal_rx_desc *desc); void (*rx_desc_get_crypto_header)(struct hal_rx_desc *desc, u8 *crypto_hdr, enum hal_encrypt_type enctype); @@ -1126,6 +1144,7 @@ void ath12k_hal_srng_get_params(struct ath12k_base *ab, struct hal_srng *srng, struct hal_srng_params *params); void *ath12k_hal_srng_dst_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng); +void *ath12k_hal_srng_src_peek(struct ath12k_base *ab, struct hal_srng *srng); void *ath12k_hal_srng_dst_peek(struct ath12k_base *ab, struct hal_srng *srng); int ath12k_hal_srng_dst_num_free(struct ath12k_base *ab, struct hal_srng *srng, bool sync_hw_ptr); @@ -1133,6 +1152,8 @@ void *ath12k_hal_srng_src_get_next_reaped(struct ath12k_base *ab, struct hal_srng *srng); void *ath12k_hal_srng_src_reap_next(struct ath12k_base *ab, struct hal_srng *srng); +void *ath12k_hal_srng_src_next_peek(struct ath12k_base *ab, + struct hal_srng *srng); void *ath12k_hal_srng_src_get_next_entry(struct ath12k_base *ab, struct hal_srng *srng); int ath12k_hal_srng_src_num_free(struct ath12k_base *ab, struct hal_srng *srng, @@ -1154,4 +1175,5 @@ int ath12k_hal_srng_update_shadow_config(struct ath12k_base *ab, void ath12k_hal_srng_shadow_config(struct ath12k_base *ab); void ath12k_hal_srng_shadow_update_hp_tp(struct ath12k_base *ab, struct hal_srng *srng); +void ath12k_hal_reo_shared_qaddr_cache_clear(struct ath12k_base *ab); #endif diff --git a/drivers/net/wireless/ath/ath12k/hal_desc.h b/drivers/net/wireless/ath/ath12k/hal_desc.h index 7b0403d245e5..0173f731bfef 100644 --- a/drivers/net/wireless/ath/ath12k/hal_desc.h +++ b/drivers/net/wireless/ath/ath12k/hal_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -707,7 +707,7 @@ enum hal_rx_msdu_desc_reo_dest_ind { #define RX_MSDU_DESC_INFO0_DECAP_FORMAT GENMASK(30, 29) #define HAL_RX_MSDU_PKT_LENGTH_GET(val) \ - (u32_get_bits((val), RX_MSDU_DESC_INFO0_MSDU_LENGTH)) + (le32_get_bits((val), RX_MSDU_DESC_INFO0_MSDU_LENGTH)) struct rx_msdu_desc { __le32 info0; @@ -1008,6 +1008,10 @@ enum hal_reo_entr_rxdma_ecode { HAL_REO_ENTR_RING_RXDMA_ECODE_FLOW_TIMEOUT_ERR, HAL_REO_ENTR_RING_RXDMA_ECODE_FLUSH_REQUEST_ERR, HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_FRAG_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_MULTICAST_ECHO_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_AMSDU_MISMATCH_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_UNAUTH_WDS_ERR, + HAL_REO_ENTR_RING_RXDMA_ECODE_GRPCAST_AMSDU_WDS_ERR, HAL_REO_ENTR_RING_RXDMA_ECODE_MAX, }; @@ -1284,11 +1288,13 @@ enum hal_tcl_encap_type { HAL_TCL_ENCAP_TYPE_NATIVE_WIFI, HAL_TCL_ENCAP_TYPE_ETHERNET, HAL_TCL_ENCAP_TYPE_802_3 = 3, + HAL_TCL_ENCAP_TYPE_MAX }; enum hal_tcl_desc_type { HAL_TCL_DESC_TYPE_BUFFER, HAL_TCL_DESC_TYPE_EXT_DESC, + HAL_TCL_DESC_TYPE_MAX, }; enum hal_wbm_htt_tx_comp_status { @@ -1298,6 +1304,7 @@ enum hal_wbm_htt_tx_comp_status { HAL_WBM_REL_HTT_TX_COMP_STATUS_REINJ, HAL_WBM_REL_HTT_TX_COMP_STATUS_INSPECT, HAL_WBM_REL_HTT_TX_COMP_STATUS_MEC_NOTIFY, + HAL_WBM_REL_HTT_TX_COMP_STATUS_VDEVID_MISMATCH, HAL_WBM_REL_HTT_TX_COMP_STATUS_MAX, }; @@ -1806,6 +1813,7 @@ enum hal_wbm_rel_src_module { HAL_WBM_REL_SRC_MODULE_REO, HAL_WBM_REL_SRC_MODULE_FW, HAL_WBM_REL_SRC_MODULE_SW, + HAL_WBM_REL_SRC_MODULE_MAX, }; enum hal_wbm_rel_desc_type { @@ -1998,6 +2006,7 @@ struct hal_wbm_release_ring_cc_rx { #define HAL_WBM_RELEASE_INFO3_CONTINUATION BIT(2) #define HAL_WBM_RELEASE_INFO5_LOOPING_COUNT GENMASK(31, 28) +#define HAL_ENCRYPT_TYPE_MAX 12 struct hal_wbm_release_ring { struct ath12k_buffer_addr buf_addr_info; @@ -2968,9 +2977,8 @@ struct hal_mon_buf_ring { #define HAL_MON_DEST_COOKIE_BUF_ID GENMASK(17, 0) -#define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(15, 0) -#define HAL_MON_DEST_INFO0_FLUSH_DETECTED BIT(16) -#define HAL_MON_DEST_INFO0_END_OF_PPDU BIT(17) +#define HAL_MON_DEST_INFO0_END_OFFSET GENMASK(11, 0) +#define HAL_MON_DEST_INFO0_END_REASON GENMASK(17, 16) #define HAL_MON_DEST_INFO0_INITIATOR BIT(18) #define HAL_MON_DEST_INFO0_EMPTY_DESC BIT(19) #define HAL_MON_DEST_INFO0_RING_ID GENMASK(27, 20) diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.c b/drivers/net/wireless/ath/ath12k/hal_rx.c index ac17d6223fa7..48aa48c48606 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.c +++ b/drivers/net/wireless/ath/ath12k/hal_rx.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "debug.h" @@ -326,7 +326,7 @@ int ath12k_hal_desc_reo_parse_err(struct ath12k_base *ab, HAL_REO_DEST_RING_INFO0_PUSH_REASON); err_code = le32_get_bits(desc->info0, HAL_REO_DEST_RING_INFO0_ERROR_CODE); - ab->soc_stats.reo_error[err_code]++; + ab->device_stats.reo_error[err_code]++; if (push_reason != HAL_REO_DEST_RING_PUSH_REASON_ERR_DETECTED && push_reason != HAL_REO_DEST_RING_PUSH_REASON_ROUTING_INSTRUCTION) { @@ -381,7 +381,7 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc, val = le32_get_bits(wbm_desc->buf_addr_info.info1, BUFFER_ADDR_INFO1_RET_BUF_MGR); if (val != HAL_RX_BUF_RBM_SW3_BM) { - ab->soc_stats.invalid_rbm++; + ab->device_stats.invalid_rbm++; return -EINVAL; } @@ -393,7 +393,7 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc, val = le32_get_bits(wbm_cc_desc->info0, HAL_WBM_RELEASE_RX_CC_INFO0_RBM); if (val != HAL_RX_BUF_RBM_SW3_BM) { - ab->soc_stats.invalid_rbm++; + ab->device_stats.invalid_rbm++; return -EINVAL; } @@ -446,17 +446,97 @@ void ath12k_hal_rx_reo_ent_paddr_get(struct ath12k_base *ab, *cookie = le32_get_bits(buff_addr->info1, BUFFER_ADDR_INFO1_SW_COOKIE); } +void ath12k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, dma_addr_t *paddr, + u32 *sw_cookie, + struct ath12k_buffer_addr **pp_buf_addr, + u8 *rbm, u32 *msdu_cnt) +{ + struct hal_reo_entrance_ring *reo_ent_ring = + (struct hal_reo_entrance_ring *)rx_desc; + struct ath12k_buffer_addr *buf_addr_info; + struct rx_mpdu_desc *rx_mpdu_desc_info_details; + + rx_mpdu_desc_info_details = + (struct rx_mpdu_desc *)&reo_ent_ring->rx_mpdu_info; + + *msdu_cnt = le32_get_bits(rx_mpdu_desc_info_details->info0, + RX_MPDU_DESC_INFO0_MSDU_COUNT); + + buf_addr_info = (struct ath12k_buffer_addr *)&reo_ent_ring->buf_addr_info; + + *paddr = (((u64)le32_get_bits(buf_addr_info->info1, + BUFFER_ADDR_INFO1_ADDR)) << 32) | + le32_get_bits(buf_addr_info->info0, + BUFFER_ADDR_INFO0_ADDR); + + *sw_cookie = le32_get_bits(buf_addr_info->info1, + BUFFER_ADDR_INFO1_SW_COOKIE); + *rbm = le32_get_bits(buf_addr_info->info1, + BUFFER_ADDR_INFO1_RET_BUF_MGR); + + *pp_buf_addr = (void *)buf_addr_info; +} + +void ath12k_hal_rx_msdu_list_get(struct ath12k *ar, + struct hal_rx_msdu_link *link_desc, + struct hal_rx_msdu_list *msdu_list, + u16 *num_msdus) +{ + struct hal_rx_msdu_details *msdu_details = NULL; + struct rx_msdu_desc *msdu_desc_info = NULL; + u32 last = 0, first = 0; + u8 tmp = 0; + int i; + + last = u32_encode_bits(last, RX_MSDU_DESC_INFO0_LAST_MSDU_IN_MPDU); + first = u32_encode_bits(first, RX_MSDU_DESC_INFO0_FIRST_MSDU_IN_MPDU); + msdu_details = &link_desc->msdu_link[0]; + + for (i = 0; i < HAL_RX_NUM_MSDU_DESC; i++) { + if (!i && le32_get_bits(msdu_details[i].buf_addr_info.info0, + BUFFER_ADDR_INFO0_ADDR) == 0) + break; + if (le32_get_bits(msdu_details[i].buf_addr_info.info0, + BUFFER_ADDR_INFO0_ADDR) == 0) { + msdu_desc_info = &msdu_details[i - 1].rx_msdu_info; + msdu_desc_info->info0 |= cpu_to_le32(last); + break; + } + msdu_desc_info = &msdu_details[i].rx_msdu_info; + + if (!i) + msdu_desc_info->info0 |= cpu_to_le32(first); + else if (i == (HAL_RX_NUM_MSDU_DESC - 1)) + msdu_desc_info->info0 |= cpu_to_le32(last); + msdu_list->msdu_info[i].msdu_flags = le32_to_cpu(msdu_desc_info->info0); + msdu_list->msdu_info[i].msdu_len = + HAL_RX_MSDU_PKT_LENGTH_GET(msdu_desc_info->info0); + msdu_list->sw_cookie[i] = + le32_get_bits(msdu_details[i].buf_addr_info.info1, + BUFFER_ADDR_INFO1_SW_COOKIE); + tmp = le32_get_bits(msdu_details[i].buf_addr_info.info1, + BUFFER_ADDR_INFO1_RET_BUF_MGR); + msdu_list->paddr[i] = + ((u64)(le32_get_bits(msdu_details[i].buf_addr_info.info1, + BUFFER_ADDR_INFO1_ADDR)) << 32) | + le32_get_bits(msdu_details[i].buf_addr_info.info0, + BUFFER_ADDR_INFO0_ADDR); + msdu_list->rbm[i] = tmp; + } + *num_msdus = i; +} + void ath12k_hal_rx_msdu_link_desc_set(struct ath12k_base *ab, - struct hal_wbm_release_ring *dst_desc, - struct hal_wbm_release_ring *src_desc, + struct hal_wbm_release_ring *desc, + struct ath12k_buffer_addr *buf_addr_info, enum hal_wbm_rel_bm_act action) { - dst_desc->buf_addr_info = src_desc->buf_addr_info; - dst_desc->info0 |= le32_encode_bits(HAL_WBM_REL_SRC_MODULE_SW, - HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE) | - le32_encode_bits(action, HAL_WBM_RELEASE_INFO0_BM_ACTION) | - le32_encode_bits(HAL_WBM_REL_DESC_TYPE_MSDU_LINK, - HAL_WBM_RELEASE_INFO0_DESC_TYPE); + desc->buf_addr_info = *buf_addr_info; + desc->info0 |= le32_encode_bits(HAL_WBM_REL_SRC_MODULE_SW, + HAL_WBM_RELEASE_INFO0_REL_SRC_MODULE) | + le32_encode_bits(action, HAL_WBM_RELEASE_INFO0_BM_ACTION) | + le32_encode_bits(HAL_WBM_REL_DESC_TYPE_MSDU_LINK, + HAL_WBM_RELEASE_INFO0_DESC_TYPE); } void ath12k_hal_reo_status_queue_stats(struct ath12k_base *ab, struct hal_tlv_64_hdr *tlv, @@ -851,3 +931,20 @@ void ath12k_hal_reo_hw_setup(struct ath12k_base *ab, u32 ring_hash_map) ath12k_hif_write32(ab, reo_base + HAL_REO1_DEST_RING_CTRL_IX_3, ring_hash_map); } + +void ath12k_hal_reo_shared_qaddr_cache_clear(struct ath12k_base *ab) +{ + u32 val; + + lockdep_assert_held(&ab->base_lock); + val = ath12k_hif_read32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO1_QDESC_ADDR(ab)); + + val |= u32_encode_bits(1, HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY); + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO1_QDESC_ADDR(ab), val); + + val &= ~HAL_REO_QDESC_ADDR_READ_CLEAR_QDESC_ARRAY; + ath12k_hif_write32(ab, HAL_SEQ_WCSS_UMAC_REO_REG + + HAL_REO1_QDESC_ADDR(ab), val); +} diff --git a/drivers/net/wireless/ath/ath12k/hal_rx.h b/drivers/net/wireless/ath/ath12k/hal_rx.h index b08aa2e79f41..a3ab588aae19 100644 --- a/drivers/net/wireless/ath/ath12k/hal_rx.h +++ b/drivers/net/wireless/ath/ath12k/hal_rx.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_HAL_RX_H @@ -22,9 +22,6 @@ struct hal_rx_wbm_rel_info { #define HAL_INVALID_PEERID 0x3fff #define VHT_SIG_SU_NSS_MASK 0x7 -#define HAL_RX_MAX_MCS 12 -#define HAL_RX_MAX_NSS 8 - #define HAL_RX_MPDU_INFO_PN_GET_BYTE1(__val) \ le32_get_bits((__val), GENMASK(7, 0)) @@ -71,6 +68,8 @@ enum hal_rx_preamble { HAL_RX_PREAMBLE_11N, HAL_RX_PREAMBLE_11AC, HAL_RX_PREAMBLE_11AX, + HAL_RX_PREAMBLE_11BA, + HAL_RX_PREAMBLE_11BE, HAL_RX_PREAMBLE_MAX, }; @@ -108,9 +107,13 @@ enum hal_rx_mon_status { HAL_RX_MON_STATUS_PPDU_NOT_DONE, HAL_RX_MON_STATUS_PPDU_DONE, HAL_RX_MON_STATUS_BUF_DONE, + HAL_RX_MON_STATUS_BUF_ADDR, + HAL_RX_MON_STATUS_MPDU_START, + HAL_RX_MON_STATUS_MPDU_END, + HAL_RX_MON_STATUS_MSDU_END, }; -#define HAL_RX_MAX_MPDU 256 +#define HAL_RX_MAX_MPDU 1024 #define HAL_RX_NUM_WORDS_PER_PPDU_BITMAP (HAL_RX_MAX_MPDU >> 5) struct hal_rx_user_status { @@ -143,10 +146,43 @@ struct hal_rx_user_status { u32 mpdu_fcs_ok_bitmap[HAL_RX_NUM_WORDS_PER_PPDU_BITMAP]; u32 mpdu_ok_byte_count; u32 mpdu_err_byte_count; + bool ampdu_present; + u16 ampdu_id; }; #define HAL_MAX_UL_MU_USERS 37 +struct hal_rx_u_sig_info { + bool ul_dl; + u8 bw; + u8 ppdu_type_comp_mode; + u8 eht_sig_mcs; + u8 num_eht_sig_sym; + struct ieee80211_radiotap_eht_usig usig; +}; + +#define HAL_RX_MON_MAX_AGGR_SIZE 128 + +struct hal_rx_tlv_aggr_info { + bool in_progress; + u16 cur_len; + u16 tlv_tag; + u8 buf[HAL_RX_MON_MAX_AGGR_SIZE]; +}; + +struct hal_rx_radiotap_eht { + __le32 known; + __le32 data[9]; +}; + +#define EHT_MAX_USER_INFO 4 + +struct hal_rx_eht_info { + u8 num_user_info; + struct hal_rx_radiotap_eht eht; + u32 user_info[EHT_MAX_USER_INFO]; +}; + struct hal_rx_mon_ppdu_info { u32 ppdu_id; u32 last_ppdu_id; @@ -227,10 +263,15 @@ struct hal_rx_mon_ppdu_info { u8 addr4[ETH_ALEN]; struct hal_rx_user_status userstats[HAL_MAX_UL_MU_USERS]; u8 userid; - u16 ampdu_id[HAL_MAX_UL_MU_USERS]; bool first_msdu_in_mpdu; bool is_ampdu; u8 medium_prot_type; + bool ppdu_continuation; + bool eht_usig; + struct hal_rx_u_sig_info u_sig_info; + bool is_eht; + struct hal_rx_eht_info eht_info; + struct hal_rx_tlv_aggr_info tlv_aggr; }; #define HAL_RX_PPDU_START_INFO0_PPDU_ID GENMASK(15, 0) @@ -466,6 +507,18 @@ struct hal_rx_mpdu_start { __le32 rsvd2[16]; } __packed; +struct hal_rx_msdu_end { + __le32 info0; + __le32 rsvd0[9]; + __le16 info00; + __le16 info01; + __le32 rsvd00[8]; + __le32 info1; + __le32 rsvd1[10]; + __le32 info2; + __le32 rsvd2; +} __packed; + #define HAL_RX_PPDU_END_DURATION GENMASK(23, 0) struct hal_rx_ppdu_end_duration { __le32 rsvd0[9]; @@ -485,6 +538,7 @@ struct hal_rx_msdu_desc_info { #define HAL_RX_NUM_MSDU_DESC 6 struct hal_rx_msdu_list { struct hal_rx_msdu_desc_info msdu_info[HAL_RX_NUM_MSDU_DESC]; + u64 paddr[HAL_RX_NUM_MSDU_DESC]; u32 sw_cookie[HAL_RX_NUM_MSDU_DESC]; u8 rbm[HAL_RX_NUM_MSDU_DESC]; }; @@ -641,6 +695,395 @@ struct hal_rx_resp_req_info { #define HAL_RX_MPDU_ERR_MPDU_LEN BIT(6) #define HAL_RX_MPDU_ERR_UNENCRYPTED_FRAME BIT(7) +#define HAL_RX_PHY_CMN_USER_INFO0_GI GENMASK(17, 16) + +struct hal_phyrx_common_user_info { + __le32 rsvd[2]; + __le32 info0; + __le32 rsvd1; +} __packed; + +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_NSS GENMASK(10, 7) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_BEAMFORMED BIT(11) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_DISREGARD GENMASK(13, 12) +#define HAL_RX_EHT_SIG_NDP_CMN_INFO0_CRC GENMASK(17, 14) + +struct hal_eht_sig_ndp_cmn_eb { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_LDPC_EXTA_SYM BIT(9) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_PRE_FEC_PAD_FACTOR GENMASK(11, 10) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_OVERFLOW_INFO0_DISREGARD GENMASK(16, 13) + +struct hal_eht_sig_usig_overflow { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_STA_ID GENMASK(10, 0) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_MCS GENMASK(14, 11) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_VALIDATE BIT(15) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_NSS GENMASK(19, 16) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_BEAMFORMED BIT(20) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CODING BIT(21) +#define HAL_RX_EHT_SIG_NON_MUMIMO_USER_INFO0_CRC GENMASK(25, 22) + +struct hal_eht_sig_non_mu_mimo { + __le32 info0; +} __packed; + +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_STA_ID GENMASK(10, 0) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_MCS GENMASK(14, 11) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CODING BIT(15) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_SPATIAL_CODING GENMASK(22, 16) +#define HAL_RX_EHT_SIG_MUMIMO_USER_INFO0_CRC GENMASK(26, 23) + +struct hal_eht_sig_mu_mimo { + __le32 info0; +} __packed; + +union hal_eht_sig_user_field { + struct hal_eht_sig_mu_mimo mu_mimo; + struct hal_eht_sig_non_mu_mimo n_mu_mimo; +}; + +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_SPATIAL_REUSE GENMASK(3, 0) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_GI_LTF GENMASK(5, 4) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_LTF_SYM GENMASK(8, 6) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_LDPC_EXTA_SYM BIT(9) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_PRE_FEC_PAD_FACTOR GENMASK(11, 10) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_DISREGARD GENMASK(16, 13) +#define HAL_RX_EHT_SIG_NON_OFDMA_INFO0_NUM_USERS GENMASK(19, 17) + +struct hal_eht_sig_non_ofdma_cmn_eb { + __le32 info0; + union hal_eht_sig_user_field user_field; +} __packed; + +#define HAL_RX_EHT_SIG_OFDMA_EB1_SPATIAL_REUSE GENMASK_ULL(3, 0) +#define HAL_RX_EHT_SIG_OFDMA_EB1_GI_LTF GENMASK_ULL(5, 4) +#define HAL_RX_EHT_SIG_OFDMA_EB1_NUM_LFT_SYM GENMASK_ULL(8, 6) +#define HAL_RX_EHT_SIG_OFDMA_EB1_LDPC_EXTRA_SYM BIT(9) +#define HAL_RX_EHT_SIG_OFDMA_EB1_PRE_FEC_PAD_FACTOR GENMASK_ULL(11, 10) +#define HAL_RX_EHT_SIG_OFDMA_EB1_PRE_DISAMBIGUITY BIT(12) +#define HAL_RX_EHT_SIG_OFDMA_EB1_DISREGARD GENMASK_ULL(16, 13) +#define HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_1 GENMASK_ULL(25, 17) +#define HAL_RX_EHT_SIG_OFDMA_EB1_RU_ALLOC_1_2 GENMASK_ULL(34, 26) +#define HAL_RX_EHT_SIG_OFDMA_EB1_CRC GENMASK_ULL(30, 27) + +struct hal_eht_sig_ofdma_cmn_eb1 { + __le64 info0; +} __packed; + +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_1 GENMASK_ULL(8, 0) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_2 GENMASK_ULL(17, 9) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_3 GENMASK_ULL(26, 18) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_4 GENMASK_ULL(35, 27) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_5 GENMASK_ULL(44, 36) +#define HAL_RX_EHT_SIG_OFDMA_EB2_RU_ALLOC_2_6 GENMASK_ULL(53, 45) +#define HAL_RX_EHT_SIG_OFDMA_EB2_MCS GNEMASK_ULL(57, 54) + +struct hal_eht_sig_ofdma_cmn_eb2 { + __le64 info0; +} __packed; + +struct hal_eht_sig_ofdma_cmn_eb { + struct hal_eht_sig_ofdma_cmn_eb1 eb1; + struct hal_eht_sig_ofdma_cmn_eb2 eb2; + union hal_eht_sig_user_field user_field; +} __packed; + +enum hal_eht_bw { + HAL_EHT_BW_20, + HAL_EHT_BW_40, + HAL_EHT_BW_80, + HAL_EHT_BW_160, + HAL_EHT_BW_320_1, + HAL_EHT_BW_320_2, +}; + +#define HAL_RX_USIG_CMN_INFO0_PHY_VERSION GENMASK(2, 0) +#define HAL_RX_USIG_CMN_INFO0_BW GENMASK(5, 3) +#define HAL_RX_USIG_CMN_INFO0_UL_DL BIT(6) +#define HAL_RX_USIG_CMN_INFO0_BSS_COLOR GENMASK(12, 7) +#define HAL_RX_USIG_CMN_INFO0_TXOP GENMASK(19, 13) +#define HAL_RX_USIG_CMN_INFO0_DISREGARD GENMASK(25, 20) +#define HAL_RX_USIG_CMN_INFO0_VALIDATE BIT(26) + +struct hal_mon_usig_cmn { + __le32 info0; +} __packed; + +#define HAL_RX_USIG_TB_INFO0_PPDU_TYPE_COMP_MODE GENMASK(1, 0) +#define HAL_RX_USIG_TB_INFO0_VALIDATE BIT(2) +#define HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_1 GENMASK(6, 3) +#define HAL_RX_USIG_TB_INFO0_SPATIAL_REUSE_2 GENMASK(10, 7) +#define HAL_RX_USIG_TB_INFO0_DISREGARD_1 GENMASK(15, 11) +#define HAL_RX_USIG_TB_INFO0_CRC GENMASK(19, 16) +#define HAL_RX_USIG_TB_INFO0_TAIL GENMASK(25, 20) +#define HAL_RX_USIG_TB_INFO0_RX_INTEG_CHECK_PASS BIT(31) + +struct hal_mon_usig_tb { + __le32 info0; +} __packed; + +#define HAL_RX_USIG_MU_INFO0_PPDU_TYPE_COMP_MODE GENMASK(1, 0) +#define HAL_RX_USIG_MU_INFO0_VALIDATE_1 BIT(2) +#define HAL_RX_USIG_MU_INFO0_PUNC_CH_INFO GENMASK(7, 3) +#define HAL_RX_USIG_MU_INFO0_VALIDATE_2 BIT(8) +#define HAL_RX_USIG_MU_INFO0_EHT_SIG_MCS GENMASK(10, 9) +#define HAL_RX_USIG_MU_INFO0_NUM_EHT_SIG_SYM GENMASK(15, 11) +#define HAL_RX_USIG_MU_INFO0_CRC GENMASK(20, 16) +#define HAL_RX_USIG_MU_INFO0_TAIL GENMASK(26, 21) +#define HAL_RX_USIG_MU_INFO0_RX_INTEG_CHECK_PASS BIT(31) + +struct hal_mon_usig_mu { + __le32 info0; +} __packed; + +union hal_mon_usig_non_cmn { + struct hal_mon_usig_tb tb; + struct hal_mon_usig_mu mu; +}; + +struct hal_mon_usig_hdr { + struct hal_mon_usig_cmn cmn; + union hal_mon_usig_non_cmn non_cmn; +} __packed; + +#define HAL_RX_USR_INFO0_PHY_PPDU_ID GENMASK(15, 0) +#define HAL_RX_USR_INFO0_USR_RSSI GENMASK(23, 16) +#define HAL_RX_USR_INFO0_PKT_TYPE GENMASK(27, 24) +#define HAL_RX_USR_INFO0_STBC BIT(28) +#define HAL_RX_USR_INFO0_RECEPTION_TYPE GENMASK(31, 29) + +#define HAL_RX_USR_INFO1_MCS GENMASK(3, 0) +#define HAL_RX_USR_INFO1_SGI GENMASK(5, 4) +#define HAL_RX_USR_INFO1_HE_RANGING_NDP BIT(6) +#define HAL_RX_USR_INFO1_MIMO_SS_BITMAP GENMASK(15, 8) +#define HAL_RX_USR_INFO1_RX_BW GENMASK(18, 16) +#define HAL_RX_USR_INFO1_DL_OFMDA_USR_IDX GENMASK(31, 24) + +#define HAL_RX_USR_INFO2_DL_OFDMA_CONTENT_CHAN BIT(0) +#define HAL_RX_USR_INFO2_NSS GENMASK(10, 8) +#define HAL_RX_USR_INFO2_STREAM_OFFSET GENMASK(13, 11) +#define HAL_RX_USR_INFO2_STA_DCM BIT(14) +#define HAL_RX_USR_INFO2_LDPC BIT(15) +#define HAL_RX_USR_INFO2_RU_TYPE_80_0 GENMASK(19, 16) +#define HAL_RX_USR_INFO2_RU_TYPE_80_1 GENMASK(23, 20) +#define HAL_RX_USR_INFO2_RU_TYPE_80_2 GENMASK(27, 24) +#define HAL_RX_USR_INFO2_RU_TYPE_80_3 GENMASK(31, 28) + +#define HAL_RX_USR_INFO3_RU_START_IDX_80_0 GENMASK(5, 0) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_1 GENMASK(13, 8) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_2 GENMASK(21, 16) +#define HAL_RX_USR_INFO3_RU_START_IDX_80_3 GENMASK(29, 24) + +struct hal_receive_user_info { + __le32 info0; + __le32 info1; + __le32 info2; + __le32 info3; + __le32 user_fd_rssi_seg0; + __le32 user_fd_rssi_seg1; + __le32 user_fd_rssi_seg2; + __le32 user_fd_rssi_seg3; +} __packed; + +enum hal_mon_reception_type { + HAL_RECEPTION_TYPE_SU, + HAL_RECEPTION_TYPE_DL_MU_MIMO, + HAL_RECEPTION_TYPE_DL_MU_OFMA, + HAL_RECEPTION_TYPE_DL_MU_OFDMA_MIMO, + HAL_RECEPTION_TYPE_UL_MU_MIMO, + HAL_RECEPTION_TYPE_UL_MU_OFDMA, + HAL_RECEPTION_TYPE_UL_MU_OFDMA_MIMO, +}; + +/* Different allowed RU in 11BE */ +#define HAL_EHT_RU_26 0ULL +#define HAL_EHT_RU_52 1ULL +#define HAL_EHT_RU_78 2ULL +#define HAL_EHT_RU_106 3ULL +#define HAL_EHT_RU_132 4ULL +#define HAL_EHT_RU_242 5ULL +#define HAL_EHT_RU_484 6ULL +#define HAL_EHT_RU_726 7ULL +#define HAL_EHT_RU_996 8ULL +#define HAL_EHT_RU_996x2 9ULL +#define HAL_EHT_RU_996x3 10ULL +#define HAL_EHT_RU_996x4 11ULL +#define HAL_EHT_RU_NONE 15ULL +#define HAL_EHT_RU_INVALID 31ULL +/* MRUs spanning above 80Mhz + * HAL_EHT_RU_996_484 = HAL_EHT_RU_484 + HAL_EHT_RU_996 + 4 (reserved) + */ +#define HAL_EHT_RU_996_484 18ULL +#define HAL_EHT_RU_996x2_484 28ULL +#define HAL_EHT_RU_996x3_484 40ULL +#define HAL_EHT_RU_996_484_242 23ULL + +#define NUM_RU_BITS_PER80 16 +#define NUM_RU_BITS_PER20 4 + +/* Different per_80Mhz band in 320Mhz bandwidth */ +#define HAL_80_0 0 +#define HAL_80_1 1 +#define HAL_80_2 2 +#define HAL_80_3 3 + +#define HAL_RU_80MHZ(num_band) ((num_band) * NUM_RU_BITS_PER80) +#define HAL_RU_20MHZ(idx_per_80) ((idx_per_80) * NUM_RU_BITS_PER20) + +#define HAL_RU_SHIFT(num_band, idx_per_80) \ + (HAL_RU_80MHZ(num_band) + HAL_RU_20MHZ(idx_per_80)) + +#define HAL_RU(ru, num_band, idx_per_80) \ + ((u64)(ru) << HAL_RU_SHIFT(num_band, idx_per_80)) + +/* MRU-996+484 */ +#define HAL_EHT_RU_996_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_2 (HAL_RU(HAL_EHT_RU_996, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1)) +#define HAL_EHT_RU_996_484_3 (HAL_RU(HAL_EHT_RU_996, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0)) +#define HAL_EHT_RU_996_484_4 (HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_3, 0)) +#define HAL_EHT_RU_996_484_5 (HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996, HAL_80_3, 0)) +#define HAL_EHT_RU_996_484_6 (HAL_RU(HAL_EHT_RU_996, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996_484_7 (HAL_RU(HAL_EHT_RU_996, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +/* MRU-996x2+484 */ +#define HAL_EHT_RU_996x2_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_2 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_3 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_4 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1)) +#define HAL_EHT_RU_996x2_484_5 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0)) +#define HAL_EHT_RU_996x2_484_6 (HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_7 (HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_8 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_9 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_3, 0)) +#define HAL_EHT_RU_996x2_484_10 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996x2_484_11 (HAL_RU(HAL_EHT_RU_996x2, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x2, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +/* MRU-996x3+484 */ +#define HAL_EHT_RU_996x3_484_0 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_1 (HAL_RU(HAL_EHT_RU_484, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_2 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_3 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_4 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 1) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_5 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_3, 0)) +#define HAL_EHT_RU_996x3_484_6 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 1)) +#define HAL_EHT_RU_996x3_484_7 (HAL_RU(HAL_EHT_RU_996x3, HAL_80_0, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_1, 0) | \ + HAL_RU(HAL_EHT_RU_996x3, HAL_80_2, 0) | \ + HAL_RU(HAL_EHT_RU_484, HAL_80_3, 0)) + +#define HAL_RU_PER80(ru_per80, num_80mhz, ru_idx_per80mhz) \ + (HAL_RU(ru_per80, num_80mhz, ru_idx_per80mhz)) + +#define RU_INVALID 0 +#define RU_26 1 +#define RU_52 2 +#define RU_106 4 +#define RU_242 9 +#define RU_484 18 +#define RU_996 37 +#define RU_2X996 74 +#define RU_3X996 111 +#define RU_4X996 148 +#define RU_52_26 (RU_52 + RU_26) +#define RU_106_26 (RU_106 + RU_26) +#define RU_484_242 (RU_484 + RU_242) +#define RU_996_484 (RU_996 + RU_484) +#define RU_996_484_242 (RU_996 + RU_484_242) +#define RU_2X996_484 (RU_2X996 + RU_484) +#define RU_3X996_484 (RU_3X996 + RU_484) + +enum ath12k_eht_ru_size { + ATH12K_EHT_RU_26, + ATH12K_EHT_RU_52, + ATH12K_EHT_RU_106, + ATH12K_EHT_RU_242, + ATH12K_EHT_RU_484, + ATH12K_EHT_RU_996, + ATH12K_EHT_RU_996x2, + ATH12K_EHT_RU_996x4, + ATH12K_EHT_RU_52_26, + ATH12K_EHT_RU_106_26, + ATH12K_EHT_RU_484_242, + ATH12K_EHT_RU_996_484, + ATH12K_EHT_RU_996_484_242, + ATH12K_EHT_RU_996x2_484, + ATH12K_EHT_RU_996x3, + ATH12K_EHT_RU_996x3_484, + + /* Keep last */ + ATH12K_EHT_RU_INVALID, +}; + +#define HAL_RX_RU_ALLOC_TYPE_MAX ATH12K_EHT_RU_INVALID + static inline enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) { @@ -662,6 +1105,9 @@ enum nl80211_he_ru_alloc ath12k_he_ru_tones_to_nl80211_he_ru_alloc(u16 ru_tones) case RU_996: ret = NL80211_RATE_INFO_HE_RU_ALLOC_996; break; + case RU_2X996: + ret = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; case RU_26: fallthrough; default: @@ -696,8 +1142,8 @@ void ath12k_hal_rx_msdu_link_info_get(struct hal_rx_msdu_link *link, u32 *num_ms u32 *msdu_cookies, enum hal_rx_buf_return_buf_manager *rbm); void ath12k_hal_rx_msdu_link_desc_set(struct ath12k_base *ab, - struct hal_wbm_release_ring *dst_desc, - struct hal_wbm_release_ring *src_desc, + struct hal_wbm_release_ring *desc, + struct ath12k_buffer_addr *buf_addr_info, enum hal_wbm_rel_bm_act action); void ath12k_hal_rx_buf_addr_info_set(struct ath12k_buffer_addr *binfo, dma_addr_t paddr, u32 cookie, u8 manager); @@ -712,5 +1158,12 @@ int ath12k_hal_wbm_desc_parse_err(struct ath12k_base *ab, void *desc, void ath12k_hal_rx_reo_ent_paddr_get(struct ath12k_base *ab, struct ath12k_buffer_addr *buff_addr, dma_addr_t *paddr, u32 *cookie); +void ath12k_hal_rx_reo_ent_buf_paddr_get(void *rx_desc, dma_addr_t *paddr, u32 *sw_cookie, + struct ath12k_buffer_addr **pp_buf_addr, + u8 *rbm, u32 *msdu_cnt); +void ath12k_hal_rx_msdu_list_get(struct ath12k *ar, + struct hal_rx_msdu_link *link_desc, + struct hal_rx_msdu_list *msdu_list, + u16 *num_msdus); #endif diff --git a/drivers/net/wireless/ath/ath12k/hal_tx.h b/drivers/net/wireless/ath/ath12k/hal_tx.h index 3cf5973771d7..eb065a79f6c6 100644 --- a/drivers/net/wireless/ath/ath12k/hal_tx.h +++ b/drivers/net/wireless/ath/ath12k/hal_tx.h @@ -1,7 +1,8 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. + * All rights reserved. */ #ifndef ATH12K_HAL_TX_H @@ -63,7 +64,12 @@ struct hal_tx_status { u8 try_cnt; u8 tid; u16 peer_id; - u32 rate_stats; + enum hal_tx_rate_stats_pkt_type pkt_type; + enum hal_tx_rate_stats_sgi sgi; + enum ath12k_supported_bw bw; + u8 mcs; + u16 tones; + u8 ofdma; }; #define HAL_TX_PHY_DESC_INFO0_BF_TYPE GENMASK(17, 16) diff --git a/drivers/net/wireless/ath/ath12k/hw.c b/drivers/net/wireless/ath/ath12k/hw.c index b7b583fadb5a..7e2cf0fb2085 100644 --- a/drivers/net/wireless/ath/ath12k/hw.c +++ b/drivers/net/wireless/ath/ath12k/hw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/types.h> @@ -118,6 +118,10 @@ static const struct ath12k_hw_ops wcn7850_ops = { #define ATH12K_TX_MON_RING_MASK_0 0x1 #define ATH12K_TX_MON_RING_MASK_1 0x2 +#define ATH12K_RX_MON_STATUS_RING_MASK_0 0x1 +#define ATH12K_RX_MON_STATUS_RING_MASK_1 0x2 +#define ATH12K_RX_MON_STATUS_RING_MASK_2 0x4 + /* Target firmware's Copy Engine configuration. */ static const struct ce_pipe_config ath12k_target_ce_config_wlan_qcn9274[] = { /* CE0: host->target HTC control and raw streams */ @@ -535,6 +539,217 @@ static const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_wcn7850 }, }; +static const struct ce_pipe_config ath12k_target_ce_config_wlan_ipq5332[] = { + /* host->target HTC control and raw streams */ + { + .pipenum = __cpu_to_le32(0), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* target->host HTT */ + { + .pipenum = __cpu_to_le32(1), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* target->host WMI + HTC control */ + { + .pipenum = __cpu_to_le32(2), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* host->target WMI */ + { + .pipenum = __cpu_to_le32(3), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* host->target HTT */ + { + .pipenum = __cpu_to_le32(4), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(256), + .nbytes_max = __cpu_to_le32(256), + .flags = __cpu_to_le32(CE_ATTR_FLAGS | CE_ATTR_DIS_INTR), + .reserved = __cpu_to_le32(0), + }, + /* Target -> host PKTLOG */ + { + .pipenum = __cpu_to_le32(5), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* Reserved for target autonomous HIF_memcpy */ + { + .pipenum = __cpu_to_le32(6), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(16384), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* CE7 Reserved for CV Prefetch */ + { + .pipenum = __cpu_to_le32(7), + .pipedir = __cpu_to_le32(PIPEDIR_OUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* CE8 Reserved for target generic HIF memcpy */ + { + .pipenum = __cpu_to_le32(8), + .pipedir = __cpu_to_le32(PIPEDIR_INOUT), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(16384), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* CE9 WMI logging/CFR/Spectral/Radar/ */ + { + .pipenum = __cpu_to_le32(9), + .pipedir = __cpu_to_le32(PIPEDIR_IN), + .nentries = __cpu_to_le32(32), + .nbytes_max = __cpu_to_le32(2048), + .flags = __cpu_to_le32(CE_ATTR_FLAGS), + .reserved = __cpu_to_le32(0), + }, + /* Unused TBD */ + { + .pipenum = __cpu_to_le32(10), + .pipedir = __cpu_to_le32(PIPEDIR_NONE), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(0), + .reserved = __cpu_to_le32(0), + }, + /* Unused TBD */ + { + .pipenum = __cpu_to_le32(11), + .pipedir = __cpu_to_le32(PIPEDIR_NONE), + .nentries = __cpu_to_le32(0), + .nbytes_max = __cpu_to_le32(0), + .flags = __cpu_to_le32(0), + .reserved = __cpu_to_le32(0), + }, +}; + +static const struct service_to_pipe ath12k_target_service_to_ce_map_wlan_ipq5332[] = { + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VO), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BK), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_BE), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_DATA_VI), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(3), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(2), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(0), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_RSVD_CTRL), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(1), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(0), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_TEST_RAW_STREAMS), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(1), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + __cpu_to_le32(PIPEDIR_OUT), + __cpu_to_le32(4), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_HTT_DATA_MSG), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(1), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_PKT_LOG), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(5), + }, + { + __cpu_to_le32(ATH12K_HTC_SVC_ID_WMI_CONTROL_DIAG), + __cpu_to_le32(PIPEDIR_IN), + __cpu_to_le32(9), + }, + /* (Additions here) */ + + { /* must be last */ + __cpu_to_le32(0), + __cpu_to_le32(0), + __cpu_to_le32(0), + }, +}; + static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = { .tx = { ATH12K_TX_RING_MASK_0, @@ -543,7 +758,11 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = { ATH12K_TX_RING_MASK_3, }, .rx_mon_dest = { - 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + ATH12K_RX_MON_RING_MASK_0, + ATH12K_RX_MON_RING_MASK_1, + ATH12K_RX_MON_RING_MASK_2, }, .rx = { 0, 0, 0, 0, @@ -573,6 +792,46 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_qcn9274 = { }, }; +static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_ipq5332 = { + .tx = { + ATH12K_TX_RING_MASK_0, + ATH12K_TX_RING_MASK_1, + ATH12K_TX_RING_MASK_2, + ATH12K_TX_RING_MASK_3, + }, + .rx_mon_dest = { + 0, 0, 0, 0, 0, 0, 0, 0, + ATH12K_RX_MON_RING_MASK_0, + }, + .rx = { + 0, 0, 0, 0, + ATH12K_RX_RING_MASK_0, + ATH12K_RX_RING_MASK_1, + ATH12K_RX_RING_MASK_2, + ATH12K_RX_RING_MASK_3, + }, + .rx_err = { + 0, 0, 0, + ATH12K_RX_ERR_RING_MASK_0, + }, + .rx_wbm_rel = { + 0, 0, 0, + ATH12K_RX_WBM_REL_RING_MASK_0, + }, + .reo_status = { + 0, 0, 0, + ATH12K_REO_STATUS_RING_MASK_0, + }, + .host2rxdma = { + 0, 0, 0, + ATH12K_HOST2RXDMA_RING_MASK_0, + }, + .tx_mon_dest = { + ATH12K_TX_MON_RING_MASK_0, + ATH12K_TX_MON_RING_MASK_1, + }, +}; + static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_wcn7850 = { .tx = { ATH12K_TX_RING_MASK_0, @@ -581,6 +840,12 @@ static const struct ath12k_hw_ring_mask ath12k_hw_ring_mask_wcn7850 = { }, .rx_mon_dest = { }, + .rx_mon_status = { + 0, 0, 0, 0, + ATH12K_RX_MON_STATUS_RING_MASK_0, + ATH12K_RX_MON_STATUS_RING_MASK_1, + ATH12K_RX_MON_STATUS_RING_MASK_2, + }, .rx = { 0, 0, 0, ATH12K_RX_RING_MASK_0, @@ -615,6 +880,9 @@ static const struct ath12k_hw_regs qcn9274_v1_regs = { .hal_tcl1_ring_msi1_base_msb = 0x0000094c, .hal_tcl1_ring_msi1_data = 0x00000950, .hal_tcl_ring_base_lsb = 0x00000b58, + .hal_tcl1_ring_base_lsb = 0x00000900, + .hal_tcl1_ring_base_msb = 0x00000904, + .hal_tcl2_ring_base_lsb = 0x00000978, /* TCL STATUS ring address */ .hal_tcl_status_ring_base_lsb = 0x00000d38, @@ -677,6 +945,12 @@ static const struct ath12k_hw_regs qcn9274_v1_regs = { /* REO status ring address */ .hal_reo_status_ring_base = 0x00000a84, + + /* CE base address */ + .hal_umac_ce0_src_reg_base = 0x01b80000, + .hal_umac_ce0_dest_reg_base = 0x01b81000, + .hal_umac_ce1_src_reg_base = 0x01b82000, + .hal_umac_ce1_dest_reg_base = 0x01b83000, }; static const struct ath12k_hw_regs qcn9274_v2_regs = { @@ -691,6 +965,9 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = { .hal_tcl1_ring_msi1_base_msb = 0x0000094c, .hal_tcl1_ring_msi1_data = 0x00000950, .hal_tcl_ring_base_lsb = 0x00000b58, + .hal_tcl1_ring_base_lsb = 0x00000900, + .hal_tcl1_ring_base_msb = 0x00000904, + .hal_tcl2_ring_base_lsb = 0x00000978, /* TCL STATUS ring address */ .hal_tcl_status_ring_base_lsb = 0x00000d38, @@ -730,6 +1007,8 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = { .hal_reo1_sw_cookie_cfg1 = 0x00000070, .hal_reo1_qdesc_lut_base0 = 0x00000074, .hal_reo1_qdesc_lut_base1 = 0x00000078, + .hal_reo1_qdesc_addr = 0x0000007c, + .hal_reo1_qdesc_max_peerid = 0x00000088, .hal_reo1_ring_base_lsb = 0x00000500, .hal_reo1_ring_base_msb = 0x00000504, .hal_reo1_ring_id = 0x00000508, @@ -757,6 +1036,100 @@ static const struct ath12k_hw_regs qcn9274_v2_regs = { /* REO status ring address */ .hal_reo_status_ring_base = 0x00000aa0, + + /* CE base address */ + .hal_umac_ce0_src_reg_base = 0x01b80000, + .hal_umac_ce0_dest_reg_base = 0x01b81000, + .hal_umac_ce1_src_reg_base = 0x01b82000, + .hal_umac_ce1_dest_reg_base = 0x01b83000, +}; + +static const struct ath12k_hw_regs ipq5332_regs = { + /* SW2TCL(x) R0 ring configuration address */ + .hal_tcl1_ring_id = 0x00000918, + .hal_tcl1_ring_misc = 0x00000920, + .hal_tcl1_ring_tp_addr_lsb = 0x0000092c, + .hal_tcl1_ring_tp_addr_msb = 0x00000930, + .hal_tcl1_ring_consumer_int_setup_ix0 = 0x00000940, + .hal_tcl1_ring_consumer_int_setup_ix1 = 0x00000944, + .hal_tcl1_ring_msi1_base_lsb = 0x00000958, + .hal_tcl1_ring_msi1_base_msb = 0x0000095c, + .hal_tcl1_ring_base_lsb = 0x00000910, + .hal_tcl1_ring_base_msb = 0x00000914, + .hal_tcl1_ring_msi1_data = 0x00000960, + .hal_tcl2_ring_base_lsb = 0x00000988, + .hal_tcl_ring_base_lsb = 0x00000b68, + + /* TCL STATUS ring address */ + .hal_tcl_status_ring_base_lsb = 0x00000d48, + + /* REO DEST ring address */ + .hal_reo2_ring_base = 0x00000578, + .hal_reo1_misc_ctrl_addr = 0x00000b9c, + .hal_reo1_sw_cookie_cfg0 = 0x0000006c, + .hal_reo1_sw_cookie_cfg1 = 0x00000070, + .hal_reo1_qdesc_lut_base0 = 0x00000074, + .hal_reo1_qdesc_lut_base1 = 0x00000078, + .hal_reo1_ring_base_lsb = 0x00000500, + .hal_reo1_ring_base_msb = 0x00000504, + .hal_reo1_ring_id = 0x00000508, + .hal_reo1_ring_misc = 0x00000510, + .hal_reo1_ring_hp_addr_lsb = 0x00000514, + .hal_reo1_ring_hp_addr_msb = 0x00000518, + .hal_reo1_ring_producer_int_setup = 0x00000524, + .hal_reo1_ring_msi1_base_lsb = 0x00000548, + .hal_reo1_ring_msi1_base_msb = 0x0000054C, + .hal_reo1_ring_msi1_data = 0x00000550, + .hal_reo1_aging_thres_ix0 = 0x00000B28, + .hal_reo1_aging_thres_ix1 = 0x00000B2C, + .hal_reo1_aging_thres_ix2 = 0x00000B30, + .hal_reo1_aging_thres_ix3 = 0x00000B34, + + /* REO Exception ring address */ + .hal_reo2_sw0_ring_base = 0x000008c0, + + /* REO Reinject ring address */ + .hal_sw2reo_ring_base = 0x00000320, + .hal_sw2reo1_ring_base = 0x00000398, + + /* REO cmd ring address */ + .hal_reo_cmd_ring_base = 0x000002A8, + + /* REO status ring address */ + .hal_reo_status_ring_base = 0x00000aa0, + + /* WBM idle link ring address */ + .hal_wbm_idle_ring_base_lsb = 0x00000d3c, + .hal_wbm_idle_ring_misc_addr = 0x00000d4c, + .hal_wbm_r0_idle_list_cntl_addr = 0x00000240, + .hal_wbm_r0_idle_list_size_addr = 0x00000244, + .hal_wbm_scattered_ring_base_lsb = 0x00000250, + .hal_wbm_scattered_ring_base_msb = 0x00000254, + .hal_wbm_scattered_desc_head_info_ix0 = 0x00000260, + .hal_wbm_scattered_desc_head_info_ix1 = 0x00000264, + .hal_wbm_scattered_desc_tail_info_ix0 = 0x00000270, + .hal_wbm_scattered_desc_tail_info_ix1 = 0x00000274, + .hal_wbm_scattered_desc_ptr_hp_addr = 0x0000027c, + + /* SW2WBM release ring address */ + .hal_wbm_sw_release_ring_base_lsb = 0x0000037c, + + /* WBM2SW release ring address */ + .hal_wbm0_release_ring_base_lsb = 0x00000e08, + .hal_wbm1_release_ring_base_lsb = 0x00000e80, + + /* PPE release ring address */ + .hal_ppe_rel_ring_base = 0x0000046c, + + /* CE address */ + .hal_umac_ce0_src_reg_base = 0x00740000 - + HAL_IPQ5332_CE_WFSS_REG_BASE, + .hal_umac_ce0_dest_reg_base = 0x00741000 - + HAL_IPQ5332_CE_WFSS_REG_BASE, + .hal_umac_ce1_src_reg_base = 0x00742000 - + HAL_IPQ5332_CE_WFSS_REG_BASE, + .hal_umac_ce1_dest_reg_base = 0x00743000 - + HAL_IPQ5332_CE_WFSS_REG_BASE, }; static const struct ath12k_hw_regs wcn7850_regs = { @@ -771,6 +1144,9 @@ static const struct ath12k_hw_regs wcn7850_regs = { .hal_tcl1_ring_msi1_base_msb = 0x0000094c, .hal_tcl1_ring_msi1_data = 0x00000950, .hal_tcl_ring_base_lsb = 0x00000b58, + .hal_tcl1_ring_base_lsb = 0x00000900, + .hal_tcl1_ring_base_msb = 0x00000904, + .hal_tcl2_ring_base_lsb = 0x00000978, /* TCL STATUS ring address */ .hal_tcl_status_ring_base_lsb = 0x00000d38, @@ -833,6 +1209,12 @@ static const struct ath12k_hw_regs wcn7850_regs = { /* REO status ring address */ .hal_reo_status_ring_base = 0x00000a84, + + /* CE base address */ + .hal_umac_ce0_src_reg_base = 0x01b80000, + .hal_umac_ce0_dest_reg_base = 0x01b81000, + .hal_umac_ce1_src_reg_base = 0x01b82000, + .hal_umac_ce1_dest_reg_base = 0x01b83000, }; static const struct ath12k_hw_hal_params ath12k_hw_hal_params_qcn9274 = { @@ -852,6 +1234,26 @@ static const struct ath12k_hw_hal_params ath12k_hw_hal_params_wcn7850 = { HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW4_EN, }; +static const struct ath12k_hw_hal_params ath12k_hw_hal_params_ipq5332 = { + .rx_buf_rbm = HAL_RX_BUF_RBM_SW3_BM, + .wbm2sw_cc_enable = HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW0_EN | + HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW1_EN | + HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW2_EN | + HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW3_EN | + HAL_WBM_SW_COOKIE_CONV_CFG_WBM2SW4_EN, +}; + +static const struct ce_ie_addr ath12k_ce_ie_addr_ipq5332 = { + .ie1_reg_addr = CE_HOST_IE_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie2_reg_addr = CE_HOST_IE_2_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, + .ie3_reg_addr = CE_HOST_IE_3_ADDRESS - HAL_IPQ5332_CE_WFSS_REG_BASE, +}; + +static const struct ce_remap ath12k_ce_remap_ipq5332 = { + .base = HAL_IPQ5332_CE_WFSS_REG_BASE, + .size = HAL_IPQ5332_CE_SIZE, +}; + static const struct ath12k_hw_params ath12k_hw_params[] = { { .name = "qcn9274 hw1.0", @@ -860,6 +1262,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .dir = "QCN9274/hw1.0", .board_size = 256 * 1024, .cal_offset = 128 * 1024, + .m3_loader = ath12k_m3_fw_loader_driver, }, .max_radios = 1, .single_pdev_only = false, @@ -895,7 +1298,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .download_calib = true, .supports_suspend = false, .tcl_ring_retry = true, - .reoq_lut_support = false, + .reoq_lut_support = true, .supports_shadow_regs = false, .num_tcl_banks = 48, @@ -928,6 +1331,14 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .iova_mask = 0, .supports_aspm = false, + + .ce_ie_addr = NULL, + .ce_remap = NULL, + .bdf_addr_offset = 0, + + .current_cc_support = false, + + .dp_primary_link_only = true, }, { .name = "wcn7850 hw2.0", @@ -937,6 +1348,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .dir = "WCN7850/hw2.0", .board_size = 256 * 1024, .cal_offset = 256 * 1024, + .m3_loader = ath12k_m3_fw_loader_driver, }, .max_radios = 1, @@ -968,7 +1380,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { BIT(NL80211_IFTYPE_P2P_DEVICE) | BIT(NL80211_IFTYPE_P2P_CLIENT) | BIT(NL80211_IFTYPE_P2P_GO), - .supports_monitor = false, + .supports_monitor = true, .idle_ps = true, .download_calib = false, @@ -1008,6 +1420,14 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .iova_mask = ATH12K_PCIE_MAX_PAYLOAD_SIZE - 1, .supports_aspm = true, + + .ce_ie_addr = NULL, + .ce_remap = NULL, + .bdf_addr_offset = 0, + + .current_cc_support = true, + + .dp_primary_link_only = false, }, { .name = "qcn9274 hw2.0", @@ -1016,6 +1436,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .dir = "QCN9274/hw2.0", .board_size = 256 * 1024, .cal_offset = 128 * 1024, + .m3_loader = ath12k_m3_fw_loader_driver, }, .max_radios = 2, .single_pdev_only = false, @@ -1035,7 +1456,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .hal_params = &ath12k_hw_hal_params_qcn9274, - .rxdma1_enable = false, + .rxdma1_enable = true, .num_rxdma_per_pdev = 1, .num_rxdma_dst_ring = 0, .rx_mac_buf_ring = false, @@ -1045,7 +1466,7 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_MESH_POINT) | BIT(NL80211_IFTYPE_AP_VLAN), - .supports_monitor = false, + .supports_monitor = true, .idle_ps = false, .download_calib = true, @@ -1084,6 +1505,92 @@ static const struct ath12k_hw_params ath12k_hw_params[] = { .iova_mask = 0, .supports_aspm = false, + + .ce_ie_addr = NULL, + .ce_remap = NULL, + .bdf_addr_offset = 0, + + .current_cc_support = false, + + .dp_primary_link_only = true, + }, + { + .name = "ipq5332 hw1.0", + .hw_rev = ATH12K_HW_IPQ5332_HW10, + .fw = { + .dir = "IPQ5332/hw1.0", + .board_size = 256 * 1024, + .cal_offset = 128 * 1024, + .m3_loader = ath12k_m3_fw_loader_remoteproc, + }, + .max_radios = 1, + .single_pdev_only = false, + .qmi_service_ins_id = ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332, + .internal_sleep_clock = false, + + .hw_ops = &qcn9274_ops, + .regs = &ipq5332_regs, + .ring_mask = &ath12k_hw_ring_mask_ipq5332, + + .host_ce_config = ath12k_host_ce_config_ipq5332, + .ce_count = 12, + .target_ce_config = ath12k_target_ce_config_wlan_ipq5332, + .target_ce_count = 12, + .svc_to_ce_map = ath12k_target_service_to_ce_map_wlan_ipq5332, + .svc_to_ce_map_len = 18, + + .hal_params = &ath12k_hw_hal_params_ipq5332, + + .rxdma1_enable = false, + .num_rxdma_per_pdev = 1, + .num_rxdma_dst_ring = 0, + .rx_mac_buf_ring = false, + .vdev_start_delay = false, + + .interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_MESH_POINT), + .supports_monitor = false, + + .idle_ps = false, + .download_calib = true, + .supports_suspend = false, + .tcl_ring_retry = true, + .reoq_lut_support = false, + .supports_shadow_regs = false, + + .num_tcl_banks = 48, + .max_tx_ring = 4, + + .wmi_init = &ath12k_wmi_init_qcn9274, + + .hal_ops = &hal_qcn9274_ops, + + .qmi_cnss_feature_bitmap = BIT(CNSS_QDSS_CFG_MISS_V01), + + .rfkill_pin = 0, + .rfkill_cfg = 0, + .rfkill_on_level = 0, + + .rddm_size = 0, + + .def_num_link = 0, + .max_mlo_peer = 256, + + .otp_board_id_register = 0, + + .supports_sta_ps = false, + + .acpi_guid = NULL, + .supports_dynamic_smps_6ghz = false, + .iova_mask = 0, + .supports_aspm = false, + + .ce_ie_addr = &ath12k_ce_ie_addr_ipq5332, + .ce_remap = &ath12k_ce_remap_ipq5332, + .bdf_addr_offset = 0xC00000, + + .dp_primary_link_only = true, }, }; diff --git a/drivers/net/wireless/ath/ath12k/hw.h b/drivers/net/wireless/ath/ath12k/hw.h index 8d52182e28ae..0fbc17649df4 100644 --- a/drivers/net/wireless/ath/ath12k/hw.h +++ b/drivers/net/wireless/ath/ath12k/hw.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_HW_H @@ -97,6 +97,7 @@ #define ATH12K_REGDB_FILE_NAME "regdb.bin" #define ATH12K_PCIE_MAX_PAYLOAD_SIZE 128 +#define ATH12K_IPQ5332_USERPD_ID 1 enum ath12k_hw_rate_cck { ATH12K_HW_RATE_CCK_LP_11M = 0, @@ -121,6 +122,7 @@ enum ath12k_hw_rate_ofdm { enum ath12k_bus { ATH12K_BUS_PCI, + ATH12K_BUS_AHB, }; #define ATH12K_EXT_IRQ_GRP_NUM_MAX 11 @@ -133,6 +135,7 @@ enum hal_encrypt_type; struct ath12k_hw_ring_mask { u8 tx[ATH12K_EXT_IRQ_GRP_NUM_MAX]; u8 rx_mon_dest[ATH12K_EXT_IRQ_GRP_NUM_MAX]; + u8 rx_mon_status[ATH12K_EXT_IRQ_GRP_NUM_MAX]; u8 rx[ATH12K_EXT_IRQ_GRP_NUM_MAX]; u8 rx_err[ATH12K_EXT_IRQ_GRP_NUM_MAX]; u8 rx_wbm_rel[ATH12K_EXT_IRQ_GRP_NUM_MAX]; @@ -146,6 +149,11 @@ struct ath12k_hw_hal_params { u32 wbm2sw_cc_enable; }; +enum ath12k_m3_fw_loaders { + ath12k_m3_fw_loader_driver, + ath12k_m3_fw_loader_remoteproc, +}; + struct ath12k_hw_params { const char *name; u16 hw_rev; @@ -154,6 +162,7 @@ struct ath12k_hw_params { const char *dir; size_t board_size; size_t cal_offset; + enum ath12k_m3_fw_loaders m3_loader; } fw; u8 max_radios; @@ -190,6 +199,7 @@ struct ath12k_hw_params { bool reoq_lut_support:1; bool supports_shadow_regs:1; bool supports_aspm:1; + bool current_cc_support:1; u32 num_tcl_banks; u32 max_tx_ring; @@ -220,6 +230,13 @@ struct ath12k_hw_params { bool supports_dynamic_smps_6ghz; u32 iova_mask; + + const struct ce_ie_addr *ce_ie_addr; + const struct ce_remap *ce_remap; + u32 bdf_addr_offset; + + /* setup REO queue, frag etc only for primary link peer */ + bool dp_primary_link_only:1; }; struct ath12k_hw_ops { @@ -293,9 +310,15 @@ struct ath12k_hw_regs { u32 hal_tcl1_ring_msi1_base_msb; u32 hal_tcl1_ring_msi1_data; u32 hal_tcl_ring_base_lsb; + u32 hal_tcl1_ring_base_lsb; + u32 hal_tcl1_ring_base_msb; + u32 hal_tcl2_ring_base_lsb; u32 hal_tcl_status_ring_base_lsb; + u32 hal_reo1_qdesc_addr; + u32 hal_reo1_qdesc_max_peerid; + u32 hal_wbm_idle_ring_base_lsb; u32 hal_wbm_idle_ring_misc_addr; u32 hal_wbm_r0_idle_list_cntl_addr; @@ -316,6 +339,11 @@ struct ath12k_hw_regs { u32 pcie_qserdes_sysclk_en_sel; u32 pcie_pcs_osc_dtct_config_base; + u32 hal_umac_ce0_src_reg_base; + u32 hal_umac_ce0_dest_reg_base; + u32 hal_umac_ce1_src_reg_base; + u32 hal_umac_ce1_dest_reg_base; + u32 hal_ppe_rel_ring_base; u32 hal_reo2_ring_base; diff --git a/drivers/net/wireless/ath/ath12k/mac.c b/drivers/net/wireless/ath/ath12k/mac.c index 2d062b5904a8..88b59f3ff87a 100644 --- a/drivers/net/wireless/ath/ath12k/mac.c +++ b/drivers/net/wireless/ath/ath12k/mac.c @@ -15,10 +15,12 @@ #include "hw.h" #include "dp_tx.h" #include "dp_rx.h" +#include "testmode.h" #include "peer.h" #include "debugfs.h" #include "hif.h" #include "wow.h" +#include "debugfs_sta.h" #define CHAN2G(_channel, _freq, _flags) { \ .band = NL80211_BAND_2GHZ, \ @@ -227,7 +229,8 @@ ath12k_phymodes[NUM_NL80211_BANDS][ATH12K_CHAN_WIDTH_NUM] = { const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default = { .rx_filter = HTT_RX_FILTER_TLV_FLAGS_MPDU_START | HTT_RX_FILTER_TLV_FLAGS_PPDU_END | - HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE, + HTT_RX_FILTER_TLV_FLAGS_PPDU_END_STATUS_DONE | + HTT_RX_FILTER_TLV_FLAGS_PPDU_START_USER_INFO, .pkt_filter_flags0 = HTT_RX_FP_MGMT_FILTER_FLAGS0, .pkt_filter_flags1 = HTT_RX_FP_MGMT_FILTER_FLAGS1, .pkt_filter_flags2 = HTT_RX_FP_CTRL_FILTER_FLASG2, @@ -337,6 +340,82 @@ static const char *ath12k_mac_phymode_str(enum wmi_phy_mode mode) return "<unknown>"; } +u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones) +{ + switch (tones) { + case 26: + return RU_26; + case 52: + return RU_52; + case 106: + return RU_106; + case 242: + return RU_242; + case 484: + return RU_484; + case 996: + return RU_996; + case (996 * 2): + return RU_2X996; + default: + return RU_26; + } +} + +enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi) +{ + switch (sgi) { + case RX_MSDU_START_SGI_0_8_US: + return NL80211_RATE_INFO_EHT_GI_0_8; + case RX_MSDU_START_SGI_1_6_US: + return NL80211_RATE_INFO_EHT_GI_1_6; + case RX_MSDU_START_SGI_3_2_US: + return NL80211_RATE_INFO_EHT_GI_3_2; + default: + return NL80211_RATE_INFO_EHT_GI_0_8; + } +} + +enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones) +{ + switch (ru_tones) { + case 26: + return NL80211_RATE_INFO_EHT_RU_ALLOC_26; + case 52: + return NL80211_RATE_INFO_EHT_RU_ALLOC_52; + case (52 + 26): + return NL80211_RATE_INFO_EHT_RU_ALLOC_52P26; + case 106: + return NL80211_RATE_INFO_EHT_RU_ALLOC_106; + case (106 + 26): + return NL80211_RATE_INFO_EHT_RU_ALLOC_106P26; + case 242: + return NL80211_RATE_INFO_EHT_RU_ALLOC_242; + case 484: + return NL80211_RATE_INFO_EHT_RU_ALLOC_484; + case (484 + 242): + return NL80211_RATE_INFO_EHT_RU_ALLOC_484P242; + case 996: + return NL80211_RATE_INFO_EHT_RU_ALLOC_996; + case (996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484; + case (996 + 484 + 242): + return NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242; + case (2 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996; + case (2 * 996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484; + case (3 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996; + case (3 * 996 + 484): + return NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484; + case (4 * 996): + return NL80211_RATE_INFO_EHT_RU_ALLOC_4x996; + default: + return NL80211_RATE_INFO_EHT_RU_ALLOC_26; + } +} + enum rate_info_bw ath12k_mac_bw_to_mac80211_bw(enum ath12k_supported_bw bw) { @@ -502,7 +581,28 @@ static int ath12k_mac_vif_link_chan(struct ieee80211_vif *vif, u8 link_id, return 0; } -static struct ieee80211_bss_conf * +static struct ath12k_link_vif * +ath12k_mac_get_tx_arvif(struct ath12k_link_vif *arvif, + struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_bss_conf *tx_bss_conf; + struct ath12k *ar = arvif->ar; + struct ath12k_vif *tx_ahvif; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + tx_bss_conf = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, + link_conf->tx_bss_conf); + if (tx_bss_conf) { + tx_ahvif = ath12k_vif_to_ahvif(tx_bss_conf->vif); + return wiphy_dereference(tx_ahvif->ah->hw->wiphy, + tx_ahvif->link[tx_bss_conf->link_id]); + } + + return NULL; +} + +struct ieee80211_bss_conf * ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif) { struct ieee80211_vif *vif = arvif->ahvif->vif; @@ -675,7 +775,10 @@ struct ath12k *ath12k_mac_get_ar_by_pdev_id(struct ath12k_base *ab, u32 pdev_id) return NULL; for (i = 0; i < ab->num_radios; i++) { - pdev = rcu_dereference(ab->pdevs_active[i]); + if (ab->fw_mode == ATH12K_FIRMWARE_MODE_FTM) + pdev = &ab->pdevs[i]; + else + pdev = rcu_dereference(ab->pdevs_active[i]); if (pdev && pdev->pdev_id == pdev_id) return (pdev->ar ? pdev->ar : NULL); @@ -725,9 +828,9 @@ static struct ath12k *ath12k_get_ar_by_ctx(struct ieee80211_hw *hw, return ath12k_mac_get_ar_by_chan(hw, ctx->def.chan); } -static struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - u8 link_id) +struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 link_id) { struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); struct ath12k_hw *ah = ath12k_hw_to_ah(hw); @@ -780,12 +883,12 @@ static bool ath12k_mac_band_match(enum nl80211_band band1, enum WMI_HOST_WLAN_BA { switch (band1) { case NL80211_BAND_2GHZ: - if (band2 & WMI_HOST_WLAN_2G_CAP) + if (band2 & WMI_HOST_WLAN_2GHZ_CAP) return true; break; case NL80211_BAND_5GHZ: case NL80211_BAND_6GHZ: - if (band2 & WMI_HOST_WLAN_5G_CAP) + if (band2 & WMI_HOST_WLAN_5GHZ_CAP) return true; break; default: @@ -886,7 +989,7 @@ static int ath12k_mac_txpower_recalc(struct ath12k *ar) ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower to set in hw %d\n", txpower / 2); - if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) && + if ((pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) && ar->txpower_limit_2g != txpower) { param = WMI_PDEV_PARAM_TXPOWER_LIMIT2G; ret = ath12k_wmi_pdev_set_param(ar, param, @@ -896,7 +999,7 @@ static int ath12k_mac_txpower_recalc(struct ath12k *ar) ar->txpower_limit_2g = txpower; } - if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) && + if ((pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP) && ar->txpower_limit_5g != txpower) { param = WMI_PDEV_PARAM_TXPOWER_LIMIT5G; ret = ath12k_wmi_pdev_set_param(ar, param, @@ -1151,61 +1254,6 @@ static int ath12k_mac_monitor_vdev_stop(struct ath12k *ar) return ret; } -static int ath12k_mac_monitor_vdev_create(struct ath12k *ar) -{ - struct ath12k_pdev *pdev = ar->pdev; - struct ath12k_wmi_vdev_create_arg arg = {}; - int bit, ret; - u8 tmp_addr[6]; - - lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - - if (ar->monitor_vdev_created) - return 0; - - if (ar->ab->free_vdev_map == 0) { - ath12k_warn(ar->ab, "failed to find free vdev id for monitor vdev\n"); - return -ENOMEM; - } - - bit = __ffs64(ar->ab->free_vdev_map); - - ar->monitor_vdev_id = bit; - - arg.if_id = ar->monitor_vdev_id; - arg.type = WMI_VDEV_TYPE_MONITOR; - arg.subtype = WMI_VDEV_SUBTYPE_NONE; - arg.pdev_id = pdev->pdev_id; - arg.if_stats_id = ATH12K_INVAL_VDEV_STATS_ID; - - if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { - arg.chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains; - arg.chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains; - } - - if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { - arg.chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains; - arg.chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains; - } - - ret = ath12k_wmi_vdev_create(ar, tmp_addr, &arg); - if (ret) { - ath12k_warn(ar->ab, "failed to request monitor vdev %i creation: %d\n", - ar->monitor_vdev_id, ret); - ar->monitor_vdev_id = -1; - return ret; - } - - ar->allocated_vdev_map |= 1LL << ar->monitor_vdev_id; - ar->ab->free_vdev_map &= ~(1LL << ar->monitor_vdev_id); - ar->num_created_vdevs++; - ar->monitor_vdev_created = true; - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor vdev %d created\n", - ar->monitor_vdev_id); - - return 0; -} - static int ath12k_mac_monitor_vdev_delete(struct ath12k *ar) { int ret; @@ -1242,19 +1290,9 @@ static int ath12k_mac_monitor_vdev_delete(struct ath12k *ar) return ret; } -static void -ath12k_mac_get_any_chandef_iter(struct ieee80211_hw *hw, - struct ieee80211_chanctx_conf *conf, - void *data) -{ - struct cfg80211_chan_def **def = data; - - *def = &conf->def; -} - static int ath12k_mac_monitor_start(struct ath12k *ar) { - struct cfg80211_chan_def *chandef = NULL; + struct ath12k_mac_get_any_chanctx_conf_arg arg; int ret; lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); @@ -1262,25 +1300,33 @@ static int ath12k_mac_monitor_start(struct ath12k *ar) if (ar->monitor_started) return 0; + arg.ar = ar; + arg.chanctx_conf = NULL; ieee80211_iter_chan_contexts_atomic(ath12k_ar_to_hw(ar), - ath12k_mac_get_any_chandef_iter, - &chandef); - if (!chandef) + ath12k_mac_get_any_chanctx_conf_iter, + &arg); + if (!arg.chanctx_conf) return 0; - ret = ath12k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id, chandef); + ret = ath12k_mac_monitor_vdev_start(ar, ar->monitor_vdev_id, + &arg.chanctx_conf->def); if (ret) { ath12k_warn(ar->ab, "failed to start monitor vdev: %d\n", ret); - ath12k_mac_monitor_vdev_delete(ar); + return ret; + } + + ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, false); + if (ret) { + ath12k_warn(ar->ab, "fail to set monitor filter: %d\n", ret); return ret; } ar->monitor_started = true; ar->num_started_vdevs++; - ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, false); - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor started ret %d\n", ret); - return ret; + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac monitor started\n"); + + return 0; } static int ath12k_mac_monitor_stop(struct ath12k *ar) @@ -1346,58 +1392,9 @@ err: return ret; } -static int ath12k_mac_config(struct ath12k *ar, u32 changed) -{ - struct ieee80211_hw *hw = ath12k_ar_to_hw(ar); - struct ieee80211_conf *conf = &hw->conf; - int ret = 0; - - lockdep_assert_wiphy(hw->wiphy); - - if (changed & IEEE80211_CONF_CHANGE_MONITOR) { - ar->monitor_conf_enabled = conf->flags & IEEE80211_CONF_MONITOR; - if (ar->monitor_conf_enabled) { - if (ar->monitor_vdev_created) - return ret; - ret = ath12k_mac_monitor_vdev_create(ar); - if (ret) - return ret; - ret = ath12k_mac_monitor_start(ar); - if (ret) - goto err_mon_del; - } else { - if (!ar->monitor_vdev_created) - return ret; - ret = ath12k_mac_monitor_stop(ar); - if (ret) - return ret; - ath12k_mac_monitor_vdev_delete(ar); - } - } - - return ret; - -err_mon_del: - ath12k_mac_monitor_vdev_delete(ar); - return ret; -} - static int ath12k_mac_op_config(struct ieee80211_hw *hw, u32 changed) { - struct ath12k_hw *ah = ath12k_hw_to_ah(hw); - struct ath12k *ar; - int ret; - - lockdep_assert_wiphy(hw->wiphy); - - ar = ath12k_ah_to_ar(ah, 0); - - ret = ath12k_mac_config(ar, changed); - if (ret) - ath12k_warn(ar->ab, "failed to update config pdev idx %d: %d\n", - ar->pdev_idx, ret); - - return ret; + return 0; } static int ath12k_mac_setup_bcn_p2p_ie(struct ath12k_link_vif *arvif, @@ -1549,30 +1546,18 @@ static void ath12k_mac_set_arvif_ies(struct ath12k_link_vif *arvif, struct sk_bu } } -static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) +static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif, + struct ath12k_link_vif *tx_arvif, + u8 bssid_index) { - struct ath12k_vif *ahvif = arvif->ahvif; - struct ieee80211_bss_conf *bss_conf; struct ath12k_wmi_bcn_tmpl_ema_arg ema_args; struct ieee80211_ema_beacons *beacons; - struct ath12k_link_vif *tx_arvif; bool nontx_profile_found = false; - struct ath12k_vif *tx_ahvif; int ret = 0; u8 i; - bss_conf = ath12k_mac_get_link_bss_conf(arvif); - if (!bss_conf) { - ath12k_warn(arvif->ar->ab, - "failed to get link bss conf to update bcn tmpl for vif %pM link %u\n", - ahvif->vif->addr, arvif->link_id); - return -ENOLINK; - } - - tx_ahvif = ath12k_vif_to_ahvif(ahvif->vif->mbssid_tx_vif); - tx_arvif = &tx_ahvif->deflink; beacons = ieee80211_beacon_get_template_ema_list(ath12k_ar_to_hw(tx_arvif->ar), - tx_ahvif->vif, + tx_arvif->ahvif->vif, tx_arvif->link_id); if (!beacons || !beacons->cnt) { ath12k_warn(arvif->ar->ab, @@ -1586,13 +1571,12 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) for (i = 0; i < beacons->cnt; i++) { if (tx_arvif != arvif && !nontx_profile_found) ath12k_mac_set_arvif_ies(arvif, beacons->bcn[i].skb, - bss_conf->bssid_index, + bssid_index, &nontx_profile_found); ema_args.bcn_cnt = beacons->cnt; ema_args.bcn_index = i; - ret = ath12k_wmi_bcn_tmpl(tx_arvif->ar, tx_arvif->vdev_id, - &beacons->bcn[i].offs, + ret = ath12k_wmi_bcn_tmpl(tx_arvif, &beacons->bcn[i].offs, beacons->bcn[i].skb, &ema_args); if (ret) { ath12k_warn(tx_arvif->ar->ab, @@ -1605,7 +1589,7 @@ static int ath12k_mac_setup_bcn_tmpl_ema(struct ath12k_link_vif *arvif) if (tx_arvif != arvif && !nontx_profile_found) ath12k_warn(arvif->ar->ab, "nontransmitted bssid index %u not found in beacon template\n", - bss_conf->bssid_index); + bssid_index); ieee80211_beacon_free_ema_list(beacons); return ret; @@ -1616,11 +1600,10 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) struct ath12k_vif *ahvif = arvif->ahvif; struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); struct ieee80211_bss_conf *link_conf; - struct ath12k_link_vif *tx_arvif = arvif; + struct ath12k_link_vif *tx_arvif; struct ath12k *ar = arvif->ar; struct ath12k_base *ab = ar->ab; struct ieee80211_mutable_offsets offs = {}; - struct ath12k_vif *tx_ahvif = ahvif; bool nontx_profile_found = false; struct sk_buff *bcn; int ret; @@ -1635,17 +1618,20 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) return -ENOLINK; } - if (vif->mbssid_tx_vif) { - tx_ahvif = ath12k_vif_to_ahvif(vif->mbssid_tx_vif); - tx_arvif = &tx_ahvif->deflink; + tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); + if (tx_arvif) { if (tx_arvif != arvif && arvif->is_up) return 0; if (link_conf->ema_ap) - return ath12k_mac_setup_bcn_tmpl_ema(arvif); + return ath12k_mac_setup_bcn_tmpl_ema(arvif, tx_arvif, + link_conf->bssid_index); + } else { + tx_arvif = arvif; } - bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar), tx_ahvif->vif, + bcn = ieee80211_beacon_get_template(ath12k_ar_to_hw(tx_arvif->ar), + tx_arvif->ahvif->vif, &offs, tx_arvif->link_id); if (!bcn) { ath12k_warn(ab, "failed to get beacon template from mac80211\n"); @@ -1686,7 +1672,7 @@ static int ath12k_mac_setup_bcn_tmpl(struct ath12k_link_vif *arvif) } } - ret = ath12k_wmi_bcn_tmpl(ar, arvif->vdev_id, &offs, bcn, NULL); + ret = ath12k_wmi_bcn_tmpl(arvif, &offs, bcn, NULL); if (ret) ath12k_warn(ab, "failed to submit beacon template command: %d\n", @@ -1702,6 +1688,8 @@ static void ath12k_control_beaconing(struct ath12k_link_vif *arvif, { struct ath12k_wmi_vdev_up_params params = {}; struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_bss_conf *link_conf; + struct ath12k_link_vif *tx_arvif; struct ath12k *ar = arvif->ar; int ret; @@ -1732,11 +1720,17 @@ static void ath12k_control_beaconing(struct ath12k_link_vif *arvif, params.vdev_id = arvif->vdev_id; params.aid = ahvif->aid; params.bssid = arvif->bssid; - if (ahvif->vif->mbssid_tx_vif) { - struct ath12k_vif *tx_ahvif = - ath12k_vif_to_ahvif(ahvif->vif->mbssid_tx_vif); - struct ath12k_link_vif *tx_arvif = &tx_ahvif->deflink; + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, + "unable to access bss link conf for link %u required to retrieve transmitting link conf\n", + arvif->link_id); + return; + } + + tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); + if (tx_arvif) { params.tx_bssid = tx_arvif->bssid; params.nontx_profile_idx = info->bssid_index; params.nontx_profile_cnt = 1 << info->bssid_indicator; @@ -2945,6 +2939,7 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, const struct ieee80211_sta_eht_cap *eht_cap; const struct ieee80211_sta_he_cap *he_cap; struct ieee80211_link_sta *link_sta; + struct ieee80211_bss_conf *link_conf; u32 *rx_mcs, *tx_mcs; lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); @@ -2956,6 +2951,12 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, return; } + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access link_conf in peer assoc eht set\n"); + return; + } + eht_cap = &link_sta->eht_cap; he_cap = &link_sta->he_cap; if (!he_cap->has_he || !eht_cap->has_eht) @@ -3027,6 +3028,7 @@ static void ath12k_peer_assoc_h_eht(struct ath12k *ar, } arg->punct_bitmap = ~arvif->punct_bitmap; + arg->eht_disable_mcs15 = link_conf->eht_disable_mcs15; } static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta, @@ -3057,6 +3059,7 @@ static void ath12k_peer_assoc_h_mlo(struct ath12k_link_sta *arsta, ml->ml_peer_id = ahsta->ml_peer_id; ml->ieee_link_id = arsta->link_id; ml->num_partner_links = 0; + ml->eml_cap = sta->eml_cap; links = ahsta->links_map; rcu_read_lock(); @@ -3116,6 +3119,7 @@ static void ath12k_peer_assoc_prepare(struct ath12k *ar, ath12k_peer_assoc_h_smps(arsta, arg); ath12k_peer_assoc_h_mlo(arsta, arg); + arsta->peer_nss = arg->peer_nss; /* TODO: amsdu_disable req? */ } @@ -3138,6 +3142,37 @@ static int ath12k_setup_peer_smps(struct ath12k *ar, struct ath12k_link_vif *arv ath12k_smps_map[smps]); } +static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, + struct ieee80211_link_sta *link_sta) +{ + u32 bw; + + switch (link_sta->bandwidth) { + case IEEE80211_STA_RX_BW_20: + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + case IEEE80211_STA_RX_BW_40: + bw = WMI_PEER_CHWIDTH_40MHZ; + break; + case IEEE80211_STA_RX_BW_80: + bw = WMI_PEER_CHWIDTH_80MHZ; + break; + case IEEE80211_STA_RX_BW_160: + bw = WMI_PEER_CHWIDTH_160MHZ; + break; + case IEEE80211_STA_RX_BW_320: + bw = WMI_PEER_CHWIDTH_320MHZ; + break; + default: + ath12k_warn(ar->ab, "Invalid bandwidth %d for link station %pM\n", + link_sta->bandwidth, link_sta->addr); + bw = WMI_PEER_CHWIDTH_20MHZ; + break; + } + + return bw; +} + static void ath12k_bss_assoc(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ieee80211_bss_conf *bss_conf) @@ -3263,6 +3298,11 @@ static void ath12k_bss_assoc(struct ath12k *ar, if (ret) ath12k_warn(ar->ab, "failed to set vdev %i OBSS PD parameters: %d\n", arvif->vdev_id, ret); + + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) + ath12k_mac_11d_scan_stop_all(ar->ab); } static void ath12k_bss_disassoc(struct ath12k *ar, @@ -3336,7 +3376,10 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar, } sband = hw->wiphy->bands[def->chan->band]; - basic_rate_idx = ffs(bss_conf->basic_rates) - 1; + if (bss_conf->basic_rates) + basic_rate_idx = __ffs(bss_conf->basic_rates); + else + basic_rate_idx = 0; bitrate = sband->bitrates[basic_rate_idx].bitrate; hw_rate_code = ath12k_mac_get_rate_hw_value(bitrate); @@ -3358,12 +3401,181 @@ static void ath12k_recalculate_mgmt_rate(struct ath12k *ar, ath12k_warn(ar->ab, "failed to set beacon tx rate %d\n", ret); } +static void ath12k_mac_init_arvif(struct ath12k_vif *ahvif, + struct ath12k_link_vif *arvif, int link_id) +{ + struct ath12k_hw *ah = ahvif->ah; + u8 _link_id; + int i; + + lockdep_assert_wiphy(ah->hw->wiphy); + + if (WARN_ON(!arvif)) + return; + + if (WARN_ON(link_id >= ATH12K_NUM_MAX_LINKS)) + return; + + if (link_id < 0) + _link_id = 0; + else + _link_id = link_id; + + arvif->ahvif = ahvif; + arvif->link_id = _link_id; + + /* Protects the datapath stats update on a per link basis */ + spin_lock_init(&arvif->link_stats_lock); + + INIT_LIST_HEAD(&arvif->list); + INIT_DELAYED_WORK(&arvif->connection_loss_work, + ath12k_mac_vif_sta_connection_loss_work); + + for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { + arvif->bitrate_mask.control[i].legacy = 0xffffffff; + memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].ht_mcs)); + memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, + sizeof(arvif->bitrate_mask.control[i].vht_mcs)); + } + + /* Handle MLO related assignments */ + if (link_id >= 0) { + rcu_assign_pointer(ahvif->link[arvif->link_id], arvif); + ahvif->links_map |= BIT(_link_id); + } + + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac init link arvif (link_id %d%s) for vif %pM. links_map 0x%x", + _link_id, (link_id < 0) ? " deflink" : "", ahvif->vif->addr, + ahvif->links_map); +} + +static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw, + struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_hw *ah = hw->priv; + struct ath12k *ar = arvif->ar; + int ret; + + lockdep_assert_wiphy(ah->hw->wiphy); + + cancel_delayed_work_sync(&arvif->connection_loss_work); + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)", + arvif->vdev_id, arvif->link_id); + + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) + ath12k_mac_11d_scan_stop(ar); + + if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) { + ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid); + if (ret) + ath12k_warn(ar->ab, "failed to submit AP self-peer removal on vdev %d link id %d: %d", + arvif->vdev_id, arvif->link_id, ret); + } + ath12k_mac_vdev_delete(ar, arvif); +} + +static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, + struct ieee80211_vif *vif, + u8 link_id) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_link_vif *arvif; + + lockdep_assert_wiphy(ah->hw->wiphy); + + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + if (arvif) + return arvif; + + /* If this is the first link arvif being created for an ML VIF + * use the preallocated deflink memory except for scan arvifs + */ + if (!ahvif->links_map && link_id != ATH12K_DEFAULT_SCAN_LINK) { + arvif = &ahvif->deflink; + + if (vif->type == NL80211_IFTYPE_STATION) + arvif->is_sta_assoc_link = true; + } else { + arvif = kzalloc(sizeof(*arvif), GFP_KERNEL); + if (!arvif) + return NULL; + } + + ath12k_mac_init_arvif(ahvif, arvif, link_id); + + return arvif; +} + +static void ath12k_mac_unassign_link_vif(struct ath12k_link_vif *arvif) +{ + struct ath12k_vif *ahvif = arvif->ahvif; + struct ath12k_hw *ah = ahvif->ah; + + lockdep_assert_wiphy(ah->hw->wiphy); + + rcu_assign_pointer(ahvif->link[arvif->link_id], NULL); + synchronize_rcu(); + ahvif->links_map &= ~BIT(arvif->link_id); + + if (arvif != &ahvif->deflink) + kfree(arvif); + else + memset(arvif, 0, sizeof(*arvif)); +} + static int ath12k_mac_op_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, u16 old_links, u16 new_links, struct ieee80211_bss_conf *ol[IEEE80211_MLD_MAX_NUM_LINKS]) { + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + unsigned long to_remove = old_links & ~new_links; + unsigned long to_add = ~old_links & new_links; + struct ath12k_hw *ah = ath12k_hw_to_ah(hw); + struct ath12k_link_vif *arvif; + u8 link_id; + + lockdep_assert_wiphy(hw->wiphy); + + ath12k_generic_dbg(ATH12K_DBG_MAC, + "mac vif link changed for MLD %pM old_links 0x%x new_links 0x%x\n", + vif->addr, old_links, new_links); + + for_each_set_bit(link_id, &to_add, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + /* mac80211 wants to add link but driver already has the + * link. This should not happen ideally. + */ + if (WARN_ON(arvif)) + return -EINVAL; + + arvif = ath12k_mac_assign_link_vif(ah, vif, link_id); + if (WARN_ON(!arvif)) + return -EINVAL; + } + + for_each_set_bit(link_id, &to_remove, IEEE80211_MLD_MAX_NUM_LINKS) { + arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); + if (WARN_ON(!arvif)) + return -EINVAL; + + if (!arvif->is_created) + continue; + + if (WARN_ON(!arvif->ar)) + return -EINVAL; + + ath12k_mac_remove_link_interface(hw, arvif); + ath12k_mac_unassign_link_vif(arvif); + } + return 0; } @@ -3422,6 +3634,8 @@ static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw, unsigned long links = ahvif->links_map; struct ieee80211_bss_conf *info; struct ath12k_link_vif *arvif; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; struct ath12k *ar; u8 link_id; @@ -3434,6 +3648,35 @@ static void ath12k_mac_op_vif_cfg_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_ASSOC) { + if (vif->cfg.assoc) { + /* only in station mode we can get here, so it's safe + * to use ap_addr + */ + rcu_read_lock(); + sta = ieee80211_find_sta(vif, vif->cfg.ap_addr); + if (!sta) { + rcu_read_unlock(); + WARN_ONCE(1, "failed to find sta with addr %pM\n", + vif->cfg.ap_addr); + return; + } + + ahsta = ath12k_sta_to_ahsta(sta); + arvif = wiphy_dereference(hw->wiphy, + ahvif->link[ahsta->assoc_link_id]); + rcu_read_unlock(); + + ar = arvif->ar; + /* there is no reason for which an assoc link's + * bss info does not exist + */ + info = ath12k_mac_get_link_bss_conf(arvif); + ath12k_bss_assoc(ar, arvif, info); + + /* exclude assoc link as it is done above */ + links &= ~BIT(ahsta->assoc_link_id); + } + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { arvif = wiphy_dereference(hw->wiphy, ahvif->link[link_id]); if (!arvif || !arvif->ar) @@ -3509,6 +3752,18 @@ static void ath12k_mac_vif_setup_ps(struct ath12k_link_vif *arvif) psmode, arvif->vdev_id, ret); } +static bool ath12k_mac_supports_station_tpc(struct ath12k *ar, + struct ath12k_vif *ahvif, + const struct cfg80211_chan_def *chandef) +{ + return ath12k_wmi_supports_6ghz_cc_ext(ar) && + test_bit(WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT, ar->ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE && + chandef->chan && + chandef->chan->band == NL80211_BAND_6GHZ; +} + static void ath12k_mac_bss_info_changed(struct ath12k *ar, struct ath12k_link_vif *arvif, struct ieee80211_bss_conf *info, @@ -3703,12 +3958,16 @@ static void ath12k_mac_bss_info_changed(struct ath12k *ar, band = def.chan->band; mcast_rate = info->mcast_rate[band]; - if (mcast_rate > 0) + if (mcast_rate > 0) { rateidx = mcast_rate - 1; - else - rateidx = ffs(info->basic_rates) - 1; + } else { + if (info->basic_rates) + rateidx = __ffs(info->basic_rates); + else + rateidx = 0; + } - if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) + if (ar->pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP) rateidx += ATH12K_MAC_FIRST_OFDM_RATE_IDX; bitrate = ath12k_legacy_rates[rateidx].bitrate; @@ -3862,109 +4121,6 @@ static void ath12k_mac_op_link_info_changed(struct ieee80211_hw *hw, ath12k_mac_bss_info_changed(ar, arvif, info, changed); } -static struct ath12k_link_vif *ath12k_mac_assign_link_vif(struct ath12k_hw *ah, - struct ieee80211_vif *vif, - u8 link_id) -{ - struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); - struct ath12k_link_vif *arvif; - int i; - - lockdep_assert_wiphy(ah->hw->wiphy); - - arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); - if (arvif) - return arvif; - - if (!vif->valid_links) { - /* Use deflink for Non-ML VIFs and mark the link id as 0 - */ - link_id = 0; - arvif = &ahvif->deflink; - } else { - /* If this is the first link arvif being created for an ML VIF - * use the preallocated deflink memory except for scan arvifs - */ - if (!ahvif->links_map && link_id != ATH12K_DEFAULT_SCAN_LINK) { - arvif = &ahvif->deflink; - } else { - arvif = (struct ath12k_link_vif *) - kzalloc(sizeof(struct ath12k_link_vif), GFP_KERNEL); - if (!arvif) - return NULL; - } - } - - arvif->ahvif = ahvif; - arvif->link_id = link_id; - ahvif->links_map |= BIT(link_id); - - INIT_LIST_HEAD(&arvif->list); - INIT_DELAYED_WORK(&arvif->connection_loss_work, - ath12k_mac_vif_sta_connection_loss_work); - - for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { - arvif->bitrate_mask.control[i].legacy = 0xffffffff; - memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].ht_mcs)); - memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].vht_mcs)); - } - - /* Allocate Default Queue now and reassign during actual vdev create */ - vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE; - for (i = 0; i < ARRAY_SIZE(vif->hw_queue); i++) - vif->hw_queue[i] = ATH12K_HW_DEFAULT_QUEUE; - - vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; - - rcu_assign_pointer(ahvif->link[arvif->link_id], arvif); - ahvif->links_map |= BIT(link_id); - synchronize_rcu(); - return arvif; -} - -static void ath12k_mac_unassign_link_vif(struct ath12k_link_vif *arvif) -{ - struct ath12k_vif *ahvif = arvif->ahvif; - struct ath12k_hw *ah = ahvif->ah; - - lockdep_assert_wiphy(ah->hw->wiphy); - - rcu_assign_pointer(ahvif->link[arvif->link_id], NULL); - synchronize_rcu(); - ahvif->links_map &= ~BIT(arvif->link_id); - - if (arvif != &ahvif->deflink) - kfree(arvif); - else - memset(arvif, 0, sizeof(*arvif)); -} - -static void ath12k_mac_remove_link_interface(struct ieee80211_hw *hw, - struct ath12k_link_vif *arvif) -{ - struct ath12k_vif *ahvif = arvif->ahvif; - struct ath12k_hw *ah = hw->priv; - struct ath12k *ar = arvif->ar; - int ret; - - lockdep_assert_wiphy(ah->hw->wiphy); - - cancel_delayed_work_sync(&arvif->connection_loss_work); - - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac remove link interface (vdev %d link id %d)", - arvif->vdev_id, arvif->link_id); - - if (ahvif->vdev_type == WMI_VDEV_TYPE_AP) { - ret = ath12k_peer_delete(ar, arvif->vdev_id, arvif->bssid); - if (ret) - ath12k_warn(ar->ab, "failed to submit AP self-peer removal on vdev %d link id %d: %d", - arvif->vdev_id, arvif->link_id, ret); - } - ath12k_mac_vdev_delete(ar, arvif); -} - static struct ath12k* ath12k_mac_select_scan_device(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -3985,9 +4141,9 @@ ath12k_mac_select_scan_device(struct ieee80211_hw *hw, * split the hw request and perform multiple scans */ - if (center_freq < ATH12K_MIN_5G_FREQ) + if (center_freq < ATH12K_MIN_5GHZ_FREQ) band = NL80211_BAND_2GHZ; - else if (center_freq < ATH12K_MIN_6G_FREQ) + else if (center_freq < ATH12K_MIN_6GHZ_FREQ) band = NL80211_BAND_5GHZ; else band = NL80211_BAND_6GHZ; @@ -4017,7 +4173,7 @@ void __ath12k_mac_scan_finish(struct ath12k *ar) fallthrough; case ATH12K_SCAN_STARTING: cancel_delayed_work(&ar->scan.timeout); - complete(&ar->scan.completed); + complete_all(&ar->scan.completed); wiphy_work_queue(ar->ah->hw->wiphy, &ar->scan.vdev_clean_wk); break; } @@ -4199,6 +4355,145 @@ static int ath12k_start_scan(struct ath12k *ar, return 0; } +int ath12k_mac_get_fw_stats(struct ath12k *ar, + struct ath12k_fw_stats_req_params *param) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_hw *ah = ath12k_ar_to_ah(ar); + unsigned long timeout, time_left; + int ret; + + guard(mutex)(&ah->hw_mutex); + + if (ah->state != ATH12K_HW_STATE_ON) + return -ENETDOWN; + + /* FW stats can get split when exceeding the stats data buffer limit. + * In that case, since there is no end marking for the back-to-back + * received 'update stats' event, we keep a 3 seconds timeout in case, + * fw_stats_done is not marked yet + */ + timeout = jiffies + msecs_to_jiffies(3 * 1000); + ath12k_fw_stats_reset(ar); + + reinit_completion(&ar->fw_stats_complete); + + ret = ath12k_wmi_send_stats_request_cmd(ar, param->stats_id, + param->vdev_id, param->pdev_id); + + if (ret) { + ath12k_warn(ab, "failed to request fw stats: %d\n", ret); + return ret; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "get fw stat pdev id %d vdev id %d stats id 0x%x\n", + param->pdev_id, param->vdev_id, param->stats_id); + + time_left = wait_for_completion_timeout(&ar->fw_stats_complete, 1 * HZ); + + if (!time_left) { + ath12k_warn(ab, "time out while waiting for get fw stats\n"); + return -ETIMEDOUT; + } + + /* Firmware sends WMI_UPDATE_STATS_EVENTID back-to-back + * when stats data buffer limit is reached. fw_stats_complete + * is completed once host receives first event from firmware, but + * still end might not be marked in the TLV. + * Below loop is to confirm that firmware completed sending all the event + * and fw_stats_done is marked true when end is marked in the TLV. + */ + for (;;) { + if (time_after(jiffies, timeout)) + break; + spin_lock_bh(&ar->data_lock); + if (ar->fw_stats.fw_stats_done) { + spin_unlock_bh(&ar->data_lock); + break; + } + spin_unlock_bh(&ar->data_lock); + } + return 0; +} + +static int ath12k_mac_op_get_txpower(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, + int *dbm) +{ + struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_fw_stats_req_params params = {}; + struct ath12k_fw_stats_pdev *pdev; + struct ath12k_hw *ah = hw->priv; + struct ath12k_link_vif *arvif; + struct ath12k_base *ab; + struct ath12k *ar; + int ret; + + /* Final Tx power is minimum of Target Power, CTL power, Regulatory + * Power, PSD EIRP Power. We just know the Regulatory power from the + * regulatory rules obtained. FW knows all these power and sets the min + * of these. Hence, we request the FW pdev stats in which FW reports + * the minimum of all vdev's channel Tx power. + */ + lockdep_assert_wiphy(hw->wiphy); + + arvif = wiphy_dereference(ah->hw->wiphy, ahvif->link[link_id]); + if (!arvif || !arvif->ar) + return -EINVAL; + + ar = arvif->ar; + ab = ar->ab; + if (ah->state != ATH12K_HW_STATE_ON) + goto err_fallback; + + if (test_bit(ATH12K_FLAG_CAC_RUNNING, &ar->dev_flags)) + return -EAGAIN; + + /* Limit the requests to Firmware for fetching the tx power */ + if (ar->chan_tx_pwr != ATH12K_PDEV_TX_POWER_INVALID && + time_before(jiffies, + msecs_to_jiffies(ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS) + + ar->last_tx_power_update)) + goto send_tx_power; + + params.pdev_id = ar->pdev->pdev_id; + params.vdev_id = arvif->vdev_id; + params.stats_id = WMI_REQUEST_PDEV_STAT; + ret = ath12k_mac_get_fw_stats(ar, ¶ms); + if (ret) { + ath12k_warn(ab, "failed to request fw pdev stats: %d\n", ret); + goto err_fallback; + } + + spin_lock_bh(&ar->data_lock); + pdev = list_first_entry_or_null(&ar->fw_stats.pdevs, + struct ath12k_fw_stats_pdev, list); + if (!pdev) { + spin_unlock_bh(&ar->data_lock); + goto err_fallback; + } + + /* tx power reported by firmware is in units of 0.5 dBm */ + ar->chan_tx_pwr = pdev->chan_tx_power / 2; + spin_unlock_bh(&ar->data_lock); + ar->last_tx_power_update = jiffies; + +send_tx_power: + *dbm = ar->chan_tx_pwr; + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower fetched from firmware %d dBm\n", + *dbm); + return 0; + +err_fallback: + /* We didn't get txpower from FW. Hence, relying on vif->bss_conf.txpower */ + *dbm = vif->bss_conf.txpower; + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "txpower from firmware NaN, reported %d dBm\n", + *dbm); + return 0; +} + static u8 ath12k_mac_find_link_id_by_ar(struct ath12k_vif *ahvif, struct ath12k *ar) { @@ -4252,7 +4547,7 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, return -EINVAL; /* check if any of the links of ML VIF is already started on - * radio(ar) correpsondig to given scan frequency and use it, + * radio(ar) corresponding to given scan frequency and use it, * if not use scan link (link 15) for scan purpose. */ link_id = ath12k_mac_find_link_id_by_ar(ahvif, ar); @@ -4361,10 +4656,16 @@ static int ath12k_mac_op_hw_scan(struct ieee80211_hw *hw, ret = ath12k_start_scan(ar, arg); if (ret) { - ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret); + if (ret == -EBUSY) + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "scan engine is busy 11d state %d\n", ar->state_11d); + else + ath12k_warn(ar->ab, "failed to start hw scan: %d\n", ret); + spin_lock_bh(&ar->data_lock); ar->scan.state = ATH12K_SCAN_IDLE; spin_unlock_bh(&ar->data_lock); + goto exit; } ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac scan started"); @@ -4388,6 +4689,11 @@ exit: kfree(arg); } + if (ar->state_11d == ATH12K_11D_PREPARING && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) + ath12k_mac_11d_scan_start(ar, arvif->vdev_id); + return ret; } @@ -4428,7 +4734,6 @@ static int ath12k_install_key(struct ath12k_link_vif *arvif, .macaddr = macaddr, }; struct ath12k_vif *ahvif = arvif->ahvif; - struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); @@ -4447,8 +4752,8 @@ static int ath12k_install_key(struct ath12k_link_vif *arvif, switch (key->cipher) { case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_CCMP_256: arg.key_cipher = WMI_CIPHER_AES_CCM; - /* TODO: Re-check if flag is valid */ key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; case WLAN_CIPHER_SUITE_TKIP: @@ -4456,12 +4761,10 @@ static int ath12k_install_key(struct ath12k_link_vif *arvif, arg.key_txmic_len = 8; arg.key_rxmic_len = 8; break; - case WLAN_CIPHER_SUITE_CCMP_256: - arg.key_cipher = WMI_CIPHER_AES_CCM; - break; case WLAN_CIPHER_SUITE_GCMP: case WLAN_CIPHER_SUITE_GCMP_256: arg.key_cipher = WMI_CIPHER_AES_GCM; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_IV_MGMT; break; default: ath12k_warn(ar->ab, "cipher %d is not supported\n", key->cipher); @@ -4481,7 +4784,7 @@ install: if (!wait_for_completion_timeout(&ar->install_key_done, 1 * HZ)) return -ETIMEDOUT; - if (ether_addr_equal(macaddr, vif->addr)) + if (ether_addr_equal(macaddr, arvif->bssid)) ahvif->key_cipher = key->cipher; return ar->install_key_status ? -EINVAL : 0; @@ -4534,9 +4837,6 @@ static int ath12k_mac_set_key(struct ath12k *ar, enum set_key_cmd cmd, struct ath12k_link_sta *arsta, struct ieee80211_key_conf *key) { - struct ath12k_vif *ahvif = arvif->ahvif; - struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); - struct ieee80211_bss_conf *link_conf; struct ieee80211_sta *sta = NULL; struct ath12k_base *ab = ar->ab; struct ath12k_peer *peer; @@ -4553,19 +4853,10 @@ static int ath12k_mac_set_key(struct ath12k *ar, enum set_key_cmd cmd, if (test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ab->dev_flags)) return 1; - link_conf = ath12k_mac_get_link_bss_conf(arvif); - if (!link_conf) { - ath12k_warn(ab, "unable to access bss link conf in set key for vif %pM link %u\n", - vif->addr, arvif->link_id); - return -ENOLINK; - } - if (sta) peer_addr = arsta->addr; - else if (ahvif->vdev_type == WMI_VDEV_TYPE_STA) - peer_addr = link_conf->bssid; else - peer_addr = link_conf->addr; + peer_addr = arvif->bssid; key->hw_key_idx = key->keyidx; @@ -4909,6 +5200,11 @@ static int ath12k_mac_station_assoc(struct ath12k *ar, return -EINVAL; } + spin_lock_bh(&ar->data_lock); + arsta->bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, link_sta); + arsta->bw_prev = link_sta->bandwidth; + spin_unlock_bh(&ar->data_lock); + if (link_sta->vht_cap.vht_supported && num_vht_rates == 1) { ret = ath12k_mac_set_peer_vht_fixed_rate(arvif, arsta, mask, band); @@ -5354,10 +5650,13 @@ static int ath12k_mac_station_add(struct ath12k *ar, ar->max_num_stations); goto exit; } - arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); - if (!arsta->rx_stats) { - ret = -ENOMEM; - goto dec_num_station; + + if (ath12k_debugfs_is_extd_rx_stats_enabled(ar) && !arsta->rx_stats) { + arsta->rx_stats = kzalloc(sizeof(*arsta->rx_stats), GFP_KERNEL); + if (!arsta->rx_stats) { + ret = -ENOMEM; + goto dec_num_station; + } } peer_param.vdev_id = arvif->vdev_id; @@ -5403,6 +5702,7 @@ static int ath12k_mac_station_add(struct ath12k *ar, } } + ewma_avg_rssi_init(&arsta->avg_rssi); return 0; free_peer: @@ -5415,37 +5715,6 @@ exit: return ret; } -static u32 ath12k_mac_ieee80211_sta_bw_to_wmi(struct ath12k *ar, - struct ieee80211_sta *sta) -{ - u32 bw = WMI_PEER_CHWIDTH_20MHZ; - - switch (sta->deflink.bandwidth) { - case IEEE80211_STA_RX_BW_20: - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - case IEEE80211_STA_RX_BW_40: - bw = WMI_PEER_CHWIDTH_40MHZ; - break; - case IEEE80211_STA_RX_BW_80: - bw = WMI_PEER_CHWIDTH_80MHZ; - break; - case IEEE80211_STA_RX_BW_160: - bw = WMI_PEER_CHWIDTH_160MHZ; - break; - case IEEE80211_STA_RX_BW_320: - bw = WMI_PEER_CHWIDTH_320MHZ; - break; - default: - ath12k_warn(ar->ab, "Invalid bandwidth %d in rc update for %pM\n", - sta->deflink.bandwidth, sta->addr); - bw = WMI_PEER_CHWIDTH_20MHZ; - break; - } - - return bw; -} - static int ath12k_mac_assign_link_sta(struct ath12k_hw *ah, struct ath12k_sta *ahsta, struct ath12k_link_sta *arsta, @@ -5529,13 +5798,15 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, enum ieee80211_sta_state new_state) { struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); - struct ieee80211_sta *sta = ath12k_ahsta_to_sta(arsta->ahsta); + struct ieee80211_bss_conf *link_conf; struct ath12k *ar = arvif->ar; + struct ath12k_reg_info *reg_info; + struct ath12k_base *ab = ar->ab; int ret = 0; lockdep_assert_wiphy(hw->wiphy); - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac handle link %u sta %pM state %d -> %d\n", + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac handle link %u sta %pM state %d -> %d\n", arsta->link_id, arsta->addr, old_state, new_state); /* IEEE80211_STA_NONE -> IEEE80211_STA_NOTEXIST: Remove the station @@ -5545,7 +5816,7 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, new_state == IEEE80211_STA_NOTEXIST)) { ret = ath12k_mac_station_remove(ar, arvif, arsta); if (ret) { - ath12k_warn(ar->ab, "Failed to remove station: %pM for VDEV: %d\n", + ath12k_warn(ab, "Failed to remove station: %pM for VDEV: %d\n", arsta->addr, arvif->vdev_id); goto exit; } @@ -5556,7 +5827,7 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, new_state == IEEE80211_STA_NONE) { ret = ath12k_mac_station_add(ar, arvif, arsta); if (ret) - ath12k_warn(ar->ab, "Failed to add station: %pM for VDEV: %d\n", + ath12k_warn(ab, "Failed to add station: %pM for VDEV: %d\n", arsta->addr, arvif->vdev_id); /* IEEE80211_STA_AUTH -> IEEE80211_STA_ASSOC: Send station assoc command for @@ -5569,25 +5840,30 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, vif->type == NL80211_IFTYPE_ADHOC)) { ret = ath12k_mac_station_assoc(ar, arvif, arsta, false); if (ret) - ath12k_warn(ar->ab, "Failed to associate station: %pM\n", + ath12k_warn(ab, "Failed to associate station: %pM\n", arsta->addr); - spin_lock_bh(&ar->data_lock); - - arsta->bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, sta); - arsta->bw_prev = sta->deflink.bandwidth; - - spin_unlock_bh(&ar->data_lock); - /* IEEE80211_STA_ASSOC -> IEEE80211_STA_AUTHORIZED: set peer status as * authorized */ } else if (old_state == IEEE80211_STA_ASSOC && new_state == IEEE80211_STA_AUTHORIZED) { ret = ath12k_mac_station_authorize(ar, arvif, arsta); - if (ret) - ath12k_warn(ar->ab, "Failed to authorize station: %pM\n", + if (ret) { + ath12k_warn(ab, "Failed to authorize station: %pM\n", arsta->addr); + goto exit; + } + + if (ath12k_wmi_supports_6ghz_cc_ext(ar) && + arvif->ahvif->vdev_type == WMI_VDEV_TYPE_STA) { + link_conf = ath12k_mac_get_link_bss_conf(arvif); + reg_info = ab->reg_info[ar->pdev_idx]; + ath12k_dbg(ab, ATH12K_DBG_MAC, "connection done, update reg rules\n"); + ath12k_hw_to_ah(hw)->regd_updated = false; + ath12k_reg_handle_chan_list(ab, reg_info, arvif->ahvif->vdev_type, + link_conf->power_type); + } /* IEEE80211_STA_AUTHORIZED -> IEEE80211_STA_ASSOC: station may be in removal, * deauthorize it. @@ -5606,7 +5882,7 @@ static int ath12k_mac_handle_link_sta_state(struct ieee80211_hw *hw, vif->type == NL80211_IFTYPE_ADHOC)) { ret = ath12k_mac_station_disassoc(ar, arvif, arsta); if (ret) - ath12k_warn(ar->ab, "Failed to disassociate station: %pM\n", + ath12k_warn(ab, "Failed to disassociate station: %pM\n", arsta->addr); } @@ -5670,6 +5946,17 @@ static int ath12k_mac_op_sta_state(struct ieee80211_hw *hw, * link sta */ if (sta->mlo) { + /* For station mode, arvif->is_sta_assoc_link has been set when + * vdev starts. Make sure the arvif/arsta pair have same setting + */ + if (vif->type == NL80211_IFTYPE_STATION && + !arsta->arvif->is_sta_assoc_link) { + ath12k_hw_warn(ah, "failed to verify assoc link setting with link id %u\n", + link_id); + ret = -EINVAL; + goto exit; + } + arsta->is_assoc_link = true; ahsta->assoc_link_id = link_id; } @@ -5846,7 +6133,7 @@ static void ath12k_mac_op_link_sta_rc_update(struct ieee80211_hw *hw, spin_lock_bh(&ar->data_lock); if (changed & IEEE80211_RC_BW_CHANGED) { - bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, sta); + bw = ath12k_mac_ieee80211_sta_bw_to_wmi(ar, link_sta); arsta->bw_prev = arsta->bw; arsta->bw = bw; } @@ -6343,7 +6630,7 @@ static void ath12k_mac_setup_ht_vht_cap(struct ath12k *ar, rate_cap_tx_chainmask = ar->cfg_tx_chainmask >> cap->tx_chain_mask_shift; rate_cap_rx_chainmask = ar->cfg_rx_chainmask >> cap->rx_chain_mask_shift; - if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) { + if (cap->supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { band = &ar->mac.sbands[NL80211_BAND_2GHZ]; ht_cap = cap->band[NL80211_BAND_2GHZ].ht_cap_info; if (ht_cap_info) @@ -6352,7 +6639,7 @@ static void ath12k_mac_setup_ht_vht_cap(struct ath12k *ar, rate_cap_rx_chainmask); } - if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP && + if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP && (ar->ab->hw_params->single_pdev_only || !ar->supports_6ghz)) { band = &ar->mac.sbands[NL80211_BAND_5GHZ]; @@ -6529,6 +6816,8 @@ static void ath12k_mac_copy_he_cap(struct ath12k_band_cap *band_cap, switch (iftype) { case NL80211_IFTYPE_AP: + he_cap_elem->mac_cap_info[2] &= + ~IEEE80211_HE_MAC_CAP2_BCAST_TWT; he_cap_elem->phy_cap_info[3] &= ~IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK; he_cap_elem->phy_cap_info[9] |= @@ -6674,7 +6963,8 @@ static void ath12k_mac_copy_eht_cap(struct ath12k *ar, memset(eht_cap, 0, sizeof(struct ieee80211_sta_eht_cap)); - if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map))) + if (!(test_bit(WMI_TLV_SERVICE_11BE, ar->ab->wmi_ab.svc_map)) || + ath12k_acpi_get_disable_11be(ar->ab)) return; eht_cap->has_eht = true; @@ -6760,7 +7050,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar, enum nl80211_band band; int count; - if (cap->supported_bands & WMI_HOST_WLAN_2G_CAP) { + if (cap->supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { band = NL80211_BAND_2GHZ; count = ath12k_mac_copy_sband_iftype_data(ar, cap, ar->mac.iftype[band], @@ -6770,7 +7060,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar, count); } - if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP) { + if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { band = NL80211_BAND_5GHZ; count = ath12k_mac_copy_sband_iftype_data(ar, cap, ar->mac.iftype[band], @@ -6780,7 +7070,7 @@ static void ath12k_mac_setup_sband_iftype_data(struct ath12k *ar, count); } - if (cap->supported_bands & WMI_HOST_WLAN_5G_CAP && + if (cap->supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) { band = NL80211_BAND_6GHZ; count = ath12k_mac_copy_sband_iftype_data(ar, cap, @@ -6908,14 +7198,17 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arv { struct ath12k_base *ab = ar->ab; struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ath12k_skb_cb *skb_cb = ATH12K_SKB_CB(skb); struct ieee80211_tx_info *info; + enum hal_encrypt_type enctype; + unsigned int mic_len; dma_addr_t paddr; int buf_id; int ret; lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - ATH12K_SKB_CB(skb)->ar = ar; + skb_cb->ar = ar; spin_lock_bh(&ar->txmgmt_idr_lock); buf_id = idr_alloc(&ar->txmgmt_idr, skb, 0, ATH12K_TX_MGMT_NUM_PENDING_MAX, GFP_ATOMIC); @@ -6924,12 +7217,15 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arv return -ENOSPC; info = IEEE80211_SKB_CB(skb); - if (!(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { + if ((skb_cb->flags & ATH12K_SKB_CIPHER_SET) && + !(info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) { 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(skb, IEEE80211_CCMP_MIC_LEN); + enctype = ath12k_dp_tx_get_encrypt_type(skb_cb->cipher); + mic_len = ath12k_dp_rx_crypto_mic_len(ar, enctype); + skb_put(skb, mic_len); } } @@ -6940,7 +7236,7 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arv goto err_free_idr; } - ATH12K_SKB_CB(skb)->paddr = paddr; + skb_cb->paddr = paddr; ret = ath12k_wmi_mgmt_send(ar, arvif->vdev_id, buf_id, skb); if (ret) { @@ -6951,7 +7247,7 @@ static int ath12k_mac_mgmt_tx_wmi(struct ath12k *ar, struct ath12k_link_vif *arv return 0; err_unmap_buf: - dma_unmap_single(ab->dev, ATH12K_SKB_CB(skb)->paddr, + dma_unmap_single(ab->dev, skb_cb->paddr, skb->len, DMA_TO_DEVICE); err_free_idr: spin_lock_bh(&ar->txmgmt_idr_lock); @@ -7071,6 +7367,22 @@ static void ath12k_mac_add_p2p_noa_ie(struct ath12k *ar, } /* Note: called under rcu_read_lock() */ +static void ath12k_mlo_mcast_update_tx_link_address(struct ieee80211_vif *vif, + u8 link_id, struct sk_buff *skb, + u32 info_flags) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + struct ieee80211_bss_conf *bss_conf; + + if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) + return; + + bss_conf = rcu_dereference(vif->link_conf[link_id]); + if (bss_conf) + ether_addr_copy(hdr->addr2, bss_conf->addr); +} + +/* Note: called under rcu_read_lock() */ static u8 ath12k_mac_get_tx_link(struct ieee80211_sta *sta, struct ieee80211_vif *vif, u8 link, struct sk_buff *skb, u32 info_flags) { @@ -7181,12 +7493,25 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_key_conf *key = info->control.hw_key; struct ieee80211_sta *sta = control->sta; + struct ath12k_link_vif *tmp_arvif; u32 info_flags = info->flags; - struct ath12k *ar; + struct sk_buff *msdu_copied; + struct ath12k *ar, *tmp_ar; + struct ath12k_peer *peer; + unsigned long links_map; + bool is_mcast = false; + bool is_dvlan = false; + struct ethhdr *eth; bool is_prb_rsp; + u16 mcbc_gsn; u8 link_id; int ret; + if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ieee80211_free_txskb(hw, skb); + return; + } + link_id = u32_get_bits(info->control.flags, IEEE80211_TX_CTRL_MLO_LINK); memset(skb_cb, 0, sizeof(*skb_cb)); skb_cb->vif = vif; @@ -7220,6 +7545,9 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, is_prb_rsp = ieee80211_is_probe_resp(hdr->frame_control); if (info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP) { + eth = (struct ethhdr *)skb->data; + is_mcast = is_multicast_ether_addr(eth->h_dest); + skb_cb->flags |= ATH12K_SKB_HW_80211_ENCAP; } else if (ieee80211_is_mgmt(hdr->frame_control)) { ret = ath12k_mac_mgmt_tx(ar, skb, is_prb_rsp); @@ -7231,14 +7559,104 @@ static void ath12k_mac_op_tx(struct ieee80211_hw *hw, return; } + if (!(info_flags & IEEE80211_TX_CTL_HW_80211_ENCAP)) + is_mcast = is_multicast_ether_addr(hdr->addr1); + /* This is case only for P2P_GO */ if (vif->type == NL80211_IFTYPE_AP && vif->p2p) ath12k_mac_add_p2p_noa_ie(ar, vif, skb, is_prb_rsp); - ret = ath12k_dp_tx(ar, arvif, skb); - if (ret) { - ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); - ieee80211_free_txskb(hw, skb); + /* Checking if it is a DVLAN frame */ + if (!test_bit(ATH12K_FLAG_HW_CRYPTO_DISABLED, &ar->ab->dev_flags) && + !(skb_cb->flags & ATH12K_SKB_HW_80211_ENCAP) && + !(skb_cb->flags & ATH12K_SKB_CIPHER_SET) && + ieee80211_has_protected(hdr->frame_control)) + is_dvlan = true; + + if (!vif->valid_links || !is_mcast || is_dvlan || + test_bit(ATH12K_FLAG_RAW_MODE, &ar->ab->dev_flags)) { + ret = ath12k_dp_tx(ar, arvif, skb, false, 0, is_mcast); + if (unlikely(ret)) { + ath12k_warn(ar->ab, "failed to transmit frame %d\n", ret); + ieee80211_free_txskb(ar->ah->hw, skb); + return; + } + } else { + mcbc_gsn = atomic_inc_return(&ahvif->mcbc_gsn) & 0xfff; + + links_map = ahvif->links_map; + for_each_set_bit(link_id, &links_map, + IEEE80211_MLD_MAX_NUM_LINKS) { + tmp_arvif = rcu_dereference(ahvif->link[link_id]); + if (!tmp_arvif || !tmp_arvif->is_up) + continue; + + tmp_ar = tmp_arvif->ar; + msdu_copied = skb_copy(skb, GFP_ATOMIC); + if (!msdu_copied) { + ath12k_err(ar->ab, + "skb copy failure link_id 0x%X vdevid 0x%X\n", + link_id, tmp_arvif->vdev_id); + continue; + } + + ath12k_mlo_mcast_update_tx_link_address(vif, link_id, + msdu_copied, + info_flags); + + skb_cb = ATH12K_SKB_CB(msdu_copied); + skb_cb->link_id = link_id; + + /* For open mode, skip peer find logic */ + if (unlikely(!ahvif->key_cipher)) + goto skip_peer_find; + + spin_lock_bh(&tmp_ar->ab->base_lock); + peer = ath12k_peer_find_by_addr(tmp_ar->ab, tmp_arvif->bssid); + if (!peer) { + spin_unlock_bh(&tmp_ar->ab->base_lock); + ath12k_warn(tmp_ar->ab, + "failed to find peer for vdev_id 0x%X addr %pM link_map 0x%X\n", + tmp_arvif->vdev_id, tmp_arvif->bssid, + ahvif->links_map); + dev_kfree_skb_any(msdu_copied); + continue; + } + + key = peer->keys[peer->mcast_keyidx]; + if (key) { + skb_cb->cipher = key->cipher; + skb_cb->flags |= ATH12K_SKB_CIPHER_SET; + + hdr = (struct ieee80211_hdr *)msdu_copied->data; + if (!ieee80211_has_protected(hdr->frame_control)) + hdr->frame_control |= + cpu_to_le16(IEEE80211_FCTL_PROTECTED); + } + spin_unlock_bh(&tmp_ar->ab->base_lock); + +skip_peer_find: + ret = ath12k_dp_tx(tmp_ar, tmp_arvif, + msdu_copied, true, mcbc_gsn, is_mcast); + if (unlikely(ret)) { + if (ret == -ENOMEM) { + /* Drops are expected during heavy multicast + * frame flood. Print with debug log + * level to avoid lot of console prints + */ + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "failed to transmit frame %d\n", + ret); + } else { + ath12k_warn(ar->ab, + "failed to transmit frame %d\n", + ret); + } + + dev_kfree_skb_any(msdu_copied); + } + } + ieee80211_free_txskb(ar->ah->hw, skb); } } @@ -7255,8 +7673,40 @@ void ath12k_mac_drain_tx(struct ath12k *ar) static int ath12k_mac_config_mon_status_default(struct ath12k *ar, bool enable) { - return -EOPNOTSUPP; - /* TODO: Need to support new monitor mode */ + struct htt_rx_ring_tlv_filter tlv_filter = {}; + struct ath12k_base *ab = ar->ab; + u32 ring_id, i; + int ret = 0; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (!ab->hw_params->rxdma1_enable) + return ret; + + if (enable) { + tlv_filter = ath12k_mac_mon_status_filter_default; + + if (ath12k_debugfs_rx_filter(ar)) + tlv_filter.rx_filter = ath12k_debugfs_rx_filter(ar); + } else { + tlv_filter.rxmon_disable = true; + } + + for (i = 0; i < ab->hw_params->num_rxdma_per_pdev; i++) { + ring_id = ar->dp.rxdma_mon_dst_ring[i].ring_id; + ret = ath12k_dp_tx_htt_rx_filter_setup(ab, ring_id, + ar->dp.mac_id + i, + HAL_RXDMA_MONITOR_DST, + DP_RXDMA_REFILL_RING_SIZE, + &tlv_filter); + if (ret) { + ath12k_err(ab, + "failed to setup filter for monitor buf %d\n", + ret); + } + } + + return ret; } static int ath12k_mac_start(struct ath12k *ar) @@ -7273,7 +7723,7 @@ static int ath12k_mac_start(struct ath12k *ar) 1, pdev->pdev_id); if (ret) { - ath12k_err(ab, "failed to enable PMF QOS: (%d\n", ret); + ath12k_err(ab, "failed to enable PMF QOS: %d\n", ret); goto err; } @@ -7318,12 +7768,13 @@ static int ath12k_mac_start(struct ath12k *ar) /* TODO: Do we need to enable ANI? */ - ath12k_reg_update_chan_list(ar); + ath12k_reg_update_chan_list(ar, false); ar->num_started_vdevs = 0; ar->num_created_vdevs = 0; ar->num_peers = 0; ar->allocated_vdev_map = 0; + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; /* Configure monitor status ring with default rx_filter to get rx status * such as rssi, rx_duration. @@ -7363,9 +7814,14 @@ err: static void ath12k_drain_tx(struct ath12k_hw *ah) { - struct ath12k *ar; + struct ath12k *ar = ah->radio; int i; + if (ath12k_ftm_mode) { + ath12k_err(ar->ab, "fail to start mac operations in ftm mode\n"); + return; + } + lockdep_assert_wiphy(ah->hw->wiphy); for_each_ar(ah, ar, i) @@ -7394,6 +7850,7 @@ static int ath12k_mac_op_start(struct ieee80211_hw *hw) case ATH12K_HW_STATE_RESTARTED: case ATH12K_HW_STATE_WEDGED: case ATH12K_HW_STATE_ON: + case ATH12K_HW_STATE_TM: ah->state = ATH12K_HW_STATE_OFF; WARN_ON(1); @@ -7499,6 +7956,9 @@ static void ath12k_mac_stop(struct ath12k *ar) wiphy_work_cancel(ath12k_ar_to_hw(ar)->wiphy, &ar->scan.vdev_clean_wk); cancel_work_sync(&ar->regd_update_work); cancel_work_sync(&ar->ab->rfkill_work); + cancel_work_sync(&ar->ab->update_11d_work); + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); spin_lock_bh(&ar->data_lock); list_for_each_entry_safe(ppdu_stats, tmp, &ar->ppdu_stats_info, list) { @@ -7561,14 +8021,9 @@ static int ath12k_mac_setup_vdev_params_mbssid(struct ath12k_link_vif *arvif, u32 *flags, u32 *tx_vdev_id) { struct ath12k_vif *ahvif = arvif->ahvif; - struct ieee80211_vif *tx_vif = ahvif->vif->mbssid_tx_vif; struct ieee80211_bss_conf *link_conf; struct ath12k *ar = arvif->ar; struct ath12k_link_vif *tx_arvif; - struct ath12k_vif *tx_ahvif; - - if (!tx_vif) - return 0; link_conf = ath12k_mac_get_link_bss_conf(arvif); if (!link_conf) { @@ -7577,11 +8032,13 @@ static int ath12k_mac_setup_vdev_params_mbssid(struct ath12k_link_vif *arvif, return -ENOLINK; } - tx_ahvif = ath12k_vif_to_ahvif(tx_vif); - tx_arvif = &tx_ahvif->deflink; + tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); + if (!tx_arvif) + return 0; if (link_conf->nontransmitted) { - if (ar->ah->hw->wiphy != ieee80211_vif_to_wdev(tx_vif)->wiphy) + if (ath12k_ar_to_hw(ar)->wiphy != + ath12k_ar_to_hw(tx_arvif->ar)->wiphy) return -EINVAL; *flags = WMI_VDEV_MBSSID_FLAGS_NON_TRANSMIT_AP; @@ -7624,15 +8081,15 @@ static int ath12k_mac_setup_vdev_create_arg(struct ath12k_link_vif *arvif, return ret; } - if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) { + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { arg->chains[NL80211_BAND_2GHZ].tx = ar->num_tx_chains; arg->chains[NL80211_BAND_2GHZ].rx = ar->num_rx_chains; } - if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP) { + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { arg->chains[NL80211_BAND_5GHZ].tx = ar->num_tx_chains; arg->chains[NL80211_BAND_5GHZ].rx = ar->num_rx_chains; } - if (pdev->cap.supported_bands & WMI_HOST_WLAN_5G_CAP && + if (pdev->cap.supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) { arg->chains[NL80211_BAND_6GHZ].tx = ar->num_tx_chains; arg->chains[NL80211_BAND_6GHZ].rx = ar->num_rx_chains; @@ -7661,7 +8118,7 @@ ath12k_mac_prepare_he_mode(struct ath12k_pdev *pdev, u32 viftype) u32 *hecap_phy_ptr = NULL; u32 hemode; - if (pdev->cap.supported_bands & WMI_HOST_WLAN_2G_CAP) + if (pdev->cap.supported_bands & WMI_HOST_WLAN_2GHZ_CAP) cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; else cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; @@ -7792,44 +8249,121 @@ static void ath12k_mac_op_update_vif_offload(struct ieee80211_hw *hw, ath12k_mac_update_vif_offload(&ahvif->deflink); } -int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) +static bool ath12k_mac_vif_ap_active_any(struct ath12k_base *ab) { - struct ath12k_hw *ah = ar->ah; - struct ath12k_base *ab = ar->ab; - struct ieee80211_hw *hw = ah->hw; - struct ath12k_vif *ahvif = arvif->ahvif; - struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); - struct ath12k_wmi_vdev_create_arg vdev_arg = {0}; - struct ath12k_wmi_peer_create_arg peer_param = {0}; - struct ieee80211_bss_conf *link_conf; - u32 param_id, param_value; - u16 nss; + struct ath12k *ar; + struct ath12k_pdev *pdev; + struct ath12k_link_vif *arvif; int i; - int ret, vdev_id; - u8 link_id; - lockdep_assert_wiphy(hw->wiphy); + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->is_up && + arvif->ahvif->vdev_type == WMI_VDEV_TYPE_AP) + return true; + } + } + return false; +} - /* If no link is active and scan vdev is requested - * use a default link conf for scan address purpose. - */ - if (arvif->link_id == ATH12K_DEFAULT_SCAN_LINK && vif->valid_links) - link_id = ffs(vif->valid_links) - 1; - else - link_id = arvif->link_id; +void ath12k_mac_11d_scan_start(struct ath12k *ar, u32 vdev_id) +{ + struct wmi_11d_scan_start_arg arg; + int ret; - link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]); - if (!link_conf) { - ath12k_warn(ar->ab, "unable to access bss link conf in vdev create for vif %pM link %u\n", - vif->addr, arvif->link_id); - return -ENOLINK; + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (ar->regdom_set_by_user) + goto fin; + + if (ar->vdev_id_11d_scan != ATH12K_11D_INVALID_VDEV_ID) + goto fin; + + if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map)) + goto fin; + + if (ath12k_mac_vif_ap_active_any(ar->ab)) + goto fin; + + arg.vdev_id = vdev_id; + arg.start_interval_msec = 0; + arg.scan_period_msec = ATH12K_SCAN_11D_INTERVAL; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "mac start 11d scan for vdev %d\n", vdev_id); + + ret = ath12k_wmi_send_11d_scan_start_cmd(ar, &arg); + if (ret) { + ath12k_warn(ar->ab, "failed to start 11d scan vdev %d ret: %d\n", + vdev_id, ret); + } else { + ar->vdev_id_11d_scan = vdev_id; + if (ar->state_11d == ATH12K_11D_PREPARING) + ar->state_11d = ATH12K_11D_RUNNING; } - memcpy(arvif->bssid, link_conf->addr, ETH_ALEN); +fin: + if (ar->state_11d == ATH12K_11D_PREPARING) { + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); + } +} - arvif->ar = ar; - vdev_id = __ffs64(ab->free_vdev_map); - arvif->vdev_id = vdev_id; +void ath12k_mac_11d_scan_stop(struct ath12k *ar) +{ + int ret; + u32 vdev_id; + + lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); + + if (!test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ar->ab->wmi_ab.svc_map)) + return; + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac stop 11d for vdev %d\n", + ar->vdev_id_11d_scan); + + if (ar->state_11d == ATH12K_11D_PREPARING) { + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); + } + + if (ar->vdev_id_11d_scan != ATH12K_11D_INVALID_VDEV_ID) { + vdev_id = ar->vdev_id_11d_scan; + + ret = ath12k_wmi_send_11d_scan_stop_cmd(ar, vdev_id); + if (ret) { + ath12k_warn(ar->ab, + "failed to stopt 11d scan vdev %d ret: %d\n", + vdev_id, ret); + } else { + ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID; + ar->state_11d = ATH12K_11D_IDLE; + complete(&ar->completed_11d_scan); + } + } +} + +void ath12k_mac_11d_scan_stop_all(struct ath12k_base *ab) +{ + struct ath12k *ar; + struct ath12k_pdev *pdev; + int i; + + ath12k_dbg(ab, ATH12K_DBG_MAC, "mac stop soc 11d scan\n"); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + + ath12k_mac_11d_scan_stop(ar); + } +} + +static void ath12k_mac_determine_vdev_type(struct ieee80211_vif *vif, + struct ath12k_vif *ahvif) +{ ahvif->vdev_subtype = WMI_VDEV_SUBTYPE_NONE; switch (vif->type) { @@ -7853,7 +8387,6 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) break; case NL80211_IFTYPE_MONITOR: ahvif->vdev_type = WMI_VDEV_TYPE_MONITOR; - ar->monitor_vdev_id = vdev_id; break; case NL80211_IFTYPE_P2P_DEVICE: ahvif->vdev_type = WMI_VDEV_TYPE_STA; @@ -7863,6 +8396,53 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) WARN_ON(1); break; } +} + +int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) +{ + struct ath12k_hw *ah = ar->ah; + struct ath12k_base *ab = ar->ab; + struct ieee80211_hw *hw = ah->hw; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); + struct ath12k_wmi_vdev_create_arg vdev_arg = {0}; + struct ath12k_wmi_peer_create_arg peer_param = {0}; + struct ieee80211_bss_conf *link_conf = NULL; + u32 param_id, param_value; + u16 nss; + int i; + int ret, vdev_id; + u8 link_id; + + lockdep_assert_wiphy(hw->wiphy); + + /* In NO_VIRTUAL_MONITOR, its necessary to restrict only one monitor + * interface in each radio + */ + if (vif->type == NL80211_IFTYPE_MONITOR && ar->monitor_vdev_created) + return -EINVAL; + + link_id = arvif->link_id; + + if (link_id < IEEE80211_MLD_MAX_NUM_LINKS) { + link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in vdev create for vif %pM link %u\n", + vif->addr, arvif->link_id); + return -ENOLINK; + } + } + + if (link_conf) + memcpy(arvif->bssid, link_conf->addr, ETH_ALEN); + else + memcpy(arvif->bssid, vif->addr, ETH_ALEN); + + arvif->ar = ar; + vdev_id = __ffs64(ab->free_vdev_map); + arvif->vdev_id = vdev_id; + if (vif->type == NL80211_IFTYPE_MONITOR) + ar->monitor_vdev_id = vdev_id; ath12k_dbg(ar->ab, ATH12K_DBG_MAC, "mac vdev create id %d type %d subtype %d map %llx\n", arvif->vdev_id, ahvif->vdev_type, ahvif->vdev_subtype, @@ -7926,6 +8506,7 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) arvif->vdev_id, ret); goto err_peer_del; } + ath12k_mac_11d_scan_stop_all(ar->ab); break; case WMI_VDEV_TYPE_STA: param_id = WMI_STA_PS_PARAM_RX_WAKE_POLICY; @@ -7964,12 +8545,26 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) arvif->vdev_id, ret); goto err_peer_del; } + + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE) { + reinit_completion(&ar->completed_11d_scan); + ar->state_11d = ATH12K_11D_PREPARING; + } + break; + case WMI_VDEV_TYPE_MONITOR: + ar->monitor_vdev_created = true; break; default: break; } - arvif->txpower = link_conf->txpower; + if (link_conf) + arvif->txpower = link_conf->txpower; + else + arvif->txpower = NL80211_TX_POWER_AUTOMATIC; + ret = ath12k_mac_txpower_recalc(ar); if (ret) goto err_peer_del; @@ -7984,8 +8579,6 @@ int ath12k_mac_vdev_create(struct ath12k *ar, struct ath12k_link_vif *arvif) } ath12k_dp_vdev_tx_attach(ar, arvif); - if (vif->type != NL80211_IFTYPE_MONITOR && ar->monitor_conf_enabled) - ath12k_mac_monitor_vdev_create(ar); return ret; @@ -8010,6 +8603,11 @@ err_peer_del: } err_vdev_del: + if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { + ar->monitor_vdev_id = -1; + ar->monitor_vdev_created = false; + } + ath12k_wmi_vdev_delete(ar, arvif->vdev_id); ar->num_created_vdevs--; arvif->is_created = false; @@ -8066,6 +8664,7 @@ static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif struct ieee80211_vif *vif = ath12k_ahvif_to_vif(ahvif); struct ath12k_vif_cache *cache = ahvif->cache[arvif->link_id]; struct ath12k_base *ab = ar->ab; + struct ieee80211_bss_conf *link_conf; int ret; @@ -8084,7 +8683,13 @@ static void ath12k_mac_vif_cache_flush(struct ath12k *ar, struct ath12k_link_vif } if (cache->bss_conf_changed) { - ath12k_mac_bss_info_changed(ar, arvif, &vif->bss_conf, + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ar->ab, "unable to access bss link conf in cache flush for vif %pM link %u\n", + vif->addr, arvif->link_id); + return; + } + ath12k_mac_bss_info_changed(ar, arvif, link_conf, cache->bss_conf_changed); } @@ -8197,7 +8802,10 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, { struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k_vif *ahvif = ath12k_vif_to_ahvif(vif); + struct ath12k_reg_info *reg_info; struct ath12k_link_vif *arvif; + struct ath12k_base *ab; + struct ath12k *ar; int i; lockdep_assert_wiphy(hw->wiphy); @@ -8207,19 +8815,8 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, ahvif->ah = ah; ahvif->vif = vif; arvif = &ahvif->deflink; - arvif->ahvif = ahvif; - INIT_LIST_HEAD(&arvif->list); - INIT_DELAYED_WORK(&arvif->connection_loss_work, - ath12k_mac_vif_sta_connection_loss_work); - - for (i = 0; i < ARRAY_SIZE(arvif->bitrate_mask.control); i++) { - arvif->bitrate_mask.control[i].legacy = 0xffffffff; - memset(arvif->bitrate_mask.control[i].ht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].ht_mcs)); - memset(arvif->bitrate_mask.control[i].vht_mcs, 0xff, - sizeof(arvif->bitrate_mask.control[i].vht_mcs)); - } + ath12k_mac_init_arvif(ahvif, arvif, -1); /* Allocate Default Queue now and reassign during actual vdev create */ vif->cab_queue = ATH12K_HW_DEFAULT_QUEUE; @@ -8227,6 +8824,22 @@ static int ath12k_mac_op_add_interface(struct ieee80211_hw *hw, vif->hw_queue[i] = ATH12K_HW_DEFAULT_QUEUE; vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + + ath12k_mac_determine_vdev_type(vif, ahvif); + + for_each_ar(ah, ar, i) { + if (!ath12k_wmi_supports_6ghz_cc_ext(ar)) + continue; + + ab = ar->ab; + reg_info = ab->reg_info[ar->pdev_idx]; + ath12k_dbg(ab, ATH12K_DBG_MAC, "interface added to change reg rules\n"); + ah->regd_updated = false; + ath12k_reg_handle_chan_list(ab, reg_info, ahvif->vdev_type, + IEEE80211_REG_UNSET_AP); + break; + } + /* Defer vdev creation until assign_chanctx or hw_scan is initiated as driver * will not know if this interface is an ML vif at this point. */ @@ -8291,8 +8904,6 @@ static int ath12k_mac_vdev_delete(struct ath12k *ar, struct ath12k_link_vif *arv if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ar->monitor_vdev_id = -1; ar->monitor_vdev_created = false; - } else if (ar->monitor_vdev_created && !ar->monitor_started) { - ret = ath12k_mac_monitor_vdev_delete(ar); } ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM deleted, vdev_id %d\n", @@ -8381,29 +8992,6 @@ static void ath12k_mac_op_remove_interface(struct ieee80211_hw *hw, FIF_PROBE_REQ | \ FIF_FCSFAIL) -static void ath12k_mac_configure_filter(struct ath12k *ar, - unsigned int total_flags) -{ - bool reset_flag; - int ret; - - lockdep_assert_wiphy(ath12k_ar_to_hw(ar)->wiphy); - - ar->filter_flags = total_flags; - - /* For monitor mode */ - reset_flag = !(ar->filter_flags & FIF_BCN_PRBRESP_PROMISC); - - ret = ath12k_dp_tx_htt_monitor_mode_ring_config(ar, reset_flag); - if (ret) - ath12k_warn(ar->ab, - "fail to set monitor filter: %d\n", ret); - - ath12k_dbg(ar->ab, ATH12K_DBG_MAC, - "total_flags:0x%x, reset_flag:%d\n", - total_flags, reset_flag); -} - static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, unsigned int changed_flags, unsigned int *total_flags, @@ -8417,7 +9005,7 @@ static void ath12k_mac_op_configure_filter(struct ieee80211_hw *hw, ar = ath12k_ah_to_ar(ah, 0); *total_flags &= SUPPORTED_FILTERS; - ath12k_mac_configure_filter(ar, *total_flags); + ar->filter_flags = *total_flags; } static int ath12k_mac_op_get_antenna(struct ieee80211_hw *hw, u32 *tx_ant, u32 *rx_ant) @@ -8546,6 +9134,7 @@ static int ath12k_mac_op_add_chanctx(struct ieee80211_hw *hw, */ ar->rx_channel = ctx->def.chan; spin_unlock_bh(&ar->data_lock); + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; return 0; } @@ -8574,6 +9163,7 @@ static void ath12k_mac_op_remove_chanctx(struct ieee80211_hw *hw, */ ar->rx_channel = NULL; spin_unlock_bh(&ar->data_lock); + ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID; } static enum wmi_phy_mode @@ -8665,6 +9255,9 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, * link vdevs which are advertised as partners below */ ml_arg->link_add = true; + + ml_arg->assoc_link = arvif->is_sta_assoc_link; + partner_info = ml_arg->partner_info; links = ahvif->links_map; @@ -8677,6 +9270,9 @@ ath12k_mac_mlo_get_vdev_args(struct ath12k_link_vif *arvif, if (arvif == arvif_p) continue; + if (!arvif_p->is_created) + continue; + link_conf = wiphy_dereference(ahvif->ah->hw->wiphy, ahvif->vif->link_conf[arvif_p->link_id]); @@ -8800,6 +9396,15 @@ ath12k_mac_vdev_start_restart(struct ath12k_link_vif *arvif, return ret; } + /* TODO: For now we only set TPC power here. However when + * channel changes, say CSA, it should be updated again. + */ + if (ath12k_mac_supports_station_tpc(ar, ahvif, chandef)) { + ath12k_mac_fill_reg_tpc_info(ar, arvif, ctx); + ath12k_wmi_send_vdev_set_tpc_power(ar, arvif->vdev_id, + &arvif->reg_tpc_info); + } + ar->num_started_vdevs++; ath12k_dbg(ab, ATH12K_DBG_MAC, "vdev %pM started, vdev_id %d\n", ahvif->vif->addr, arvif->vdev_id); @@ -8982,9 +9587,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, int n_vifs) { struct ath12k_wmi_vdev_up_params params = {}; + struct ath12k_link_vif *arvif, *tx_arvif; struct ieee80211_bss_conf *link_conf; struct ath12k_base *ab = ar->ab; - struct ath12k_link_vif *arvif; struct ieee80211_vif *vif; struct ath12k_vif *ahvif; u8 link_id; @@ -9002,8 +9607,10 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, arvif = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, ahvif->link[link_id]); - if (vif->type == NL80211_IFTYPE_MONITOR) + if (vif->type == NL80211_IFTYPE_MONITOR) { monitor_vif = true; + continue; + } ath12k_dbg(ab, ATH12K_DBG_MAC, "mac chanctx switch vdev_id %i freq %u->%u width %d->%d\n", @@ -9052,11 +9659,9 @@ ath12k_mac_update_vif_chan(struct ath12k *ar, params.vdev_id = arvif->vdev_id; params.aid = ahvif->aid; params.bssid = arvif->bssid; - if (vif->mbssid_tx_vif) { - struct ath12k_vif *tx_ahvif = - ath12k_vif_to_ahvif(vif->mbssid_tx_vif); - struct ath12k_link_vif *tx_arvif = &tx_ahvif->deflink; + tx_arvif = ath12k_mac_get_tx_arvif(arvif, link_conf); + if (tx_arvif) { params.tx_bssid = tx_arvif->bssid; params.nontx_profile_idx = link_conf->bssid_index; params.nontx_profile_cnt = 1 << link_conf->bssid_indicator; @@ -9155,16 +9760,26 @@ static int ath12k_start_vdev_delay(struct ath12k *ar, struct ath12k_base *ab = ar->ab; struct ath12k_vif *ahvif = arvif->ahvif; struct ieee80211_vif *vif = ath12k_ahvif_to_vif(arvif->ahvif); + struct ieee80211_chanctx_conf *chanctx; + struct ieee80211_bss_conf *link_conf; int ret; if (WARN_ON(arvif->is_started)) return -EBUSY; - ret = ath12k_mac_vdev_start(arvif, &arvif->chanctx); + link_conf = ath12k_mac_get_link_bss_conf(arvif); + if (!link_conf) { + ath12k_warn(ab, "failed to get link conf for vdev %u\n", arvif->vdev_id); + return -EINVAL; + } + + chanctx = wiphy_dereference(ath12k_ar_to_hw(arvif->ar)->wiphy, + link_conf->chanctx_conf); + ret = ath12k_mac_vdev_start(arvif, chanctx); if (ret) { ath12k_warn(ab, "failed to start vdev %i addr %pM on freq %d: %d\n", arvif->vdev_id, vif->addr, - arvif->chanctx.def.chan->center_freq, ret); + chanctx->def.chan->center_freq, ret); return ret; } @@ -9182,6 +9797,391 @@ static int ath12k_start_vdev_delay(struct ath12k *ar, return 0; } +static u8 ath12k_mac_get_num_pwr_levels(struct cfg80211_chan_def *chan_def) +{ + if (chan_def->chan->flags & IEEE80211_CHAN_PSD) { + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + return 1; + case NL80211_CHAN_WIDTH_40: + return 2; + case NL80211_CHAN_WIDTH_80: + return 4; + case NL80211_CHAN_WIDTH_160: + return 8; + case NL80211_CHAN_WIDTH_320: + return 16; + default: + return 1; + } + } else { + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_20: + return 1; + case NL80211_CHAN_WIDTH_40: + return 2; + case NL80211_CHAN_WIDTH_80: + return 3; + case NL80211_CHAN_WIDTH_160: + return 4; + case NL80211_CHAN_WIDTH_320: + return 5; + default: + return 1; + } + } +} + +static u16 ath12k_mac_get_6ghz_start_frequency(struct cfg80211_chan_def *chan_def) +{ + u16 diff_seq; + + /* It is to get the lowest channel number's center frequency of the chan. + * For example, + * bandwidth=40 MHz, center frequency is 5965, lowest channel is 1 + * with center frequency 5955, its diff is 5965 - 5955 = 10. + * bandwidth=80 MHz, center frequency is 5985, lowest channel is 1 + * with center frequency 5955, its diff is 5985 - 5955 = 30. + * bandwidth=160 MHz, center frequency is 6025, lowest channel is 1 + * with center frequency 5955, its diff is 6025 - 5955 = 70. + * bandwidth=320 MHz, center frequency is 6105, lowest channel is 1 + * with center frequency 5955, its diff is 6105 - 5955 = 70. + */ + switch (chan_def->width) { + case NL80211_CHAN_WIDTH_320: + diff_seq = 150; + break; + case NL80211_CHAN_WIDTH_160: + diff_seq = 70; + break; + case NL80211_CHAN_WIDTH_80: + diff_seq = 30; + break; + case NL80211_CHAN_WIDTH_40: + diff_seq = 10; + break; + default: + diff_seq = 0; + } + + return chan_def->center_freq1 - diff_seq; +} + +static u16 ath12k_mac_get_seg_freq(struct cfg80211_chan_def *chan_def, + u16 start_seq, u8 seq) +{ + u16 seg_seq; + + /* It is to get the center frequency of the specific bandwidth. + * start_seq means the lowest channel number's center frequency. + * seq 0/1/2/3 means 20 MHz/40 MHz/80 MHz/160 MHz. + * For example, + * lowest channel is 1, its center frequency 5955, + * center frequency is 5955 when bandwidth=20 MHz, its diff is 5955 - 5955 = 0. + * lowest channel is 1, its center frequency 5955, + * center frequency is 5965 when bandwidth=40 MHz, its diff is 5965 - 5955 = 10. + * lowest channel is 1, its center frequency 5955, + * center frequency is 5985 when bandwidth=80 MHz, its diff is 5985 - 5955 = 30. + * lowest channel is 1, its center frequency 5955, + * center frequency is 6025 when bandwidth=160 MHz, its diff is 6025 - 5955 = 70. + */ + seg_seq = 10 * (BIT(seq) - 1); + return seg_seq + start_seq; +} + +static void ath12k_mac_get_psd_channel(struct ath12k *ar, + u16 step_freq, + u16 *start_freq, + u16 *center_freq, + u8 i, + struct ieee80211_channel **temp_chan, + s8 *tx_power) +{ + /* It is to get the center frequency for each 20 MHz. + * For example, if the chan is 160 MHz and center frequency is 6025, + * then it include 8 channels, they are 1/5/9/13/17/21/25/29, + * channel number 1's center frequency is 5955, it is parameter start_freq. + * parameter i is the step of the 8 channels. i is 0~7 for the 8 channels. + * the channel 1/5/9/13/17/21/25/29 maps i=0/1/2/3/4/5/6/7, + * and maps its center frequency is 5955/5975/5995/6015/6035/6055/6075/6095, + * the gap is 20 for each channel, parameter step_freq means the gap. + * after get the center frequency of each channel, it is easy to find the + * struct ieee80211_channel of it and get the max_reg_power. + */ + *center_freq = *start_freq + i * step_freq; + *temp_chan = ieee80211_get_channel(ar->ah->hw->wiphy, *center_freq); + *tx_power = (*temp_chan)->max_reg_power; +} + +static void ath12k_mac_get_eirp_power(struct ath12k *ar, + u16 *start_freq, + u16 *center_freq, + u8 i, + struct ieee80211_channel **temp_chan, + struct cfg80211_chan_def *def, + s8 *tx_power) +{ + /* It is to get the center frequency for 20 MHz/40 MHz/80 MHz/ + * 160 MHz bandwidth, and then plus 10 to the center frequency, + * it is the center frequency of a channel number. + * For example, when configured channel number is 1. + * center frequency is 5965 when bandwidth=40 MHz, after plus 10, it is 5975, + * then it is channel number 5. + * center frequency is 5985 when bandwidth=80 MHz, after plus 10, it is 5995, + * then it is channel number 9. + * center frequency is 6025 when bandwidth=160 MHz, after plus 10, it is 6035, + * then it is channel number 17. + * after get the center frequency of each channel, it is easy to find the + * struct ieee80211_channel of it and get the max_reg_power. + */ + *center_freq = ath12k_mac_get_seg_freq(def, *start_freq, i); + + /* For the 20 MHz, its center frequency is same with same channel */ + if (i != 0) + *center_freq += 10; + + *temp_chan = ieee80211_get_channel(ar->ah->hw->wiphy, *center_freq); + *tx_power = (*temp_chan)->max_reg_power; +} + +void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ieee80211_chanctx_conf *ctx) +{ + struct ath12k_base *ab = ar->ab; + struct ath12k_reg_tpc_power_info *reg_tpc_info = &arvif->reg_tpc_info; + struct ieee80211_bss_conf *bss_conf = ath12k_mac_get_link_bss_conf(arvif); + struct ieee80211_channel *chan, *temp_chan; + u8 pwr_lvl_idx, num_pwr_levels, pwr_reduction; + bool is_psd_power = false, is_tpe_present = false; + s8 max_tx_power[ATH12K_NUM_PWR_LEVELS], + psd_power, tx_power, eirp_power; + u16 start_freq, center_freq; + + chan = ctx->def.chan; + start_freq = ath12k_mac_get_6ghz_start_frequency(&ctx->def); + pwr_reduction = bss_conf->pwr_reduction; + + if (arvif->reg_tpc_info.num_pwr_levels) { + is_tpe_present = true; + num_pwr_levels = arvif->reg_tpc_info.num_pwr_levels; + } else { + num_pwr_levels = ath12k_mac_get_num_pwr_levels(&ctx->def); + } + + for (pwr_lvl_idx = 0; pwr_lvl_idx < num_pwr_levels; pwr_lvl_idx++) { + /* STA received TPE IE*/ + if (is_tpe_present) { + /* local power is PSD power*/ + if (chan->flags & IEEE80211_CHAN_PSD) { + /* Connecting AP is psd power */ + if (reg_tpc_info->is_psd_power) { + is_psd_power = true; + ath12k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + psd_power = temp_chan->psd; + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = + min_t(s8, + psd_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + /* Connecting AP is not psd power */ + } else { + ath12k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + psd_power = temp_chan->psd; + /* convert psd power to EIRP power based + * on channel width + */ + tx_power = + min_t(s8, tx_power, + psd_power + 13 + pwr_lvl_idx * 3); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + /* local power is not PSD power */ + } else { + /* Connecting AP is psd power */ + if (reg_tpc_info->is_psd_power) { + is_psd_power = true; + ath12k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = + reg_tpc_info->tpe[pwr_lvl_idx]; + /* Connecting AP is not psd power */ + } else { + ath12k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + max_tx_power[pwr_lvl_idx] = + min_t(s8, + tx_power, + reg_tpc_info->tpe[pwr_lvl_idx]); + } + } + /* STA not received TPE IE */ + } else { + /* local power is PSD power*/ + if (chan->flags & IEEE80211_CHAN_PSD) { + is_psd_power = true; + ath12k_mac_get_psd_channel(ar, 20, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &tx_power); + psd_power = temp_chan->psd; + eirp_power = tx_power; + max_tx_power[pwr_lvl_idx] = psd_power; + } else { + ath12k_mac_get_eirp_power(ar, + &start_freq, + ¢er_freq, + pwr_lvl_idx, + &temp_chan, + &ctx->def, + &tx_power); + max_tx_power[pwr_lvl_idx] = tx_power; + } + } + + if (is_psd_power) { + /* If AP local power constraint is present */ + if (pwr_reduction) + eirp_power = eirp_power - pwr_reduction; + + /* If firmware updated max tx power is non zero, then take + * the min of firmware updated ap tx power + * and max power derived from above mentioned parameters. + */ + ath12k_dbg(ab, ATH12K_DBG_MAC, + "eirp power : %d firmware report power : %d\n", + eirp_power, ar->max_allowed_tx_power); + /* Firmware reports lower max_allowed_tx_power during vdev + * start response. In case of 6 GHz, firmware is not aware + * of EIRP power unless driver sets EIRP power through WMI + * TPC command. So radio which does not support idle power + * save can set maximum calculated EIRP power directly to + * firmware through TPC command without min comparison with + * vdev start response's max_allowed_tx_power. + */ + if (ar->max_allowed_tx_power && ab->hw_params->idle_ps) + eirp_power = min_t(s8, + eirp_power, + ar->max_allowed_tx_power); + } else { + /* If AP local power constraint is present */ + if (pwr_reduction) + max_tx_power[pwr_lvl_idx] = + max_tx_power[pwr_lvl_idx] - pwr_reduction; + /* If firmware updated max tx power is non zero, then take + * the min of firmware updated ap tx power + * and max power derived from above mentioned parameters. + */ + if (ar->max_allowed_tx_power && ab->hw_params->idle_ps) + max_tx_power[pwr_lvl_idx] = + min_t(s8, + max_tx_power[pwr_lvl_idx], + ar->max_allowed_tx_power); + } + reg_tpc_info->chan_power_info[pwr_lvl_idx].chan_cfreq = center_freq; + reg_tpc_info->chan_power_info[pwr_lvl_idx].tx_power = + max_tx_power[pwr_lvl_idx]; + } + + reg_tpc_info->num_pwr_levels = num_pwr_levels; + reg_tpc_info->is_psd_power = is_psd_power; + reg_tpc_info->eirp_power = eirp_power; + reg_tpc_info->ap_power_type = + ath12k_reg_ap_pwr_convert(bss_conf->power_type); +} + +static void ath12k_mac_parse_tx_pwr_env(struct ath12k *ar, + struct ath12k_link_vif *arvif) +{ + struct ieee80211_bss_conf *bss_conf = ath12k_mac_get_link_bss_conf(arvif); + struct ath12k_reg_tpc_power_info *tpc_info = &arvif->reg_tpc_info; + struct ieee80211_parsed_tpe_eirp *local_non_psd, *reg_non_psd; + struct ieee80211_parsed_tpe_psd *local_psd, *reg_psd; + struct ieee80211_parsed_tpe *tpe = &bss_conf->tpe; + enum wmi_reg_6g_client_type client_type; + struct ath12k_reg_info *reg_info; + struct ath12k_base *ab = ar->ab; + bool psd_valid, non_psd_valid; + int i; + + reg_info = ab->reg_info[ar->pdev_idx]; + client_type = reg_info->client_type; + + local_psd = &tpe->psd_local[client_type]; + reg_psd = &tpe->psd_reg_client[client_type]; + local_non_psd = &tpe->max_local[client_type]; + reg_non_psd = &tpe->max_reg_client[client_type]; + + psd_valid = local_psd->valid | reg_psd->valid; + non_psd_valid = local_non_psd->valid | reg_non_psd->valid; + + if (!psd_valid && !non_psd_valid) { + ath12k_warn(ab, + "no transmit power envelope match client power type %d\n", + client_type); + return; + }; + + if (psd_valid) { + tpc_info->is_psd_power = true; + + tpc_info->num_pwr_levels = max(local_psd->count, + reg_psd->count); + if (tpc_info->num_pwr_levels > ATH12K_NUM_PWR_LEVELS) + tpc_info->num_pwr_levels = ATH12K_NUM_PWR_LEVELS; + + for (i = 0; i < tpc_info->num_pwr_levels; i++) { + tpc_info->tpe[i] = min(local_psd->power[i], + reg_psd->power[i]) / 2; + ath12k_dbg(ab, ATH12K_DBG_MAC, + "TPE PSD power[%d] : %d\n", + i, tpc_info->tpe[i]); + } + } else { + tpc_info->is_psd_power = false; + tpc_info->eirp_power = 0; + + tpc_info->num_pwr_levels = max(local_non_psd->count, + reg_non_psd->count); + if (tpc_info->num_pwr_levels > ATH12K_NUM_PWR_LEVELS) + tpc_info->num_pwr_levels = ATH12K_NUM_PWR_LEVELS; + + for (i = 0; i < tpc_info->num_pwr_levels; i++) { + tpc_info->tpe[i] = min(local_non_psd->power[i], + reg_non_psd->power[i]) / 2; + ath12k_dbg(ab, ATH12K_DBG_MAC, + "non PSD power[%d] : %d\n", + i, tpc_info->tpe[i]); + } + } +} + static int ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -9209,8 +10209,8 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, ar = ath12k_mac_assign_vif_to_vdev(hw, arvif, ctx); if (!ar) { - ath12k_warn(arvif->ar->ab, "failed to assign chanctx for vif %pM link id %u link vif is already started", - vif->addr, link_id); + ath12k_hw_warn(ah, "failed to assign chanctx for vif %pM link id %u link vif is already started", + vif->addr, link_id); return -EINVAL; } @@ -9220,6 +10220,11 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, "mac chanctx assign ptr %p vdev_id %i\n", ctx, arvif->vdev_id); + if (ath12k_wmi_supports_6ghz_cc_ext(ar) && + ctx->def.chan->band == NL80211_BAND_6GHZ && + ahvif->vdev_type == WMI_VDEV_TYPE_STA) + ath12k_mac_parse_tx_pwr_env(ar, arvif); + arvif->punct_bitmap = ctx->def.punctured; /* for some targets bss peer must be created before vdev_start */ @@ -9227,7 +10232,6 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, ahvif->vdev_type != WMI_VDEV_TYPE_AP && ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR && !ath12k_peer_exist_by_vdev_id(ab, arvif->vdev_id)) { - memcpy(&arvif->chanctx, ctx, sizeof(*ctx)); ret = 0; goto out; } @@ -9239,8 +10243,10 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, if (ahvif->vdev_type == WMI_VDEV_TYPE_MONITOR) { ret = ath12k_mac_monitor_start(ar); - if (ret) + if (ret) { + ath12k_mac_monitor_vdev_delete(ar); goto out; + } arvif->is_started = true; goto out; @@ -9254,9 +10260,6 @@ ath12k_mac_op_assign_vif_chanctx(struct ieee80211_hw *hw, goto out; } - if (ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR && ar->monitor_vdev_created) - ath12k_mac_monitor_start(ar); - arvif->is_started = true; /* TODO: Setup ps and cts/rts protection */ @@ -9319,12 +10322,18 @@ ath12k_mac_op_unassign_vif_chanctx(struct ieee80211_hw *hw, } arvif->is_started = false; - if (ahvif->vdev_type != WMI_VDEV_TYPE_MONITOR && - ar->num_started_vdevs == 1 && ar->monitor_vdev_created) - ath12k_mac_monitor_stop(ar); + if (test_bit(WMI_TLV_SERVICE_11D_OFFLOAD, ab->wmi_ab.svc_map) && + ahvif->vdev_type == WMI_VDEV_TYPE_STA && + ahvif->vdev_subtype == WMI_VDEV_SUBTYPE_NONE && + ar->state_11d != ATH12K_11D_PREPARING) { + reinit_completion(&ar->completed_11d_scan); + ar->state_11d = ATH12K_11D_PREPARING; + } - ath12k_mac_remove_link_interface(hw, arvif); - ath12k_mac_unassign_link_vif(arvif); + if (ar->scan.arvif == arvif && ar->scan.state == ATH12K_SCAN_RUNNING) { + ath12k_scan_abort(ar); + ar->scan.arvif = NULL; + } } static int @@ -9886,6 +10895,14 @@ ath12k_mac_op_reconfig_complete(struct ieee80211_hw *hw, ath12k_warn(ar->ab, "pdev %d successfully recovered\n", ar->pdev->pdev_id); + if (ar->ab->hw_params->current_cc_support && + ar->alpha2[0] != 0 && ar->alpha2[1] != 0) { + struct wmi_set_current_country_arg arg = {}; + + memcpy(&arg.alpha2, ar->alpha2, 2); + ath12k_wmi_send_set_current_country_cmd(ar, &arg); + } + if (ab->is_reset) { recovery_count = atomic_inc_return(&ab->recovery_count); @@ -10023,11 +11040,21 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, struct station_info *sinfo) { struct ath12k_sta *ahsta = ath12k_sta_to_ahsta(sta); + struct ath12k_fw_stats_req_params params = {}; struct ath12k_link_sta *arsta; + struct ath12k *ar; + s8 signal; + bool db2dbm; lockdep_assert_wiphy(hw->wiphy); arsta = &ahsta->deflink; + ar = ath12k_get_ar_by_vif(hw, vif, arsta->link_id); + if (!ar) + return; + + db2dbm = test_bit(WMI_TLV_SERVICE_HW_DB2DBM_CONVERSION_SUPPORT, + ar->ab->wmi_ab.svc_map); sinfo->rx_duration = arsta->rx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_DURATION); @@ -10035,25 +11062,46 @@ static void ath12k_mac_op_sta_statistics(struct ieee80211_hw *hw, sinfo->tx_duration = arsta->tx_duration; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_DURATION); - if (!arsta->txrate.legacy && !arsta->txrate.nss) - return; - - if (arsta->txrate.legacy) { - sinfo->txrate.legacy = arsta->txrate.legacy; - } else { - sinfo->txrate.mcs = arsta->txrate.mcs; - sinfo->txrate.nss = arsta->txrate.nss; - sinfo->txrate.bw = arsta->txrate.bw; - sinfo->txrate.he_gi = arsta->txrate.he_gi; - sinfo->txrate.he_dcm = arsta->txrate.he_dcm; - sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; + if (arsta->txrate.legacy || arsta->txrate.nss) { + if (arsta->txrate.legacy) { + sinfo->txrate.legacy = arsta->txrate.legacy; + } else { + sinfo->txrate.mcs = arsta->txrate.mcs; + sinfo->txrate.nss = arsta->txrate.nss; + sinfo->txrate.bw = arsta->txrate.bw; + sinfo->txrate.he_gi = arsta->txrate.he_gi; + sinfo->txrate.he_dcm = arsta->txrate.he_dcm; + sinfo->txrate.he_ru_alloc = arsta->txrate.he_ru_alloc; + sinfo->txrate.eht_gi = arsta->txrate.eht_gi; + sinfo->txrate.eht_ru_alloc = arsta->txrate.eht_ru_alloc; + } + sinfo->txrate.flags = arsta->txrate.flags; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); } - sinfo->txrate.flags = arsta->txrate.flags; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); /* TODO: Use real NF instead of default one. */ - sinfo->signal = arsta->rssi_comb + ATH12K_DEFAULT_NOISE_FLOOR; - sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + signal = arsta->rssi_comb; + + params.pdev_id = ar->pdev->pdev_id; + params.vdev_id = 0; + params.stats_id = WMI_REQUEST_VDEV_STAT; + + if (!signal && + ahsta->ahvif->vdev_type == WMI_VDEV_TYPE_STA && + !(ath12k_mac_get_fw_stats(ar, ¶ms))) + signal = arsta->rssi_beacon; + + if (signal) { + sinfo->signal = db2dbm ? signal : signal + ATH12K_DEFAULT_NOISE_FLOOR; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); + } + + sinfo->signal_avg = ewma_avg_rssi_read(&arsta->avg_rssi); + + if (!db2dbm) + sinfo->signal_avg += ATH12K_DEFAULT_NOISE_FLOOR; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); } static int ath12k_mac_op_cancel_remain_on_channel(struct ieee80211_hw *hw, @@ -10100,7 +11148,7 @@ static int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw, return -EINVAL; /* check if any of the links of ML VIF is already started on - * radio(ar) correpsondig to given scan frequency and use it, + * radio(ar) corresponding to given scan frequency and use it, * if not use deflink(link 0) for scan purpose. */ @@ -10284,6 +11332,7 @@ static const struct ieee80211_ops ath12k_ops = { .assign_vif_chanctx = ath12k_mac_op_assign_vif_chanctx, .unassign_vif_chanctx = ath12k_mac_op_unassign_vif_chanctx, .switch_vif_chanctx = ath12k_mac_op_switch_vif_chanctx, + .get_txpower = ath12k_mac_op_get_txpower, .set_rts_threshold = ath12k_mac_op_set_rts_threshold, .set_frag_threshold = ath12k_mac_op_set_frag_threshold, .set_bitrate_mask = ath12k_mac_op_set_bitrate_mask, @@ -10299,8 +11348,37 @@ static const struct ieee80211_ops ath12k_ops = { .resume = ath12k_wow_op_resume, .set_wakeup = ath12k_wow_op_set_wakeup, #endif +#ifdef CONFIG_ATH12K_DEBUGFS + .vif_add_debugfs = ath12k_debugfs_op_vif_add, +#endif + CFG80211_TESTMODE_CMD(ath12k_tm_cmd) +#ifdef CONFIG_ATH12K_DEBUGFS + .link_sta_add_debugfs = ath12k_debugfs_link_sta_op_add, +#endif }; +void ath12k_mac_update_freq_range(struct ath12k *ar, + u32 freq_low, u32 freq_high) +{ + if (!(freq_low && freq_high)) + return; + + if (ar->freq_range.start_freq || ar->freq_range.end_freq) { + ar->freq_range.start_freq = min(ar->freq_range.start_freq, + MHZ_TO_KHZ(freq_low)); + ar->freq_range.end_freq = max(ar->freq_range.end_freq, + MHZ_TO_KHZ(freq_high)); + } else { + ar->freq_range.start_freq = MHZ_TO_KHZ(freq_low); + ar->freq_range.end_freq = MHZ_TO_KHZ(freq_high); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_MAC, + "mac pdev %u freq limit updated. New range %u->%u MHz\n", + ar->pdev->pdev_id, KHZ_TO_MHZ(ar->freq_range.start_freq), + KHZ_TO_MHZ(ar->freq_range.end_freq)); +} + static void ath12k_mac_update_ch_list(struct ath12k *ar, struct ieee80211_supported_band *band, u32 freq_low, u32 freq_high) @@ -10315,9 +11393,6 @@ static void ath12k_mac_update_ch_list(struct ath12k *ar, band->channels[i].center_freq > freq_high) band->channels[i].flags |= IEEE80211_CHAN_DISABLED; } - - ar->freq_range.start_freq = MHZ_TO_KHZ(freq_low); - ar->freq_range.end_freq = MHZ_TO_KHZ(freq_high); } static u32 ath12k_get_phy_id(struct ath12k *ar, u32 band) @@ -10325,10 +11400,10 @@ static u32 ath12k_get_phy_id(struct ath12k *ar, u32 band) struct ath12k_pdev *pdev = ar->pdev; struct ath12k_pdev_cap *pdev_cap = &pdev->cap; - if (band == WMI_HOST_WLAN_2G_CAP) + if (band == WMI_HOST_WLAN_2GHZ_CAP) return pdev_cap->band[NL80211_BAND_2GHZ].phy_id; - if (band == WMI_HOST_WLAN_5G_CAP) + if (band == WMI_HOST_WLAN_5GHZ_CAP) return pdev_cap->band[NL80211_BAND_5GHZ].phy_id; ath12k_warn(ar->ab, "unsupported phy cap:%d\n", band); @@ -10342,18 +11417,19 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, { struct ieee80211_supported_band *band; struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap; + struct ath12k_base *ab = ar->ab; + u32 phy_id, freq_low, freq_high; struct ath12k_hw *ah = ar->ah; void *channels; - u32 phy_id; BUILD_BUG_ON((ARRAY_SIZE(ath12k_2ghz_channels) + ARRAY_SIZE(ath12k_5ghz_channels) + ARRAY_SIZE(ath12k_6ghz_channels)) != ATH12K_NUM_CHANS); - reg_cap = &ar->ab->hal_reg_cap[ar->pdev_idx]; + reg_cap = &ab->hal_reg_cap[ar->pdev_idx]; - if (supported_bands & WMI_HOST_WLAN_2G_CAP) { + if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { channels = kmemdup(ath12k_2ghz_channels, sizeof(ath12k_2ghz_channels), GFP_KERNEL); @@ -10368,17 +11444,25 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->bitrates = ath12k_g_rates; bands[NL80211_BAND_2GHZ] = band; - if (ar->ab->hw_params->single_pdev_only) { - phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_2G_CAP); - reg_cap = &ar->ab->hal_reg_cap[phy_id]; + if (ab->hw_params->single_pdev_only) { + phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_2GHZ_CAP); + reg_cap = &ab->hal_reg_cap[phy_id]; } + + freq_low = max(reg_cap->low_2ghz_chan, + ab->reg_freq_2ghz.start_freq); + freq_high = min(reg_cap->high_2ghz_chan, + ab->reg_freq_2ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_2ghz_chan, reg_cap->high_2ghz_chan); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); } - if (supported_bands & WMI_HOST_WLAN_5G_CAP) { - if (reg_cap->high_5ghz_chan >= ATH12K_MIN_6G_FREQ) { + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP) { + if (reg_cap->high_5ghz_chan >= ATH12K_MIN_6GHZ_FREQ) { channels = kmemdup(ath12k_6ghz_channels, sizeof(ath12k_6ghz_channels), GFP_KERNEL); if (!channels) { @@ -10394,13 +11478,21 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->n_bitrates = ath12k_a_rates_size; band->bitrates = ath12k_a_rates; bands[NL80211_BAND_6GHZ] = band; + + freq_low = max(reg_cap->low_5ghz_chan, + ab->reg_freq_6ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, + ab->reg_freq_6ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); ah->use_6ghz_regd = true; } - if (reg_cap->low_5ghz_chan < ATH12K_MIN_6G_FREQ) { + if (reg_cap->low_5ghz_chan < ATH12K_MIN_6GHZ_FREQ) { channels = kmemdup(ath12k_5ghz_channels, sizeof(ath12k_5ghz_channels), GFP_KERNEL); @@ -10418,14 +11510,21 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar, band->bitrates = ath12k_a_rates; bands[NL80211_BAND_5GHZ] = band; - if (ar->ab->hw_params->single_pdev_only) { - phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5G_CAP); - reg_cap = &ar->ab->hal_reg_cap[phy_id]; + if (ab->hw_params->single_pdev_only) { + phy_id = ath12k_get_phy_id(ar, WMI_HOST_WLAN_5GHZ_CAP); + reg_cap = &ab->hal_reg_cap[phy_id]; } + freq_low = max(reg_cap->low_5ghz_chan, + ab->reg_freq_5ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, + ab->reg_freq_5ghz.end_freq); + ath12k_mac_update_ch_list(ar, band, reg_cap->low_5ghz_chan, reg_cap->high_5ghz_chan); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); } } @@ -10525,13 +11624,18 @@ ath12k_mac_setup_radio_iface_comb(struct ath12k *ar, comb[0].limits = limits; comb[0].n_limits = n_limits; comb[0].max_interfaces = max_interfaces; - comb[0].num_different_channels = 1; comb[0].beacon_int_infra_match = true; comb[0].beacon_int_min_gcd = 100; - comb[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | - BIT(NL80211_CHAN_WIDTH_20) | - BIT(NL80211_CHAN_WIDTH_40) | - BIT(NL80211_CHAN_WIDTH_80); + + if (ar->ab->hw_params->single_pdev_only) { + comb[0].num_different_channels = 2; + } else { + comb[0].num_different_channels = 1; + comb[0].radar_detect_widths = BIT(NL80211_CHAN_WIDTH_20_NOHT) | + BIT(NL80211_CHAN_WIDTH_20) | + BIT(NL80211_CHAN_WIDTH_40) | + BIT(NL80211_CHAN_WIDTH_80); + } return 0; } @@ -10756,6 +11860,7 @@ static void ath12k_mac_hw_unregister(struct ath12k_hw *ah) for_each_ar(ah, ar, i) { cancel_work_sync(&ar->regd_update_work); ath12k_debugfs_unregister(ar); + ath12k_fw_stats_reset(ar); } ieee80211_unregister_hw(hw); @@ -10904,6 +12009,7 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ieee80211_hw_set(hw, QUEUE_CONTROL); ieee80211_hw_set(hw, SUPPORTS_TX_FRAG); ieee80211_hw_set(hw, REPORTS_LOW_ACK); + ieee80211_hw_set(hw, NO_VIRTUAL_MONITOR); if ((ht_cap & WMI_HT_CAP_ENABLED) || is_6ghz) { ieee80211_hw_set(hw, AMPDU_AGGREGATION); @@ -10950,6 +12056,8 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) ath12k_iftypes_ext_capa[2].eml_capabilities = cap->eml_cap; ath12k_iftypes_ext_capa[2].mld_capa_and_ops = cap->mld_cap; wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + + ieee80211_hw_set(hw, MLO_MCAST_MULTI_LINK_TX); } hw->queues = ATH12K_HW_MAX_QUEUES; @@ -11030,6 +12138,19 @@ static int ath12k_mac_hw_register(struct ath12k_hw *ah) goto err_unregister_hw; } + if (ar->ab->hw_params->current_cc_support && ab->new_alpha2[0]) { + struct wmi_set_current_country_arg current_cc = {}; + + memcpy(¤t_cc.alpha2, ab->new_alpha2, 2); + memcpy(&ar->alpha2, ab->new_alpha2, 2); + ret = ath12k_wmi_send_set_current_country_cmd(ar, ¤t_cc); + if (ret) + ath12k_warn(ar->ab, + "failed set cc code for mac register: %d\n", + ret); + } + + ath12k_fw_stats_init(ar); ath12k_debugfs_register(ar); } @@ -11077,6 +12198,7 @@ static void ath12k_mac_setup(struct ath12k *ar) ar->num_tx_chains = hweight32(pdev->cap.tx_chain_mask); ar->num_rx_chains = hweight32(pdev->cap.rx_chain_mask); ar->scan.arvif = NULL; + ar->vdev_id_11d_scan = ATH12K_11D_INVALID_VDEV_ID; spin_lock_init(&ar->data_lock); INIT_LIST_HEAD(&ar->arvifs); @@ -11092,6 +12214,7 @@ static void ath12k_mac_setup(struct ath12k *ar) init_completion(&ar->scan.completed); init_completion(&ar->scan.on_channel); init_completion(&ar->mlo_setup_done); + init_completion(&ar->completed_11d_scan); INIT_DELAYED_WORK(&ar->scan.timeout, ath12k_scan_timeout_work); wiphy_work_init(&ar->scan.vdev_clean_wk, ath12k_scan_vdev_clean_work); @@ -11099,6 +12222,10 @@ static void ath12k_mac_setup(struct ath12k *ar) wiphy_work_init(&ar->wmi_mgmt_tx_work, ath12k_mgmt_over_wmi_tx_work); skb_queue_head_init(&ar->wmi_mgmt_tx_queue); + + ar->monitor_vdev_id = -1; + ar->monitor_vdev_created = false; + ar->monitor_started = false; } static int __ath12k_mac_mlo_setup(struct ath12k *ar) @@ -11133,6 +12260,9 @@ static int __ath12k_mac_mlo_setup(struct ath12k *ar) } } + if (num_link == 0) + return 0; + mlo.group_id = cpu_to_le32(ag->id); mlo.partner_link_id = partner_link_id; mlo.num_partner_links = num_link; @@ -11162,10 +12292,16 @@ static int __ath12k_mac_mlo_teardown(struct ath12k *ar) { struct ath12k_base *ab = ar->ab; int ret; + u8 num_link; if (test_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags)) return 0; + num_link = ath12k_get_num_partner_link(ar); + + if (num_link == 0) + return 0; + ret = ath12k_wmi_mlo_teardown(ar); if (ret) { ath12k_warn(ab, "failed to send MLO teardown WMI command for pdev %d: %d\n", @@ -11244,7 +12380,6 @@ void ath12k_mac_mlo_teardown(struct ath12k_hw_group *ag) int ath12k_mac_register(struct ath12k_hw_group *ag) { - struct ath12k_base *ab = ag->ab[0]; struct ath12k_hw *ah; int i; int ret; @@ -11257,8 +12392,6 @@ int ath12k_mac_register(struct ath12k_hw_group *ag) goto err; } - set_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); - return 0; err: @@ -11275,12 +12408,9 @@ err: void ath12k_mac_unregister(struct ath12k_hw_group *ag) { - struct ath12k_base *ab = ag->ab[0]; struct ath12k_hw *ah; int i; - clear_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags); - for (i = ag->num_hw - 1; i >= 0; i--) { ah = ath12k_ag_to_ah(ag, i); if (!ah) @@ -11394,6 +12524,7 @@ int ath12k_mac_allocate(struct ath12k_hw_group *ag) if (!ab) continue; + ath12k_debugfs_pdev_create(ab); ath12k_mac_set_device_defaults(ab); total_radio += ab->num_radios; } diff --git a/drivers/net/wireless/ath/ath12k/mac.h b/drivers/net/wireless/ath/ath12k/mac.h index 3594729b6397..e6e74b45bfa4 100644 --- a/drivers/net/wireless/ath/ath12k/mac.h +++ b/drivers/net/wireless/ath/ath12k/mac.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_MAC_H @@ -33,6 +33,9 @@ struct ath12k_generic_iter { #define ATH12K_KEEPALIVE_MAX_IDLE 3895 #define ATH12K_KEEPALIVE_MAX_UNRESPONSIVE 3900 +#define ATH12K_PDEV_TX_POWER_INVALID ((u32)-1) +#define ATH12K_PDEV_TX_POWER_REFRESH_TIME_MSECS 5000 /* msecs */ + /* FIXME: should these be in ieee80211.h? */ #define IEEE80211_VHT_MCS_SUPPORT_0_11_MASK GENMASK(23, 16) #define IEEE80211_DISABLE_VHT_MCS_SUPPORT_0_11 BIT(24) @@ -64,8 +67,55 @@ struct ath12k_mac_get_any_chanctx_conf_arg { struct ieee80211_chanctx_conf *chanctx_conf; }; +/** + * struct ath12k_chan_power_info - TPE containing power info per channel chunk + * @chan_cfreq: channel center freq (MHz) + * e.g. + * channel 37/20 MHz, it is 6135 + * channel 37/40 MHz, it is 6125 + * channel 37/80 MHz, it is 6145 + * channel 37/160 MHz, it is 6185 + * @tx_power: transmit power (dBm) + */ +struct ath12k_chan_power_info { + u16 chan_cfreq; + s8 tx_power; +}; + +/* ath12k only deals with 320 MHz, so 16 subchannels */ +#define ATH12K_NUM_PWR_LEVELS 16 + +/** + * struct ath12k_reg_tpc_power_info - regulatory TPC power info + * @is_psd_power: is PSD power or not + * @eirp_power: Maximum EIRP power (dBm), valid only if power is PSD + * @ap_power_type: type of power (SP/LPI/VLP) + * @num_pwr_levels: number of power levels + * @reg_max: Array of maximum TX power (dBm) per PSD value + * @ap_constraint_power: AP constraint power (dBm) + * @tpe: TPE values processed from TPE IE + * @chan_power_info: power info to send to firmware + */ +struct ath12k_reg_tpc_power_info { + bool is_psd_power; + u8 eirp_power; + enum wmi_reg_6g_ap_type ap_power_type; + u8 num_pwr_levels; + u8 reg_max[ATH12K_NUM_PWR_LEVELS]; + u8 ap_constraint_power; + s8 tpe[ATH12K_NUM_PWR_LEVELS]; + struct ath12k_chan_power_info chan_power_info[ATH12K_NUM_PWR_LEVELS]; +}; + extern const struct htt_rx_ring_tlv_filter ath12k_mac_mon_status_filter_default; +#define ATH12K_SCAN_11D_INTERVAL 600000 +#define ATH12K_11D_INVALID_VDEV_ID 0xFFFF + +void ath12k_mac_11d_scan_start(struct ath12k *ar, u32 vdev_id); +void ath12k_mac_11d_scan_stop(struct ath12k *ar); +void ath12k_mac_11d_scan_stop_all(struct ath12k_base *ab); + void ath12k_mac_destroy(struct ath12k_hw_group *ag); void ath12k_mac_unregister(struct ath12k_hw_group *ag); int ath12k_mac_register(struct ath12k_hw_group *ag); @@ -108,5 +158,17 @@ int ath12k_mac_vdev_stop(struct ath12k_link_vif *arvif); void ath12k_mac_get_any_chanctx_conf_iter(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *conf, void *data); - +u16 ath12k_mac_he_convert_tones_to_ru_tones(u16 tones); +enum nl80211_eht_ru_alloc ath12k_mac_eht_ru_tones_to_nl80211_eht_ru_alloc(u16 ru_tones); +enum nl80211_eht_gi ath12k_mac_eht_gi_to_nl80211_eht_gi(u8 sgi); +struct ieee80211_bss_conf *ath12k_mac_get_link_bss_conf(struct ath12k_link_vif *arvif); +struct ath12k *ath12k_get_ar_by_vif(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u8 link_id); +int ath12k_mac_get_fw_stats(struct ath12k *ar, struct ath12k_fw_stats_req_params *param); +void ath12k_mac_update_freq_range(struct ath12k *ar, + u32 freq_low, u32 freq_high); +void ath12k_mac_fill_reg_tpc_info(struct ath12k *ar, + struct ath12k_link_vif *arvif, + struct ieee80211_chanctx_conf *ctx); #endif diff --git a/drivers/net/wireless/ath/ath12k/mhi.c b/drivers/net/wireless/ath/ath12k/mhi.c index 2f6d14382ed7..08f44baf182a 100644 --- a/drivers/net/wireless/ath/ath12k/mhi.c +++ b/drivers/net/wireless/ath/ath12k/mhi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/msi.h> @@ -285,8 +285,11 @@ static void ath12k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl, break; } - if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) + if (!(test_bit(ATH12K_FLAG_UNREGISTERING, &ab->dev_flags))) { + set_bit(ATH12K_FLAG_CRASH_FLUSH, &ab->dev_flags); + set_bit(ATH12K_FLAG_RECOVERY, &ab->dev_flags); queue_work(ab->workqueue_aux, &ab->reset_work); + } break; default: break; @@ -379,7 +382,7 @@ int ath12k_mhi_register(struct ath12k_pci *ab_pci) mhi_ctrl->irq_flags = IRQF_SHARED | IRQF_NOBALANCING; mhi_ctrl->iova_start = 0; - mhi_ctrl->iova_stop = 0xffffffff; + mhi_ctrl->iova_stop = ab_pci->dma_mask; mhi_ctrl->sbl_size = SZ_512K; mhi_ctrl->seg_len = SZ_512K; mhi_ctrl->fbc_download = true; diff --git a/drivers/net/wireless/ath/ath12k/pci.c b/drivers/net/wireless/ath/ath12k/pci.c index 06cff3849ab8..489d546390fc 100644 --- a/drivers/net/wireless/ath/ath12k/pci.c +++ b/drivers/net/wireless/ath/ath12k/pci.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/module.h> @@ -17,7 +17,7 @@ #include "debug.h" #define ATH12K_PCI_BAR_NUM 0 -#define ATH12K_PCI_DMA_MASK 32 +#define ATH12K_PCI_DMA_MASK 36 #define ATH12K_PCI_IRQ_CE0_OFFSET 3 @@ -483,8 +483,11 @@ static void __ath12k_pci_ext_irq_disable(struct ath12k_base *ab) ath12k_pci_ext_grp_disable(irq_grp); - napi_synchronize(&irq_grp->napi); - napi_disable(&irq_grp->napi); + if (irq_grp->napi_enabled) { + napi_synchronize(&irq_grp->napi); + napi_disable(&irq_grp->napi); + irq_grp->napi_enabled = false; + } } } @@ -597,7 +600,8 @@ static int ath12k_pci_ext_irq_config(struct ath12k_base *ab) ab->hw_params->ring_mask->rx_wbm_rel[i] || ab->hw_params->ring_mask->reo_status[i] || ab->hw_params->ring_mask->host2rxdma[i] || - ab->hw_params->ring_mask->rx_mon_dest[i]) { + ab->hw_params->ring_mask->rx_mon_dest[i] || + ab->hw_params->ring_mask->rx_mon_status[i]) { num_irq = 1; } @@ -646,7 +650,7 @@ static int ath12k_pci_set_irq_affinity_hint(struct ath12k_pci *ab_pci, if (test_bit(ATH12K_PCI_FLAG_MULTI_MSI_VECTORS, &ab_pci->flags)) return 0; - return irq_set_affinity_hint(ab_pci->pdev->irq, m); + return irq_set_affinity_and_hint(ab_pci->pdev->irq, m); } static int ath12k_pci_config_irq(struct ath12k_base *ab) @@ -715,7 +719,7 @@ static void ath12k_pci_init_qmi_ce_config(struct ath12k_base *ab) cfg->svc_to_ce_map_len = ab->hw_params->svc_to_ce_map_len; ab->qmi.service_ins_id = ab->hw_params->qmi_service_ins_id; - if (test_bit(ATH12K_FW_FEATURE_MULTI_QRTR_ID, ab->fw.fw_features)) { + if (ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MULTI_QRTR_ID)) { ab_pci->qmi_instance = u32_encode_bits(pci_domain_nr(bus), DOMAIN_NUMBER_MASK) | u32_encode_bits(bus->number, BUS_NUMBER_MASK); @@ -874,13 +878,9 @@ static int ath12k_pci_claim(struct ath12k_pci *ab_pci, struct pci_dev *pdev) goto disable_device; } - ret = dma_set_mask_and_coherent(&pdev->dev, - DMA_BIT_MASK(ATH12K_PCI_DMA_MASK)); - if (ret) { - ath12k_err(ab, "failed to set pci dma mask to %d: %d\n", - ATH12K_PCI_DMA_MASK, ret); - goto release_region; - } + ab_pci->dma_mask = DMA_BIT_MASK(ATH12K_PCI_DMA_MASK); + dma_set_mask(&pdev->dev, ab_pci->dma_mask); + dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); pci_set_master(pdev); @@ -1114,7 +1114,11 @@ void ath12k_pci_ext_irq_enable(struct ath12k_base *ab) for (i = 0; i < ATH12K_EXT_IRQ_GRP_NUM_MAX; i++) { struct ath12k_ext_irq_grp *irq_grp = &ab->ext_irq_grp[i]; - napi_enable(&irq_grp->napi); + if (!irq_grp->napi_enabled) { + napi_enable(&irq_grp->napi); + irq_grp->napi_enabled = true; + } + ath12k_pci_ext_grp_enable(irq_grp); } @@ -1465,7 +1469,7 @@ int ath12k_pci_power_up(struct ath12k_base *ab) ath12k_pci_msi_enable(ab_pci); - if (test_bit(ATH12K_FW_FEATURE_MULTI_QRTR_ID, ab->fw.fw_features)) + if (ath12k_fw_feature_supported(ab, ATH12K_FW_FEATURE_MULTI_QRTR_ID)) ath12k_pci_update_qrtr_node_id(ab); ret = ath12k_mhi_start(ab_pci); @@ -1484,6 +1488,9 @@ void ath12k_pci_power_down(struct ath12k_base *ab, bool is_suspend) { struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); + if (!test_bit(ATH12K_PCI_FLAG_INIT_DONE, &ab_pci->flags)) + return; + /* restore aspm in case firmware bootup fails */ ath12k_pci_aspm_restore(ab_pci); @@ -1561,6 +1568,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev, ab_pci->ab = ab; ab_pci->pdev = pdev; ab->hif.ops = &ath12k_pci_hif_ops; + ab->fw_mode = ATH12K_FIRMWARE_MODE_NORMAL; pci_set_drvdata(pdev, ab); spin_lock_init(&ab_pci->window_lock); @@ -1689,6 +1697,8 @@ static int ath12k_pci_probe(struct pci_dev *pdev, return 0; err_free_irq: + /* __free_irq() expects the caller to have cleared the affinity hint */ + ath12k_pci_set_irq_affinity_hint(ab_pci, NULL); ath12k_pci_free_irq(ab); err_ce_free: @@ -1700,12 +1710,12 @@ err_hal_srng_deinit: err_mhi_unregister: ath12k_mhi_unregister(ab_pci); -err_pci_msi_free: - ath12k_pci_msi_free(ab_pci); - err_irq_affinity_cleanup: ath12k_pci_set_irq_affinity_hint(ab_pci, NULL); +err_pci_msi_free: + ath12k_pci_msi_free(ab_pci); + err_pci_free_region: ath12k_pci_free_region(ab_pci); @@ -1724,8 +1734,6 @@ static void ath12k_pci_remove(struct pci_dev *pdev) if (test_bit(ATH12K_FLAG_QMI_FAIL, &ab->dev_flags)) { ath12k_pci_power_down(ab, false); - ath12k_qmi_deinit_service(ab); - ath12k_core_hw_group_unassign(ab); goto qmi_fail; } @@ -1733,10 +1741,11 @@ static void ath12k_pci_remove(struct pci_dev *pdev) cancel_work_sync(&ab->reset_work); cancel_work_sync(&ab->dump_work); - ath12k_core_deinit(ab); - ath12k_fw_unmap(ab); + ath12k_core_hw_group_cleanup(ab->ag); qmi_fail: + ath12k_core_deinit(ab); + ath12k_fw_unmap(ab); ath12k_mhi_unregister(ab_pci); ath12k_pci_free_irq(ab); @@ -1748,13 +1757,34 @@ qmi_fail: ath12k_core_free(ab); } +static void ath12k_pci_hw_group_power_down(struct ath12k_hw_group *ag) +{ + struct ath12k_base *ab; + int i; + + if (!ag) + return; + + mutex_lock(&ag->mutex); + + for (i = 0; i < ag->num_devices; i++) { + ab = ag->ab[i]; + if (!ab) + continue; + + ath12k_pci_power_down(ab, false); + } + + mutex_unlock(&ag->mutex); +} + static void ath12k_pci_shutdown(struct pci_dev *pdev) { struct ath12k_base *ab = pci_get_drvdata(pdev); struct ath12k_pci *ab_pci = ath12k_pci_priv(ab); ath12k_pci_set_irq_affinity_hint(ab_pci, NULL); - ath12k_pci_power_down(ab, false); + ath12k_pci_hw_group_power_down(ab->ag); } static __maybe_unused int ath12k_pci_pm_suspend(struct device *dev) @@ -1821,7 +1851,7 @@ static struct pci_driver ath12k_pci_driver = { .driver.pm = &ath12k_pci_pm_ops, }; -static int ath12k_pci_init(void) +int ath12k_pci_init(void) { int ret; @@ -1834,14 +1864,8 @@ static int ath12k_pci_init(void) return 0; } -module_init(ath12k_pci_init); -static void ath12k_pci_exit(void) +void ath12k_pci_exit(void) { pci_unregister_driver(&ath12k_pci_driver); } - -module_exit(ath12k_pci_exit); - -MODULE_DESCRIPTION("Driver support for Qualcomm Technologies PCIe 802.11be WLAN devices"); -MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/ath/ath12k/pci.h b/drivers/net/wireless/ath/ath12k/pci.h index 31584a7ad80e..0b4c459d6d8e 100644 --- a/drivers/net/wireless/ath/ath12k/pci.h +++ b/drivers/net/wireless/ath/ath12k/pci.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_PCI_H #define ATH12K_PCI_H @@ -116,6 +116,7 @@ struct ath12k_pci { unsigned long irq_flags; const struct ath12k_pci_ops *pci_ops; u32 qmi_instance; + u64 dma_mask; }; static inline struct ath12k_pci *ath12k_pci_priv(struct ath12k_base *ab) @@ -145,4 +146,6 @@ void ath12k_pci_stop(struct ath12k_base *ab); int ath12k_pci_start(struct ath12k_base *ab); int ath12k_pci_power_up(struct ath12k_base *ab); void ath12k_pci_power_down(struct ath12k_base *ab, bool is_suspend); +int ath12k_pci_init(void); +void ath12k_pci_exit(void); #endif /* ATH12K_PCI_H */ diff --git a/drivers/net/wireless/ath/ath12k/peer.c b/drivers/net/wireless/ath/ath12k/peer.c index 792cca8a3fb1..ec7236bbccc0 100644 --- a/drivers/net/wireless/ath/ath12k/peer.c +++ b/drivers/net/wireless/ath/ath12k/peer.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2022, 2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2022, 2024-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include "core.h" @@ -383,6 +383,9 @@ int ath12k_peer_create(struct ath12k *ar, struct ath12k_link_vif *arvif, arvif->ast_idx = peer->hw_peer_id; } + if (vif->type == NL80211_IFTYPE_AP) + peer->ucast_ra_only = true; + if (sta) { ahsta = ath12k_sta_to_ahsta(sta); arsta = wiphy_dereference(ath12k_ar_to_hw(ar)->wiphy, diff --git a/drivers/net/wireless/ath/ath12k/peer.h b/drivers/net/wireless/ath/ath12k/peer.h index 5870ee11a8c7..f3a5e054d2b5 100644 --- a/drivers/net/wireless/ath/ath12k/peer.h +++ b/drivers/net/wireless/ath/ath12k/peer.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_PEER_H @@ -62,6 +62,7 @@ struct ath12k_peer { /* for reference to ath12k_link_sta */ u8 link_id; + bool ucast_ra_only; }; struct ath12k_ml_peer { diff --git a/drivers/net/wireless/ath/ath12k/qmi.c b/drivers/net/wireless/ath/ath12k/qmi.c index 5c3563383fab..99e1fb2910d0 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.c +++ b/drivers/net/wireless/ath/ath12k/qmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/elf.h> @@ -11,6 +11,8 @@ #include "debug.h" #include <linux/of.h> #include <linux/firmware.h> +#include <linux/of_address.h> +#include <linux/ioport.h> #define SLEEP_CLOCK_SELECT_INTERNAL_BIT 0x02 #define HOST_CSTATE_BIT 0x04 @@ -2056,8 +2058,7 @@ static int ath12k_host_cap_parse_mlo(struct ath12k_base *ab, } if (!ab->qmi.num_radios || ab->qmi.num_radios == U8_MAX) { - ab->single_chip_mlo_supp = false; - + ag->mlo_capable = false; ath12k_dbg(ab, ATH12K_DBG_QMI, "skip QMI MLO cap due to invalid num_radio %d\n", ab->qmi.num_radios); @@ -2169,10 +2170,12 @@ int ath12k_qmi_host_cap_send(struct ath12k_base *ab) req.bdf_support_valid = 1; req.bdf_support = 1; - req.m3_support_valid = 1; - req.m3_support = 1; - req.m3_cache_support_valid = 1; - req.m3_cache_support = 1; + if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_driver) { + req.m3_support_valid = 1; + req.m3_support = 1; + req.m3_cache_support_valid = 1; + req.m3_cache_support = 1; + } req.cal_done_valid = 1; req.cal_done = ab->qmi.cal_done; @@ -2265,9 +2268,8 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) goto out; } - if (resp.single_chip_mlo_support_valid && - resp.single_chip_mlo_support) - ab->single_chip_mlo_supp = true; + if (resp.single_chip_mlo_support_valid && resp.single_chip_mlo_support) + ab->single_chip_mlo_support = true; if (!resp.num_phy_valid) { ret = -ENODATA; @@ -2277,10 +2279,10 @@ static void ath12k_qmi_phy_cap_send(struct ath12k_base *ab) ab->qmi.num_radios = resp.num_phy; ath12k_dbg(ab, ATH12K_DBG_QMI, - "phy capability resp valid %d num_phy %d valid %d board_id %d valid %d single_chip_mlo_support %d\n", + "phy capability resp valid %d single_chip_mlo_support %d valid %d num_phy %d valid %d board_id %d\n", + resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support, resp.num_phy_valid, resp.num_phy, - resp.board_id_valid, resp.board_id, - resp.single_chip_mlo_support_valid, resp.single_chip_mlo_support); + resp.board_id_valid, resp.board_id); return; @@ -2382,7 +2384,8 @@ int ath12k_qmi_respond_fw_mem_request(struct ath12k_base *ab) * failure to firmware and firmware then request multiple blocks of * small chunk size memory. */ - if (ab->qmi.target_mem_delayed) { + if (!test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) && + ab->qmi.target_mem_delayed) { delayed = true; ath12k_dbg(ab, ATH12K_DBG_QMI, "qmi delays mem_request %d\n", ab->qmi.mem_seg_count); @@ -2440,12 +2443,35 @@ out: return ret; } +void ath12k_qmi_reset_mlo_mem(struct ath12k_hw_group *ag) +{ + struct target_mem_chunk *mlo_chunk; + int i; + + lockdep_assert_held(&ag->mutex); + + if (!ag->mlo_mem.init_done || ag->num_started) + return; + + for (i = 0; i < ARRAY_SIZE(ag->mlo_mem.chunk); i++) { + mlo_chunk = &ag->mlo_mem.chunk[i]; + + if (mlo_chunk->v.addr) + /* TODO: Mode 0 recovery is the default mode hence resetting the + * whole memory region for now. Once Mode 1 support is added, this + * needs to be handled properly + */ + memset(mlo_chunk->v.addr, 0, mlo_chunk->size); + } +} + static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab, struct target_mem_chunk *chunk, int idx) { struct ath12k_hw_group *ag = ab->ag; struct target_mem_chunk *mlo_chunk; + bool fixed_mem; lockdep_assert_held(&ag->mutex); @@ -2457,8 +2483,13 @@ static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab, return; } + fixed_mem = test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags); mlo_chunk = &ag->mlo_mem.chunk[idx]; - if (mlo_chunk->v.addr) { + + if (fixed_mem && mlo_chunk->v.ioaddr) { + iounmap(mlo_chunk->v.ioaddr); + mlo_chunk->v.ioaddr = NULL; + } else if (mlo_chunk->v.addr) { dma_free_coherent(ab->dev, mlo_chunk->size, mlo_chunk->v.addr, @@ -2468,7 +2499,10 @@ static void ath12k_qmi_free_mlo_mem_chunk(struct ath12k_base *ab, mlo_chunk->paddr = 0; mlo_chunk->size = 0; - chunk->v.addr = NULL; + if (fixed_mem) + chunk->v.ioaddr = NULL; + else + chunk->v.addr = NULL; chunk->paddr = 0; chunk->size = 0; } @@ -2479,19 +2513,24 @@ static void ath12k_qmi_free_target_mem_chunk(struct ath12k_base *ab) int i, mlo_idx; for (i = 0, mlo_idx = 0; i < ab->qmi.mem_seg_count; i++) { - if (!ab->qmi.target_mem[i].v.addr) - continue; - if (ab->qmi.target_mem[i].type == MLO_GLOBAL_MEM_REGION_TYPE) { ath12k_qmi_free_mlo_mem_chunk(ab, &ab->qmi.target_mem[i], mlo_idx++); } else { - dma_free_coherent(ab->dev, - ab->qmi.target_mem[i].prev_size, - ab->qmi.target_mem[i].v.addr, - ab->qmi.target_mem[i].paddr); - ab->qmi.target_mem[i].v.addr = NULL; + if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags) && + ab->qmi.target_mem[i].v.ioaddr) { + iounmap(ab->qmi.target_mem[i].v.ioaddr); + ab->qmi.target_mem[i].v.ioaddr = NULL; + } else { + if (!ab->qmi.target_mem[i].v.addr) + continue; + dma_free_coherent(ab->dev, + ab->qmi.target_mem[i].prev_size, + ab->qmi.target_mem[i].v.addr, + ab->qmi.target_mem[i].paddr); + ab->qmi.target_mem[i].v.addr = NULL; + } } } @@ -2644,6 +2683,130 @@ err: return ret; } +static int ath12k_qmi_assign_target_mem_chunk(struct ath12k_base *ab) +{ + struct reserved_mem *rmem; + size_t avail_rmem_size; + int i, idx, ret; + + for (i = 0, idx = 0; i < ab->qmi.mem_seg_count; i++) { + switch (ab->qmi.target_mem[i].type) { + case HOST_DDR_REGION_TYPE: + rmem = ath12k_core_get_reserved_mem(ab, 0); + if (!rmem) { + ret = -ENODEV; + goto out; + } + + avail_rmem_size = rmem->size; + if (avail_rmem_size < ab->qmi.target_mem[i].size) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "failed to assign mem type %u req size %u avail size %zu\n", + ab->qmi.target_mem[i].type, + ab->qmi.target_mem[i].size, + avail_rmem_size); + ret = -EINVAL; + goto out; + } + + ab->qmi.target_mem[idx].paddr = rmem->base; + ab->qmi.target_mem[idx].v.ioaddr = + ioremap(ab->qmi.target_mem[idx].paddr, + ab->qmi.target_mem[i].size); + if (!ab->qmi.target_mem[idx].v.ioaddr) { + ret = -EIO; + goto out; + } + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + case BDF_MEM_REGION_TYPE: + rmem = ath12k_core_get_reserved_mem(ab, 0); + if (!rmem) { + ret = -ENODEV; + goto out; + } + + avail_rmem_size = rmem->size - ab->hw_params->bdf_addr_offset; + if (avail_rmem_size < ab->qmi.target_mem[i].size) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "failed to assign mem type %u req size %u avail size %zu\n", + ab->qmi.target_mem[i].type, + ab->qmi.target_mem[i].size, + avail_rmem_size); + ret = -EINVAL; + goto out; + } + ab->qmi.target_mem[idx].paddr = + rmem->base + ab->hw_params->bdf_addr_offset; + ab->qmi.target_mem[idx].v.ioaddr = + ioremap(ab->qmi.target_mem[idx].paddr, + ab->qmi.target_mem[i].size); + if (!ab->qmi.target_mem[idx].v.ioaddr) { + ret = -EIO; + goto out; + } + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + case CALDB_MEM_REGION_TYPE: + /* Cold boot calibration is not enabled in Ath12k. Hence, + * assign paddr = 0. + * Once cold boot calibration is enabled add support to + * assign reserved memory from DT. + */ + ab->qmi.target_mem[idx].paddr = 0; + ab->qmi.target_mem[idx].v.ioaddr = NULL; + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + case M3_DUMP_REGION_TYPE: + rmem = ath12k_core_get_reserved_mem(ab, 1); + if (!rmem) { + ret = -EINVAL; + goto out; + } + + avail_rmem_size = rmem->size; + if (avail_rmem_size < ab->qmi.target_mem[i].size) { + ath12k_dbg(ab, ATH12K_DBG_QMI, + "failed to assign mem type %u req size %u avail size %zu\n", + ab->qmi.target_mem[i].type, + ab->qmi.target_mem[i].size, + avail_rmem_size); + ret = -EINVAL; + goto out; + } + + ab->qmi.target_mem[idx].paddr = rmem->base; + ab->qmi.target_mem[idx].v.ioaddr = + ioremap(ab->qmi.target_mem[idx].paddr, + ab->qmi.target_mem[i].size); + if (!ab->qmi.target_mem[idx].v.ioaddr) { + ret = -EIO; + goto out; + } + ab->qmi.target_mem[idx].size = ab->qmi.target_mem[i].size; + ab->qmi.target_mem[idx].type = ab->qmi.target_mem[i].type; + idx++; + break; + default: + ath12k_warn(ab, "qmi ignore invalid mem req type %u\n", + ab->qmi.target_mem[i].type); + break; + } + } + ab->qmi.mem_seg_count = idx; + + return 0; +out: + ath12k_qmi_free_target_mem_chunk(ab); + return ret; +} + /* clang stack usage explodes if this is inlined */ static noinline_for_stack int ath12k_qmi_request_target_cap(struct ath12k_base *ab) @@ -2740,6 +2903,15 @@ int ath12k_qmi_request_target_cap(struct ath12k_base *ab) if (r) ath12k_dbg(ab, ATH12K_DBG_QMI, "SMBIOS bdf variant name not set.\n"); + r = ath12k_acpi_start(ab); + if (r) + /* ACPI is optional so continue in case of an error */ + ath12k_dbg(ab, ATH12K_DBG_BOOT, "acpi failed: %d\n", r); + + r = ath12k_acpi_check_bdf_variant_name(ab); + if (r) + ath12k_dbg(ab, ATH12K_DBG_BOOT, "ACPI bdf variant name not set.\n"); + out: return ret; } @@ -2936,6 +3108,9 @@ static void ath12k_qmi_m3_free(struct ath12k_base *ab) { struct m3_mem_region *m3_mem = &ab->qmi.m3_mem; + if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_remoteproc) + return; + if (!m3_mem->vaddr) return; @@ -3016,15 +3191,16 @@ int ath12k_qmi_wlanfw_m3_info_send(struct ath12k_base *ab) struct qmi_txn txn; int ret = 0; - ret = ath12k_qmi_m3_load(ab); - if (ret) { - ath12k_err(ab, "failed to load m3 firmware: %d", ret); - return ret; + if (ab->hw_params->fw.m3_loader == ath12k_m3_fw_loader_driver) { + ret = ath12k_qmi_m3_load(ab); + if (ret) { + ath12k_err(ab, "failed to load m3 firmware: %d", ret); + return ret; + } + req.addr = m3_mem->paddr; + req.size = m3_mem->size; } - req.addr = m3_mem->paddr; - req.size = m3_mem->size; - ret = qmi_txn_init(&ab->qmi.handle, &txn, qmi_wlanfw_m3_info_resp_msg_v01_ei, &resp); if (ret < 0) @@ -3474,11 +3650,20 @@ static void ath12k_qmi_msg_mem_request_cb(struct qmi_handle *qmi_hdl, msg->mem_seg[i].type, msg->mem_seg[i].size); } - ret = ath12k_qmi_alloc_target_mem_chunk(ab); - if (ret) { - ath12k_warn(ab, "qmi failed to alloc target memory: %d\n", - ret); - return; + if (test_bit(ATH12K_FLAG_FIXED_MEM_REGION, &ab->dev_flags)) { + ret = ath12k_qmi_assign_target_mem_chunk(ab); + if (ret) { + ath12k_warn(ab, "failed to assign qmi target memory: %d\n", + ret); + return; + } + } else { + ret = ath12k_qmi_alloc_target_mem_chunk(ab); + if (ret) { + ath12k_warn(ab, "qmi failed to alloc target memory: %d\n", + ret); + return; + } } ath12k_qmi_driver_event_post(qmi, ATH12K_QMI_EVENT_REQUEST_MEM, NULL); diff --git a/drivers/net/wireless/ath/ath12k/qmi.h b/drivers/net/wireless/ath/ath12k/qmi.h index 45d7c3fcafdd..96e6c3daecfe 100644 --- a/drivers/net/wireless/ath/ath12k/qmi.h +++ b/drivers/net/wireless/ath/ath12k/qmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_QMI_H @@ -21,6 +21,7 @@ #define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850 0x1 #define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_QCN9274 0x07 +#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_IPQ5332 0x2 #define ATH12K_QMI_WLANFW_MAX_TIMESTAMP_LEN_V01 32 #define ATH12K_QMI_RESP_LEN_MAX 8192 #define ATH12K_QMI_WLANFW_MAX_NUM_MEM_SEG_V01 52 @@ -41,6 +42,7 @@ #define ATH12K_BOARD_ID_DEFAULT 0xFF struct ath12k_base; +struct ath12k_hw_group; enum ath12k_qmi_file_type { ATH12K_QMI_FILE_TYPE_BDF_GOLDEN = 0, @@ -621,5 +623,6 @@ void ath12k_qmi_deinit_service(struct ath12k_base *ab); int ath12k_qmi_init_service(struct ath12k_base *ab); void ath12k_qmi_free_resource(struct ath12k_base *ab); void ath12k_qmi_trigger_host_cap(struct ath12k_base *ab); +void ath12k_qmi_reset_mlo_mem(struct ath12k_hw_group *ag); #endif diff --git a/drivers/net/wireless/ath/ath12k/reg.c b/drivers/net/wireless/ath/ath12k/reg.c index 439d61f284d8..2598b39d5d7e 100644 --- a/drivers/net/wireless/ath/ath12k/reg.c +++ b/drivers/net/wireless/ath/ath12k/reg.c @@ -1,11 +1,12 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/rtnetlink.h> #include "core.h" #include "debug.h" +#include "mac.h" /* World regdom to be used in case default regd from fw is unavailable */ #define ATH12K_2GHZ_CH01_11 REG_RULE(2412 - 10, 2462 + 10, 40, 0, 20, 0) @@ -48,6 +49,7 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct ath12k_wmi_init_country_arg arg; + struct wmi_set_current_country_arg current_arg = {}; struct ath12k_hw *ah = ath12k_hw_to_ah(hw); struct ath12k *ar = ath12k_ah_to_ar(ah, 0); int ret, i; @@ -55,6 +57,24 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) ath12k_dbg(ar->ab, ATH12K_DBG_REG, "Regulatory Notification received for %s\n", wiphy_name(wiphy)); + if (request->initiator == NL80211_REGDOM_SET_BY_DRIVER) { + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "driver initiated regd update\n"); + if (ah->state != ATH12K_HW_STATE_ON) + return; + + for_each_ar(ah, ar, i) { + ret = ath12k_reg_update_chan_list(ar, true); + if (ret) { + ath12k_warn(ar->ab, + "failed to update chan list for pdev %u, ret %d\n", + i, ret); + break; + } + } + return; + } + /* Currently supporting only General User Hints. Cell base user * hints to be handled later. * Hints from other sources like Core, Beacons are not expected for @@ -77,27 +97,38 @@ ath12k_reg_notifier(struct wiphy *wiphy, struct regulatory_request *request) return; } - /* Set the country code to the firmware and wait for - * the WMI_REG_CHAN_LIST_CC EVENT for updating the - * reg info - */ - arg.flags = ALPHA_IS_SET; - memcpy(&arg.cc_info.alpha2, request->alpha2, 2); - arg.cc_info.alpha2[2] = 0; - /* Allow fresh updates to wiphy regd */ ah->regd_updated = false; /* Send the reg change request to all the radios */ for_each_ar(ah, ar, i) { - ret = ath12k_wmi_send_init_country_cmd(ar, &arg); - if (ret) - ath12k_warn(ar->ab, - "INIT Country code set to fw failed : %d\n", ret); + if (ar->ab->hw_params->current_cc_support) { + memcpy(¤t_arg.alpha2, request->alpha2, 2); + memcpy(&ar->alpha2, ¤t_arg.alpha2, 2); + ret = ath12k_wmi_send_set_current_country_cmd(ar, ¤t_arg); + if (ret) + ath12k_warn(ar->ab, + "failed set current country code: %d\n", ret); + } else { + arg.flags = ALPHA_IS_SET; + memcpy(&arg.cc_info.alpha2, request->alpha2, 2); + arg.cc_info.alpha2[2] = 0; + + ret = ath12k_wmi_send_init_country_cmd(ar, &arg); + if (ret) + ath12k_warn(ar->ab, + "failed set INIT Country code: %d\n", ret); + } + + wiphy_lock(wiphy); + ath12k_mac_11d_scan_stop(ar); + wiphy_unlock(wiphy); + + ar->regdom_set_by_user = true; } } -int ath12k_reg_update_chan_list(struct ath12k *ar) +int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait) { struct ieee80211_supported_band **bands; struct ath12k_wmi_scan_chan_list_arg *arg; @@ -106,7 +137,35 @@ int ath12k_reg_update_chan_list(struct ath12k *ar) struct ath12k_wmi_channel_arg *ch; enum nl80211_band band; int num_channels = 0; - int i, ret; + int i, ret, left; + + if (wait && ar->state_11d == ATH12K_11D_RUNNING) { + left = wait_for_completion_timeout(&ar->completed_11d_scan, + ATH12K_SCAN_TIMEOUT_HZ); + if (!left) { + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "failed to receive 11d scan complete: timed out\n"); + ar->state_11d = ATH12K_11D_IDLE; + } + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "reg 11d scan wait left time %d\n", left); + } + + if (wait && + (ar->scan.state == ATH12K_SCAN_STARTING || + ar->scan.state == ATH12K_SCAN_RUNNING)) { + left = wait_for_completion_timeout(&ar->scan.completed, + ATH12K_SCAN_TIMEOUT_HZ); + if (!left) + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "failed to receive hw scan complete: timed out\n"); + + ath12k_dbg(ar->ab, ATH12K_DBG_REG, + "reg hw scan wait left time %d\n", left); + } + + if (ar->ah->state == ATH12K_HW_STATE_RESTARTING) + return 0; bands = hw->wiphy->bands; for (band = 0; band < NUM_NL80211_BANDS; band++) { @@ -206,15 +265,57 @@ static void ath12k_copy_regd(struct ieee80211_regdomain *regd_orig, int ath12k_regd_update(struct ath12k *ar, bool init) { + struct ath12k_wmi_hal_reg_capabilities_ext_arg *reg_cap; + u32 phy_id, freq_low, freq_high, supported_bands; struct ath12k_hw *ah = ath12k_ar_to_ah(ar); struct ieee80211_hw *hw = ah->hw; struct ieee80211_regdomain *regd, *regd_copy = NULL; int ret, regd_len, pdev_id; struct ath12k_base *ab; - int i; ab = ar->ab; + supported_bands = ar->pdev->cap.supported_bands; + reg_cap = &ab->hal_reg_cap[ar->pdev_idx]; + + /* Possible that due to reg change, current limits for supported + * frequency changed. Update it. As a first step, reset the + * previous values and then compute and set the new values. + */ + ar->freq_range.start_freq = 0; + ar->freq_range.end_freq = 0; + + if (supported_bands & WMI_HOST_WLAN_2GHZ_CAP) { + if (ab->hw_params->single_pdev_only) { + phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_2GHZ_CAP].phy_id; + reg_cap = &ab->hal_reg_cap[phy_id]; + } + + freq_low = max(reg_cap->low_2ghz_chan, ab->reg_freq_2ghz.start_freq); + freq_high = min(reg_cap->high_2ghz_chan, ab->reg_freq_2ghz.end_freq); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); + } + + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && !ar->supports_6ghz) { + if (ab->hw_params->single_pdev_only) { + phy_id = ar->pdev->cap.band[WMI_HOST_WLAN_5GHZ_CAP].phy_id; + reg_cap = &ab->hal_reg_cap[phy_id]; + } + + freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_5ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_5ghz.end_freq); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); + } + + if (supported_bands & WMI_HOST_WLAN_5GHZ_CAP && ar->supports_6ghz) { + freq_low = max(reg_cap->low_5ghz_chan, ab->reg_freq_6ghz.start_freq); + freq_high = min(reg_cap->high_5ghz_chan, ab->reg_freq_6ghz.end_freq); + + ath12k_mac_update_freq_range(ar, freq_low, freq_high); + } + /* If one of the radios within ah has already updated the regd for * the wiphy, then avoid setting regd again */ @@ -275,11 +376,7 @@ int ath12k_regd_update(struct ath12k *ar, bool init) goto err; } - rtnl_lock(); - wiphy_lock(hw->wiphy); - ret = regulatory_set_wiphy_regd_sync(hw->wiphy, regd_copy); - wiphy_unlock(hw->wiphy); - rtnl_unlock(); + ret = regulatory_set_wiphy_regd(hw->wiphy, regd_copy); kfree(regd_copy); @@ -290,15 +387,7 @@ int ath12k_regd_update(struct ath12k *ar, bool init) goto skip; ah->regd_updated = true; - /* Apply the new regd to all the radios, this is expected to be received only once - * since we check for ah->regd_updated and allow here only once. - */ - for_each_ar(ah, ar, i) { - ab = ar->ab; - ret = ath12k_reg_update_chan_list(ar); - if (ret) - goto err; - } + skip: return 0; err: @@ -365,129 +454,6 @@ static u32 ath12k_map_fw_phy_flags(u32 phy_flags) return flags; } -static bool -ath12k_reg_can_intersect(struct ieee80211_reg_rule *rule1, - struct ieee80211_reg_rule *rule2) -{ - u32 start_freq1, end_freq1; - u32 start_freq2, end_freq2; - - start_freq1 = rule1->freq_range.start_freq_khz; - start_freq2 = rule2->freq_range.start_freq_khz; - - end_freq1 = rule1->freq_range.end_freq_khz; - end_freq2 = rule2->freq_range.end_freq_khz; - - if ((start_freq1 >= start_freq2 && - start_freq1 < end_freq2) || - (start_freq2 > start_freq1 && - start_freq2 < end_freq1)) - return true; - - /* TODO: Should we restrict intersection feasibility - * based on min bandwidth of the intersected region also, - * say the intersected rule should have a min bandwidth - * of 20MHz? - */ - - return false; -} - -static void ath12k_reg_intersect_rules(struct ieee80211_reg_rule *rule1, - struct ieee80211_reg_rule *rule2, - struct ieee80211_reg_rule *new_rule) -{ - u32 start_freq1, end_freq1; - u32 start_freq2, end_freq2; - u32 freq_diff, max_bw; - - start_freq1 = rule1->freq_range.start_freq_khz; - start_freq2 = rule2->freq_range.start_freq_khz; - - end_freq1 = rule1->freq_range.end_freq_khz; - end_freq2 = rule2->freq_range.end_freq_khz; - - new_rule->freq_range.start_freq_khz = max_t(u32, start_freq1, - start_freq2); - new_rule->freq_range.end_freq_khz = min_t(u32, end_freq1, end_freq2); - - freq_diff = new_rule->freq_range.end_freq_khz - - new_rule->freq_range.start_freq_khz; - max_bw = min_t(u32, rule1->freq_range.max_bandwidth_khz, - rule2->freq_range.max_bandwidth_khz); - new_rule->freq_range.max_bandwidth_khz = min_t(u32, max_bw, freq_diff); - - new_rule->power_rule.max_antenna_gain = - min_t(u32, rule1->power_rule.max_antenna_gain, - rule2->power_rule.max_antenna_gain); - - new_rule->power_rule.max_eirp = min_t(u32, rule1->power_rule.max_eirp, - rule2->power_rule.max_eirp); - - /* Use the flags of both the rules */ - new_rule->flags = rule1->flags | rule2->flags; - - /* To be safe, lts use the max cac timeout of both rules */ - new_rule->dfs_cac_ms = max_t(u32, rule1->dfs_cac_ms, - rule2->dfs_cac_ms); -} - -static struct ieee80211_regdomain * -ath12k_regd_intersect(struct ieee80211_regdomain *default_regd, - struct ieee80211_regdomain *curr_regd) -{ - u8 num_old_regd_rules, num_curr_regd_rules, num_new_regd_rules; - struct ieee80211_reg_rule *old_rule, *curr_rule, *new_rule; - struct ieee80211_regdomain *new_regd = NULL; - u8 i, j, k; - - num_old_regd_rules = default_regd->n_reg_rules; - num_curr_regd_rules = curr_regd->n_reg_rules; - num_new_regd_rules = 0; - - /* Find the number of intersecting rules to allocate new regd memory */ - for (i = 0; i < num_old_regd_rules; i++) { - old_rule = default_regd->reg_rules + i; - for (j = 0; j < num_curr_regd_rules; j++) { - curr_rule = curr_regd->reg_rules + j; - - if (ath12k_reg_can_intersect(old_rule, curr_rule)) - num_new_regd_rules++; - } - } - - if (!num_new_regd_rules) - return NULL; - - new_regd = kzalloc(sizeof(*new_regd) + (num_new_regd_rules * - sizeof(struct ieee80211_reg_rule)), - GFP_ATOMIC); - - if (!new_regd) - return NULL; - - /* We set the new country and dfs region directly and only trim - * the freq, power, antenna gain by intersecting with the - * default regdomain. Also MAX of the dfs cac timeout is selected. - */ - new_regd->n_reg_rules = num_new_regd_rules; - memcpy(new_regd->alpha2, curr_regd->alpha2, sizeof(new_regd->alpha2)); - new_regd->dfs_region = curr_regd->dfs_region; - new_rule = new_regd->reg_rules; - - for (i = 0, k = 0; i < num_old_regd_rules; i++) { - old_rule = default_regd->reg_rules + i; - for (j = 0; j < num_curr_regd_rules; j++) { - curr_rule = curr_regd->reg_rules + j; - - if (ath12k_reg_can_intersect(old_rule, curr_rule)) - ath12k_reg_intersect_rules(old_rule, curr_rule, - (new_rule + k++)); - } - } - return new_regd; -} - static const char * ath12k_reg_get_regdom_str(enum nl80211_dfs_regions dfs_region) { @@ -524,13 +490,14 @@ ath12k_reg_adjust_bw(u16 start_freq, u16 end_freq, u16 max_bw) static void ath12k_reg_update_rule(struct ieee80211_reg_rule *reg_rule, u32 start_freq, u32 end_freq, u32 bw, u32 ant_gain, u32 reg_pwr, - u32 reg_flags) + s8 psd, u32 reg_flags) { reg_rule->freq_range.start_freq_khz = MHZ_TO_KHZ(start_freq); reg_rule->freq_range.end_freq_khz = MHZ_TO_KHZ(end_freq); reg_rule->freq_range.max_bandwidth_khz = MHZ_TO_KHZ(bw); reg_rule->power_rule.max_antenna_gain = DBI_TO_MBI(ant_gain); reg_rule->power_rule.max_eirp = DBM_TO_MBM(reg_pwr); + reg_rule->psd = psd; reg_rule->flags = reg_flags; } @@ -552,7 +519,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, ath12k_reg_update_rule(regd->reg_rules + i, reg_rule->start_freq, ETSI_WEATHER_RADAR_BAND_LOW, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", @@ -574,7 +541,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, ath12k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_LOW, end_freq, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); regd->reg_rules[i].dfs_cac_ms = ETSI_WEATHER_RADAR_BAND_CAC_TIMEOUT; @@ -599,7 +566,7 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, ath12k_reg_update_rule(regd->reg_rules + i, ETSI_WEATHER_RADAR_BAND_HIGH, reg_rule->end_freq, bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", @@ -611,26 +578,77 @@ ath12k_reg_update_weather_radar_band(struct ath12k_base *ab, *rule_idx = i; } +static void ath12k_reg_update_freq_range(struct ath12k_reg_freq *reg_freq, + struct ath12k_reg_rule *reg_rule) +{ + if (reg_freq->start_freq > reg_rule->start_freq) + reg_freq->start_freq = reg_rule->start_freq; + + if (reg_freq->end_freq < reg_rule->end_freq) + reg_freq->end_freq = reg_rule->end_freq; +} + +enum wmi_reg_6g_ap_type +ath12k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type) +{ + switch (power_type) { + case IEEE80211_REG_LPI_AP: + return WMI_REG_INDOOR_AP; + case IEEE80211_REG_SP_AP: + return WMI_REG_STD_POWER_AP; + case IEEE80211_REG_VLP_AP: + return WMI_REG_VLP_AP; + default: + return WMI_REG_MAX_AP_TYPE; + } +} + struct ieee80211_regdomain * ath12k_reg_build_regd(struct ath12k_base *ab, - struct ath12k_reg_info *reg_info, bool intersect) + struct ath12k_reg_info *reg_info, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type) { - struct ieee80211_regdomain *tmp_regd, *default_regd, *new_regd = NULL; - struct ath12k_reg_rule *reg_rule; + struct ieee80211_regdomain *new_regd = NULL; + struct ath12k_reg_rule *reg_rule, *reg_rule_6ghz; + u32 flags, reg_6ghz_number, max_bw_6ghz; u8 i = 0, j = 0, k = 0; u8 num_rules; u16 max_bw; - u32 flags; char alpha2[3]; num_rules = reg_info->num_5g_reg_rules + reg_info->num_2g_reg_rules; - /* FIXME: Currently taking reg rules for 6G only from Indoor AP mode list. - * This can be updated to choose the combination dynamically based on AP - * type and client type, after complete 6G regulatory support is added. - */ - if (reg_info->is_ext_reg_event) - num_rules += reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP]; + if (reg_info->is_ext_reg_event) { + if (vdev_type == WMI_VDEV_TYPE_STA) { + enum wmi_reg_6g_ap_type ap_type; + + ap_type = ath12k_reg_ap_pwr_convert(power_type); + if (ap_type == WMI_REG_MAX_AP_TYPE) + ap_type = WMI_REG_INDOOR_AP; + + reg_6ghz_number = reg_info->num_6g_reg_rules_cl + [ap_type][WMI_REG_DEFAULT_CLIENT]; + if (reg_6ghz_number == 0) { + ap_type = WMI_REG_INDOOR_AP; + reg_6ghz_number = reg_info->num_6g_reg_rules_cl + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } + + reg_rule_6ghz = reg_info->reg_rules_6g_client_ptr + [ap_type][WMI_REG_DEFAULT_CLIENT]; + max_bw_6ghz = reg_info->max_bw_6g_client + [ap_type][WMI_REG_DEFAULT_CLIENT]; + } else { + reg_6ghz_number = reg_info->num_6g_reg_rules_ap + [WMI_REG_INDOOR_AP]; + reg_rule_6ghz = + reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP]; + max_bw_6ghz = reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]; + } + + num_rules += reg_6ghz_number; + } if (!num_rules) goto ret; @@ -639,21 +657,31 @@ ath12k_reg_build_regd(struct ath12k_base *ab, if (reg_info->dfs_region == ATH12K_DFS_REG_ETSI) num_rules += 2; - tmp_regd = kzalloc(sizeof(*tmp_regd) + + new_regd = kzalloc(sizeof(*new_regd) + (num_rules * sizeof(struct ieee80211_reg_rule)), GFP_ATOMIC); - if (!tmp_regd) + if (!new_regd) goto ret; - memcpy(tmp_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); + memcpy(new_regd->alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); memcpy(alpha2, reg_info->alpha2, REG_ALPHA2_LEN + 1); alpha2[2] = '\0'; - tmp_regd->dfs_region = ath12k_map_fw_dfs_region(reg_info->dfs_region); + new_regd->dfs_region = ath12k_map_fw_dfs_region(reg_info->dfs_region); ath12k_dbg(ab, ATH12K_DBG_REG, "\r\nCountry %s, CFG Regdomain %s FW Regdomain %d, num_reg_rules %d\n", - alpha2, ath12k_reg_get_regdom_str(tmp_regd->dfs_region), + alpha2, ath12k_reg_get_regdom_str(new_regd->dfs_region), reg_info->dfs_region, num_rules); + + /* Reset start and end frequency for each band + */ + ab->reg_freq_5ghz.start_freq = INT_MAX; + ab->reg_freq_5ghz.end_freq = 0; + ab->reg_freq_2ghz.start_freq = INT_MAX; + ab->reg_freq_2ghz.end_freq = 0; + ab->reg_freq_6ghz.start_freq = INT_MAX; + ab->reg_freq_6ghz.end_freq = 0; + /* Update reg_rules[] below. Firmware is expected to * send these rules in order(2G rules first and then 5G) */ @@ -664,6 +692,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab, max_bw = min_t(u16, reg_rule->max_bw, reg_info->max_bw_2g); flags = 0; + ath12k_reg_update_freq_range(&ab->reg_freq_2ghz, reg_rule); } else if (reg_info->num_5g_reg_rules && (j < reg_info->num_5g_reg_rules)) { reg_rule = reg_info->reg_rules_5g_ptr + j++; @@ -677,13 +706,15 @@ ath12k_reg_build_regd(struct ath12k_base *ab, * per other BW rule flags we pass from here */ flags = NL80211_RRF_AUTO_BW; - } else if (reg_info->is_ext_reg_event && - reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP] && - (k < reg_info->num_6g_reg_rules_ap[WMI_REG_INDOOR_AP])) { - reg_rule = reg_info->reg_rules_6g_ap_ptr[WMI_REG_INDOOR_AP] + k++; - max_bw = min_t(u16, reg_rule->max_bw, - reg_info->max_bw_6g_ap[WMI_REG_INDOOR_AP]); + ath12k_reg_update_freq_range(&ab->reg_freq_5ghz, reg_rule); + } else if (reg_info->is_ext_reg_event && reg_6ghz_number && + (k < reg_6ghz_number)) { + reg_rule = reg_rule_6ghz + k++; + max_bw = min_t(u16, reg_rule->max_bw, max_bw_6ghz); flags = NL80211_RRF_AUTO_BW; + if (reg_rule->psd_flag) + flags |= NL80211_RRF_PSD; + ath12k_reg_update_freq_range(&ab->reg_freq_6ghz, reg_rule); } else { break; } @@ -691,11 +722,11 @@ ath12k_reg_build_regd(struct ath12k_base *ab, flags |= ath12k_map_fw_reg_flags(reg_rule->flags); flags |= ath12k_map_fw_phy_flags(reg_info->phybitmap); - ath12k_reg_update_rule(tmp_regd->reg_rules + i, + ath12k_reg_update_rule(new_regd->reg_rules + i, reg_rule->start_freq, reg_rule->end_freq, max_bw, reg_rule->ant_gain, reg_rule->reg_power, - flags); + reg_rule->psd_eirp, flags); /* Update dfs cac timeout if the dfs domain is ETSI and the * new rule covers weather radar band. @@ -706,7 +737,7 @@ ath12k_reg_build_regd(struct ath12k_base *ab, reg_info->dfs_region == ATH12K_DFS_REG_ETSI && (reg_rule->end_freq > ETSI_WEATHER_RADAR_BAND_LOW && reg_rule->start_freq < ETSI_WEATHER_RADAR_BAND_HIGH)){ - ath12k_reg_update_weather_radar_band(ab, tmp_regd, + ath12k_reg_update_weather_radar_band(ab, new_regd, reg_rule, &i, flags, max_bw); continue; @@ -716,36 +747,19 @@ ath12k_reg_build_regd(struct ath12k_base *ab, ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d) (%d, %d)\n", i + 1, reg_rule->start_freq, reg_rule->end_freq, max_bw, reg_rule->ant_gain, reg_rule->reg_power, - tmp_regd->reg_rules[i].dfs_cac_ms, + new_regd->reg_rules[i].dfs_cac_ms, flags, reg_rule->psd_flag, reg_rule->psd_eirp); } else { ath12k_dbg(ab, ATH12K_DBG_REG, "\t%d. (%d - %d @ %d) (%d, %d) (%d ms) (FLAGS %d)\n", i + 1, reg_rule->start_freq, reg_rule->end_freq, max_bw, reg_rule->ant_gain, reg_rule->reg_power, - tmp_regd->reg_rules[i].dfs_cac_ms, + new_regd->reg_rules[i].dfs_cac_ms, flags); } } - tmp_regd->n_reg_rules = i; - - if (intersect) { - default_regd = ab->default_regd[reg_info->phy_id]; - - /* Get a new regd by intersecting the received regd with - * our default regd. - */ - new_regd = ath12k_regd_intersect(default_regd, tmp_regd); - kfree(tmp_regd); - if (!new_regd) { - ath12k_warn(ab, "Unable to create intersected regdomain\n"); - goto ret; - } - } else { - new_regd = tmp_regd; - } - + new_regd->n_reg_rules = i; ret: return new_regd; } @@ -767,9 +781,109 @@ void ath12k_regd_update_work(struct work_struct *work) } } +void ath12k_reg_reset_reg_info(struct ath12k_reg_info *reg_info) +{ + u8 i, j; + + if (!reg_info) + return; + + kfree(reg_info->reg_rules_2g_ptr); + kfree(reg_info->reg_rules_5g_ptr); + + if (reg_info->is_ext_reg_event) { + for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { + kfree(reg_info->reg_rules_6g_ap_ptr[i]); + + for (j = 0; j < WMI_REG_MAX_CLIENT_TYPE; j++) + kfree(reg_info->reg_rules_6g_client_ptr[i][j]); + } + } +} + +enum ath12k_reg_status ath12k_reg_validate_reg_info(struct ath12k_base *ab, + struct ath12k_reg_info *reg_info) +{ + int pdev_idx = reg_info->phy_id; + + if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { + /* In case of failure to set the requested country, + * firmware retains the current regd. We print a failure info + * and return from here. + */ + ath12k_warn(ab, "Failed to set the requested Country regulatory setting\n"); + return ATH12K_REG_STATUS_DROP; + } + + if (pdev_idx >= ab->num_radios) { + /* Process the event for phy0 only if single_pdev_only + * is true. If pdev_idx is valid but not 0, discard the + * event. Otherwise, it goes to fallback. + */ + if (ab->hw_params->single_pdev_only && + pdev_idx < ab->hw_params->num_rxdma_per_pdev) + return ATH12K_REG_STATUS_DROP; + else + return ATH12K_REG_STATUS_FALLBACK; + } + + /* Avoid multiple overwrites to default regd, during core + * stop-start after mac registration. + */ + if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && + !memcmp(ab->default_regd[pdev_idx]->alpha2, + reg_info->alpha2, 2)) + return ATH12K_REG_STATUS_DROP; + + return ATH12K_REG_STATUS_VALID; +} + +int ath12k_reg_handle_chan_list(struct ath12k_base *ab, + struct ath12k_reg_info *reg_info, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type) +{ + struct ieee80211_regdomain *regd = NULL; + int pdev_idx = reg_info->phy_id; + struct ath12k *ar; + + regd = ath12k_reg_build_regd(ab, reg_info, vdev_type, power_type); + if (!regd) + return -EINVAL; + + spin_lock_bh(&ab->base_lock); + if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) { + /* Once mac is registered, ar is valid and all CC events from + * firmware is considered to be received due to user requests + * currently. + * Free previously built regd before assigning the newly + * generated regd to ar. NULL pointer handling will be + * taken care by kfree itself. + */ + ar = ab->pdevs[pdev_idx].ar; + kfree(ab->new_regd[pdev_idx]); + ab->new_regd[pdev_idx] = regd; + queue_work(ab->workqueue, &ar->regd_update_work); + } else { + /* Multiple events for the same *ar is not expected. But we + * can still clear any previously stored default_regd if we + * are receiving this event for the same radio by mistake. + * NULL pointer handling will be taken care by kfree itself. + */ + kfree(ab->default_regd[pdev_idx]); + /* This regd would be applied during mac registration */ + ab->default_regd[pdev_idx] = regd; + } + ab->dfs_region = reg_info->dfs_region; + spin_unlock_bh(&ab->base_lock); + + return 0; +} + void ath12k_reg_init(struct ieee80211_hw *hw) { hw->wiphy->regulatory_flags = REGULATORY_WIPHY_SELF_MANAGED; + hw->wiphy->flags |= WIPHY_FLAG_NOTIFY_REGDOM_BY_DRIVER; hw->wiphy->reg_notifier = ath12k_reg_notifier; } @@ -777,8 +891,18 @@ void ath12k_reg_free(struct ath12k_base *ab) { int i; + mutex_lock(&ab->core_lock); + for (i = 0; i < MAX_RADIOS; i++) { + ath12k_reg_reset_reg_info(ab->reg_info[i]); + kfree(ab->reg_info[i]); + ab->reg_info[i] = NULL; + } + for (i = 0; i < ab->hw_params->max_radios; i++) { kfree(ab->default_regd[i]); kfree(ab->new_regd[i]); + ab->default_regd[i] = NULL; + ab->new_regd[i] = NULL; } + mutex_unlock(&ab->core_lock); } diff --git a/drivers/net/wireless/ath/ath12k/reg.h b/drivers/net/wireless/ath/ath12k/reg.h index 29c7ec3260da..8af8e9ba462e 100644 --- a/drivers/net/wireless/ath/ath12k/reg.h +++ b/drivers/net/wireless/ath/ath12k/reg.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2019-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_REG_H @@ -13,6 +13,9 @@ struct ath12k_base; struct ath12k; +#define ATH12K_2GHZ_MAX_FREQUENCY 2495 +#define ATH12K_5GHZ_MAX_FREQUENCY 5920 + /* DFS regdomains supported by Firmware */ enum ath12k_dfs_region { ATH12K_DFS_REG_UNSET, @@ -89,13 +92,29 @@ enum ath12k_reg_phy_bitmap { ATH12K_REG_PHY_BITMAP_NO11BE = BIT(6), }; +enum ath12k_reg_status { + ATH12K_REG_STATUS_VALID, + ATH12K_REG_STATUS_DROP, + ATH12K_REG_STATUS_FALLBACK, +}; + void ath12k_reg_init(struct ieee80211_hw *hw); void ath12k_reg_free(struct ath12k_base *ab); void ath12k_regd_update_work(struct work_struct *work); struct ieee80211_regdomain *ath12k_reg_build_regd(struct ath12k_base *ab, struct ath12k_reg_info *reg_info, - bool intersect); + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type); int ath12k_regd_update(struct ath12k *ar, bool init); -int ath12k_reg_update_chan_list(struct ath12k *ar); +int ath12k_reg_update_chan_list(struct ath12k *ar, bool wait); +void ath12k_reg_reset_reg_info(struct ath12k_reg_info *reg_info); +int ath12k_reg_handle_chan_list(struct ath12k_base *ab, + struct ath12k_reg_info *reg_info, + enum wmi_vdev_type vdev_type, + enum ieee80211_ap_reg_power power_type); +enum wmi_reg_6g_ap_type +ath12k_reg_ap_pwr_convert(enum ieee80211_ap_reg_power power_type); +enum ath12k_reg_status ath12k_reg_validate_reg_info(struct ath12k_base *ab, + struct ath12k_reg_info *reg_info); #endif diff --git a/drivers/net/wireless/ath/ath12k/rx_desc.h b/drivers/net/wireless/ath/ath12k/rx_desc.h index 10366bbe9999..6c600473b402 100644 --- a/drivers/net/wireless/ath/ath12k/rx_desc.h +++ b/drivers/net/wireless/ath/ath12k/rx_desc.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_RX_DESC_H #define ATH12K_RX_DESC_H @@ -637,6 +637,8 @@ enum rx_msdu_start_pkt_type { RX_MSDU_START_PKT_TYPE_11N, RX_MSDU_START_PKT_TYPE_11AC, RX_MSDU_START_PKT_TYPE_11AX, + RX_MSDU_START_PKT_TYPE_11BA, + RX_MSDU_START_PKT_TYPE_11BE, }; enum rx_msdu_start_sgi { @@ -1539,12 +1541,4 @@ struct hal_rx_desc { #define MAX_MU_GROUP_SHOW 16 #define MAX_MU_GROUP_LENGTH (6 * MAX_MU_GROUP_SHOW) -#define HAL_RX_RU_ALLOC_TYPE_MAX 6 -#define RU_26 1 -#define RU_52 2 -#define RU_106 4 -#define RU_242 9 -#define RU_484 18 -#define RU_996 37 - #endif /* ATH12K_RX_DESC_H */ diff --git a/drivers/net/wireless/ath/ath12k/testmode.c b/drivers/net/wireless/ath/ath12k/testmode.c new file mode 100644 index 000000000000..fb6af7ccf71f --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/testmode.c @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: BSD-3-Clause-Clear +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "testmode.h" +#include <net/netlink.h> +#include "debug.h" +#include "wmi.h" +#include "hw.h" +#include "core.h" +#include "hif.h" +#include "../testmode_i.h" + +#define ATH12K_FTM_SEGHDR_CURRENT_SEQ GENMASK(3, 0) +#define ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS GENMASK(7, 4) + +static const struct nla_policy ath12k_tm_policy[ATH_TM_ATTR_MAX + 1] = { + [ATH_TM_ATTR_CMD] = { .type = NLA_U32 }, + [ATH_TM_ATTR_DATA] = { .type = NLA_BINARY, + .len = ATH_TM_DATA_MAX_LEN }, + [ATH_TM_ATTR_WMI_CMDID] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MAJOR] = { .type = NLA_U32 }, + [ATH_TM_ATTR_VERSION_MINOR] = { .type = NLA_U32 }, +}; + +static struct ath12k *ath12k_tm_get_ar(struct ath12k_base *ab) +{ + struct ath12k_pdev *pdev; + struct ath12k *ar; + int i; + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + + if (ar && ar->ah->state == ATH12K_HW_STATE_TM) + return ar; + } + + return NULL; +} + +void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ + struct sk_buff *nl_skb; + struct ath12k *ar; + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "testmode event wmi cmd_id %d skb length %d\n", + cmd_id, skb->len); + + ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", skb->data, skb->len); + + ar = ath12k_tm_get_ar(ab); + if (!ar) { + ath12k_warn(ab, "testmode event not handled due to invalid pdev\n"); + return; + } + + spin_lock_bh(&ar->data_lock); + + nl_skb = cfg80211_testmode_alloc_event_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32)) + + nla_total_size(skb->len), + GFP_ATOMIC); + spin_unlock_bh(&ar->data_lock); + + if (!nl_skb) { + ath12k_warn(ab, + "failed to allocate skb for unsegmented testmode wmi event\n"); + return; + } + + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, ATH_TM_CMD_WMI) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, skb->len, skb->data)) { + ath12k_warn(ab, "failed to populate testmode unsegmented event\n"); + kfree_skb(nl_skb); + return; + } + + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); +} + +void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *ftm_msg, + u16 length) +{ + struct sk_buff *nl_skb; + struct ath12k *ar; + u32 data_pos, pdev_id; + u16 datalen; + u8 total_segments, current_seq; + u8 const *buf_pos; + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "testmode event wmi cmd_id %d ftm event msg %p datalen %d\n", + cmd_id, ftm_msg, length); + ath12k_dbg_dump(ab, ATH12K_DBG_TESTMODE, NULL, "", ftm_msg, length); + pdev_id = DP_HW2SW_MACID(le32_to_cpu(ftm_msg->seg_hdr.pdev_id)); + + if (pdev_id >= ab->num_radios) { + ath12k_warn(ab, "testmode event not handled due to invalid pdev id\n"); + return; + } + + ar = ab->pdevs[pdev_id].ar; + + if (!ar) { + ath12k_warn(ab, "testmode event not handled due to absence of pdev\n"); + return; + } + + current_seq = le32_get_bits(ftm_msg->seg_hdr.segmentinfo, + ATH12K_FTM_SEGHDR_CURRENT_SEQ); + total_segments = le32_get_bits(ftm_msg->seg_hdr.segmentinfo, + ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS); + datalen = length - (sizeof(struct ath12k_wmi_ftm_seg_hdr_params)); + buf_pos = ftm_msg->data; + + if (current_seq == 0) { + ab->ftm_event_obj.expected_seq = 0; + ab->ftm_event_obj.data_pos = 0; + } + + data_pos = ab->ftm_event_obj.data_pos; + + if ((data_pos + datalen) > ATH_FTM_EVENT_MAX_BUF_LENGTH) { + ath12k_warn(ab, + "Invalid event length date_pos[%d] datalen[%d]\n", + data_pos, datalen); + return; + } + + memcpy(&ab->ftm_event_obj.eventdata[data_pos], buf_pos, datalen); + data_pos += datalen; + + if (++ab->ftm_event_obj.expected_seq != total_segments) { + ab->ftm_event_obj.data_pos = data_pos; + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "partial data received current_seq[%d], total_seg[%d]\n", + current_seq, total_segments); + return; + } + + ath12k_dbg(ab, ATH12K_DBG_TESTMODE, + "total data length[%d] = [%d]\n", + data_pos, ftm_msg->seg_hdr.len); + + spin_lock_bh(&ar->data_lock); + nl_skb = cfg80211_testmode_alloc_event_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32)) + + nla_total_size(data_pos), + GFP_ATOMIC); + spin_unlock_bh(&ar->data_lock); + + if (!nl_skb) { + ath12k_warn(ab, + "failed to allocate skb for testmode wmi event\n"); + return; + } + + if (nla_put_u32(nl_skb, ATH_TM_ATTR_CMD, + ATH_TM_CMD_WMI_FTM) || + nla_put_u32(nl_skb, ATH_TM_ATTR_WMI_CMDID, cmd_id) || + nla_put(nl_skb, ATH_TM_ATTR_DATA, data_pos, + &ab->ftm_event_obj.eventdata[0])) { + ath12k_warn(ab, "failed to populate testmode event"); + kfree_skb(nl_skb); + return; + } + + cfg80211_testmode_event(nl_skb, GFP_ATOMIC); +} + +static int ath12k_tm_cmd_get_version(struct ath12k *ar, struct nlattr *tb[]) +{ + struct sk_buff *skb; + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd get version_major %d version_minor %d\n", + ATH_TESTMODE_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MINOR); + + spin_lock_bh(&ar->data_lock); + skb = cfg80211_testmode_alloc_reply_skb(ar->ah->hw->wiphy, + 2 * nla_total_size(sizeof(u32))); + spin_unlock_bh(&ar->data_lock); + + if (!skb) + return -ENOMEM; + + if (nla_put_u32(skb, ATH_TM_ATTR_VERSION_MAJOR, + ATH_TESTMODE_VERSION_MAJOR) || + nla_put_u32(skb, ATH_TM_ATTR_VERSION_MINOR, + ATH_TESTMODE_VERSION_MINOR)) { + kfree_skb(skb); + return -ENOBUFS; + } + + return cfg80211_testmode_reply(skb); +} + +static int ath12k_tm_cmd_process_ftm(struct ath12k *ar, struct nlattr *tb[]) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct ath12k_wmi_ftm_cmd *ftm_cmd; + int ret = 0; + void *buf; + size_t aligned_len; + u32 cmd_id, buf_len; + u16 chunk_len, total_bytes, num_segments; + u8 segnumber = 0, *bufpos; + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, "ah->state %d\n", ar->ah->state); + if (ar->ah->state != ATH12K_HW_STATE_TM) + return -ENETDOWN; + + if (!tb[ATH_TM_ATTR_DATA]) + return -EINVAL; + + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); + cmd_id = WMI_PDEV_UTF_CMDID; + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buf %p buf_len %d\n", + cmd_id, buf, buf_len); + ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len); + bufpos = buf; + total_bytes = buf_len; + num_segments = total_bytes / MAX_WMI_UTF_LEN; + + if (buf_len - (num_segments * MAX_WMI_UTF_LEN)) + num_segments++; + + while (buf_len) { + if (buf_len > MAX_WMI_UTF_LEN) + chunk_len = MAX_WMI_UTF_LEN; /* MAX message */ + else + chunk_len = buf_len; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, (chunk_len + + sizeof(struct ath12k_wmi_ftm_cmd))); + + if (!skb) + return -ENOMEM; + + ftm_cmd = (struct ath12k_wmi_ftm_cmd *)skb->data; + aligned_len = chunk_len + sizeof(struct ath12k_wmi_ftm_seg_hdr_params); + ftm_cmd->tlv_header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_BYTE, aligned_len); + ftm_cmd->seg_hdr.len = cpu_to_le32(total_bytes); + ftm_cmd->seg_hdr.msgref = cpu_to_le32(ar->ftm_msgref); + ftm_cmd->seg_hdr.segmentinfo = + le32_encode_bits(num_segments, + ATH12K_FTM_SEGHDR_TOTAL_SEGMENTS) | + le32_encode_bits(segnumber, + ATH12K_FTM_SEGHDR_CURRENT_SEQ); + ftm_cmd->seg_hdr.pdev_id = cpu_to_le32(ar->pdev->pdev_id); + segnumber++; + memcpy(&ftm_cmd->data, bufpos, chunk_len); + ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id); + + if (ret) { + ath12k_warn(ar->ab, "ftm wmi command fail: %d\n", ret); + kfree_skb(skb); + return ret; + } + + buf_len -= chunk_len; + bufpos += chunk_len; + } + + ++ar->ftm_msgref; + return ret; +} + +static int ath12k_tm_cmd_testmode_start(struct ath12k *ar, struct nlattr *tb[]) +{ + if (ar->ah->state == ATH12K_HW_STATE_TM) + return -EALREADY; + + if (ar->ah->state != ATH12K_HW_STATE_OFF) + return -EBUSY; + + ar->ab->ftm_event_obj.eventdata = kzalloc(ATH_FTM_EVENT_MAX_BUF_LENGTH, + GFP_KERNEL); + + if (!ar->ab->ftm_event_obj.eventdata) + return -ENOMEM; + + ar->ah->state = ATH12K_HW_STATE_TM; + ar->ftm_msgref = 0; + return 0; +} + +static int ath12k_tm_cmd_wmi(struct ath12k *ar, struct nlattr *tb[]) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct wmi_pdev_set_param_cmd *cmd; + int ret = 0, tag; + void *buf; + u32 cmd_id, buf_len; + + if (!tb[ATH_TM_ATTR_DATA]) + return -EINVAL; + + if (!tb[ATH_TM_ATTR_WMI_CMDID]) + return -EINVAL; + + buf = nla_data(tb[ATH_TM_ATTR_DATA]); + buf_len = nla_len(tb[ATH_TM_ATTR_DATA]); + + if (!buf_len) { + ath12k_warn(ar->ab, "No data present in testmode command\n"); + return -EINVAL; + } + + cmd_id = nla_get_u32(tb[ATH_TM_ATTR_WMI_CMDID]); + + cmd = buf; + tag = le32_get_bits(cmd->tlv_header, WMI_TLV_TAG); + + if (tag == WMI_TAG_PDEV_SET_PARAM_CMD) + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + + ath12k_dbg(ar->ab, ATH12K_DBG_TESTMODE, + "testmode cmd wmi cmd_id %d buf length %d\n", + cmd_id, buf_len); + + ath12k_dbg_dump(ar->ab, ATH12K_DBG_TESTMODE, NULL, "", buf, buf_len); + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + + if (!skb) + return -ENOMEM; + + memcpy(skb->data, buf, buf_len); + + ret = ath12k_wmi_cmd_send(wmi, skb, cmd_id); + if (ret) { + dev_kfree_skb(skb); + ath12k_warn(ar->ab, "failed to transmit wmi command (testmode): %d\n", + ret); + } + + return ret; +} + +int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct ath12k_hw *ah = hw->priv; + struct ath12k *ar = NULL; + struct nlattr *tb[ATH_TM_ATTR_MAX + 1]; + struct ath12k_base *ab; + struct wiphy *wiphy = hw->wiphy; + int ret; + + lockdep_assert_held(&wiphy->mtx); + + ret = nla_parse(tb, ATH_TM_ATTR_MAX, data, len, ath12k_tm_policy, + NULL); + if (ret) + return ret; + + if (!tb[ATH_TM_ATTR_CMD]) + return -EINVAL; + + /* TODO: have to handle ar for MLO case */ + if (ah->num_radio) + ar = ah->radio; + + if (!ar) + return -EINVAL; + + ab = ar->ab; + switch (nla_get_u32(tb[ATH_TM_ATTR_CMD])) { + case ATH_TM_CMD_WMI: + return ath12k_tm_cmd_wmi(ar, tb); + case ATH_TM_CMD_TESTMODE_START: + return ath12k_tm_cmd_testmode_start(ar, tb); + case ATH_TM_CMD_GET_VERSION: + return ath12k_tm_cmd_get_version(ar, tb); + case ATH_TM_CMD_WMI_FTM: + set_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags); + return ath12k_tm_cmd_process_ftm(ar, tb); + default: + return -EOPNOTSUPP; + } +} diff --git a/drivers/net/wireless/ath/ath12k/testmode.h b/drivers/net/wireless/ath/ath12k/testmode.h new file mode 100644 index 000000000000..ef6ab21d19b8 --- /dev/null +++ b/drivers/net/wireless/ath/ath12k/testmode.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: BSD-3-Clause-Clear */ +/* + * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. + */ + +#include "core.h" +#include "hif.h" + +#ifdef CONFIG_NL80211_TESTMODE + +void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb); +void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *ftm_msg, + u16 length); +int ath12k_tm_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); + +#else + +static inline void ath12k_tm_wmi_event_unsegmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ +} + +static inline void ath12k_tm_process_event(struct ath12k_base *ab, u32 cmd_id, + const struct ath12k_wmi_ftm_event *msg, + u16 length) +{ +} + +static inline int ath12k_tm_cmd(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + void *data, int len) +{ + return 0; +} + +#endif diff --git a/drivers/net/wireless/ath/ath12k/wmi.c b/drivers/net/wireless/ath/ath12k/wmi.c index abb510d235a5..60e2444fe08c 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.c +++ b/drivers/net/wireless/ath/ath12k/wmi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/skbuff.h> #include <linux/ctype.h> @@ -15,16 +15,23 @@ #include <linux/time.h> #include <linux/of.h> #include "core.h" +#include "debugfs.h" #include "debug.h" #include "mac.h" #include "hw.h" #include "peer.h" #include "p2p.h" +#include "testmode.h" struct ath12k_wmi_svc_ready_parse { bool wmi_svc_bitmap_done; }; +struct wmi_tlv_fw_stats_parse { + const struct wmi_stats_event *ev; + struct ath12k_fw_stats *stats; +}; + struct ath12k_wmi_dma_ring_caps_parse { struct ath12k_wmi_dma_ring_caps_params *dma_ring_caps; u32 n_dma_ring_caps; @@ -171,9 +178,11 @@ static const struct ath12k_wmi_tlv_policy ath12k_wmi_tlv_policies[] = { .min_len = sizeof(struct ath12k_wmi_p2p_noa_info) }, [WMI_TAG_P2P_NOA_EVENT] = { .min_len = sizeof(struct wmi_p2p_noa_event) }, + [WMI_TAG_11D_NEW_COUNTRY_EVENT] = { + .min_len = sizeof(struct wmi_11d_new_cc_event) }, }; -static __le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len) +__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len) { return le32_encode_bits(cmd, WMI_TLV_TAG) | le32_encode_bits(len, WMI_TLV_LEN); @@ -514,10 +523,10 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, * band to band for a single radio, need to see how this should be * handled. */ - if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2G_CAP) { + if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2GHZ_CAP) { pdev_cap->tx_chain_mask = le32_to_cpu(mac_caps->tx_chain_mask_2g); pdev_cap->rx_chain_mask = le32_to_cpu(mac_caps->rx_chain_mask_2g); - } else if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5G_CAP) { + } else if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5GHZ_CAP) { pdev_cap->vht_cap = le32_to_cpu(mac_caps->vht_cap_info_5g); pdev_cap->vht_mcs = le32_to_cpu(mac_caps->vht_supp_mcs_5g); pdev_cap->he_mcs = le32_to_cpu(mac_caps->he_supp_mcs_5g); @@ -540,7 +549,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, pdev_cap->rx_chain_mask_shift = find_first_bit((unsigned long *)&pdev_cap->rx_chain_mask, 32); - if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2G_CAP) { + if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_2GHZ_CAP) { cap_band = &pdev_cap->band[NL80211_BAND_2GHZ]; cap_band->phy_id = le32_to_cpu(mac_caps->phy_id); cap_band->max_bw_supported = le32_to_cpu(mac_caps->max_bw_supported_2g); @@ -560,7 +569,7 @@ ath12k_pull_mac_phy_cap_svc_ready_ext(struct ath12k_wmi_pdev *wmi_handle, le32_to_cpu(mac_caps->he_ppet2g.ppet16_ppet8_ru3_ru0[i]); } - if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5G_CAP) { + if (le32_to_cpu(mac_caps->supported_bands) & WMI_HOST_WLAN_5GHZ_CAP) { cap_band = &pdev_cap->band[NL80211_BAND_5GHZ]; cap_band->phy_id = le32_to_cpu(mac_caps->phy_id); cap_band->max_bw_supported = @@ -814,6 +823,39 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id, return ret; } +int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id, + u32 vdev_id, u32 pdev_id) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_request_stats_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_request_stats_cmd *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_REQUEST_STATS_CMD, + sizeof(*cmd)); + + cmd->stats_id = cpu_to_le32(stats_id); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->pdev_id = cpu_to_le32(pdev_id); + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_STATS_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_REQUEST_STATS cmd\n"); + dev_kfree_skb(skb); + } + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "WMI request stats 0x%x vdev id %d pdev id %d\n", + stats_id, vdev_id, pdev_id); + + return ret; +} + int ath12k_wmi_vdev_create(struct ath12k *ar, u8 *macaddr, struct ath12k_wmi_vdev_create_arg *args) { @@ -998,14 +1040,32 @@ int ath12k_wmi_vdev_down(struct ath12k *ar, u8 vdev_id) static void ath12k_wmi_put_wmi_channel(struct ath12k_wmi_channel_params *chan, struct wmi_vdev_start_req_arg *arg) { + u32 center_freq1 = arg->band_center_freq1; + memset(chan, 0, sizeof(*chan)); chan->mhz = cpu_to_le32(arg->freq); - chan->band_center_freq1 = cpu_to_le32(arg->band_center_freq1); - if (arg->mode == MODE_11AC_VHT80_80) + chan->band_center_freq1 = cpu_to_le32(center_freq1); + if (arg->mode == MODE_11BE_EHT320) { + if (arg->freq > center_freq1) + chan->band_center_freq1 = cpu_to_le32(center_freq1 + 80); + else + chan->band_center_freq1 = cpu_to_le32(center_freq1 - 80); + + chan->band_center_freq2 = cpu_to_le32(center_freq1); + + } else if (arg->mode == MODE_11BE_EHT160) { + if (arg->freq > center_freq1) + chan->band_center_freq1 = cpu_to_le32(center_freq1 + 40); + else + chan->band_center_freq1 = cpu_to_le32(center_freq1 - 40); + + chan->band_center_freq2 = cpu_to_le32(center_freq1); + } else if (arg->mode == MODE_11BE_EHT80_80) { chan->band_center_freq2 = cpu_to_le32(arg->band_center_freq2); - else + } else { chan->band_center_freq2 = 0; + } chan->info |= le32_encode_bits(arg->mode, WMI_CHAN_INFO_MODE); if (arg->passive) @@ -1888,14 +1948,19 @@ int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id, return ret; } -int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, +int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, struct ieee80211_mutable_offsets *offs, struct sk_buff *bcn, struct ath12k_wmi_bcn_tmpl_ema_arg *ema_args) { + struct ath12k *ar = arvif->ar; struct ath12k_wmi_pdev *wmi = ar->wmi; + struct ath12k_base *ab = ar->ab; struct wmi_bcn_tmpl_cmd *cmd; struct ath12k_wmi_bcn_prb_info_params *bcn_prb_info; + struct ath12k_vif *ahvif = arvif->ahvif; + struct ieee80211_bss_conf *conf; + u32 vdev_id = arvif->vdev_id; struct wmi_tlv *tlv; struct sk_buff *skb; u32 ema_params = 0; @@ -1903,6 +1968,14 @@ int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, int ret, len; size_t aligned_len = roundup(bcn->len, 4); + conf = ath12k_mac_get_link_bss_conf(arvif); + if (!conf) { + ath12k_warn(ab, + "unable to access bss link conf in beacon template command for vif %pM link %u\n", + ahvif->vif->addr, arvif->link_id); + return -EINVAL; + } + len = sizeof(*cmd) + sizeof(*bcn_prb_info) + TLV_HDR_SIZE + aligned_len; skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); @@ -1914,8 +1987,16 @@ int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, sizeof(*cmd)); cmd->vdev_id = cpu_to_le32(vdev_id); cmd->tim_ie_offset = cpu_to_le32(offs->tim_offset); - cmd->csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[0]); - cmd->ext_csa_switch_count_offset = cpu_to_le32(offs->cntdwn_counter_offs[1]); + + if (conf->csa_active) { + cmd->csa_switch_count_offset = + cpu_to_le32(offs->cntdwn_counter_offs[0]); + cmd->ext_csa_switch_count_offset = + cpu_to_le32(offs->cntdwn_counter_offs[1]); + cmd->csa_event_bitmap = cpu_to_le32(0xFFFFFFFF); + arvif->current_cntdown_counter = bcn->data[offs->cntdwn_counter_offs[0]]; + } + cmd->buf_len = cpu_to_le32(bcn->len); cmd->mbssid_ie_offset = cpu_to_le32(offs->mbssid_off); if (ema_args) { @@ -1945,7 +2026,7 @@ int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, ret = ath12k_wmi_cmd_send(wmi, skb, WMI_BCN_TMPL_CMDID); if (ret) { - ath12k_warn(ar->ab, "failed to send WMI_BCN_TMPL_CMDID\n"); + ath12k_warn(ab, "failed to send WMI_BCN_TMPL_CMDID\n"); dev_kfree_skb(skb); } @@ -2106,9 +2187,10 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar, struct sk_buff *skb; struct wmi_tlv *tlv; void *ptr; - u32 peer_legacy_rates_align; - u32 peer_ht_rates_align; + u32 peer_legacy_rates_align, eml_pad_delay, eml_trans_delay; + u32 peer_ht_rates_align, eml_trans_timeout; int i, ret, len; + u16 eml_cap; __le32 v; peer_legacy_rates_align = roundup(arg->peer_legacy_rates.num_rates, @@ -2280,6 +2362,24 @@ int ath12k_wmi_send_peer_assoc_cmd(struct ath12k *ar, ml_params->logical_link_idx = cpu_to_le32(arg->ml.logical_link_idx); ml_params->ml_peer_id = cpu_to_le32(arg->ml.ml_peer_id); ml_params->ieee_link_id = cpu_to_le32(arg->ml.ieee_link_id); + + eml_cap = arg->ml.eml_cap; + if (u16_get_bits(eml_cap, IEEE80211_EML_CAP_EMLSR_SUPP)) { + /* Padding delay */ + eml_pad_delay = ieee80211_emlsr_pad_delay_in_us(eml_cap); + ml_params->emlsr_padding_delay_us = cpu_to_le32(eml_pad_delay); + /* Transition delay */ + eml_trans_delay = ieee80211_emlsr_trans_delay_in_us(eml_cap); + ml_params->emlsr_trans_delay_us = cpu_to_le32(eml_trans_delay); + /* Transition timeout */ + eml_trans_timeout = ieee80211_eml_trans_timeout_in_us(eml_cap); + ml_params->emlsr_trans_timeout_us = + cpu_to_le32(eml_trans_timeout); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "wmi peer %pM emlsr padding delay %u, trans delay %u trans timeout %u", + arg->peer_mac, eml_pad_delay, eml_trans_delay, + eml_trans_timeout); + } + ptr += sizeof(*ml_params); skip_ml_params: @@ -2291,7 +2391,7 @@ skip_ml_params: for (i = 0; i < arg->peer_eht_mcs_count; i++) { eht_mcs = ptr; - eht_mcs->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HE_RATE_SET, + eht_mcs->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_EHT_RATE_SET, sizeof(*eht_mcs)); eht_mcs->rx_mcs_set = cpu_to_le32(arg->peer_eht_rx_mcs_set[i]); @@ -2299,6 +2399,10 @@ skip_ml_params: ptr += sizeof(*eht_mcs); } + /* Update MCS15 capability */ + if (arg->eht_disable_mcs15) + cmd->peer_eht_ops = cpu_to_le32(IEEE80211_EHT_OPER_MCS15_DISABLE); + tlv = ptr; len = arg->ml.enabled ? arg->ml.num_partner_links * sizeof(*partner_info) : 0; /* fill ML Partner links */ @@ -2339,7 +2443,7 @@ skip_ml_params: send: ath12k_dbg(ar->ab, ATH12K_DBG_WMI, - "wmi peer assoc vdev id %d assoc id %d peer mac %pM peer_flags %x rate_caps %x peer_caps %x listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x peer_flags_ext %x eht mac_cap %x %x eht phy_cap %x %x %x\n", + "wmi peer assoc vdev id %d assoc id %d peer mac %pM peer_flags %x rate_caps %x peer_caps %x listen_intval %d ht_caps %x max_mpdu %d nss %d phymode %d peer_mpdu_density %d vht_caps %x he cap_info %x he ops %x he cap_info_ext %x he phy %x %x %x peer_bw_rxnss_override %x peer_flags_ext %x eht mac_cap %x %x eht phy_cap %x %x %x peer_eht_ops %x\n", cmd->vdev_id, cmd->peer_associd, arg->peer_mac, cmd->peer_flags, cmd->peer_rate_caps, cmd->peer_caps, cmd->peer_listen_intval, cmd->peer_ht_caps, @@ -2352,7 +2456,7 @@ send: cmd->peer_bw_rxnss_override, cmd->peer_flags_ext, cmd->peer_eht_cap_mac[0], cmd->peer_eht_cap_mac[1], cmd->peer_eht_cap_phy[0], cmd->peer_eht_cap_phy[1], - cmd->peer_eht_cap_phy[2]); + cmd->peer_eht_cap_phy[2], cmd->peer_eht_ops); ret = ath12k_wmi_cmd_send(wmi, skb, WMI_PEER_ASSOC_CMDID); if (ret) { @@ -2373,8 +2477,8 @@ void ath12k_wmi_start_scan_init(struct ath12k *ar, arg->dwell_time_active = 50; arg->dwell_time_active_2g = 0; arg->dwell_time_passive = 150; - arg->dwell_time_active_6g = 40; - arg->dwell_time_passive_6g = 30; + arg->dwell_time_active_6g = 70; + arg->dwell_time_passive_6g = 70; arg->min_rest_time = 50; arg->max_rest_time = 500; arg->repeat_probe_time = 0; @@ -2531,7 +2635,10 @@ int ath12k_wmi_send_scan_start_cmd(struct ath12k *ar, cmd->scan_id = cpu_to_le32(arg->scan_id); cmd->scan_req_id = cpu_to_le32(arg->scan_req_id); cmd->vdev_id = cpu_to_le32(arg->vdev_id); - cmd->scan_priority = cpu_to_le32(arg->scan_priority); + if (ar->state_11d == ATH12K_11D_PREPARING) + arg->scan_priority = WMI_SCAN_PRIORITY_MEDIUM; + else + arg->scan_priority = WMI_SCAN_PRIORITY_LOW; cmd->notify_scan_events = cpu_to_le32(arg->notify_scan_events); ath12k_wmi_copy_scan_event_cntrl_flags(cmd, arg); @@ -2794,6 +2901,8 @@ int ath12k_wmi_send_scan_chan_list_cmd(struct ath12k *ar, WMI_CHAN_REG_INFO1_REG_CLS); *reg2 |= le32_encode_bits(channel_arg->antennamax, WMI_CHAN_REG_INFO2_ANT_MAX); + *reg2 |= le32_encode_bits(channel_arg->maxregpower, + WMI_CHAN_REG_INFO2_MAX_TX_PWR); ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI chan scan list chan[%d] = %u, chan_info->info %8x\n", @@ -3251,6 +3360,110 @@ out: return ret; } +int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar, + struct wmi_set_current_country_arg *arg) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_set_current_country_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_set_current_country_cmd *)skb->data; + cmd->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_SET_CURRENT_COUNTRY_CMD, + sizeof(*cmd)); + + cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id); + memcpy(&cmd->new_alpha2, &arg->alpha2, sizeof(arg->alpha2)); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_SET_CURRENT_COUNTRY_CMDID); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "set current country pdev id %d alpha2 %c%c\n", + ar->pdev->pdev_id, + arg->alpha2[0], + arg->alpha2[1]); + + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_SET_CURRENT_COUNTRY_CMDID: %d\n", ret); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar, + struct wmi_11d_scan_start_arg *arg) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_11d_scan_start_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_11d_scan_start_cmd *)skb->data; + cmd->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_11D_SCAN_START_CMD, + sizeof(*cmd)); + + cmd->vdev_id = cpu_to_le32(arg->vdev_id); + cmd->scan_period_msec = cpu_to_le32(arg->scan_period_msec); + cmd->start_interval_msec = cpu_to_le32(arg->start_interval_msec); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_START_CMDID); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "send 11d scan start vdev id %d period %d ms internal %d ms\n", + arg->vdev_id, arg->scan_period_msec, + arg->start_interval_msec); + + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_11D_SCAN_START_CMDID: %d\n", ret); + dev_kfree_skb(skb); + } + + return ret; +} + +int ath12k_wmi_send_11d_scan_stop_cmd(struct ath12k *ar, u32 vdev_id) +{ + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_11d_scan_stop_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_11d_scan_stop_cmd *)skb->data; + cmd->tlv_header = + ath12k_wmi_tlv_cmd_hdr(WMI_TAG_11D_SCAN_STOP_CMD, + sizeof(*cmd)); + + cmd->vdev_id = cpu_to_le32(vdev_id); + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_11D_SCAN_STOP_CMDID); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "send 11d scan stop vdev id %d\n", + cmd->vdev_id); + + if (ret) { + ath12k_warn(ar->ab, + "failed to send WMI_11D_SCAN_STOP_CMDID: %d\n", ret); + dev_kfree_skb(skb); + } + + return ret; +} + int ath12k_wmi_send_twt_enable_cmd(struct ath12k *ar, u32 pdev_id) { @@ -3584,15 +3797,15 @@ ath12k_fill_band_to_mac_param(struct ath12k_base *soc, arg[i].pdev_id = pdev->pdev_id; switch (pdev->cap.supported_bands) { - case WMI_HOST_WLAN_2G_5G_CAP: + case WMI_HOST_WLAN_2GHZ_5GHZ_CAP: arg[i].start_freq = hal_reg_cap->low_2ghz_chan; arg[i].end_freq = hal_reg_cap->high_5ghz_chan; break; - case WMI_HOST_WLAN_2G_CAP: + case WMI_HOST_WLAN_2GHZ_CAP: arg[i].start_freq = hal_reg_cap->low_2ghz_chan; arg[i].end_freq = hal_reg_cap->high_2ghz_chan; break; - case WMI_HOST_WLAN_5G_CAP: + case WMI_HOST_WLAN_5GHZ_CAP: arg[i].start_freq = hal_reg_cap->low_5ghz_chan; arg[i].end_freq = hal_reg_cap->high_5ghz_chan; break; @@ -3603,7 +3816,8 @@ ath12k_fill_band_to_mac_param(struct ath12k_base *soc, } static void -ath12k_wmi_copy_resource_config(struct ath12k_wmi_resource_config_params *wmi_cfg, +ath12k_wmi_copy_resource_config(struct ath12k_base *ab, + struct ath12k_wmi_resource_config_params *wmi_cfg, struct ath12k_wmi_resource_config_arg *tg_cfg) { wmi_cfg->num_vdevs = cpu_to_le32(tg_cfg->num_vdevs); @@ -3670,6 +3884,9 @@ ath12k_wmi_copy_resource_config(struct ath12k_wmi_resource_config_params *wmi_cf WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION); wmi_cfg->host_service_flags = cpu_to_le32(tg_cfg->is_reg_cc_ext_event_supported << WMI_RSRC_CFG_HOST_SVC_FLAG_REG_CC_EXT_SUPPORT_BIT); + if (ab->hw_params->reoq_lut_support) + wmi_cfg->host_service_flags |= + cpu_to_le32(1 << WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT); wmi_cfg->ema_max_vap_cnt = cpu_to_le32(tg_cfg->ema_max_vap_cnt); wmi_cfg->ema_max_profile_period = cpu_to_le32(tg_cfg->ema_max_profile_period); wmi_cfg->flags2 |= cpu_to_le32(WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET); @@ -3710,7 +3927,7 @@ static int ath12k_init_cmd_send(struct ath12k_wmi_pdev *wmi, ptr = skb->data + sizeof(*cmd); cfg = ptr; - ath12k_wmi_copy_resource_config(cfg, &arg->res_cfg); + ath12k_wmi_copy_resource_config(ab, cfg, &arg->res_cfg); cfg->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_RESOURCE_CONFIG, sizeof(*cfg)); @@ -4539,6 +4756,7 @@ static int ath12k_service_ready_ext_event(struct ath12k_base *ab, return 0; err: + kfree(svc_rdy_ext.mac_phy_caps); ath12k_wmi_free_dbring_caps(ab); return ret; } @@ -4637,7 +4855,7 @@ ath12k_wmi_tlv_mac_phy_caps_ext_parse(struct ath12k_base *ab, bands = pdev->cap.supported_bands; } - if (bands & WMI_HOST_WLAN_2G_CAP) { + if (bands & WMI_HOST_WLAN_2GHZ_CAP) { ath12k_wmi_eht_caps_parse(pdev, NL80211_BAND_2GHZ, caps->eht_cap_mac_info_2ghz, caps->eht_cap_phy_info_2ghz, @@ -4646,7 +4864,7 @@ ath12k_wmi_tlv_mac_phy_caps_ext_parse(struct ath12k_base *ab, caps->eht_cap_info_internal); } - if (bands & WMI_HOST_WLAN_5G_CAP) { + if (bands & WMI_HOST_WLAN_5GHZ_CAP) { ath12k_wmi_eht_caps_parse(pdev, NL80211_BAND_5GHZ, caps->eht_cap_mac_info_5ghz, caps->eht_cap_phy_info_5ghz, @@ -4860,7 +5078,7 @@ static u8 ath12k_wmi_ignore_num_extra_rules(struct ath12k_wmi_reg_rule_ext_param for (count = 0; count < num_reg_rules; count++) { start_freq = le32_get_bits(rule[count].freq_info, REG_RULE_START_FREQ); - if (start_freq >= ATH12K_MIN_6G_FREQ) + if (start_freq >= ATH12K_MIN_6GHZ_FREQ) num_invalid_5ghz_rules++; } @@ -4930,9 +5148,9 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) { num_6g_reg_rules_ap[i] = reg_info->num_6g_reg_rules_ap[i]; - if (num_6g_reg_rules_ap[i] > MAX_6G_REG_RULES) { + if (num_6g_reg_rules_ap[i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6G reg rules for AP mode(%d) exceeds max limit (num_6g_reg_rules_ap: %d, max_rules: %d)\n", - i, num_6g_reg_rules_ap[i], MAX_6G_REG_RULES); + i, num_6g_reg_rules_ap[i], MAX_6GHZ_REG_RULES); kfree(tb); return -EINVAL; } @@ -4953,9 +5171,9 @@ static int ath12k_pull_reg_chan_list_ext_update_ev(struct ath12k_base *ab, reg_info->num_6g_reg_rules_cl[WMI_REG_VLP_AP][i]; total_reg_rules += num_6g_reg_rules_cl[WMI_REG_VLP_AP][i]; - if (num_6g_reg_rules_cl[WMI_REG_INDOOR_AP][i] > MAX_6G_REG_RULES || - num_6g_reg_rules_cl[WMI_REG_STD_POWER_AP][i] > MAX_6G_REG_RULES || - num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6G_REG_RULES) { + if (num_6g_reg_rules_cl[WMI_REG_INDOOR_AP][i] > MAX_6GHZ_REG_RULES || + num_6g_reg_rules_cl[WMI_REG_STD_POWER_AP][i] > MAX_6GHZ_REG_RULES || + num_6g_reg_rules_cl[WMI_REG_VLP_AP][i] > MAX_6GHZ_REG_RULES) { ath12k_warn(ab, "Num 6g client reg rules exceeds max limit, for client(type: %d)\n", i); kfree(tb); @@ -5871,30 +6089,62 @@ static void ath12k_wmi_op_ep_tx_credits(struct ath12k_base *ab) wake_up(&ab->wmi_ab.tx_credits_wq); } -static void ath12k_wmi_htc_tx_complete(struct ath12k_base *ab, - struct sk_buff *skb) +static int ath12k_reg_11d_new_cc_event(struct ath12k_base *ab, struct sk_buff *skb) { - dev_kfree_skb(skb); -} + const struct wmi_11d_new_cc_event *ev; + struct ath12k *ar; + struct ath12k_pdev *pdev; + const void **tb; + int ret, i; -static bool ath12k_reg_is_world_alpha(char *alpha) -{ - if (alpha[0] == '0' && alpha[1] == '0') - return true; + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse tlv: %d\n", ret); + return ret; + } - if (alpha[0] == 'n' && alpha[1] == 'a') - return true; + ev = tb[WMI_TAG_11D_NEW_COUNTRY_EVENT]; + if (!ev) { + kfree(tb); + ath12k_warn(ab, "failed to fetch 11d new cc ev"); + return -EPROTO; + } + + spin_lock_bh(&ab->base_lock); + memcpy(&ab->new_alpha2, &ev->new_alpha2, REG_ALPHA2_LEN); + spin_unlock_bh(&ab->base_lock); + + ath12k_dbg(ab, ATH12K_DBG_WMI, "wmi 11d new cc %c%c\n", + ab->new_alpha2[0], + ab->new_alpha2[1]); - return false; + kfree(tb); + + for (i = 0; i < ab->num_radios; i++) { + pdev = &ab->pdevs[i]; + ar = pdev->ar; + ar->state_11d = ATH12K_11D_IDLE; + ar->ah->regd_updated = false; + complete(&ar->completed_11d_scan); + } + + queue_work(ab->workqueue, &ab->update_11d_work); + + return 0; +} + +static void ath12k_wmi_htc_tx_complete(struct ath12k_base *ab, + struct sk_buff *skb) +{ + dev_kfree_skb(skb); } static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *skb) { - struct ath12k_reg_info *reg_info = NULL; - struct ieee80211_regdomain *regd = NULL; - bool intersect = false; - int ret = 0, pdev_idx, i, j; - struct ath12k *ar; + struct ath12k_reg_info *reg_info; + u8 pdev_idx; + int ret; reg_info = kzalloc(sizeof(*reg_info), GFP_ATOMIC); if (!reg_info) { @@ -5903,86 +6153,52 @@ static int ath12k_reg_chan_list_event(struct ath12k_base *ab, struct sk_buff *sk } ret = ath12k_pull_reg_chan_list_ext_update_ev(ab, skb, reg_info); - if (ret) { ath12k_warn(ab, "failed to extract regulatory info from received event\n"); - goto fallback; + goto mem_free; } - if (reg_info->status_code != REG_SET_CC_STATUS_PASS) { - /* In case of failure to set the requested ctry, - * fw retains the current regd. We print a failure info - * and return from here. + ret = ath12k_reg_validate_reg_info(ab, reg_info); + if (ret == ATH12K_REG_STATUS_FALLBACK) { + ath12k_warn(ab, "failed to validate reg info %d\n", ret); + /* firmware has successfully switches to new regd but host can not + * continue, so free reginfo and fallback to old regd */ - ath12k_warn(ab, "Failed to set the requested Country regulatory setting\n"); + goto mem_free; + } else if (ret == ATH12K_REG_STATUS_DROP) { + /* reg info is valid but we will not store it and + * not going to create new regd for it + */ + ret = ATH12K_REG_STATUS_VALID; goto mem_free; } + /* free old reg_info if it exist */ pdev_idx = reg_info->phy_id; - - if (pdev_idx >= ab->num_radios) { - /* Process the event for phy0 only if single_pdev_only - * is true. If pdev_idx is valid but not 0, discard the - * event. Otherwise, it goes to fallback. - */ - if (ab->hw_params->single_pdev_only && - pdev_idx < ab->hw_params->num_rxdma_per_pdev) - goto mem_free; - else - goto fallback; + if (ab->reg_info[pdev_idx]) { + ath12k_reg_reset_reg_info(ab->reg_info[pdev_idx]); + kfree(ab->reg_info[pdev_idx]); } - - /* Avoid multiple overwrites to default regd, during core - * stop-start after mac registration. + /* reg_info is valid, we store it for later use + * even below regd build failed */ - if (ab->default_regd[pdev_idx] && !ab->new_regd[pdev_idx] && - !memcmp(ab->default_regd[pdev_idx]->alpha2, - reg_info->alpha2, 2)) - goto mem_free; + ab->reg_info[pdev_idx] = reg_info; - /* Intersect new rules with default regd if a new country setting was - * requested, i.e a default regd was already set during initialization - * and the regd coming from this event has a valid country info. - */ - if (ab->default_regd[pdev_idx] && - !ath12k_reg_is_world_alpha((char *) - ab->default_regd[pdev_idx]->alpha2) && - !ath12k_reg_is_world_alpha((char *)reg_info->alpha2)) - intersect = true; - - regd = ath12k_reg_build_regd(ab, reg_info, intersect); - if (!regd) { - ath12k_warn(ab, "failed to build regd from reg_info\n"); + ret = ath12k_reg_handle_chan_list(ab, reg_info, WMI_VDEV_TYPE_UNSPEC, + IEEE80211_REG_UNSET_AP); + if (ret) { + ath12k_warn(ab, "failed to handle chan list %d\n", ret); goto fallback; } - spin_lock(&ab->base_lock); - if (test_bit(ATH12K_FLAG_REGISTERED, &ab->dev_flags)) { - /* Once mac is registered, ar is valid and all CC events from - * fw is considered to be received due to user requests - * currently. - * Free previously built regd before assigning the newly - * generated regd to ar. NULL pointer handling will be - * taken care by kfree itself. - */ - ar = ab->pdevs[pdev_idx].ar; - kfree(ab->new_regd[pdev_idx]); - ab->new_regd[pdev_idx] = regd; - queue_work(ab->workqueue, &ar->regd_update_work); - } else { - /* Multiple events for the same *ar is not expected. But we - * can still clear any previously stored default_regd if we - * are receiving this event for the same radio by mistake. - * NULL pointer handling will be taken care by kfree itself. - */ - kfree(ab->default_regd[pdev_idx]); - /* This regd would be applied during mac registration */ - ab->default_regd[pdev_idx] = regd; - } - ab->dfs_region = reg_info->dfs_region; - spin_unlock(&ab->base_lock); + goto out; + +mem_free: + ath12k_reg_reset_reg_info(reg_info); + kfree(reg_info); - goto mem_free; + if (ret == ATH12K_REG_STATUS_VALID) + return ret; fallback: /* Fallback to older reg (by sending previous country setting @@ -5994,20 +6210,8 @@ fallback: */ /* TODO: This is rare, but still should also be handled */ WARN_ON(1); -mem_free: - if (reg_info) { - kfree(reg_info->reg_rules_2g_ptr); - kfree(reg_info->reg_rules_5g_ptr); - if (reg_info->is_ext_reg_event) { - for (i = 0; i < WMI_REG_CURRENT_MAX_AP_TYPE; i++) - kfree(reg_info->reg_rules_6g_ap_ptr[i]); - - for (j = 0; j < WMI_REG_CURRENT_MAX_AP_TYPE; j++) - for (i = 0; i < WMI_REG_MAX_CLIENT_TYPE; i++) - kfree(reg_info->reg_rules_6g_client_ptr[j][i]); - } - kfree(reg_info); - } + +out: return ret; } @@ -6163,13 +6367,14 @@ static void ath12k_vdev_start_resp_event(struct ath12k_base *ab, struct sk_buff ar->last_wmi_vdev_start_status = 0; status = le32_to_cpu(vdev_start_resp.status); - if (WARN_ON_ONCE(status)) { ath12k_warn(ab, "vdev start resp error status %d (%s)\n", status, ath12k_wmi_vdev_resp_print(status)); ar->last_wmi_vdev_start_status = status; } + ar->max_allowed_tx_power = (s8)le32_to_cpu(vdev_start_resp.max_allowed_tx_power); + complete(&ar->vdev_setup_done); rcu_read_unlock(); @@ -6255,13 +6460,13 @@ static void ath12k_mgmt_rx_event(struct ath12k_base *ab, struct sk_buff *skb) if (rx_ev.status & WMI_RX_STATUS_ERR_MIC) status->flag |= RX_FLAG_MMIC_ERROR; - if (rx_ev.chan_freq >= ATH12K_MIN_6G_FREQ && - rx_ev.chan_freq <= ATH12K_MAX_6G_FREQ) { + if (rx_ev.chan_freq >= ATH12K_MIN_6GHZ_FREQ && + rx_ev.chan_freq <= ATH12K_MAX_6GHZ_FREQ) { status->band = NL80211_BAND_6GHZ; status->freq = rx_ev.chan_freq; } else if (rx_ev.channel >= 1 && rx_ev.channel <= 14) { status->band = NL80211_BAND_2GHZ; - } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH12K_MAX_5G_CHAN) { + } else if (rx_ev.channel >= 36 && rx_ev.channel <= ATH12K_MAX_5GHZ_CHAN) { status->band = NL80211_BAND_5GHZ; } else { /* Shouldn't happen unless list of advertised channels to @@ -6842,8 +7047,640 @@ static void ath12k_peer_assoc_conf_event(struct ath12k_base *ab, struct sk_buff rcu_read_unlock(); } +static void +ath12k_wmi_fw_vdev_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_vdev *vdev; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + struct ath12k_link_vif *arvif; + u32 len = *length; + u8 *vif_macaddr; + int i; + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath12k VDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + list_for_each_entry(vdev, &fw_stats->vdevs, list) { + arvif = ath12k_mac_get_arvif(ar, vdev->vdev_id); + if (!arvif) + continue; + vif_macaddr = arvif->ahvif->vif->addr; + + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", vdev->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", vif_macaddr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "beacon snr", vdev->beacon_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "data snr", vdev->data_snr); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx frames", vdev->num_rx_frames); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts fail", vdev->num_rts_fail); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rts success", vdev->num_rts_success); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx err", vdev->num_rx_err); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num rx discard", vdev->num_rx_discard); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "num tx not acked", vdev->num_tx_not_acked); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames", i, + vdev->num_tx_frames[i]); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames retries", i, + vdev->num_tx_frames_retries[i]); + + for (i = 0 ; i < WLAN_MAX_AC; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "num tx frames failures", i, + vdev->num_tx_frames_failures[i]); + + for (i = 0 ; i < MAX_TX_RATE_VALUES; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] 0x%08x\n", + "tx rate history", i, + vdev->tx_rate_history[i]); + for (i = 0 ; i < MAX_TX_RATE_VALUES; i++) + len += scnprintf(buf + len, buf_len - len, + "%25s [%02d] %u\n", + "beacon rssi history", i, + vdev->beacon_rssi_history[i]); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; + } +} + +static void +ath12k_wmi_fw_bcn_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_bcn *bcn; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + struct ath12k_link_vif *arvif; + u32 len = *length; + size_t num_bcn; + + num_bcn = list_count_nodes(&fw_stats->bcn); + + len += scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s (%zu)\n", + "ath12k Beacon stats", num_bcn); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "==================="); + + list_for_each_entry(bcn, &fw_stats->bcn, list) { + arvif = ath12k_mac_get_arvif(ar, bcn->vdev_id); + if (!arvif) + continue; + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "VDEV ID", bcn->vdev_id); + len += scnprintf(buf + len, buf_len - len, "%30s %pM\n", + "VDEV MAC address", arvif->ahvif->vif->addr); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================"); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx success", bcn->tx_bcn_succ_cnt); + len += scnprintf(buf + len, buf_len - len, "%30s %u\n", + "Num of beacon tx failures", bcn->tx_bcn_outage_cnt); + + len += scnprintf(buf + len, buf_len - len, "\n"); + *length = len; + } +} + +static void +ath12k_wmi_fw_pdev_base_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length, u64 fw_soc_drop_cnt) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len = scnprintf(buf + len, buf_len - len, "\n"); + len += scnprintf(buf + len, buf_len - len, "%30s\n", + "ath12k PDEV stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Channel noise floor", pdev->ch_noise_floor); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Channel TX power", pdev->chan_tx_power); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX frame count", pdev->tx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX frame count", pdev->rx_frame_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "RX clear count", pdev->rx_clear_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Cycle count", pdev->cycle_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY error count", pdev->phy_err_count); + len += scnprintf(buf + len, buf_len - len, "%30s %10llu\n", + "soc drop count", fw_soc_drop_cnt); + + *length = len; +} + +static void +ath12k_wmi_fw_pdev_tx_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath12k PDEV TX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies queued", pdev->comp_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HTT cookies disp.", pdev->comp_delivered); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDU queued", pdev->msdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU queued", pdev->mpdu_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs dropped", pdev->wmm_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local enqued", pdev->local_enqued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Local freed", pdev->local_freed); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "HW queued", pdev->hw_queued); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs reaped", pdev->hw_reaped); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Num underruns", pdev->underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PPDUs cleaned", pdev->tx_abort); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs requeued", pdev->mpdus_requed); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Excessive retries", pdev->tx_ko); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "HW rate", pdev->data_rc); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Sched self triggers", pdev->self_triggers); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Dropped due to SW retries", + pdev->sw_retry_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Illegal rate phy errors", + pdev->illgl_rate_phy_err); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV continuous xretry", pdev->pdev_cont_xretry); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "TX timeout", pdev->pdev_tx_timeout); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PDEV resets", pdev->pdev_resets); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "Stateless TIDs alloc failures", + pdev->stateless_tid_alloc_failure); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "PHY underrun", pdev->phy_underrun); + len += scnprintf(buf + len, buf_len - len, "%30s %10u\n", + "MPDU is more than txop limit", pdev->txop_ovf); + *length = len; +} + +static void +ath12k_wmi_fw_pdev_rx_stats_dump(const struct ath12k_fw_stats_pdev *pdev, + char *buf, u32 *length) +{ + u32 len = *length; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + len += scnprintf(buf + len, buf_len - len, "\n%30s\n", + "ath12k PDEV RX stats"); + len += scnprintf(buf + len, buf_len - len, "%30s\n\n", + "===================="); + + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Mid PPDU route change", + pdev->mid_ppdu_route_change); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Tot. number of statuses", pdev->status_rcvd); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 0", pdev->r0_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 1", pdev->r1_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 2", pdev->r2_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Extra frags on rings 3", pdev->r3_frags); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to HTT", pdev->htt_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to HTT", pdev->htt_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MSDUs delivered to stack", pdev->loc_msdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDUs delivered to stack", pdev->loc_mpdus); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "Oversized AMSUs", pdev->oversize_amsdu); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors", pdev->phy_errs); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "PHY errors drops", pdev->phy_err_drop); + len += scnprintf(buf + len, buf_len - len, "%30s %10d\n", + "MPDU errors (FCS, MIC, ENC)", pdev->mpdu_errs); + *length = len; +} + +static void +ath12k_wmi_fw_pdev_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + char *buf, u32 *length) +{ + const struct ath12k_fw_stats_pdev *pdev; + u32 len = *length; + + pdev = list_first_entry_or_null(&fw_stats->pdevs, + struct ath12k_fw_stats_pdev, list); + if (!pdev) { + ath12k_warn(ar->ab, "failed to get pdev stats\n"); + return; + } + + ath12k_wmi_fw_pdev_base_stats_dump(pdev, buf, &len, + ar->ab->fw_soc_drop_count); + ath12k_wmi_fw_pdev_tx_stats_dump(pdev, buf, &len); + ath12k_wmi_fw_pdev_rx_stats_dump(pdev, buf, &len); + + *length = len; +} + +void ath12k_wmi_fw_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, + u32 stats_id, char *buf) +{ + u32 len = 0; + u32 buf_len = ATH12K_FW_STATS_BUF_SIZE; + + spin_lock_bh(&ar->data_lock); + + switch (stats_id) { + case WMI_REQUEST_VDEV_STAT: + ath12k_wmi_fw_vdev_stats_dump(ar, fw_stats, buf, &len); + break; + case WMI_REQUEST_BCN_STAT: + ath12k_wmi_fw_bcn_stats_dump(ar, fw_stats, buf, &len); + break; + case WMI_REQUEST_PDEV_STAT: + ath12k_wmi_fw_pdev_stats_dump(ar, fw_stats, buf, &len); + break; + default: + break; + } + + spin_unlock_bh(&ar->data_lock); + + if (len >= buf_len) + buf[len - 1] = 0; + else + buf[len] = 0; + + ath12k_fw_stats_reset(ar); +} + +static void +ath12k_wmi_pull_vdev_stats(const struct wmi_vdev_stats_params *src, + struct ath12k_fw_stats_vdev *dst) +{ + int i; + + dst->vdev_id = le32_to_cpu(src->vdev_id); + dst->beacon_snr = le32_to_cpu(src->beacon_snr); + dst->data_snr = le32_to_cpu(src->data_snr); + dst->num_rx_frames = le32_to_cpu(src->num_rx_frames); + dst->num_rts_fail = le32_to_cpu(src->num_rts_fail); + dst->num_rts_success = le32_to_cpu(src->num_rts_success); + dst->num_rx_err = le32_to_cpu(src->num_rx_err); + dst->num_rx_discard = le32_to_cpu(src->num_rx_discard); + dst->num_tx_not_acked = le32_to_cpu(src->num_tx_not_acked); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames[i] = + le32_to_cpu(src->num_tx_frames[i]); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames_retries[i] = + le32_to_cpu(src->num_tx_frames_retries[i]); + + for (i = 0; i < WLAN_MAX_AC; i++) + dst->num_tx_frames_failures[i] = + le32_to_cpu(src->num_tx_frames_failures[i]); + + for (i = 0; i < MAX_TX_RATE_VALUES; i++) + dst->tx_rate_history[i] = + le32_to_cpu(src->tx_rate_history[i]); + + for (i = 0; i < MAX_TX_RATE_VALUES; i++) + dst->beacon_rssi_history[i] = + le32_to_cpu(src->beacon_rssi_history[i]); +} + +static void +ath12k_wmi_pull_bcn_stats(const struct ath12k_wmi_bcn_stats_params *src, + struct ath12k_fw_stats_bcn *dst) +{ + dst->vdev_id = le32_to_cpu(src->vdev_id); + dst->tx_bcn_succ_cnt = le32_to_cpu(src->tx_bcn_succ_cnt); + dst->tx_bcn_outage_cnt = le32_to_cpu(src->tx_bcn_outage_cnt); +} + +static void +ath12k_wmi_pull_pdev_stats_base(const struct ath12k_wmi_pdev_base_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->ch_noise_floor = a_sle32_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); + dst->rx_clear_count = __le32_to_cpu(src->rx_clear_count); + 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); +} + +static void +ath12k_wmi_pull_pdev_stats_tx(const struct ath12k_wmi_pdev_tx_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->comp_queued = a_sle32_to_cpu(src->comp_queued); + dst->comp_delivered = a_sle32_to_cpu(src->comp_delivered); + dst->msdu_enqued = a_sle32_to_cpu(src->msdu_enqued); + dst->mpdu_enqued = a_sle32_to_cpu(src->mpdu_enqued); + dst->wmm_drop = a_sle32_to_cpu(src->wmm_drop); + dst->local_enqued = a_sle32_to_cpu(src->local_enqued); + dst->local_freed = a_sle32_to_cpu(src->local_freed); + dst->hw_queued = a_sle32_to_cpu(src->hw_queued); + dst->hw_reaped = a_sle32_to_cpu(src->hw_reaped); + dst->underrun = a_sle32_to_cpu(src->underrun); + dst->tx_abort = a_sle32_to_cpu(src->tx_abort); + dst->mpdus_requed = a_sle32_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->stateless_tid_alloc_failure = + __le32_to_cpu(src->stateless_tid_alloc_failure); + dst->phy_underrun = __le32_to_cpu(src->phy_underrun); + dst->txop_ovf = __le32_to_cpu(src->txop_ovf); +} + +static void +ath12k_wmi_pull_pdev_stats_rx(const struct ath12k_wmi_pdev_rx_stats_params *src, + struct ath12k_fw_stats_pdev *dst) +{ + dst->mid_ppdu_route_change = + a_sle32_to_cpu(src->mid_ppdu_route_change); + dst->status_rcvd = a_sle32_to_cpu(src->status_rcvd); + dst->r0_frags = a_sle32_to_cpu(src->r0_frags); + dst->r1_frags = a_sle32_to_cpu(src->r1_frags); + dst->r2_frags = a_sle32_to_cpu(src->r2_frags); + dst->r3_frags = a_sle32_to_cpu(src->r3_frags); + dst->htt_msdus = a_sle32_to_cpu(src->htt_msdus); + dst->htt_mpdus = a_sle32_to_cpu(src->htt_mpdus); + dst->loc_msdus = a_sle32_to_cpu(src->loc_msdus); + dst->loc_mpdus = a_sle32_to_cpu(src->loc_mpdus); + dst->oversize_amsdu = a_sle32_to_cpu(src->oversize_amsdu); + dst->phy_errs = a_sle32_to_cpu(src->phy_errs); + dst->phy_err_drop = a_sle32_to_cpu(src->phy_err_drop); + dst->mpdu_errs = a_sle32_to_cpu(src->mpdu_errs); +} + +static int ath12k_wmi_tlv_fw_stats_data_parse(struct ath12k_base *ab, + struct wmi_tlv_fw_stats_parse *parse, + const void *ptr, + u16 len) +{ + const struct wmi_stats_event *ev = parse->ev; + struct ath12k_fw_stats *stats = parse->stats; + struct ath12k *ar; + struct ath12k_link_vif *arvif; + struct ieee80211_sta *sta; + struct ath12k_sta *ahsta; + struct ath12k_link_sta *arsta; + int i, ret = 0; + const void *data = ptr; + + if (!ev) { + ath12k_warn(ab, "failed to fetch update stats ev"); + return -EPROTO; + } + + if (!stats) + return -EINVAL; + + rcu_read_lock(); + + stats->pdev_id = le32_to_cpu(ev->pdev_id); + ar = ath12k_mac_get_ar_by_pdev_id(ab, stats->pdev_id); + if (!ar) { + ath12k_warn(ab, "invalid pdev id %d in update stats event\n", + le32_to_cpu(ev->pdev_id)); + ret = -EPROTO; + goto exit; + } + + for (i = 0; i < le32_to_cpu(ev->num_vdev_stats); i++) { + const struct wmi_vdev_stats_params *src; + struct ath12k_fw_stats_vdev *dst; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + arvif = ath12k_mac_get_arvif(ar, le32_to_cpu(src->vdev_id)); + if (arvif) { + sta = ieee80211_find_sta_by_ifaddr(ath12k_ar_to_hw(ar), + arvif->bssid, + NULL); + if (sta) { + ahsta = ath12k_sta_to_ahsta(sta); + arsta = &ahsta->deflink; + arsta->rssi_beacon = le32_to_cpu(src->beacon_snr); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "wmi stats vdev id %d snr %d\n", + src->vdev_id, src->beacon_snr); + } else { + ath12k_dbg(ab, ATH12K_DBG_WMI, + "not found station bssid %pM for vdev stat\n", + arvif->bssid); + } + } + + data += sizeof(*src); + len -= sizeof(*src); + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + ath12k_wmi_pull_vdev_stats(src, dst); + stats->stats_id = WMI_REQUEST_VDEV_STAT; + list_add_tail(&dst->list, &stats->vdevs); + } + for (i = 0; i < le32_to_cpu(ev->num_bcn_stats); i++) { + const struct ath12k_wmi_bcn_stats_params *src; + struct ath12k_fw_stats_bcn *dst; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + data += sizeof(*src); + len -= sizeof(*src); + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + ath12k_wmi_pull_bcn_stats(src, dst); + stats->stats_id = WMI_REQUEST_BCN_STAT; + list_add_tail(&dst->list, &stats->bcn); + } + for (i = 0; i < le32_to_cpu(ev->num_pdev_stats); i++) { + const struct ath12k_wmi_pdev_stats_params *src; + struct ath12k_fw_stats_pdev *dst; + + src = data; + if (len < sizeof(*src)) { + ret = -EPROTO; + goto exit; + } + + stats->stats_id = WMI_REQUEST_PDEV_STAT; + + data += sizeof(*src); + len -= sizeof(*src); + + dst = kzalloc(sizeof(*dst), GFP_ATOMIC); + if (!dst) + continue; + + ath12k_wmi_pull_pdev_stats_base(&src->base, dst); + ath12k_wmi_pull_pdev_stats_tx(&src->tx, dst); + ath12k_wmi_pull_pdev_stats_rx(&src->rx, dst); + list_add_tail(&dst->list, &stats->pdevs); + } + +exit: + rcu_read_unlock(); + return ret; +} + +static int ath12k_wmi_tlv_fw_stats_parse(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tlv_fw_stats_parse *parse = data; + int ret = 0; + + switch (tag) { + case WMI_TAG_STATS_EVENT: + parse->ev = ptr; + break; + case WMI_TAG_ARRAY_BYTE: + ret = ath12k_wmi_tlv_fw_stats_data_parse(ab, parse, ptr, len); + break; + default: + break; + } + return ret; +} + +static int ath12k_wmi_pull_fw_stats(struct ath12k_base *ab, struct sk_buff *skb, + struct ath12k_fw_stats *stats) +{ + struct wmi_tlv_fw_stats_parse parse = {}; + + stats->stats_id = 0; + parse.stats = stats; + + return ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tlv_fw_stats_parse, + &parse); +} + static void ath12k_update_stats_event(struct ath12k_base *ab, struct sk_buff *skb) { + struct ath12k_fw_stats stats = {}; + struct ath12k *ar; + int ret; + + INIT_LIST_HEAD(&stats.pdevs); + INIT_LIST_HEAD(&stats.vdevs); + INIT_LIST_HEAD(&stats.bcn); + + ret = ath12k_wmi_pull_fw_stats(ab, skb, &stats); + if (ret) { + ath12k_warn(ab, "failed to pull fw stats: %d\n", ret); + goto free; + } + + ath12k_dbg(ab, ATH12K_DBG_WMI, "event update stats"); + + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, stats.pdev_id); + if (!ar) { + rcu_read_unlock(); + ath12k_warn(ab, "failed to get ar for pdev_id %d: %d\n", + stats.pdev_id, ret); + goto free; + } + + spin_lock_bh(&ar->data_lock); + + /* WMI_REQUEST_PDEV_STAT can be requested via .get_txpower mac ops or via + * debugfs fw stats. Therefore, processing it separately. + */ + if (stats.stats_id == WMI_REQUEST_PDEV_STAT) { + list_splice_tail_init(&stats.pdevs, &ar->fw_stats.pdevs); + ar->fw_stats.fw_stats_done = true; + goto complete; + } + + /* WMI_REQUEST_VDEV_STAT and WMI_REQUEST_BCN_STAT are currently requested only + * via debugfs fw stats. Hence, processing these in debugfs context. + */ + ath12k_debugfs_fw_stats_process(ar, &stats); + +complete: + complete(&ar->fw_stats_complete); + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); + + /* Since the stats's pdev, vdev and beacon list are spliced and reinitialised + * at this point, no need to free the individual list. + */ + return; + +free: + ath12k_fw_stats_free(&stats); } /* PDEV_CTL_FAILSAFE_CHECK_EVENT is received from FW when the frequency scanned @@ -6889,17 +7726,15 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, const struct ath12k_wmi_pdev_csa_event *ev, const u32 *vdev_ids) { - int i; + u32 current_switch_count = le32_to_cpu(ev->current_switch_count); + u32 num_vdevs = le32_to_cpu(ev->num_vdevs); struct ieee80211_bss_conf *conf; struct ath12k_link_vif *arvif; struct ath12k_vif *ahvif; - - /* Finish CSA once the switch count becomes NULL */ - if (ev->current_switch_count) - return; + int i; rcu_read_lock(); - for (i = 0; i < le32_to_cpu(ev->num_vdevs); i++) { + for (i = 0; i < num_vdevs; i++) { arvif = ath12k_mac_get_arvif_by_vdev_id(ab, vdev_ids[i]); if (!arvif) { @@ -6922,8 +7757,26 @@ ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab, continue; } - if (arvif->is_up && conf->csa_active) - ieee80211_csa_finish(ahvif->vif, 0); + if (!arvif->is_up || !conf->csa_active) + continue; + + /* Finish CSA when counter reaches zero */ + if (!current_switch_count) { + ieee80211_csa_finish(ahvif->vif, arvif->link_id); + arvif->current_cntdown_counter = 0; + } else if (current_switch_count > 1) { + /* If the count in event is not what we expect, don't update the + * mac80211 count. Since during beacon Tx failure, count in the + * firmware will not decrement and this event will come with the + * previous count value again + */ + if (current_switch_count != arvif->current_cntdown_counter) + continue; + + arvif->current_cntdown_counter = + ieee80211_beacon_update_cntdwn(ahvif->vif, + arvif->link_id); + } } rcu_read_unlock(); } @@ -7026,6 +7879,35 @@ exit: kfree(tb); } +static void ath12k_tm_wmi_event_segmented(struct ath12k_base *ab, u32 cmd_id, + struct sk_buff *skb) +{ + const struct ath12k_wmi_ftm_event *ev; + const void **tb; + int ret; + u16 length; + + tb = ath12k_wmi_tlv_parse_alloc(ab, skb, GFP_ATOMIC); + + if (IS_ERR(tb)) { + ret = PTR_ERR(tb); + ath12k_warn(ab, "failed to parse ftm event tlv: %d\n", ret); + return; + } + + ev = tb[WMI_TAG_ARRAY_BYTE]; + if (!ev) { + ath12k_warn(ab, "failed to fetch ftm msg\n"); + kfree(tb); + return; + } + + length = skb->len - TLV_HDR_SIZE; + ath12k_tm_process_event(ab, cmd_id, ev, length); + kfree(tb); + tb = NULL; +} + static void ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab, struct sk_buff *skb) @@ -7446,6 +8328,389 @@ static void ath12k_wmi_event_teardown_complete(struct ath12k_base *ab, kfree(tb); } +#ifdef CONFIG_ATH12K_DEBUGFS +static int ath12k_wmi_tpc_stats_copy_buffer(struct ath12k_base *ab, + const void *ptr, u16 tag, u16 len, + struct wmi_tpc_stats_arg *tpc_stats) +{ + u32 len1, len2, len3, len4; + s16 *dst_ptr; + s8 *dst_ptr_ctl; + + len1 = le32_to_cpu(tpc_stats->max_reg_allowed_power.tpc_reg_pwr.reg_array_len); + len2 = le32_to_cpu(tpc_stats->rates_array1.tpc_rates_array.rate_array_len); + len3 = le32_to_cpu(tpc_stats->rates_array2.tpc_rates_array.rate_array_len); + len4 = le32_to_cpu(tpc_stats->ctl_array.tpc_ctl_pwr.ctl_array_len); + + switch (tpc_stats->event_count) { + case ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT: + if (len1 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_REG_PWR_ALLOWED) { + dst_ptr = tpc_stats->max_reg_allowed_power.reg_pwr_array; + memcpy(dst_ptr, ptr, len1); + } + break; + case ATH12K_TPC_STATS_RATES_EVENT1: + if (len2 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY1) { + dst_ptr = tpc_stats->rates_array1.rate_array; + memcpy(dst_ptr, ptr, len2); + } + break; + case ATH12K_TPC_STATS_RATES_EVENT2: + if (len3 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_RATES_ARRAY2) { + dst_ptr = tpc_stats->rates_array2.rate_array; + memcpy(dst_ptr, ptr, len3); + } + break; + case ATH12K_TPC_STATS_CTL_TABLE_EVENT: + if (len4 > len) + return -ENOBUFS; + + if (tpc_stats->tlvs_rcvd & WMI_TPC_CTL_PWR_ARRAY) { + dst_ptr_ctl = tpc_stats->ctl_array.ctl_pwr_table; + memcpy(dst_ptr_ctl, ptr, len4); + } + break; + } + return 0; +} + +static int ath12k_tpc_get_reg_pwr(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_max_reg_power_fixed_params *ev) +{ + struct wmi_max_reg_power_allowed_arg *reg_pwr; + u32 total_size; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received reg power array type %d length %d for tpc stats\n", + ev->reg_power_type, ev->reg_array_len); + + switch (le32_to_cpu(ev->reg_power_type)) { + case TPC_STATS_REG_PWR_ALLOWED_TYPE: + reg_pwr = &tpc_stats->max_reg_allowed_power; + break; + default: + return -EINVAL; + } + + /* Each entry is 2 byte hence multiplying the indices with 2 */ + total_size = le32_to_cpu(ev->d1) * le32_to_cpu(ev->d2) * + le32_to_cpu(ev->d3) * le32_to_cpu(ev->d4) * 2; + if (le32_to_cpu(ev->reg_array_len) != total_size) { + ath12k_warn(ab, + "Total size and reg_array_len doesn't match for tpc stats\n"); + return -EINVAL; + } + + memcpy(®_pwr->tpc_reg_pwr, ev, sizeof(struct wmi_max_reg_power_fixed_params)); + + reg_pwr->reg_pwr_array = kzalloc(le32_to_cpu(reg_pwr->tpc_reg_pwr.reg_array_len), + GFP_ATOMIC); + if (!reg_pwr->reg_pwr_array) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= WMI_TPC_REG_PWR_ALLOWED; + + return 0; +} + +static int ath12k_tpc_get_rate_array(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_tpc_rates_array_fixed_params *ev) +{ + struct wmi_tpc_rates_array_arg *rates_array; + u32 flag = 0, rate_array_len; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received rates array type %d length %d for tpc stats\n", + ev->rate_array_type, ev->rate_array_len); + + switch (le32_to_cpu(ev->rate_array_type)) { + case ATH12K_TPC_STATS_RATES_ARRAY1: + rates_array = &tpc_stats->rates_array1; + flag = WMI_TPC_RATES_ARRAY1; + break; + case ATH12K_TPC_STATS_RATES_ARRAY2: + rates_array = &tpc_stats->rates_array2; + flag = WMI_TPC_RATES_ARRAY2; + break; + default: + ath12k_warn(ab, + "Received invalid type of rates array for tpc stats\n"); + return -EINVAL; + } + memcpy(&rates_array->tpc_rates_array, ev, + sizeof(struct wmi_tpc_rates_array_fixed_params)); + rate_array_len = le32_to_cpu(rates_array->tpc_rates_array.rate_array_len); + rates_array->rate_array = kzalloc(rate_array_len, GFP_ATOMIC); + if (!rates_array->rate_array) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= flag; + return 0; +} + +static int ath12k_tpc_get_ctl_pwr_tbl(struct ath12k_base *ab, + struct wmi_tpc_stats_arg *tpc_stats, + struct wmi_tpc_ctl_pwr_fixed_params *ev) +{ + struct wmi_tpc_ctl_pwr_table_arg *ctl_array; + u32 total_size, ctl_array_len, flag = 0; + + ath12k_dbg(ab, ATH12K_DBG_WMI, + "Received ctl array type %d length %d for tpc stats\n", + ev->ctl_array_type, ev->ctl_array_len); + + switch (le32_to_cpu(ev->ctl_array_type)) { + case ATH12K_TPC_STATS_CTL_ARRAY: + ctl_array = &tpc_stats->ctl_array; + flag = WMI_TPC_CTL_PWR_ARRAY; + break; + default: + ath12k_warn(ab, + "Received invalid type of ctl pwr table for tpc stats\n"); + return -EINVAL; + } + + total_size = le32_to_cpu(ev->d1) * le32_to_cpu(ev->d2) * + le32_to_cpu(ev->d3) * le32_to_cpu(ev->d4); + if (le32_to_cpu(ev->ctl_array_len) != total_size) { + ath12k_warn(ab, + "Total size and ctl_array_len doesn't match for tpc stats\n"); + return -EINVAL; + } + + memcpy(&ctl_array->tpc_ctl_pwr, ev, sizeof(struct wmi_tpc_ctl_pwr_fixed_params)); + ctl_array_len = le32_to_cpu(ctl_array->tpc_ctl_pwr.ctl_array_len); + ctl_array->ctl_pwr_table = kzalloc(ctl_array_len, GFP_ATOMIC); + if (!ctl_array->ctl_pwr_table) + return -ENOMEM; + + tpc_stats->tlvs_rcvd |= flag; + return 0; +} + +static int ath12k_wmi_tpc_stats_subtlv_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tpc_rates_array_fixed_params *tpc_rates_array; + struct wmi_max_reg_power_fixed_params *tpc_reg_pwr; + struct wmi_tpc_ctl_pwr_fixed_params *tpc_ctl_pwr; + struct wmi_tpc_stats_arg *tpc_stats = data; + struct wmi_tpc_config_params *tpc_config; + int ret = 0; + + if (!tpc_stats) { + ath12k_warn(ab, "tpc stats memory unavailable\n"); + return -EINVAL; + } + + switch (tag) { + case WMI_TAG_TPC_STATS_CONFIG_EVENT: + tpc_config = (struct wmi_tpc_config_params *)ptr; + memcpy(&tpc_stats->tpc_config, tpc_config, + sizeof(struct wmi_tpc_config_params)); + break; + case WMI_TAG_TPC_STATS_REG_PWR_ALLOWED: + tpc_reg_pwr = (struct wmi_max_reg_power_fixed_params *)ptr; + ret = ath12k_tpc_get_reg_pwr(ab, tpc_stats, tpc_reg_pwr); + break; + case WMI_TAG_TPC_STATS_RATES_ARRAY: + tpc_rates_array = (struct wmi_tpc_rates_array_fixed_params *)ptr; + ret = ath12k_tpc_get_rate_array(ab, tpc_stats, tpc_rates_array); + break; + case WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT: + tpc_ctl_pwr = (struct wmi_tpc_ctl_pwr_fixed_params *)ptr; + ret = ath12k_tpc_get_ctl_pwr_tbl(ab, tpc_stats, tpc_ctl_pwr); + break; + default: + ath12k_warn(ab, + "Received invalid tag for tpc stats in subtlvs\n"); + return -EINVAL; + } + return ret; +} + +static int ath12k_wmi_tpc_stats_event_parser(struct ath12k_base *ab, + u16 tag, u16 len, + const void *ptr, void *data) +{ + struct wmi_tpc_stats_arg *tpc_stats = (struct wmi_tpc_stats_arg *)data; + int ret; + + switch (tag) { + case WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM: + ret = 0; + /* Fixed param is already processed*/ + break; + case WMI_TAG_ARRAY_STRUCT: + /* len 0 is expected for array of struct when there + * is no content of that type to pack inside that tlv + */ + if (len == 0) + return 0; + ret = ath12k_wmi_tlv_iter(ab, ptr, len, + ath12k_wmi_tpc_stats_subtlv_parser, + tpc_stats); + break; + case WMI_TAG_ARRAY_INT16: + if (len == 0) + return 0; + ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr, + WMI_TAG_ARRAY_INT16, + len, tpc_stats); + break; + case WMI_TAG_ARRAY_BYTE: + if (len == 0) + return 0; + ret = ath12k_wmi_tpc_stats_copy_buffer(ab, ptr, + WMI_TAG_ARRAY_BYTE, + len, tpc_stats); + break; + default: + ath12k_warn(ab, "Received invalid tag for tpc stats\n"); + ret = -EINVAL; + break; + } + return ret; +} + +void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar) +{ + struct wmi_tpc_stats_arg *tpc_stats = ar->debug.tpc_stats; + + lockdep_assert_held(&ar->data_lock); + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "tpc stats mem free\n"); + if (tpc_stats) { + kfree(tpc_stats->max_reg_allowed_power.reg_pwr_array); + kfree(tpc_stats->rates_array1.rate_array); + kfree(tpc_stats->rates_array2.rate_array); + kfree(tpc_stats->ctl_array.ctl_pwr_table); + kfree(tpc_stats); + ar->debug.tpc_stats = NULL; + } +} + +static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, + struct sk_buff *skb) +{ + struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *fixed_param; + struct wmi_tpc_stats_arg *tpc_stats; + const struct wmi_tlv *tlv; + void *ptr = skb->data; + struct ath12k *ar; + u16 tlv_tag; + u32 event_count; + int ret; + + if (!skb->data) { + ath12k_warn(ab, "No data present in tpc stats event\n"); + return; + } + + if (skb->len < (sizeof(*fixed_param) + TLV_HDR_SIZE)) { + ath12k_warn(ab, "TPC stats event size invalid\n"); + return; + } + + tlv = (struct wmi_tlv *)ptr; + tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG); + ptr += sizeof(*tlv); + + if (tlv_tag != WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM) { + ath12k_warn(ab, "TPC stats without fixed param tlv at start\n"); + return; + } + + fixed_param = (struct ath12k_wmi_pdev_tpc_stats_event_fixed_params *)ptr; + rcu_read_lock(); + ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(fixed_param->pdev_id) + 1); + if (!ar) { + ath12k_warn(ab, "Failed to get ar for tpc stats\n"); + rcu_read_unlock(); + return; + } + spin_lock_bh(&ar->data_lock); + if (!ar->debug.tpc_request) { + /* Event is received either without request or the + * timeout, if memory is already allocated free it + */ + if (ar->debug.tpc_stats) { + ath12k_warn(ab, "Freeing memory for tpc_stats\n"); + ath12k_wmi_free_tpc_stats_mem(ar); + } + goto unlock; + } + + event_count = le32_to_cpu(fixed_param->event_count); + if (event_count == 0) { + if (ar->debug.tpc_stats) { + ath12k_warn(ab, + "Invalid tpc memory present\n"); + goto unlock; + } + ar->debug.tpc_stats = + kzalloc(sizeof(struct wmi_tpc_stats_arg), + GFP_ATOMIC); + if (!ar->debug.tpc_stats) { + ath12k_warn(ab, + "Failed to allocate memory for tpc stats\n"); + goto unlock; + } + } + + tpc_stats = ar->debug.tpc_stats; + if (!tpc_stats) { + ath12k_warn(ab, "tpc stats memory unavailable\n"); + goto unlock; + } + + if (!(event_count == 0)) { + if (event_count != tpc_stats->event_count + 1) { + ath12k_warn(ab, + "Invalid tpc event received\n"); + goto unlock; + } + } + tpc_stats->pdev_id = le32_to_cpu(fixed_param->pdev_id); + tpc_stats->end_of_event = le32_to_cpu(fixed_param->end_of_event); + tpc_stats->event_count = le32_to_cpu(fixed_param->event_count); + ath12k_dbg(ab, ATH12K_DBG_WMI, + "tpc stats event_count %d\n", + tpc_stats->event_count); + ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len, + ath12k_wmi_tpc_stats_event_parser, + tpc_stats); + if (ret) { + ath12k_wmi_free_tpc_stats_mem(ar); + ath12k_warn(ab, "failed to parse tpc_stats tlv: %d\n", ret); + goto unlock; + } + + if (tpc_stats->end_of_event) + complete(&ar->debug.tpc_complete); + +unlock: + spin_unlock_bh(&ar->data_lock); + rcu_read_unlock(); +} +#else +static void ath12k_wmi_process_tpc_stats(struct ath12k_base *ab, + struct sk_buff *skb) +{ +} +#endif + static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) { struct wmi_cmd_hdr *cmd_hdr; @@ -7571,6 +8836,12 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_MLO_TEARDOWN_COMPLETE_EVENTID: ath12k_wmi_event_teardown_complete(ab, skb); break; + case WMI_HALPHY_STATS_CTRL_PATH_EVENTID: + ath12k_wmi_process_tpc_stats(ab, skb); + break; + case WMI_11D_NEW_COUNTRY_EVENTID: + ath12k_reg_11d_new_cc_event(ab, skb); + break; /* add Unsupported events (rare) here */ case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID: case WMI_PEER_OPER_MODE_CHANGE_EVENTID: @@ -7584,7 +8855,12 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb) case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID: /* debug might flood hence silently ignore (no-op) */ break; - /* TODO: Add remaining events */ + case WMI_PDEV_UTF_EVENTID: + if (test_bit(ATH12K_FLAG_FTM_SEGMENTED, &ab->dev_flags)) + ath12k_tm_wmi_event_segmented(ab, id, skb); + else + ath12k_tm_wmi_event_unsegmented(ab, id, skb); + break; default: ath12k_dbg(ab, ATH12K_DBG_WMI, "Unknown eventid: 0x%x\n", id); break; @@ -7721,6 +8997,74 @@ int ath12k_wmi_simulate_radar(struct ath12k *ar) return ath12k_wmi_send_unit_test_cmd(ar, wmi_ut, dfs_args); } +int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type) +{ + struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct sk_buff *skb; + struct wmi_tlv *tlv; + __le32 *pdev_id; + u32 buf_len; + void *ptr; + int ret; + + buf_len = sizeof(*cmd) + TLV_HDR_SIZE + sizeof(u32) + TLV_HDR_SIZE + TLV_HDR_SIZE; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, buf_len); + if (!skb) + return -ENOMEM; + cmd = (struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params *)skb->data; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_HALPHY_CTRL_PATH_CMD_FIXED_PARAM, + sizeof(*cmd)); + + cmd->stats_id_mask = cpu_to_le32(WMI_REQ_CTRL_PATH_PDEV_TX_STAT); + cmd->action = cpu_to_le32(WMI_REQUEST_CTRL_PATH_STAT_GET); + cmd->subid = cpu_to_le32(tpc_stats_type); + + ptr = skb->data + sizeof(*cmd); + + /* The below TLV arrays optionally follow this fixed param TLV structure + * 1. ARRAY_UINT32 pdev_ids[] + * If this array is present and non-zero length, stats should only + * be provided from the pdevs identified in the array. + * 2. ARRAY_UNIT32 vdev_ids[] + * If this array is present and non-zero length, stats should only + * be provided from the vdevs identified in the array. + * 3. ath12k_wmi_mac_addr_params peer_macaddr[]; + * If this array is present and non-zero length, stats should only + * be provided from the peers with the MAC addresses specified + * in the array + */ + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, sizeof(u32)); + ptr += TLV_HDR_SIZE; + + pdev_id = ptr; + *pdev_id = cpu_to_le32(ath12k_mac_get_target_pdev_id(ar)); + ptr += sizeof(*pdev_id); + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_UINT32, 0); + ptr += TLV_HDR_SIZE; + + tlv = ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_FIXED_STRUCT, 0); + ptr += TLV_HDR_SIZE; + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_REQUEST_HALPHY_CTRL_PATH_STATS_CMDID); + if (ret) { + ath12k_warn(ar->ab, + "failed to submit WMI_REQUEST_STATS_CTRL_PATH_CMDID\n"); + dev_kfree_skb(skb); + return ret; + } + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "WMI get TPC STATS sent on pdev %d\n", + ar->pdev->pdev_id); + + return ret; +} + int ath12k_wmi_connect(struct ath12k_base *ab) { u32 i; @@ -8501,3 +9845,69 @@ int ath12k_wmi_mlo_teardown(struct ath12k *ar) return 0; } + +bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar) +{ + return test_bit(WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT, + ar->ab->wmi_ab.svc_map) && ar->supports_6ghz; +} + +int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, + u32 vdev_id, + struct ath12k_reg_tpc_power_info *param) +{ + struct wmi_vdev_set_tpc_power_cmd *cmd; + struct ath12k_wmi_pdev *wmi = ar->wmi; + struct wmi_vdev_ch_power_params *ch; + int i, ret, len, array_len; + struct sk_buff *skb; + struct wmi_tlv *tlv; + u8 *ptr; + + array_len = sizeof(*ch) * param->num_pwr_levels; + len = sizeof(*cmd) + TLV_HDR_SIZE + array_len; + + skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len); + if (!skb) + return -ENOMEM; + + ptr = skb->data; + + cmd = (struct wmi_vdev_set_tpc_power_cmd *)ptr; + cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_SET_TPC_POWER_CMD, + sizeof(*cmd)); + cmd->vdev_id = cpu_to_le32(vdev_id); + cmd->psd_power = cpu_to_le32(param->is_psd_power); + cmd->eirp_power = cpu_to_le32(param->eirp_power); + cmd->power_type_6ghz = cpu_to_le32(param->ap_power_type); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, + "tpc vdev id %d is psd power %d eirp power %d 6 ghz power type %d\n", + vdev_id, param->is_psd_power, param->eirp_power, param->ap_power_type); + + ptr += sizeof(*cmd); + tlv = (struct wmi_tlv *)ptr; + tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT, array_len); + + ptr += TLV_HDR_SIZE; + ch = (struct wmi_vdev_ch_power_params *)ptr; + + for (i = 0; i < param->num_pwr_levels; i++, ch++) { + ch->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_VDEV_CH_POWER_INFO, + sizeof(*ch)); + ch->chan_cfreq = cpu_to_le32(param->chan_power_info[i].chan_cfreq); + ch->tx_power = cpu_to_le32(param->chan_power_info[i].tx_power); + + ath12k_dbg(ar->ab, ATH12K_DBG_WMI, "tpc chan freq %d TX power %d\n", + ch->chan_cfreq, ch->tx_power); + } + + ret = ath12k_wmi_cmd_send(wmi, skb, WMI_VDEV_SET_TPC_POWER_CMDID); + if (ret) { + ath12k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n"); + dev_kfree_skb(skb); + return ret; + } + + return 0; +} diff --git a/drivers/net/wireless/ath/ath12k/wmi.h b/drivers/net/wireless/ath/ath12k/wmi.h index 45fe699ce8a5..ac18f75e0449 100644 --- a/drivers/net/wireless/ath/ath12k/wmi.h +++ b/drivers/net/wireless/ath/ath12k/wmi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2021 The Linux Foundation. All rights reserved. - * Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #ifndef ATH12K_WMI_H @@ -25,6 +25,8 @@ struct ath12k_base; struct ath12k; struct ath12k_link_vif; +struct ath12k_fw_stats; +struct ath12k_reg_tpc_power_info; /* There is no signed version of __le32, so for a temporary solution come * up with our own version. The idea is from fs/ntfs/endian.h. @@ -215,9 +217,9 @@ enum wmi_host_hw_mode_priority { }; enum WMI_HOST_WLAN_BAND { - WMI_HOST_WLAN_2G_CAP = 1, - WMI_HOST_WLAN_5G_CAP = 2, - WMI_HOST_WLAN_2G_5G_CAP = 3, + WMI_HOST_WLAN_2GHZ_CAP = 1, + WMI_HOST_WLAN_5GHZ_CAP = 2, + WMI_HOST_WLAN_2GHZ_5GHZ_CAP = 3, }; enum wmi_cmd_group { @@ -385,6 +387,22 @@ enum wmi_tlv_cmd_id { WMI_VDEV_SET_CUSTOM_AGGR_SIZE_CMDID, WMI_VDEV_ENCRYPT_DECRYPT_DATA_REQ_CMDID, WMI_VDEV_ADD_MAC_ADDR_TO_RX_FILTER_CMDID, + WMI_VDEV_SET_ARP_STAT_CMDID, + WMI_VDEV_GET_ARP_STAT_CMDID, + WMI_VDEV_GET_TX_POWER_CMDID, + WMI_VDEV_LIMIT_OFFCHAN_CMDID, + WMI_VDEV_SET_CUSTOM_SW_RETRY_TH_CMDID, + WMI_VDEV_CHAINMASK_CONFIG_CMDID, + WMI_VDEV_GET_BCN_RECEPTION_STATS_CMDID, + WMI_VDEV_GET_MWS_COEX_INFO_CMDID, + WMI_VDEV_DELETE_ALL_PEER_CMDID, + WMI_VDEV_BSS_MAX_IDLE_TIME_CMDID, + WMI_VDEV_AUDIO_SYNC_TRIGGER_CMDID, + WMI_VDEV_AUDIO_SYNC_QTIMER_CMDID, + WMI_VDEV_SET_PCL_CMDID, + WMI_VDEV_GET_BIG_DATA_CMDID, + WMI_VDEV_GET_BIG_DATA_P2_CMDID, + WMI_VDEV_SET_TPC_POWER_CMDID, WMI_PEER_CREATE_CMDID = WMI_TLV_CMD(WMI_GRP_PEER), WMI_PEER_DELETE_CMDID, WMI_PEER_FLUSH_TIDS_CMDID, @@ -516,6 +534,9 @@ enum wmi_tlv_cmd_id { WMI_REQUEST_RCPI_CMDID, WMI_REQUEST_PEER_STATS_INFO_CMDID, WMI_REQUEST_RADIO_CHAN_STATS_CMDID, + WMI_REQUEST_WLM_STATS_CMDID, + WMI_REQUEST_CTRL_PATH_STATS_CMDID, + WMI_REQUEST_HALPHY_CTRL_PATH_STATS_CMDID = WMI_REQUEST_CTRL_PATH_STATS_CMDID + 3, WMI_SET_ARP_NS_OFFLOAD_CMDID = WMI_TLV_CMD(WMI_GRP_ARP_NS_OFL), WMI_ADD_PROACTIVE_ARP_RSP_PATTERN_CMDID, WMI_DEL_PROACTIVE_ARP_RSP_PATTERN_CMDID, @@ -785,6 +806,9 @@ enum wmi_tlv_event_id { WMI_UPDATE_RCPI_EVENTID, WMI_PEER_STATS_INFO_EVENTID, WMI_RADIO_CHAN_STATS_EVENTID, + WMI_WLM_STATS_EVENTID, + WMI_CTRL_PATH_STATS_EVENTID, + WMI_HALPHY_STATS_CTRL_PATH_EVENTID, WMI_NLO_MATCH_EVENTID = WMI_TLV_CMD(WMI_GRP_NLO_OFL), WMI_NLO_SCAN_COMPLETE_EVENTID, WMI_APFIND_EVENTID, @@ -1191,6 +1215,7 @@ enum wmi_tlv_tag { WMI_TAG_ARRAY_BYTE, WMI_TAG_ARRAY_STRUCT, WMI_TAG_ARRAY_FIXED_STRUCT, + WMI_TAG_ARRAY_INT16, WMI_TAG_LAST_ARRAY_ENUM = 31, WMI_TAG_SERVICE_READY_EVENT, WMI_TAG_HAL_REG_CAPABILITIES, @@ -1941,6 +1966,14 @@ enum wmi_tlv_tag { WMI_TAG_MAC_PHY_CAPABILITIES_EXT = 0x36F, WMI_TAG_REGULATORY_RULE_EXT_STRUCT = 0x3A9, WMI_TAG_REG_CHAN_LIST_CC_EXT_EVENT, + WMI_TAG_TPC_STATS_GET_CMD = 0x38B, + WMI_TAG_TPC_STATS_EVENT_FIXED_PARAM, + WMI_TAG_TPC_STATS_CONFIG_EVENT, + WMI_TAG_TPC_STATS_REG_PWR_ALLOWED, + WMI_TAG_TPC_STATS_RATES_ARRAY, + WMI_TAG_TPC_STATS_CTL_PWR_TABLE_EVENT, + WMI_TAG_VDEV_SET_TPC_POWER_CMD = 0x3B5, + WMI_TAG_VDEV_CH_POWER_INFO, WMI_TAG_EHT_RATE_SET = 0x3C4, WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5, WMI_TAG_MLO_TX_SEND_PARAMS, @@ -1958,6 +1991,8 @@ enum wmi_tlv_tag { WMI_TAG_PDEV_SET_BIOS_SAR_TABLE_CMD = 0x3D8, WMI_TAG_PDEV_SET_BIOS_GEO_TABLE_CMD = 0x3D9, WMI_TAG_PDEV_SET_BIOS_INTERFACE_CMD = 0x3FB, + WMI_TAG_HALPHY_CTRL_PATH_CMD_FIXED_PARAM = 0x442, + WMI_TAG_HALPHY_CTRL_PATH_EVENT_FIXED_PARAM, WMI_TAG_MAX }; @@ -2185,6 +2220,8 @@ enum wmi_tlv_service { WMI_MAX_EXT_SERVICE = 256, + WMI_TLV_SERVICE_EXT_TPC_REG_SUPPORT = 280, + WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281, WMI_TLV_SERVICE_11BE = 289, @@ -2445,6 +2482,7 @@ struct wmi_init_cmd { } __packed; #define WMI_RSRC_CFG_HOST_SVC_FLAG_REG_CC_EXT_SUPPORT_BIT 4 +#define WMI_RSRC_CFG_HOST_SVC_FLAG_REO_QREF_SUPPORT_BIT 12 #define WMI_RSRC_CFG_FLAGS2_RX_PEER_METADATA_VERSION GENMASK(5, 4) #define WMI_RSRC_CFG_FLAG1_BSS_CHANNEL_INFO_64 BIT(5) #define WMI_RSRC_CFG_FLAGS2_CALC_NEXT_DTIM_COUNT_SET BIT(9) @@ -2674,8 +2712,8 @@ enum wmi_channel_width { * 2 - index for 160 MHz, first 3 bytes valid * 3 - index for 320 MHz, first 3 bytes valid */ -#define WMI_MAX_EHT_SUPP_MCS_2G_SIZE 2 -#define WMI_MAX_EHT_SUPP_MCS_5G_SIZE 4 +#define WMI_MAX_EHT_SUPP_MCS_2GHZ_SIZE 2 +#define WMI_MAX_EHT_SUPP_MCS_5GHZ_SIZE 4 #define WMI_EHTCAP_TXRX_MCS_NSS_IDX_80 0 #define WMI_EHTCAP_TXRX_MCS_NSS_IDX_160 1 @@ -2714,8 +2752,8 @@ struct ath12k_wmi_caps_ext_params { struct ath12k_wmi_ppe_threshold_params eht_ppet_2ghz; struct ath12k_wmi_ppe_threshold_params eht_ppet_5ghz; __le32 eht_cap_info_internal; - __le32 eht_supp_mcs_ext_2ghz[WMI_MAX_EHT_SUPP_MCS_2G_SIZE]; - __le32 eht_supp_mcs_ext_5ghz[WMI_MAX_EHT_SUPP_MCS_5G_SIZE]; + __le32 eht_supp_mcs_ext_2ghz[WMI_MAX_EHT_SUPP_MCS_2GHZ_SIZE]; + __le32 eht_supp_mcs_ext_5ghz[WMI_MAX_EHT_SUPP_MCS_5GHZ_SIZE]; __le32 eml_capability; __le32 mld_capability; } __packed; @@ -3624,6 +3662,26 @@ struct ath12k_wmi_p2p_noa_info { struct ath12k_wmi_p2p_noa_descriptor descriptors[WMI_P2P_MAX_NOA_DESCRIPTORS]; } __packed; +#define MAX_WMI_UTF_LEN 252 + +struct ath12k_wmi_ftm_seg_hdr_params { + __le32 len; + __le32 msgref; + __le32 segmentinfo; + __le32 pdev_id; +} __packed; + +struct ath12k_wmi_ftm_cmd { + __le32 tlv_header; + struct ath12k_wmi_ftm_seg_hdr_params seg_hdr; + u8 data[]; +} __packed; + +struct ath12k_wmi_ftm_event { + struct ath12k_wmi_ftm_seg_hdr_params seg_hdr; + u8 data[]; +} __packed; + #define WMI_BEACON_TX_BUFFER_SIZE 512 #define WMI_EMA_BEACON_CNT GENMASK(7, 0) @@ -3718,6 +3776,7 @@ struct peer_assoc_mlo_params { u32 ieee_link_id; u8 num_partner_links; struct wmi_ml_partner_info partner_info[ATH12K_WMI_MLO_MAX_LINKS]; + u16 eml_cap; }; struct wmi_rate_set_arg { @@ -3796,6 +3855,7 @@ struct ath12k_wmi_peer_assoc_arg { u32 punct_bitmap; bool is_assoc; struct peer_assoc_mlo_params ml; + bool eht_disable_mcs15; }; #define ATH12K_WMI_FLAG_MLO_ENABLED BIT(0) @@ -3990,6 +4050,28 @@ struct wmi_init_country_cmd { } cc_info; } __packed; +struct wmi_11d_scan_start_arg { + u32 vdev_id; + u32 scan_period_msec; + u32 start_interval_msec; +}; + +struct wmi_11d_scan_start_cmd { + __le32 tlv_header; + __le32 vdev_id; + __le32 scan_period_msec; + __le32 start_interval_msec; +} __packed; + +struct wmi_11d_scan_stop_cmd { + __le32 tlv_header; + __le32 vdev_id; +} __packed; + +struct wmi_11d_new_cc_event { + __le32 new_alpha2; +} __packed; + struct wmi_delba_send_cmd { __le32 tlv_header; __le32 vdev_id; @@ -4072,7 +4154,17 @@ struct ath12k_wmi_eht_rate_set_params { #define MAX_REG_RULES 10 #define REG_ALPHA2_LEN 2 -#define MAX_6G_REG_RULES 5 +#define MAX_6GHZ_REG_RULES 5 + +struct wmi_set_current_country_arg { + u8 alpha2[REG_ALPHA2_LEN]; +}; + +struct wmi_set_current_country_cmd { + __le32 tlv_header; + __le32 pdev_id; + __le32 new_alpha2; +} __packed; enum wmi_start_event_param { WMI_VDEV_START_RESP_EVENT = 0, @@ -4093,6 +4185,7 @@ struct wmi_vdev_start_resp_event { }; __le32 cfgd_tx_streams; __le32 cfgd_rx_streams; + __le32 max_allowed_tx_power; } __packed; /* VDEV start response status codes */ @@ -4438,6 +4531,7 @@ struct ath12k_wmi_target_cap_arg { }; enum wmi_vdev_type { + WMI_VDEV_TYPE_UNSPEC = 0, WMI_VDEV_TYPE_AP = 1, WMI_VDEV_TYPE_STA = 2, WMI_VDEV_TYPE_IBSS = 3, @@ -4604,6 +4698,7 @@ enum wmi_rate_preamble { WMI_RATE_PREAMBLE_HT, WMI_RATE_PREAMBLE_VHT, WMI_RATE_PREAMBLE_HE, + WMI_RATE_PREAMBLE_EHT, }; /** @@ -5628,6 +5723,280 @@ enum wmi_sta_keepalive_method { #define WMI_STA_KEEPALIVE_INTERVAL_DEFAULT 30 #define WMI_STA_KEEPALIVE_INTERVAL_DISABLE 0 +struct wmi_stats_event { + __le32 stats_id; + __le32 num_pdev_stats; + __le32 num_vdev_stats; + __le32 num_peer_stats; + __le32 num_bcnflt_stats; + __le32 num_chan_stats; + __le32 num_mib_stats; + __le32 pdev_id; + __le32 num_bcn_stats; + __le32 num_peer_extd_stats; + __le32 num_peer_extd2_stats; +} __packed; + +enum wmi_stats_id { + WMI_REQUEST_PDEV_STAT = BIT(2), + WMI_REQUEST_VDEV_STAT = BIT(3), + WMI_REQUEST_BCN_STAT = BIT(11), +}; + +struct wmi_request_stats_cmd { + __le32 tlv_header; + __le32 stats_id; + __le32 vdev_id; + struct ath12k_wmi_mac_addr_params peer_macaddr; + __le32 pdev_id; +} __packed; + +#define WLAN_MAX_AC 4 +#define MAX_TX_RATE_VALUES 10 + +struct wmi_vdev_stats_params { + __le32 vdev_id; + __le32 beacon_snr; + __le32 data_snr; + __le32 num_tx_frames[WLAN_MAX_AC]; + __le32 num_rx_frames; + __le32 num_tx_frames_retries[WLAN_MAX_AC]; + __le32 num_tx_frames_failures[WLAN_MAX_AC]; + __le32 num_rts_fail; + __le32 num_rts_success; + __le32 num_rx_err; + __le32 num_rx_discard; + __le32 num_tx_not_acked; + __le32 tx_rate_history[MAX_TX_RATE_VALUES]; + __le32 beacon_rssi_history[MAX_TX_RATE_VALUES]; +} __packed; + +struct ath12k_wmi_bcn_stats_params { + __le32 vdev_id; + __le32 tx_bcn_succ_cnt; + __le32 tx_bcn_outage_cnt; +} __packed; + +struct ath12k_wmi_pdev_base_stats_params { + a_sle32 chan_nf; + __le32 tx_frame_count; /* Cycles spent transmitting frames */ + __le32 rx_frame_count; /* Cycles spent receiving frames */ + __le32 rx_clear_count; /* Total channel busy time, evidently */ + __le32 cycle_count; /* Total on-channel time */ + __le32 phy_err_count; + __le32 chan_tx_pwr; +} __packed; + +struct ath12k_wmi_pdev_tx_stats_params { + a_sle32 comp_queued; + a_sle32 comp_delivered; + a_sle32 msdu_enqued; + a_sle32 mpdu_enqued; + a_sle32 wmm_drop; + a_sle32 local_enqued; + a_sle32 local_freed; + a_sle32 hw_queued; + a_sle32 hw_reaped; + a_sle32 underrun; + a_sle32 tx_abort; + a_sle32 mpdus_requed; + __le32 tx_ko; + __le32 data_rc; + __le32 self_triggers; + __le32 sw_retry_failure; + __le32 illgl_rate_phy_err; + __le32 pdev_cont_xretry; + __le32 pdev_tx_timeout; + __le32 pdev_resets; + __le32 stateless_tid_alloc_failure; + __le32 phy_underrun; + __le32 txop_ovf; +} __packed; + +struct ath12k_wmi_pdev_rx_stats_params { + a_sle32 mid_ppdu_route_change; + a_sle32 status_rcvd; + a_sle32 r0_frags; + a_sle32 r1_frags; + a_sle32 r2_frags; + a_sle32 r3_frags; + a_sle32 htt_msdus; + a_sle32 htt_mpdus; + a_sle32 loc_msdus; + a_sle32 loc_mpdus; + a_sle32 oversize_amsdu; + a_sle32 phy_errs; + a_sle32 phy_err_drop; + a_sle32 mpdu_errs; +} __packed; + +struct ath12k_wmi_pdev_stats_params { + struct ath12k_wmi_pdev_base_stats_params base; + struct ath12k_wmi_pdev_tx_stats_params tx; + struct ath12k_wmi_pdev_rx_stats_params rx; +} __packed; + +struct ath12k_fw_stats_req_params { + u32 stats_id; + u32 vdev_id; + u32 pdev_id; +}; + +#define WMI_REQ_CTRL_PATH_PDEV_TX_STAT 1 +#define WMI_REQUEST_CTRL_PATH_STAT_GET 1 + +#define WMI_TPC_CONFIG BIT(1) +#define WMI_TPC_REG_PWR_ALLOWED BIT(2) +#define WMI_TPC_RATES_ARRAY1 BIT(3) +#define WMI_TPC_RATES_ARRAY2 BIT(4) +#define WMI_TPC_RATES_DL_OFDMA_ARRAY BIT(5) +#define WMI_TPC_CTL_PWR_ARRAY BIT(6) +#define WMI_TPC_CONFIG_PARAM 0x1 +#define ATH12K_TPC_RATE_ARRAY_MU GENMASK(15, 8) +#define ATH12K_TPC_RATE_ARRAY_SU GENMASK(7, 0) +#define TPC_STATS_REG_PWR_ALLOWED_TYPE 0 + +enum wmi_halphy_ctrl_path_stats_id { + WMI_HALPHY_PDEV_TX_SU_STATS = 0, + WMI_HALPHY_PDEV_TX_SUTXBF_STATS, + WMI_HALPHY_PDEV_TX_MU_STATS, + WMI_HALPHY_PDEV_TX_MUTXBF_STATS, + WMI_HALPHY_PDEV_TX_STATS_MAX, +}; + +enum ath12k_wmi_tpc_stats_rates_array { + ATH12K_TPC_STATS_RATES_ARRAY1, + ATH12K_TPC_STATS_RATES_ARRAY2, +}; + +enum ath12k_wmi_tpc_stats_ctl_array { + ATH12K_TPC_STATS_CTL_ARRAY, + ATH12K_TPC_STATS_CTL_160ARRAY, +}; + +enum ath12k_wmi_tpc_stats_events { + ATH12K_TPC_STATS_CONFIG_REG_PWR_EVENT, + ATH12K_TPC_STATS_RATES_EVENT1, + ATH12K_TPC_STATS_RATES_EVENT2, + ATH12K_TPC_STATS_CTL_TABLE_EVENT +}; + +struct wmi_request_halphy_ctrl_path_stats_cmd_fixed_params { + __le32 tlv_header; + __le32 stats_id_mask; + __le32 request_id; + __le32 action; + __le32 subid; +} __packed; + +struct ath12k_wmi_pdev_tpc_stats_event_fixed_params { + __le32 pdev_id; + __le32 end_of_event; + __le32 event_count; +} __packed; + +struct wmi_tpc_config_params { + __le32 reg_domain; + __le32 chan_freq; + __le32 phy_mode; + __le32 twice_antenna_reduction; + __le32 twice_max_reg_power; + __le32 twice_antenna_gain; + __le32 power_limit; + __le32 rate_max; + __le32 num_tx_chain; + __le32 ctl; + __le32 flags; + __le32 caps; +} __packed; + +struct wmi_max_reg_power_fixed_params { + __le32 reg_power_type; + __le32 reg_array_len; + __le32 d1; + __le32 d2; + __le32 d3; + __le32 d4; +} __packed; + +struct wmi_max_reg_power_allowed_arg { + struct wmi_max_reg_power_fixed_params tpc_reg_pwr; + s16 *reg_pwr_array; +}; + +struct wmi_tpc_rates_array_fixed_params { + __le32 rate_array_type; + __le32 rate_array_len; +} __packed; + +struct wmi_tpc_rates_array_arg { + struct wmi_tpc_rates_array_fixed_params tpc_rates_array; + s16 *rate_array; +}; + +struct wmi_tpc_ctl_pwr_fixed_params { + __le32 ctl_array_type; + __le32 ctl_array_len; + __le32 end_of_ctl_pwr; + __le32 ctl_pwr_count; + __le32 d1; + __le32 d2; + __le32 d3; + __le32 d4; +} __packed; + +struct wmi_tpc_ctl_pwr_table_arg { + struct wmi_tpc_ctl_pwr_fixed_params tpc_ctl_pwr; + s8 *ctl_pwr_table; +}; + +struct wmi_tpc_stats_arg { + u32 pdev_id; + u32 event_count; + u32 end_of_event; + u32 tlvs_rcvd; + struct wmi_max_reg_power_allowed_arg max_reg_allowed_power; + struct wmi_tpc_rates_array_arg rates_array1; + struct wmi_tpc_rates_array_arg rates_array2; + struct wmi_tpc_config_params tpc_config; + struct wmi_tpc_ctl_pwr_table_arg ctl_array; +}; + +struct wmi_vdev_ch_power_params { + __le32 tlv_header; + + /* Channel center frequency (MHz) */ + __le32 chan_cfreq; + + /* Unit: dBm, either PSD/EIRP power for this frequency or + * incremental for non-PSD BW + */ + __le32 tx_power; +} __packed; + +struct wmi_vdev_set_tpc_power_cmd { + __le32 tlv_header; + __le32 vdev_id; + + /* Value: 0 or 1, is PSD power or not */ + __le32 psd_power; + + /* Maximum EIRP power (dBm units), valid only if power is PSD */ + __le32 eirp_power; + + /* Type: WMI_6GHZ_REG_TYPE, used for halphy CTL lookup */ + __le32 power_type_6ghz; + + /* This fixed_param TLV is followed by the below TLVs: + * num_pwr_levels of wmi_vdev_ch_power_info + * For PSD power, it is the PSD/EIRP power of the frequency (20 MHz chunks). + * For non-PSD power, the power values are for 20, 40, and till + * BSS BW power levels. + * The num_pwr_levels will be checked by sw how many elements present + * in the variable-length array. + */ +} __packed; + void ath12k_wmi_init_qcn9274(struct ath12k_base *ab, struct ath12k_wmi_resource_config_arg *config); void ath12k_wmi_init_wcn7850(struct ath12k_base *ab, @@ -5639,7 +6008,7 @@ int ath12k_wmi_mgmt_send(struct ath12k *ar, u32 vdev_id, u32 buf_id, struct sk_buff *frame); int ath12k_wmi_p2p_go_bcn_ie(struct ath12k *ar, u32 vdev_id, const u8 *p2p_ie); -int ath12k_wmi_bcn_tmpl(struct ath12k *ar, u32 vdev_id, +int ath12k_wmi_bcn_tmpl(struct ath12k_link_vif *arvif, struct ieee80211_mutable_offsets *offs, struct sk_buff *bcn, struct ath12k_wmi_bcn_tmpl_ema_arg *ema_args); @@ -5714,11 +6083,17 @@ int ath12k_wmi_send_bcn_offload_control_cmd(struct ath12k *ar, u32 vdev_id, u32 bcn_ctrl_op); int ath12k_wmi_send_init_country_cmd(struct ath12k *ar, struct ath12k_wmi_init_country_arg *arg); +int +ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar, + struct wmi_set_current_country_arg *arg); int ath12k_wmi_peer_rx_reorder_queue_setup(struct ath12k *ar, int vdev_id, const u8 *addr, dma_addr_t paddr, u8 tid, u8 ba_window_size_valid, u32 ba_window_size); +int ath12k_wmi_send_11d_scan_start_cmd(struct ath12k *ar, + struct wmi_11d_scan_start_arg *arg); +int ath12k_wmi_send_11d_scan_stop_cmd(struct ath12k *ar, u32 vdev_id); int ath12k_wmi_rx_reord_queue_remove(struct ath12k *ar, struct ath12k_wmi_rx_reorder_queue_remove_arg *arg); @@ -5753,6 +6128,13 @@ int ath12k_wmi_set_bios_cmd(struct ath12k_base *ab, u32 param_id, const u8 *buf, size_t buf_len); int ath12k_wmi_set_bios_sar_cmd(struct ath12k_base *ab, const u8 *psar_table); int ath12k_wmi_set_bios_geo_cmd(struct ath12k_base *ab, const u8 *pgeo_table); +int ath12k_wmi_send_stats_request_cmd(struct ath12k *ar, u32 stats_id, + u32 vdev_id, u32 pdev_id); +__le32 ath12k_wmi_tlv_hdr(u32 cmd, u32 len); + +int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar, + enum wmi_halphy_ctrl_path_stats_id tpc_stats_type); +void ath12k_wmi_free_tpc_stats_mem(struct ath12k *ar); static inline u32 ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param) @@ -5806,5 +6188,12 @@ int ath12k_wmi_sta_keepalive(struct ath12k *ar, int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params); int ath12k_wmi_mlo_ready(struct ath12k *ar); int ath12k_wmi_mlo_teardown(struct ath12k *ar); +void ath12k_wmi_fw_stats_dump(struct ath12k *ar, + struct ath12k_fw_stats *fw_stats, u32 stats_id, + char *buf); +bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar); +int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar, + u32 vdev_id, + struct ath12k_reg_tpc_power_info *param); #endif diff --git a/drivers/net/wireless/ath/ath12k/wow.c b/drivers/net/wireless/ath/ath12k/wow.c index 9e1c0bfd212f..dce9bd0bcaef 100644 --- a/drivers/net/wireless/ath/ath12k/wow.c +++ b/drivers/net/wireless/ath/ath12k/wow.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause-Clear /* * Copyright (c) 2020 The Linux Foundation. All rights reserved. - * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2022-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ #include <linux/delay.h> @@ -990,6 +990,7 @@ exit: case ATH12K_HW_STATE_RESTARTING: case ATH12K_HW_STATE_RESTARTED: case ATH12K_HW_STATE_WEDGED: + case ATH12K_HW_STATE_TM: ath12k_warn(ar->ab, "encountered unexpected device state %d on resume, cannot recover\n", ah->state); ret = -EIO; diff --git a/drivers/net/wireless/ath/ath6kl/cfg80211.c b/drivers/net/wireless/ath/ath6kl/cfg80211.c index 72ce321f2a77..8c2e8081112e 100644 --- a/drivers/net/wireless/ath/ath6kl/cfg80211.c +++ b/drivers/net/wireless/ath/ath6kl/cfg80211.c @@ -149,7 +149,7 @@ static bool __ath6kl_cfg80211_sscan_stop(struct ath6kl_vif *vif) if (!test_and_clear_bit(SCHED_SCANNING, &vif->flags)) return false; - del_timer_sync(&vif->sched_scan_timer); + timer_delete_sync(&vif->sched_scan_timer); if (ar->state == ATH6KL_STATE_RECOVERY) return true; @@ -1200,7 +1200,7 @@ static int ath6kl_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, if (((vif->auth_mode == WPA_PSK_AUTH) || (vif->auth_mode == WPA2_PSK_AUTH)) && (key_usage & GROUP_USAGE)) - del_timer(&vif->disconnect_timer); + timer_delete(&vif->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "%s: index %d, key_len %d, key_type 0x%x, key_usage 0x%x, seq_len %d\n", @@ -3612,7 +3612,7 @@ void ath6kl_cfg80211_vif_stop(struct ath6kl_vif *vif, bool wmi_ready) discon_issued = test_bit(CONNECTED, &vif->flags) || test_bit(CONNECT_PEND, &vif->flags); ath6kl_disconnect(vif); - del_timer(&vif->disconnect_timer); + timer_delete(&vif->disconnect_timer); if (discon_issued) ath6kl_disconnect_event(vif, DISCONNECT_CMD, diff --git a/drivers/net/wireless/ath/ath6kl/init.c b/drivers/net/wireless/ath/ath6kl/init.c index 15f455adb860..9b100ee2ebc3 100644 --- a/drivers/net/wireless/ath/ath6kl/init.c +++ b/drivers/net/wireless/ath/ath6kl/init.c @@ -1915,7 +1915,7 @@ void ath6kl_stop_txrx(struct ath6kl *ar) clear_bit(WMI_READY, &ar->flag); if (ar->fw_recovery.enable) - del_timer_sync(&ar->fw_recovery.hb_timer); + timer_delete_sync(&ar->fw_recovery.hb_timer); /* * After wmi_shudown all WMI events will be dropped. We diff --git a/drivers/net/wireless/ath/ath6kl/main.c b/drivers/net/wireless/ath/ath6kl/main.c index 8f9fe23e9755..867089a3c096 100644 --- a/drivers/net/wireless/ath/ath6kl/main.c +++ b/drivers/net/wireless/ath/ath6kl/main.c @@ -1027,7 +1027,7 @@ void ath6kl_disconnect_event(struct ath6kl_vif *vif, u8 reason, u8 *bssid, aggr_reset_state(vif->aggr_cntxt->aggr_conn); - del_timer(&vif->disconnect_timer); + timer_delete(&vif->disconnect_timer); ath6kl_dbg(ATH6KL_DBG_WLAN_CFG, "disconnect reason is %d\n", reason); diff --git a/drivers/net/wireless/ath/ath6kl/recovery.c b/drivers/net/wireless/ath/ath6kl/recovery.c index c09e40c9010f..fd2dceb8b63d 100644 --- a/drivers/net/wireless/ath/ath6kl/recovery.c +++ b/drivers/net/wireless/ath/ath6kl/recovery.c @@ -25,7 +25,7 @@ static void ath6kl_recovery_work(struct work_struct *work) ar->state = ATH6KL_STATE_RECOVERY; - del_timer_sync(&ar->fw_recovery.hb_timer); + timer_delete_sync(&ar->fw_recovery.hb_timer); ath6kl_init_hw_restart(ar); @@ -119,7 +119,7 @@ void ath6kl_recovery_cleanup(struct ath6kl *ar) set_bit(RECOVERY_CLEANUP, &ar->flag); - del_timer_sync(&ar->fw_recovery.hb_timer); + timer_delete_sync(&ar->fw_recovery.hb_timer); cancel_work_sync(&ar->fw_recovery.recovery_work); } diff --git a/drivers/net/wireless/ath/ath6kl/txrx.c b/drivers/net/wireless/ath/ath6kl/txrx.c index 80e66acc5cf6..3a6f0b647e17 100644 --- a/drivers/net/wireless/ath/ath6kl/txrx.c +++ b/drivers/net/wireless/ath/ath6kl/txrx.c @@ -1827,7 +1827,7 @@ void aggr_reset_state(struct aggr_info_conn *aggr_conn) return; if (aggr_conn->timer_scheduled) { - del_timer(&aggr_conn->timer); + timer_delete(&aggr_conn->timer); aggr_conn->timer_scheduled = false; } diff --git a/drivers/net/wireless/ath/ath9k/ahb.c b/drivers/net/wireless/ath/ath9k/ahb.c index d4805e02b927..49b7ab26c477 100644 --- a/drivers/net/wireless/ath/ath9k/ahb.c +++ b/drivers/net/wireless/ath/ath9k/ahb.c @@ -74,7 +74,6 @@ static int ath_ahb_probe(struct platform_device *pdev) void __iomem *mem; struct ath_softc *sc; struct ieee80211_hw *hw; - struct resource *res; const struct platform_device_id *id = platform_get_device_id(pdev); int irq; int ret = 0; @@ -86,16 +85,10 @@ static int ath_ahb_probe(struct platform_device *pdev) return -EINVAL; } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (res == NULL) { - dev_err(&pdev->dev, "no memory resource found\n"); - return -ENXIO; - } - - mem = devm_ioremap(&pdev->dev, res->start, resource_size(res)); - if (mem == NULL) { + mem = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(mem)) { dev_err(&pdev->dev, "ioremap failed\n"); - return -ENOMEM; + return PTR_ERR(mem); } irq = platform_get_irq(pdev, 0); diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h index a728cc0387df..6e38aa7351e3 100644 --- a/drivers/net/wireless/ath/ath9k/ath9k.h +++ b/drivers/net/wireless/ath/ath9k/ath9k.h @@ -274,7 +274,6 @@ struct ath_node { struct ath_tx_control { struct ath_txq *txq; - struct ath_node *an; struct ieee80211_sta *sta; u8 paprd; }; @@ -1018,7 +1017,7 @@ struct ath_softc { u8 gtt_cnt; u32 intrstatus; - u32 rx_active_check_time; + unsigned long rx_active_check_time; u32 rx_active_count; u16 ps_flags; /* PS_* */ bool ps_enabled; diff --git a/drivers/net/wireless/ath/ath9k/channel.c b/drivers/net/wireless/ath/ath9k/channel.c index bae24e3d3168..799be0be24f4 100644 --- a/drivers/net/wireless/ath/ath9k/channel.c +++ b/drivers/net/wireless/ath/ath9k/channel.c @@ -1556,7 +1556,7 @@ void ath9k_p2p_ps_timer(void *priv) struct ath_node *an; u32 tsf; - del_timer_sync(&sc->sched.timer); + timer_delete_sync(&sc->sched.timer); ath9k_hw_gen_timer_stop(sc->sc_ah, sc->p2p_ps_timer); ath_chanctx_event(sc, NULL, ATH_CHANCTX_EVENT_TSF_TIMER); diff --git a/drivers/net/wireless/ath/ath9k/common-spectral.c b/drivers/net/wireless/ath/ath9k/common-spectral.c index 628eeec4b82f..300d178830ad 100644 --- a/drivers/net/wireless/ath/ath9k/common-spectral.c +++ b/drivers/net/wireless/ath/ath9k/common-spectral.c @@ -628,12 +628,12 @@ int ath_cmn_process_fft(struct ath_spec_scan_priv *spec_priv, struct ieee80211_h else RX_STAT_INC(sc, rx_spectral_sample_err); - memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); - /* Mix the received bins to the /dev/random * pool */ add_device_randomness(sample_buf, num_bins); + + memset(sample_buf, 0, SPECTRAL_SAMPLE_MAX_LEN); } /* Process a normal frame */ diff --git a/drivers/net/wireless/ath/ath9k/gpio.c b/drivers/net/wireless/ath/ath9k/gpio.c index b457e52dd365..5a26f1d05f04 100644 --- a/drivers/net/wireless/ath/ath9k/gpio.c +++ b/drivers/net/wireless/ath/ath9k/gpio.c @@ -305,7 +305,7 @@ void ath9k_btcoex_timer_resume(struct ath_softc *sc) ath_dbg(ath9k_hw_common(ah), BTCOEX, "Starting btcoex timers\n"); /* make sure duty cycle timer is also stopped when resuming */ - del_timer_sync(&btcoex->no_stomp_timer); + timer_delete_sync(&btcoex->no_stomp_timer); btcoex->bt_priority_cnt = 0; btcoex->bt_priority_time = jiffies; @@ -329,15 +329,15 @@ void ath9k_btcoex_timer_pause(struct ath_softc *sc) ath_dbg(ath9k_hw_common(ah), BTCOEX, "Stopping btcoex timers\n"); - del_timer_sync(&btcoex->period_timer); - del_timer_sync(&btcoex->no_stomp_timer); + timer_delete_sync(&btcoex->period_timer); + timer_delete_sync(&btcoex->no_stomp_timer); } void ath9k_btcoex_stop_gen_timer(struct ath_softc *sc) { struct ath_btcoex *btcoex = &sc->btcoex; - del_timer_sync(&btcoex->no_stomp_timer); + timer_delete_sync(&btcoex->no_stomp_timer); } u16 ath9k_btcoex_aggr_limit(struct ath_softc *sc, u32 max_4ms_framelen) diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c index 547634f82183..81fa7cbad892 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_beacon.c @@ -290,6 +290,9 @@ void ath9k_htc_swba(struct ath9k_htc_priv *priv, struct ath_common *common = ath9k_hw_common(priv->ah); int slot; + if (!priv->cur_beacon_conf.enable_beacon) + return; + if (swba->beacon_pending != 0) { priv->beacon.bmisscnt++; if (priv->beacon.bmisscnt > BSTUCK_THRESHOLD) { diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c index 57094bd45d98..19600018e562 100644 --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c @@ -198,7 +198,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv) ath9k_htc_stop_ani(priv); ieee80211_stop_queues(priv->hw); - del_timer_sync(&priv->tx.cleanup_timer); + timer_delete_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); @@ -260,7 +260,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv, ath9k_htc_ps_wakeup(priv); ath9k_htc_stop_ani(priv); - del_timer_sync(&priv->tx.cleanup_timer); + timer_delete_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); WMI_CMD(WMI_DISABLE_INTR_CMDID); @@ -997,7 +997,7 @@ static void ath9k_htc_stop(struct ieee80211_hw *hw, bool suspend) tasklet_kill(&priv->rx_tasklet); - del_timer_sync(&priv->tx.cleanup_timer); + timer_delete_sync(&priv->tx.cleanup_timer); ath9k_htc_tx_drain(priv); ath9k_wmi_event_drain(priv); diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c index f9e77c4624d9..ee951493e993 100644 --- a/drivers/net/wireless/ath/ath9k/init.c +++ b/drivers/net/wireless/ath/ath9k/init.c @@ -647,7 +647,9 @@ static int ath9k_of_init(struct ath_softc *sc) ah->ah_flags |= AH_NO_EEP_SWAP; } - of_get_mac_address(np, common->macaddr); + ret = of_get_mac_address(np, common->macaddr); + if (ret == -EPROBE_DEFER) + return ret; return 0; } @@ -1097,7 +1099,7 @@ static void ath9k_deinit_softc(struct ath_softc *sc) if (ATH_TXQ_SETUP(sc, i)) ath_tx_cleanupq(sc, &sc->tx.txq[i]); - del_timer_sync(&sc->sleep_timer); + timer_delete_sync(&sc->sleep_timer); ath9k_hw_deinit(sc->sc_ah); if (sc->dfs_detector != NULL) sc->dfs_detector->exit(sc->dfs_detector); diff --git a/drivers/net/wireless/ath/ath9k/link.c b/drivers/net/wireless/ath/ath9k/link.c index d078a59d7d3c..7f890997bb53 100644 --- a/drivers/net/wireless/ath/ath9k/link.c +++ b/drivers/net/wireless/ath/ath9k/link.c @@ -472,7 +472,7 @@ void ath_stop_ani(struct ath_softc *sc) struct ath_common *common = ath9k_hw_common(sc->sc_ah); ath_dbg(common, ANI, "Stopping ANI\n"); - del_timer_sync(&common->ani.timer); + timer_delete_sync(&common->ani.timer); } void ath_check_ani(struct ath_softc *sc) diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c index a70c94564814..92fc5e3d756e 100644 --- a/drivers/net/wireless/ath/ath9k/main.c +++ b/drivers/net/wireless/ath/ath9k/main.c @@ -123,7 +123,7 @@ void ath9k_ps_wakeup(struct ath_softc *sc) if (++sc->ps_usecount != 1) goto unlock; - del_timer_sync(&sc->sleep_timer); + timer_delete_sync(&sc->sleep_timer); power_mode = sc->sc_ah->power_mode; ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_AWAKE); @@ -2418,7 +2418,7 @@ static void ath9k_cancel_pending_offchannel(struct ath_softc *sc) ath_dbg(common, CHAN_CTX, "%s: Aborting RoC\n", __func__); - del_timer_sync(&sc->offchannel.timer); + timer_delete_sync(&sc->offchannel.timer); if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) ath_roc_complete(sc, ATH_ROC_COMPLETE_ABORT); } @@ -2427,7 +2427,7 @@ static void ath9k_cancel_pending_offchannel(struct ath_softc *sc) ath_dbg(common, CHAN_CTX, "%s: Aborting HW scan\n", __func__); - del_timer_sync(&sc->offchannel.timer); + timer_delete_sync(&sc->offchannel.timer); ath_scan_complete(sc, true); } } @@ -2476,7 +2476,7 @@ static void ath9k_cancel_hw_scan(struct ieee80211_hw *hw, ath_dbg(common, CHAN_CTX, "Cancel HW scan on vif: %pM\n", vif->addr); mutex_lock(&sc->mutex); - del_timer_sync(&sc->offchannel.timer); + timer_delete_sync(&sc->offchannel.timer); ath_scan_complete(sc, true); mutex_unlock(&sc->mutex); } @@ -2526,7 +2526,7 @@ static int ath9k_cancel_remain_on_channel(struct ieee80211_hw *hw, mutex_lock(&sc->mutex); ath_dbg(common, CHAN_CTX, "Cancel RoC\n"); - del_timer_sync(&sc->offchannel.timer); + timer_delete_sync(&sc->offchannel.timer); if (sc->offchannel.roc_vif) { if (sc->offchannel.state >= ATH_OFFCHANNEL_ROC_START) diff --git a/drivers/net/wireless/ath/ath9k/pci.c b/drivers/net/wireless/ath/ath9k/pci.c index 1ff53520f0a3..27d4034c814e 100644 --- a/drivers/net/wireless/ath/ath9k/pci.c +++ b/drivers/net/wireless/ath/ath9k/pci.c @@ -1029,7 +1029,7 @@ static int ath_pci_suspend(struct device *device) */ ath9k_stop_btcoex(sc); ath9k_hw_disable(sc->sc_ah); - del_timer_sync(&sc->sleep_timer); + timer_delete_sync(&sc->sleep_timer); ath9k_hw_setpower(sc->sc_ah, ATH9K_PM_FULL_SLEEP); return 0; diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index db07ce6dbc08..0ac9212e42f7 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c @@ -2291,19 +2291,10 @@ static int ath_tx_prepare(struct ieee80211_hw *hw, struct sk_buff *skb, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_sta *sta = txctl->sta; struct ieee80211_vif *vif = info->control.vif; - struct ath_vif *avp; struct ath_softc *sc = hw->priv; int frmlen = skb->len + FCS_LEN; int padpos, padsize; - /* NOTE: sta can be NULL according to net/mac80211.h */ - if (sta) - txctl->an = (struct ath_node *)sta->drv_priv; - else if (vif && ieee80211_is_data(hdr->frame_control)) { - avp = (void *)vif->drv_priv; - txctl->an = &avp->mcast_node; - } - if (info->control.hw_key) frmlen += info->control.hw_key->icv_len; diff --git a/drivers/net/wireless/ath/carl9170/fw.c b/drivers/net/wireless/ath/carl9170/fw.c index 4c1aecd1163c..419f5530f885 100644 --- a/drivers/net/wireless/ath/carl9170/fw.c +++ b/drivers/net/wireless/ath/carl9170/fw.c @@ -15,7 +15,7 @@ #include "fwcmd.h" #include "version.h" -static const u8 otus_magic[4] = { OTUS_MAGIC }; +static const u8 otus_magic[4] __nonstring = { OTUS_MAGIC }; static const void *carl9170_fw_find_desc(struct ar9170 *ar, const u8 descid[4], const unsigned int len, const u8 compatible_revision) diff --git a/drivers/net/wireless/ath/carl9170/tx.c b/drivers/net/wireless/ath/carl9170/tx.c index 0226c31a6cae..b7717f9e1e9b 100644 --- a/drivers/net/wireless/ath/carl9170/tx.c +++ b/drivers/net/wireless/ath/carl9170/tx.c @@ -366,8 +366,7 @@ static void carl9170_tx_shift_bm(struct ar9170 *ar, if (WARN_ON_ONCE(off >= CARL9170_BAW_BITS)) return; - if (!bitmap_empty(tid_info->bitmap, off)) - off = find_first_bit(tid_info->bitmap, off); + off = min(off, find_first_bit(tid_info->bitmap, off)); tid_info->bsn += off; tid_info->bsn &= 0x0fff; diff --git a/drivers/net/wireless/ath/ath11k/testmode_i.h b/drivers/net/wireless/ath/testmode_i.h index 91b83873d660..980ef2f3f05f 100644 --- a/drivers/net/wireless/ath/ath11k/testmode_i.h +++ b/drivers/net/wireless/ath/testmode_i.h @@ -1,59 +1,59 @@ /* SPDX-License-Identifier: BSD-3-Clause-Clear */ /* * Copyright (c) 2018-2019 The Linux Foundation. All rights reserved. - * Copyright (c) 2023 Qualcomm Innovation Center, Inc. All rights reserved. + * Copyright (c) 2023-2025 Qualcomm Innovation Center, Inc. All rights reserved. */ -/* "API" level of the ath11k testmode interface. Bump it after every +/* "API" level of the ath testmode interface. Bump it after every * incompatible interface change. */ -#define ATH11K_TESTMODE_VERSION_MAJOR 1 +#define ATH_TESTMODE_VERSION_MAJOR 1 /* Bump this after every _compatible_ interface change, for example * addition of a new command or an attribute. */ -#define ATH11K_TESTMODE_VERSION_MINOR 1 +#define ATH_TESTMODE_VERSION_MINOR 1 -#define ATH11K_TM_DATA_MAX_LEN 5000 -#define ATH11K_FTM_EVENT_MAX_BUF_LENGTH 2048 +#define ATH_TM_DATA_MAX_LEN 5000 +#define ATH_FTM_EVENT_MAX_BUF_LENGTH 2048 -enum ath11k_tm_attr { - __ATH11K_TM_ATTR_INVALID = 0, - ATH11K_TM_ATTR_CMD = 1, - ATH11K_TM_ATTR_DATA = 2, - ATH11K_TM_ATTR_WMI_CMDID = 3, - ATH11K_TM_ATTR_VERSION_MAJOR = 4, - ATH11K_TM_ATTR_VERSION_MINOR = 5, - ATH11K_TM_ATTR_WMI_OP_VERSION = 6, +enum ath_tm_attr { + __ATH_TM_ATTR_INVALID = 0, + ATH_TM_ATTR_CMD = 1, + ATH_TM_ATTR_DATA = 2, + ATH_TM_ATTR_WMI_CMDID = 3, + ATH_TM_ATTR_VERSION_MAJOR = 4, + ATH_TM_ATTR_VERSION_MINOR = 5, + ATH_TM_ATTR_WMI_OP_VERSION = 6, /* keep last */ - __ATH11K_TM_ATTR_AFTER_LAST, - ATH11K_TM_ATTR_MAX = __ATH11K_TM_ATTR_AFTER_LAST - 1, + __ATH_TM_ATTR_AFTER_LAST, + ATH_TM_ATTR_MAX = __ATH_TM_ATTR_AFTER_LAST - 1, }; -/* All ath11k testmode interface commands specified in - * ATH11K_TM_ATTR_CMD +/* All ath testmode interface commands specified in + * ATH_TM_ATTR_CMD */ -enum ath11k_tm_cmd { - /* Returns the supported ath11k testmode interface version in - * ATH11K_TM_ATTR_VERSION. Always guaranteed to work. User space +enum ath_tm_cmd { + /* Returns the supported ath testmode interface version in + * ATH_TM_ATTR_VERSION. Always guaranteed to work. User space * uses this to verify it's using the correct version of the * testmode interface */ - ATH11K_TM_CMD_GET_VERSION = 0, + ATH_TM_CMD_GET_VERSION = 0, /* The command used to transmit a WMI command to the firmware and * the event to receive WMI events from the firmware. Without * struct wmi_cmd_hdr header, only the WMI payload. Command id is - * provided with ATH11K_TM_ATTR_WMI_CMDID and payload in - * ATH11K_TM_ATTR_DATA. + * provided with ATH_TM_ATTR_WMI_CMDID and payload in + * ATH_TM_ATTR_DATA. */ - ATH11K_TM_CMD_WMI = 1, + ATH_TM_CMD_WMI = 1, /* Boots the UTF firmware, the netdev interface must be down at the * time. */ - ATH11K_TM_CMD_TESTMODE_START = 2, + ATH_TM_CMD_TESTMODE_START = 2, /* The command used to transmit a FTM WMI command to the firmware * and the event to receive WMI events from the firmware. The data @@ -62,5 +62,5 @@ enum ath11k_tm_cmd { * The data payload size could be large and the driver needs to * send segmented data to firmware. */ - ATH11K_TM_CMD_WMI_FTM = 3, + ATH_TM_CMD_WMI_FTM = 3, }; diff --git a/drivers/net/wireless/ath/wcn36xx/dxe.c b/drivers/net/wireless/ath/wcn36xx/dxe.c index d405a4c34059..cc2a033e87f5 100644 --- a/drivers/net/wireless/ath/wcn36xx/dxe.c +++ b/drivers/net/wireless/ath/wcn36xx/dxe.c @@ -350,7 +350,7 @@ void wcn36xx_dxe_tx_ack_ind(struct wcn36xx *wcn, u32 status) spin_lock_irqsave(&wcn->dxe_lock, flags); skb = wcn->tx_ack_skb; wcn->tx_ack_skb = NULL; - del_timer(&wcn->tx_ack_timer); + timer_delete(&wcn->tx_ack_timer); spin_unlock_irqrestore(&wcn->dxe_lock, flags); if (!skb) { @@ -1055,7 +1055,7 @@ void wcn36xx_dxe_deinit(struct wcn36xx *wcn) free_irq(wcn->tx_irq, wcn); free_irq(wcn->rx_irq, wcn); - del_timer(&wcn->tx_ack_timer); + timer_delete(&wcn->tx_ack_timer); if (wcn->tx_ack_skb) { ieee80211_tx_status_irqsafe(wcn->hw, wcn->tx_ack_skb); diff --git a/drivers/net/wireless/ath/wcn36xx/testmode.c b/drivers/net/wireless/ath/wcn36xx/testmode.c index e5142c052985..d7a2a483cbc4 100644 --- a/drivers/net/wireless/ath/wcn36xx/testmode.c +++ b/drivers/net/wireless/ath/wcn36xx/testmode.c @@ -56,7 +56,7 @@ static int wcn36xx_tm_cmd_ptt(struct wcn36xx *wcn, struct ieee80211_vif *vif, msg = buf; wcn36xx_dbg(WCN36XX_DBG_TESTMODE, - "testmode cmd wmi msg_id 0x%04X msg_len %d buf %pK buf_len %d\n", + "testmode cmd wmi msg_id 0x%04X msg_len %d buf %p buf_len %d\n", msg->msg_id, msg->msg_body_length, buf, buf_len); diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c index a1a0a9223e74..5473c01cbe66 100644 --- a/drivers/net/wireless/ath/wil6210/cfg80211.c +++ b/drivers/net/wireless/ath/wil6210/cfg80211.c @@ -1017,7 +1017,7 @@ static int wil_cfg80211_scan(struct wiphy *wiphy, out_restore: if (rc) { - del_timer_sync(&vif->scan_timer); + timer_delete_sync(&vif->scan_timer); if (vif->mid == 0) wil->radio_wdev = wil->main_ndev->ieee80211_ptr; vif->scan_request = NULL; diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c index 94e61dbe94f8..44c24c6c8360 100644 --- a/drivers/net/wireless/ath/wil6210/main.c +++ b/drivers/net/wireless/ath/wil6210/main.c @@ -798,7 +798,7 @@ void wil6210_disconnect(struct wil6210_vif *vif, const u8 *bssid, wil_dbg_misc(wil, "disconnecting\n"); - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); _wil6210_disconnect(vif, bssid, reason_code); } @@ -818,7 +818,7 @@ void wil6210_disconnect_complete(struct wil6210_vif *vif, const u8 *bssid, wil_dbg_misc(wil, "got disconnect\n"); - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); _wil6210_disconnect_complete(vif, bssid, reason_code); } @@ -1465,7 +1465,7 @@ void wil_abort_scan(struct wil6210_vif *vif, bool sync) return; wil_dbg_misc(wil, "Abort scan_request 0x%p\n", vif->scan_request); - del_timer_sync(&vif->scan_timer); + timer_delete_sync(&vif->scan_timer); mutex_unlock(&wil->vif_mutex); rc = wmi_abort_scan(vif); if (!rc && sync) diff --git a/drivers/net/wireless/ath/wil6210/netdev.c b/drivers/net/wireless/ath/wil6210/netdev.c index d5d364683c0e..59884e8e3765 100644 --- a/drivers/net/wireless/ath/wil6210/netdev.c +++ b/drivers/net/wireless/ath/wil6210/netdev.c @@ -200,8 +200,8 @@ static void wil_dev_setup(struct net_device *dev) static void wil_vif_deinit(struct wil6210_vif *vif) { - del_timer_sync(&vif->scan_timer); - del_timer_sync(&vif->p2p.discovery_timer); + timer_delete_sync(&vif->scan_timer); + timer_delete_sync(&vif->p2p.discovery_timer); cancel_work_sync(&vif->disconnect_worker); cancel_work_sync(&vif->p2p.discovery_expired_work); cancel_work_sync(&vif->p2p.delayed_listen_work); @@ -533,7 +533,7 @@ void wil_vif_remove(struct wil6210_priv *wil, u8 mid) mutex_unlock(&wil->vif_mutex); flush_work(&wil->wmi_event_worker); - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); cancel_work_sync(&vif->disconnect_worker); wil_probe_client_flush(vif); cancel_work_sync(&vif->probe_client_worker); diff --git a/drivers/net/wireless/ath/wil6210/p2p.c b/drivers/net/wireless/ath/wil6210/p2p.c index f26bf046d889..f20caf1a3905 100644 --- a/drivers/net/wireless/ath/wil6210/p2p.c +++ b/drivers/net/wireless/ath/wil6210/p2p.c @@ -184,7 +184,7 @@ u8 wil_p2p_stop_discovery(struct wil6210_vif *vif) /* discovery not really started, only pending */ p2p->pending_listen_wdev = NULL; } else { - del_timer_sync(&p2p->discovery_timer); + timer_delete_sync(&p2p->discovery_timer); wmi_stop_discovery(vif); } p2p->discovery_started = 0; diff --git a/drivers/net/wireless/ath/wil6210/txrx.h b/drivers/net/wireless/ath/wil6210/txrx.h index 689f68d89a44..fff8b7f8abc5 100644 --- a/drivers/net/wireless/ath/wil6210/txrx.h +++ b/drivers/net/wireless/ath/wil6210/txrx.h @@ -7,6 +7,7 @@ #ifndef WIL6210_TXRX_H #define WIL6210_TXRX_H +#include <net/sock.h> #include "wil6210.h" #include "txrx_edma.h" @@ -616,8 +617,7 @@ static inline bool wil_need_txstat(struct sk_buff *skb) { const u8 *da = wil_skb_get_da(skb); - return is_unicast_ether_addr(da) && skb->sk && - (skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS); + return is_unicast_ether_addr(da) && sk_requests_wifi_status(skb->sk); } static inline void wil_consume_skb(struct sk_buff *skb, bool acked) diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c index 8ff69dc72fb9..74edd007cd8d 100644 --- a/drivers/net/wireless/ath/wil6210/wmi.c +++ b/drivers/net/wireless/ath/wil6210/wmi.c @@ -933,7 +933,7 @@ static void wmi_evt_scan_complete(struct wil6210_vif *vif, int id, wil_dbg_wmi(wil, "SCAN_COMPLETE(0x%08x)\n", status); wil_dbg_misc(wil, "Complete scan_request 0x%p aborted %d\n", vif->scan_request, info.aborted); - del_timer_sync(&vif->scan_timer); + timer_delete_sync(&vif->scan_timer); cfg80211_scan_done(vif->scan_request, &info); if (vif->mid == 0) wil->radio_wdev = wil->main_ndev->ieee80211_ptr; @@ -1023,7 +1023,7 @@ static void wmi_evt_connect(struct wil6210_vif *vif, int id, void *d, int len) mutex_unlock(&wil->mutex); return; } - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); } else if ((wdev->iftype == NL80211_IFTYPE_AP) || (wdev->iftype == NL80211_IFTYPE_P2P_GO)) { if (wil->sta[evt->cid].status != wil_sta_unused) { @@ -1814,7 +1814,7 @@ wmi_evt_reassoc_status(struct wil6210_vif *vif, int id, void *d, int len) wil->sta[cid].stats.ft_roams++; ether_addr_copy(wil->sta[cid].addr, vif->bss->bssid); mutex_unlock(&wil->mutex); - del_timer_sync(&vif->connect_timer); + timer_delete_sync(&vif->connect_timer); cfg80211_ref_bss(wiphy, vif->bss); freq = ieee80211_channel_to_frequency(ch, NL80211_BAND_60GHZ); diff --git a/drivers/net/wireless/atmel/at76c50x-usb.c b/drivers/net/wireless/atmel/at76c50x-usb.c index 504e05ea30f2..6842c2b02b39 100644 --- a/drivers/net/wireless/atmel/at76c50x-usb.c +++ b/drivers/net/wireless/atmel/at76c50x-usb.c @@ -2417,7 +2417,7 @@ static void at76_delete_device(struct at76_priv *priv) kfree(priv->bulk_out_buffer); - del_timer_sync(&ledtrig_tx_timer); + timer_delete_sync(&ledtrig_tx_timer); kfree_skb(priv->rx_skb); @@ -2552,7 +2552,7 @@ static void at76_disconnect(struct usb_interface *interface) wiphy_info(priv->hw->wiphy, "disconnecting\n"); at76_delete_device(priv); - usb_put_dev(priv->udev); + usb_put_dev(interface_to_usbdev(interface)); dev_info(&interface->dev, "disconnected\n"); } diff --git a/drivers/net/wireless/broadcom/b43/main.c b/drivers/net/wireless/broadcom/b43/main.c index 25b4ef9d3c9a..7529afd24aed 100644 --- a/drivers/net/wireless/broadcom/b43/main.c +++ b/drivers/net/wireless/broadcom/b43/main.c @@ -2166,7 +2166,7 @@ static void b43_print_fw_helptext(struct b43_wl *wl, bool error) { const char text[] = "You must go to " \ - "https://wireless.wiki.kernel.org/en/users/Drivers/b43#devicefirmware " \ + "https://wireless.docs.kernel.org/en/latest/en/users/drivers/b43/developers.html#list-of-firmware " \ "and download the correct firmware for this driver version. " \ "Please carefully read all instructions on this website.\n"; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c index 60eb95fc19a5..6bc107476a2a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/bcmsdh.c @@ -1172,6 +1172,7 @@ static int brcmf_ops_sdio_suspend(struct device *dev) struct brcmf_bus *bus_if; struct brcmf_sdio_dev *sdiodev; mmc_pm_flag_t sdio_flags; + bool cap_power_off; int ret = 0; func = container_of(dev, struct sdio_func, dev); @@ -1179,19 +1180,23 @@ static int brcmf_ops_sdio_suspend(struct device *dev) if (func->num != 1) return 0; + cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD); bus_if = dev_get_drvdata(dev); sdiodev = bus_if->bus_priv.sdio; - if (sdiodev->wowl_enabled) { + if (sdiodev->wowl_enabled || !cap_power_off) { brcmf_sdiod_freezer_on(sdiodev); brcmf_sdio_wd_timer(sdiodev->bus, 0); sdio_flags = MMC_PM_KEEP_POWER; - if (sdiodev->settings->bus.sdio.oob_irq_supported) - enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); - else - sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; + + if (sdiodev->wowl_enabled) { + if (sdiodev->settings->bus.sdio.oob_irq_supported) + enable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); + else + sdio_flags |= MMC_PM_WAKE_SDIO_IRQ; + } if (sdio_set_host_pm_flags(sdiodev->func1, sdio_flags)) brcmf_err("Failed to set pm_flags %x\n", sdio_flags); @@ -1213,18 +1218,19 @@ static int brcmf_ops_sdio_resume(struct device *dev) struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; struct sdio_func *func = container_of(dev, struct sdio_func, dev); int ret = 0; + bool cap_power_off = !!(func->card->host->caps & MMC_CAP_POWER_OFF_CARD); brcmf_dbg(SDIO, "Enter: F%d\n", func->num); if (func->num != 2) return 0; - if (!sdiodev->wowl_enabled) { + if (!sdiodev->wowl_enabled && cap_power_off) { /* bus was powered off and device removed, probe again */ ret = brcmf_sdiod_probe(sdiodev); if (ret) brcmf_err("Failed to probe device on resume\n"); } else { - if (sdiodev->settings->bus.sdio.oob_irq_supported) + if (sdiodev->wowl_enabled && sdiodev->settings->bus.sdio.oob_irq_supported) disable_irq_wake(sdiodev->settings->bus.sdio.oob_irq_nr); brcmf_sdiod_freezer_off(sdiodev); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c index 1e8495f50c16..e0de34a3e43a 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/btcoex.c @@ -289,7 +289,7 @@ static void brcmf_btcoex_handler(struct work_struct *work) btci = container_of(work, struct brcmf_btcoex_info, work); if (btci->timer_on) { btci->timer_on = false; - del_timer_sync(&btci->timer); + timer_delete_sync(&btci->timer); } switch (btci->bt_state) { @@ -428,7 +428,7 @@ static void brcmf_btcoex_dhcp_end(struct brcmf_btcoex_info *btci) if (btci->timer_on) { brcmf_dbg(INFO, "disable BT DHCP Timer\n"); btci->timer_on = false; - del_timer_sync(&btci->timer); + timer_delete_sync(&btci->timer); /* schedule worker if transition to IDLE is needed */ if (btci->bt_state != BRCMF_BT_DHCP_IDLE) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c index 4b70845e1a26..dc2383faddd1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c @@ -74,7 +74,6 @@ #define VNDR_IE_HDR_SIZE 12 #define VNDR_IE_PARSE_LIMIT 5 -#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ #define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */ #define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 @@ -1945,17 +1944,22 @@ static s32 brcmf_set_wpa_version(struct net_device *ndev, struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); struct brcmf_pub *drvr = ifp->drvr; struct brcmf_cfg80211_security *sec; - s32 val = 0; - s32 err = 0; + s32 val; + s32 err; - if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) + if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) { val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; - else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) - val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; - else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) + } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) { + if (drvr->bus_if->fwvid == BRCMF_FWVENDOR_CYW && + sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_SAE) + val = WPA3_AUTH_SAE_PSK; + else + val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; + } else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) { val = WPA3_AUTH_SAE_PSK; - else + } else { val = WPA_AUTH_DISABLED; + } brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val); if (err) { @@ -2163,28 +2167,25 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) switch (sme->crypto.akm_suites[0]) { case WLAN_AKM_SUITE_SAE: val = WPA3_AUTH_SAE_PSK; - if (sme->crypto.sae_pwd) { - brcmf_dbg(INFO, "using SAE offload\n"); - profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; - } break; case WLAN_AKM_SUITE_FT_OVER_SAE: val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT; profile->is_ft = true; - if (sme->crypto.sae_pwd) { - brcmf_dbg(INFO, "using SAE offload\n"); - profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; - } break; default: bphy_err(drvr, "invalid akm suite (%d)\n", sme->crypto.akm_suites[0]); return -EINVAL; } + if (sme->crypto.sae_pwd) { + profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; + } } if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) brcmf_dbg(INFO, "using 1X offload\n"); + if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) + brcmf_dbg(INFO, "using SAE offload\n"); if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) goto skip_mfp_config; @@ -2221,7 +2222,7 @@ brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp); skip_mfp_config: - brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); + brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); if (err) { bphy_err(drvr, "could not set wpa_auth (%d)\n", err); @@ -5509,7 +5510,7 @@ brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy, } -static int +int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, struct cfg80211_mgmt_tx_params *params, u64 *cookie) { @@ -5616,6 +5617,7 @@ brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, exit: return err; } +BRCMF_EXPORT_SYMBOL_GPL(brcmf_cfg80211_mgmt_tx); static int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy, struct net_device *ndev, @@ -6009,6 +6011,7 @@ struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, vif->wdev.wiphy = cfg->wiphy; vif->wdev.iftype = type; + init_completion(&vif->mgmt_tx); brcmf_init_prof(&vif->profile); @@ -6760,6 +6763,8 @@ static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP, brcmf_notify_connect_status); brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi); + + brcmf_fwvid_register_event_handlers(cfg->pub); } static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) @@ -7346,6 +7351,7 @@ brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { [NL80211_IFTYPE_STATION] = { .tx = 0xffff, .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | + BIT(IEEE80211_STYPE_AUTH >> 4) | BIT(IEEE80211_STYPE_PROBE_REQ >> 4) }, [NL80211_IFTYPE_P2P_CLIENT] = { @@ -7653,6 +7659,8 @@ static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SAE_OFFLOAD_AP); } + if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT)) + wiphy->features |= NL80211_FEATURE_SAE; wiphy->mgmt_stypes = brcmf_txrx_stypes; wiphy->max_remain_on_channel_duration = 5000; if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h index 2abae8894614..b83485ec7b87 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h @@ -13,6 +13,8 @@ #include "fwil_types.h" #include "p2p.h" +#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ + #define BRCMF_SCAN_IE_LEN_MAX 2048 #define WL_NUM_SCAN_MAX 10 @@ -142,6 +144,21 @@ enum brcmf_profile_fwauth { }; /** + * enum brcmf_mgmt_tx_status - mgmt frame tx status + * + * @BRCMF_MGMT_TX_ACK: mgmt frame acked + * @BRCMF_MGMT_TX_NOACK: mgmt frame not acked + * @BRCMF_MGMT_TX_OFF_CHAN_COMPLETED: off-channel complete + * @BRCMF_MGMT_TX_SEND_FRAME: mgmt frame tx is in progres + */ +enum brcmf_mgmt_tx_status { + BRCMF_MGMT_TX_ACK, + BRCMF_MGMT_TX_NOACK, + BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, + BRCMF_MGMT_TX_SEND_FRAME +}; + +/** * struct brcmf_cfg80211_profile - profile information. * * @bssid: bssid of joined/joining ibss. @@ -211,6 +228,9 @@ struct vif_saved_ie { * @profile: profile information. * @sme_state: SME state using enum brcmf_vif_status bits. * @list: linked list. + * @mgmt_tx: completion for management frame transmit. + * @mgmt_tx_status: status of last management frame sent to firmware. + * @mgmt_tx_id: * @mgmt_rx_reg: registered rx mgmt frame types. * @mbss: Multiple BSS type, set if not first AP (not relevant for P2P). * @cqm_rssi_low: Lower RSSI limit for CQM monitoring @@ -224,6 +244,9 @@ struct brcmf_cfg80211_vif { unsigned long sme_state; struct vif_saved_ie saved_ie; struct list_head list; + struct completion mgmt_tx; + unsigned long mgmt_tx_status; + u32 mgmt_tx_id; u16 mgmt_rx_reg; bool mbss; int is_11d; @@ -468,5 +491,7 @@ void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg); void brcmf_cfg80211_free_netdev(struct net_device *ndev); int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags); +int brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie); #endif /* BRCMFMAC_CFG80211_H */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c index cfcf01eb0daa..75f101622db1 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c @@ -491,6 +491,7 @@ void __brcmf_dbg(u32 level, const char *func, const char *fmt, ...) trace_brcmf_dbg(level, func, &vaf); va_end(args); } +BRCMF_EXPORT_SYMBOL_GPL(__brcmf_dbg); #endif static void brcmf_mp_attach(void) @@ -561,8 +562,10 @@ struct brcmf_mp_device *brcmf_get_module_param(struct device *dev, if (!found) { /* No platform data for this device, try OF and DMI data */ brcmf_dmi_probe(settings, chip, chiprev); - if (brcmf_of_probe(dev, bus_type, settings) == -EPROBE_DEFER) + if (brcmf_of_probe(dev, bus_type, settings) == -EPROBE_DEFER) { + kfree(settings); return ERR_PTR(-EPROBE_DEFER); + } brcmf_acpi_probe(dev, bus_type, settings); } return settings; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c index 3d63010ae079..04f41c09deca 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c @@ -1363,6 +1363,8 @@ int brcmf_attach(struct device *dev) brcmf_fweh_register(drvr, BRCMF_E_PSM_WATCHDOG, brcmf_psm_watchdog_notify); + brcmf_fwvid_get_cfg80211_ops(drvr); + ret = brcmf_bus_started(drvr, drvr->ops); if (ret != 0) { bphy_err(drvr, "dongle is not responding: err=%d\n", ret); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c index 9a4837881486..c9537fb597ce 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/core.c @@ -8,11 +8,21 @@ #include <bus.h> #include <fwvid.h> #include <fwil.h> +#include <fweh.h> #include "vops.h" +#include "fwil_types.h" +/* event definitions */ +#define BRCMF_CYW_E_EXT_AUTH_REQ 187 +#define BRCMF_CYW_E_EXT_AUTH_FRAME_RX 188 +#define BRCMF_CYW_E_MGMT_FRAME_TXS 189 +#define BRCMF_CYW_E_MGMT_FRAME_TXS_OC 190 #define BRCMF_CYW_E_LAST 197 +#define MGMT_AUTH_FRAME_DWELL_TIME 4000 +#define MGMT_AUTH_FRAME_WAIT_TIME (MGMT_AUTH_FRAME_DWELL_TIME + 100) + static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp, struct cfg80211_crypto_settings *crypto) { @@ -39,6 +49,19 @@ static int brcmf_cyw_set_sae_pwd(struct brcmf_if *ifp, return err; } +static const struct brcmf_fweh_event_map brcmf_cyw_event_map = { + .items = { + { BRCMF_E_EXT_AUTH_REQ, BRCMF_CYW_E_EXT_AUTH_REQ }, + { BRCMF_E_EXT_AUTH_FRAME_RX, BRCMF_CYW_E_EXT_AUTH_FRAME_RX }, + { BRCMF_E_MGMT_FRAME_TXSTATUS, BRCMF_CYW_E_MGMT_FRAME_TXS }, + { + BRCMF_E_MGMT_FRAME_OFFCHAN_DONE, + BRCMF_CYW_E_MGMT_FRAME_TXS_OC + }, + }, + .n_items = 4 +}; + static int brcmf_cyw_alloc_fweh_info(struct brcmf_pub *drvr) { struct brcmf_fweh_info *fweh; @@ -49,11 +72,296 @@ static int brcmf_cyw_alloc_fweh_info(struct brcmf_pub *drvr) return -ENOMEM; fweh->num_event_codes = BRCMF_CYW_E_LAST; + fweh->event_map = &brcmf_cyw_event_map; drvr->fweh = fweh; return 0; } +static int brcmf_cyw_activate_events(struct brcmf_if *ifp) +{ + struct brcmf_fweh_info *fweh = ifp->drvr->fweh; + struct brcmf_eventmsgs_ext *eventmask_msg; + u32 msglen; + int err; + + msglen = sizeof(*eventmask_msg) + fweh->event_mask_len; + eventmask_msg = kzalloc(msglen, GFP_KERNEL); + if (!eventmask_msg) + return -ENOMEM; + eventmask_msg->ver = EVENTMSGS_VER; + eventmask_msg->command = CYW_EVENTMSGS_SET_MASK; + eventmask_msg->len = fweh->event_mask_len; + memcpy(eventmask_msg->mask, fweh->event_mask, fweh->event_mask_len); + + err = brcmf_fil_iovar_data_set(ifp, "event_msgs_ext", eventmask_msg, + msglen); + kfree(eventmask_msg); + return err; +} + +static +int brcmf_cyw_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, + struct cfg80211_mgmt_tx_params *params, u64 *cookie) +{ + struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); + struct ieee80211_channel *chan = params->chan; + struct brcmf_pub *drvr = cfg->pub; + const u8 *buf = params->buf; + size_t len = params->len; + const struct ieee80211_mgmt *mgmt; + struct brcmf_cfg80211_vif *vif; + s32 err = 0; + bool ack = false; + s32 chan_nr; + u32 freq; + struct brcmf_mf_params_le *mf_params; + u32 mf_params_len; + s32 ready; + + brcmf_dbg(TRACE, "Enter\n"); + + mgmt = (const struct ieee80211_mgmt *)buf; + + if (!ieee80211_is_auth(mgmt->frame_control)) + return brcmf_cfg80211_mgmt_tx(wiphy, wdev, params, cookie); + + *cookie = 0; + vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); + + reinit_completion(&vif->mgmt_tx); + clear_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status); + clear_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status); + clear_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, + &vif->mgmt_tx_status); + mf_params_len = offsetof(struct brcmf_mf_params_le, data) + + (len - DOT11_MGMT_HDR_LEN); + mf_params = kzalloc(mf_params_len, GFP_KERNEL); + if (!mf_params) + return -ENOMEM; + + mf_params->dwell_time = cpu_to_le32(MGMT_AUTH_FRAME_DWELL_TIME); + mf_params->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); + mf_params->frame_control = mgmt->frame_control; + + if (chan) + freq = chan->center_freq; + else + brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, + &freq); + chan_nr = ieee80211_frequency_to_channel(freq); + mf_params->channel = cpu_to_le16(chan_nr); + memcpy(&mf_params->da[0], &mgmt->da[0], ETH_ALEN); + memcpy(&mf_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); + mf_params->packet_id = cpu_to_le32(*cookie); + memcpy(mf_params->data, &buf[DOT11_MGMT_HDR_LEN], + le16_to_cpu(mf_params->len)); + + brcmf_dbg(TRACE, "Auth frame, cookie=%d, fc=%04x, len=%d, channel=%d\n", + le32_to_cpu(mf_params->packet_id), + le16_to_cpu(mf_params->frame_control), + le16_to_cpu(mf_params->len), chan_nr); + + vif->mgmt_tx_id = le32_to_cpu(mf_params->packet_id); + set_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status); + + err = brcmf_fil_bsscfg_data_set(vif->ifp, "mgmt_frame", + mf_params, mf_params_len); + if (err) { + bphy_err(drvr, "Failed to send Auth frame: err=%d\n", + err); + goto tx_status; + } + + ready = wait_for_completion_timeout(&vif->mgmt_tx, + MGMT_AUTH_FRAME_WAIT_TIME); + if (test_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status)) { + brcmf_dbg(TRACE, "TX Auth frame operation is success\n"); + ack = true; + } else { + bphy_err(drvr, "TX Auth frame operation is %s: status=%ld)\n", + ready ? "failed" : "timedout", vif->mgmt_tx_status); + } + +tx_status: + cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, + GFP_KERNEL); + kfree(mf_params); + return err; +} + +static int +brcmf_cyw_external_auth(struct wiphy *wiphy, struct net_device *dev, + struct cfg80211_external_auth_params *params) +{ + struct brcmf_if *ifp; + struct brcmf_pub *drvr; + struct brcmf_auth_req_status_le auth_status; + int ret = 0; + + brcmf_dbg(TRACE, "Enter\n"); + + ifp = netdev_priv(dev); + drvr = ifp->drvr; + if (params->status == WLAN_STATUS_SUCCESS) { + auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_SUCCESS); + } else { + bphy_err(drvr, "External authentication failed: status=%d\n", + params->status); + auth_status.flags = cpu_to_le16(BRCMF_EXTAUTH_FAIL); + } + + memcpy(auth_status.peer_mac, params->bssid, ETH_ALEN); + params->ssid.ssid_len = min_t(u8, params->ssid.ssid_len, + IEEE80211_MAX_SSID_LEN); + auth_status.ssid_len = cpu_to_le32(params->ssid.ssid_len); + memcpy(auth_status.ssid, params->ssid.ssid, params->ssid.ssid_len); + + ret = brcmf_fil_iovar_data_set(ifp, "auth_status", &auth_status, + sizeof(auth_status)); + if (ret < 0) + bphy_err(drvr, "auth_status iovar failed: ret=%d\n", ret); + + return ret; +} + +static void brcmf_cyw_get_cfg80211_ops(struct brcmf_pub *drvr) +{ + drvr->ops->mgmt_tx = brcmf_cyw_mgmt_tx; + drvr->ops->external_auth = brcmf_cyw_external_auth; +} + +static s32 +brcmf_cyw_notify_ext_auth_req(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct cfg80211_external_auth_params params; + struct brcmf_auth_req_status_le *auth_req = + (struct brcmf_auth_req_status_le *)data; + s32 err = 0; + + brcmf_dbg(INFO, "Enter: event %s (%d) received\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + + if (e->datalen < sizeof(*auth_req)) { + bphy_err(drvr, "Event %s (%d) data too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + + memset(¶ms, 0, sizeof(params)); + params.action = NL80211_EXTERNAL_AUTH_START; + params.key_mgmt_suite = WLAN_AKM_SUITE_SAE; + params.status = WLAN_STATUS_SUCCESS; + params.ssid.ssid_len = min_t(u32, 32, le32_to_cpu(auth_req->ssid_len)); + memcpy(params.ssid.ssid, auth_req->ssid, params.ssid.ssid_len); + memcpy(params.bssid, auth_req->peer_mac, ETH_ALEN); + + err = cfg80211_external_auth_request(ifp->ndev, ¶ms, GFP_KERNEL); + if (err) + bphy_err(drvr, "Ext Auth request to supplicant failed (%d)\n", + err); + + return err; +} + +static s32 +brcmf_notify_auth_frame_rx(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_pub *drvr = ifp->drvr; + struct brcmf_cfg80211_info *cfg = drvr->config; + struct wireless_dev *wdev; + u32 mgmt_frame_len = e->datalen - sizeof(struct brcmf_rx_mgmt_data); + struct brcmf_rx_mgmt_data *rxframe = (struct brcmf_rx_mgmt_data *)data; + u8 *frame = (u8 *)(rxframe + 1); + struct brcmu_chan ch; + struct ieee80211_mgmt *mgmt_frame; + s32 freq; + + brcmf_dbg(INFO, "Enter: event %s (%d) received\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + + if (e->datalen < sizeof(*rxframe)) { + bphy_err(drvr, "Event %s (%d) data too small. Ignore\n", + brcmf_fweh_event_name(e->event_code), e->event_code); + return -EINVAL; + } + + wdev = &ifp->vif->wdev; + WARN_ON(!wdev); + + ch.chspec = be16_to_cpu(rxframe->chanspec); + cfg->d11inf.decchspec(&ch); + + mgmt_frame = kzalloc(mgmt_frame_len, GFP_KERNEL); + if (!mgmt_frame) + return -ENOMEM; + + mgmt_frame->frame_control = cpu_to_le16(IEEE80211_STYPE_AUTH); + memcpy(mgmt_frame->da, ifp->mac_addr, ETH_ALEN); + memcpy(mgmt_frame->sa, e->addr, ETH_ALEN); + brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSSID, mgmt_frame->bssid, + ETH_ALEN); + frame += offsetof(struct ieee80211_mgmt, u); + memcpy(&mgmt_frame->u, frame, + mgmt_frame_len - offsetof(struct ieee80211_mgmt, u)); + + freq = ieee80211_channel_to_frequency(ch.control_ch_num, + ch.band == BRCMU_CHAN_BAND_2G ? + NL80211_BAND_2GHZ : + NL80211_BAND_5GHZ); + + cfg80211_rx_mgmt(wdev, freq, 0, (u8 *)mgmt_frame, mgmt_frame_len, + NL80211_RXMGMT_FLAG_EXTERNAL_AUTH); + kfree(mgmt_frame); + return 0; +} + +static s32 +brcmf_notify_mgmt_tx_status(struct brcmf_if *ifp, + const struct brcmf_event_msg *e, void *data) +{ + struct brcmf_cfg80211_vif *vif = ifp->vif; + u32 *packet_id = (u32 *)data; + + brcmf_dbg(INFO, "Enter: event %s (%d), status=%d\n", + brcmf_fweh_event_name(e->event_code), e->event_code, + e->status); + + if (!test_bit(BRCMF_MGMT_TX_SEND_FRAME, &vif->mgmt_tx_status) || + (*packet_id != vif->mgmt_tx_id)) + return 0; + + if (e->event_code == BRCMF_E_MGMT_FRAME_TXSTATUS) { + if (e->status == BRCMF_E_STATUS_SUCCESS) + set_bit(BRCMF_MGMT_TX_ACK, &vif->mgmt_tx_status); + else + set_bit(BRCMF_MGMT_TX_NOACK, &vif->mgmt_tx_status); + } else { + set_bit(BRCMF_MGMT_TX_OFF_CHAN_COMPLETED, &vif->mgmt_tx_status); + } + + complete(&vif->mgmt_tx); + return 0; +} + +static void brcmf_cyw_register_event_handlers(struct brcmf_pub *drvr) +{ + brcmf_fweh_register(drvr, BRCMF_E_EXT_AUTH_REQ, + brcmf_cyw_notify_ext_auth_req); + brcmf_fweh_register(drvr, BRCMF_E_EXT_AUTH_FRAME_RX, + brcmf_notify_auth_frame_rx); + brcmf_fweh_register(drvr, BRCMF_E_MGMT_FRAME_TXSTATUS, + brcmf_notify_mgmt_tx_status); + brcmf_fweh_register(drvr, BRCMF_E_MGMT_FRAME_OFFCHAN_DONE, + brcmf_notify_mgmt_tx_status); +} + const struct brcmf_fwvid_ops brcmf_cyw_ops = { .set_sae_password = brcmf_cyw_set_sae_pwd, .alloc_fweh_info = brcmf_cyw_alloc_fweh_info, + .activate_events = brcmf_cyw_activate_events, + .get_cfg80211_ops = brcmf_cyw_get_cfg80211_ops, + .register_event_handlers = brcmf_cyw_register_event_handlers, }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h new file mode 100644 index 000000000000..08c69142495a --- /dev/null +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cyw/fwil_types.h @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: ISC +/* + * Copyright (c) 2012 Broadcom Corporation + */ + +#ifndef CYW_FWIL_TYPES_H_ +#define CYW_FWIL_TYPES_H_ + +#include <fwil_types.h> + +enum brcmf_event_msgs_ext_command { + CYW_EVENTMSGS_NONE = 0, + CYW_EVENTMSGS_SET_BIT = 1, + CYW_EVENTMSGS_RESET_BIT = 2, + CYW_EVENTMSGS_SET_MASK = 3, +}; + +#define EVENTMSGS_VER 1 +#define EVENTMSGS_EXT_STRUCT_SIZE offsetof(struct eventmsgs_ext, mask[0]) + +/** + * struct brcmf_eventmsgs_ext - structure used with "eventmsgs_ext" iovar. + * + * @ver: version. + * @command: requested operation (see &enum event_msgs_ext_command). + * @len: length of the @mask array. + * @maxgetsize: indicates maximum mask size that may be returned by firmware + * upon iovar GET. + * @mask: array where each bit represents firmware event. + */ +struct brcmf_eventmsgs_ext { + u8 ver; + u8 command; + u8 len; + u8 maxgetsize; + u8 mask[] __counted_by(len); +}; + +#define BRCMF_EXTAUTH_START 1 +#define BRCMF_EXTAUTH_ABORT 2 +#define BRCMF_EXTAUTH_FAIL 3 +#define BRCMF_EXTAUTH_SUCCESS 4 + +/** + * struct brcmf_auth_req_status_le - external auth request and status update + * + * @flags: flags for external auth status + * @peer_mac: peer MAC address + * @ssid_len: length of ssid + * @ssid: ssid characters + * @pmkid: PMKSA identifier + */ +struct brcmf_auth_req_status_le { + __le16 flags; + u8 peer_mac[ETH_ALEN]; + __le32 ssid_len; + u8 ssid[IEEE80211_MAX_SSID_LEN]; + u8 pmkid[WLAN_PMKID_LEN]; +}; + +/** + * struct brcmf_mf_params_le - management frame parameters for mgmt_frame iovar + * + * @version: version of the iovar + * @dwell_time: dwell duration in ms + * @len: length of frame data + * @frame_control: frame control + * @channel: channel + * @da: peer MAC address + * @bssid: BSS network identifier + * @packet_id: packet identifier + * @data: frame data + */ +struct brcmf_mf_params_le { + __le32 version; + __le32 dwell_time; + __le16 len; + __le16 frame_control; + __le16 channel; + u8 da[ETH_ALEN]; + u8 bssid[ETH_ALEN]; + __le32 packet_id; + u8 data[] __counted_by(len); +}; + +#endif /* CYW_FWIL_TYPES_H_ */ + diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c index 0d9ae197fa1e..488364ef8ff2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c @@ -42,8 +42,9 @@ static const struct brcmf_feat_fwcap brcmf_fwcap_map[] = { { BRCMF_FEAT_MONITOR_FLAG, "rtap" }, { BRCMF_FEAT_MONITOR_FMT_RADIOTAP, "rtap" }, { BRCMF_FEAT_DOT11H, "802.11h" }, - { BRCMF_FEAT_SAE, "sae" }, + { BRCMF_FEAT_SAE, "sae " }, { BRCMF_FEAT_FWAUTH, "idauth" }, + { BRCMF_FEAT_SAE_EXT, "sae_ext" }, }; #ifdef DEBUG diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h index 7f4f0b3e4a7b..31f8695ca417 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h @@ -31,6 +31,7 @@ * FWAUTH: Firmware authenticator * DUMP_OBSS: Firmware has capable to dump obss info to support ACS * SCAN_V2: Version 2 scan params + * SAE_EXT: SAE authentication handled by user-space supplicant */ #define BRCMF_FEAT_LIST \ BRCMF_FEAT_DEF(MBSS) \ @@ -57,7 +58,8 @@ BRCMF_FEAT_DEF(DUMP_OBSS) \ BRCMF_FEAT_DEF(SCAN_V2) \ BRCMF_FEAT_DEF(PMKID_V2) \ - BRCMF_FEAT_DEF(PMKID_V3) + BRCMF_FEAT_DEF(PMKID_V3) \ + BRCMF_FEAT_DEF(SAE_EXT) /* * Quirks: diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c index f0b6a7607f16..c2d98ee6652f 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.c @@ -75,6 +75,7 @@ const char *brcmf_fweh_event_name(enum brcmf_fweh_event_code code) return "nodebug"; } #endif +BRCMF_EXPORT_SYMBOL_GPL(brcmf_fweh_event_name); /** * brcmf_fweh_queue_event() - create and queue event. @@ -405,6 +406,7 @@ int brcmf_fweh_register(struct brcmf_pub *drvr, enum brcmf_fweh_event_code code, brcmf_fweh_event_name(code)); return 0; } +BRCMF_EXPORT_SYMBOL_GPL(brcmf_fweh_register); /** * brcmf_fweh_unregister() - remove handler for given code. @@ -448,11 +450,14 @@ int brcmf_fweh_activate_events(struct brcmf_if *ifp) brcmf_dbg(EVENT, "enable event IF\n"); setbit(fweh->event_mask, BRCMF_E_IF); + /* allow per-vendor method to activate firmware events */ + if (!brcmf_fwvid_activate_events(ifp)) + return 0; + err = brcmf_fil_iovar_data_set(ifp, "event_msgs", fweh->event_mask, fweh->event_mask_len); if (err) bphy_err(fweh->drvr, "Set event_msgs error (%d)\n", err); - return err; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h index eed439b84010..e327dd58d29c 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fweh.h @@ -94,7 +94,11 @@ struct brcmf_cfg80211_info; BRCMF_ENUM_DEF(FIFO_CREDIT_MAP, 74) \ BRCMF_ENUM_DEF(ACTION_FRAME_RX, 75) \ BRCMF_ENUM_DEF(TDLS_PEER_EVENT, 92) \ - BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) + BRCMF_ENUM_DEF(BCMC_CREDIT_SUPPORT, 127) \ + BRCMF_ABSTRACT_ENUM_DEF(EXT_AUTH_REQ, 0) \ + BRCMF_ABSTRACT_ENUM_DEF(EXT_AUTH_FRAME_RX, 1) \ + BRCMF_ABSTRACT_ENUM_DEF(MGMT_FRAME_TXSTATUS, 2) \ + BRCMF_ABSTRACT_ENUM_DEF(MGMT_FRAME_OFFCHAN_DONE, 3) #define BRCMF_ENUM_DEF(id, val) \ BRCMF_E_##id = (val), @@ -337,7 +341,7 @@ struct brcmf_fweh_info { struct list_head event_q; uint event_mask_len; u8 *event_mask; - struct brcmf_fweh_event_map *event_map; + const struct brcmf_fweh_event_map *event_map; uint num_event_codes; brcmf_fweh_handler_t evt_handler[] __counted_by(num_event_codes); }; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h index e6ac9fc341bc..f3e011d090f2 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwvid.h @@ -15,6 +15,9 @@ struct brcmf_fwvid_ops { void (*feat_attach)(struct brcmf_if *ifp); int (*set_sae_password)(struct brcmf_if *ifp, struct cfg80211_crypto_settings *crypto); int (*alloc_fweh_info)(struct brcmf_pub *drvr); + int (*activate_events)(struct brcmf_if *ifp); + void (*get_cfg80211_ops)(struct brcmf_pub *drvr); + void (*register_event_handlers)(struct brcmf_pub *drvr); }; /* exported functions */ @@ -56,4 +59,30 @@ static inline int brcmf_fwvid_alloc_fweh_info(struct brcmf_pub *drvr) return drvr->vops->alloc_fweh_info(drvr); } +static inline int brcmf_fwvid_activate_events(struct brcmf_if *ifp) +{ + const struct brcmf_fwvid_ops *vops = ifp->drvr->vops; + + if (!vops || !vops->activate_events) + return -EOPNOTSUPP; + + return vops->activate_events(ifp); +} + +static inline void brcmf_fwvid_get_cfg80211_ops(struct brcmf_pub *drvr) +{ + if (!drvr->vops || !drvr->vops->get_cfg80211_ops) + return; + + drvr->vops->get_cfg80211_ops(drvr); +} + +static inline void brcmf_fwvid_register_event_handlers(struct brcmf_pub *drvr) +{ + if (!drvr->vops || !drvr->vops->register_event_handlers) + return; + + drvr->vops->register_event_handlers(drvr); +} + #endif /* FWVID_H_ */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c index d2caa80e9412..9f1854b3d1a5 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/pcie.c @@ -2304,7 +2304,7 @@ brcmf_pcie_fwcon_timer(struct brcmf_pciedev_info *devinfo, bool active) { if (!active) { if (devinfo->console_active) { - del_timer_sync(&devinfo->timer); + timer_delete_sync(&devinfo->timer); devinfo->console_active = false; } return; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c index b1727f35217b..93727b9a5f0d 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/sdio.c @@ -4611,7 +4611,7 @@ void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, bool active) { /* Totally stop the timer */ if (!active && bus->wd_active) { - del_timer_sync(&bus->timer); + timer_delete_sync(&bus->timer); bus->wd_active = false; return; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c index 2821c27f317e..b056336d5da6 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/usb.c @@ -741,15 +741,19 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, void *buffer, int buflen) { int ret; - char *tmpbuf; + char *tmpbuf = NULL; u16 size; - if ((!devinfo) || (devinfo->ctl_urb == NULL)) - return -EINVAL; + if (!devinfo || !devinfo->ctl_urb) { + ret = -EINVAL; + goto err; + } tmpbuf = kmalloc(buflen, GFP_ATOMIC); - if (!tmpbuf) - return -ENOMEM; + if (!tmpbuf) { + ret = -ENOMEM; + goto err; + } size = buflen; devinfo->ctl_urb->transfer_buffer_length = size; @@ -770,18 +774,23 @@ static int brcmf_usb_dl_cmd(struct brcmf_usbdev_info *devinfo, u8 cmd, ret = usb_submit_urb(devinfo->ctl_urb, GFP_ATOMIC); if (ret < 0) { brcmf_err("usb_submit_urb failed %d\n", ret); - goto finalize; + goto err; } if (!brcmf_usb_ioctl_resp_wait(devinfo)) { usb_kill_urb(devinfo->ctl_urb); ret = -ETIMEDOUT; + goto err; } else { memcpy(buffer, tmpbuf, buflen); } -finalize: kfree(tmpbuf); + return 0; + +err: + kfree(tmpbuf); + brcmf_err("dl cmd %u failed: err=%d\n", cmd, ret); return ret; } @@ -896,14 +905,16 @@ brcmf_usb_dl_writeimage(struct brcmf_usbdev_info *devinfo, u8 *fw, int fwlen) } /* 1) Prepare USB boot loader for runtime image */ - brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state)); + err = brcmf_usb_dl_cmd(devinfo, DL_START, &state, sizeof(state)); + if (err) + goto fail; rdlstate = le32_to_cpu(state.state); rdlbytes = le32_to_cpu(state.bytes); /* 2) Check we are in the Waiting state */ if (rdlstate != DL_WAITING) { - brcmf_err("Failed to DL_START\n"); + brcmf_err("Invalid DL state: %u\n", rdlstate); err = -EINVAL; goto fail; } diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c index 50d817485cf9..0cb64fc56783 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.c @@ -198,7 +198,7 @@ #define CST4313_SPROM_OTP_SEL_SHIFT 0 /* 4313 Chip specific ChipControl register bits */ - /* 12 mA drive strengh for later 4313 */ + /* 12 mA drive strength for later 4313 */ #define CCTRL_4313_12MA_LED_DRIVE 0x00000007 /* Manufacturer Ids */ @@ -453,7 +453,7 @@ ai_buscore_setup(struct si_info *sii, struct bcma_device *cc) /* get chipcommon chipstatus */ sii->chipst = bcma_read32(cc, CHIPCREGOFFS(chipstatus)); - /* get chipcommon capabilites */ + /* get chipcommon capabilities */ sii->pub.cccaps = bcma_read32(cc, CHIPCREGOFFS(capabilities)); /* get pmu rev and caps */ @@ -657,7 +657,7 @@ u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih) } /* - * clock control policy function throught chipcommon + * clock control policy function through chipcommon * * set dynamic clk control mode (forceslow, forcefast, dynamic) * returns true if we are forcing fast clock diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h index 90b6e3982d2c..e791dd07ca78 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/aiutils.h @@ -76,7 +76,7 @@ * conventions for the use the flash space: */ -/* Minumum amount of flash we support */ +/* Minimum amount of flash we support */ #define FLASH_MIN 0x00020000 /* Minimum flash size */ #define CC_SROM_OTP 0x800 /* SROM/OTP address space */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c index a767cbb79185..e1d707a7c964 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/ampdu.c @@ -479,7 +479,7 @@ void brcms_c_ampdu_reset_session(struct brcms_ampdu_session *session, /* * Preps the given packet for AMPDU based on the session data. If the - * frame cannot be accomodated in the current session, -ENOSPC is + * frame cannot be accommodated in the current session, -ENOSPC is * returned. */ int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, @@ -529,7 +529,7 @@ int brcms_c_ampdu_add_frame(struct brcms_ampdu_session *session, } /* - * Now that we're sure this frame can be accomodated, update the + * Now that we're sure this frame can be accommodated, update the * session information. */ session->ampdu_len += len; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c index d1b9a18d0374..3878c4124e25 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/channel.c @@ -445,7 +445,7 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, /* * OFDM 40 MHz SISO has the same power as the corresponding - * MCS0-7 rate unless overriden by the locale specific code. + * MCS0-7 rate unless overridden by the locale specific code. * We set this value to 0 as a flag (presumably 0 dBm isn't * a possibility) and then copy the MCS0-7 value to the 40 MHz * value if it wasn't explicitly set. @@ -479,7 +479,7 @@ brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, u16 chanspec, /* * 20 MHz has the same power as the corresponding OFDM rate - * unless overriden by the locale specific code. + * unless overridden by the locale specific code. */ txpwr->mcs_20_siso[i] = txpwr->ofdm[i]; txpwr->mcs_40_siso[i] = 0; diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c index 80c35027787a..c739bf7463b3 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/dma.c @@ -1348,7 +1348,7 @@ static void prep_ampdu_frame(struct dma_info *di, struct sk_buff *p) ret = brcms_c_ampdu_add_frame(session, p); if (ret == -ENOSPC) { /* - * AMPDU cannot accomodate this frame. Close out the in- + * AMPDU cannot accommodate this frame. Close out the in- * progress AMPDU session and start a new one. */ ampdu_finalize(di); diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c index 5b1d35601bbd..1c3d29dca424 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/mac80211_if.c @@ -48,9 +48,9 @@ FIF_BCN_PRBRESP_PROMISC | \ FIF_PSPOLL) -#define CHAN2GHZ(channel, freqency, chflags) { \ +#define CHAN2GHZ(channel, frequency, chflags) { \ .band = NL80211_BAND_2GHZ, \ - .center_freq = (freqency), \ + .center_freq = (frequency), \ .hw_value = (channel), \ .flags = chflags, \ .max_antenna_gain = 0, \ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c index 2738d4d6c60a..c1a9c1e442ee 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.c @@ -921,7 +921,7 @@ brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) * The "fallback limit" is the number of tx attempts a given * MPDU is sent at the "primary" rate. Tx attempts beyond that * limit are sent at the "secondary" rate. - * A 'short frame' does not exceed RTS treshold. + * A 'short frame' does not exceed RTS threshold. */ u16 sfbl, /* Short Frame Rate Fallback Limit */ lfbl, /* Long Frame Rate Fallback Limit */ @@ -6259,7 +6259,7 @@ brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, } /* - * Currently only support same setting for primay and + * Currently only support same setting for primary and * fallback rates. Unify flags for each rate into a * single value for the frame */ diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h index 9f76b880814e..b7ca0d9891c4 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/main.h @@ -603,7 +603,7 @@ enum brcms_bss_type { * cur_etheraddr: h/w address * flags: BSSCFG flags; see below * - * current_bss: BSS parms in ASSOCIATED state + * current_bss: BSS parameters in ASSOCIATED state * * * ID: 'unique' ID of this bsscfg, assigned at bsscfg allocation diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c index 71b80381f3ad..c9a29e626daa 100644 --- a/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c +++ b/drivers/net/wireless/broadcom/brcm80211/brcmsmac/pmu.c @@ -31,7 +31,7 @@ #define EXT_ILP_HZ 32768 /* - * Duration for ILP clock frequency measurment in milliseconds + * Duration for ILP clock frequency measurement in milliseconds * * remark: 1000 must be an integer multiple of this duration */ diff --git a/drivers/net/wireless/intel/ipw2x00/ipw2200.c b/drivers/net/wireless/intel/ipw2x00/ipw2200.c index be1d971b3d32..24a5624ef207 100644 --- a/drivers/net/wireless/intel/ipw2x00/ipw2200.c +++ b/drivers/net/wireless/intel/ipw2x00/ipw2200.c @@ -3295,7 +3295,7 @@ static int ipw_init_nic(struct ipw_priv *priv) rc = ipw_poll_bit(priv, IPW_GP_CNTRL_RW, IPW_GP_CNTRL_BIT_CLOCK_READY, 250); if (rc < 0) - IPW_DEBUG_INFO("FAILED wait for clock stablization\n"); + IPW_DEBUG_INFO("FAILED wait for clock stabilization\n"); /* assert SW reset */ ipw_set_bit(priv, IPW_RESET_REG, IPW_RESET_REG_SW_RESET); diff --git a/drivers/net/wireless/intel/ipw2x00/libipw.h b/drivers/net/wireless/intel/ipw2x00/libipw.h index 3c20353e5a41..e031e8692ca6 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw.h +++ b/drivers/net/wireless/intel/ipw2x00/libipw.h @@ -1011,8 +1011,6 @@ netdev_tx_t libipw_xmit(struct sk_buff *skb, struct net_device *dev); void libipw_txb_free(struct libipw_txb *); /* libipw_rx.c */ -void libipw_rx_any(struct libipw_device *ieee, struct sk_buff *skb, - struct libipw_rx_stats *stats); int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, struct libipw_rx_stats *rx_stats); /* make sure to set stats->len */ diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_crypto.c b/drivers/net/wireless/intel/ipw2x00/libipw_crypto.c index 32639e0e8430..dfcc12aa8620 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw_crypto.c +++ b/drivers/net/wireless/intel/ipw2x00/libipw_crypto.c @@ -59,7 +59,7 @@ void libipw_crypt_info_free(struct libipw_crypt_info *info) int i; libipw_crypt_quiescing(info); - del_timer_sync(&info->crypt_deinit_timer); + timer_delete_sync(&info->crypt_deinit_timer); libipw_crypt_deinit_entries(info, 1); for (i = 0; i < NUM_WEP_KEYS; i++) { diff --git a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c index dc4e91f58bb4..b7bc94f7abd8 100644 --- a/drivers/net/wireless/intel/ipw2x00/libipw_rx.c +++ b/drivers/net/wireless/intel/ipw2x00/libipw_rx.c @@ -823,96 +823,6 @@ int libipw_rx(struct libipw_device *ieee, struct sk_buff *skb, return 0; } -/* Filter out unrelated packets, call libipw_rx[_mgt] - * This function takes over the skb, it should not be used again after calling - * this function. */ -void libipw_rx_any(struct libipw_device *ieee, - struct sk_buff *skb, struct libipw_rx_stats *stats) -{ - struct libipw_hdr_4addr *hdr; - int is_packet_for_us; - u16 fc; - - if (ieee->iw_mode == IW_MODE_MONITOR) { - if (!libipw_rx(ieee, skb, stats)) - dev_kfree_skb_irq(skb); - return; - } - - if (skb->len < sizeof(struct ieee80211_hdr)) - goto drop_free; - - hdr = (struct libipw_hdr_4addr *)skb->data; - fc = le16_to_cpu(hdr->frame_ctl); - - if ((fc & IEEE80211_FCTL_VERS) != 0) - goto drop_free; - - switch (fc & IEEE80211_FCTL_FTYPE) { - case IEEE80211_FTYPE_MGMT: - if (skb->len < sizeof(struct libipw_hdr_3addr)) - goto drop_free; - libipw_rx_mgt(ieee, hdr, stats); - dev_kfree_skb_irq(skb); - return; - case IEEE80211_FTYPE_DATA: - break; - case IEEE80211_FTYPE_CTL: - return; - default: - return; - } - - is_packet_for_us = 0; - switch (ieee->iw_mode) { - case IW_MODE_ADHOC: - /* our BSS and not from/to DS */ - if (ether_addr_equal(hdr->addr3, ieee->bssid) && - ((fc & (IEEE80211_FCTL_TODS + IEEE80211_FCTL_FROMDS)) == 0)) { - /* promisc: get all */ - if (ieee->dev->flags & IFF_PROMISC) - is_packet_for_us = 1; - /* to us */ - else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) - is_packet_for_us = 1; - /* mcast */ - else if (is_multicast_ether_addr(hdr->addr1)) - is_packet_for_us = 1; - } - break; - case IW_MODE_INFRA: - /* our BSS (== from our AP) and from DS */ - if (ether_addr_equal(hdr->addr2, ieee->bssid) && - ((fc & (IEEE80211_FCTL_TODS + IEEE80211_FCTL_FROMDS)) == IEEE80211_FCTL_FROMDS)) { - /* promisc: get all */ - if (ieee->dev->flags & IFF_PROMISC) - is_packet_for_us = 1; - /* to us */ - else if (ether_addr_equal(hdr->addr1, ieee->dev->dev_addr)) - is_packet_for_us = 1; - /* mcast */ - else if (is_multicast_ether_addr(hdr->addr1)) { - /* not our own packet bcasted from AP */ - if (!ether_addr_equal(hdr->addr3, ieee->dev->dev_addr)) - is_packet_for_us = 1; - } - } - break; - default: - /* ? */ - break; - } - - if (is_packet_for_us) - if (!libipw_rx(ieee, skb, stats)) - dev_kfree_skb_irq(skb); - return; - -drop_free: - dev_kfree_skb_irq(skb); - ieee->dev->stats.rx_dropped++; -} - #define MGMT_FRAME_FIXED_PART_LENGTH 0x24 static u8 qos_oui[QOS_OUI_LEN] = { 0x00, 0x50, 0xF2 }; @@ -1729,6 +1639,5 @@ void libipw_rx_mgt(struct libipw_device *ieee, } } -EXPORT_SYMBOL_GPL(libipw_rx_any); EXPORT_SYMBOL(libipw_rx_mgt); EXPORT_SYMBOL(libipw_rx); diff --git a/drivers/net/wireless/intel/iwlegacy/3945-mac.c b/drivers/net/wireless/intel/iwlegacy/3945-mac.c index 4013443698a2..104748fcdc33 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-mac.c @@ -2188,7 +2188,7 @@ __il3945_down(struct il_priv *il) /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set * to prevent rearm timer */ - del_timer_sync(&il->watchdog); + timer_delete_sync(&il->watchdog); /* Station information will now be cleared in device */ il_clear_ucode_stations(il); diff --git a/drivers/net/wireless/intel/iwlegacy/3945-rs.c b/drivers/net/wireless/intel/iwlegacy/3945-rs.c index 0eaad980c85c..df1b8ec86651 100644 --- a/drivers/net/wireless/intel/iwlegacy/3945-rs.c +++ b/drivers/net/wireless/intel/iwlegacy/3945-rs.c @@ -413,7 +413,7 @@ il3945_rs_free_sta(void *il_priv, struct ieee80211_sta *sta, void *il_sta) * to use il_priv to print out debugging) since it may not be fully * initialized at this point. */ - del_timer_sync(&rs_sta->rate_scale_flush); + timer_delete_sync(&rs_sta->rate_scale_flush); } /* diff --git a/drivers/net/wireless/intel/iwlegacy/4965-mac.c b/drivers/net/wireless/intel/iwlegacy/4965-mac.c index 05c4af41bdb9..dc8c408902e6 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-mac.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-mac.c @@ -5350,7 +5350,7 @@ __il4965_down(struct il_priv *il) /* Stop TX queues watchdog. We need to have S_EXIT_PENDING bit set * to prevent rearm timer */ - del_timer_sync(&il->watchdog); + timer_delete_sync(&il->watchdog); il_clear_ucode_stations(il); @@ -6243,7 +6243,7 @@ il4965_cancel_deferred_work(struct il_priv *il) il_cancel_scan_deferred_work(il); - del_timer_sync(&il->stats_periodic); + timer_delete_sync(&il->stats_periodic); } static void diff --git a/drivers/net/wireless/intel/iwlegacy/4965-rs.c b/drivers/net/wireless/intel/iwlegacy/4965-rs.c index 718efb1aa1b0..0e5130d1fccd 100644 --- a/drivers/net/wireless/intel/iwlegacy/4965-rs.c +++ b/drivers/net/wireless/intel/iwlegacy/4965-rs.c @@ -132,15 +132,8 @@ static void il4965_rs_fill_link_cmd(struct il_priv *il, static void il4965_rs_stay_in_table(struct il_lq_sta *lq_sta, bool force_search); -#ifdef CONFIG_MAC80211_DEBUGFS static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 *rate_n_flags, int idx); -#else -static void -il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) -{ -} -#endif /* * The following tables contain the expected throughput metrics for all rates @@ -2495,8 +2488,6 @@ il4965_rs_free_sta(void *il_r, struct ieee80211_sta *sta, void *il_sta) D_RATE("leave\n"); } -#ifdef CONFIG_MAC80211_DEBUGFS - static void il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) { @@ -2504,6 +2495,9 @@ il4965_rs_dbgfs_set_mcs(struct il_lq_sta *lq_sta, u32 * rate_n_flags, int idx) u8 valid_tx_ant; u8 ant_sel_tx; + if (!IS_ENABLED(CONFIG_MAC80211_DEBUGFS)) + return; + il = lq_sta->drv; valid_tx_ant = il->hw_params.valid_tx_ant; if (lq_sta->dbg_fixed_rate) { @@ -2758,7 +2752,6 @@ il4965_rs_add_debugfs(void *il, void *il_sta, struct dentry *dir) debugfs_create_u8("tx_agg_tid_enable", 0600, dir, &lq_sta->tx_agg_tid_en); } -#endif /* * Initialization of rate scaling information is done by driver after @@ -2781,9 +2774,8 @@ static const struct rate_control_ops rs_4965_ops = { .free = il4965_rs_free, .alloc_sta = il4965_rs_alloc_sta, .free_sta = il4965_rs_free_sta, -#ifdef CONFIG_MAC80211_DEBUGFS - .add_sta_debugfs = il4965_rs_add_debugfs, -#endif + .add_sta_debugfs = PTR_IF(IS_ENABLED(CONFIG_MAC80211_DEBUGFS), + il4965_rs_add_debugfs), }; int diff --git a/drivers/net/wireless/intel/iwlegacy/common.c b/drivers/net/wireless/intel/iwlegacy/common.c index af4f42534ea0..09fb4b758704 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.c +++ b/drivers/net/wireless/intel/iwlegacy/common.c @@ -4842,7 +4842,7 @@ il_setup_watchdog(struct il_priv *il) mod_timer(&il->watchdog, jiffies + msecs_to_jiffies(IL_WD_TICK(timeout))); else - del_timer(&il->watchdog); + timer_delete(&il->watchdog); } EXPORT_SYMBOL(il_setup_watchdog); diff --git a/drivers/net/wireless/intel/iwlegacy/common.h b/drivers/net/wireless/intel/iwlegacy/common.h index 92285412ab10..52610f5e57a3 100644 --- a/drivers/net/wireless/intel/iwlegacy/common.h +++ b/drivers/net/wireless/intel/iwlegacy/common.h @@ -2815,9 +2815,7 @@ struct il_lq_sta { struct il_scale_tbl_info lq_info[LQ_SIZE]; /* "active", "search" */ struct il_traffic_load load[TID_MAX_LOAD_COUNT]; u8 tx_agg_tid_en; -#ifdef CONFIG_MAC80211_DEBUGFS u32 dbg_fixed_rate; -#endif struct il_priv *drv; /* used to be in sta_info */ diff --git a/drivers/net/wireless/intel/iwlwifi/Kconfig b/drivers/net/wireless/intel/iwlwifi/Kconfig index 4b04865fc2c9..82f577da1a8b 100644 --- a/drivers/net/wireless/intel/iwlwifi/Kconfig +++ b/drivers/net/wireless/intel/iwlwifi/Kconfig @@ -81,14 +81,25 @@ config IWLMVM of the devices that use this firmware is available here: https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware +config IWLMLD + tristate "Intel Wireless WiFi MLD Firmware support" + select WANT_DEV_COREDUMP + depends on MAC80211 + depends on PTP_1588_CLOCK_OPTIONAL + help + This is the driver that supports firmwares of MLD capable devices. + The list of the devices that use this firmware is available here: + https://wireless.wiki.kernel.org/en/users/drivers/iwlwifi#firmware + # don't call it _MODULE -- will confuse Kconfig/fixdep/... config IWLWIFI_OPMODE_MODULAR bool default y if IWLDVM=m default y if IWLMVM=m + default y if IWLMLD=m -comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM" - depends on IWLDVM=n && IWLMVM=n +comment "WARNING: iwlwifi is useless without IWLDVM or IWLMVM or IWLMLD" + depends on IWLDVM=n && IWLMVM=n && IWLMLD=n menu "Debugging Options" diff --git a/drivers/net/wireless/intel/iwlwifi/Makefile b/drivers/net/wireless/intel/iwlwifi/Makefile index 19c4ce6f2465..3f476e333726 100644 --- a/drivers/net/wireless/intel/iwlwifi/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/Makefile @@ -8,11 +8,23 @@ iwlwifi-objs += iwl-nvm-utils.o iwlwifi-objs += iwl-utils.o iwlwifi-objs += iwl-phy-db.o iwl-nvm-parse.o iwlwifi-objs += pcie/drv.o pcie/rx.o pcie/tx.o pcie/trans.o -iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-gen3.o +iwlwifi-objs += pcie/ctxt-info.o pcie/ctxt-info-v2.o iwlwifi-objs += pcie/trans-gen2.o pcie/tx-gen2.o -iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o cfg/5000.o cfg/6000.o -iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o cfg/9000.o cfg/22000.o -iwlwifi-$(CONFIG_IWLMVM) += cfg/ax210.o cfg/bz.o cfg/sc.o cfg/dr.o + +CFLAGS_pcie/drv.o += -Wno-override-init + +# combined MAC/RF configurations +iwlwifi-$(CONFIG_IWLDVM) += cfg/1000.o cfg/2000.o +iwlwifi-$(CONFIG_IWLDVM) += cfg/5000.o cfg/6000.o +iwlwifi-$(CONFIG_IWLMVM) += cfg/7000.o cfg/8000.o +# MAC configurations +iwlwifi-$(CONFIG_IWLMVM) += cfg/9000.o cfg/22000.o +iwlwifi-$(CONFIG_IWLMVM) += cfg/ax210.o +iwlwifi-$(CONFIG_IWLMLD) += cfg/bz.o cfg/sc.o cfg/dr.o +# RF configurations +iwlwifi-$(CONFIG_IWLMVM) += cfg/rf-jf.o cfg/rf-hr.o cfg/rf-gf.o +iwlwifi-$(CONFIG_IWLMLD) += cfg/rf-fm.o cfg/rf-wh.o cfg/rf-pe.o + iwlwifi-objs += iwl-dbg-tlv.o iwlwifi-objs += iwl-trans.o @@ -20,6 +32,7 @@ iwlwifi-objs += fw/img.o fw/notif-wait.o fw/rs.o iwlwifi-objs += fw/dbg.o fw/pnvm.o fw/dump.o iwlwifi-objs += fw/regulatory.o iwlwifi-$(CONFIG_IWLMVM) += fw/paging.o fw/smem.o fw/init.o +iwlwifi-$(CONFIG_IWLMLD) += fw/smem.o fw/init.o iwlwifi-$(CONFIG_ACPI) += fw/acpi.o iwlwifi-$(CONFIG_EFI) += fw/uefi.o iwlwifi-$(CONFIG_IWLWIFI_DEBUGFS) += fw/debugfs.o @@ -33,6 +46,7 @@ ccflags-y += -I$(src) obj-$(CONFIG_IWLDVM) += dvm/ obj-$(CONFIG_IWLMVM) += mvm/ obj-$(CONFIG_IWLMEI) += mei/ +obj-$(CONFIG_IWLMLD) += mld/ obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += tests/ diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/1000.c b/drivers/net/wireless/intel/iwlwifi/cfg/1000.c index f172ffd2a841..c029e595e7c2 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/1000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/1000.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2018 - 2020, 2023 Intel Corporation + * Copyright(c) 2018 - 2020, 2023, 2025 Intel Corporation *****************************************************************************/ #include <linux/module.h> @@ -29,7 +29,7 @@ #define IWL100_MODULE_FIRMWARE(api) IWL100_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl1000_base_params = { +static const struct iwl_family_base_params iwl1000_base = { .num_of_queues = IWLAGN_NUM_QUEUES, .max_tfd_queue_size = 256, .eeprom_size = OTP_LOW_IMAGE_SIZE_2K, @@ -42,12 +42,6 @@ static const struct iwl_base_params iwl1000_base_params = { .scd_chain_ext_wa = true, }; -static const struct iwl_ht_params iwl1000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ - .ht40_bands = BIT(NL80211_BAND_2GHZ), -}; - static const struct iwl_eeprom_params iwl1000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -60,54 +54,67 @@ static const struct iwl_eeprom_params iwl1000_eeprom_params = { } }; +const struct iwl_mac_cfg iwl1000_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_1000, + .base = &iwl1000_base, +}; + #define IWL_DEVICE_1000 \ .fw_name_pre = IWL1000_FW_PRE, \ .ucode_api_max = IWL1000_UCODE_API_MAX, \ .ucode_api_min = IWL1000_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_1000, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_1000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ - .trans.base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_BLINK -const struct iwl_cfg iwl1000_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1000 BGN", +const struct iwl_rf_cfg iwl1000_bgn_cfg = { IWL_DEVICE_1000, - .ht_params = &iwl1000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ), + }, }; -const struct iwl_cfg iwl1000_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1000 BG", +const char iwl1000_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 1000 BGN"; + +const struct iwl_rf_cfg iwl1000_bg_cfg = { IWL_DEVICE_1000, }; +const char iwl1000_bg_name[] = "Intel(R) Centrino(R) Wireless-N 1000 BG"; + #define IWL_DEVICE_100 \ .fw_name_pre = IWL100_FW_PRE, \ .ucode_api_max = IWL100_UCODE_API_MAX, \ .ucode_api_min = IWL100_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_100, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_1000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_1000_TX_POWER_VERSION, \ - .trans.base_params = &iwl1000_base_params, \ .eeprom_params = &iwl1000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ .rx_with_siso_diversity = true -const struct iwl_cfg iwl100_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 100 BGN", +const struct iwl_rf_cfg iwl100_bgn_cfg = { IWL_DEVICE_100, - .ht_params = &iwl1000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ), + }, }; -const struct iwl_cfg iwl100_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 100 BG", +const char iwl100_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 100 BGN"; + +const struct iwl_rf_cfg iwl100_bg_cfg = { IWL_DEVICE_100, }; +const char iwl100_bg_name[] = "Intel(R) Centrino(R) Wireless-N 100 BG"; + MODULE_FIRMWARE(IWL1000_MODULE_FIRMWARE(IWL1000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL100_MODULE_FIRMWARE(IWL100_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/2000.c b/drivers/net/wireless/intel/iwlwifi/cfg/2000.c index 6f3f26da0ad5..47554a5f406e 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/2000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/2000.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2018 - 2020, 2023 Intel Corporation + * Copyright(c) 2018 - 2020, 2023, 2025 Intel Corporation *****************************************************************************/ #include <linux/module.h> @@ -40,7 +40,7 @@ #define IWL135_FW_PRE "iwlwifi-135" #define IWL135_MODULE_FIRMWARE(api) IWL135_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl2000_base_params = { +static const struct iwl_family_base_params iwl2000_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_2K, .num_of_queues = IWLAGN_NUM_QUEUES, .max_tfd_queue_size = 256, @@ -54,7 +54,7 @@ static const struct iwl_base_params iwl2000_base_params = { }; -static const struct iwl_base_params iwl2030_base_params = { +static const struct iwl_family_base_params iwl2030_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_2K, .num_of_queues = IWLAGN_NUM_QUEUES, .max_tfd_queue_size = 256, @@ -67,12 +67,6 @@ static const struct iwl_base_params iwl2030_base_params = { .scd_chain_ext_wa = true, }; -static const struct iwl_ht_params iwl2000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ - .ht40_bands = BIT(NL80211_BAND_2GHZ), -}; - static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -86,97 +80,119 @@ static const struct iwl_eeprom_params iwl20x0_eeprom_params = { .enhanced_txpower = true, }; +const struct iwl_mac_cfg iwl2000_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_2000, + .base = &iwl2000_base, +}; + #define IWL_DEVICE_2000 \ .fw_name_pre = IWL2000_FW_PRE, \ .ucode_api_max = IWL2000_UCODE_API_MAX, \ .ucode_api_min = IWL2000_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_2000, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .trans.base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE -const struct iwl_cfg iwl2000_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2200 BGN", +const struct iwl_rf_cfg iwl2000_2bgn_cfg = { IWL_DEVICE_2000, - .ht_params = &iwl2000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ), + }, }; -const struct iwl_cfg iwl2000_2bgn_d_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2200D BGN", - IWL_DEVICE_2000, - .ht_params = &iwl2000_ht_params, +const char iwl2000_2bgn_name[] = "Intel(R) Centrino(R) Wireless-N 2200 BGN"; +const char iwl2000_2bgn_d_name[] = "Intel(R) Centrino(R) Wireless-N 2200D BGN"; + +const struct iwl_mac_cfg iwl2030_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_2030, + .base = &iwl2030_base, }; #define IWL_DEVICE_2030 \ .fw_name_pre = IWL2030_FW_PRE, \ .ucode_api_max = IWL2030_UCODE_API_MAX, \ .ucode_api_min = IWL2030_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_2030, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .trans.base_params = &iwl2030_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE -const struct iwl_cfg iwl2030_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 2230 BGN", +const struct iwl_rf_cfg iwl2030_2bgn_cfg = { IWL_DEVICE_2030, - .ht_params = &iwl2000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ), + }, +}; + +const char iwl2030_2bgn_name[] = "Intel(R) Centrino(R) Wireless-N 2230 BGN"; + +const struct iwl_mac_cfg iwl105_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_105, + .base = &iwl2000_base, }; #define IWL_DEVICE_105 \ .fw_name_pre = IWL105_FW_PRE, \ .ucode_api_max = IWL105_UCODE_API_MAX, \ .ucode_api_min = IWL105_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_105, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .trans.base_params = &iwl2000_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ .rx_with_siso_diversity = true -const struct iwl_cfg iwl105_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 105 BGN", +const struct iwl_rf_cfg iwl105_bgn_cfg = { IWL_DEVICE_105, - .ht_params = &iwl2000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ), + }, }; -const struct iwl_cfg iwl105_bgn_d_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 105D BGN", - IWL_DEVICE_105, - .ht_params = &iwl2000_ht_params, +const char iwl105_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 105 BGN"; +const char iwl105_bgn_d_name[] = "Intel(R) Centrino(R) Wireless-N 105D BGN"; + +const struct iwl_mac_cfg iwl135_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_135, + .base = &iwl2030_base, }; #define IWL_DEVICE_135 \ .fw_name_pre = IWL135_FW_PRE, \ .ucode_api_max = IWL135_UCODE_API_MAX, \ .ucode_api_min = IWL135_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_135, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_2000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_2000_TX_POWER_VERSION, \ - .trans.base_params = &iwl2030_base_params, \ .eeprom_params = &iwl20x0_eeprom_params, \ .led_mode = IWL_LED_RF_STATE, \ .rx_with_siso_diversity = true -const struct iwl_cfg iwl135_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 135 BGN", +const struct iwl_rf_cfg iwl135_bgn_cfg = { IWL_DEVICE_135, - .ht_params = &iwl2000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ), + }, }; +const char iwl135_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 135 BGN"; + MODULE_FIRMWARE(IWL2000_MODULE_FIRMWARE(IWL2000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL2030_MODULE_FIRMWARE(IWL2030_UCODE_API_MAX)); MODULE_FIRMWARE(IWL105_MODULE_FIRMWARE(IWL105_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index 2e2fcb3807ef..52e0beebf9ce 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include <linux/module.h> #include <linux/stringify.h> @@ -15,14 +15,7 @@ /* Lowest firmware API version supported */ #define IWL_22000_UCODE_API_MIN 77 -/* NVM versions */ -#define IWL_22000_NVM_VERSION 0x0a1d - /* Memory offsets and lengths */ -#define IWL_22000_DCCM_OFFSET 0x800000 /* LMAC1 */ -#define IWL_22000_DCCM_LEN 0x10000 /* LMAC1 */ -#define IWL_22000_DCCM2_OFFSET 0x880000 -#define IWL_22000_DCCM2_LEN 0x8000 #define IWL_22000_SMEM_OFFSET 0x400000 #define IWL_22000_SMEM_LEN 0xD0000 @@ -44,11 +37,12 @@ IWL_QU_C_HR_B_FW_PRE "-" __stringify(api) ".ucode" #define IWL_QU_B_JF_B_MODULE_FIRMWARE(api) \ IWL_QU_B_JF_B_FW_PRE "-" __stringify(api) ".ucode" +#define IWL_QU_C_JF_B_MODULE_FIRMWARE(api) \ + IWL_QU_C_JF_B_FW_PRE "-" __stringify(api) ".ucode" #define IWL_CC_A_MODULE_FIRMWARE(api) \ IWL_CC_A_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl_22000_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, +static const struct iwl_family_base_params iwl_22000_base = { .num_of_queues = 512, .max_tfd_queue_size = 256, .shadow_ram_support = true, @@ -57,156 +51,78 @@ static const struct iwl_base_params iwl_22000_base_params = { .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, -}; - -const struct iwl_ht_params iwl_22000_ht_params = { - .stbc = true, - .ldpc = true, - .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ) | - BIT(NL80211_BAND_6GHZ), -}; - -#define IWL_DEVICE_22000_COMMON \ - .ucode_api_min = IWL_22000_UCODE_API_MIN, \ - .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = 10, \ - .non_shared_ant = ANT_B, \ - .dccm_offset = IWL_22000_DCCM_OFFSET, \ - .dccm_len = IWL_22000_DCCM_LEN, \ - .dccm2_offset = IWL_22000_DCCM2_OFFSET, \ - .dccm2_len = IWL_22000_DCCM2_LEN, \ - .smem_offset = IWL_22000_SMEM_OFFSET, \ - .smem_len = IWL_22000_SMEM_LEN, \ - .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ - .apmg_not_supported = true, \ - .trans.mq_rx_supported = true, \ - .vht_mu_mimo_supported = true, \ - .mac_addr_from_csr = 0x380, \ - .ht_params = &iwl_22000_ht_params, \ - .nvm_ver = IWL_22000_NVM_VERSION, \ - .trans.rf_id = true, \ - .trans.gen2 = true, \ - .nvm_type = IWL_NVM_EXT, \ - .dbgc_supported = true, \ - .min_umac_error_event_table = 0x400000, \ - .d3_debug_data_base_addr = 0x401000, \ - .d3_debug_data_length = 60 * 1024, \ - .mon_smem_regs = { \ - .write_ptr = { \ - .addr = LDBG_M2S_BUF_WPTR, \ - .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = LDBG_M2S_BUF_WRAP_CNT, \ - .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \ - }, \ - } - -#define IWL_DEVICE_22500 \ - IWL_DEVICE_22000_COMMON, \ - .ucode_api_max = IWL_22000_UCODE_API_MAX, \ - .trans.device_family = IWL_DEVICE_FAMILY_22000, \ - .trans.base_params = &iwl_22000_base_params, \ - .gp2_reg_addr = 0xa02c68, \ - .mon_dram_regs = { \ - .write_ptr = { \ - .addr = MON_BUFF_WRPTR_VER2, \ - .mask = 0xffffffff, \ - }, \ - .cycle_cnt = { \ - .addr = MON_BUFF_CYCLE_CNT_VER2, \ - .mask = 0xffffffff, \ - }, \ - } - -const struct iwl_cfg_trans_params iwl_qu_trans_cfg = { + .smem_offset = IWL_22000_SMEM_OFFSET, + .smem_len = IWL_22000_SMEM_LEN, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, + .apmg_not_supported = true, + .mac_addr_from_csr = 0x380, + .min_umac_error_event_table = 0x400000, + .d3_debug_data_base_addr = 0x401000, + .d3_debug_data_length = 60 * 1024, + .mon_smem_regs = { + .write_ptr = { + .addr = LDBG_M2S_BUF_WPTR, + .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, + }, + .cycle_cnt = { + .addr = LDBG_M2S_BUF_WRAP_CNT, + .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, + }, + }, + .gp2_reg_addr = 0xa02c68, + .mon_dram_regs = { + .write_ptr = { + .addr = MON_BUFF_WRPTR_VER2, + .mask = 0xffffffff, + }, + .cycle_cnt = { + .addr = MON_BUFF_CYCLE_CNT_VER2, + .mask = 0xffffffff, + }, + }, + .ucode_api_min = IWL_22000_UCODE_API_MIN, + .ucode_api_max = IWL_22000_UCODE_API_MAX, +}; + +const struct iwl_mac_cfg iwl_qu_mac_cfg = { .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_22000, - .base_params = &iwl_22000_base_params, + .base = &iwl_22000_base, .integrated = true, .xtal_latency = 500, .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_200US, }; -const struct iwl_cfg_trans_params iwl_qu_medium_latency_trans_cfg = { +const struct iwl_mac_cfg iwl_qu_medium_latency_mac_cfg = { .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_22000, - .base_params = &iwl_22000_base_params, + .base = &iwl_22000_base, .integrated = true, .xtal_latency = 1820, .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_1820US, }; -const struct iwl_cfg_trans_params iwl_qu_long_latency_trans_cfg = { +const struct iwl_mac_cfg iwl_qu_long_latency_mac_cfg = { .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_22000, - .base_params = &iwl_22000_base_params, + .base = &iwl_22000_base, .integrated = true, .xtal_latency = 12000, .low_latency_xtal = true, .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; -/* - * If the device doesn't support HE, no need to have that many buffers. - * 22000 devices can split multiple frames into a single RB, so fewer are - * needed; AX210 cannot (but use smaller RBs by default) - these sizes - * were picked according to 8 MSDUs inside 256 A-MSDUs in an A-MPDU, with - * additional overhead to account for processing time. - */ -#define IWL_NUM_RBDS_NON_HE 512 -#define IWL_NUM_RBDS_22000_HE 2048 - -/* - * All JF radio modules are part of the 9000 series, but the MAC part - * looks more like 22000. That's why this device is here, but called - * 9560 nevertheless. - */ -const struct iwl_cfg iwl9560_qu_b0_jf_b0_cfg = { - .fw_name_pre = IWL_QU_B_JF_B_FW_PRE, - IWL_DEVICE_22500, - .num_rbds = IWL_NUM_RBDS_NON_HE, -}; - -const struct iwl_cfg iwl9560_qu_c0_jf_b0_cfg = { - .fw_name_pre = IWL_QU_C_JF_B_FW_PRE, - IWL_DEVICE_22500, - .num_rbds = IWL_NUM_RBDS_NON_HE, -}; - -const struct iwl_cfg iwl9560_quz_a0_jf_b0_cfg = { - .fw_name_pre = IWL_QUZ_A_JF_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_NON_HE, -}; - -const struct iwl_cfg_trans_params iwl_ax200_trans_cfg = { +const struct iwl_mac_cfg iwl_ax200_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_22000, - .base_params = &iwl_22000_base_params, + .base = &iwl_22000_base, .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .bisr_workaround = 1, }; -const char iwl_ax101_name[] = "Intel(R) Wi-Fi 6 AX101"; -const char iwl_ax200_name[] = "Intel(R) Wi-Fi 6 AX200 160MHz"; -const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz"; -const char iwl_ax203_name[] = "Intel(R) Wi-Fi 6 AX203"; -const char iwl_ax204_name[] = "Intel(R) Wi-Fi 6 AX204 160MHz"; - const char iwl_ax200_killer_1650w_name[] = "Killer(R) Wi-Fi 6 AX1650w 160MHz Wireless Network Adapter (200D2W)"; const char iwl_ax200_killer_1650x_name[] = @@ -216,213 +132,10 @@ const char iwl_ax201_killer_1650s_name[] = const char iwl_ax201_killer_1650i_name[] = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201NGW)"; -const struct iwl_cfg iwl_qu_b0_hr1_b0 = { - .fw_name_pre = IWL_QU_B_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .tx_with_siso_diversity = true, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_qu_b0_hr_b0 = { - .fw_name_pre = IWL_QU_B_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_ax201_cfg_qu_hr = { - .name = "Intel(R) Wi-Fi 6 AX201 160MHz", - .fw_name_pre = IWL_QU_B_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_qu_c0_hr1_b0 = { - .fw_name_pre = IWL_QU_C_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .tx_with_siso_diversity = true, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_qu_c0_hr_b0 = { - .fw_name_pre = IWL_QU_C_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_ax201_cfg_qu_c0_hr_b0 = { - .name = "Intel(R) Wi-Fi 6 AX201 160MHz", - .fw_name_pre = IWL_QU_C_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_quz_a0_hr1_b0 = { - .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .tx_with_siso_diversity = true, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_ax201_cfg_quz_hr = { - .name = "Intel(R) Wi-Fi 6 AX201 160MHz", - .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_ax1650s_cfg_quz_hr = { - .name = "Killer(R) Wi-Fi 6 AX1650s 160MHz Wireless Network Adapter (201D2W)", - .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_ax1650i_cfg_quz_hr = { - .name = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201NGW)", - .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_ax200_cfg_cc = { - .fw_name_pre = IWL_CC_A_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg killer1650s_2ax_cfg_qu_b0_hr_b0 = { - .name = "Killer(R) Wi-Fi 6 AX1650s 160MHz Wireless Network Adapter (201NGW)", - .fw_name_pre = IWL_QU_B_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg killer1650i_2ax_cfg_qu_b0_hr_b0 = { - .name = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201D2W)", - .fw_name_pre = IWL_QU_B_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg killer1650s_2ax_cfg_qu_c0_hr_b0 = { - .name = "Killer(R) Wi-Fi 6 AX1650s 160MHz Wireless Network Adapter (201NGW)", - .fw_name_pre = IWL_QU_C_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg killer1650i_2ax_cfg_qu_c0_hr_b0 = { - .name = "Killer(R) Wi-Fi 6 AX1650i 160MHz Wireless Network Adapter (201D2W)", - .fw_name_pre = IWL_QU_C_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - -const struct iwl_cfg iwl_cfg_quz_a0_hr_b0 = { - .fw_name_pre = IWL_QUZ_A_HR_B_FW_PRE, - IWL_DEVICE_22500, - /* - * This device doesn't support receiving BlockAck with a large bitmap - * so we need to restrict the size of transmitted aggregation to the - * HT size; mac80211 would otherwise pick the HE max (256) by default. - */ - .max_tx_agg_size = IEEE80211_MAX_AMPDU_BUF_HT, - .num_rbds = IWL_NUM_RBDS_22000_HE, -}; - MODULE_FIRMWARE(IWL_QU_B_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_QU_C_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_QU_B_JF_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); +MODULE_FIRMWARE(IWL_QU_C_JF_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_QUZ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_QUZ_A_JF_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_CC_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/5000.c b/drivers/net/wireless/intel/iwlwifi/cfg/5000.c index de7ede59a994..aaae3b1f5433 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/5000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/5000.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2018 - 2020, 2023 Intel Corporation + * Copyright(c) 2018 - 2020, 2023, 2025 Intel Corporation *****************************************************************************/ #include <linux/module.h> @@ -30,7 +30,7 @@ #define IWL5150_FW_PRE "iwlwifi-5150" #define IWL5150_MODULE_FIRMWARE(api) IWL5150_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl5000_base_params = { +static const struct iwl_family_base_params iwl5000_base = { .eeprom_size = IWLAGN_EEPROM_IMG_SIZE, .num_of_queues = IWLAGN_NUM_QUEUES, .max_tfd_queue_size = 256, @@ -41,11 +41,6 @@ static const struct iwl_base_params iwl5000_base_params = { .scd_chain_ext_wa = true, }; -static const struct iwl_ht_params iwl5000_ht_params = { - .ht_greenfield_support = true, - .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), -}; - static const struct iwl_eeprom_params iwl5000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -58,93 +53,107 @@ static const struct iwl_eeprom_params iwl5000_eeprom_params = { }, }; +const struct iwl_mac_cfg iwl5000_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_5000, + .base = &iwl5000_base, +}; + #define IWL_DEVICE_5000 \ .fw_name_pre = IWL5000_FW_PRE, \ .ucode_api_max = IWL5000_UCODE_API_MAX, \ .ucode_api_min = IWL5000_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_5000, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_5000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_5000_TX_POWER_VERSION, \ - .trans.base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ .led_mode = IWL_LED_BLINK -const struct iwl_cfg iwl5300_agn_cfg = { - .name = "Intel(R) Ultimate N WiFi Link 5300 AGN", +const struct iwl_rf_cfg iwl5300_agn_cfg = { IWL_DEVICE_5000, /* at least EEPROM 0x11A has wrong info */ .valid_tx_ant = ANT_ABC, /* .cfg overwrite */ .valid_rx_ant = ANT_ABC, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl5100_bgn_cfg = { - .name = "Intel(R) WiFi Link 5100 BGN", - IWL_DEVICE_5000, - .valid_tx_ant = ANT_B, /* .cfg overwrite */ - .valid_rx_ant = ANT_AB, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, -}; +const char iwl5300_agn_name[] = "Intel(R) Ultimate N WiFi Link 5300 AGN"; -const struct iwl_cfg iwl5100_abg_cfg = { - .name = "Intel(R) WiFi Link 5100 ABG", +const struct iwl_rf_cfg iwl5100_n_cfg = { IWL_DEVICE_5000, .valid_tx_ant = ANT_B, /* .cfg overwrite */ .valid_rx_ant = ANT_AB, /* .cfg overwrite */ + .ht_params = { + .ht_greenfield_support = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl5100_agn_cfg = { - .name = "Intel(R) WiFi Link 5100 AGN", +const char iwl5100_bgn_name[] = "Intel(R) WiFi Link 5100 BGN"; + +const struct iwl_rf_cfg iwl5100_abg_cfg = { IWL_DEVICE_5000, .valid_tx_ant = ANT_B, /* .cfg overwrite */ .valid_rx_ant = ANT_AB, /* .cfg overwrite */ - .ht_params = &iwl5000_ht_params, }; -const struct iwl_cfg iwl5350_agn_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5350 AGN", +const char iwl5100_abg_name[] = "Intel(R) WiFi Link 5100 ABG"; +const char iwl5100_agn_name[] = "Intel(R) WiFi Link 5100 AGN"; + +const struct iwl_rf_cfg iwl5350_agn_cfg = { .fw_name_pre = IWL5000_FW_PRE, .ucode_api_max = IWL5000_UCODE_API_MAX, .ucode_api_min = IWL5000_UCODE_API_MIN, - .trans.device_family = IWL_DEVICE_FAMILY_5000, .max_inst_size = IWLAGN_RTC_INST_SIZE, .max_data_size = IWLAGN_RTC_DATA_SIZE, .nvm_ver = EEPROM_5050_EEPROM_VERSION, .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, - .trans.base_params = &iwl5000_base_params, .eeprom_params = &iwl5000_eeprom_params, - .ht_params = &iwl5000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .led_mode = IWL_LED_BLINK, .internal_wimax_coex = true, }; +const char iwl5350_agn_name[] = "Intel(R) WiMAX/WiFi Link 5350 AGN"; + +const struct iwl_mac_cfg iwl5150_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_5150, + .base = &iwl5000_base, +}; + #define IWL_DEVICE_5150 \ .fw_name_pre = IWL5150_FW_PRE, \ .ucode_api_max = IWL5150_UCODE_API_MAX, \ .ucode_api_min = IWL5150_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_5150, \ .max_inst_size = IWLAGN_RTC_INST_SIZE, \ .max_data_size = IWLAGN_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_5050_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_5050_TX_POWER_VERSION, \ - .trans.base_params = &iwl5000_base_params, \ .eeprom_params = &iwl5000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true -const struct iwl_cfg iwl5150_agn_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5150 AGN", +const struct iwl_rf_cfg iwl5150_agn_cfg = { IWL_DEVICE_5150, - .ht_params = &iwl5000_ht_params, - + .ht_params = { + .ht_greenfield_support = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl5150_abg_cfg = { - .name = "Intel(R) WiMAX/WiFi Link 5150 ABG", +const char iwl5150_agn_name[] = "Intel(R) WiMAX/WiFi Link 5150 AGN"; + +const struct iwl_rf_cfg iwl5150_abg_cfg = { IWL_DEVICE_5150, }; +const char iwl5150_abg_name[] = "Intel(R) WiMAX/WiFi Link 5150 ABG"; + MODULE_FIRMWARE(IWL5000_MODULE_FIRMWARE(IWL5000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL5150_MODULE_FIRMWARE(IWL5150_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c index f013cf420569..ab13394896e0 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/6000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/6000.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2018 - 2020, 2023 Intel Corporation + * Copyright(c) 2018 - 2020, 2023, 2025 Intel Corporation *****************************************************************************/ #include <linux/module.h> @@ -49,7 +49,7 @@ #define IWL6030_FW_PRE "iwlwifi-6000g2b" #define IWL6030_MODULE_FIRMWARE(api) IWL6030_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl6000_base_params = { +static const struct iwl_family_base_params iwl6000_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_2K, .num_of_queues = IWLAGN_NUM_QUEUES, .max_tfd_queue_size = 256, @@ -62,7 +62,7 @@ static const struct iwl_base_params iwl6000_base_params = { .scd_chain_ext_wa = true, }; -static const struct iwl_base_params iwl6050_base_params = { +static const struct iwl_family_base_params iwl6050_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_2K, .num_of_queues = IWLAGN_NUM_QUEUES, .max_tfd_queue_size = 256, @@ -75,7 +75,7 @@ static const struct iwl_base_params iwl6050_base_params = { .scd_chain_ext_wa = true, }; -static const struct iwl_base_params iwl6000_g2_base_params = { +static const struct iwl_family_base_params iwl6000_g2_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_2K, .num_of_queues = IWLAGN_NUM_QUEUES, .max_tfd_queue_size = 256, @@ -88,12 +88,6 @@ static const struct iwl_base_params iwl6000_g2_base_params = { .scd_chain_ext_wa = true, }; -static const struct iwl_ht_params iwl6000_ht_params = { - .ht_greenfield_support = true, - .use_rts_for_aggregation = true, /* use rts/cts protection */ - .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), -}; - static const struct iwl_eeprom_params iwl6000_eeprom_params = { .regulatory_bands = { EEPROM_REG_BAND_1_CHANNELS, @@ -107,141 +101,126 @@ static const struct iwl_eeprom_params iwl6000_eeprom_params = { .enhanced_txpower = true, }; +const struct iwl_mac_cfg iwl6005_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_6005, + .base = &iwl6000_g2_base, +}; + #define IWL_DEVICE_6005 \ .fw_name_pre = IWL6005_FW_PRE, \ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_6005, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_6005_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6005_TX_POWER_VERSION, \ - .trans.base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE -const struct iwl_cfg iwl6005_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; - -const struct iwl_cfg iwl6005_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 ABG", - IWL_DEVICE_6005, -}; - -const struct iwl_cfg iwl6005_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205 BG", +const struct iwl_rf_cfg iwl6005_n_cfg = { IWL_DEVICE_6005, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl6005_2agn_sff_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205S AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; +const char iwl6005_2agn_name[] = "Intel(R) Centrino(R) Advanced-N 6205 AGN"; +const char iwl6005_2agn_sff_name[] = "Intel(R) Centrino(R) Advanced-N 6205S AGN"; +const char iwl6005_2agn_d_name[] = "Intel(R) Centrino(R) Advanced-N 6205D AGN"; +const char iwl6005_2agn_mow1_name[] = "Intel(R) Centrino(R) Advanced-N 6206 AGN"; +const char iwl6005_2agn_mow2_name[] = "Intel(R) Centrino(R) Advanced-N 6207 AGN"; -const struct iwl_cfg iwl6005_2agn_d_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6205D AGN", +const struct iwl_rf_cfg iwl6005_non_n_cfg = { IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, }; -const struct iwl_cfg iwl6005_2agn_mow1_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6206 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, -}; +const char iwl6005_2abg_name[] = "Intel(R) Centrino(R) Advanced-N 6205 ABG"; +const char iwl6005_2bg_name[] = "Intel(R) Centrino(R) Advanced-N 6205 BG"; -const struct iwl_cfg iwl6005_2agn_mow2_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6207 AGN", - IWL_DEVICE_6005, - .ht_params = &iwl6000_ht_params, +const struct iwl_mac_cfg iwl6030_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_6030, + .base = &iwl6000_g2_base, }; #define IWL_DEVICE_6030 \ .fw_name_pre = IWL6030_FW_PRE, \ .ucode_api_max = IWL6000G2_UCODE_API_MAX, \ .ucode_api_min = IWL6000G2_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_6030, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ - .trans.base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE -const struct iwl_cfg iwl6030_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 AGN", +const struct iwl_rf_cfg iwl6030_n_cfg = { IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl6030_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 ABG", - IWL_DEVICE_6030, -}; +const char iwl6030_2agn_name[] = "Intel(R) Centrino(R) Advanced-N 6230 AGN"; +const char iwl6030_2bgn_name[] = "Intel(R) Centrino(R) Advanced-N 6230 BGN"; +const char iwl1030_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 1030 BGN"; +const char iwl1030_bg_name[] = "Intel(R) Centrino(R) Wireless-N 1030 BG"; -const struct iwl_cfg iwl6030_2bgn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 BGN", +const struct iwl_rf_cfg iwl6030_non_n_cfg = { IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, }; -const struct iwl_cfg iwl6030_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6230 BG", - IWL_DEVICE_6030, -}; +const char iwl6030_2abg_name[] = "Intel(R) Centrino(R) Advanced-N 6230 ABG"; +const char iwl6030_2bg_name[] = "Intel(R) Centrino(R) Advanced-N 6230 BG"; #define IWL_DEVICE_6035 \ .fw_name_pre = IWL6030_FW_PRE, \ .ucode_api_max = IWL6035_UCODE_API_MAX, \ .ucode_api_min = IWL6035_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_6030, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_6030_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6030_TX_POWER_VERSION, \ - .trans.base_params = &iwl6000_g2_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_RF_STATE -const struct iwl_cfg iwl6035_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6235 AGN", +const struct iwl_rf_cfg iwl6035_2agn_cfg = { IWL_DEVICE_6035, - .ht_params = &iwl6000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl6035_2agn_sff_cfg = { - .name = "Intel(R) Centrino(R) Ultimate-N 6235 AGN", - IWL_DEVICE_6035, - .ht_params = &iwl6000_ht_params, -}; +const char iwl6035_2agn_name[] = "Intel(R) Centrino(R) Advanced-N 6235 AGN"; +const char iwl6035_2agn_sff_name[] = "Intel(R) Centrino(R) Ultimate-N 6235 AGN"; -const struct iwl_cfg iwl1030_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1030 BGN", +const struct iwl_rf_cfg iwl130_bgn_cfg = { IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, + .rx_with_siso_diversity = true, }; -const struct iwl_cfg iwl1030_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 1030 BG", - IWL_DEVICE_6030, -}; +const char iwl130_bgn_name[] = "Intel(R) Centrino(R) Wireless-N 130 BGN"; -const struct iwl_cfg iwl130_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 130 BGN", +const struct iwl_rf_cfg iwl130_bg_cfg = { IWL_DEVICE_6030, - .ht_params = &iwl6000_ht_params, .rx_with_siso_diversity = true, }; -const struct iwl_cfg iwl130_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N 130 BG", - IWL_DEVICE_6030, - .rx_with_siso_diversity = true, +const char iwl130_bg_name[] = "Intel(R) Centrino(R) Wireless-N 130 BG"; + +const struct iwl_mac_cfg iwl6000i_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_6000i, + .base = &iwl6000_base, }; /* @@ -251,101 +230,127 @@ const struct iwl_cfg iwl130_bg_cfg = { .fw_name_pre = IWL6000_FW_PRE, \ .ucode_api_max = IWL6000_UCODE_API_MAX, \ .ucode_api_min = IWL6000_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_6000i, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .valid_tx_ant = ANT_BC, /* .cfg overwrite */ \ .valid_rx_ant = ANT_BC, /* .cfg overwrite */ \ .nvm_ver = EEPROM_6000_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, \ - .trans.base_params = &iwl6000_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK -const struct iwl_cfg iwl6000i_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 AGN", +const struct iwl_rf_cfg iwl6000i_2agn_cfg = { IWL_DEVICE_6000i, - .ht_params = &iwl6000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl6000i_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 ABG", +const char iwl6000i_2agn_name[] = "Intel(R) Centrino(R) Advanced-N 6200 AGN"; + +const struct iwl_rf_cfg iwl6000i_non_n_cfg = { IWL_DEVICE_6000i, }; -const struct iwl_cfg iwl6000i_2bg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N 6200 BG", - IWL_DEVICE_6000i, +const char iwl6000i_2abg_name[] = "Intel(R) Centrino(R) Advanced-N 6200 ABG"; +const char iwl6000i_2bg_name[] = "Intel(R) Centrino(R) Advanced-N 6200 BG"; + +const struct iwl_mac_cfg iwl6050_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_6050, + .base = &iwl6050_base, }; #define IWL_DEVICE_6050 \ .fw_name_pre = IWL6050_FW_PRE, \ .ucode_api_max = IWL6050_UCODE_API_MAX, \ .ucode_api_min = IWL6050_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_6050, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .valid_tx_ant = ANT_AB, /* .cfg overwrite */ \ .valid_rx_ant = ANT_AB, /* .cfg overwrite */ \ .nvm_ver = EEPROM_6050_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6050_TX_POWER_VERSION, \ - .trans.base_params = &iwl6050_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true -const struct iwl_cfg iwl6050_2agn_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN", +const struct iwl_rf_cfg iwl6050_2agn_cfg = { IWL_DEVICE_6050, - .ht_params = &iwl6000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl6050_2abg_cfg = { - .name = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG", +const char iwl6050_2agn_name[] = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 AGN"; + +const struct iwl_rf_cfg iwl6050_2abg_cfg = { IWL_DEVICE_6050, }; +const char iwl6050_2abg_name[] = "Intel(R) Centrino(R) Advanced-N + WiMAX 6250 ABG"; + +const struct iwl_mac_cfg iwl6150_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_6150, + .base = &iwl6050_base, +}; + #define IWL_DEVICE_6150 \ .fw_name_pre = IWL6050_FW_PRE, \ .ucode_api_max = IWL6050_UCODE_API_MAX, \ .ucode_api_min = IWL6050_UCODE_API_MIN, \ - .trans.device_family = IWL_DEVICE_FAMILY_6150, \ .max_inst_size = IWL60_RTC_INST_SIZE, \ .max_data_size = IWL60_RTC_DATA_SIZE, \ .nvm_ver = EEPROM_6150_EEPROM_VERSION, \ .nvm_calib_ver = EEPROM_6150_TX_POWER_VERSION, \ - .trans.base_params = &iwl6050_base_params, \ .eeprom_params = &iwl6000_eeprom_params, \ .led_mode = IWL_LED_BLINK, \ .internal_wimax_coex = true -const struct iwl_cfg iwl6150_bgn_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN", +const struct iwl_rf_cfg iwl6150_bgn_cfg = { IWL_DEVICE_6150, - .ht_params = &iwl6000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, }; -const struct iwl_cfg iwl6150_bg_cfg = { - .name = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG", +const char iwl6150_bgn_name[] = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BGN"; + +const struct iwl_rf_cfg iwl6150_bg_cfg = { IWL_DEVICE_6150, }; -const struct iwl_cfg iwl6000_3agn_cfg = { - .name = "Intel(R) Centrino(R) Ultimate-N 6300 AGN", +const char iwl6150_bg_name[] = "Intel(R) Centrino(R) Wireless-N + WiMAX 6150 BG"; + +const struct iwl_mac_cfg iwl6000_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_6000, + .base = &iwl6000_base, +}; + +const struct iwl_rf_cfg iwl6000_3agn_cfg = { .fw_name_pre = IWL6000_FW_PRE, .ucode_api_max = IWL6000_UCODE_API_MAX, .ucode_api_min = IWL6000_UCODE_API_MIN, - .trans.device_family = IWL_DEVICE_FAMILY_6000, .max_inst_size = IWL60_RTC_INST_SIZE, .max_data_size = IWL60_RTC_DATA_SIZE, .nvm_ver = EEPROM_6000_EEPROM_VERSION, .nvm_calib_ver = EEPROM_6000_TX_POWER_VERSION, - .trans.base_params = &iwl6000_base_params, .eeprom_params = &iwl6000_eeprom_params, - .ht_params = &iwl6000_ht_params, + .ht_params = { + .ht_greenfield_support = true, + .use_rts_for_aggregation = true, /* use rts/cts protection */ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .led_mode = IWL_LED_BLINK, }; +const char iwl6000_3agn_name[] = "Intel(R) Centrino(R) Ultimate-N 6300 AGN"; + MODULE_FIRMWARE(IWL6000_MODULE_FIRMWARE(IWL6000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL6050_MODULE_FIRMWARE(IWL6050_UCODE_API_MAX)); MODULE_FIRMWARE(IWL6005_MODULE_FIRMWARE(IWL6000G2_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c index 4e2afdedf4c6..f987ad3192c1 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/7000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/7000.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2020, 2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2020, 2023, 2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 Intel Deutschland GmbH */ @@ -49,7 +49,7 @@ #define IWL7265D_FW_PRE "iwlwifi-7265D" #define IWL7265D_MODULE_FIRMWARE(api) IWL7265D_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl7000_base_params = { +static const struct iwl_family_base_params iwl7000_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_16K, .num_of_queues = 31, .max_tfd_queue_size = 256, @@ -60,6 +60,7 @@ static const struct iwl_base_params iwl7000_base_params = { .shadow_reg_enable = true, .pcie_l1_allowed = true, .apmg_wake_up_wa = true, + .nvm_hw_section_num = 0, }; static const struct iwl_tt_params iwl7000_high_temp_tt_params = { @@ -84,16 +85,13 @@ static const struct iwl_tt_params iwl7000_high_temp_tt_params = { .support_tx_backoff = true, }; -static const struct iwl_ht_params iwl7000_ht_params = { - .stbc = true, - .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), +const struct iwl_mac_cfg iwl7000_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_7000, + .base = &iwl7000_base, }; #define IWL_DEVICE_7000_COMMON \ - .trans.device_family = IWL_DEVICE_FAMILY_7000, \ - .trans.base_params = &iwl7000_base_params, \ .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = 0, \ .non_shared_ant = ANT_A, \ .dccm_offset = IWL7000_DCCM_OFFSET @@ -117,77 +115,52 @@ static const struct iwl_ht_params iwl7000_ht_params = { .ucode_api_max = IWL7265D_UCODE_API_MAX, \ .ucode_api_min = IWL7265D_UCODE_API_MIN -const struct iwl_cfg iwl7260_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 7260", +const char iwl7260_2ac_name[] = "Intel(R) Dual Band Wireless AC 7260"; +const char iwl7260_2n_name[] = "Intel(R) Dual Band Wireless N 7260"; +const char iwl7260_n_name[] = "Intel(R) Wireless N 7260"; +const char iwl3160_2ac_name[] = "Intel(R) Dual Band Wireless AC 3160"; +const char iwl3160_2n_name[] = "Intel(R) Dual Band Wireless N 3160"; +const char iwl3160_n_name[] = "Intel(R) Wireless N 3160"; +const char iwl3165_2ac_name[] = "Intel(R) Dual Band Wireless-AC 3165"; +const char iwl3168_2ac_name[] = "Intel(R) Dual Band Wireless-AC 3168"; +const char iwl7265_2ac_name[] = "Intel(R) Dual Band Wireless-AC 7265"; +const char iwl7265_2n_name[] = "Intel(R) Dual Band Wireless-N 7265"; +const char iwl7265_n_name[] = "Intel(R) Wireless-N 7265"; + +const struct iwl_rf_cfg iwl7260_cfg = { .fw_name_pre = IWL7260_FW_PRE, IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, + .ht_params = { + .stbc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL7260_NVM_VERSION, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, .dccm_len = IWL7260_DCCM_LEN, }; -const struct iwl_cfg iwl7260_2ac_cfg_high_temp = { - .name = "Intel(R) Dual Band Wireless AC 7260", +const struct iwl_rf_cfg iwl7260_high_temp_cfg = { .fw_name_pre = IWL7260_FW_PRE, IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, + .ht_params = { + .stbc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL7260_NVM_VERSION, - .high_temp = true, .host_interrupt_operation_mode = true, .lp_xtal_workaround = true, .dccm_len = IWL7260_DCCM_LEN, .thermal_params = &iwl7000_high_temp_tt_params, }; -const struct iwl_cfg iwl7260_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 7260", - .fw_name_pre = IWL7260_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL7260_NVM_VERSION, - .host_interrupt_operation_mode = true, - .lp_xtal_workaround = true, - .dccm_len = IWL7260_DCCM_LEN, -}; - -const struct iwl_cfg iwl7260_n_cfg = { - .name = "Intel(R) Wireless N 7260", - .fw_name_pre = IWL7260_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL7260_NVM_VERSION, - .host_interrupt_operation_mode = true, - .lp_xtal_workaround = true, - .dccm_len = IWL7260_DCCM_LEN, -}; - -const struct iwl_cfg iwl3160_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 3160", +const struct iwl_rf_cfg iwl3160_cfg = { .fw_name_pre = IWL3160_FW_PRE, IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL3160_NVM_VERSION, - .host_interrupt_operation_mode = true, - .dccm_len = IWL3160_DCCM_LEN, -}; - -const struct iwl_cfg iwl3160_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 3160", - .fw_name_pre = IWL3160_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, - .nvm_ver = IWL3160_NVM_VERSION, - .host_interrupt_operation_mode = true, - .dccm_len = IWL3160_DCCM_LEN, -}; - -const struct iwl_cfg iwl3160_n_cfg = { - .name = "Intel(R) Wireless N 3160", - .fw_name_pre = IWL3160_FW_PRE, - IWL_DEVICE_7000, - .ht_params = &iwl7000_ht_params, + .ht_params = { + .stbc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL3160_NVM_VERSION, .host_interrupt_operation_mode = true, .dccm_len = IWL3160_DCCM_LEN, @@ -204,88 +177,52 @@ static const struct iwl_pwr_tx_backoff iwl7265_pwr_tx_backoffs[] = { {0}, }; -static const struct iwl_ht_params iwl7265_ht_params = { - .stbc = true, - .ldpc = true, - .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), -}; - -const struct iwl_cfg iwl3165_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 3165", +const struct iwl_rf_cfg iwl3165_2ac_cfg = { .fw_name_pre = IWL7265D_FW_PRE, IWL_DEVICE_7005D, - .ht_params = &iwl7000_ht_params, + .ht_params = { + .stbc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL3165_NVM_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, .dccm_len = IWL7265_DCCM_LEN, }; -const struct iwl_cfg iwl3168_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 3168", +const struct iwl_rf_cfg iwl3168_2ac_cfg = { .fw_name_pre = IWL3168_FW_PRE, IWL_DEVICE_3008, - .ht_params = &iwl7000_ht_params, + .ht_params = { + .stbc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL3168_NVM_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, .dccm_len = IWL7265_DCCM_LEN, .nvm_type = IWL_NVM_SDP, }; -const struct iwl_cfg iwl7265_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 7265", - .fw_name_pre = IWL7265_FW_PRE, - IWL_DEVICE_7005, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265_NVM_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 7265", - .fw_name_pre = IWL7265_FW_PRE, - IWL_DEVICE_7005, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265_NVM_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265_n_cfg = { - .name = "Intel(R) Wireless N 7265", +const struct iwl_rf_cfg iwl7265_cfg = { .fw_name_pre = IWL7265_FW_PRE, IWL_DEVICE_7005, - .ht_params = &iwl7265_ht_params, + .ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL7265_NVM_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, .dccm_len = IWL7265_DCCM_LEN, }; -const struct iwl_cfg iwl7265d_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 7265", - .fw_name_pre = IWL7265D_FW_PRE, - IWL_DEVICE_7005D, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265D_NVM_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265d_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 7265", +const struct iwl_rf_cfg iwl7265d_cfg = { .fw_name_pre = IWL7265D_FW_PRE, IWL_DEVICE_7005D, - .ht_params = &iwl7265_ht_params, - .nvm_ver = IWL7265D_NVM_VERSION, - .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, - .dccm_len = IWL7265_DCCM_LEN, -}; - -const struct iwl_cfg iwl7265d_n_cfg = { - .name = "Intel(R) Wireless N 7265", - .fw_name_pre = IWL7265D_FW_PRE, - IWL_DEVICE_7005D, - .ht_params = &iwl7265_ht_params, + .ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL7265D_NVM_VERSION, .pwr_tx_backoffs = iwl7265_pwr_tx_backoffs, .dccm_len = IWL7265_DCCM_LEN, diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c index d09cf8d7dc01..b56574006ee0 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/8000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/8000.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2014, 2018-2020, 2023 Intel Corporation + * Copyright (C) 2014, 2018-2020, 2023, 2025 Intel Corporation * Copyright (C) 2014-2015 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -35,9 +35,7 @@ #define IWL8265_MODULE_FIRMWARE(api) \ IWL8265_FW_PRE "-" __stringify(api) ".ucode" -#define DEFAULT_NVM_FILE_FAMILY_8000C "nvmData-8000C" - -static const struct iwl_base_params iwl8000_base_params = { +static const struct iwl_family_base_params iwl8000_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, .num_of_queues = 31, .max_tfd_queue_size = 256, @@ -47,12 +45,12 @@ static const struct iwl_base_params iwl8000_base_params = { .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, -}; - -static const struct iwl_ht_params iwl8000_ht_params = { - .stbc = true, - .ldpc = true, - .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + .nvm_hw_section_num = 10, + .features = NETIF_F_RXCSUM, + .smem_offset = IWL8260_SMEM_OFFSET, + .smem_len = IWL8260_SMEM_LEN, + .apmg_not_supported = true, + .min_umac_error_event_table = 0x800000, }; static const struct iwl_tt_params iwl8000_tt_params = { @@ -76,30 +74,20 @@ static const struct iwl_tt_params iwl8000_tt_params = { .support_tx_backoff = true, }; +const struct iwl_mac_cfg iwl8000_mac_cfg = { + .device_family = IWL_DEVICE_FAMILY_8000, + .base = &iwl8000_base, +}; + #define IWL_DEVICE_8000_COMMON \ - .trans.device_family = IWL_DEVICE_FAMILY_8000, \ - .trans.base_params = &iwl8000_base_params, \ .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = 10, \ - .features = NETIF_F_RXCSUM, \ .non_shared_ant = ANT_A, \ .dccm_offset = IWL8260_DCCM_OFFSET, \ .dccm_len = IWL8260_DCCM_LEN, \ .dccm2_offset = IWL8260_DCCM2_OFFSET, \ .dccm2_len = IWL8260_DCCM2_LEN, \ - .smem_offset = IWL8260_SMEM_OFFSET, \ - .smem_len = IWL8260_SMEM_LEN, \ - .default_nvm_file_C_step = DEFAULT_NVM_FILE_FAMILY_8000C, \ .thermal_params = &iwl8000_tt_params, \ - .apmg_not_supported = true, \ - .nvm_type = IWL_NVM_EXT, \ - .dbgc_supported = true, \ - .min_umac_error_event_table = 0x800000 - -#define IWL_DEVICE_8000 \ - IWL_DEVICE_8000_COMMON, \ - .ucode_api_max = IWL8000_UCODE_API_MAX, \ - .ucode_api_min = IWL8000_UCODE_API_MIN \ + .nvm_type = IWL_NVM_EXT #define IWL_DEVICE_8260 \ IWL_DEVICE_8000_COMMON, \ @@ -111,47 +99,39 @@ static const struct iwl_tt_params iwl8000_tt_params = { .ucode_api_max = IWL8265_UCODE_API_MAX, \ .ucode_api_min = IWL8265_UCODE_API_MIN \ -const struct iwl_cfg iwl8260_2n_cfg = { - .name = "Intel(R) Dual Band Wireless N 8260", - .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8260, - .ht_params = &iwl8000_ht_params, - .nvm_ver = IWL8000_NVM_VERSION, -}; +const char iwl8260_2n_name[] = "Intel(R) Dual Band Wireless-N 8260"; +const char iwl8260_2ac_name[] = "Intel(R) Dual Band Wireless-AC 8260"; +const char iwl8265_2ac_name[] = "Intel(R) Dual Band Wireless-AC 8265"; +const char iwl8275_2ac_name[] = "Intel(R) Dual Band Wireless-AC 8275"; +const char iwl4165_2ac_name[] = "Intel(R) Dual Band Wireless-AC 4165"; -const struct iwl_cfg iwl8260_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 8260", +const char iwl_killer_1435i_name[] = + "Killer(R) Wireless-AC 1435i Wireless Network Adapter (8265D2W)"; +const char iwl_killer_1434_kix_name[] = + "Killer(R) Wireless-AC 1435-KIX Wireless Network Adapter (8265NGW)"; + +const struct iwl_rf_cfg iwl8260_cfg = { .fw_name_pre = IWL8000_FW_PRE, IWL_DEVICE_8260, - .ht_params = &iwl8000_ht_params, + .ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL8000_NVM_VERSION, }; -const struct iwl_cfg iwl8265_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 8265", +const struct iwl_rf_cfg iwl8265_cfg = { .fw_name_pre = IWL8265_FW_PRE, IWL_DEVICE_8265, - .ht_params = &iwl8000_ht_params, - .nvm_ver = IWL8000_NVM_VERSION, - .vht_mu_mimo_supported = true, -}; - -const struct iwl_cfg iwl8275_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 8275", - .fw_name_pre = IWL8265_FW_PRE, - IWL_DEVICE_8265, - .ht_params = &iwl8000_ht_params, + .ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), + }, .nvm_ver = IWL8000_NVM_VERSION, .vht_mu_mimo_supported = true, }; -const struct iwl_cfg iwl4165_2ac_cfg = { - .name = "Intel(R) Dual Band Wireless AC 4165", - .fw_name_pre = IWL8000_FW_PRE, - IWL_DEVICE_8000, - .ht_params = &iwl8000_ht_params, - .nvm_ver = IWL8000_NVM_VERSION, -}; - MODULE_FIRMWARE(IWL8000_MODULE_FIRMWARE(IWL8000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL8265_MODULE_FIRMWARE(IWL8265_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c index 0130d9a9b78b..ac1fa291cf2f 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/9000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/9000.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2021, 2023 Intel Corporation + * Copyright (C) 2018-2021, 2023, 2025 Intel Corporation */ #include <linux/module.h> #include <linux/stringify.h> @@ -15,14 +15,7 @@ /* Lowest firmware API version supported */ #define IWL9000_UCODE_API_MIN 30 -/* NVM versions */ -#define IWL9000_NVM_VERSION 0x0a1d - /* Memory offsets and lengths */ -#define IWL9000_DCCM_OFFSET 0x800000 -#define IWL9000_DCCM_LEN 0x18000 -#define IWL9000_DCCM2_OFFSET 0x880000 -#define IWL9000_DCCM2_LEN 0x8000 #define IWL9000_SMEM_OFFSET 0x400000 #define IWL9000_SMEM_LEN 0x68000 @@ -33,7 +26,7 @@ #define IWL9260_MODULE_FIRMWARE(api) \ IWL9260_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl9000_base_params = { +static const struct iwl_family_base_params iwl9000_base = { .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, .num_of_queues = 31, .max_tfd_queue_size = 256, @@ -43,150 +36,69 @@ static const struct iwl_base_params iwl9000_base_params = { .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, -}; - -static const struct iwl_ht_params iwl9000_ht_params = { - .stbc = true, - .ldpc = true, - .ht40_bands = BIT(NL80211_BAND_2GHZ) | BIT(NL80211_BAND_5GHZ), -}; - -static const struct iwl_tt_params iwl9000_tt_params = { - .ct_kill_entry = 115, - .ct_kill_exit = 93, - .ct_kill_duration = 5, - .dynamic_smps_entry = 111, - .dynamic_smps_exit = 107, - .tx_protection_entry = 112, - .tx_protection_exit = 105, - .tx_backoff = { - {.temperature = 110, .backoff = 200}, - {.temperature = 111, .backoff = 600}, - {.temperature = 112, .backoff = 1200}, - {.temperature = 113, .backoff = 2000}, - {.temperature = 114, .backoff = 4000}, + .smem_offset = IWL9000_SMEM_OFFSET, + .smem_len = IWL9000_SMEM_LEN, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, + .apmg_not_supported = true, + .mac_addr_from_csr = 0x380, + .min_umac_error_event_table = 0x800000, + .d3_debug_data_base_addr = 0x401000, + .d3_debug_data_length = 92 * 1024, + .nvm_hw_section_num = 10, + .mon_smem_regs = { + .write_ptr = { + .addr = LDBG_M2S_BUF_WPTR, + .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, + }, + .cycle_cnt = { + .addr = LDBG_M2S_BUF_WRAP_CNT, + .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, + }, + }, + .mon_dram_regs = { + .write_ptr = { + .addr = MON_BUFF_WRPTR_VER2, + .mask = 0xffffffff, + }, + .cycle_cnt = { + .addr = MON_BUFF_CYCLE_CNT_VER2, + .mask = 0xffffffff, + }, }, - .support_ct_kill = true, - .support_dynamic_smps = true, - .support_tx_protection = true, - .support_tx_backoff = true, + .ucode_api_max = IWL9000_UCODE_API_MAX, + .ucode_api_min = IWL9000_UCODE_API_MIN, }; -#define IWL_DEVICE_9000 \ - .ucode_api_max = IWL9000_UCODE_API_MAX, \ - .ucode_api_min = IWL9000_UCODE_API_MIN, \ - .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = 10, \ - .non_shared_ant = ANT_B, \ - .dccm_offset = IWL9000_DCCM_OFFSET, \ - .dccm_len = IWL9000_DCCM_LEN, \ - .dccm2_offset = IWL9000_DCCM2_OFFSET, \ - .dccm2_len = IWL9000_DCCM2_LEN, \ - .smem_offset = IWL9000_SMEM_OFFSET, \ - .smem_len = IWL9000_SMEM_LEN, \ - .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ - .thermal_params = &iwl9000_tt_params, \ - .apmg_not_supported = true, \ - .num_rbds = 512, \ - .vht_mu_mimo_supported = true, \ - .mac_addr_from_csr = 0x380, \ - .nvm_type = IWL_NVM_EXT, \ - .dbgc_supported = true, \ - .min_umac_error_event_table = 0x800000, \ - .d3_debug_data_base_addr = 0x401000, \ - .d3_debug_data_length = 92 * 1024, \ - .ht_params = &iwl9000_ht_params, \ - .nvm_ver = IWL9000_NVM_VERSION, \ - .mon_smem_regs = { \ - .write_ptr = { \ - .addr = LDBG_M2S_BUF_WPTR, \ - .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = LDBG_M2S_BUF_WRAP_CNT, \ - .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \ - }, \ - }, \ - .mon_dram_regs = { \ - .write_ptr = { \ - .addr = MON_BUFF_WRPTR_VER2, \ - .mask = 0xffffffff, \ - }, \ - .cycle_cnt = { \ - .addr = MON_BUFF_CYCLE_CNT_VER2, \ - .mask = 0xffffffff, \ - }, \ - } - -const struct iwl_cfg_trans_params iwl9000_trans_cfg = { +const struct iwl_mac_cfg iwl9000_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_9000, - .base_params = &iwl9000_base_params, + .base = &iwl9000_base, .mq_rx_supported = true, - .rf_id = true, }; -const struct iwl_cfg_trans_params iwl9560_trans_cfg = { +const struct iwl_mac_cfg iwl9560_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_9000, - .base_params = &iwl9000_base_params, + .base = &iwl9000_base, .mq_rx_supported = true, - .rf_id = true, .integrated = true, .xtal_latency = 650, }; -const struct iwl_cfg_trans_params iwl9560_long_latency_trans_cfg = { +const struct iwl_mac_cfg iwl9560_long_latency_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_9000, - .base_params = &iwl9000_base_params, + .base = &iwl9000_base, .mq_rx_supported = true, - .rf_id = true, .integrated = true, .xtal_latency = 2820, }; -const struct iwl_cfg_trans_params iwl9560_shared_clk_trans_cfg = { +const struct iwl_mac_cfg iwl9560_shared_clk_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_9000, - .base_params = &iwl9000_base_params, + .base = &iwl9000_base, .mq_rx_supported = true, - .rf_id = true, .integrated = true, .xtal_latency = 670, .extra_phy_cfg_flags = FW_PHY_CFG_SHARED_CLK }; -const char iwl9162_name[] = "Intel(R) Wireless-AC 9162"; -const char iwl9260_name[] = "Intel(R) Wireless-AC 9260"; -const char iwl9260_1_name[] = "Intel(R) Wireless-AC 9260-1"; -const char iwl9270_name[] = "Intel(R) Wireless-AC 9270"; -const char iwl9461_name[] = "Intel(R) Wireless-AC 9461"; -const char iwl9462_name[] = "Intel(R) Wireless-AC 9462"; -const char iwl9560_name[] = "Intel(R) Wireless-AC 9560"; -const char iwl9162_160_name[] = "Intel(R) Wireless-AC 9162 160MHz"; -const char iwl9260_160_name[] = "Intel(R) Wireless-AC 9260 160MHz"; -const char iwl9270_160_name[] = "Intel(R) Wireless-AC 9270 160MHz"; -const char iwl9461_160_name[] = "Intel(R) Wireless-AC 9461 160MHz"; -const char iwl9462_160_name[] = "Intel(R) Wireless-AC 9462 160MHz"; -const char iwl9560_160_name[] = "Intel(R) Wireless-AC 9560 160MHz"; - -const char iwl9260_killer_1550_name[] = - "Killer (R) Wireless-AC 1550 Wireless Network Adapter (9260NGW) 160MHz"; -const char iwl9560_killer_1550i_name[] = - "Killer (R) Wireless-AC 1550i Wireless Network Adapter (9560NGW)"; -const char iwl9560_killer_1550i_160_name[] = - "Killer(R) Wireless-AC 1550i Wireless Network Adapter (9560NGW) 160MHz"; -const char iwl9560_killer_1550s_name[] = - "Killer (R) Wireless-AC 1550s Wireless Network Adapter (9560NGW)"; -const char iwl9560_killer_1550s_160_name[] = - "Killer(R) Wireless-AC 1550s Wireless Network Adapter (9560D2W) 160MHz"; - -const struct iwl_cfg iwl9260_2ac_cfg = { - .fw_name_pre = IWL9260_FW_PRE, - IWL_DEVICE_9000, -}; - -const struct iwl_cfg iwl9560_2ac_cfg_soc = { - .fw_name_pre = IWL9000_FW_PRE, - IWL_DEVICE_9000, -}; - MODULE_FIRMWARE(IWL9000_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); MODULE_FIRMWARE(IWL9260_MODULE_FIRMWARE(IWL9000_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c index dcba1a5d793b..3bf9fdbe01c6 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/ax210.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include <linux/module.h> #include <linux/stringify.h> @@ -15,14 +15,7 @@ /* Lowest firmware API version supported */ #define IWL_AX210_UCODE_API_MIN 77 -/* NVM versions */ -#define IWL_AX210_NVM_VERSION 0x0a1d - /* Memory offsets and lengths */ -#define IWL_AX210_DCCM_OFFSET 0x800000 /* LMAC1 */ -#define IWL_AX210_DCCM_LEN 0x10000 /* LMAC1 */ -#define IWL_AX210_DCCM2_OFFSET 0x880000 -#define IWL_AX210_DCCM2_LEN 0x8000 #define IWL_AX210_SMEM_OFFSET 0x400000 #define IWL_AX210_SMEM_LEN 0xD0000 @@ -47,8 +40,7 @@ #define IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(api) \ IWL_MA_B_HR_B_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl_ax210_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, +static const struct iwl_family_base_params iwl_ax210_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, .shadow_ram_support = true, @@ -57,74 +49,60 @@ static const struct iwl_base_params iwl_ax210_base_params = { .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, + .smem_offset = IWL_AX210_SMEM_OFFSET, + .smem_len = IWL_AX210_SMEM_LEN, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, + .apmg_not_supported = true, + .mac_addr_from_csr = 0x380, + .min_umac_error_event_table = 0x400000, + .d3_debug_data_base_addr = 0x401000, + .d3_debug_data_length = 60 * 1024, + .mon_smem_regs = { + .write_ptr = { + .addr = LDBG_M2S_BUF_WPTR, + .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, + }, + .cycle_cnt = { + .addr = LDBG_M2S_BUF_WRAP_CNT, + .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, + }, + }, + .min_txq_size = 128, + .gp2_reg_addr = 0xd02c68, + .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_HE, + .mon_dram_regs = { + .write_ptr = { + .addr = DBGC_CUR_DBGBUF_STATUS, + .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, + }, + .cycle_cnt = { + .addr = DBGC_DBGBUF_WRAP_AROUND, + .mask = 0xffffffff, + }, + .cur_frag = { + .addr = DBGC_CUR_DBGBUF_STATUS, + .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, + }, + }, + .ucode_api_min = IWL_AX210_UCODE_API_MIN, + .ucode_api_max = IWL_AX210_UCODE_API_MAX, }; -#define IWL_DEVICE_AX210_COMMON \ - .ucode_api_min = IWL_AX210_UCODE_API_MIN, \ - .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = 10, \ - .non_shared_ant = ANT_B, \ - .dccm_offset = IWL_AX210_DCCM_OFFSET, \ - .dccm_len = IWL_AX210_DCCM_LEN, \ - .dccm2_offset = IWL_AX210_DCCM2_OFFSET, \ - .dccm2_len = IWL_AX210_DCCM2_LEN, \ - .smem_offset = IWL_AX210_SMEM_OFFSET, \ - .smem_len = IWL_AX210_SMEM_LEN, \ - .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ - .apmg_not_supported = true, \ - .trans.mq_rx_supported = true, \ - .vht_mu_mimo_supported = true, \ - .mac_addr_from_csr = 0x380, \ - .ht_params = &iwl_22000_ht_params, \ - .nvm_ver = IWL_AX210_NVM_VERSION, \ - .trans.rf_id = true, \ - .trans.gen2 = true, \ - .nvm_type = IWL_NVM_EXT, \ - .dbgc_supported = true, \ - .min_umac_error_event_table = 0x400000, \ - .d3_debug_data_base_addr = 0x401000, \ - .d3_debug_data_length = 60 * 1024, \ - .mon_smem_regs = { \ - .write_ptr = { \ - .addr = LDBG_M2S_BUF_WPTR, \ - .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = LDBG_M2S_BUF_WRAP_CNT, \ - .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \ - }, \ - } - -#define IWL_DEVICE_AX210 \ - IWL_DEVICE_AX210_COMMON, \ - .ucode_api_max = IWL_AX210_UCODE_API_MAX, \ - .trans.umac_prph_offset = 0x300000, \ - .trans.device_family = IWL_DEVICE_FAMILY_AX210, \ - .trans.base_params = &iwl_ax210_base_params, \ - .min_txq_size = 128, \ - .gp2_reg_addr = 0xd02c68, \ - .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_HE, \ - .mon_dram_regs = { \ - .write_ptr = { \ - .addr = DBGC_CUR_DBGBUF_STATUS, \ - .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = DBGC_DBGBUF_WRAP_AROUND, \ - .mask = 0xffffffff, \ - }, \ - .cur_frag = { \ - .addr = DBGC_CUR_DBGBUF_STATUS, \ - .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \ - }, \ - } +const struct iwl_mac_cfg iwl_ty_mac_cfg = { + .mq_rx_supported = true, + .gen2 = true, + .device_family = IWL_DEVICE_FAMILY_AX210, + .base = &iwl_ax210_base, + .umac_prph_offset = 0x300000, + /* TODO: the following values need to be checked */ + .xtal_latency = 500, +}; -const struct iwl_cfg_trans_params iwl_so_trans_cfg = { +const struct iwl_mac_cfg iwl_so_mac_cfg = { .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_AX210, - .base_params = &iwl_ax210_base_params, + .base = &iwl_ax210_base, .umac_prph_offset = 0x300000, .integrated = true, /* TODO: the following values need to be checked */ @@ -132,12 +110,11 @@ const struct iwl_cfg_trans_params iwl_so_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_200US, }; -const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg = { +const struct iwl_mac_cfg iwl_so_long_latency_mac_cfg = { .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_AX210, - .base_params = &iwl_ax210_base_params, + .base = &iwl_ax210_base, .umac_prph_offset = 0x300000, .integrated = true, .low_latency_xtal = true, @@ -145,12 +122,11 @@ const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; -const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg = { +const struct iwl_mac_cfg iwl_so_long_latency_imr_mac_cfg = { .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_AX210, - .base_params = &iwl_ax210_base_params, + .base = &iwl_ax210_base, .umac_prph_offset = 0x300000, .integrated = true, .low_latency_xtal = true, @@ -159,108 +135,15 @@ const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg = { .imr_enabled = true, }; -/* - * If the device doesn't support HE, no need to have that many buffers. - * AX210 devices can split multiple frames into a single RB, so fewer are - * needed; AX210 cannot (but use smaller RBs by default) - these sizes - * were picked according to 8 MSDUs inside 256 A-MSDUs in an A-MPDU, with - * additional overhead to account for processing time. - */ -#define IWL_NUM_RBDS_NON_HE 512 -#define IWL_NUM_RBDS_AX210_HE 4096 - -const struct iwl_cfg_trans_params iwl_ma_trans_cfg = { +const struct iwl_mac_cfg iwl_ma_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_AX210, - .base_params = &iwl_ax210_base_params, + .base = &iwl_ax210_base, .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .integrated = true, .umac_prph_offset = 0x300000 }; -const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz"; -const char iwl_ax221_name[] = "Intel(R) Wi-Fi 6E AX221 160MHz"; -const char iwl_ax231_name[] = "Intel(R) Wi-Fi 6E AX231 160MHz"; -const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6E AX411 160MHz"; - -const char iwl_ax210_killer_1675w_name[] = - "Killer(R) Wi-Fi 6E AX1675w 160MHz Wireless Network Adapter (210D2W)"; -const char iwl_ax210_killer_1675x_name[] = - "Killer(R) Wi-Fi 6E AX1675x 160MHz Wireless Network Adapter (210NGW)"; -const char iwl_ax211_killer_1675s_name[] = - "Killer(R) Wi-Fi 6E AX1675s 160MHz Wireless Network Adapter (211NGW)"; -const char iwl_ax211_killer_1675i_name[] = - "Killer(R) Wi-Fi 6E AX1675i 160MHz Wireless Network Adapter (211NGW)"; -const char iwl_ax411_killer_1690s_name[] = - "Killer(R) Wi-Fi 6E AX1690s 160MHz Wireless Network Adapter (411D2W)"; -const char iwl_ax411_killer_1690i_name[] = - "Killer(R) Wi-Fi 6E AX1690i 160MHz Wireless Network Adapter (411NGW)"; - -const struct iwl_cfg iwlax210_2ax_cfg_so_jf_b0 = { - .name = "Intel(R) Wireless-AC 9560 160MHz", - .fw_name_pre = IWL_SO_A_JF_B_FW_PRE, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_NON_HE, -}; - -const struct iwl_cfg iwlax211_2ax_cfg_so_gf_a0 = { - .name = iwl_ax211_name, - .fw_name_pre = IWL_SO_A_GF_A_FW_PRE, - .uhb_supported = true, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_AX210_HE, -}; - -const struct iwl_cfg iwlax211_2ax_cfg_so_gf_a0_long = { - .name = iwl_ax211_name, - .fw_name_pre = IWL_SO_A_GF_A_FW_PRE, - .uhb_supported = true, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_AX210_HE, - .trans.xtal_latency = 12000, - .trans.low_latency_xtal = true, -}; - -const struct iwl_cfg iwlax210_2ax_cfg_ty_gf_a0 = { - .name = "Intel(R) Wi-Fi 6 AX210 160MHz", - .fw_name_pre = IWL_TY_A_GF_A_FW_PRE, - .uhb_supported = true, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_AX210_HE, -}; - -const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0 = { - .name = iwl_ax411_name, - .fw_name_pre = IWL_SO_A_GF4_A_FW_PRE, - .uhb_supported = true, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_AX210_HE, -}; - -const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0_long = { - .name = iwl_ax411_name, - .fw_name_pre = IWL_SO_A_GF4_A_FW_PRE, - .uhb_supported = true, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_AX210_HE, - .trans.xtal_latency = 12000, - .trans.low_latency_xtal = true, -}; - -const struct iwl_cfg iwl_cfg_ma = { - .fw_name_mac = "ma", - .uhb_supported = true, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_AX210_HE, -}; - -const struct iwl_cfg iwl_cfg_so_a0_hr_a0 = { - .fw_name_pre = IWL_SO_A_HR_B_FW_PRE, - IWL_DEVICE_AX210, - .num_rbds = IWL_NUM_RBDS_AX210_HE, -}; - MODULE_FIRMWARE(IWL_SO_A_JF_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX)); IWL_FW_AND_PNVM(IWL_SO_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c index efa3e0e35f79..05e45fff8b36 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/bz.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/bz.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include <linux/module.h> #include <linux/stringify.h> @@ -10,19 +10,12 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_BZ_UCODE_API_MAX 96 +#define IWL_BZ_UCODE_API_MAX 99 /* Lowest firmware API version supported */ -#define IWL_BZ_UCODE_API_MIN 92 - -/* NVM versions */ -#define IWL_BZ_NVM_VERSION 0x0a1d +#define IWL_BZ_UCODE_API_MIN 93 /* Memory offsets and lengths */ -#define IWL_BZ_DCCM_OFFSET 0x800000 /* LMAC1 */ -#define IWL_BZ_DCCM_LEN 0x10000 /* LMAC1 */ -#define IWL_BZ_DCCM2_OFFSET 0x880000 -#define IWL_BZ_DCCM2_LEN 0x8000 #define IWL_BZ_SMEM_OFFSET 0x400000 #define IWL_BZ_SMEM_LEN 0xD0000 @@ -38,8 +31,7 @@ #define IWL_BZ_A_HR_B_MODULE_FIRMWARE(api) \ IWL_BZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl_bz_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, +static const struct iwl_family_base_params iwl_bz_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, .shadow_ram_support = true, @@ -48,84 +40,55 @@ static const struct iwl_base_params iwl_bz_base_params = { .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, + .smem_offset = IWL_BZ_SMEM_OFFSET, + .smem_len = IWL_BZ_SMEM_LEN, + .apmg_not_supported = true, + .mac_addr_from_csr = 0x30, + .min_umac_error_event_table = 0xD0000, + .d3_debug_data_base_addr = 0x401000, + .d3_debug_data_length = 60 * 1024, + .mon_smem_regs = { + .write_ptr = { + .addr = LDBG_M2S_BUF_WPTR, + .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, + }, + .cycle_cnt = { + .addr = LDBG_M2S_BUF_WRAP_CNT, + .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, + }, + }, + .min_txq_size = 128, + .gp2_reg_addr = 0xd02c68, + .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, + .mon_dram_regs = { + .write_ptr = { + .addr = DBGC_CUR_DBGBUF_STATUS, + .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, + }, + .cycle_cnt = { + .addr = DBGC_DBGBUF_WRAP_AROUND, + .mask = 0xffffffff, + }, + .cur_frag = { + .addr = DBGC_CUR_DBGBUF_STATUS, + .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, + }, + }, + .mon_dbgi_regs = { + .write_ptr = { + .addr = DBGI_SRAM_FIFO_POINTERS, + .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, + }, + }, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, + .ucode_api_max = IWL_BZ_UCODE_API_MAX, + .ucode_api_min = IWL_BZ_UCODE_API_MIN, }; -#define IWL_DEVICE_BZ_COMMON \ - .ucode_api_max = IWL_BZ_UCODE_API_MAX, \ - .ucode_api_min = IWL_BZ_UCODE_API_MIN, \ - .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = 10, \ - .non_shared_ant = ANT_B, \ - .dccm_offset = IWL_BZ_DCCM_OFFSET, \ - .dccm_len = IWL_BZ_DCCM_LEN, \ - .dccm2_offset = IWL_BZ_DCCM2_OFFSET, \ - .dccm2_len = IWL_BZ_DCCM2_LEN, \ - .smem_offset = IWL_BZ_SMEM_OFFSET, \ - .smem_len = IWL_BZ_SMEM_LEN, \ - .apmg_not_supported = true, \ - .trans.mq_rx_supported = true, \ - .vht_mu_mimo_supported = true, \ - .mac_addr_from_csr = 0x30, \ - .nvm_ver = IWL_BZ_NVM_VERSION, \ - .trans.rf_id = true, \ - .trans.gen2 = true, \ - .nvm_type = IWL_NVM_EXT, \ - .dbgc_supported = true, \ - .min_umac_error_event_table = 0xD0000, \ - .d3_debug_data_base_addr = 0x401000, \ - .d3_debug_data_length = 60 * 1024, \ - .mon_smem_regs = { \ - .write_ptr = { \ - .addr = LDBG_M2S_BUF_WPTR, \ - .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = LDBG_M2S_BUF_WRAP_CNT, \ - .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \ - }, \ - }, \ - .trans.umac_prph_offset = 0x300000, \ - .trans.device_family = IWL_DEVICE_FAMILY_BZ, \ - .trans.base_params = &iwl_bz_base_params, \ - .min_txq_size = 128, \ - .gp2_reg_addr = 0xd02c68, \ - .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, \ - .mon_dram_regs = { \ - .write_ptr = { \ - .addr = DBGC_CUR_DBGBUF_STATUS, \ - .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = DBGC_DBGBUF_WRAP_AROUND, \ - .mask = 0xffffffff, \ - }, \ - .cur_frag = { \ - .addr = DBGC_CUR_DBGBUF_STATUS, \ - .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \ - }, \ - }, \ - .mon_dbgi_regs = { \ - .write_ptr = { \ - .addr = DBGI_SRAM_FIFO_POINTERS, \ - .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, \ - }, \ - } - -#define IWL_DEVICE_BZ \ - IWL_DEVICE_BZ_COMMON, \ - .ht_params = &iwl_22000_ht_params - -/* - * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an - * A-MPDU, with additional overhead to account for processing time. - */ -#define IWL_NUM_RBDS_BZ_EHT (512 * 16) - -const struct iwl_cfg_trans_params iwl_bz_trans_cfg = { +const struct iwl_mac_cfg iwl_bz_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_BZ, - .base_params = &iwl_bz_base_params, + .base = &iwl_bz_base, .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .integrated = true, .umac_prph_offset = 0x300000, @@ -134,39 +97,16 @@ const struct iwl_cfg_trans_params iwl_bz_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; -const struct iwl_cfg_trans_params iwl_gl_trans_cfg = { +const struct iwl_mac_cfg iwl_gl_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_BZ, - .base_params = &iwl_bz_base_params, + .base = &iwl_bz_base, .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .umac_prph_offset = 0x300000, .xtal_latency = 12000, .low_latency_xtal = true, }; -const char iwl_bz_name[] = "Intel(R) TBD Bz device"; -const char iwl_fm_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz"; -const char iwl_wh_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz"; -const char iwl_gl_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz"; -const char iwl_mtp_name[] = "Intel(R) Wi-Fi 7 BE202 160MHz"; - -const struct iwl_cfg iwl_cfg_bz = { - .fw_name_mac = "bz", - .uhb_supported = true, - IWL_DEVICE_BZ, - .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, - .num_rbds = IWL_NUM_RBDS_BZ_EHT, -}; - -const struct iwl_cfg iwl_cfg_gl = { - .fw_name_mac = "gl", - .uhb_supported = true, - IWL_DEVICE_BZ, - .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, - .num_rbds = IWL_NUM_RBDS_BZ_EHT, -}; - MODULE_FIRMWARE(IWL_BZ_A_HR_B_MODULE_FIRMWARE(IWL_BZ_UCODE_API_MAX)); IWL_FW_AND_PNVM(IWL_BZ_A_GF_A_FW_PRE, IWL_BZ_UCODE_API_MAX); IWL_FW_AND_PNVM(IWL_BZ_A_GF4_A_FW_PRE, IWL_BZ_UCODE_API_MAX); diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c index ab7c0f8d54f4..45e55cef42ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/dr.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/dr.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #include <linux/module.h> #include <linux/stringify.h> @@ -9,35 +9,21 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_DR_UCODE_API_MAX 96 +#define IWL_DR_UCODE_API_MAX 99 /* Lowest firmware API version supported */ -#define IWL_DR_UCODE_API_MIN 96 - -/* NVM versions */ -#define IWL_DR_NVM_VERSION 0x0a1d +#define IWL_DR_UCODE_API_MIN 97 /* Memory offsets and lengths */ -#define IWL_DR_DCCM_OFFSET 0x800000 /* LMAC1 */ -#define IWL_DR_DCCM_LEN 0x10000 /* LMAC1 */ -#define IWL_DR_DCCM2_OFFSET 0x880000 -#define IWL_DR_DCCM2_LEN 0x8000 #define IWL_DR_SMEM_OFFSET 0x400000 #define IWL_DR_SMEM_LEN 0xD0000 #define IWL_DR_A_PE_A_FW_PRE "iwlwifi-dr-a0-pe-a0" -#define IWL_BR_A_PET_A_FW_PRE "iwlwifi-br-a0-petc-a0" -#define IWL_BR_A_PE_A_FW_PRE "iwlwifi-br-a0-pe-a0" #define IWL_DR_A_PE_A_FW_MODULE_FIRMWARE(api) \ IWL_DR_A_PE_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_BR_A_PET_A_FW_MODULE_FIRMWARE(api) \ - IWL_BR_A_PET_A_FW_PRE "-" __stringify(api) ".ucode" -#define IWL_BR_A_PE_A_FW_MODULE_FIRMWARE(api) \ - IWL_BR_A_PE_A_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl_dr_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, +static const struct iwl_family_base_params iwl_dr_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, .shadow_ram_support = true, @@ -46,87 +32,55 @@ static const struct iwl_base_params iwl_dr_base_params = { .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, + .smem_offset = IWL_DR_SMEM_OFFSET, + .smem_len = IWL_DR_SMEM_LEN, + .apmg_not_supported = true, + .mac_addr_from_csr = 0x30, + .min_umac_error_event_table = 0xD0000, + .d3_debug_data_base_addr = 0x401000, + .d3_debug_data_length = 60 * 1024, + .mon_smem_regs = { + .write_ptr = { + .addr = LDBG_M2S_BUF_WPTR, + .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, + }, + .cycle_cnt = { + .addr = LDBG_M2S_BUF_WRAP_CNT, + .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, + }, + }, + .min_txq_size = 128, + .gp2_reg_addr = 0xd02c68, + .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, + .mon_dram_regs = { + .write_ptr = { + .addr = DBGC_CUR_DBGBUF_STATUS, + .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, + }, + .cycle_cnt = { + .addr = DBGC_DBGBUF_WRAP_AROUND, + .mask = 0xffffffff, + }, + .cur_frag = { + .addr = DBGC_CUR_DBGBUF_STATUS, + .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, + }, + }, + .mon_dbgi_regs = { + .write_ptr = { + .addr = DBGI_SRAM_FIFO_POINTERS, + .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, + }, + }, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, + .ucode_api_max = IWL_DR_UCODE_API_MAX, + .ucode_api_min = IWL_DR_UCODE_API_MIN, }; -#define IWL_DEVICE_DR_COMMON \ - .ucode_api_max = IWL_DR_UCODE_API_MAX, \ - .ucode_api_min = IWL_DR_UCODE_API_MIN, \ - .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = 10, \ - .non_shared_ant = ANT_B, \ - .dccm_offset = IWL_DR_DCCM_OFFSET, \ - .dccm_len = IWL_DR_DCCM_LEN, \ - .dccm2_offset = IWL_DR_DCCM2_OFFSET, \ - .dccm2_len = IWL_DR_DCCM2_LEN, \ - .smem_offset = IWL_DR_SMEM_OFFSET, \ - .smem_len = IWL_DR_SMEM_LEN, \ - .apmg_not_supported = true, \ - .trans.mq_rx_supported = true, \ - .vht_mu_mimo_supported = true, \ - .mac_addr_from_csr = 0x30, \ - .nvm_ver = IWL_DR_NVM_VERSION, \ - .trans.rf_id = true, \ - .trans.gen2 = true, \ - .nvm_type = IWL_NVM_EXT, \ - .dbgc_supported = true, \ - .min_umac_error_event_table = 0xD0000, \ - .d3_debug_data_base_addr = 0x401000, \ - .d3_debug_data_length = 60 * 1024, \ - .mon_smem_regs = { \ - .write_ptr = { \ - .addr = LDBG_M2S_BUF_WPTR, \ - .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = LDBG_M2S_BUF_WRAP_CNT, \ - .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \ - }, \ - }, \ - .trans.umac_prph_offset = 0x300000, \ - .trans.device_family = IWL_DEVICE_FAMILY_DR, \ - .trans.base_params = &iwl_dr_base_params, \ - .min_txq_size = 128, \ - .gp2_reg_addr = 0xd02c68, \ - .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, \ - .mon_dram_regs = { \ - .write_ptr = { \ - .addr = DBGC_CUR_DBGBUF_STATUS, \ - .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = DBGC_DBGBUF_WRAP_AROUND, \ - .mask = 0xffffffff, \ - }, \ - .cur_frag = { \ - .addr = DBGC_CUR_DBGBUF_STATUS, \ - .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \ - }, \ - }, \ - .mon_dbgi_regs = { \ - .write_ptr = { \ - .addr = DBGI_SRAM_FIFO_POINTERS, \ - .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, \ - }, \ - } - -#define IWL_DEVICE_DR \ - IWL_DEVICE_DR_COMMON, \ - .uhb_supported = true, \ - .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ - .num_rbds = IWL_NUM_RBDS_DR_EHT, \ - .ht_params = &iwl_22000_ht_params - -/* - * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an - * A-MPDU, with additional overhead to account for processing time. - */ -#define IWL_NUM_RBDS_DR_EHT (512 * 16) - -const struct iwl_cfg_trans_params iwl_dr_trans_cfg = { +const struct iwl_mac_cfg iwl_dr_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_DR, - .base_params = &iwl_dr_base_params, + .base = &iwl_dr_base, .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .integrated = true, .umac_prph_offset = 0x300000, @@ -135,33 +89,5 @@ const struct iwl_cfg_trans_params iwl_dr_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; -const char iwl_dr_name[] = "Intel(R) TBD Dr device"; - -const struct iwl_cfg iwl_cfg_dr = { - .fw_name_mac = "dr", - IWL_DEVICE_DR, -}; - -const struct iwl_cfg_trans_params iwl_br_trans_cfg = { - .device_family = IWL_DEVICE_FAMILY_DR, - .base_params = &iwl_dr_base_params, - .mq_rx_supported = true, - .rf_id = true, - .gen2 = true, - .integrated = true, - .umac_prph_offset = 0x300000, - .xtal_latency = 12000, - .low_latency_xtal = true, - .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, -}; - -const char iwl_br_name[] = "Intel(R) TBD Br device"; - -const struct iwl_cfg iwl_cfg_br = { - .fw_name_mac = "br", - IWL_DEVICE_DR, -}; - MODULE_FIRMWARE(IWL_DR_A_PE_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_BR_A_PET_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX)); -MODULE_FIRMWARE(IWL_BR_A_PE_A_FW_MODULE_FIRMWARE(IWL_DR_UCODE_API_MAX)); + diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c new file mode 100644 index 000000000000..456a666c8dfd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-fm.c @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2025 Intel Corporation + */ +#include "iwl-config.h" + +/* NVM versions */ +#define IWL_FM_NVM_VERSION 0x0a1d + +#define IWL_DEVICE_FM \ + .ht_params = { \ + .stbc = true, \ + .ldpc = true, \ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | \ + BIT(NL80211_BAND_5GHZ), \ + }, \ + .led_mode = IWL_LED_RF_STATE, \ + .non_shared_ant = ANT_B, \ + .vht_mu_mimo_supported = true, \ + .uhb_supported = true, \ + .num_rbds = IWL_NUM_RBDS_EHT, \ + .nvm_ver = IWL_FM_NVM_VERSION, \ + .nvm_type = IWL_NVM_EXT + +const struct iwl_rf_cfg iwl_rf_fm = { + IWL_DEVICE_FM, +}; + +const struct iwl_rf_cfg iwl_rf_fm_160mhz = { + IWL_DEVICE_FM, + .bw_limit = 160, +}; + +const char iwl_killer_be1750s_name[] = + "Killer(R) Wi-Fi 7 BE1750s 320MHz Wireless Network Adapter (BE201D2W)"; +const char iwl_killer_be1750i_name[] = + "Killer(R) Wi-Fi 7 BE1750i 320MHz Wireless Network Adapter (BE201NGW)"; +const char iwl_killer_be1750w_name[] = + "Killer(TM) Wi-Fi 7 BE1750w 320MHz Wireless Network Adapter (BE200D2W)"; +const char iwl_killer_be1750x_name[] = + "Killer(TM) Wi-Fi 7 BE1750x 320MHz Wireless Network Adapter (BE200NGW)"; +const char iwl_killer_be1790s_name[] = + "Killer(R) Wi-Fi 7 BE1790s 320MHz Wireless Network Adapter (BE401D2W)"; +const char iwl_killer_be1790i_name[] = + "Killer(R) Wi-Fi 7 BE1790i 320MHz Wireless Network Adapter (BE401NGW)"; + +const char iwl_be201_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz"; +const char iwl_be200_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz"; +const char iwl_be202_name[] = "Intel(R) Wi-Fi 7 BE202 160MHz"; +const char iwl_be401_name[] = "Intel(R) Wi-Fi 7 BE401 320MHz"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c new file mode 100644 index 000000000000..f55c286e83be --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-gf.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2025 Intel Corporation + */ +#include "iwl-config.h" + +/* NVM versions */ +#define IWL_GF_NVM_VERSION 0x0a1d + +const struct iwl_rf_cfg iwl_rf_gf = { + .uhb_supported = true, + .led_mode = IWL_LED_RF_STATE, + .non_shared_ant = ANT_B, + .vht_mu_mimo_supported = true, + .ht_params = { + .stbc = true, + .ldpc = true, + .ht40_bands = BIT(NL80211_BAND_2GHZ) | + BIT(NL80211_BAND_5GHZ), + }, + .nvm_ver = IWL_GF_NVM_VERSION, + .nvm_type = IWL_NVM_EXT, + .num_rbds = IWL_NUM_RBDS_HE, +}; + +const char iwl_ax210_killer_1675w_name[] = + "Killer(R) Wi-Fi 6E AX1675w 160MHz Wireless Network Adapter (210D2W)"; +const char iwl_ax210_killer_1675x_name[] = + "Killer(R) Wi-Fi 6E AX1675x 160MHz Wireless Network Adapter (210NGW)"; +const char iwl_ax211_killer_1675s_name[] = + "Killer(R) Wi-Fi 6E AX1675s 160MHz Wireless Network Adapter (211D2W)"; +const char iwl_ax211_killer_1675i_name[] = + "Killer(R) Wi-Fi 6E AX1675i 160MHz Wireless Network Adapter (211NGW)"; +const char iwl_ax411_killer_1690s_name[] = + "Killer(R) Wi-Fi 6E AX1690s 160MHz Wireless Network Adapter (411D2W)"; +const char iwl_ax411_killer_1690i_name[] = + "Killer(R) Wi-Fi 6E AX1690i 160MHz Wireless Network Adapter (411NGW)"; + +const char iwl_ax210_name[] = "Intel(R) Wi-Fi 6E AX210 160MHz"; +const char iwl_ax211_name[] = "Intel(R) Wi-Fi 6E AX211 160MHz"; +const char iwl_ax411_name[] = "Intel(R) Wi-Fi 6E AX411 160MHz"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c new file mode 100644 index 000000000000..db02664e3917 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-hr.c @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2025 Intel Corporation + */ +#include "iwl-config.h" + +/* NVM versions */ +#define IWL_HR_NVM_VERSION 0x0a1d + +#define IWL_DEVICE_HR \ + .led_mode = IWL_LED_RF_STATE, \ + .non_shared_ant = ANT_B, \ + .vht_mu_mimo_supported = true, \ + .ht_params = { \ + .stbc = true, \ + .ldpc = true, \ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | \ + BIT(NL80211_BAND_5GHZ), \ + }, \ + .num_rbds = IWL_NUM_RBDS_HE, \ + .nvm_ver = IWL_HR_NVM_VERSION, \ + .nvm_type = IWL_NVM_EXT + +const struct iwl_rf_cfg iwl_rf_hr1 = { + IWL_DEVICE_HR, + .tx_with_siso_diversity = true, +}; + +const struct iwl_rf_cfg iwl_rf_hr = { + IWL_DEVICE_HR, +}; + +const struct iwl_rf_cfg iwl_rf_hr_80mhz = { + IWL_DEVICE_HR, + .bw_limit = 80, +}; + +const char iwl_ax101_name[] = "Intel(R) Wi-Fi 6 AX101"; +const char iwl_ax200_name[] = "Intel(R) Wi-Fi 6 AX200 160MHz"; +const char iwl_ax201_name[] = "Intel(R) Wi-Fi 6 AX201 160MHz"; +const char iwl_ax203_name[] = "Intel(R) Wi-Fi 6 AX203"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-jf.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-jf.c new file mode 100644 index 000000000000..467eaeae6deb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-jf.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2015-2017 Intel Deutschland GmbH + * Copyright (C) 2018-2021, 2023, 2025 Intel Corporation + */ +#include "iwl-config.h" + +/* NVM versions */ +#define IWL_JF_NVM_VERSION 0x0a1d + +/* Memory offsets and lengths */ +#define IWL9000_DCCM_OFFSET 0x800000 +#define IWL9000_DCCM_LEN 0x18000 +#define IWL9000_DCCM2_OFFSET 0x880000 +#define IWL9000_DCCM2_LEN 0x8000 + +static const struct iwl_tt_params iwl_jf_tt_params = { + .ct_kill_entry = 115, + .ct_kill_exit = 93, + .ct_kill_duration = 5, + .dynamic_smps_entry = 111, + .dynamic_smps_exit = 107, + .tx_protection_entry = 112, + .tx_protection_exit = 105, + .tx_backoff = { + {.temperature = 110, .backoff = 200}, + {.temperature = 111, .backoff = 600}, + {.temperature = 112, .backoff = 1200}, + {.temperature = 113, .backoff = 2000}, + {.temperature = 114, .backoff = 4000}, + }, + .support_ct_kill = true, + .support_dynamic_smps = true, + .support_tx_protection = true, + .support_tx_backoff = true, +}; + +/* these values are ignored if not with Pu/Th MAC firmware, due to offload */ +#define IWL_DEVICE_JF_PU \ + .dccm_offset = IWL9000_DCCM_OFFSET, \ + .dccm_len = IWL9000_DCCM_LEN, \ + .dccm2_offset = IWL9000_DCCM2_OFFSET, \ + .dccm2_len = IWL9000_DCCM2_LEN, \ + .thermal_params = &iwl_jf_tt_params + +#define IWL_DEVICE_JF \ + IWL_DEVICE_JF_PU, \ + .led_mode = IWL_LED_RF_STATE, \ + .non_shared_ant = ANT_B, \ + .num_rbds = IWL_NUM_RBDS_NON_HE, \ + .vht_mu_mimo_supported = true, \ + .ht_params = { \ + .stbc = true, \ + .ldpc = true, \ + .ht40_bands = BIT(NL80211_BAND_2GHZ) | \ + BIT(NL80211_BAND_5GHZ), \ + }, \ + .nvm_ver = IWL_JF_NVM_VERSION, \ + .nvm_type = IWL_NVM_EXT + +const struct iwl_rf_cfg iwl_rf_jf = { + IWL_DEVICE_JF, +}; + +const struct iwl_rf_cfg iwl_rf_jf_80mhz = { + IWL_DEVICE_JF, + .bw_limit = 80, +}; + +const char iwl9260_name[] = "Intel(R) Wireless-AC 9260"; +const char iwl9461_name[] = "Intel(R) Wireless-AC 9461"; +const char iwl9462_name[] = "Intel(R) Wireless-AC 9462"; +const char iwl9560_name[] = "Intel(R) Wireless-AC 9560"; +const char iwl9260_160_name[] = "Intel(R) Wireless-AC 9260 160MHz"; +const char iwl9461_160_name[] = "Intel(R) Wireless-AC 9461 160MHz"; +const char iwl9462_160_name[] = "Intel(R) Wireless-AC 9462 160MHz"; +const char iwl9560_160_name[] = "Intel(R) Wireless-AC 9560 160MHz"; + +const char iwl9260_killer_1550_name[] = + "Killer(R) Wireless-AC 1550 Wireless Network Adapter (9260NGW) 160MHz"; +const char iwl9560_killer_1550i_name[] = + "Killer(R) Wireless-AC 1550i Wireless Network Adapter (9560NGW) 160MHz"; +const char iwl9560_killer_1550s_name[] = + "Killer(R) Wireless-AC 1550s Wireless Network Adapter (9560D2W) 160MHz"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c new file mode 100644 index 000000000000..483f21659eff --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-pe.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2025 Intel Corporation + */ +#include "iwl-config.h" + +/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */ + +const char iwl_killer_bn1850w2_name[] = + "Killer(R) Wi-Fi 8 BN1850w2 320MHz Wireless Network Adapter (BN201.D2W)"; +const char iwl_killer_bn1850i_name[] = + "Killer(R) Wi-Fi 8 BN1850i 320MHz Wireless Network Adapter (BN201.NGW)"; + +const char iwl_bn201_name[] = "Intel(R) Wi-Fi 8 BN201"; +const char iwl_be221_name[] = "Intel(R) Wi-Fi 7 BE221"; +const char iwl_be223_name[] = "Intel(R) Wi-Fi 7 BE223"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c b/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c new file mode 100644 index 000000000000..97735175cb0e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/cfg/rf-wh.c @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2025 Intel Corporation + */ +#include "iwl-config.h" + +/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */ + +const char iwl_killer_be1775s_name[] = + "Killer(R) Wi-Fi 7 BE1775s 320MHz Wireless Network Adapter (BE211D2W)"; +const char iwl_killer_be1775i_name[] = + "Killer(R) Wi-Fi 7 BE1775i 320MHz Wireless Network Adapter (BE211NGW)"; + +const char iwl_be211_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz"; +const char iwl_be213_name[] = "Intel(R) Wi-Fi 7 BE213 160MHz"; diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c index c9eeb3f6704e..b2e4d4035296 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/sc.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/sc.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include <linux/module.h> #include <linux/stringify.h> @@ -10,19 +10,15 @@ #include "fw/api/txq.h" /* Highest firmware API version supported */ -#define IWL_SC_UCODE_API_MAX 96 +#define IWL_SC_UCODE_API_MAX 99 /* Lowest firmware API version supported */ -#define IWL_SC_UCODE_API_MIN 92 +#define IWL_SC_UCODE_API_MIN 97 /* NVM versions */ #define IWL_SC_NVM_VERSION 0x0a1d /* Memory offsets and lengths */ -#define IWL_SC_DCCM_OFFSET 0x800000 /* LMAC1 */ -#define IWL_SC_DCCM_LEN 0x10000 /* LMAC1 */ -#define IWL_SC_DCCM2_OFFSET 0x880000 -#define IWL_SC_DCCM2_LEN 0x8000 #define IWL_SC_SMEM_OFFSET 0x400000 #define IWL_SC_SMEM_LEN 0xD0000 @@ -43,8 +39,7 @@ #define IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(api) \ IWL_SC_A_HR_B_FW_PRE "-" __stringify(api) ".ucode" -static const struct iwl_base_params iwl_sc_base_params = { - .eeprom_size = OTP_LOW_IMAGE_SIZE_32K, +static const struct iwl_family_base_params iwl_sc_base = { .num_of_queues = 512, .max_tfd_queue_size = 65536, .shadow_ram_support = true, @@ -53,87 +48,55 @@ static const struct iwl_base_params iwl_sc_base_params = { .max_event_log_size = 512, .shadow_reg_enable = true, .pcie_l1_allowed = true, + .smem_offset = IWL_SC_SMEM_OFFSET, + .smem_len = IWL_SC_SMEM_LEN, + .apmg_not_supported = true, + .mac_addr_from_csr = 0x30, + .min_umac_error_event_table = 0xD0000, + .d3_debug_data_base_addr = 0x401000, + .d3_debug_data_length = 60 * 1024, + .mon_smem_regs = { + .write_ptr = { + .addr = LDBG_M2S_BUF_WPTR, + .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, + }, + .cycle_cnt = { + .addr = LDBG_M2S_BUF_WRAP_CNT, + .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, + }, + }, + .min_txq_size = 128, + .gp2_reg_addr = 0xd02c68, + .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, + .mon_dram_regs = { + .write_ptr = { + .addr = DBGC_CUR_DBGBUF_STATUS, + .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, + }, + .cycle_cnt = { + .addr = DBGC_DBGBUF_WRAP_AROUND, + .mask = 0xffffffff, + }, + .cur_frag = { + .addr = DBGC_CUR_DBGBUF_STATUS, + .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, + }, + }, + .mon_dbgi_regs = { + .write_ptr = { + .addr = DBGI_SRAM_FIFO_POINTERS, + .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, + }, + }, + .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, + .ucode_api_max = IWL_SC_UCODE_API_MAX, + .ucode_api_min = IWL_SC_UCODE_API_MIN, }; -#define IWL_DEVICE_BZ_COMMON \ - .ucode_api_max = IWL_SC_UCODE_API_MAX, \ - .ucode_api_min = IWL_SC_UCODE_API_MIN, \ - .led_mode = IWL_LED_RF_STATE, \ - .nvm_hw_section_num = 10, \ - .non_shared_ant = ANT_B, \ - .dccm_offset = IWL_SC_DCCM_OFFSET, \ - .dccm_len = IWL_SC_DCCM_LEN, \ - .dccm2_offset = IWL_SC_DCCM2_OFFSET, \ - .dccm2_len = IWL_SC_DCCM2_LEN, \ - .smem_offset = IWL_SC_SMEM_OFFSET, \ - .smem_len = IWL_SC_SMEM_LEN, \ - .apmg_not_supported = true, \ - .trans.mq_rx_supported = true, \ - .vht_mu_mimo_supported = true, \ - .mac_addr_from_csr = 0x30, \ - .nvm_ver = IWL_SC_NVM_VERSION, \ - .trans.rf_id = true, \ - .trans.gen2 = true, \ - .nvm_type = IWL_NVM_EXT, \ - .dbgc_supported = true, \ - .min_umac_error_event_table = 0xD0000, \ - .d3_debug_data_base_addr = 0x401000, \ - .d3_debug_data_length = 60 * 1024, \ - .mon_smem_regs = { \ - .write_ptr = { \ - .addr = LDBG_M2S_BUF_WPTR, \ - .mask = LDBG_M2S_BUF_WPTR_VAL_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = LDBG_M2S_BUF_WRAP_CNT, \ - .mask = LDBG_M2S_BUF_WRAP_CNT_VAL_MSK, \ - }, \ - }, \ - .trans.umac_prph_offset = 0x300000, \ - .trans.device_family = IWL_DEVICE_FAMILY_SC, \ - .trans.base_params = &iwl_sc_base_params, \ - .min_txq_size = 128, \ - .gp2_reg_addr = 0xd02c68, \ - .min_ba_txq_size = IWL_DEFAULT_QUEUE_SIZE_EHT, \ - .mon_dram_regs = { \ - .write_ptr = { \ - .addr = DBGC_CUR_DBGBUF_STATUS, \ - .mask = DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK, \ - }, \ - .cycle_cnt = { \ - .addr = DBGC_DBGBUF_WRAP_AROUND, \ - .mask = 0xffffffff, \ - }, \ - .cur_frag = { \ - .addr = DBGC_CUR_DBGBUF_STATUS, \ - .mask = DBGC_CUR_DBGBUF_STATUS_IDX_MSK, \ - }, \ - }, \ - .mon_dbgi_regs = { \ - .write_ptr = { \ - .addr = DBGI_SRAM_FIFO_POINTERS, \ - .mask = DBGI_SRAM_FIFO_POINTERS_WR_PTR_MSK, \ - }, \ - } - -#define IWL_DEVICE_SC \ - IWL_DEVICE_BZ_COMMON, \ - .uhb_supported = true, \ - .features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM, \ - .num_rbds = IWL_NUM_RBDS_SC_EHT, \ - .ht_params = &iwl_22000_ht_params - -/* - * This size was picked according to 8 MSDUs inside 512 A-MSDUs in an - * A-MPDU, with additional overhead to account for processing time. - */ -#define IWL_NUM_RBDS_SC_EHT (512 * 16) - -const struct iwl_cfg_trans_params iwl_sc_trans_cfg = { +const struct iwl_mac_cfg iwl_sc_mac_cfg = { .device_family = IWL_DEVICE_FAMILY_SC, - .base_params = &iwl_sc_base_params, + .base = &iwl_sc_base, .mq_rx_supported = true, - .rf_id = true, .gen2 = true, .integrated = true, .umac_prph_offset = 0x300000, @@ -142,27 +105,6 @@ const struct iwl_cfg_trans_params iwl_sc_trans_cfg = { .ltr_delay = IWL_CFG_TRANS_LTR_DELAY_2500US, }; -const char iwl_sc_name[] = "Intel(R) TBD Sc device"; - -const struct iwl_cfg iwl_cfg_sc = { - .fw_name_mac = "sc", - IWL_DEVICE_SC, -}; - -const char iwl_sc2_name[] = "Intel(R) TBD Sc2 device"; - -const struct iwl_cfg iwl_cfg_sc2 = { - .fw_name_mac = "sc2", - IWL_DEVICE_SC, -}; - -const char iwl_sc2f_name[] = "Intel(R) TBD Sc2f device"; - -const struct iwl_cfg iwl_cfg_sc2f = { - .fw_name_mac = "sc2f", - IWL_DEVICE_SC, -}; - IWL_FW_AND_PNVM(IWL_SC_A_FM_B_FW_PRE, IWL_SC_UCODE_API_MAX); IWL_FW_AND_PNVM(IWL_SC_A_FM_C_FW_PRE, IWL_SC_UCODE_API_MAX); MODULE_FIRMWARE(IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(IWL_SC_UCODE_API_MAX)); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h index a13add556a7b..1ebc7effcc2a 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/agn.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/agn.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2021, 2024 Intel Corporation + * Copyright (C) 2005-2014, 2021, 2024-2025 Intel Corporation */ #ifndef __iwl_agn_h__ #define __iwl_agn_h__ @@ -399,7 +399,7 @@ static inline void iwl_dvm_set_pmi(struct iwl_priv *priv, bool state) * later with iwl_free_nvm_data(). */ struct iwl_nvm_data * -iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const u8 *eeprom, size_t eeprom_size); int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h index 3f49c0bccb28..96ea6c8dfc89 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/commands.h @@ -1180,85 +1180,87 @@ struct iwl_dram_scratch { } __packed; struct iwl_tx_cmd { - /* - * MPDU byte count: - * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, - * + 8 byte IV for CCM or TKIP (not used for WEP) - * + Data payload - * + 8-byte MIC (not used for CCM/WEP) - * NOTE: Does not include Tx command bytes, post-MAC pad bytes, - * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i - * Range: 14-2342 bytes. - */ - __le16 len; - - /* - * MPDU or MSDU byte count for next frame. - * Used for fragmentation and bursting, but not 11n aggregation. - * Same as "len", but for next frame. Set to 0 if not applicable. - */ - __le16 next_frame_len; - - __le32 tx_flags; /* TX_CMD_FLG_* */ - - /* uCode may modify this field of the Tx command (in host DRAM!). - * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ - struct iwl_dram_scratch scratch; - - /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ - __le32 rate_n_flags; /* RATE_MCS_* */ - - /* Index of destination station in uCode's station table */ - u8 sta_id; - - /* Type of security encryption: CCM or TKIP */ - u8 sec_ctl; /* TX_CMD_SEC_* */ - - /* - * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial - * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for - * data frames, this field may be used to selectively reduce initial - * rate (via non-0 value) for special frames (e.g. management), while - * still supporting rate scaling for all frames. - */ - u8 initial_rate_index; - u8 reserved; - u8 key[16]; - __le16 next_frame_flags; - __le16 reserved2; - union { - __le32 life_time; - __le32 attempt; - } stop_time; - - /* Host DRAM physical address pointer to "scratch" in this command. - * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ - __le32 dram_lsb_ptr; - u8 dram_msb_ptr; - - u8 rts_retry_limit; /*byte 50 */ - u8 data_retry_limit; /*byte 51 */ - u8 tid_tspec; - union { - __le16 pm_frame_timeout; - __le16 attempt_duration; - } timeout; - - /* - * Duration of EDCA burst Tx Opportunity, in 32-usec units. - * Set this if txop time is not specified by HCCA protocol (e.g. by AP). - */ - __le16 driver_txop; - + /* New members MUST be added within the __struct_group() macro below. */ + __struct_group(iwl_tx_cmd_hdr, __hdr, __packed, + /* + * MPDU byte count: + * MAC header (24/26/30/32 bytes) + 2 bytes pad if 26/30 header size, + * + 8 byte IV for CCM or TKIP (not used for WEP) + * + Data payload + * + 8-byte MIC (not used for CCM/WEP) + * NOTE: Does not include Tx command bytes, post-MAC pad bytes, + * MIC (CCM) 8 bytes, ICV (WEP/TKIP/CKIP) 4 bytes, CRC 4 bytes.i + * Range: 14-2342 bytes. + */ + __le16 len; + + /* + * MPDU or MSDU byte count for next frame. + * Used for fragmentation and bursting, but not 11n aggregation. + * Same as "len", but for next frame. Set to 0 if not applicable. + */ + __le16 next_frame_len; + + __le32 tx_flags; /* TX_CMD_FLG_* */ + + /* uCode may modify this field of the Tx command (in host DRAM!). + * Driver must also set dram_lsb_ptr and dram_msb_ptr in this cmd. */ + struct iwl_dram_scratch scratch; + + /* Rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is cleared. */ + __le32 rate_n_flags; /* RATE_MCS_* */ + + /* Index of destination station in uCode's station table */ + u8 sta_id; + + /* Type of security encryption: CCM or TKIP */ + u8 sec_ctl; /* TX_CMD_SEC_* */ + + /* + * Index into rate table (see REPLY_TX_LINK_QUALITY_CMD) for initial + * Tx attempt, if TX_CMD_FLG_STA_RATE_MSK is set. Normally "0" for + * data frames, this field may be used to selectively reduce initial + * rate (via non-0 value) for special frames (e.g. management), while + * still supporting rate scaling for all frames. + */ + u8 initial_rate_index; + u8 reserved; + u8 key[16]; + __le16 next_frame_flags; + __le16 reserved2; + union { + __le32 life_time; + __le32 attempt; + } stop_time; + + /* Host DRAM physical address pointer to "scratch" in this command. + * Must be dword aligned. "0" in dram_lsb_ptr disables usage. */ + __le32 dram_lsb_ptr; + u8 dram_msb_ptr; + + u8 rts_retry_limit; /*byte 50 */ + u8 data_retry_limit; /*byte 51 */ + u8 tid_tspec; + union { + __le16 pm_frame_timeout; + __le16 attempt_duration; + } timeout; + + /* + * Duration of EDCA burst Tx Opportunity, in 32-usec units. + * Set this if txop time is not specified by HCCA protocol (e.g. by AP). + */ + __le16 driver_txop; + + ); /* * MAC header goes here, followed by 2 bytes padding if MAC header * length is 26 or 30 bytes, followed by payload data */ - union { - DECLARE_FLEX_ARRAY(u8, payload); - DECLARE_FLEX_ARRAY(struct ieee80211_hdr, hdr); - }; + struct ieee80211_hdr hdr[]; } __packed; +static_assert(offsetof(struct iwl_tx_cmd, hdr) == sizeof(struct iwl_tx_cmd_hdr), + "struct member likely outside of __struct_group()"); /* * TX command response is sent after *agn* transmission attempts. @@ -2312,7 +2314,7 @@ struct iwl_scan_cmd { /* For active scans (set to all-0s for passive scans). * Does not include payload. Must specify Tx rate; no rate scaling. */ - struct iwl_tx_cmd tx_cmd; + struct iwl_tx_cmd_hdr tx_cmd; /* For directed active scans (set to all-0s otherwise) */ struct iwl_ssid_ie direct_scan[PROBE_OPTION_MAX]; @@ -2423,7 +2425,7 @@ struct iwlagn_beacon_notif { */ struct iwl_tx_beacon_cmd { - struct iwl_tx_cmd tx; + struct iwl_tx_cmd_hdr tx; __le16 tim_idx; u8 tim_size; u8 reserved1; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c index b246dbd371b3..ec94c43ba28c 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/debugfs.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright (C) 2018 Intel Corporation + * Copyright (C) 2018, 2025 Intel Corporation *****************************************************************************/ #include <linux/slab.h> @@ -1870,7 +1870,7 @@ static ssize_t iwl_dbgfs_ucode_tracing_write(struct file *file, } } else { priv->event_log.ucode_trace = false; - del_timer_sync(&priv->ucode_trace); + timer_delete_sync(&priv->ucode_trace); } return count; @@ -2097,7 +2097,8 @@ static ssize_t iwl_dbgfs_protection_mode_read(struct file *file, char buf[40]; const size_t bufsz = sizeof(buf); - if (priv->cfg->ht_params) + /* HT devices also have at least one HT40 band */ + if (priv->cfg->ht_params.ht40_bands) pos += scnprintf(buf + pos, bufsz - pos, "use %s for aggregation\n", (priv->hw_params.use_rts_for_aggregation) ? @@ -2117,7 +2118,8 @@ static ssize_t iwl_dbgfs_protection_mode_write(struct file *file, int buf_size; int rts; - if (!priv->cfg->ht_params) + /* HT devices also have at least one HT40 band */ + if (!priv->cfg->ht_params.ht40_bands) return -EINVAL; memset(buf, 0, sizeof(buf)); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h index 4ac8b862ad41..25b24820466d 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/dev.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/dev.h @@ -2,6 +2,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014, 2020, 2023 Intel Corporation. All rights reserved. + * Copyright (C) 2025 Intel Corporation *****************************************************************************/ /* * Please use this file (dev.h) for driver implementation definitions. @@ -627,7 +628,7 @@ struct iwl_priv { struct iwl_trans *trans; struct device *dev; /* for debug prints only */ - const struct iwl_cfg *cfg; + const struct iwl_rf_cfg *cfg; const struct iwl_fw *fw; const struct iwl_dvm_cfg *lib; unsigned long status; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c index 48a8349680fc..3447ae0b160a 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/devices.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/devices.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. - * Copyright (C) 2019 Intel Corporation + * Copyright (C) 2019, 2025 Intel Corporation *****************************************************************************/ #include <linux/units.h> @@ -481,7 +481,7 @@ static void iwl6000_set_ct_threshold(struct iwl_priv *priv) /* NIC configuration for 6000 series */ static void iwl6000_nic_config(struct iwl_priv *priv) { - switch (priv->trans->trans_cfg->device_family) { + switch (priv->trans->mac_cfg->device_family) { case IWL_DEVICE_FAMILY_6005: case IWL_DEVICE_FAMILY_6030: case IWL_DEVICE_FAMILY_6000: diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c b/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c index cdc05f7e75a6..2423125e5284 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/eeprom.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2019, 2021, 2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2019, 2021, 2024-2025 Intel Corporation */ #include <linux/types.h> #include <linux/slab.h> @@ -151,7 +151,7 @@ static u16 iwl_eeprom_query16(const u8 *eeprom, size_t eeprom_size, int offset) { if (WARN_ON(offset + sizeof(u16) > eeprom_size)) return 0; - return le16_to_cpup((__le16 *)(eeprom + offset)); + return le16_to_cpup((const __le16 *)(eeprom + offset)); } static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size, @@ -204,8 +204,8 @@ static u32 eeprom_indirect_address(const u8 *eeprom, size_t eeprom_size, return (address & ADDRESS_MSK) + (offset << 1); } -static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size, - u32 offset) +static const void *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size, + u32 offset) { u32 address = eeprom_indirect_address(eeprom, eeprom_size, offset); @@ -218,10 +218,9 @@ static const u8 *iwl_eeprom_query_addr(const u8 *eeprom, size_t eeprom_size, static int iwl_eeprom_read_calib(const u8 *eeprom, size_t eeprom_size, struct iwl_nvm_data *data) { - struct iwl_eeprom_calib_hdr *hdr; + const struct iwl_eeprom_calib_hdr *hdr; - hdr = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, - EEPROM_CALIB_ALL); + hdr = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_CALIB_ALL); if (!hdr) return -ENODATA; data->calib_version = hdr->version; @@ -295,7 +294,7 @@ struct iwl_eeprom_enhanced_txpwr { } __packed; static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data, - struct iwl_eeprom_enhanced_txpwr *txp) + const struct iwl_eeprom_enhanced_txpwr *txp) { s8 result = 0; /* (.5 dBm) */ @@ -329,7 +328,7 @@ static s8 iwl_get_max_txpwr_half_dbm(const struct iwl_nvm_data *data, static void iwl_eeprom_enh_txp_read_element(struct iwl_nvm_data *data, - struct iwl_eeprom_enhanced_txpwr *txp, + const struct iwl_eeprom_enhanced_txpwr *txp, int n_channels, s8 max_txpower_avg) { int ch_idx; @@ -360,20 +359,18 @@ static void iwl_eeprom_enhanced_txpower(struct device *dev, const u8 *eeprom, size_t eeprom_size, int n_channels) { - struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; + const struct iwl_eeprom_enhanced_txpwr *txp_array, *txp; int idx, entries; - __le16 *txp_len; + const __le16 *txp_len; s8 max_txp_avg_halfdbm; BUILD_BUG_ON(sizeof(struct iwl_eeprom_enhanced_txpwr) != 8); /* the length is in 16-bit words, but we want entries */ - txp_len = (__le16 *)iwl_eeprom_query_addr(eeprom, eeprom_size, - EEPROM_TXP_SZ_OFFS); + txp_len = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_TXP_SZ_OFFS); entries = le16_to_cpup(txp_len) * 2 / EEPROM_TXP_ENTRY_LEN; - txp_array = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, - EEPROM_TXP_OFFS); + txp_array = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_TXP_OFFS); for (idx = 0; idx < entries; idx++) { txp = &txp_array[idx]; @@ -416,7 +413,7 @@ static void iwl_eeprom_enhanced_txpower(struct device *dev, } } -static void iwl_init_band_reference(const struct iwl_cfg *cfg, +static void iwl_init_band_reference(const struct iwl_rf_cfg *cfg, const u8 *eeprom, size_t eeprom_size, int eeprom_band, int *eeprom_ch_count, const struct iwl_eeprom_channel **ch_info, @@ -426,7 +423,7 @@ static void iwl_init_band_reference(const struct iwl_cfg *cfg, offset |= INDIRECT_ADDRESS | INDIRECT_REGULATORY; - *ch_info = (void *)iwl_eeprom_query_addr(eeprom, eeprom_size, offset); + *ch_info = iwl_eeprom_query_addr(eeprom, eeprom_size, offset); switch (eeprom_band) { case 1: /* 2.4GHz band */ @@ -510,7 +507,7 @@ static void iwl_mod_ht40_chan_info(struct device *dev, #define CHECK_AND_PRINT_I(x) \ ((eeprom_ch_info[ch_idx].flags & EEPROM_CHANNEL_##x) ? # x " " : "") -static int iwl_init_channel_map(struct device *dev, const struct iwl_cfg *cfg, +static int iwl_init_channel_map(struct device *dev, const struct iwl_rf_cfg *cfg, struct iwl_nvm_data *data, const u8 *eeprom, size_t eeprom_size) { @@ -749,7 +746,7 @@ static int iwl_nvm_is_otp(struct iwl_trans *trans) u32 otpgp; /* OTP only valid for CP/PP and after */ - switch (trans->hw_rev & CSR_HW_REV_TYPE_MSK) { + switch (trans->info.hw_rev & CSR_HW_REV_TYPE_MSK) { case CSR_HW_REV_TYPE_NONE: IWL_ERR(trans, "Unknown hardware type\n"); return -EIO; @@ -784,7 +781,7 @@ static int iwl_init_otp_access(struct iwl_trans *trans) * CSR auto clock gate disable bit - * this is only applicable for HW with OTP shadow RAM */ - if (trans->trans_cfg->base_params->shadow_ram_support) + if (trans->mac_cfg->base->shadow_ram_support) iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); @@ -905,7 +902,7 @@ static int iwl_find_otp_image(struct iwl_trans *trans, } /* more in the link list, continue */ usedblocks++; - } while (usedblocks <= trans->trans_cfg->base_params->max_ll_items); + } while (usedblocks <= trans->mac_cfg->base->max_ll_items); /* OTP has no valid blocks */ IWL_DEBUG_EEPROM(trans->dev, "OTP has no valid blocks\n"); @@ -938,7 +935,7 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size) if (nvm_is_otp < 0) return nvm_is_otp; - sz = trans->trans_cfg->base_params->eeprom_size; + sz = trans->mac_cfg->base->eeprom_size; IWL_DEBUG_EEPROM(trans->dev, "NVM size = %d\n", sz); e = kmalloc(sz, GFP_KERNEL); @@ -973,7 +970,7 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size) CSR_OTP_GP_REG_ECC_CORR_STATUS_MSK | CSR_OTP_GP_REG_ECC_UNCORR_STATUS_MSK); /* traversing the linked list if no shadow ram supported */ - if (!trans->trans_cfg->base_params->shadow_ram_support) { + if (!trans->mac_cfg->base->shadow_ram_support) { ret = iwl_find_otp_image(trans, &validblockaddr); if (ret) goto err_unlock; @@ -1027,7 +1024,7 @@ int iwl_read_eeprom(struct iwl_trans *trans, u8 **eeprom, size_t *eeprom_size) return ret; } -static void iwl_init_sbands(struct iwl_trans *trans, const struct iwl_cfg *cfg, +static void iwl_init_sbands(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, struct iwl_nvm_data *data, const u8 *eeprom, size_t eeprom_size) { @@ -1062,7 +1059,7 @@ static void iwl_init_sbands(struct iwl_trans *trans, const struct iwl_cfg *cfg, /* EEPROM data functions */ struct iwl_nvm_data * -iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const u8 *eeprom, size_t eeprom_size) { struct iwl_nvm_data *data; @@ -1098,14 +1095,14 @@ iwl_parse_eeprom_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, EEPROM_RAW_TEMPERATURE); if (!tmp) goto err_free; - data->raw_temperature = *(__le16 *)tmp; + data->raw_temperature = *(const __le16 *)tmp; tmp = iwl_eeprom_query_addr(eeprom, eeprom_size, EEPROM_KELVIN_TEMPERATURE); if (!tmp) goto err_free; - data->kelvin_temperature = *(__le16 *)tmp; - data->kelvin_voltage = *((__le16 *)tmp + 1); + data->kelvin_temperature = *(const __le16 *)tmp; + data->kelvin_voltage = *((const __le16 *)tmp + 1); radio_cfg = iwl_eeprom_query16(eeprom, eeprom_size, EEPROM_RADIO_CONFIG); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/led.c b/drivers/net/wireless/intel/iwlwifi/dvm/led.c index 5ca85d90a8d6..cec2ebdfd651 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/led.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright (C) 2019 Intel Corporation + * Copyright (C) 2019, 2025 Intel Corporation *****************************************************************************/ @@ -116,9 +116,9 @@ static int iwl_led_cmd(struct iwl_priv *priv, } led_cmd.on = iwl_blink_compensation(priv, on, - priv->trans->trans_cfg->base_params->led_compensation); + priv->trans->mac_cfg->base->led_compensation); led_cmd.off = iwl_blink_compensation(priv, off, - priv->trans->trans_cfg->base_params->led_compensation); + priv->trans->mac_cfg->base->led_compensation); ret = iwl_send_led_cmd(priv, &led_cmd); if (!ret) { diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c index 56d19a034c24..0771a46bd552 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/mac80211.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(C) 2018 - 2019, 2022 - 2024 Intel Corporation + * Copyright(C) 2018 - 2019, 2022 - 2025 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -96,7 +96,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); ieee80211_hw_set(hw, WANT_MONITOR_VIF); - if (priv->trans->max_skb_frags) + if (priv->trans->info.max_skb_frags) hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; hw->offchannel_tx_hw_queue = IWL_AUX_QUEUE; @@ -188,7 +188,7 @@ int iwlagn_mac_setup_register(struct iwl_priv *priv, priv->hw->wiphy->bands[NL80211_BAND_5GHZ] = &priv->nvm_data->bands[NL80211_BAND_5GHZ]; - hw->wiphy->hw_version = priv->trans->hw_id; + hw->wiphy->hw_version = priv->trans->info.hw_id; iwl_leds_init(priv); @@ -549,7 +549,7 @@ static int iwlagn_mac_resume(struct ieee80211_hw *hw) iwlagn_prepare_restart(priv); - memset((void *)&ctx->active, 0, sizeof(ctx->active)); + memset((void *)(uintptr_t)&ctx->active, 0, sizeof(ctx->active)); iwl_connection_init_rx_config(priv, ctx); iwlagn_set_rxon_chain(priv, ctx); @@ -1091,7 +1091,7 @@ static void iwlagn_mac_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto done; } - scd_queues = BIT(priv->trans->trans_cfg->base_params->num_of_queues) - 1; + scd_queues = BIT(priv->trans->mac_cfg->base->num_of_queues) - 1; scd_queues &= ~(BIT(IWL_IPAN_CMD_QUEUE_NUM) | BIT(IWL_DEFAULT_CMD_QUEUE_NUM)); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/main.c b/drivers/net/wireless/intel/iwlwifi/dvm/main.c index 30789ba06d9d..1d619384c629 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/main.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/main.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014, 2018 - 2022 Intel Corporation. All rights reserved. - * Copyright(c) 2024 Intel Corporation. All rights reserved. + * Copyright(c) 2024-2025 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH * * Portions of this file are derived from the ipw3945 project, as well @@ -824,11 +824,11 @@ int iwl_alive_start(struct iwl_priv *priv) iwlagn_send_tx_ant_config(priv, priv->nvm_data->valid_tx_ant); if (iwl_is_associated_ctx(ctx) && !priv->wowlan) { - struct iwl_rxon_cmd *active_rxon = - (struct iwl_rxon_cmd *)&ctx->active; + struct iwl_rxon_cmd *active = (void *)(uintptr_t)&ctx->active; + /* apply any changes in staging */ ctx->staging.filter_flags |= RXON_FILTER_ASSOC_MSK; - active_rxon->filter_flags &= ~RXON_FILTER_ASSOC_MSK; + active->filter_flags &= ~RXON_FILTER_ASSOC_MSK; } else { struct iwl_rxon_context *tmp; /* Initialize our rx_config data */ @@ -1082,8 +1082,8 @@ void iwl_cancel_deferred_work(struct iwl_priv *priv) cancel_work_sync(&priv->bt_full_concurrency); cancel_work_sync(&priv->bt_runtime_config); - del_timer_sync(&priv->statistics_periodic); - del_timer_sync(&priv->ucode_trace); + timer_delete_sync(&priv->statistics_periodic); + timer_delete_sync(&priv->ucode_trace); } static int iwl_init_drv(struct iwl_priv *priv) @@ -1137,9 +1137,10 @@ static void iwl_uninit_drv(struct iwl_priv *priv) static void iwl_set_hw_params(struct iwl_priv *priv) { - if (priv->cfg->ht_params) + /* there are no devices with HT but without HT40 on all bands */ + if (priv->cfg->ht_params.ht40_bands) priv->hw_params.use_rts_for_aggregation = - priv->cfg->ht_params->use_rts_for_aggregation; + priv->cfg->ht_params.use_rts_for_aggregation; /* Device-specific setup */ priv->lib->set_hw_params(priv); @@ -1173,8 +1174,9 @@ static int iwl_eeprom_init_hw_params(struct iwl_priv *priv) { struct iwl_nvm_data *data = priv->nvm_data; + /* all HT devices also have HT40 on at least one band */ if (data->sku_cap_11n_enable && - !priv->cfg->ht_params) { + !priv->cfg->ht_params.ht40_bands) { IWL_ERR(priv, "Invalid 11n configuration\n"); return -EINVAL; } @@ -1224,7 +1226,7 @@ static int iwl_nvm_check_version(struct iwl_nvm_data *data, } static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, - const struct iwl_cfg *cfg, + const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) { @@ -1233,7 +1235,6 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, struct iwl_op_mode *op_mode; u16 num_mac; u32 ucode_flags; - struct iwl_trans_config trans_cfg = {}; static const u8 no_reclaim_cmds[] = { REPLY_RX_PHY_CMD, REPLY_RX_MPDU_CMD, @@ -1248,7 +1249,8 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, ************************/ hw = iwl_alloc_all(); if (!hw) { - pr_err("%s: Cannot allocate network device\n", trans->name); + pr_err("%s: Cannot allocate network device\n", + trans->info.name); err = -ENOMEM; goto out; } @@ -1261,7 +1263,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, priv->cfg = cfg; priv->fw = fw; - switch (priv->trans->trans_cfg->device_family) { + switch (priv->trans->mac_cfg->device_family) { case IWL_DEVICE_FAMILY_1000: case IWL_DEVICE_FAMILY_100: priv->lib = &iwl_dvm_1000_cfg; @@ -1309,52 +1311,50 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, * Populate the state variables that the transport layer needs * to know about. */ - trans_cfg.op_mode = op_mode; - trans_cfg.no_reclaim_cmds = no_reclaim_cmds; - trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + BUILD_BUG_ON(sizeof(no_reclaim_cmds) > + sizeof(trans->conf.no_reclaim_cmds)); + memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds, + sizeof(no_reclaim_cmds)); switch (iwlwifi_mod_params.amsdu_size) { case IWL_AMSDU_DEF: case IWL_AMSDU_4K: - trans_cfg.rx_buf_size = IWL_AMSDU_4K; + trans->conf.rx_buf_size = IWL_AMSDU_4K; break; case IWL_AMSDU_8K: - trans_cfg.rx_buf_size = IWL_AMSDU_8K; + trans->conf.rx_buf_size = IWL_AMSDU_8K; break; case IWL_AMSDU_12K: default: - trans_cfg.rx_buf_size = IWL_AMSDU_4K; + trans->conf.rx_buf_size = IWL_AMSDU_4K; pr_err("Unsupported amsdu_size: %d\n", iwlwifi_mod_params.amsdu_size); } - trans_cfg.command_groups = iwl_dvm_groups; - trans_cfg.command_groups_size = ARRAY_SIZE(iwl_dvm_groups); + trans->conf.command_groups = iwl_dvm_groups; + trans->conf.command_groups_size = ARRAY_SIZE(iwl_dvm_groups); - trans_cfg.cmd_fifo = IWLAGN_CMD_FIFO_NUM; - trans_cfg.cb_data_offs = offsetof(struct ieee80211_tx_info, - driver_data[2]); + trans->conf.cmd_fifo = IWLAGN_CMD_FIFO_NUM; + trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info, + driver_data[2]); WARN_ON(sizeof(priv->transport_queue_stop) * BITS_PER_BYTE < - priv->trans->trans_cfg->base_params->num_of_queues); + priv->trans->mac_cfg->base->num_of_queues); ucode_flags = fw->ucode_capa.flags; if (ucode_flags & IWL_UCODE_TLV_FLAGS_PAN) { priv->sta_key_max_num = STA_KEY_MAX_NUM_PAN; - trans_cfg.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; + trans->conf.cmd_queue = IWL_IPAN_CMD_QUEUE_NUM; } else { priv->sta_key_max_num = STA_KEY_MAX_NUM; - trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; + trans->conf.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; } - /* Configure transport layer */ - iwl_trans_configure(priv->trans, &trans_cfg); + trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD; + trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); - trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; - trans->rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); - trans->command_groups = trans_cfg.command_groups; - trans->command_groups_size = trans_cfg.command_groups_size; + iwl_trans_op_mode_enter(priv->trans, op_mode); /* At this point both hw and priv are allocated. */ @@ -1378,18 +1378,18 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, * 2. Read REV register ***********************/ IWL_INFO(priv, "Detected %s, REV=0x%X\n", - priv->trans->name, priv->trans->hw_rev); + priv->trans->info.name, priv->trans->info.hw_rev); err = iwl_trans_start_hw(priv->trans); if (err) - goto out_free_hw; + goto out_leave_trans; /* Read the EEPROM */ err = iwl_read_eeprom(priv->trans, &priv->eeprom_blob, &priv->eeprom_blob_size); if (err) { IWL_ERR(priv, "Unable to init EEPROM\n"); - goto out_free_hw; + goto out_leave_trans; } /* Reset chip to save power until we load uCode during "up". */ @@ -1437,10 +1437,7 @@ static struct iwl_op_mode *iwl_op_mode_dvm_start(struct iwl_trans *trans, * packaging bug or due to the eeprom check above */ priv->sta_key_max_num = STA_KEY_MAX_NUM; - trans_cfg.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; - - /* Configure transport layer again*/ - iwl_trans_configure(priv->trans, &trans_cfg); + trans->conf.cmd_queue = IWL_DEFAULT_CMD_QUEUE_NUM; } /******************* @@ -1508,6 +1505,8 @@ out_free_eeprom_blob: kfree(priv->eeprom_blob); out_free_eeprom: kfree(priv->nvm_data); +out_leave_trans: + iwl_trans_op_mode_leave(priv->trans); out_free_hw: ieee80211_free_hw(priv->hw); out: @@ -1992,7 +1991,7 @@ static void iwl_nic_config(struct iwl_op_mode *op_mode) /* SKU Control */ iwl_trans_set_bits_mask(priv->trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP_DASH, - CSR_HW_REV_STEP_DASH(priv->trans->hw_rev)); + CSR_HW_REV_STEP_DASH(priv->trans->info.hw_rev)); /* write radio config values to register */ if (priv->nvm_data->radio_cfg_type <= EEPROM_RF_CONFIG_TYPE_MAX) { diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/power.c b/drivers/net/wireless/intel/iwlwifi/dvm/power.c index 6d16a7105656..6b42d6e5f30f 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/power.c @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2007 - 2014 Intel Corporation. All rights reserved. - * Copyright (C) 2019 Intel Corporation + * Copyright (C) 2019, 2025 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portions of the ieee80211 subsystem header files. @@ -64,32 +64,32 @@ struct iwl_power_vec_entry { /* for DTIM period 0 through IWL_DTIM_RANGE_0_MAX */ /* DTIM 0 - 2 */ static const struct iwl_power_vec_entry range_0[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF)}, 1}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF)}, 2} + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 1, 2, 2, 0xFF), 0}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 2, 2, 0xFF), 0}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 2, 2, 2, 0xFF), 0}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 2, 4, 4, 0xFF), 0}, 1}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 2, 4, 6, 0xFF), 0}, 2} }; /* for DTIM period IWL_DTIM_RANGE_0_MAX + 1 through IWL_DTIM_RANGE_1_MAX */ /* DTIM 3 - 10 */ static const struct iwl_power_vec_entry range_1[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10)}, 1}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10)}, 2} + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 4), 0}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(1, 2, 3, 4, 7), 0}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 4, 6, 7, 9), 0}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 4, 6, 9, 10), 0}, 1}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(2, 4, 6, 10, 10), 0}, 2} }; /* for DTIM period > IWL_DTIM_RANGE_1_MAX */ /* DTIM 11 - */ static const struct iwl_power_vec_entry range_2[IWL_POWER_NUM] = { - {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF)}, 0}, - {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, - {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF)}, 0}, - {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF)}, 0} + {{SLP, SLP_TOUT(200), SLP_TOUT(500), SLP_VEC(1, 2, 3, 4, 0xFF), 0}, 0}, + {{SLP, SLP_TOUT(200), SLP_TOUT(300), SLP_VEC(2, 4, 6, 7, 0xFF), 0}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(100), SLP_VEC(2, 7, 9, 9, 0xFF), 0}, 0}, + {{SLP, SLP_TOUT(50), SLP_TOUT(25), SLP_VEC(2, 7, 9, 9, 0xFF), 0}, 0}, + {{SLP, SLP_TOUT(25), SLP_TOUT(25), SLP_VEC(4, 7, 10, 10, 0xFF), 0}, 0} }; /* advance power management */ @@ -196,7 +196,7 @@ static void iwl_static_sleep_cmd(struct iwl_priv *priv, else cmd->flags &= ~IWL_POWER_SLEEP_OVER_DTIM_MSK; - if (priv->trans->trans_cfg->base_params->shadow_reg_enable) + if (priv->trans->mac_cfg->base->shadow_reg_enable) cmd->flags |= IWL_POWER_SHADOW_REG_ENA; else cmd->flags &= ~IWL_POWER_SHADOW_REG_ENA; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c index 7f67e602940c..5f8b60824043 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rx.c @@ -3,7 +3,7 @@ * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH - * Copyright(c) 2018, 2020-2021 Intel Corporation + * Copyright(c) 2018, 2020-2021, 2025 Intel Corporation * * Portions of this file are derived from the ipw3945 project, as well * as portionhelp of the ieee80211 subsystem header files. @@ -50,7 +50,7 @@ static void iwlagn_rx_csa(struct iwl_priv *priv, struct iwl_rx_cmd_buffer *rxb) * See iwlagn_mac_channel_switch. */ struct iwl_rxon_context *ctx = &priv->contexts[IWL_RXON_CTX_BSS]; - struct iwl_rxon_cmd *rxon = (void *)&ctx->active; + struct iwl_rxon_cmd *rxon = (void *)(uintptr_t)&ctx->active; if (!test_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status)) return; @@ -643,8 +643,8 @@ static void iwlagn_pass_packet_to_mac80211(struct iwl_priv *priv, fraglen = len - hdrlen; if (fraglen) { - int offset = (void *)hdr + hdrlen - - rxb_addr(rxb) + rxb_offset(rxb); + int offset = (u8 *)hdr + hdrlen - + (u8 *)rxb_addr(rxb) + rxb_offset(rxb); skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, fraglen, rxb->truesize); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c index f80cce37e2c0..2d3c1627f283 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/rxon.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-only /****************************************************************************** * - * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. + * Copyright(c) 2003 - 2014, 2025 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH *****************************************************************************/ @@ -341,7 +341,7 @@ static int iwlagn_rxon_disconn(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int ret; - struct iwl_rxon_cmd *active = (void *)&ctx->active; + struct iwl_rxon_cmd *active = (void *)(uintptr_t)&ctx->active; if (ctx->ctxid == IWL_RXON_CTX_BSS) { ret = iwlagn_disable_bss(priv, ctx, &ctx->staging); @@ -441,7 +441,7 @@ static int iwlagn_rxon_connect(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { int ret; - struct iwl_rxon_cmd *active = (void *)&ctx->active; + struct iwl_rxon_cmd *active = (void *)(uintptr_t)&ctx->active; /* RXON timing must be before associated RXON */ if (ctx->ctxid == IWL_RXON_CTX_BSS) { @@ -1023,7 +1023,7 @@ static void iwl_calc_basic_rates(struct iwl_priv *priv, int iwlagn_commit_rxon(struct iwl_priv *priv, struct iwl_rxon_context *ctx) { /* cast away the const for active_rxon in this function */ - struct iwl_rxon_cmd *active = (void *)&ctx->active; + struct iwl_rxon_cmd *active = (void *)(uintptr_t)&ctx->active; bool new_assoc = !!(ctx->staging.filter_flags & RXON_FILTER_ASSOC_MSK); int ret; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c index 43e8d04d5a8b..98f0949b3683 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.c @@ -124,17 +124,6 @@ enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv) return restriction->tx_stream; } -enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv) -{ - struct iwl_tt_mgmt *tt = &priv->thermal_throttle; - struct iwl_tt_restriction *restriction; - - if (!priv->thermal_throttle.advanced_tt) - return IWL_ANT_OK_MULTI; - restriction = tt->restriction + tt->state; - return restriction->rx_stream; -} - #define CT_KILL_EXIT_DURATION (5) /* 5 seconds duration */ #define CT_KILL_WAITING_DURATION (300) /* 300ms duration */ @@ -268,7 +257,7 @@ static void iwl_legacy_tt_handler(struct iwl_priv *priv, s32 temp, bool force) tt->tt_previous_temp = temp; #endif /* stop ct_kill_waiting_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + timer_delete_sync(&priv->thermal_throttle.ct_kill_waiting_tm); if (tt->state != old_state) { switch (tt->state) { case IWL_TI_0: @@ -389,7 +378,7 @@ static void iwl_advance_tt_handler(struct iwl_priv *priv, s32 temp, bool force) } } /* stop ct_kill_waiting_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + timer_delete_sync(&priv->thermal_throttle.ct_kill_waiting_tm); if (changed) { if (tt->state >= IWL_TI_1) { /* force PI = IWL_POWER_INDEX_5 in the case of TI > 0 */ @@ -517,7 +506,7 @@ static void iwl_bg_ct_exit(struct work_struct *work) return; /* stop ct_kill_exit_tm timer */ - del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + timer_delete_sync(&priv->thermal_throttle.ct_kill_exit_tm); if (tt->state == IWL_TI_CT_KILL) { IWL_ERR(priv, @@ -651,9 +640,9 @@ void iwl_tt_exit(struct iwl_priv *priv) struct iwl_tt_mgmt *tt = &priv->thermal_throttle; /* stop ct_kill_exit_tm timer if activated */ - del_timer_sync(&priv->thermal_throttle.ct_kill_exit_tm); + timer_delete_sync(&priv->thermal_throttle.ct_kill_exit_tm); /* stop ct_kill_waiting_tm timer if activated */ - del_timer_sync(&priv->thermal_throttle.ct_kill_waiting_tm); + timer_delete_sync(&priv->thermal_throttle.ct_kill_waiting_tm); cancel_work_sync(&priv->tt_work); cancel_work_sync(&priv->ct_enter); cancel_work_sync(&priv->ct_exit); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h index 4af792d41dce..198f934a0d16 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tt.h +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tt.h @@ -100,7 +100,6 @@ u8 iwl_tt_current_power_mode(struct iwl_priv *priv); bool iwl_tt_is_low_power_state(struct iwl_priv *priv); bool iwl_ht_enabled(struct iwl_priv *priv); enum iwl_antenna_ok iwl_tx_ant_restriction(struct iwl_priv *priv); -enum iwl_antenna_ok iwl_rx_ant_restriction(struct iwl_priv *priv); void iwl_tt_enter_ct_kill(struct iwl_priv *priv); void iwl_tt_exit_ct_kill(struct iwl_priv *priv); void iwl_tt_handler(struct iwl_priv *priv); diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c index 111ed1873006..24fefa0e8148 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/tx.c @@ -3,7 +3,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright (C) 2019 Intel Corporation - * Copyright (C) 2023 Intel Corporation + * Copyright (C) 2023, 2025 Intel Corporation *****************************************************************************/ #include <linux/kernel.h> @@ -463,7 +463,7 @@ static int iwlagn_alloc_agg_txq(struct iwl_priv *priv, int mq) int q; for (q = IWLAGN_FIRST_AMPDU_QUEUE; - q < priv->trans->trans_cfg->base_params->num_of_queues; q++) { + q < priv->trans->mac_cfg->base->num_of_queues; q++) { if (!test_and_set_bit(q, priv->agg_q_alloc)) { priv->queue_to_mac80211[q] = mq; return q; @@ -1277,7 +1277,7 @@ void iwlagn_rx_reply_compressed_ba(struct iwl_priv *priv, * (in Tx queue's circular buffer) of first TFD/frame in window */ u16 ba_resp_scd_ssn = le16_to_cpu(ba_resp->scd_ssn); - if (scd_flow >= priv->trans->trans_cfg->base_params->num_of_queues) { + if (scd_flow >= priv->trans->mac_cfg->base->num_of_queues) { IWL_ERR(priv, "BUG_ON scd_flow is bigger than number of queues\n"); return; diff --git a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c index bb13ca5d666c..ac90191a3973 100644 --- a/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c +++ b/drivers/net/wireless/intel/iwlwifi/dvm/ucode.c @@ -3,6 +3,7 @@ * * Copyright(c) 2008 - 2014 Intel Corporation. All rights reserved. * Copyright(c) 2015 Intel Deutschland GmbH + * Copyright (C) 2025 Intel Corporation *****************************************************************************/ #include <linux/kernel.h> @@ -222,7 +223,7 @@ static int iwl_alive_notify(struct iwl_priv *priv) int ret; int i; - iwl_trans_fw_alive(priv->trans, 0); + iwl_trans_fw_alive(priv->trans); if (priv->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_PAN && priv->nvm_data->sku_cap_ipan_enable) { @@ -293,15 +294,10 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv, { struct iwl_notification_wait alive_wait; struct iwl_alive_data alive_data; - const struct fw_img *fw; int ret; enum iwl_ucode_type old_type; static const u16 alive_cmd[] = { REPLY_ALIVE }; - fw = iwl_get_ucode_image(priv->fw, ucode_type); - if (WARN_ON(!fw)) - return -EINVAL; - old_type = priv->cur_ucode; priv->cur_ucode = ucode_type; priv->ucode_loaded = false; @@ -310,7 +306,7 @@ int iwl_load_ucode_wait_alive(struct iwl_priv *priv, alive_cmd, ARRAY_SIZE(alive_cmd), iwl_alive_fn, &alive_data); - ret = iwl_trans_start_fw(priv->trans, fw, false); + ret = iwl_trans_start_fw(priv->trans, priv->fw, ucode_type, false); if (ret) { priv->cur_ucode = old_type; iwl_remove_notification(&priv->notif_wait, &alive_wait); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c index efa7b673ebc7..bee7d92293b8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2019-2024 Intel Corporation + * Copyright (C) 2019-2025 Intel Corporation */ #include <linux/uuid.h> #include "iwl-drv.h" @@ -847,12 +847,12 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) if (IS_ERR(data)) return PTR_ERR(data); - /* try to read ppag table rev 3, 2 or 1 (all have the same data size) */ + /* try to read ppag table rev 1 to 4 (all have the same data size) */ wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_PPAG_WIFI_DATA_SIZE_V2, &tbl_rev); if (!IS_ERR(wifi_pkg)) { - if (tbl_rev >= 1 && tbl_rev <= 3) { + if (tbl_rev >= 1 && tbl_rev <= 4) { num_sub_bands = IWL_NUM_SUB_BANDS_V2; IWL_DEBUG_RADIO(fwrt, "Reading PPAG table (tbl_rev=%d)\n", @@ -882,7 +882,7 @@ int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) goto out_free; read_table: - fwrt->ppag_ver = tbl_rev; + fwrt->ppag_bios_rev = tbl_rev; flags = &wifi_pkg->package.elements[1]; if (flags->type != ACPI_TYPE_INTEGER) { @@ -891,7 +891,7 @@ read_table: } fwrt->ppag_flags = iwl_bios_get_ppag_flags(flags->integer.value, - fwrt->ppag_ver); + fwrt->ppag_bios_rev); /* * read, verify gain values and save them into the PPAG table. @@ -912,6 +912,7 @@ read_table: } } + fwrt->ppag_bios_source = BIOS_SOURCE_ACPI; ret = 0; out_free: @@ -919,40 +920,39 @@ out_free: return ret; } -void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, - struct iwl_phy_specific_cfg *filters) +int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt) { + struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters; struct iwl_phy_specific_cfg tmp = {}; - union acpi_object *wifi_pkg, *data; + union acpi_object *wifi_pkg, *data __free(kfree); int tbl_rev, i; data = iwl_acpi_get_object(fwrt->dev, ACPI_WPFC_METHOD); if (IS_ERR(data)) - return; + return PTR_ERR(data); wifi_pkg = iwl_acpi_get_wifi_pkg(fwrt->dev, data, ACPI_WPFC_WIFI_DATA_SIZE, &tbl_rev); if (IS_ERR(wifi_pkg)) - goto out_free; + return PTR_ERR(wifi_pkg); if (tbl_rev != 0) - goto out_free; + return -EINVAL; BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) != ACPI_WPFC_WIFI_DATA_SIZE - 1); for (i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) { if (wifi_pkg->package.elements[i + 1].type != ACPI_TYPE_INTEGER) - goto out_free; + return -EINVAL; tmp.filter_cfg_chains[i] = cpu_to_le32(wifi_pkg->package.elements[i + 1].integer.value); } IWL_DEBUG_RADIO(fwrt, "Loaded WPFC filter config from ACPI\n"); *filters = tmp; -out_free: - kfree(data); + return 0; } IWL_EXPORT_SYMBOL(iwl_acpi_get_phy_filters); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h index e50b93472dd2..68d8fb5f6357 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/acpi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/acpi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2023 Intel Corporation + * Copyright (C) 2018-2023, 2025 Intel Corporation */ #ifndef __iwl_fw_acpi__ #define __iwl_fw_acpi__ @@ -180,8 +180,7 @@ int iwl_acpi_get_tas_table(struct iwl_fw_runtime *fwrt, int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt); -void iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt, - struct iwl_phy_specific_cfg *filters); +int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt); void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt); @@ -244,8 +243,10 @@ static inline int iwl_acpi_get_ppag_table(struct iwl_fw_runtime *fwrt) return -ENOENT; } -/* macro since the second argument doesn't always exist */ -#define iwl_acpi_get_phy_filters(fwrt, filters) do { } while (0) +static inline int iwl_acpi_get_phy_filters(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} static inline void iwl_acpi_get_guid_lock_status(struct iwl_fw_runtime *fwrt) { diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h index ebe85fdf08d3..3ce477c248ce 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/alive.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2020-2021, 2024 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2020-2021, 2024-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -112,12 +112,22 @@ struct iwl_alive_ntf_v6 { struct iwl_imr_alive_info imr; } __packed; /* UCODE_ALIVE_NTFY_API_S_VER_6 */ +struct iwl_alive_ntf { + __le16 status; + __le16 flags; + struct iwl_lmac_alive lmac_data[2]; + struct iwl_umac_alive umac_data; + struct iwl_sku_id sku_id; + struct iwl_imr_alive_info imr; + __le64 platform_id; +} __packed; /* UCODE_ALIVE_NTFY_API_S_VER_8 */ + /** * enum iwl_extended_cfg_flags - commands driver may send before * finishing init flow * @IWL_INIT_DEBUG_CFG: driver is going to send debug config command * @IWL_INIT_NVM: driver is going to send NVM_ACCESS commands - * @IWL_INIT_PHY: driver is going to send PHY_DB commands + * @IWL_INIT_PHY: driver is going to send the PHY_CONFIGURATION_CMD */ enum iwl_extended_cfg_flags { IWL_INIT_DEBUG_CFG, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h index 34a1f97653c0..1c86a858aaab 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/commands.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2022, 2024 Intel Corporation + * Copyright (C) 2018-2022, 2024-2025 Intel Corporation */ #ifndef __iwl_fw_api_commands_h__ #define __iwl_fw_api_commands_h__ @@ -145,8 +145,8 @@ enum iwl_legacy_cmds { REMOVE_STA = 0x19, /** - * @TX_CMD: uses &struct iwl_tx_cmd or &struct iwl_tx_cmd_gen2 or - * &struct iwl_tx_cmd_gen3, + * @TX_CMD: uses &struct iwl_tx_cmd_v6 or &struct iwl_tx_cmd_v9 or + * &struct iwl_tx_cmd, * response in &struct iwl_tx_resp or * &struct iwl_tx_resp_v3 */ @@ -502,11 +502,16 @@ enum iwl_legacy_cmds { /** * @DTS_MEASUREMENT_NOTIFICATION: * &struct iwl_dts_measurement_notif_v1 or - * &struct iwl_dts_measurement_notif_v2 + * &struct iwl_dts_measurement_notif */ DTS_MEASUREMENT_NOTIFICATION = 0xdd, /** + * @DEBUG_HOST_COMMAND: &struct iwl_dhc_cmd + */ + DEBUG_HOST_COMMAND = 0xf1, + + /** * @LDBG_CONFIG_CMD: configure continuous trace recording */ LDBG_CONFIG_CMD = 0xf6, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/context.h b/drivers/net/wireless/intel/iwlwifi/fw/api/context.h index a9fa5f054ce0..464eed9b5e71 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/context.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/context.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2022 Intel Corporation + * Copyright (C) 2012-2014, 2022, 2024 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -14,6 +14,9 @@ * @FW_CTXT_COLOR_POS: position of the color * @FW_CTXT_COLOR_MSK: mask of the color * @FW_CTXT_INVALID: value used to indicate unused/invalid + * @FW_CTXT_ID_INVALID: value used to indicate unused/invalid. This can be + * used with newer firmware which no longer use the color. Typically, + * firmware versions supported by iwlmld can use this value. */ enum iwl_ctxt_id_and_color { FW_CTXT_ID_POS = 0, @@ -21,6 +24,7 @@ enum iwl_ctxt_id_and_color { FW_CTXT_COLOR_POS = 8, FW_CTXT_COLOR_MSK = 0xff << FW_CTXT_COLOR_POS, FW_CTXT_INVALID = 0xffffffff, + FW_CTXT_ID_INVALID = 0xff, }; #define FW_CMD_ID_AND_COLOR(_id, _color) (((_id) << FW_CTXT_ID_POS) |\ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h index c2362bc786b2..9c271ea67155 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/d3.h @@ -1006,7 +1006,7 @@ struct iwl_wowlan_wake_pkt_notif { * struct iwl_mvm_d3_end_notif - d3 end notification * @flags: See &enum iwl_d0i3_flags */ -struct iwl_mvm_d3_end_notif { +struct iwl_d3_end_notif { __le32 flags; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h index 570a3f722510..9c88bb280609 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/datapath.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation * Copyright (C) 2012-2014, 2018-2022 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH @@ -91,8 +91,14 @@ enum iwl_data_path_subcmd_ids { SEC_KEY_CMD = 0x18, /** + * @OMI_SEND_STATUS_NOTIF: notification after OMI was sent + * uses &struct iwl_omi_send_status_notif + */ + OMI_SEND_STATUS_NOTIF = 0xF2, + + /** * @ESR_MODE_NOTIF: notification to recommend/force a wanted esr mode, - * uses &struct iwl_mvm_esr_mode_notif + * uses &struct iwl_esr_mode_notif or &struct iwl_esr_mode_notif_v1 */ ESR_MODE_NOTIF = 0xF3, @@ -688,4 +694,13 @@ struct iwl_sec_key_cmd { } __packed u; /* SEC_KEY_OPERATION_API_U_VER_1 */ } __packed; /* SEC_KEY_CMD_API_S_VER_1 */ +/** + * struct iwl_omi_send_status_notif - OMI status notification + * @success: indicates that the OMI was sent successfully + * (currently always set) + */ +struct iwl_omi_send_status_notif { + __le32 success; +} __packed; /* OMI_SEND_STATUS_NTFY_API_S_VER_1 */ + #endif /* __iwl_fw_api_datapath_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h index 550de6db1834..3173fa96cb48 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dbg-tlv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #ifndef __iwl_fw_dbg_tlv_h__ #define __iwl_fw_dbg_tlv_h__ @@ -527,6 +527,8 @@ enum iwl_fw_ini_time_point { * @IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA: override trigger data. * Append otherwise * @IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD: send cmd once dump collected + * @IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET: split this dump into regions + * before and after the reset handshake */ enum iwl_fw_ini_trigger_apply_policy { IWL_FW_INI_APPLY_POLICY_MATCH_TIME_POINT = BIT(0), @@ -535,6 +537,7 @@ enum iwl_fw_ini_trigger_apply_policy { IWL_FW_INI_APPLY_POLICY_OVERRIDE_CFG = BIT(9), IWL_FW_INI_APPLY_POLICY_OVERRIDE_DATA = BIT(10), IWL_FW_INI_APPLY_POLICY_DUMP_COMPLETE_CMD = BIT(16), + IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET = BIT(17), }; /** @@ -556,12 +559,14 @@ enum iwl_fw_ini_trigger_reset_fw_policy { * @IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT: OS has no limit of dump size * @IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB: mini dump only 600KB region dump * @IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB: mini dump 5MB size dump + * @IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET: dump this region before reset + * handshake (if requested by %IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET) */ enum iwl_fw_ini_dump_policy { IWL_FW_INI_DEBUG_DUMP_POLICY_NO_LIMIT = BIT(0), IWL_FW_INI_DEBUG_DUMP_POLICY_MAX_LIMIT_600KB = BIT(1), IWL_FW_IWL_DEBUG_DUMP_POLICY_MAX_LIMIT_5MB = BIT(2), - + IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET = BIT(3), }; /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h index aa88e91d117e..0cf1e5124fba 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/debug.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -51,7 +51,7 @@ enum iwl_debug_cmds { /** * @GET_TAS_STATUS: * sends command to fw to get TAS status - * the response is &struct iwl_mvm_tas_status_resp + * the response is &struct iwl_tas_status_resp */ GET_TAS_STATUS = 0xA, /** @@ -439,25 +439,20 @@ struct iwl_dbg_dump_complete_cmd { __le32 tp_data; } __packed; /* FW_DUMP_COMPLETE_CMD_API_S_VER_1 */ -#define TAS_LMAC_BAND_HB 0 -#define TAS_LMAC_BAND_LB 1 -#define TAS_LMAC_BAND_UHB 2 -#define TAS_LMAC_BAND_INVALID 3 - /** - * struct iwl_mvm_tas_status_per_mac - tas status per lmac + * struct iwl_tas_status_per_mac - tas status per lmac * @static_status: tas statically enabled or disabled per lmac - TRUE/FALSE * @static_dis_reason: TAS static disable reason, uses - * &enum iwl_mvm_tas_statically_disabled_reason + * &enum iwl_tas_statically_disabled_reason * @dynamic_status: Current TAS status. uses - * &enum iwl_mvm_tas_dyna_status + * &enum iwl_tas_dyna_status * @near_disconnection: is TAS currently near disconnection per lmac? - TRUE/FALSE * @max_reg_pwr_limit: Regulatory power limits in dBm * @sar_limit: SAR limits per lmac in dBm * @band: Band per lmac * @reserved: reserved */ -struct iwl_mvm_tas_status_per_mac { +struct iwl_tas_status_per_mac { u8 static_status; u8 static_dis_reason; u8 dynamic_status; @@ -466,35 +461,35 @@ struct iwl_mvm_tas_status_per_mac { __le16 sar_limit; u8 band; u8 reserved[3]; -} __packed; /*DEBUG_GET_TAS_STATUS_PER_MAC_S_VER_1*/ +} __packed; /* DEBUG_GET_TAS_STATUS_PER_MAC_S_VER_1 */ /** - * struct iwl_mvm_tas_status_resp - Response to GET_TAS_STATUS + * struct iwl_tas_status_resp - Response to GET_TAS_STATUS * @tas_fw_version: TAS FW version * @is_uhb_for_usa_enable: is UHB enabled in USA? - TRUE/FALSE * @curr_mcc: current mcc * @block_list: country block list * @tas_status_mac: TAS status per lmac, uses - * &struct iwl_mvm_tas_status_per_mac + * &struct iwl_tas_status_per_mac * @in_dual_radio: is TAS in dual radio? - TRUE/FALSE * @uhb_allowed_flags: see &enum iwl_tas_uhb_allowed_flags. * This member is valid only when fw has * %IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT capability. * @reserved: reserved */ -struct iwl_mvm_tas_status_resp { +struct iwl_tas_status_resp { u8 tas_fw_version; u8 is_uhb_for_usa_enable; __le16 curr_mcc; __le16 block_list[16]; - struct iwl_mvm_tas_status_per_mac tas_status_mac[2]; + struct iwl_tas_status_per_mac tas_status_mac[2]; u8 in_dual_radio; u8 uhb_allowed_flags; u8 reserved[2]; -} __packed; /*DEBUG_GET_TAS_STATUS_RSP_API_S_VER_3*/ +} __packed; /* DEBUG_GET_TAS_STATUS_RSP_API_S_VER_3 */ /** - * enum iwl_mvm_tas_dyna_status - TAS current running status + * enum iwl_tas_dyna_status - TAS current running status * @TAS_DYNA_INACTIVE: TAS status is inactive * @TAS_DYNA_INACTIVE_MVM_MODE: TAS is disabled due because FW is in MVM mode * or is in softap mode. @@ -507,7 +502,7 @@ struct iwl_mvm_tas_status_resp { * @TAS_DYNA_ACTIVE: TAS is currently active * @TAS_DYNA_STATUS_MAX: TAS status max value */ -enum iwl_mvm_tas_dyna_status { +enum iwl_tas_dyna_status { TAS_DYNA_INACTIVE, TAS_DYNA_INACTIVE_MVM_MODE, TAS_DYNA_INACTIVE_TRIGGER_MODE, @@ -516,19 +511,22 @@ enum iwl_mvm_tas_dyna_status { TAS_DYNA_ACTIVE, TAS_DYNA_STATUS_MAX, -}; /*_TAS_DYNA_STATUS_E*/ +}; /** - * enum iwl_mvm_tas_statically_disabled_reason - TAS statically disabled reason + * enum iwl_tas_statically_disabled_reason - TAS statically disabled reason * @TAS_DISABLED_DUE_TO_BIOS: TAS is disabled because TAS is disabled in BIOS * @TAS_DISABLED_DUE_TO_SAR_6DBM: TAS is disabled because SAR limit is less than 6 Dbm * @TAS_DISABLED_REASON_INVALID: TAS disable reason is invalid + * @TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID: TAS is disabled due to + * table source invalid * @TAS_DISABLED_REASON_MAX: TAS disable reason max value */ -enum iwl_mvm_tas_statically_disabled_reason { +enum iwl_tas_statically_disabled_reason { TAS_DISABLED_DUE_TO_BIOS, TAS_DISABLED_DUE_TO_SAR_6DBM, TAS_DISABLED_REASON_INVALID, + TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID, TAS_DISABLED_REASON_MAX, }; /*_TAS_STATICALLY_DISABLED_REASON_E*/ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h new file mode 100644 index 000000000000..b6d79c678cd8 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/dhc.h @@ -0,0 +1,226 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2025 Intel Corporation + */ +#ifndef __iwl_fw_api_dhc_h__ +#define __iwl_fw_api_dhc_h__ + +#define DHC_TABLE_MASK_POS (28) + +/** + * enum iwl_dhc_table_id - DHC table operations index + */ +enum iwl_dhc_table_id { + /** + * @DHC_TABLE_INTEGRATION: select the integration table + */ + DHC_TABLE_INTEGRATION = 2 << DHC_TABLE_MASK_POS, + /** + * @DHC_TABLE_TOOLS: select the tools table + */ + DHC_TABLE_TOOLS = 0, +}; + +/** + * enum iwl_dhc_umac_tools_table - tools operations + * @DHC_TOOLS_UMAC_GET_TAS_STATUS: Get TAS status. + * See @struct iwl_dhc_tas_status_resp + */ +enum iwl_dhc_umac_tools_table { + DHC_TOOLS_UMAC_GET_TAS_STATUS = 0, +}; + +/** + * enum iwl_dhc_umac_integration_table - integration operations + */ +enum iwl_dhc_umac_integration_table { + /** + * @DHC_INT_UMAC_TWT_OPERATION: trigger a TWT operation + */ + DHC_INT_UMAC_TWT_OPERATION = 4, + /** + * @DHC_INTEGRATION_TLC_DEBUG_CONFIG: TLC debug + */ + DHC_INTEGRATION_TLC_DEBUG_CONFIG = 1, + /** + * @DHC_INTEGRATION_MAX: Maximum UMAC integration table entries + */ + DHC_INTEGRATION_MAX +}; + +#define DHC_TARGET_UMAC BIT(27) + +/** + * struct iwl_dhc_cmd - debug host command + * @length: length in DWs of the data structure that is concatenated to the end + * of this struct + * @index_and_mask: bit 31 is 1 for data set operation else it's 0 + * bits 28-30 is the index of the table of the operation - + * &enum iwl_dhc_table_id * + * bit 27 is 0 if the cmd targeted to LMAC and 1 if targeted to UMAC, + * (LMAC is 0 for backward compatibility) + * bit 26 is 0 if the cmd targeted to LMAC0 and 1 if targeted to LMAC1, + * relevant only if bit 27 set to 0 + * bits 0-25 is a specific entry index in the table specified in bits 28-30 + * + * @data: the concatenated data. + */ +struct iwl_dhc_cmd { + __le32 length; + __le32 index_and_mask; + __le32 data[]; +} __packed; /* DHC_CMD_API_S */ + +/** + * struct iwl_dhc_payload_hdr - DHC payload header + * @version: a version of a payload + * @reserved: reserved for alignment + */ +struct iwl_dhc_payload_hdr { + u8 version; + u8 reserved[3]; +} __packed; /* DHC_PAYLOAD_HDR_API_S_VER_1 */ + +/** + * struct iwl_dhc_tas_status_per_radio - TAS status per radio + * @band: &PHY_BAND_5 for high band, PHY_BAND_24 for low band and + * &PHY_BAND_6 for ultra high band. + * @static_status: TAS statically enabled or disabled + * @static_disable_reason: TAS static disable reason, uses + * &enum iwl_tas_statically_disabled_reason + * @near_disconnection: is TAS currently near disconnection per radio + * @dynamic_status_ant_a: Antenna A current TAS status. + * uses &enum iwl_tas_dyna_status + * @dynamic_status_ant_b: Antenna B current TAS status. + * uses &enum iwl_tas_dyna_status + * @max_reg_pwr_limit_ant_a: Antenna A regulatory power limits in dBm + * @max_reg_pwr_limit_ant_b: Antenna B regulatory power limits in dBm + * @sar_limit_ant_a: Antenna A SAR limit per radio in dBm + * @sar_limit_ant_b: Antenna B SAR limit per radio in dBm + * @reserved: reserved for alignment + */ +struct iwl_dhc_tas_status_per_radio { + u8 band; + u8 static_status; + u8 static_disable_reason; + u8 near_disconnection; + u8 dynamic_status_ant_a; + u8 dynamic_status_ant_b; + __le16 max_reg_pwr_limit_ant_a; + __le16 max_reg_pwr_limit_ant_b; + __le16 sar_limit_ant_a; + __le16 sar_limit_ant_b; + u8 reserved[2]; +} __packed; /* DHC_TAS_STATUS_PER_RADIO_S_VER_1 */ + +/** + * struct iwl_dhc_tas_status_resp - Response to DHC_TOOLS_UMAC_GET_TAS_STATUS + * @header: DHC payload header, uses &struct iwl_dhc_payload_hdr + * @tas_config_info: see @struct bios_value_u32 + * @mcc_block_list: block listed country codes + * @tas_status_radio: TAS status, uses &struct iwl_dhc_tas_status_per_radio + * @curr_mcc: current mcc + * @valid_radio_mask: represent entry in tas_status_per_radio is valid. + * @reserved: reserved for alignment + */ +struct iwl_dhc_tas_status_resp { + struct iwl_dhc_payload_hdr header; + struct bios_value_u32 tas_config_info; + __le16 mcc_block_list[IWL_WTAS_BLACK_LIST_MAX]; + struct iwl_dhc_tas_status_per_radio tas_status_radio[2]; + __le16 curr_mcc; + u8 valid_radio_mask; + u8 reserved; +} __packed; /* DHC_TAS_STATUS_RSP_API_S_VER_1 */ + +/** + * struct iwl_dhc_cmd_resp_v1 - debug host command response + * @status: status of the command + * @data: the response data + */ +struct iwl_dhc_cmd_resp_v1 { + __le32 status; + __le32 data[]; +} __packed; /* DHC_RESP_API_S_VER_1 */ + +/** + * struct iwl_dhc_cmd_resp - debug host command response + * @status: status of the command + * @descriptor: command descriptor (index_and_mask) returned + * @data: the response data + */ +struct iwl_dhc_cmd_resp { + __le32 status; + __le32 descriptor; + __le32 data[]; +} __packed; /* DHC_RESP_API_S_VER_2 and DHC_RESP_API_S_VER_3 */ + +/** + * enum iwl_dhc_twt_operation_type - describes the TWT operation type + * + * @DHC_TWT_REQUEST: Send a Request TWT command + * @DHC_TWT_SUGGEST: Send a Suggest TWT command + * @DHC_TWT_DEMAND: Send a Demand TWT command + * @DHC_TWT_GROUPING: Send a Grouping TWT command + * @DHC_TWT_ACCEPT: Send a Accept TWT command + * @DHC_TWT_ALTERNATE: Send a Alternate TWT command + * @DHC_TWT_DICTATE: Send a Dictate TWT command + * @DHC_TWT_REJECT: Send a Reject TWT command + * @DHC_TWT_TEARDOWN: Send a TearDown TWT command + */ +enum iwl_dhc_twt_operation_type { + DHC_TWT_REQUEST, + DHC_TWT_SUGGEST, + DHC_TWT_DEMAND, + DHC_TWT_GROUPING, + DHC_TWT_ACCEPT, + DHC_TWT_ALTERNATE, + DHC_TWT_DICTATE, + DHC_TWT_REJECT, + DHC_TWT_TEARDOWN, +}; /* DHC_TWT_OPERATION_TYPE_E */ + +/** + * struct iwl_dhc_twt_operation - trigger a TWT operation + * + * @mac_id: the mac Id on which to trigger TWT operation + * @twt_operation: see &enum iwl_dhc_twt_operation_type + * @target_wake_time: when should we be on channel + * @interval_exp: the exponent for the interval + * @interval_mantissa: the mantissa for the interval + * @min_wake_duration: the minimum duration for the wake period + * @trigger: is the TWT triggered or not + * @flow_type: is the TWT announced or not + * @flow_id: the TWT flow identifier from 0 to 7 + * @protection: is the TWT protected + * @ndo_paging_indicator: is ndo_paging_indicator set + * @responder_pm_mode: is responder_pm_mode set + * @negotiation_type: if the responder wants to doze outside the TWT SP + * @twt_request: 1 for TWT request, 0 otherwise + * @implicit: is TWT implicit + * @twt_group_assignment: the TWT group assignment + * @twt_channel: the TWT channel + * @reserved: reserved + */ +struct iwl_dhc_twt_operation { + __le32 mac_id; + __le32 twt_operation; + __le64 target_wake_time; + __le32 interval_exp; + __le32 interval_mantissa; + __le32 min_wake_duration; + u8 trigger; + u8 flow_type; + u8 flow_id; + u8 protection; + u8 ndo_paging_indicator; + u8 responder_pm_mode; + u8 negotiation_type; + u8 twt_request; + u8 implicit; + u8 twt_group_assignment; + u8 twt_channel; + u8 reserved; +}; /* DHC_TWT_OPERATION_API_S */ + +#endif /* __iwl_fw_api_dhc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h index b8dff139aa05..33541f92c7c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/location.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/location.h @@ -2,10 +2,15 @@ /* * Copyright (C) 2015-2017 Intel Deutschland GmbH * Copyright (C) 2018-2022 Intel Corporation - * Copyright (C) 2024 Intel Corporation + * Copyright (C) 2024-2025 Intel Corporation */ #ifndef __iwl_fw_api_location_h__ #define __iwl_fw_api_location_h__ +#include <linux/ieee80211.h> +#include <linux/if_ether.h> +#include <linux/types.h> +#include <linux/bits.h> +#include "rs.h" /** * enum iwl_location_subcmd_ids - location group command IDs @@ -1015,7 +1020,7 @@ struct iwl_tof_range_req_ap_entry_v9 { } __packed; /* LOCATION_RANGE_REQ_AP_ENTRY_CMD_API_S_VER_9 */ /** - * struct iwl_tof_range_req_ap_entry_v10 - AP configuration parameters + * struct iwl_tof_range_req_ap_entry - AP configuration parameters * @initiator_ap_flags: see &enum iwl_initiator_ap_flags. * @band: 0 for 5.2 GHz, 1 for 2.4 GHz, 2 for 6GHz * @channel_num: AP Channel number @@ -1063,7 +1068,7 @@ struct iwl_tof_range_req_ap_entry_v9 { * @min_time_between_msr: For non trigger based NDP ranging, the minimum time * between measurements in units of milliseconds */ -struct iwl_tof_range_req_ap_entry_v10 { +struct iwl_tof_range_req_ap_entry { __le32 initiator_ap_flags; u8 band; u8 channel_num; @@ -1134,7 +1139,7 @@ enum iwl_tof_initiator_flags { IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT = BIT(20), }; /* LOCATION_RANGE_REQ_CMD_API_S_VER_5 */ -#define IWL_MVM_TOF_MAX_APS 5 +#define IWL_TOF_MAX_APS 5 #define IWL_MVM_TOF_MAX_TWO_SIDED_APS 5 /** @@ -1153,7 +1158,7 @@ enum iwl_tof_initiator_flags { * when the session is done (successfully / partially). * one of iwl_tof_response_mode. * @reserved0: reserved - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @macaddr_random: '0' Use default source MAC address (i.e. p2_p), * '1' Use MAC Address randomization according to the below * @range_req_bssid: ranging request BSSID @@ -1183,7 +1188,7 @@ struct iwl_tof_range_req_cmd_v5 { u8 ftm_tx_chains; __le16 common_calib; __le16 specific_calib; - struct iwl_tof_range_req_ap_entry_v2 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v2 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_5 */ @@ -1192,7 +1197,7 @@ struct iwl_tof_range_req_cmd_v5 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1216,7 +1221,7 @@ struct iwl_tof_range_req_cmd_v7 { __le32 tsf_mac_id; __le16 common_calib; __le16 specific_calib; - struct iwl_tof_range_req_ap_entry_v3 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v3 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_7 */ /** @@ -1224,7 +1229,7 @@ struct iwl_tof_range_req_cmd_v7 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1248,7 +1253,7 @@ struct iwl_tof_range_req_cmd_v8 { __le32 tsf_mac_id; __le16 common_calib; __le16 specific_calib; - struct iwl_tof_range_req_ap_entry_v4 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v4 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_8 */ /** @@ -1256,7 +1261,7 @@ struct iwl_tof_range_req_cmd_v8 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1276,7 +1281,7 @@ struct iwl_tof_range_req_cmd_v9 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v6 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v6 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_9 */ /** @@ -1284,7 +1289,7 @@ struct iwl_tof_range_req_cmd_v9 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1304,7 +1309,7 @@ struct iwl_tof_range_req_cmd_v11 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v7 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v7 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_11 */ /** @@ -1312,7 +1317,7 @@ struct iwl_tof_range_req_cmd_v11 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1332,7 +1337,7 @@ struct iwl_tof_range_req_cmd_v12 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v8 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v8 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_12 */ /** @@ -1340,7 +1345,7 @@ struct iwl_tof_range_req_cmd_v12 { * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1360,15 +1365,15 @@ struct iwl_tof_range_req_cmd_v13 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v9 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_req_ap_entry_v9 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_13 */ /** - * struct iwl_tof_range_req_cmd_v14 - start measurement cmd + * struct iwl_tof_range_req_cmd - start measurement cmd * @initiator_flags: see flags @ iwl_tof_initiator_flags * @request_id: A Token incremented per request. The same Token will be * sent back in the range response - * @num_of_ap: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_ap: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @range_req_bssid: ranging request BSSID * @macaddr_mask: Bits set to 0 shall be copied from the MAC address template. * Bits set to 1 shall be randomized by the UMAC @@ -1377,9 +1382,9 @@ struct iwl_tof_range_req_cmd_v13 { * This is the session time for completing the measurement. * @tsf_mac_id: report the measurement start time for each ap in terms of the * TSF of this mac id. 0xff to disable TSF reporting. - * @ap: per-AP request data, see &struct iwl_tof_range_req_ap_entry_v10. + * @ap: per-AP request data, see &struct iwl_tof_range_req_ap_entry. */ -struct iwl_tof_range_req_cmd_v14 { +struct iwl_tof_range_req_cmd { __le32 initiator_flags; u8 request_id; u8 num_of_ap; @@ -1388,8 +1393,8 @@ struct iwl_tof_range_req_cmd_v14 { u8 macaddr_template[ETH_ALEN]; __le32 req_timeout_ms; __le32 tsf_mac_id; - struct iwl_tof_range_req_ap_entry_v10 ap[IWL_MVM_TOF_MAX_APS]; -} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_13 */ + struct iwl_tof_range_req_ap_entry ap[IWL_TOF_MAX_APS]; +} __packed; /* LOCATION_RANGE_REQ_CMD_API_S_VER_15 */ /* * enum iwl_tof_range_request_status - status of the sent request @@ -1609,7 +1614,7 @@ struct iwl_tof_range_rsp_ap_entry_ntfy_v5 { } __packed; /* LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_5 */ /** - * struct iwl_tof_range_rsp_ap_entry_ntfy_v6 - AP parameters (response) + * struct iwl_tof_range_rsp_ap_entry_ntfy_v7 - AP parameters (response) * @bssid: BSSID of the AP * @measure_status: current APs measurement status, one of * &enum iwl_tof_entry_status. @@ -1645,7 +1650,7 @@ struct iwl_tof_range_rsp_ap_entry_ntfy_v5 { * @tx_pn: the last PN used for this responder Tx in case PMF is configured in * LE byte order. */ -struct iwl_tof_range_rsp_ap_entry_ntfy_v6 { +struct iwl_tof_range_rsp_ap_entry_ntfy_v7 { u8 bssid[ETH_ALEN]; u8 measure_status; u8 measure_bw; @@ -1672,6 +1677,65 @@ struct iwl_tof_range_rsp_ap_entry_ntfy_v6 { } __packed; /* LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_6, LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_7 */ +/** + * struct iwl_tof_range_rsp_ap_entry_ntfy - AP parameters (response) + * @bssid: BSSID of the AP + * @measure_status: current APs measurement status, one of + * &enum iwl_tof_entry_status. + * @measure_bw: Current AP Bandwidth: 0 20MHz, 1 40MHz, 2 80MHz + * @rtt: The Round Trip Time that took for the last measurement for + * current AP [pSec] + * @rtt_variance: The Variance of the RTT values measured for current AP + * @rtt_spread: The Difference between the maximum and the minimum RTT + * values measured for current AP in the current session [pSec] + * @rssi: RSSI as uploaded in the Channel Estimation notification + * @rssi_spread: The Difference between the maximum and the minimum RSSI values + * measured for current AP in the current session + * @last_burst: 1 if no more FTM sessions are scheduled for this responder + * @refusal_period: refusal period in case of + * @IWL_TOF_ENTRY_RESPONDER_CANNOT_COLABORATE [sec] + * @timestamp: The GP2 Clock [usec] where Channel Estimation notification was + * uploaded by the LMAC + * @start_tsf: measurement start time in TSF of the mac specified in the range + * request + * @reserved1: reserved, for backwards compatibility + * @t2t3_initiator: as calculated from the algo in the initiator + * @t1t4_responder: as calculated from the algo in the responder + * @common_calib: Calib val that was used in for this AP measurement + * @specific_calib: val that was used in for this AP measurement + * @papd_calib_output: The result of the tof papd calibration that was injected + * into the algorithm. + * @rttConfidence: a value between 0 - 31 that represents the rtt accuracy. + * @reserved: for alignment + * @rx_pn: the last PN used for this responder Rx in case PMF is configured in + * LE byte order. + * @tx_pn: the last PN used for this responder Tx in case PMF is configured in + * LE byte order. + */ +struct iwl_tof_range_rsp_ap_entry_ntfy { + u8 bssid[ETH_ALEN]; + u8 measure_status; + u8 measure_bw; + __le32 rtt; + __le32 rtt_variance; + __le32 rtt_spread; + s8 rssi; + u8 rssi_spread; + u8 last_burst; + u8 refusal_period; + __le32 timestamp; + __le32 start_tsf; + __le32 reserved1[2]; + __le32 t2t3_initiator; + __le32 t1t4_responder; + __le16 common_calib; + __le16 specific_calib; + __le32 papd_calib_output; + u8 rttConfidence; + u8 reserved[3]; + u8 rx_pn[IEEE80211_CCMP_PN_LEN]; + u8 tx_pn[IEEE80211_CCMP_PN_LEN]; +} __packed; /* LOCATION_RANGE_RSP_AP_ETRY_NTFY_API_S_VER_8 */ /** * enum iwl_tof_response_status - tof response status @@ -1695,7 +1759,7 @@ enum iwl_tof_response_status { * @request_status: status of current measurement session, one of * &enum iwl_tof_response_status. * @last_in_batch: reprot policy (when not all responses are uploaded at once) - * @num_of_aps: Number of APs to measure (error if > IWL_MVM_TOF_MAX_APS) + * @num_of_aps: Number of APs to measure (error if > IWL_TOF_MAX_APS) * @ap: per-AP data */ struct iwl_tof_range_rsp_ntfy_v5 { @@ -1703,7 +1767,7 @@ struct iwl_tof_range_rsp_ntfy_v5 { u8 request_status; u8 last_in_batch; u8 num_of_aps; - struct iwl_tof_range_rsp_ap_entry_ntfy_v3 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_rsp_ap_entry_ntfy_v3 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_5 */ /** @@ -1719,7 +1783,7 @@ struct iwl_tof_range_rsp_ntfy_v6 { u8 num_of_aps; u8 last_report; u8 reserved; - struct iwl_tof_range_rsp_ap_entry_ntfy_v4 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_rsp_ap_entry_ntfy_v4 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_6 */ /** @@ -1735,25 +1799,42 @@ struct iwl_tof_range_rsp_ntfy_v7 { u8 num_of_aps; u8 last_report; u8 reserved; - struct iwl_tof_range_rsp_ap_entry_ntfy_v5 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_rsp_ap_entry_ntfy_v5 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_7 */ /** - * struct iwl_tof_range_rsp_ntfy_v8 - ranging response notification + * struct iwl_tof_range_rsp_ntfy_v9 - ranging response notification * @request_id: A Token ID of the corresponding Range request * @num_of_aps: Number of APs results * @last_report: 1 if no more FTM sessions are scheduled, 0 otherwise. * @reserved: reserved * @ap: per-AP data */ -struct iwl_tof_range_rsp_ntfy_v8 { +struct iwl_tof_range_rsp_ntfy_v9 { u8 request_id; u8 num_of_aps; u8 last_report; u8 reserved; - struct iwl_tof_range_rsp_ap_entry_ntfy_v6 ap[IWL_MVM_TOF_MAX_APS]; + struct iwl_tof_range_rsp_ap_entry_ntfy_v7 ap[IWL_TOF_MAX_APS]; } __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_8, - LOCATION_RANGE_RSP_NTFY_API_S_VER_9 */ + * LOCATION_RANGE_RSP_NTFY_API_S_VER_9 + */ + +/** + * struct iwl_tof_range_rsp_ntfy - ranging response notification + * @request_id: A Token ID of the corresponding Range request + * @num_of_aps: Number of APs results + * @last_report: 1 if no more FTM sessions are scheduled, 0 otherwise. + * @reserved: reserved + * @ap: per-AP data + */ +struct iwl_tof_range_rsp_ntfy { + u8 request_id; + u8 num_of_aps; + u8 last_report; + u8 reserved; + struct iwl_tof_range_rsp_ap_entry_ntfy ap[IWL_TOF_MAX_APS]; +} __packed; /* LOCATION_RANGE_RSP_NTFY_API_S_VER_10 */ #define IWL_MVM_TOF_MCSI_BUF_SIZE (245) /** diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h index 37bb7002c1c9..b9f559dac39f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac-cfg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -46,7 +46,7 @@ enum iwl_mac_conf_subcmd_ids { */ STA_CONFIG_CMD = 0xA, /** - * @AUX_STA_CMD: &struct iwl_mvm_aux_sta_cmd + * @AUX_STA_CMD: &struct iwl_aux_sta_cmd */ AUX_STA_CMD = 0xB, /** @@ -62,6 +62,10 @@ enum iwl_mac_conf_subcmd_ids { */ ROC_CMD = 0xE, /** + * @TWT_OPERATION_CMD: &struct iwl_twt_operation_cmd + */ + TWT_OPERATION_CMD = 0x10, + /** * @MISSED_BEACONS_NOTIF: &struct iwl_missed_beacons_notif */ MISSED_BEACONS_NOTIF = 0xF6, @@ -307,8 +311,41 @@ enum iwl_mac_config_filter_flags { }; /* MAC_FILTER_FLAGS_MASK_E_VER_1 */ /** + * struct iwl_mac_wifi_gen_support_v2 - parameters of iwl_mac_config_cmd + * with support up to eht as in version 2 of the command + * + * @he_support: does this MAC support HE + * @he_ap_support: HE AP enabled, "pseudo HE", no trigger frame handling + * @eht_support: does this MAC support EHT. Requires he_support + */ +struct iwl_mac_wifi_gen_support_v2 { + __le16 he_support; + __le16 he_ap_support; + __le32 eht_support; +} __packed; + +/** + * struct iwl_mac_wifi_gen_support - parameters of iwl_mac_config_cmd + * with support up to uhr as in version 3 of the command + * ( MAC_CONTEXT_CONFIG_CMD = 0x8 ) + * + * @he_support: does this MAC support HE + * @he_ap_support: HE AP enabled, "pseudo HE", no trigger frame handling + * @eht_support: does this MAC support EHT. Requires he_support + * @uhr_support: does this MAC support UHR. Requires eht_support + * @reserved: reserved for alignment and to match version 2's size + */ +struct iwl_mac_wifi_gen_support { + u8 he_support; + u8 he_ap_support; + u8 eht_support; + u8 uhr_support; + __le32 reserved; +} __packed; + +/** * struct iwl_mac_config_cmd - command structure to configure MAC contexts in - * MLD API + * MLD API for versions 2 and 3 * ( MAC_CONTEXT_CONFIG_CMD = 0x8 ) * * @id_and_color: ID and color of the MAC @@ -317,9 +354,8 @@ enum iwl_mac_config_filter_flags { * @local_mld_addr: mld address * @reserved_for_local_mld_addr: reserved * @filter_flags: combination of &enum iwl_mac_config_filter_flags - * @he_support: does this MAC support HE - * @he_ap_support: HE AP enabled, "pseudo HE", no trigger frame handling - * @eht_support: does this MAC support EHT. Requires he_support + * @wifi_gen_v2: he/eht parameters as in cmd version 2 + * @wifi_gen: he/eht/uhr parameters as in cmd version 3 * @nic_not_ack_enabled: mark that the NIC doesn't support receiving * ACK-enabled AGG, (i.e. both BACK and non-BACK frames in single AGG). * If the NIC is not ACK_ENABLED it may use the EOF-bit in first non-0 @@ -328,7 +364,6 @@ enum iwl_mac_config_filter_flags { * @p2p_dev: mac data for p2p device */ struct iwl_mac_config_cmd { - /* COMMON_INDEX_HDR_API_S_VER_1 */ __le32 id_and_color; __le32 action; /* MAC_CONTEXT_TYPE_API_E */ @@ -336,16 +371,17 @@ struct iwl_mac_config_cmd { u8 local_mld_addr[6]; __le16 reserved_for_local_mld_addr; __le32 filter_flags; - __le16 he_support; - __le16 he_ap_support; - __le32 eht_support; + union { + struct iwl_mac_wifi_gen_support_v2 wifi_gen_v2; + struct iwl_mac_wifi_gen_support wifi_gen; + }; __le32 nic_not_ack_enabled; /* MAC_CONTEXT_CONFIG_SPECIFIC_DATA_API_U_VER_2 */ union { struct iwl_mac_client_data client; struct iwl_mac_p2p_dev_data p2p_dev; }; -} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2 */ +} __packed; /* MAC_CONTEXT_CONFIG_CMD_API_S_VER_2_VER_3 */ /** * enum iwl_link_ctx_modify_flags - indicate to the fw what fields are being @@ -453,6 +489,24 @@ enum iwl_link_modify_bandwidth { }; /** + * struct iwl_npca_params - NPCA parameters (non-primary channel access) + * + * @switch_delay: after switch, delay TX according to destination AP + * @switch_back_delay: switch back to control channel before OBSS frame end + * @min_dur_threshold: minimum PPDU time to switch to the non-primary + * NPCA channel + * @flags: NPCA flags - bit 0: puncturing allowed, bit 1: new TX allowed + * @reserved: reserved for alignment purposes + */ +struct iwl_npca_params { + u8 switch_delay; + u8 switch_back_delay; + __le16 min_dur_threshold; + __le16 flags; + __le16 reserved; +} __packed; /* NPCA_PARAM_API_S_VER_1 */ + +/** * struct iwl_link_config_cmd - command structure to configure the LINK context * in MLD API * ( LINK_CONFIG_CMD =0x9 ) @@ -509,6 +563,8 @@ enum iwl_link_modify_bandwidth { * IEEE802.11REVme-D5.0 * @ibss_bssid_addr: bssid for ibss * @reserved_for_ibss_bssid_addr: reserved + * @npca_params: NPCA parameters + * @prio_edca_params: priority EDCA parameters for enhanced QoS * @reserved3: reserved for future use */ struct iwl_link_config_cmd { @@ -556,8 +612,10 @@ struct iwl_link_config_cmd { u8 ul_mu_data_disable; u8 ibss_bssid_addr[6]; __le16 reserved_for_ibss_bssid_addr; - __le32 reserved3[8]; -} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1, _VER_2, _VER_3, _VER_4, _VER_5, _VER_6 */ + struct iwl_npca_params npca_params; /* since _VER_7 */ + struct iwl_ac_qos prio_edca_params; /* since _VER_7 */ + __le32 reserved3[4]; +} __packed; /* LINK_CONTEXT_CONFIG_CMD_API_S_VER_1, _VER_2, _VER_3, _VER_4, _VER_5, _VER_6, _VER_7 */ /* Currently FW supports link ids in the range 0-3 and can have * at most two active links for each vif. @@ -585,6 +643,62 @@ enum iwl_fw_sta_type { }; /* STATION_TYPE_E_VER_1 */ /** + * struct iwl_sta_cfg_cmd_v1 - cmd structure to add a peer sta to the uCode's + * station table + * ( STA_CONFIG_CMD = 0xA ) + * + * @sta_id: index of station in uCode's station table + * @link_id: the id of the link that is used to communicate with this sta + * @peer_mld_address: the peers mld address + * @reserved_for_peer_mld_address: reserved + * @peer_link_address: the address of the link that is used to communicate + * with this sta + * @reserved_for_peer_link_address: reserved + * @station_type: type of this station. See &enum iwl_fw_sta_type + * @assoc_id: for GO only + * @beamform_flags: beam forming controls + * @mfp: indicates whether the STA uses management frame protection or not. + * @mimo: indicates whether the sta uses mimo or not + * @mimo_protection: indicates whether the sta uses mimo protection or not + * @ack_enabled: indicates that the AP supports receiving ACK- + * enabled AGG, i.e. both BACK and non-BACK frames in a single AGG + * @trig_rnd_alloc: indicates that trigger based random allocation + * is enabled according to UORA element existence + * @tx_ampdu_spacing: minimum A-MPDU spacing: + * 4 - 2us density, 5 - 4us density, 6 - 8us density, 7 - 16us density + * @tx_ampdu_max_size: maximum A-MPDU length: 0 - 8K, 1 - 16K, 2 - 32K, + * 3 - 64K, 4 - 128K, 5 - 256K, 6 - 512K, 7 - 1024K. + * @sp_length: the size of the SP in actual number of frames + * @uapsd_acs: 4 LS bits are trigger enabled ACs, 4 MS bits are the deliver + * enabled ACs. + * @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY + * capa + * @htc_flags: which features are supported in HTC + */ +struct iwl_sta_cfg_cmd_v1 { + __le32 sta_id; + __le32 link_id; + u8 peer_mld_address[ETH_ALEN]; + __le16 reserved_for_peer_mld_address; + u8 peer_link_address[ETH_ALEN]; + __le16 reserved_for_peer_link_address; + __le32 station_type; + __le32 assoc_id; + __le32 beamform_flags; + __le32 mfp; + __le32 mimo; + __le32 mimo_protection; + __le32 ack_enabled; + __le32 trig_rnd_alloc; + __le32 tx_ampdu_spacing; + __le32 tx_ampdu_max_size; + __le32 sp_length; + __le32 uapsd_acs; + struct iwl_he_pkt_ext_v2 pkt_ext; + __le32 htc_flags; +} __packed; /* STA_CMD_API_S_VER_1 */ + +/** * struct iwl_sta_cfg_cmd - cmd structure to add a peer sta to the uCode's * station table * ( STA_CONFIG_CMD = 0xA ) @@ -616,6 +730,14 @@ enum iwl_fw_sta_type { * @pkt_ext: optional, exists according to PPE-present bit in the HE/EHT-PHY * capa * @htc_flags: which features are supported in HTC + * @use_ldpc_x2_cw: Indicates whether to use LDPC with double CW + * @use_icf: Indicates whether to use ICF instead of RTS + * @dps_pad_time: DPS (Dynamic Power Save) padding delay resolution to ensure + * proper timing alignment + * @dps_trans_delay: DPS minimal time that takes the peer to return to low power + * @mic_prep_pad_delay: MIC prep time padding + * @mic_compute_pad_delay: MIC compute time padding + * @reserved: Reserved for alignment */ struct iwl_sta_cfg_cmd { __le32 sta_id; @@ -638,10 +760,17 @@ struct iwl_sta_cfg_cmd { __le32 uapsd_acs; struct iwl_he_pkt_ext_v2 pkt_ext; __le32 htc_flags; -} __packed; /* STA_CMD_API_S_VER_1 */ + u8 use_ldpc_x2_cw; + u8 use_icf; + u8 dps_pad_time; + u8 dps_trans_delay; + u8 mic_prep_pad_delay; + u8 mic_compute_pad_delay; + u8 reserved[2]; +} __packed; /* STA_CMD_API_S_VER_2 */ /** - * struct iwl_mvm_aux_sta_cmd - command for AUX STA configuration + * struct iwl_aux_sta_cmd - command for AUX STA configuration * ( AUX_STA_CMD = 0xB ) * * @sta_id: index of aux sta to configure @@ -649,7 +778,7 @@ struct iwl_sta_cfg_cmd { * @mac_addr: mac addr of the auxilary sta * @reserved_for_mac_addr: reserved */ -struct iwl_mvm_aux_sta_cmd { +struct iwl_aux_sta_cmd { __le32 sta_id; __le32 lmac_id; u8 mac_addr[ETH_ALEN]; @@ -682,9 +811,9 @@ struct iwl_mvm_sta_disable_tx_cmd { /** * enum iwl_mvm_fw_esr_recommendation - FW recommendation code - * @ESR_RECOMMEND_LEAVE: recommendation to leave esr - * @ESR_FORCE_LEAVE: force exiting esr - * @ESR_RECOMMEND_ENTER: recommendation to enter esr + * @ESR_RECOMMEND_LEAVE: recommendation to leave EMLSR + * @ESR_FORCE_LEAVE: force exiting EMLSR + * @ESR_RECOMMEND_ENTER: recommendation to enter EMLSR */ enum iwl_mvm_fw_esr_recommendation { ESR_RECOMMEND_LEAVE, @@ -693,15 +822,46 @@ enum iwl_mvm_fw_esr_recommendation { }; /* ESR_MODE_RECOMMENDATION_CODE_API_E_VER_1 */ /** - * struct iwl_mvm_esr_mode_notif - FWs recommendation/force for esr mode + * struct iwl_esr_mode_notif_v1 - FW recommendation/force for EMLSR mode * - * @action: the action to apply on esr state. See &iwl_mvm_fw_esr_recommendation + * @action: the action to apply on EMLSR state. + * See &iwl_mvm_fw_esr_recommendation */ -struct iwl_mvm_esr_mode_notif { +struct iwl_esr_mode_notif_v1 { __le32 action; } __packed; /* ESR_MODE_RECOMMENDATION_NTFY_API_S_VER_1 */ /** + * enum iwl_esr_leave_reason - reasons for leaving EMLSR mode + * + * @ESR_LEAVE_REASON_OMI_MU_UL_DISALLOWED: OMI MU UL disallowed + * @ESR_LEAVE_REASON_NO_TRIG_FOR_ESR_STA: No trigger for EMLSR station + * @ESR_LEAVE_REASON_NO_ESR_STA_IN_MU_DL: No EMLSR station in MU DL + * @ESR_LEAVE_REASON_BAD_ACTIV_FRAME_TH: Bad activation frame threshold + * @ESR_LEAVE_REASON_RTS_IN_DUAL_LISTEN: RTS in dual listen + */ +enum iwl_esr_leave_reason { + ESR_LEAVE_REASON_OMI_MU_UL_DISALLOWED = BIT(0), + ESR_LEAVE_REASON_NO_TRIG_FOR_ESR_STA = BIT(1), + ESR_LEAVE_REASON_NO_ESR_STA_IN_MU_DL = BIT(2), + ESR_LEAVE_REASON_BAD_ACTIV_FRAME_TH = BIT(3), + ESR_LEAVE_REASON_RTS_IN_DUAL_LISTEN = BIT(4), +}; + +/** + * struct iwl_esr_mode_notif - FW recommendation/force for EMLSR mode + * + * @action: the action to apply on EMLSR state. + * See &iwl_mvm_fw_esr_recommendation + * @leave_reason_mask: mask for various reasons to leave EMLSR mode. + * See &iwl_esr_leave_reason + */ +struct iwl_esr_mode_notif { + __le32 action; + __le32 leave_reason_mask; +} __packed; /* ESR_MODE_RECOMMENDATION_NTFY_API_S_VER_2 */ + +/** * struct iwl_missed_beacons_notif - sent when by the firmware upon beacon loss * ( MISSED_BEACONS_NOTIF = 0xF6 ) * @link_id: fw link ID @@ -748,4 +908,83 @@ struct iwl_esr_trans_fail_notif { __le32 err_code; } __packed; /* ESR_TRANSITION_FAILED_NTFY_API_S_VER_1 */ +/* + * enum iwl_twt_operation_type: TWT operation in a TWT action frame + * + * @TWT_OPERATION_REQUEST: TWT Request + * @TWT_OPERATION_SUGGEST: TWT Suggest + * @TWT_OPERATION_DEMAND: TWT Demand + * @TWT_OPERATION_GROUPING: TWT Grouping + * @TWT_OPERATION_ACCEPT: TWT Accept + * @TWT_OPERATION_ALTERNATE: TWT Alternate + * @TWT_OPERATION_DICTATE: TWT Dictate + * @TWT_OPERATION_REJECT: TWT Reject + * @TWT_OPERATION_TEARDOWN: TWT Teardown + * @TWT_OPERATION_UNAVAILABILITY: TWT Unavailability + */ +enum iwl_twt_operation_type { + TWT_OPERATION_REQUEST, + TWT_OPERATION_SUGGEST, + TWT_OPERATION_DEMAND, + TWT_OPERATION_GROUPING, + TWT_OPERATION_ACCEPT, + TWT_OPERATION_ALTERNATE, + TWT_OPERATION_DICTATE, + TWT_OPERATION_REJECT, + TWT_OPERATION_TEARDOWN, + TWT_OPERATION_UNAVAILABILITY, + TWT_OPERATION_MAX, +}; /* TWT_OPERATION_TYPE_E_VER_1 */ + +/** + * struct iwl_twt_operation_cmd - initiate a TWT session from driver + * + * @link_id: FW link id to initiate the TWT + * @twt_operation: &enum iwl_twt_operation_type + * @target_wake_time: TSF time to start the TWT + * @interval_exponent: the exponent for the interval + * @interval_mantissa: the mantissa for the interval + * @minimum_wake_duration: the minimum duration for the wake period + * @trigger: is the TWT triggered or not + * @flow_type: is the TWT announced (0) or not (1) + * @flow_id: the TWT flow identifier 0 - 7 + * @twt_protection: is the TWT protected + * @ndp_paging_indicator: is ndp paging indicator set + * @responder_pm_mode: is responder pm mode set + * @negotiation_type: if the responder wants to doze outside the TWT SP + * @twt_request: 1 for TWT request (STA), 0 for TWT response (AP) + * @implicit: is TWT implicit + * @twt_group_assignment: the TWT group assignment + * @twt_channel: the TWT channel + * @restricted_info_present: is this a restricted TWT + * @dl_bitmap_valid: is DL (download) bitmap valid (restricted TWT) + * @ul_bitmap_valid: is UL (upload) bitmap valid (restricted TWT) + * @dl_tid_bitmap: DL TID bitmap (restricted TWT) + * @ul_tid_bitmap: UL TID bitmap (restricted TWT) + */ +struct iwl_twt_operation_cmd { + __le32 link_id; + __le32 twt_operation; + __le64 target_wake_time; + __le32 interval_exponent; + __le32 interval_mantissa; + __le32 minimum_wake_duration; + u8 trigger; + u8 flow_type; + u8 flow_id; + u8 twt_protection; + u8 ndp_paging_indicator; + u8 responder_pm_mode; + u8 negotiation_type; + u8 twt_request; + u8 implicit; + u8 twt_group_assignment; + u8 twt_channel; + u8 restricted_info_present; + u8 dl_bitmap_valid; + u8 ul_bitmap_valid; + u8 dl_tid_bitmap; + u8 ul_tid_bitmap; +} __packed; /* TWT_OPERATION_API_S_VER_1 */ + #endif /* __iwl_fw_api_mac_cfg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h index 26301c0b06a1..2a174c00b712 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/mac.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2022, 2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022, 2024-2025 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_mac_h__ @@ -287,9 +287,9 @@ struct iwl_ac_qos { __le16 cw_min; __le16 cw_max; u8 aifsn; - u8 fifos_mask; + u8 fifos_mask; /* not in use since _VER_3 */ __le16 edca_txop; -} __packed; /* AC_QOS_API_S_VER_2 */ +} __packed; /* AC_QOS_API_S_VER_2, _VER_3 */ /** * struct iwl_mac_ctx_cmd - command structure to configure MAC contexts diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h index 4d8a12799c4d..4594a7c94bd6 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy-ctxt.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2020-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2020-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -146,6 +146,7 @@ struct iwl_phy_context_cmd_v1 { * @sbb_ctrl_channel_loc: location of the control channel * @puncture_mask: bitmap of punctured subchannels * @dsp_cfg_flags: set to 0 + * @secondary_ctrl_chnl_loc: location of secondary control channel * @reserved: reserved to align to 64 bit */ struct iwl_phy_context_cmd { @@ -164,11 +165,13 @@ struct iwl_phy_context_cmd { }; }; __le32 dsp_cfg_flags; - __le32 reserved; + u8 secondary_ctrl_chnl_loc; + u8 reserved[3]; } __packed; /* PHY_CONTEXT_CMD_API_VER_3, * PHY_CONTEXT_CMD_API_VER_4, * PHY_CONTEXT_CMD_API_VER_5, - * PHY_CONTEXT_CMD_API_VER_6 + * PHY_CONTEXT_CMD_API_VER_6, + * PHY_CONTEXT_CMD_API_S_VER_7 */ #endif /* __iwl_fw_api_phy_ctxt_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h index c73d4d597857..f63b25b03b7e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/phy.h @@ -1,11 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2019-2022, 2024 Intel Corporation + * Copyright (C) 2012-2014, 2019-2022, 2024-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_phy_h__ #define __iwl_fw_api_phy_h__ +#include <linux/types.h> +#include <linux/bits.h> /** * enum iwl_phy_ops_subcmd_ids - PHY group commands @@ -19,7 +21,7 @@ enum iwl_phy_ops_subcmd_ids { CMD_DTS_MEASUREMENT_TRIGGER_WIDE = 0x0, /** - * @CTDP_CONFIG_CMD: &struct iwl_mvm_ctdp_cmd + * @CTDP_CONFIG_CMD: &struct iwl_ctdp_cmd */ CTDP_CONFIG_CMD = 0x03, @@ -55,7 +57,7 @@ enum iwl_phy_ops_subcmd_ids { /** * @DTS_MEASUREMENT_NOTIF_WIDE: * &struct iwl_dts_measurement_notif_v1 or - * &struct iwl_dts_measurement_notif_v2 + * &struct iwl_dts_measurement_notif */ DTS_MEASUREMENT_NOTIF_WIDE = 0xFF, }; @@ -152,13 +154,13 @@ struct iwl_dts_measurement_notif_v1 { } __packed; /* TEMPERATURE_MEASUREMENT_TRIGGER_NTFY_S_VER_1*/ /** - * struct iwl_dts_measurement_notif_v2 - measurements notification + * struct iwl_dts_measurement_notif - measurements notification * * @temp: the measured temperature * @voltage: the measured voltage * @threshold_idx: the trip index that was crossed */ -struct iwl_dts_measurement_notif_v2 { +struct iwl_dts_measurement_notif { __le32 temp; __le32 voltage; __le32 threshold_idx; @@ -195,25 +197,25 @@ struct ct_kill_notif { } __packed; /* CT_KILL_NOTIFICATION_API_S_VER_1, CT_KILL_NOTIFICATION_API_S_VER_2 */ /** -* enum iwl_mvm_ctdp_cmd_operation - CTDP command operations +* enum iwl_ctdp_cmd_operation - CTDP command operations * @CTDP_CMD_OPERATION_START: update the current budget * @CTDP_CMD_OPERATION_STOP: stop ctdp * @CTDP_CMD_OPERATION_REPORT: get the average budget */ -enum iwl_mvm_ctdp_cmd_operation { +enum iwl_ctdp_cmd_operation { CTDP_CMD_OPERATION_START = 0x1, CTDP_CMD_OPERATION_STOP = 0x2, CTDP_CMD_OPERATION_REPORT = 0x4, };/* CTDP_CMD_OPERATION_TYPE_E */ /** - * struct iwl_mvm_ctdp_cmd - track and manage the FW power consumption budget + * struct iwl_ctdp_cmd - track and manage the FW power consumption budget * - * @operation: see &enum iwl_mvm_ctdp_cmd_operation + * @operation: see &enum iwl_ctdp_cmd_operation * @budget: the budget in milliwatt * @window_size: defined in API but not used */ -struct iwl_mvm_ctdp_cmd { +struct iwl_ctdp_cmd { __le32 operation; __le32 budget; __le32 window_size; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h index 37ec26596ee7..23140205ccb9 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/power.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/power.h @@ -1,12 +1,14 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_power_h__ #define __iwl_fw_api_power_h__ +#include "nvm-reg.h" + /* Power Management Commands, Responses, Notifications */ /** @@ -54,7 +56,7 @@ struct iwl_ltr_config_cmd_v1 { * @flags: See &enum iwl_ltr_config_flags * @static_long: static LTR Long register value. * @static_short: static LTR Short register value. - * @ltr_cfg_values: LTR parameters table values (in usec) in folowing order: + * @ltr_cfg_values: LTR parameters table values (in usec) in following order: * TX, RX, Short Idle, Long Idle. Used only if %LTR_CFG_FLAG_UPDATE_VALUES * is set. * @ltr_short_idle_timeout: LTR Short Idle timeout (in usec). Used only if @@ -89,6 +91,7 @@ struct iwl_ltr_config_cmd { * @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable. * @POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving * detection enablement + * @POWER_FLAGS_ENABLE_SMPS_MSK: SMPS is allowed for this vif */ enum iwl_power_flags { POWER_FLAGS_POWER_SAVE_ENA_MSK = BIT(0), @@ -99,6 +102,7 @@ enum iwl_power_flags { POWER_FLAGS_ADVANCE_PM_ENA_MSK = BIT(9), POWER_FLAGS_LPRX_ENA_MSK = BIT(11), POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK = BIT(12), + POWER_FLAGS_ENABLE_SMPS_MSK = BIT(14), }; #define IWL_POWER_VEC_SIZE 5 @@ -216,7 +220,6 @@ struct iwl_mac_power_cmd { /* CONTEXT_DESC_API_T_VER_1 */ __le32 id_and_color; - /* CLIENT_PM_POWER_TABLE_S_VER_1 */ __le16 flags; __le16 keep_alive_seconds; __le32 rx_data_timeout; @@ -237,7 +240,7 @@ struct iwl_mac_power_cmd { u8 heavy_rx_thld_percentage; u8 limited_ps_threshold; u8 reserved; -} __packed; +} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_1, VER_2 */ /* * struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when @@ -628,28 +631,37 @@ enum iwl_ppag_flags { /** * union iwl_ppag_table_cmd - union for all versions of PPAG command - * @v1: version 1 - * @v2: version 2 - * version 3, 4, 5 and 6 are the same structure as v2, + * @v1: command version 1 structure. + * @v2: command version from 2 to 6 are same structure as v2. * but has a different format of the flags bitmap + * @v3: command version 7 structure. * @v1.flags: values from &enum iwl_ppag_flags * @v1.gain: table of antenna gain values per chain and sub-band * @v1.reserved: reserved * @v2.flags: values from &enum iwl_ppag_flags * @v2.gain: table of antenna gain values per chain and sub-band - * @v2.reserved: reserved + * @v3.ppag_config_info: see @struct bios_value_u32 + * @v3.gain: table of antenna gain values per chain and sub-band + * @v3.reserved: reserved */ union iwl_ppag_table_cmd { struct { __le32 flags; s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V1]; s8 reserved[2]; - } v1; + } __packed v1; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_1 */ struct { __le32 flags; s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2]; s8 reserved[2]; - } v2; + } __packed v2; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_2, VER3, VER4, + * VER5, VER6 + */ + struct { + struct bios_value_u32 ppag_config_info; + s8 gain[IWL_NUM_CHAIN_LIMITS][IWL_NUM_SUB_BANDS_V2]; + s8 reserved[2]; + } __packed v3; /* PER_PLAT_ANTENNA_GAIN_CMD_API_S_VER_7 */ } __packed; #define IWL_PPAG_CMD_V4_MASK (IWL_PPAG_ETSI_MASK | IWL_PPAG_CHINA_MASK) @@ -657,6 +669,15 @@ union iwl_ppag_table_cmd { IWL_PPAG_ETSI_LPI_UHB_MASK | \ IWL_PPAG_USA_LPI_UHB_MASK) +#define IWL_PPAG_CMD_V6_MASK (IWL_PPAG_CMD_V5_MASK | \ + IWL_PPAG_ETSI_VLP_UHB_MASK | \ + IWL_PPAG_ETSI_SP_UHB_MASK | \ + IWL_PPAG_USA_VLP_UHB_MASK | \ + IWL_PPAG_USA_SP_UHB_MASK | \ + IWL_PPAG_CANADA_LPI_UHB_MASK | \ + IWL_PPAG_CANADA_VLP_UHB_MASK | \ + IWL_PPAG_CANADA_SP_UHB_MASK) + #define MCC_TO_SAR_OFFSET_TABLE_ROW_SIZE 26 #define MCC_TO_SAR_OFFSET_TABLE_COL_SIZE 13 @@ -690,7 +711,7 @@ struct iwl_sar_offset_mapping_cmd { * Roaming Energy Delta Threshold, otherwise use normal Energy Delta * Threshold. Typical energy threshold is -72dBm. * @bf_temp_threshold: This threshold determines the type of temperature - * filtering (Slow or Fast) that is selected (Units are in Celsuis): + * filtering (Slow or Fast) that is selected (Units are in Celsius): * If the current temperature is above this threshold - Fast filter * will be used, If the current temperature is below this threshold - * Slow filter will be used. @@ -698,12 +719,12 @@ struct iwl_sar_offset_mapping_cmd { * calculated for this and the last passed beacon is greater than this * threshold. Zero value means that the temperature change is ignored for * beacon filtering; beacons will not be forced to be sent to driver - * regardless of whether its temerature has been changed. + * regardless of whether its temperature has been changed. * @bf_temp_slow_filter: Send Beacon to driver if delta in temperature values * calculated for this and the last passed beacon is greater than this * threshold. Zero value means that the temperature change is ignored for * beacon filtering; beacons will not be forced to be sent to driver - * regardless of whether its temerature has been changed. + * regardless of whether its temperature has been changed. * @bf_enable_beacon_filter: 1, beacon filtering is enabled; 0, disabled. * @bf_debug_flag: beacon filtering debug configuration * @bf_escape_timer: Send beacons to to driver if no beacons were passed diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h index 1a60f0cdf972..3222cbcbe1ab 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rs.h @@ -1,11 +1,13 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2022, 2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2022, 2024-2025 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_rs_h__ #define __iwl_fw_api_rs_h__ - +#include <linux/bitfield.h> +#include <linux/types.h> +#include <linux/bits.h> #include "mac.h" /** @@ -213,7 +215,8 @@ enum iwl_tlc_update_flags { * @sta_id: station id * @reserved: reserved * @flags: bitmap of notifications reported - * @rate: current initial rate + * @rate: current initial rate, format depends on the notification + * version * @amsdu_size: Max AMSDU size, in bytes * @amsdu_enabled: bitmap for per-TID AMSDU enablement */ @@ -224,8 +227,60 @@ struct iwl_tlc_update_notif { __le32 rate; __le32 amsdu_size; __le32 amsdu_enabled; -} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2 */ +} __packed; /* TLC_MNG_UPDATE_NTFY_API_S_VER_2, _VER_3, _VER_4 */ +/** + * enum iwl_tlc_debug_types - debug options + */ +enum iwl_tlc_debug_types { + /** + * @IWL_TLC_DEBUG_FIXED_RATE: set fixed rate for rate scaling + */ + IWL_TLC_DEBUG_FIXED_RATE, + /** + * @IWL_TLC_DEBUG_AGG_DURATION_LIM: time limit for a BA + * session, in usec + */ + IWL_TLC_DEBUG_AGG_DURATION_LIM, + /** + * @IWL_TLC_DEBUG_AGG_FRAME_CNT_LIM: set max number of frames + * in an aggregation + */ + IWL_TLC_DEBUG_AGG_FRAME_CNT_LIM, + /** + * @IWL_TLC_DEBUG_TPC_ENABLED: enable or disable tpc + */ + IWL_TLC_DEBUG_TPC_ENABLED, + /** + * @IWL_TLC_DEBUG_TPC_STATS: get number of frames Tx'ed in each + * tpc step + */ + IWL_TLC_DEBUG_TPC_STATS, + /** + * @IWL_TLC_DEBUG_RTS_DISABLE: disable RTS (bool true/false). + */ + IWL_TLC_DEBUG_RTS_DISABLE, + /** + * @IWL_TLC_DEBUG_PARTIAL_FIXED_RATE: set partial fixed rate to fw + */ + IWL_TLC_DEBUG_PARTIAL_FIXED_RATE, +}; /* TLC_MNG_DEBUG_TYPES_API_E */ + +#define MAX_DATA_IN_DHC_TLC_CMD 10 + +/** + * struct iwl_dhc_tlc_cmd - fixed debug config + * @sta_id: bit 0 - enable/disable, bits 1 - 7 hold station id + * @reserved1: reserved + * @type: type id of %enum iwl_tlc_debug_types + * @data: data to send + */ +struct iwl_dhc_tlc_cmd { + u8 sta_id; + u8 reserved1[3]; + __le32 type; + __le32 data[MAX_DATA_IN_DHC_TLC_CMD]; +} __packed; /* TLC_MNG_DEBUG_CMD_S */ #define IWL_MAX_MCS_DISPLAY_SIZE 12 @@ -375,6 +430,7 @@ enum { /* Bit 4-5: (0) SISO, (1) MIMO2 (2) MIMO3 */ #define RATE_VHT_MCS_RATE_CODE_MSK 0xf +#define RATE_VHT_MCS_NSS_MSK 0x30 /* * Legacy OFDM rate format for bits 7:0 @@ -489,7 +545,7 @@ enum { #define RATE_MCS_CTS_REQUIRED_POS (31) #define RATE_MCS_CTS_REQUIRED_MSK (0x1 << RATE_MCS_CTS_REQUIRED_POS) -/* rate_n_flags bit field version 2 +/* rate_n_flags bit field version 2 and 3 * * The 32-bit value has different layouts in the low 8 bits depending on the * format. There are three formats, HT, VHT and legacy (11abg, with subformats @@ -501,23 +557,25 @@ enum { * (0) Legacy CCK (1) Legacy OFDM (2) High-throughput (HT) * (3) Very High-throughput (VHT) (4) High-efficiency (HE) * (5) Extremely High-throughput (EHT) + * (6) Ultra High Reliability (UHR) (v3 rate format only) */ #define RATE_MCS_MOD_TYPE_POS 8 #define RATE_MCS_MOD_TYPE_MSK (0x7 << RATE_MCS_MOD_TYPE_POS) -#define RATE_MCS_CCK_MSK (0 << RATE_MCS_MOD_TYPE_POS) -#define RATE_MCS_LEGACY_OFDM_MSK (1 << RATE_MCS_MOD_TYPE_POS) -#define RATE_MCS_HT_MSK (2 << RATE_MCS_MOD_TYPE_POS) -#define RATE_MCS_VHT_MSK (3 << RATE_MCS_MOD_TYPE_POS) -#define RATE_MCS_HE_MSK (4 << RATE_MCS_MOD_TYPE_POS) -#define RATE_MCS_EHT_MSK (5 << RATE_MCS_MOD_TYPE_POS) +#define RATE_MCS_MOD_TYPE_CCK (0 << RATE_MCS_MOD_TYPE_POS) +#define RATE_MCS_MOD_TYPE_LEGACY_OFDM (1 << RATE_MCS_MOD_TYPE_POS) +#define RATE_MCS_MOD_TYPE_HT (2 << RATE_MCS_MOD_TYPE_POS) +#define RATE_MCS_MOD_TYPE_VHT (3 << RATE_MCS_MOD_TYPE_POS) +#define RATE_MCS_MOD_TYPE_HE (4 << RATE_MCS_MOD_TYPE_POS) +#define RATE_MCS_MOD_TYPE_EHT (5 << RATE_MCS_MOD_TYPE_POS) +#define RATE_MCS_MOD_TYPE_UHR (6 << RATE_MCS_MOD_TYPE_POS) /* * Legacy CCK rate format for bits 0:3: * - * (0) 0xa - 1 Mbps - * (1) 0x14 - 2 Mbps - * (2) 0x37 - 5.5 Mbps - * (3) 0x6e - 11 nbps + * (0) 1 Mbps + * (1) 2 Mbps + * (2) 5.5 Mbps + * (3) 11 Mbps * * Legacy OFDM rate format for bis 3:0: * @@ -534,15 +592,19 @@ enum { #define RATE_LEGACY_RATE_MSK 0x7 /* - * HT, VHT, HE, EHT rate format for bits 3:0 - * 3-0: MCS - * + * HT, VHT, HE, EHT, UHR rate format + * Version 2: (not applicable for UHR) + * 3-0: MCS + * 4: NSS==2 indicator + * Version 3: + * 4-0: MCS + * 5: NSS==2 indicator */ #define RATE_HT_MCS_CODE_MSK 0x7 -#define RATE_MCS_NSS_POS 4 -#define RATE_MCS_NSS_MSK (1 << RATE_MCS_NSS_POS) -#define RATE_MCS_CODE_MSK 0xf -#define RATE_HT_MCS_INDEX(r) ((((r) & RATE_MCS_NSS_MSK) >> 1) | \ +#define RATE_MCS_NSS_MSK_V2 0x10 +#define RATE_MCS_NSS_MSK 0x20 +#define RATE_MCS_CODE_MSK 0x1f +#define RATE_HT_MCS_INDEX(r) ((((r) & RATE_MCS_NSS_MSK) >> 2) | \ ((r) & RATE_HT_MCS_CODE_MSK)) /* Bits 7-5: reserved */ @@ -758,11 +820,38 @@ struct iwl_lq_cmd { }; /* LINK_QUALITY_CMD_API_S_VER_1 */ u8 iwl_fw_rate_idx_to_plcp(int idx); -u32 iwl_new_rate_from_v1(u32 rate_v1); const struct iwl_rate_mcs_info *iwl_rate_mcs(int idx); const char *iwl_rs_pretty_ant(u8 ant); const char *iwl_rs_pretty_bw(int bw); int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate); bool iwl_he_is_sgi(u32 rate_n_flags); +static inline u32 iwl_v3_rate_from_v2_v3(__le32 rate, bool fw_v3) +{ + u32 val; + + if (fw_v3) + return le32_to_cpu(rate); + + val = le32_to_cpu(rate) & ~RATE_MCS_NSS_MSK_V2; + val |= u32_encode_bits(le32_get_bits(rate, RATE_MCS_NSS_MSK_V2), + RATE_MCS_NSS_MSK); + + return val; +} + +static inline __le32 iwl_v3_rate_to_v2_v3(u32 rate, bool fw_v3) +{ + __le32 val; + + if (fw_v3) + return cpu_to_le32(rate); + + val = cpu_to_le32(rate & ~RATE_MCS_NSS_MSK); + val |= le32_encode_bits(u32_get_bits(rate, RATE_MCS_NSS_MSK), + RATE_MCS_NSS_MSK_V2); + + return val; +} + #endif /* __iwl_fw_api_rs_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h index 691c879cb90d..7cf6d6ac7430 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/rx.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -193,10 +193,11 @@ enum iwl_rx_mpdu_amsdu_info { IWL_RX_MPDU_AMSDU_LAST_SUBFRAME = 0x80, }; -#define RX_MPDU_BAND_POS 6 -#define RX_MPDU_BAND_MASK 0xC0 -#define BAND_IN_RX_STATUS(_val) \ - (((_val) & RX_MPDU_BAND_MASK) >> RX_MPDU_BAND_POS) +enum iwl_rx_mpdu_mac_phy_band { + IWL_RX_MPDU_MAC_PHY_BAND_MAC_MASK = 0x0f, + IWL_RX_MPDU_MAC_PHY_BAND_PHY_MASK = 0x30, + IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK = 0xc0, +}; enum iwl_rx_l3_proto_values { IWL_RX_L3_TYPE_NONE, @@ -639,7 +640,9 @@ struct iwl_rx_mpdu_desc_v3 { */ __le32 reserved[1]; } __packed; /* RX_MPDU_RES_START_API_S_VER_3, - RX_MPDU_RES_START_API_S_VER_5 */ + * RX_MPDU_RES_START_API_S_VER_5, + * RX_MPDU_RES_START_API_S_VER_6 + */ /** * struct iwl_rx_mpdu_desc - RX MPDU descriptor @@ -668,9 +671,10 @@ struct iwl_rx_mpdu_desc { */ __le16 phy_info; /** - * @mac_phy_idx: MAC/PHY index + * @mac_phy_band: MAC ID, PHY ID, band; + * see &enum iwl_rx_mpdu_mac_phy_band */ - u8 mac_phy_idx; + u8 mac_phy_band; /* DW4 */ union { struct { @@ -722,8 +726,10 @@ struct iwl_rx_mpdu_desc { struct iwl_rx_mpdu_desc_v3 v3; }; } __packed; /* RX_MPDU_RES_START_API_S_VER_3, - RX_MPDU_RES_START_API_S_VER_4, - RX_MPDU_RES_START_API_S_VER_5 */ + * RX_MPDU_RES_START_API_S_VER_4, + * RX_MPDU_RES_START_API_S_VER_5, + * RX_MPDU_RES_START_API_S_VER_6 + */ #define IWL_RX_DESC_SIZE_V1 offsetofend(struct iwl_rx_mpdu_desc, v1) @@ -819,7 +825,7 @@ struct iwl_rx_no_data { * 15:8 chain-B, measured at FINA time (FINA_ENERGY), 16:23 channel * @on_air_rise_time: GP2 during on air rise * @fr_time: frame time - * @rate: rate/mcs of frame + * @rate: rate/mcs of frame, format depends on the notification version * @phy_info: &enum iwl_rx_phy_eht_data0 and &enum iwl_rx_phy_info_type * @rx_vec: DW-12:9 raw RX vectors from DSP according to modulation type. * for VHT: OFDM_RX_VECTOR_SIGA1_OUT, OFDM_RX_VECTOR_SIGA2_OUT @@ -835,9 +841,7 @@ struct iwl_rx_no_data_ver_3 { __le32 rate; __le32 phy_info[2]; __le32 rx_vec[4]; -} __packed; /* RX_NO_DATA_NTFY_API_S_VER_1, - RX_NO_DATA_NTFY_API_S_VER_2 - RX_NO_DATA_NTFY_API_S_VER_3 */ +} __packed; /* RX_NO_DATA_NTFY_API_S_VER_3, _VER_4 */ struct iwl_frame_release { u8 baid; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h index d7f8a276b683..ecbcd5084cd8 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/sta.h @@ -191,6 +191,7 @@ enum iwl_sta_sleep_flag { #define STA_KEY_IDX_INVALID (0xff) #define STA_KEY_MAX_DATA_KEY_NUM (4) #define IWL_MAX_GLOBAL_KEYS (4) +#define IWL_MAX_NUM_IGTKS 2 #define STA_KEY_LEN_WEP40 (5) #define STA_KEY_LEN_WEP104 (13) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h index 0a9f14fb04be..00713a991879 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/stats.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2020 - 2021, 2023 - 2024 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2020-2021, 2023-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -584,6 +584,9 @@ struct iwl_stats_ntfy_per_phy { __le32 last_tx_ch_width_indx; } __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_1 */ +/* unknown channel load (due to not being active on channel) */ +#define IWL_STATS_UNKNOWN_CHANNEL_LOAD 0xffffffff + /** * struct iwl_stats_ntfy_per_sta * diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h index cfa6532a3cdd..58d5a6ef633e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tdls.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018, 2024 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2024-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -50,7 +50,7 @@ struct iwl_tdls_channel_switch_timing { */ struct iwl_tdls_channel_switch_frame { __le32 switch_time_offset; - struct iwl_tx_cmd tx_cmd; + struct iwl_tx_cmd_v6 tx_cmd; u8 data[IWL_TDLS_CH_SW_FRAME_MAX_SIZE]; } __packed; /* TDLS_STA_CHANNEL_SWITCH_FRAME_API_S_VER_1 */ @@ -131,7 +131,7 @@ struct iwl_tdls_config_cmd { struct iwl_tdls_sta_info sta_info[IWL_TDLS_STA_COUNT]; __le32 pti_req_data_offset; - struct iwl_tx_cmd pti_req_tx_cmd; + struct iwl_tx_cmd_v6 pti_req_tx_cmd; u8 pti_req_template[]; } __packed; /* TDLS_CONFIG_CMD_API_S_VER_1 */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h index 18d030334a6a..f586379d66dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/time-event.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2020, 2022-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2020, 2022-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -351,7 +351,7 @@ enum iwl_roc_activity { }; /* ROC_ACTIVITY_API_E_VER_1 */ /* - * ROC command + * ROC command v5 * * Command requests the firmware to remain on a channel for a certain duration. * @@ -366,7 +366,7 @@ enum iwl_roc_activity { * @max_delay: max delay the ROC can start in TU * @duration: remain on channel duration in TU */ -struct iwl_roc_req { +struct iwl_roc_req_v5 { __le32 action; __le32 activity; __le32 sta_id; @@ -375,7 +375,41 @@ struct iwl_roc_req { __le16 reserved; __le32 max_delay; __le32 duration; -} __packed; /* ROC_CMD_API_S_VER_3 */ +} __packed; /* ROC_CMD_API_S_VER_5 */ + +/* + * ROC command + * + * Command requests the firmware to remain on a channel for a certain duration. + * + * ( MAC_CONF_GROUP 0x3, ROC_CMD 0xE ) + * + * @action: action to perform, see &enum iwl_ctxt_action + * @activity: type of activity, see &enum iwl_roc_activity + * @sta_id: station id, resumed during "Remain On Channel" activity. + * @channel_info: &struct iwl_fw_channel_info + * @node_addr: node MAC address for Rx filtering + * @reserved1: align to a dword + * @max_delay: max delay the ROC can start in TU + * @duration: remain on channel duration in TU + * @interval: interval between repetitions (when repetitions > 1). + * @repetitions: number of repetitions + * 0xFF: infinite repetitions. 0 or 1: single repetition. + * @reserved2: align to a dword + */ +struct iwl_roc_req { + __le32 action; + __le32 activity; + __le32 sta_id; + struct iwl_fw_channel_info channel_info; + u8 node_addr[ETH_ALEN]; + __le16 reserved1; + __le32 max_delay; + __le32 duration; + __le32 interval; + u8 repetitions; + u8 reserved2[3]; +} __packed; /* ROC_CMD_API_S_VER_6 */ /* * ROC notification diff --git a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h index 0a39e4b6eb62..557832563f89 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/api/tx.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_fw_api_tx_h__ @@ -151,7 +151,7 @@ enum iwl_tx_cmd_sec_ctrl { #define IWL_LOW_RETRY_LIMIT 7 /** - * enum iwl_tx_offload_assist_flags_pos - set %iwl_tx_cmd offload_assist values + * enum iwl_tx_offload_assist_flags_pos - set %iwl_tx_cmd_v6 offload_assist values * @TX_CMD_OFFLD_IP_HDR: offset to start of IP header (in words) * from mac header end. For normal case it is 4 words for SNAP. * note: tx_cmd, mac header and pad are not counted in the offset. @@ -181,7 +181,7 @@ enum iwl_tx_offload_assist_flags_pos { /* TODO: complete documentation for try_cnt and btkill_cnt */ /** - * struct iwl_tx_cmd - TX command struct to FW + * struct iwl_tx_cmd_v6 - TX command struct to FW * ( TX_CMD = 0x1c ) * @len: in bytes of the payload, see below for details * @offload_assist: TX offload configuration @@ -221,7 +221,7 @@ enum iwl_tx_offload_assist_flags_pos { * After the struct fields the MAC header is placed, plus any padding, * and then the actial payload. */ -struct iwl_tx_cmd { +struct iwl_tx_cmd_v6 { __le16 len; __le16 offload_assist; __le32 tx_flags; @@ -258,7 +258,7 @@ struct iwl_dram_sec_info { } __packed; /* DRAM_SEC_INFO_API_S_VER_1 */ /** - * struct iwl_tx_cmd_gen2 - TX command struct to FW for 22000 devices + * struct iwl_tx_cmd_v9 - TX command struct to FW for 22000 devices * ( TX_CMD = 0x1c ) * @len: in bytes of the payload, see below for details * @offload_assist: TX offload configuration @@ -268,7 +268,7 @@ struct iwl_dram_sec_info { * cleared. Combination of RATE_MCS_* * @hdr: 802.11 header */ -struct iwl_tx_cmd_gen2 { +struct iwl_tx_cmd_v9 { __le16 len; __le16 offload_assist; __le32 flags; @@ -279,18 +279,18 @@ struct iwl_tx_cmd_gen2 { TX_CMD_API_S_VER_9 */ /** - * struct iwl_tx_cmd_gen3 - TX command struct to FW for AX210+ devices + * struct iwl_tx_cmd - TX command struct to FW for AX210+ devices * ( TX_CMD = 0x1c ) * @len: in bytes of the payload, see below for details * @flags: combination of &enum iwl_tx_cmd_flags * @offload_assist: TX offload configuration * @dram_info: FW internal DRAM storage * @rate_n_flags: rate for *all* Tx attempts, if TX_CMD_FLG_STA_RATE_MSK is - * cleared. Combination of RATE_MCS_* + * cleared. Combination of RATE_MCS_*; format depends on version * @reserved: reserved * @hdr: 802.11 header */ -struct iwl_tx_cmd_gen3 { +struct iwl_tx_cmd { __le16 len; __le16 flags; __le32 offload_assist; @@ -298,7 +298,9 @@ struct iwl_tx_cmd_gen3 { __le32 rate_n_flags; u8 reserved[8]; struct ieee80211_hdr hdr[]; -} __packed; /* TX_CMD_API_S_VER_8, TX_CMD_API_S_VER_10 */ +} __packed; /* TX_CMD_API_S_VER_10, + * TX_CMD_API_S_VER_11 + */ /* * TX response related data @@ -549,7 +551,7 @@ struct iwl_tx_resp_v3 { * @failure_rts: num of failures due to unsuccessful RTS * @failure_frame: num failures due to no ACK (unused for agg) * @initial_rate: for non-agg: rate of the successful Tx. For agg: rate of the - * Tx of all the batch. RATE_MCS_* + * Tx of all the batch. RATE_MCS_*; format depends on command version * @wireless_media_time: for non-agg: RTS + CTS + frame tx attempts time + ACK. * for agg: RTS + CTS + aggregation tx time + block-ack time. * in usec. @@ -600,8 +602,10 @@ struct iwl_tx_resp { __le16 reserved2; struct agg_tx_status status; } __packed; /* TX_RSP_API_S_VER_6, - TX_RSP_API_S_VER_7, - TX_RSP_API_S_VER_8 */ + * TX_RSP_API_S_VER_7, + * TX_RSP_API_S_VER_8, + * TX_RSP_API_S_VER_9 + */ /** * struct iwl_mvm_ba_notif - notifies about reception of BA @@ -701,7 +705,8 @@ enum iwl_mvm_ba_resp_flags { * @rts_retry_cnt: RTS retry count * @reserved: reserved (for alignment) * @wireless_time: Wireless-media time - * @tx_rate: the rate the aggregation was sent at + * @tx_rate: the rate the aggregation was sent at. Format depends on command + * version. * @tfd_cnt: number of TFD-Q elements * @ra_tid_cnt: number of RATID-Q elements * @tfd: array of TFD queue status updates. See &iwl_compressed_ba_tfd @@ -730,7 +735,8 @@ struct iwl_compressed_ba_notif { DECLARE_FLEX_ARRAY(struct iwl_compressed_ba_tfd, tfd); }; } __packed; /* COMPRESSED_BA_RES_API_S_VER_4, - COMPRESSED_BA_RES_API_S_VER_5 */ + COMPRESSED_BA_RES_API_S_VER_6, + COMPRESSED_BA_RES_API_S_VER_7 */ /** * struct iwl_mac_beacon_cmd_v6 - beacon template command @@ -742,7 +748,7 @@ struct iwl_compressed_ba_notif { * @frame: the template of the beacon frame */ struct iwl_mac_beacon_cmd_v6 { - struct iwl_tx_cmd tx; + struct iwl_tx_cmd_v6 tx; __le32 template_id; __le32 tim_idx; __le32 tim_size; @@ -761,7 +767,7 @@ struct iwl_mac_beacon_cmd_v6 { * @frame: the template of the beacon frame */ struct iwl_mac_beacon_cmd_v7 { - struct iwl_tx_cmd tx; + struct iwl_tx_cmd_v6 tx; __le32 template_id; __le32 tim_idx; __le32 tim_size; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c index fb2ea38e89ac..ea739ebe7cb0 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -187,7 +187,7 @@ static void iwl_fw_dump_rxf(struct iwl_fw_runtime *fwrt, /* Pull RXF2 */ iwl_fwrt_dump_rxf(fwrt, dump_data, cfg->rxfifo2_size, RXF_DIFF_FROM_PREV + - fwrt->trans->trans_cfg->umac_prph_offset, 1); + fwrt->trans->mac_cfg->umac_prph_offset, 1); /* Pull LMAC2 RXF1 */ if (fwrt->smem_cfg.num_lmacs > 1) iwl_fwrt_dump_rxf(fwrt, dump_data, @@ -558,41 +558,71 @@ static void iwl_dump_prph(struct iwl_fw_runtime *fwrt, } /* - * alloc_sgtable - allocates scallerlist table in the given size, - * fills it with pages and returns it + * alloc_sgtable - allocates (chained) scatterlist in the given size, + * fills it with pages and returns it * @size: the size (in bytes) of the table -*/ -static struct scatterlist *alloc_sgtable(int size) + */ +static struct scatterlist *alloc_sgtable(ssize_t size) { - int alloc_size, nents, i; - struct page *new_page; - struct scatterlist *iter; - struct scatterlist *table; + struct scatterlist *result = NULL, *prev; + int nents, i, n_prev; nents = DIV_ROUND_UP(size, PAGE_SIZE); - table = kcalloc(nents, sizeof(*table), GFP_KERNEL); - if (!table) - return NULL; - sg_init_table(table, nents); - iter = table; - for_each_sg(table, iter, sg_nents(table), i) { - new_page = alloc_page(GFP_KERNEL); - if (!new_page) { - /* release all previous allocated pages in the table */ - iter = table; - for_each_sg(table, iter, sg_nents(table), i) { - new_page = sg_page(iter); - if (new_page) - __free_page(new_page); - } - kfree(table); + +#define N_ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(*result)) + /* + * We need an additional entry for table chaining, + * this ensures the loop can finish i.e. we can + * fit at least two entries per page (obviously, + * many more really fit.) + */ + BUILD_BUG_ON(N_ENTRIES_PER_PAGE < 2); + + while (nents > 0) { + struct scatterlist *new, *iter; + int n_fill, n_alloc; + + if (nents <= N_ENTRIES_PER_PAGE) { + /* last needed table */ + n_fill = nents; + n_alloc = nents; + nents = 0; + } else { + /* fill a page with entries */ + n_alloc = N_ENTRIES_PER_PAGE; + /* reserve one for chaining */ + n_fill = n_alloc - 1; + nents -= n_fill; + } + + new = kcalloc(n_alloc, sizeof(*new), GFP_KERNEL); + if (!new) { + if (result) + _devcd_free_sgtable(result); return NULL; } - alloc_size = min_t(int, size, PAGE_SIZE); - size -= PAGE_SIZE; - sg_set_page(iter, new_page, alloc_size, 0); + sg_init_table(new, n_alloc); + + if (!result) + result = new; + else + sg_chain(prev, n_prev, new); + prev = new; + n_prev = n_alloc; + + for_each_sg(new, iter, n_fill, i) { + struct page *new_page = alloc_page(GFP_KERNEL); + + if (!new_page) { + _devcd_free_sgtable(result); + return NULL; + } + + sg_set_page(iter, new_page, PAGE_SIZE, 0); + } } - return table; + + return result; } static void iwl_fw_get_prph_len(struct iwl_fw_runtime *fwrt, @@ -624,10 +654,10 @@ static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr, { u32 range_len; - if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { range_len = ARRAY_SIZE(iwl_prph_dump_addr_ax210); handler(fwrt, iwl_prph_dump_addr_ax210, range_len, ptr); - } else if (fwrt->trans->trans_cfg->device_family >= + } else if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { range_len = ARRAY_SIZE(iwl_prph_dump_addr_22000); handler(fwrt, iwl_prph_dump_addr_22000, range_len, ptr); @@ -635,7 +665,7 @@ static void iwl_fw_prph_handler(struct iwl_fw_runtime *fwrt, void *ptr, range_len = ARRAY_SIZE(iwl_prph_dump_addr_comm); handler(fwrt, iwl_prph_dump_addr_comm, range_len, ptr); - if (fwrt->trans->trans_cfg->mq_rx_supported) { + if (fwrt->trans->mac_cfg->mq_rx_supported) { range_len = ARRAY_SIZE(iwl_prph_dump_addr_9000); handler(fwrt, iwl_prph_dump_addr_9000, range_len, ptr); } @@ -779,13 +809,14 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, const struct iwl_fw_dbg_mem_seg_tlv *fw_mem = fwrt->fw->dbg.mem_tlv; struct iwl_fwrt_shared_mem_cfg *mem_cfg = &fwrt->smem_cfg; u32 file_len, fifo_len = 0, prph_len = 0, radio_len = 0; - u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->cfg->smem_len; + u32 smem_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->mac_cfg->base->smem_len; u32 sram2_len = fwrt->fw->dbg.n_mem_tlv ? 0 : fwrt->trans->cfg->dccm2_len; int i; /* SRAM - include stack CCM if driver knows the values for it */ - if (!fwrt->trans->cfg->dccm_offset || !fwrt->trans->cfg->dccm_len) { + if (!fwrt->trans->cfg->dccm_offset || + !fwrt->trans->cfg->dccm_len) { const struct fw_img *img; if (fwrt->cur_fw_img >= IWL_UCODE_TYPE_MAX) @@ -808,7 +839,7 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, iwl_fw_prph_handler(fwrt, &prph_len, iwl_fw_get_prph_len); - if (fwrt->trans->trans_cfg->device_family == + if (fwrt->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000 && iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_RADIO_REG)) radio_len = sizeof(*dump_data) + RADIO_REG_MAX_READ; @@ -846,7 +877,7 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { file_len += sizeof(*dump_data) + - fwrt->trans->cfg->d3_debug_data_length * 2; + fwrt->trans->mac_cfg->base->d3_debug_data_length * 2; } /* If we only want a monitor dump, reset the file length */ @@ -874,13 +905,14 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, dump_data->len = cpu_to_le32(sizeof(*dump_info)); dump_info = (void *)dump_data->data; dump_info->hw_type = - cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->hw_rev)); + cpu_to_le32(CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev)); dump_info->hw_step = - cpu_to_le32(fwrt->trans->hw_rev_step); + cpu_to_le32(fwrt->trans->info.hw_rev_step); memcpy(dump_info->fw_human_readable, fwrt->fw->human_readable, sizeof(dump_info->fw_human_readable)); - strscpy_pad(dump_info->dev_human_readable, fwrt->trans->name, - sizeof(dump_info->dev_human_readable)); + strscpy_pad(dump_info->dev_human_readable, + fwrt->trans->info.name, + sizeof(dump_info->dev_human_readable)); strscpy_pad(dump_info->bus_human_readable, fwrt->dev->bus->name, sizeof(dump_info->bus_human_readable)); dump_info->num_of_lmacs = fwrt->smem_cfg.num_lmacs; @@ -966,7 +998,7 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, } iwl_fw_dump_mem(fwrt, &dump_data, smem_len, - fwrt->trans->cfg->smem_offset, + fwrt->trans->mac_cfg->base->smem_offset, IWL_FW_ERROR_DUMP_MEM_SMEM); iwl_fw_dump_mem(fwrt, &dump_data, sram2_len, @@ -975,8 +1007,8 @@ iwl_fw_error_dump_file(struct iwl_fw_runtime *fwrt, } if (iwl_fw_dbg_is_d3_debug_enabled(fwrt) && fwrt->dump.d3_debug_data) { - u32 addr = fwrt->trans->cfg->d3_debug_data_base_addr; - size_t data_size = fwrt->trans->cfg->d3_debug_data_length; + u32 addr = fwrt->trans->mac_cfg->base->d3_debug_data_base_addr; + size_t data_size = fwrt->trans->mac_cfg->base->d3_debug_data_length; dump_data->type = cpu_to_le32(IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); dump_data->len = cpu_to_le32(data_size * 2); @@ -1079,7 +1111,7 @@ static int iwl_dump_ini_prph_phy_iter_common(struct iwl_fw_runtime *fwrt, range->internal_base_addr = cpu_to_le32(addr); range->range_data_size = size; - if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) indirect_wr_addr = WMAL_INDRCT_CMD1; indirect_wr_addr += le32_to_cpu(offset); @@ -1236,7 +1268,7 @@ static int iwl_dump_ini_paging_iter(struct iwl_fw_runtime *fwrt, /* all paged index start from 1 to skip CSS section */ idx++; - if (!fwrt->trans->trans_cfg->gen2) + if (!fwrt->trans->mac_cfg->gen2) return _iwl_dump_ini_paging_iter(fwrt, range_ptr, range_len, idx); range = range_ptr; @@ -1760,7 +1792,7 @@ iwl_dump_ini_mon_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id, data->write_ptr = iwl_get_mon_reg(fwrt, alloc_id, &addrs->write_ptr); - if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { u32 wrt_ptr = le32_to_cpu(data->write_ptr); data->write_ptr = cpu_to_le32(wrt_ptr >> 2); @@ -1787,7 +1819,7 @@ iwl_dump_ini_mon_dram_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id = le32_to_cpu(reg->dram_alloc_id); return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump, - &fwrt->trans->cfg->mon_dram_regs); + &fwrt->trans->mac_cfg->base->mon_dram_regs); } static void * @@ -1800,7 +1832,7 @@ iwl_dump_ini_mon_smem_fill_header(struct iwl_fw_runtime *fwrt, u32 alloc_id = le32_to_cpu(reg->internal_buffer.alloc_id); return iwl_dump_ini_mon_fill_header(fwrt, alloc_id, mon_dump, - &fwrt->trans->cfg->mon_smem_regs); + &fwrt->trans->mac_cfg->base->mon_smem_regs); } static void * @@ -1814,7 +1846,7 @@ iwl_dump_ini_mon_dbgi_fill_header(struct iwl_fw_runtime *fwrt, /* no offset calculation later */ IWL_FW_INI_ALLOCATION_ID_DBGC1, mon_dump, - &fwrt->trans->cfg->mon_dbgi_regs); + &fwrt->trans->mac_cfg->base->mon_dbgi_regs); } static void * @@ -1879,7 +1911,7 @@ iwl_dump_ini_mem_block_ranges(struct iwl_fw_runtime *fwrt, static u32 iwl_dump_ini_paging_ranges(struct iwl_fw_runtime *fwrt, struct iwl_dump_ini_region_data *reg_data) { - if (fwrt->trans->trans_cfg->gen2) { + if (fwrt->trans->mac_cfg->gen2) { if (fwrt->trans->init_dram.paging_cnt) return fwrt->trans->init_dram.paging_cnt - 1; else @@ -1991,7 +2023,7 @@ iwl_dump_ini_paging_get_size(struct iwl_fw_runtime *fwrt, /* start from 1 to skip CSS section */ for (i = 1; i <= iwl_dump_ini_paging_ranges(fwrt, reg_data); i++) { size += range_header_len; - if (fwrt->trans->trans_cfg->gen2) + if (fwrt->trans->mac_cfg->gen2) size += fwrt->trans->init_dram.paging[i].size; else size += fwrt->fw_paging_db[i].fw_paging_size; @@ -2345,7 +2377,7 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, struct iwl_fw_ini_dump_cfg_name *cfg_name; u32 size = sizeof(*tlv) + sizeof(*dump); u32 num_of_cfg_names = 0; - u32 hw_type; + u32 hw_type, is_cdb, is_jacket; list_for_each_entry(node, &fwrt->trans->dbg.debug_info_tlv_list, list) { size += sizeof(*cfg_name); @@ -2373,33 +2405,24 @@ static u32 iwl_dump_ini_info(struct iwl_fw_runtime *fwrt, dump->ver_type = cpu_to_le32(fwrt->dump.fw_ver.type); dump->ver_subtype = cpu_to_le32(fwrt->dump.fw_ver.subtype); - dump->hw_step = cpu_to_le32(fwrt->trans->hw_rev_step); + dump->hw_step = cpu_to_le32(fwrt->trans->info.hw_rev_step); - /* - * Several HWs all have type == 0x42, so we'll override this value - * according to the detected HW - */ - hw_type = CSR_HW_REV_TYPE(fwrt->trans->hw_rev); - if (hw_type == IWL_AX210_HW_TYPE) { - u32 prph_val = iwl_read_umac_prph(fwrt->trans, WFPM_OTP_CFG1_ADDR); - u32 is_jacket = !!(prph_val & WFPM_OTP_CFG1_IS_JACKET_BIT); - u32 is_cdb = !!(prph_val & WFPM_OTP_CFG1_IS_CDB_BIT); - u32 masked_bits = is_jacket | (is_cdb << 1); + hw_type = CSR_HW_REV_TYPE(fwrt->trans->info.hw_rev); + + is_cdb = CSR_HW_RFID_IS_CDB(fwrt->trans->info.hw_rf_id); + is_jacket = !!(iwl_read_umac_prph(fwrt->trans, WFPM_OTP_CFG1_ADDR) & + WFPM_OTP_CFG1_IS_JACKET_BIT); + + /* Use bits 12 and 13 to indicate jacket/CDB, respectively */ + hw_type |= (is_jacket | (is_cdb << 1)) << IWL_JACKET_CDB_SHIFT; - /* - * The HW type depends on certain bits in this case, so add - * these bits to the HW type. We won't have collisions since we - * add these bits after the highest possible bit in the mask. - */ - hw_type |= masked_bits << IWL_AX210_HW_TYPE_ADDITION_SHIFT; - } dump->hw_type = cpu_to_le32(hw_type); dump->rf_id_flavor = - cpu_to_le32(CSR_HW_RFID_FLAVOR(fwrt->trans->hw_rf_id)); - dump->rf_id_dash = cpu_to_le32(CSR_HW_RFID_DASH(fwrt->trans->hw_rf_id)); - dump->rf_id_step = cpu_to_le32(CSR_HW_RFID_STEP(fwrt->trans->hw_rf_id)); - dump->rf_id_type = cpu_to_le32(CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)); + cpu_to_le32(CSR_HW_RFID_FLAVOR(fwrt->trans->info.hw_rf_id)); + dump->rf_id_dash = cpu_to_le32(CSR_HW_RFID_DASH(fwrt->trans->info.hw_rf_id)); + dump->rf_id_step = cpu_to_le32(CSR_HW_RFID_STEP(fwrt->trans->info.hw_rf_id)); + dump->rf_id_type = cpu_to_le32(CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id)); dump->lmac_major = cpu_to_le32(fwrt->dump.fw_ver.lmac_major); dump->lmac_minor = cpu_to_le32(fwrt->dump.fw_ver.lmac_minor); @@ -2588,29 +2611,34 @@ static const struct iwl_dump_ini_mem_ops iwl_dump_ini_region_ops[] = { }, }; -static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, - struct iwl_fwrt_dump_data *dump_data, - struct list_head *list) +enum iwl_dump_ini_region_selector { + IWL_INI_DUMP_ALL_REGIONS, + IWL_INI_DUMP_EARLY_REGIONS, + IWL_INI_DUMP_LATE_REGIONS, +}; + +static bool iwl_dump_due_to_error(enum iwl_fw_ini_time_point tp_id) { - struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig; - enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point); - struct iwl_dump_ini_region_data reg_data = { - .dump_data = dump_data, - }; - struct iwl_dump_ini_region_data imr_reg_data = { - .dump_data = dump_data, - }; - int i; - u32 size = 0; - u64 regions_mask = le64_to_cpu(trigger->regions_mask) & - ~(fwrt->trans->dbg.unsupported_region_msk); + return tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT || + tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR; +} - BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask)); - BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) < - ARRAY_SIZE(fwrt->trans->dbg.active_regions)); +static u32 +iwl_dump_ini_dump_regions(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_dump_data *dump_data, + struct list_head *list, + enum iwl_fw_ini_time_point tp_id, + u64 regions_mask, + struct iwl_dump_ini_region_data *imr_reg_data, + enum iwl_dump_ini_region_selector which) +{ + u32 size = 0; - for (i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) { - u32 reg_type; + for (int i = 0; i < ARRAY_SIZE(fwrt->trans->dbg.active_regions); i++) { + struct iwl_dump_ini_region_data reg_data = { + .dump_data = dump_data, + }; + u32 reg_type, dp; struct iwl_fw_ini_region_tlv *reg; if (!(BIT_ULL(i) & regions_mask)) @@ -2628,6 +2656,8 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, if (reg_type >= ARRAY_SIZE(iwl_dump_ini_region_ops)) continue; + dp = le32_get_bits(reg->id, IWL_FW_INI_REGION_DUMP_POLICY_MASK); + if ((reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY || reg_type == IWL_FW_INI_REGION_PERIPHERY_PHY_RANGE || reg_type == IWL_FW_INI_REGION_PERIPHERY_SNPS_DPHYIP) && @@ -2637,6 +2667,20 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, tp_id); continue; } + + switch (which) { + case IWL_INI_DUMP_ALL_REGIONS: + break; + case IWL_INI_DUMP_EARLY_REGIONS: + if (!(dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET)) + continue; + break; + case IWL_INI_DUMP_LATE_REGIONS: + if (dp & IWL_FW_IWL_DEBUG_DUMP_POLICY_BEFORE_RESET) + continue; + break; + } + /* * DRAM_IMR can be collected only for FW/HW error timepoint * when fw is not alive. In addition, it must be collected @@ -2644,9 +2688,9 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, * debug data which also need to be collected. */ if (reg_type == IWL_FW_INI_REGION_DRAM_IMR) { - if (tp_id == IWL_FW_INI_TIME_POINT_FW_ASSERT || - tp_id == IWL_FW_INI_TIME_POINT_FW_HW_ERROR) - imr_reg_data.reg_tlv = fwrt->trans->dbg.active_regions[i]; + if (iwl_dump_due_to_error(tp_id)) + imr_reg_data->reg_tlv = + fwrt->trans->dbg.active_regions[i]; else IWL_INFO(fwrt, "WRT: trying to collect DRAM_IMR at time point: %d, skipping\n", @@ -2659,9 +2703,48 @@ static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, size += iwl_dump_ini_mem(fwrt, list, ®_data, &iwl_dump_ini_region_ops[reg_type]); } + + return size; +} + +static u32 iwl_dump_ini_trigger(struct iwl_fw_runtime *fwrt, + struct iwl_fwrt_dump_data *dump_data, + struct list_head *list) +{ + struct iwl_fw_ini_trigger_tlv *trigger = dump_data->trig; + enum iwl_fw_ini_time_point tp_id = le32_to_cpu(trigger->time_point); + struct iwl_dump_ini_region_data imr_reg_data = { + .dump_data = dump_data, + }; + u32 size = 0; + u64 regions_mask = le64_to_cpu(trigger->regions_mask) & + ~(fwrt->trans->dbg.unsupported_region_msk); + + BUILD_BUG_ON(sizeof(trigger->regions_mask) != sizeof(regions_mask)); + BUILD_BUG_ON((sizeof(trigger->regions_mask) * BITS_PER_BYTE) < + ARRAY_SIZE(fwrt->trans->dbg.active_regions)); + + if (trigger->apply_policy & + cpu_to_le32(IWL_FW_INI_APPLY_POLICY_SPLIT_DUMP_RESET)) { + size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, + regions_mask, &imr_reg_data, + IWL_INI_DUMP_EARLY_REGIONS); + iwl_trans_pcie_fw_reset_handshake(fwrt->trans); + size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, + regions_mask, &imr_reg_data, + IWL_INI_DUMP_LATE_REGIONS); + } else { + if (fw_has_capa(&fwrt->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT) && + iwl_dump_due_to_error(tp_id)) + iwl_trans_pcie_fw_reset_handshake(fwrt->trans); + size += iwl_dump_ini_dump_regions(fwrt, dump_data, list, tp_id, + regions_mask, &imr_reg_data, + IWL_INI_DUMP_ALL_REGIONS); + } /* collect DRAM_IMR region in the last */ if (imr_reg_data.reg_tlv) - size += iwl_dump_ini_mem(fwrt, list, ®_data, + size += iwl_dump_ini_mem(fwrt, list, &imr_reg_data, &iwl_dump_ini_region_ops[IWL_FW_INI_REGION_DRAM_IMR]); if (size) { @@ -3042,9 +3125,8 @@ int iwl_fw_start_dbg_conf(struct iwl_fw_runtime *fwrt, u8 conf_id) } IWL_EXPORT_SYMBOL(iwl_fw_start_dbg_conf); -void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, - u32 timepoint, - u32 timepoint_data) +static void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, + u32 timepoint, u32 timepoint_data) { struct iwl_dbg_dump_complete_cmd hcmd_data; struct iwl_host_cmd hcmd = { @@ -3072,6 +3154,7 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) struct iwl_fw_dbg_params params = {0}; struct iwl_fwrt_dump_data *dump_data = &fwrt->dump.wks[wk_idx].dump_data; + if (!test_bit(wk_idx, &fwrt->dump.active_wks)) return; @@ -3096,9 +3179,9 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection start\n"); if (iwl_trans_dbg_ini_valid(fwrt->trans)) - iwl_fw_error_ini_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data); + iwl_fw_error_ini_dump(fwrt, dump_data); else - iwl_fw_error_dump(fwrt, &fwrt->dump.wks[wk_idx].dump_data); + iwl_fw_error_dump(fwrt, dump_data); IWL_DEBUG_FW_INFO(fwrt, "WRT: Data collection done\n"); iwl_fw_dbg_stop_restart_recording(fwrt, ¶ms, false); @@ -3115,7 +3198,6 @@ static void iwl_fw_dbg_collect_sync(struct iwl_fw_runtime *fwrt, u8 wk_idx) if (fwrt->trans->dbg.last_tp_resetfw == IWL_FW_INI_RESET_FW_MODE_STOP_FW_ONLY) iwl_force_nmi(fwrt->trans); - out: if (iwl_trans_dbg_ini_valid(fwrt->trans)) { iwl_fw_error_dump_data_free(dump_data); @@ -3202,13 +3284,13 @@ void iwl_fw_error_dump_wk(struct work_struct *work) void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt) { - const struct iwl_cfg *cfg = fwrt->trans->cfg; + const struct iwl_mac_cfg *mac_cfg = fwrt->trans->mac_cfg; if (!iwl_fw_dbg_is_d3_debug_enabled(fwrt)) return; if (!fwrt->dump.d3_debug_data) { - fwrt->dump.d3_debug_data = kmalloc(cfg->d3_debug_data_length, + fwrt->dump.d3_debug_data = kmalloc(mac_cfg->base->d3_debug_data_length, GFP_KERNEL); if (!fwrt->dump.d3_debug_data) { IWL_ERR(fwrt, @@ -3218,15 +3300,15 @@ void iwl_fw_dbg_read_d3_debug_data(struct iwl_fw_runtime *fwrt) } /* if the buffer holds previous debug data it is overwritten */ - iwl_trans_read_mem_bytes(fwrt->trans, cfg->d3_debug_data_base_addr, + iwl_trans_read_mem_bytes(fwrt->trans, mac_cfg->base->d3_debug_data_base_addr, fwrt->dump.d3_debug_data, - cfg->d3_debug_data_length); + mac_cfg->base->d3_debug_data_length); if (fwrt->sanitize_ops && fwrt->sanitize_ops->frob_mem) fwrt->sanitize_ops->frob_mem(fwrt->sanitize_ctx, - cfg->d3_debug_data_base_addr, + mac_cfg->base->d3_debug_data_base_addr, fwrt->dump.d3_debug_data, - cfg->d3_debug_data_length); + mac_cfg->base->d3_debug_data_length); } IWL_EXPORT_SYMBOL(iwl_fw_dbg_read_d3_debug_data); @@ -3261,7 +3343,7 @@ static int iwl_fw_dbg_suspend_resume_hcmd(struct iwl_trans *trans, bool suspend) static void iwl_fw_dbg_stop_recording(struct iwl_trans *trans, struct iwl_fw_dbg_params *params) { - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) { + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) { iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); return; } @@ -3285,7 +3367,7 @@ static int iwl_fw_dbg_restart_recording(struct iwl_trans *trans, if (!params) return -EIO; - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) { + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) { iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x100); iwl_clear_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); iwl_set_bits_prph(trans, MON_BUFF_SAMPLE_CTL, 0x1); @@ -3387,7 +3469,7 @@ void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt) GENMASK(31, IWL_FW_DBG_DOMAIN_POS + 1)); /* supported starting from 9000 devices */ - if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000) + if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000) return; if (fwrt->trans->dbg.yoyo_bin_loaded || (preset && preset != 1)) diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h index 87998374f459..8034c9ecba69 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dbg.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/dbg.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2019, 2021-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2019, 2021-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -201,7 +201,7 @@ static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt) { return fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_D3_DEBUG) && - fwrt->trans->cfg->d3_debug_data_length && fwrt->ops && + fwrt->trans->mac_cfg->base->d3_debug_data_length && fwrt->ops && fwrt->ops->d3_debug_enable && fwrt->ops->d3_debug_enable(fwrt->ops_ctx) && iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_D3_DEBUG_DATA); @@ -210,7 +210,7 @@ static inline bool iwl_fw_dbg_is_d3_debug_enabled(struct iwl_fw_runtime *fwrt) static inline bool iwl_fw_dbg_is_paging_enabled(struct iwl_fw_runtime *fwrt) { return iwl_fw_dbg_type_on(fwrt, IWL_FW_ERROR_DUMP_PAGING) && - !fwrt->trans->trans_cfg->gen2 && + !fwrt->trans->mac_cfg->gen2 && fwrt->cur_fw_img < IWL_UCODE_TYPE_MAX && fwrt->fw->img[fwrt->cur_fw_img].paging_mem_size && fwrt->fw_paging_db[0].fw_paging_block; @@ -324,9 +324,6 @@ static inline void iwl_fwrt_update_fw_versions(struct iwl_fw_runtime *fwrt, } void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt); -void iwl_send_dbg_dump_complete_cmd(struct iwl_fw_runtime *fwrt, - u32 timepoint, - u32 timepoint_data); bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id); void iwl_fw_disable_dbg_asserts(struct iwl_fw_runtime *fwrt); void iwl_fw_dbg_clear_monitor_buf(struct iwl_fw_runtime *fwrt); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c index f0c813d675f4..c70f2a20f7d5 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/debugfs.c @@ -311,7 +311,7 @@ static ssize_t iwl_dbgfs_fw_ver_read(struct iwl_fw_runtime *fwrt, pos += scnprintf(pos, endpos - pos, "FW: %s\n", fwrt->fw->human_readable); pos += scnprintf(pos, endpos - pos, "Device: %s\n", - fwrt->trans->name); + fwrt->trans->info.name); pos += scnprintf(pos, endpos - pos, "Bus: %s\n", fwrt->dev->bus->name); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h b/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h new file mode 100644 index 000000000000..983acee5cd7d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/fw/dhc-utils.h @@ -0,0 +1,75 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2021, 2025 Intel Corporation + */ +#ifndef __iwl_fw_dhc_utils_h__ +#define __iwl_fw_dhc_utils_h__ + +#include <linux/types.h> +#include "fw/img.h" +#include "api/commands.h" +#include "api/dhc.h" + +/** + * iwl_dhc_resp_status - return status of DHC response + * @fw: firwmware image information + * @pkt: response packet, must not be %NULL + * + * Returns: the status value of the DHC command or (u32)-1 if the + * response was too short. + */ +static inline u32 iwl_dhc_resp_status(const struct iwl_fw *fw, + struct iwl_rx_packet *pkt) +{ + if (iwl_fw_lookup_notif_ver(fw, IWL_ALWAYS_LONG_GROUP, + DEBUG_HOST_COMMAND, 1) >= 2) { + struct iwl_dhc_cmd_resp *resp = (void *)pkt->data; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp)) + return (u32)-1; + + return le32_to_cpu(resp->status); + } else { + struct iwl_dhc_cmd_resp_v1 *resp = (void *)pkt->data; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp)) + return (u32)-1; + + return le32_to_cpu(resp->status); + } +} + +/** + * iwl_dhc_resp_data - return data pointer of DHC response + * @fw: firwmware image information + * @pkt: response packet, must not be %NULL + * @len: where to store the length + * + * Returns: The data pointer, or an ERR_PTR() if the data was + * not valid (too short). + */ +static inline void *iwl_dhc_resp_data(const struct iwl_fw *fw, + struct iwl_rx_packet *pkt, + unsigned int *len) +{ + if (iwl_fw_lookup_notif_ver(fw, IWL_ALWAYS_LONG_GROUP, + DEBUG_HOST_COMMAND, 1) >= 2) { + struct iwl_dhc_cmd_resp *resp = (void *)pkt->data; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp)) + return ERR_PTR(-EINVAL); + + *len = iwl_rx_packet_payload_len(pkt) - sizeof(*resp); + return (void *)&resp->data; + } else { + struct iwl_dhc_cmd_resp_v1 *resp = (void *)pkt->data; + + if (iwl_rx_packet_payload_len(pkt) < sizeof(*resp)) + return ERR_PTR(-EINVAL); + + *len = iwl_rx_packet_payload_len(pkt) - sizeof(*resp); + return (void *)&resp->data; + } +} + +#endif /* __iwl_fw_dhc_utils_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/dump.c b/drivers/net/wireless/intel/iwlwifi/fw/dump.c index 8e0c85a1240d..3ec42a4ea801 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/dump.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/dump.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -417,10 +417,10 @@ static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt) struct iwl_trans *trans = fwrt->trans; u32 error, data1; - if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { + if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { error = UMAG_SB_CPU_2_STATUS; data1 = UMAG_SB_CPU_1_STATUS; - } else if (fwrt->trans->trans_cfg->device_family >= + } else if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000) { error = SB_CPU_2_STATUS; data1 = SB_CPU_1_STATUS; @@ -439,7 +439,7 @@ static void iwl_fwrt_dump_iml_error_log(struct iwl_fw_runtime *fwrt) IWL_ERR(fwrt, "0x%08X | IML/ROM data1\n", iwl_read_umac_prph(trans, data1)); - if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) + if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) IWL_ERR(fwrt, "0x%08X | IML/ROM WFPM_AUTH_KEY_0\n", iwl_read_umac_prph(trans, SB_MODIFY_CFG_FLAG)); } @@ -508,7 +508,7 @@ void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt) iwl_fwrt_dump_rcm_error_log(fwrt, 1); iwl_fwrt_dump_iml_error_log(fwrt); iwl_fwrt_dump_fseq_regs(fwrt); - if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { + if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { pc_data = fwrt->trans->dbg.pc_data; if (!iwl_trans_grab_nic_access(fwrt->trans)) @@ -522,7 +522,7 @@ void iwl_fwrt_dump_error_logs(struct iwl_fw_runtime *fwrt) iwl_trans_release_nic_access(fwrt->trans); } - if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { u32 scratch = iwl_read32(fwrt->trans, CSR_FUNC_SCRATCH); IWL_ERR(fwrt, "Function Scratch status:\n"); @@ -540,6 +540,9 @@ bool iwl_fwrt_read_err_table(struct iwl_trans *trans, u32 base, u32 *err_id) } err_info = {}; int ret; + if (err_id) + *err_id = 0; + if (!base) return false; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h index 3af275133da0..cf41021d59ad 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/error-dump.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2014, 2018-2024 Intel Corporation + * Copyright (C) 2014, 2018-2025 Intel Corporation * Copyright (C) 2014-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -372,10 +372,7 @@ struct iwl_fw_ini_dump_cfg_name { u8 cfg_name[IWL_FW_INI_MAX_CFG_NAME]; } __packed; -/* AX210's HW type */ -#define IWL_AX210_HW_TYPE 0x42 -/* How many bits to roll when adding to the HW type of AX210 HW */ -#define IWL_AX210_HW_TYPE_ADDITION_SHIFT 12 +#define IWL_JACKET_CDB_SHIFT 12 /* struct iwl_fw_ini_dump_info - ini dump information * @version: dump version diff --git a/drivers/net/wireless/intel/iwlwifi/fw/file.h b/drivers/net/wireless/intel/iwlwifi/fw/file.h index 9860903ecd3f..5a1ec880ed72 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/file.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/file.h @@ -102,6 +102,7 @@ enum iwl_ucode_tlv_type { IWL_UCODE_TLV_SEC_TABLE_ADDR = 66, IWL_UCODE_TLV_D3_KEK_KCK_ADDR = 67, IWL_UCODE_TLV_CURRENT_PC = 68, + IWL_UCODE_TLV_FSEQ_BIN_VERSION = 72, IWL_UCODE_TLV_FW_NUM_STATIONS = IWL_UCODE_TLV_CONST_BASE + 0, IWL_UCODE_TLV_FW_NUM_LINKS = IWL_UCODE_TLV_CONST_BASE + 1, @@ -402,6 +403,7 @@ typedef unsigned int __bitwise iwl_ucode_tlv_capa_t; * @IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_5G9_FOR_CA: supports (de)activating 5G9 * for CA from BIOS. * @IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT: supports %TAS_UHB_ALLOWED_CANADA + * @IWL_UCODE_TLV_CAPA_EXT_FSEQ_IMAGE_SUPPORT: external FSEQ image support * * @NUM_IWL_UCODE_TLV_CAPA: number of bits used */ @@ -504,6 +506,14 @@ enum iwl_ucode_tlv_capa { IWL_UCODE_TLV_CAPA_MONITOR_PASSIVE_CHANS = (__force iwl_ucode_tlv_capa_t)122, IWL_UCODE_TLV_CAPA_BIOS_OVERRIDE_5G9_FOR_CA = (__force iwl_ucode_tlv_capa_t)123, IWL_UCODE_TLV_CAPA_UHB_CANADA_TAS_SUPPORT = (__force iwl_ucode_tlv_capa_t)124, + IWL_UCODE_TLV_CAPA_EXT_FSEQ_IMAGE_SUPPORT = (__force iwl_ucode_tlv_capa_t)125, + + /* set 4 */ + /** + * @IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT: FW reset handshake is needed + * during assert handling even if the dump isn't split + */ + IWL_UCODE_TLV_CAPA_RESET_DURING_ASSERT = (__force iwl_ucode_tlv_capa_t)(4 * 32 + 0), NUM_IWL_UCODE_TLV_CAPA /* * This construction make both sparse (which cannot increment the previous @@ -994,6 +1004,10 @@ struct iwl_fw_dump_exclude { __le32 addr, size; }; +struct iwl_fw_fseq_bin_version { + __le32 major, minor; +}; /* FW_TLV_FSEQ_BIN_VERSION_S */ + static inline size_t _iwl_tlv_array_len(const struct iwl_ucode_tlv *tlv, size_t fixed_size, size_t var_size) { @@ -1011,4 +1025,18 @@ static inline size_t _iwl_tlv_array_len(const struct iwl_ucode_tlv *tlv, #define iwl_tlv_array_len_with_size(_tlv_ptr, _struct_ptr, _size) \ _iwl_tlv_array_len((_tlv_ptr), sizeof(*(_struct_ptr)), _size) + +/* external FSEQ file */ +#define IWL_FSEQ_FILE "intel/fseq-%04x-%04x" +#define IWL_FSEQ_MAGIC "INTEL-CNV-FSEQ\n\0" + +struct iwl_fseq_file { + char magic[16]; + char version[16]; + __le32 bt_len; + __le32 wifi_len; + u8 reserved[8]; + u8 data[]; +} __packed; + #endif /* __iwl_fw_file_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/init.c b/drivers/net/wireless/intel/iwlwifi/fw/init.c index de87e0e3e072..d1d8058ad29f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/init.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/init.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2019-2021, 2024 Intel Corporation + * Copyright (C) 2019-2021, 2024-2025 Intel Corporation */ #include "iwl-drv.h" #include "runtime.h" @@ -72,7 +72,7 @@ int iwl_set_soc_latency(struct iwl_fw_runtime *fwrt) * values in VER_1, this is backwards-compatible with VER_2, * as long as we don't set any other bits. */ - if (!fwrt->trans->trans_cfg->integrated) + if (!fwrt->trans->mac_cfg->integrated) cmd.flags = cpu_to_le32(SOC_CONFIG_CMD_FLAGS_DISCRETE); BUILD_BUG_ON(IWL_CFG_TRANS_LTR_DELAY_NONE != @@ -84,17 +84,17 @@ int iwl_set_soc_latency(struct iwl_fw_runtime *fwrt) BUILD_BUG_ON(IWL_CFG_TRANS_LTR_DELAY_1820US != SOC_FLAGS_LTR_APPLY_DELAY_1820); - if (fwrt->trans->trans_cfg->ltr_delay != IWL_CFG_TRANS_LTR_DELAY_NONE && - !WARN_ON(!fwrt->trans->trans_cfg->integrated)) - cmd.flags |= le32_encode_bits(fwrt->trans->trans_cfg->ltr_delay, + if (fwrt->trans->mac_cfg->ltr_delay != IWL_CFG_TRANS_LTR_DELAY_NONE && + !WARN_ON(!fwrt->trans->mac_cfg->integrated)) + cmd.flags |= le32_encode_bits(fwrt->trans->mac_cfg->ltr_delay, SOC_FLAGS_LTR_APPLY_DELAY_MASK); if (iwl_fw_lookup_cmd_ver(fwrt->fw, SCAN_REQ_UMAC, IWL_FW_CMD_VER_UNKNOWN) >= 2 && - fwrt->trans->trans_cfg->low_latency_xtal) + fwrt->trans->mac_cfg->low_latency_xtal) cmd.flags |= cpu_to_le32(SOC_CONFIG_CMD_FLAGS_LOW_LATENCY); - cmd.latency = cpu_to_le32(fwrt->trans->trans_cfg->xtal_latency); + cmd.latency = cpu_to_le32(fwrt->trans->mac_cfg->xtal_latency); ret = iwl_trans_send_cmd(fwrt->trans, &hcmd); if (ret) @@ -116,14 +116,14 @@ int iwl_configure_rxq(struct iwl_fw_runtime *fwrt) * The default queue is configured via context info, so if we * have a single queue, there's nothing to do here. */ - if (fwrt->trans->num_rx_queues == 1) + if (fwrt->trans->info.num_rxqs == 1) return 0; - if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_22000) + if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_22000) return 0; /* skip the default queue */ - num_queues = fwrt->trans->num_rx_queues - 1; + num_queues = fwrt->trans->info.num_rxqs - 1; size = struct_size(cmd, data, num_queues); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/paging.c b/drivers/net/wireless/intel/iwlwifi/fw/paging.c index a7b7cae874a2..826409f6f710 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/paging.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/paging.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2019, 2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021, 2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -267,7 +267,7 @@ int iwl_init_paging(struct iwl_fw_runtime *fwrt, enum iwl_ucode_type type) const struct fw_img *fw = &fwrt->fw->img[type]; int ret; - if (fwrt->trans->trans_cfg->gen2) + if (fwrt->trans->mac_cfg->gen2) return 0; /* diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c index 1195e708caa9..4f3c2f7f4f5b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2020-2024 Intel Corporation + * Copyright(c) 2020-2025 Intel Corporation */ #include "iwl-drv.h" @@ -96,8 +96,8 @@ static int iwl_pnvm_handle_section(struct iwl_trans *trans, const u8 *data, "Got IWL_UCODE_TLV_HW_TYPE mac_type 0x%0x rf_id 0x%0x\n", mac_type, rf_id); - if (mac_type == CSR_HW_REV_TYPE(trans->hw_rev) && - rf_id == CSR_HW_RFID_TYPE(trans->hw_rf_id)) + if (mac_type == CSR_HW_REV_TYPE(trans->info.hw_rev) && + rf_id == CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) hw_match = true; break; case IWL_UCODE_TLV_SEC_RT: { @@ -152,8 +152,8 @@ done: if (!hw_match) { IWL_DEBUG_FW(trans, "HW mismatch, skipping PNVM section (need mac_type 0x%x rf_id 0x%x)\n", - CSR_HW_REV_TYPE(trans->hw_rev), - CSR_HW_RFID_TYPE(trans->hw_rf_id)); + CSR_HW_REV_TYPE(trans->info.hw_rev), + CSR_HW_RFID_TYPE(trans->info.hw_rf_id)); return -ENOENT; } @@ -167,7 +167,8 @@ done: static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, size_t len, - struct iwl_pnvm_image *pnvm_data) + struct iwl_pnvm_image *pnvm_data, + __le32 sku_id[3]) { const struct iwl_ucode_tlv *tlv; @@ -190,23 +191,23 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, } if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) { - const struct iwl_sku_id *sku_id = + const struct iwl_sku_id *tlv_sku_id = (const void *)(data + sizeof(*tlv)); IWL_DEBUG_FW(trans, "Got IWL_UCODE_TLV_PNVM_SKU len %d\n", tlv_len); IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n", - le32_to_cpu(sku_id->data[0]), - le32_to_cpu(sku_id->data[1]), - le32_to_cpu(sku_id->data[2])); + le32_to_cpu(tlv_sku_id->data[0]), + le32_to_cpu(tlv_sku_id->data[1]), + le32_to_cpu(tlv_sku_id->data[2])); data += sizeof(*tlv) + ALIGN(tlv_len, 4); len -= ALIGN(tlv_len, 4); trans->reduced_cap_sku = false; - rf_type = CSR_HW_RFID_TYPE(trans->hw_rf_id); - if ((trans->sku_id[0] & IWL_PNVM_REDUCED_CAP_BIT) && + rf_type = CSR_HW_RFID_TYPE(trans->info.hw_rf_id); + if ((sku_id[0] & cpu_to_le32(IWL_PNVM_REDUCED_CAP_BIT)) && rf_type == IWL_CFG_RF_TYPE_FM) trans->reduced_cap_sku = true; @@ -214,9 +215,9 @@ static int iwl_pnvm_parse(struct iwl_trans *trans, const u8 *data, "Reduced SKU device %d\n", trans->reduced_cap_sku); - if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) && - trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) && - trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) { + if (sku_id[0] == tlv_sku_id->data[0] && + sku_id[1] == tlv_sku_id->data[1] && + sku_id[2] == tlv_sku_id->data[2]) { int ret; ret = iwl_pnvm_handle_section(trans, data, len, @@ -263,13 +264,14 @@ static int iwl_pnvm_get_from_fs(struct iwl_trans *trans, u8 **data, size_t *len) return 0; } -static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len) +static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len, + __le32 sku_id[3]) { struct pnvm_sku_package *package; u8 *image = NULL; /* Get PNVM from BIOS for non-Intel SKU */ - if (trans_p->sku_id[2]) { + if (sku_id[2]) { package = iwl_uefi_get_pnvm(trans_p, len); if (!IS_ERR_OR_NULL(package)) { if (*len >= sizeof(*package)) { @@ -294,8 +296,10 @@ static u8 *iwl_get_pnvm_image(struct iwl_trans *trans_p, size_t *len) return image; } -static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans, - const struct iwl_ucode_capabilities *capa) +static void +iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans, + const struct iwl_ucode_capabilities *capa, + __le32 sku_id[3]) { struct iwl_pnvm_image *pnvm_data = NULL; u8 *data = NULL; @@ -309,7 +313,7 @@ static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans, if (trans->pnvm_loaded) goto set; - data = iwl_get_pnvm_image(trans, &length); + data = iwl_get_pnvm_image(trans, &length, sku_id); if (!data) { trans->fail_to_parse_pnvm_image = true; return; @@ -319,7 +323,7 @@ static void iwl_pnvm_load_pnvm_to_trans(struct iwl_trans *trans, if (!pnvm_data) goto free; - ret = iwl_pnvm_parse(trans, data, length, pnvm_data); + ret = iwl_pnvm_parse(trans, data, length, pnvm_data, sku_id); if (ret) { trans->fail_to_parse_pnvm_image = true; goto free; @@ -339,7 +343,8 @@ free: static void iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans *trans, - const struct iwl_ucode_capabilities *capa) + const struct iwl_ucode_capabilities *capa, + __le32 sku_id[3]) { struct iwl_pnvm_image *pnvm_data = NULL; u8 *data = NULL; @@ -362,7 +367,8 @@ iwl_pnvm_load_reduce_power_to_trans(struct iwl_trans *trans, if (!pnvm_data) goto free; - ret = iwl_uefi_reduce_power_parse(trans, data, length, pnvm_data); + ret = iwl_uefi_reduce_power_parse(trans, data, length, pnvm_data, + sku_id); if (ret) { trans->failed_to_load_reduce_power_image = true; goto free; @@ -386,18 +392,19 @@ free: int iwl_pnvm_load(struct iwl_trans *trans, struct iwl_notif_wait_data *notif_wait, - const struct iwl_ucode_capabilities *capa) + const struct iwl_ucode_capabilities *capa, + __le32 sku_id[3]) { struct iwl_notification_wait pnvm_wait; static const u16 ntf_cmds[] = { WIDE_ID(REGULATORY_AND_NVM_GROUP, PNVM_INIT_COMPLETE_NTFY) }; /* if the SKU_ID is empty, there's nothing to do */ - if (!trans->sku_id[0] && !trans->sku_id[1] && !trans->sku_id[2]) + if (!sku_id[0] && !sku_id[1] && !sku_id[2]) return 0; - iwl_pnvm_load_pnvm_to_trans(trans, capa); - iwl_pnvm_load_reduce_power_to_trans(trans, capa); + iwl_pnvm_load_pnvm_to_trans(trans, capa, sku_id); + iwl_pnvm_load_reduce_power_to_trans(trans, capa, sku_id); iwl_init_notification_wait(notif_wait, &pnvm_wait, ntf_cmds, ARRAY_SIZE(ntf_cmds), diff --git a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.h b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.h index 1bac3466154c..9540926e8a0f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/pnvm.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/pnvm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright(c) 2020-2023 Intel Corporation + * Copyright(c) 2020-2023, 2025 Intel Corporation */ #ifndef __IWL_PNVM_H__ #define __IWL_PNVM_H__ @@ -14,7 +14,8 @@ int iwl_pnvm_load(struct iwl_trans *trans, struct iwl_notif_wait_data *notif_wait, - const struct iwl_ucode_capabilities *capa); + const struct iwl_ucode_capabilities *capa, + __le32 sku_id[3]); static inline void iwl_pnvm_get_fs_name(struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c index ea435ee94312..74b90bd92c48 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2023 Intel Corporation + * Copyright (C) 2023, 2025 Intel Corporation */ #include <linux/dmi.h> #include "iwl-drv.h" @@ -34,6 +34,7 @@ IWL_BIOS_TABLE_LOADER(wrds_table); IWL_BIOS_TABLE_LOADER(ewrd_table); IWL_BIOS_TABLE_LOADER(wgds_table); IWL_BIOS_TABLE_LOADER(ppag_table); +IWL_BIOS_TABLE_LOADER(phy_filters); IWL_BIOS_TABLE_LOADER_DATA(tas_table, struct iwl_tas_data); IWL_BIOS_TABLE_LOADER_DATA(pwr_limit, u64); IWL_BIOS_TABLE_LOADER_DATA(mcc, char); @@ -180,9 +181,9 @@ bool iwl_sar_geo_support(struct iwl_fw_runtime *fwrt) */ return IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) >= 38 || (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 17 && - fwrt->trans->hw_rev != CSR_HW_REV_TYPE_3160) || + fwrt->trans->info.hw_rev != CSR_HW_REV_TYPE_3160) || (IWL_UCODE_SERIAL(fwrt->fw->ucode_ver) == 29 && - ((fwrt->trans->hw_rev & CSR_HW_REV_TYPE_MSK) == + ((fwrt->trans->info.hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D)); } IWL_EXPORT_SYMBOL(iwl_sar_geo_support); @@ -313,7 +314,7 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, bool send_ppag_always; /* many firmware images for JF lie about this */ - if (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id) == + if (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id) == CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF)) return -EOPNOTSUPP; @@ -338,31 +339,35 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, return -EINVAL; } - /* The 'flags' field is the same in v1 and in v2 so we can just - * use v1 to access it. - */ - cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags); - IWL_DEBUG_RADIO(fwrt, "PPAG cmd ver is %d\n", cmd_ver); if (cmd_ver == 1) { num_sub_bands = IWL_NUM_SUB_BANDS_V1; gain = cmd->v1.gain[0]; *cmd_size = sizeof(cmd->v1); - if (fwrt->ppag_ver >= 1) { + cmd->v1.flags = cpu_to_le32(fwrt->ppag_flags); + if (fwrt->ppag_bios_rev >= 1) { /* in this case FW supports revision 0 */ IWL_DEBUG_RADIO(fwrt, "PPAG table rev is %d, send truncated table\n", - fwrt->ppag_ver); + fwrt->ppag_bios_rev); } } else if (cmd_ver >= 2 && cmd_ver <= 6) { num_sub_bands = IWL_NUM_SUB_BANDS_V2; gain = cmd->v2.gain[0]; *cmd_size = sizeof(cmd->v2); - if (fwrt->ppag_ver == 0) { + cmd->v2.flags = cpu_to_le32(fwrt->ppag_flags); + if (fwrt->ppag_bios_rev == 0) { /* in this case FW supports revisions 1,2 or 3 */ IWL_DEBUG_RADIO(fwrt, "PPAG table rev is 0, send padded table\n"); } + } else if (cmd_ver == 7) { + num_sub_bands = IWL_NUM_SUB_BANDS_V2; + gain = cmd->v3.gain[0]; + *cmd_size = sizeof(cmd->v3); + cmd->v3.ppag_config_info.table_source = fwrt->ppag_bios_source; + cmd->v3.ppag_config_info.table_revision = fwrt->ppag_bios_rev; + cmd->v3.ppag_config_info.value = cpu_to_le32(fwrt->ppag_flags); } else { IWL_DEBUG_RADIO(fwrt, "Unsupported PPAG command version\n"); return -EINVAL; @@ -371,9 +376,11 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, /* ppag mode */ IWL_DEBUG_RADIO(fwrt, "PPAG MODE bits were read from bios: %d\n", - le32_to_cpu(cmd->v1.flags)); + fwrt->ppag_flags); - if (cmd_ver == 5) + if (cmd_ver == 6) + cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V6_MASK); + else if (cmd_ver == 5) cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V5_MASK); else if (cmd_ver < 5) cmd->v1.flags &= cpu_to_le32(IWL_PPAG_CMD_V4_MASK); @@ -381,16 +388,20 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, if ((cmd_ver == 1 && !fw_has_capa(&fwrt->fw->ucode_capa, IWL_UCODE_TLV_CAPA_PPAG_CHINA_BIOS_SUPPORT)) || - (cmd_ver == 2 && fwrt->ppag_ver >= 2)) { + (cmd_ver == 2 && fwrt->ppag_bios_rev >= 2)) { cmd->v1.flags &= cpu_to_le32(IWL_PPAG_ETSI_MASK); IWL_DEBUG_RADIO(fwrt, "masking ppag China bit\n"); } else { IWL_DEBUG_RADIO(fwrt, "isn't masking ppag China bit\n"); } + /* The 'flags' field is the same in v1 and v2 so we can just + * use v1 to access it. + */ IWL_DEBUG_RADIO(fwrt, "PPAG MODE bits going to be sent: %d\n", - le32_to_cpu(cmd->v1.flags)); + (cmd_ver < 7) ? le32_to_cpu(cmd->v1.flags) : + le32_to_cpu(cmd->v3.ppag_config_info.value)); for (i = 0; i < IWL_NUM_CHAIN_LIMITS; i++) { for (j = 0; j < num_sub_bands; j++) { @@ -456,13 +467,31 @@ iwl_parse_tas_selection(const u32 tas_selection_in, const u8 tbl_rev) } IWL_EXPORT_SYMBOL(iwl_parse_tas_selection); -static __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) +bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc) +{ + for (int i = 0; i < *size; i++) { + if (list[i] == mcc) + return true; + } + + /* Verify that there is room for another country + * If *size == IWL_WTAS_BLACK_LIST_MAX, then the table is full. + */ + if (*size >= IWL_WTAS_BLACK_LIST_MAX) + return false; + + list[*size++] = mcc; + return true; +} +IWL_EXPORT_SYMBOL(iwl_add_mcc_to_tas_block_list); + +__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) { int ret; u32 val; __le32 config_bitmap = 0; - switch (CSR_HW_RFID_TYPE(fwrt->trans->hw_rf_id)) { + switch (CSR_HW_RFID_TYPE(fwrt->trans->info.hw_rf_id)) { case IWL_CFG_RF_TYPE_HR1: case IWL_CFG_RF_TYPE_HR2: case IWL_CFG_RF_TYPE_JF1: @@ -503,6 +532,7 @@ static __le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt) return config_bitmap; } +IWL_EXPORT_SYMBOL(iwl_get_lari_config_bitmap); static size_t iwl_get_lari_config_cmd_size(u8 cmd_ver) { @@ -553,6 +583,10 @@ int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt, WIDE_ID(REGULATORY_AND_NVM_GROUP, LARI_CONFIG_CHANGE), 1); + if (WARN_ONCE(cmd_ver > 12, + "Don't add newer versions to this function\n")) + return -EINVAL; + memset(cmd, 0, sizeof(*cmd)); *cmd_size = iwl_get_lari_config_cmd_size(cmd_ver); @@ -674,3 +708,34 @@ bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc) } } IWL_EXPORT_SYMBOL(iwl_puncturing_is_allowed_in_bios); + +bool iwl_rfi_is_enabled_in_bios(struct iwl_fw_runtime *fwrt) +{ + /* default behaviour is disabled */ + u32 value = 0; + int ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_RFI_CONFIG, &value); + + if (ret < 0) { + IWL_DEBUG_RADIO(fwrt, "Failed to get DSM RFI, ret=%d\n", ret); + return false; + } + + value &= DSM_VALUE_RFI_DISABLE; + /* RFI BIOS CONFIG value can be 0 or 3 only. + * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled. + * 1 and 2 are invalid BIOS configurations, So, it's not possible to + * disable ddr/dlvr separately. + */ + if (!value) { + IWL_DEBUG_RADIO(fwrt, "DSM RFI is evaluated to enable\n"); + return true; + } else if (value == DSM_VALUE_RFI_DISABLE) { + IWL_DEBUG_RADIO(fwrt, "DSM RFI is evaluated to disable\n"); + } else { + IWL_DEBUG_RADIO(fwrt, + "DSM RFI got invalid value, value=%d\n", value); + } + + return false; +} +IWL_EXPORT_SYMBOL(iwl_rfi_is_enabled_in_bios); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h index b355d7bef14c..9bed3d573b1e 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/regulatory.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2023-2024 Intel Corporation + * Copyright (C) 2023-2025 Intel Corporation */ #ifndef __fw_regulatory_h__ @@ -167,6 +167,8 @@ enum iwl_dsm_values_rfi { #define DSM_VALUE_RFI_DISABLE (DSM_VALUE_RFI_DLVR_DISABLE |\ DSM_VALUE_RFI_DDR_DISABLE) +bool iwl_rfi_is_enabled_in_bios(struct iwl_fw_runtime *fwrt); + enum iwl_dsm_masks_reg { DSM_MASK_CHINA_22_REG = BIT(2) }; @@ -190,6 +192,7 @@ int iwl_fill_ppag_table(struct iwl_fw_runtime *fwrt, bool iwl_is_ppag_approved(struct iwl_fw_runtime *fwrt); bool iwl_is_tas_approved(void); +bool iwl_add_mcc_to_tas_block_list(u16 *list, u8 *size, u16 mcc); struct iwl_tas_selection_data iwl_parse_tas_selection(const u32 tas_selection, const u8 tbl_rev); @@ -212,6 +215,7 @@ int iwl_bios_get_mcc(struct iwl_fw_runtime *fwrt, char *mcc); int iwl_bios_get_eckv(struct iwl_fw_runtime *fwrt, u32 *ext_clk); int iwl_bios_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value); +__le32 iwl_get_lari_config_bitmap(struct iwl_fw_runtime *fwrt); int iwl_fill_lari_config(struct iwl_fw_runtime *fwrt, struct iwl_lari_config_change_cmd *cmd, size_t *cmd_size); @@ -220,10 +224,14 @@ int iwl_bios_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, u32 *value); static inline u32 iwl_bios_get_ppag_flags(const u32 ppag_modes, - const u8 ppag_ver) + const u8 ppag_bios_rev) { - return ppag_modes & (ppag_ver < 3 ? IWL_PPAG_ETSI_CHINA_MASK : - IWL_PPAG_REV3_MASK); + /* For revision 4 and above driver is pipe */ + if (ppag_bios_rev >= 4) + return ppag_modes; + + return ppag_modes & (ppag_bios_rev < 3 ? IWL_PPAG_ETSI_CHINA_MASK : + IWL_PPAG_REV3_MASK); } bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc); @@ -232,22 +240,25 @@ bool iwl_puncturing_is_allowed_in_bios(u32 puncturing, u16 mcc); #define IWL_DSBR_PERMANENT_URM_MASK BIT(9) int iwl_bios_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value); +int iwl_bios_get_phy_filters(struct iwl_fw_runtime *fwrt); static inline void iwl_bios_setup_step(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { u32 dsbr; - if (!trans->trans_cfg->integrated) + if (!trans->mac_cfg->integrated) return; - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ) return; if (iwl_bios_get_dsbr(fwrt, &dsbr)) dsbr = 0; - trans->dsbr_urm_fw_dependent = !!(dsbr & IWL_DSBR_FW_MODIFIED_URM_MASK); - trans->dsbr_urm_permanent = !!(dsbr & IWL_DSBR_PERMANENT_URM_MASK); + trans->conf.dsbr_urm_fw_dependent = + !!(dsbr & IWL_DSBR_FW_MODIFIED_URM_MASK); + trans->conf.dsbr_urm_permanent = + !!(dsbr & IWL_DSBR_PERMANENT_URM_MASK); } #endif /* __fw_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/fw/rs.c b/drivers/net/wireless/intel/iwlwifi/fw/rs.c index 8f99e501629e..746f2acffb8f 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/rs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2021-2022 Intel Corporation + * Copyright (C) 2021-2022, 2025 Intel Corporation */ #include <net/mac80211.h> @@ -91,104 +91,6 @@ const char *iwl_rs_pretty_bw(int bw) } IWL_EXPORT_SYMBOL(iwl_rs_pretty_bw); -static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags) -{ - int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1; - int idx; - bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1); - int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0; - int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE; - - for (idx = offset; idx < last; idx++) - if (iwl_fw_rate_idx_to_plcp(idx) == rate) - return idx - offset; - return IWL_RATE_INVALID; -} - -u32 iwl_new_rate_from_v1(u32 rate_v1) -{ - u32 rate_v2 = 0; - u32 dup = 0; - - if (rate_v1 == 0) - return rate_v1; - /* convert rate */ - if (rate_v1 & RATE_MCS_HT_MSK_V1) { - u32 nss = 0; - - rate_v2 |= RATE_MCS_HT_MSK; - rate_v2 |= - rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1; - nss = (rate_v1 & RATE_HT_MCS_MIMO2_MSK) >> - RATE_HT_MCS_NSS_POS_V1; - rate_v2 |= nss << RATE_MCS_NSS_POS; - } else if (rate_v1 & RATE_MCS_VHT_MSK_V1 || - rate_v1 & RATE_MCS_HE_MSK_V1) { - rate_v2 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK; - - rate_v2 |= rate_v1 & RATE_MCS_NSS_MSK; - - if (rate_v1 & RATE_MCS_HE_MSK_V1) { - u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1; - u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1; - u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >> - RATE_MCS_HE_106T_POS_V1; - u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >> - RATE_MCS_HE_GI_LTF_POS; - - if ((he_type_bits == RATE_MCS_HE_TYPE_SU || - he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) && - he_gi_ltf == RATE_MCS_HE_SU_4_LTF) - /* the new rate have an additional bit to - * represent the value 4 rather then using SGI - * bit for this purpose - as it was done in the old - * rate */ - he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >> - RATE_MCS_SGI_POS_V1; - - rate_v2 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS; - rate_v2 |= he_type << RATE_MCS_HE_TYPE_POS; - rate_v2 |= he_106t << RATE_MCS_HE_106T_POS; - rate_v2 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK; - rate_v2 |= RATE_MCS_HE_MSK; - } else { - rate_v2 |= RATE_MCS_VHT_MSK; - } - /* if legacy format */ - } else { - u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1); - - if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID)) - legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ? - IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE; - - rate_v2 |= legacy_rate; - if (!(rate_v1 & RATE_MCS_CCK_MSK_V1)) - rate_v2 |= RATE_MCS_LEGACY_OFDM_MSK; - } - - /* convert flags */ - if (rate_v1 & RATE_MCS_LDPC_MSK_V1) - rate_v2 |= RATE_MCS_LDPC_MSK; - rate_v2 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) | - (rate_v1 & RATE_MCS_ANT_AB_MSK) | - (rate_v1 & RATE_MCS_STBC_MSK) | - (rate_v1 & RATE_MCS_BF_MSK); - - dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1; - if (dup) { - rate_v2 |= RATE_MCS_DUP_MSK; - rate_v2 |= dup << RATE_MCS_CHAN_WIDTH_POS; - } - - if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) && - (rate_v1 & RATE_MCS_SGI_MSK_V1)) - rate_v2 |= RATE_MCS_SGI_MSK; - - return rate_v2; -} -IWL_EXPORT_SYMBOL(iwl_new_rate_from_v1); - int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate) { char *type; @@ -197,37 +99,40 @@ int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate) u32 bw = (rate & RATE_MCS_CHAN_WIDTH_MSK) >> RATE_MCS_CHAN_WIDTH_POS; u32 format = rate & RATE_MCS_MOD_TYPE_MSK; + int index = 0; bool sgi; - if (format == RATE_MCS_CCK_MSK || - format == RATE_MCS_LEGACY_OFDM_MSK) { - int legacy_rate = rate & RATE_LEGACY_RATE_MSK; - int index = format == RATE_MCS_CCK_MSK ? - legacy_rate : - legacy_rate + IWL_FIRST_OFDM_RATE; + switch (format) { + case RATE_MCS_MOD_TYPE_LEGACY_OFDM: + index = IWL_FIRST_OFDM_RATE; + fallthrough; + case RATE_MCS_MOD_TYPE_CCK: + index += rate & RATE_LEGACY_RATE_MSK; return scnprintf(buf, bufsz, "Legacy | ANT: %s Rate: %s Mbps", iwl_rs_pretty_ant(ant), iwl_rate_mcs(index)->mbps); - } - - if (format == RATE_MCS_VHT_MSK) + case RATE_MCS_MOD_TYPE_VHT: type = "VHT"; - else if (format == RATE_MCS_HT_MSK) + break; + case RATE_MCS_MOD_TYPE_HT: type = "HT"; - else if (format == RATE_MCS_HE_MSK) + break; + case RATE_MCS_MOD_TYPE_HE: type = "HE"; - else if (format == RATE_MCS_EHT_MSK) + break; + case RATE_MCS_MOD_TYPE_EHT: type = "EHT"; - else + break; + default: type = "Unknown"; /* shouldn't happen */ + } - mcs = format == RATE_MCS_HT_MSK ? + mcs = format == RATE_MCS_MOD_TYPE_HT ? RATE_HT_MCS_INDEX(rate) : rate & RATE_MCS_CODE_MSK; - nss = ((rate & RATE_MCS_NSS_MSK) - >> RATE_MCS_NSS_POS) + 1; - sgi = format == RATE_MCS_HE_MSK ? + nss = u32_get_bits(rate, RATE_MCS_NSS_MSK); + sgi = format == RATE_MCS_MOD_TYPE_HE ? iwl_he_is_sgi(rate) : rate & RATE_MCS_SGI_MSK; diff --git a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h index 048877fa7c71..0444a736c2b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/runtime.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/runtime.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #ifndef __iwl_fw_runtime_h__ #define __iwl_fw_runtime_h__ @@ -104,11 +104,15 @@ struct iwl_txf_iter_data { * the driver by calling &iwl_fw_set_current_image() * @dump: debug dump data * @uats_table: AP type table + * @uats_valid: is AP type table valid * @uefi_tables_lock_status: The status of the WIFI GUID UEFI variables lock: * 0: Unlocked, 1 and 2: Locked. * Only read the UEFI variables if locked. * @sar_profiles: sar profiles as read from WRDS/EWRD BIOS tables * @geo_profiles: geographic profiles as read from WGDS BIOS table + * @phy_filters: specific phy filters as read from WPFC BIOS table + * @ppag_bios_rev: PPAG BIOS revision + * @ppag_bios_source: see &enum bios_source */ struct iwl_fw_runtime { struct iwl_trans *trans; @@ -177,11 +181,14 @@ struct iwl_fw_runtime { bool geo_enabled; struct iwl_ppag_chain ppag_chains[IWL_NUM_CHAIN_LIMITS]; u32 ppag_flags; - u8 ppag_ver; + u8 ppag_bios_rev; + u8 ppag_bios_source; struct iwl_sar_offset_mapping_cmd sgom_table; bool sgom_enabled; struct iwl_mcc_allowed_ap_type_cmd uats_table; + bool uats_valid; u8 uefi_tables_lock_status; + struct iwl_phy_specific_cfg phy_filters; }; void iwl_fw_runtime_init(struct iwl_fw_runtime *fwrt, struct iwl_trans *trans, diff --git a/drivers/net/wireless/intel/iwlwifi/fw/smem.c b/drivers/net/wireless/intel/iwlwifi/fw/smem.c index 3f1272014daf..90fd69b4860c 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/smem.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/smem.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2021 Intel Corporation + * Copyright (C) 2012-2014, 2018-2021, 2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -102,7 +102,7 @@ void iwl_get_shared_mem_conf(struct iwl_fw_runtime *fwrt) } pkt = cmd.resp_pkt; - if (fwrt->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) + if (fwrt->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) iwl_parse_shared_mem_22000(fwrt, pkt); else iwl_parse_shared_mem(fwrt, pkt); diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c index 434eed4130b9..48126ec6b94b 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.c +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright(c) 2021-2024 Intel Corporation + * Copyright(c) 2021-2025 Intel Corporation */ #include "iwl-drv.h" @@ -219,7 +219,8 @@ done: int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, const u8 *data, size_t len, - struct iwl_pnvm_image *pnvm_data) + struct iwl_pnvm_image *pnvm_data, + __le32 sku_id[3]) { const struct iwl_ucode_tlv *tlv; @@ -241,23 +242,23 @@ int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, } if (tlv_type == IWL_UCODE_TLV_PNVM_SKU) { - const struct iwl_sku_id *sku_id = + const struct iwl_sku_id *tlv_sku_id = (const void *)(data + sizeof(*tlv)); IWL_DEBUG_FW(trans, "Got IWL_UCODE_TLV_PNVM_SKU len %d\n", tlv_len); IWL_DEBUG_FW(trans, "sku_id 0x%0x 0x%0x 0x%0x\n", - le32_to_cpu(sku_id->data[0]), - le32_to_cpu(sku_id->data[1]), - le32_to_cpu(sku_id->data[2])); + le32_to_cpu(tlv_sku_id->data[0]), + le32_to_cpu(tlv_sku_id->data[1]), + le32_to_cpu(tlv_sku_id->data[2])); data += sizeof(*tlv) + ALIGN(tlv_len, 4); len -= ALIGN(tlv_len, 4); - if (trans->sku_id[0] == le32_to_cpu(sku_id->data[0]) && - trans->sku_id[1] == le32_to_cpu(sku_id->data[1]) && - trans->sku_id[2] == le32_to_cpu(sku_id->data[2])) { + if (sku_id[0] == tlv_sku_id->data[0] && + sku_id[1] == tlv_sku_id->data[1] && + sku_id[2] == tlv_sku_id->data[2]) { int ret = iwl_uefi_reduce_power_section(trans, data, len, pnvm_data); @@ -310,11 +311,12 @@ static int iwl_uefi_step_parse(struct uefi_cnv_common_step_data *common_step_dat if (common_step_data->revision != 1) return -EINVAL; - trans->mbx_addr_0_step = (u32)common_step_data->revision | + trans->conf.mbx_addr_0_step = + (u32)common_step_data->revision | (u32)common_step_data->cnvi_eq_channel << 8 | (u32)common_step_data->cnvr_eq_channel << 16 | (u32)common_step_data->radio1 << 24; - trans->mbx_addr_1_step = (u32)common_step_data->radio2; + trans->conf.mbx_addr_1_step = (u32)common_step_data->radio2; return 0; } @@ -323,7 +325,7 @@ void iwl_uefi_get_step_table(struct iwl_trans *trans) struct uefi_cnv_common_step_data *data; int ret; - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return; data = iwl_uefi_get_verified_variable_guid(trans, &IWL_EFI_WIFI_BT_GUID, @@ -402,11 +404,14 @@ static int iwl_uefi_uats_parse(struct uefi_cnv_wlan_uats_data *uats_data, memcpy(fwrt->uats_table.offset_map, uats_data->offset_map, sizeof(fwrt->uats_table.offset_map)); + + fwrt->uats_valid = true; + return 0; } -int iwl_uefi_get_uats_table(struct iwl_trans *trans, - struct iwl_fw_runtime *fwrt) +void iwl_uefi_get_uats_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt) { struct uefi_cnv_wlan_uats_data *data; int ret; @@ -414,17 +419,12 @@ int iwl_uefi_get_uats_table(struct iwl_trans *trans, data = iwl_uefi_get_verified_variable(trans, IWL_UEFI_UATS_NAME, "UATS", sizeof(*data), NULL); if (IS_ERR(data)) - return -EINVAL; + return; ret = iwl_uefi_uats_parse(data, fwrt); - if (ret < 0) { + if (ret < 0) IWL_DEBUG_FW(trans, "Cannot read UATS table. rev is invalid\n"); - kfree(data); - return ret; - } - kfree(data); - return 0; } IWL_EXPORT_SYMBOL(iwl_uefi_get_uats_table); @@ -554,13 +554,14 @@ int iwl_uefi_get_ppag_table(struct iwl_fw_runtime *fwrt) goto out; } - fwrt->ppag_ver = data->revision; + fwrt->ppag_bios_rev = data->revision; fwrt->ppag_flags = iwl_bios_get_ppag_flags(data->ppag_modes, - fwrt->ppag_ver); + fwrt->ppag_bios_rev); BUILD_BUG_ON(sizeof(fwrt->ppag_chains) != sizeof(data->ppag_chains)); memcpy(&fwrt->ppag_chains, &data->ppag_chains, sizeof(data->ppag_chains)); + fwrt->ppag_bios_source = BIOS_SOURCE_UEFI; out: kfree(data); return ret; @@ -678,14 +679,16 @@ int iwl_uefi_get_eckv(struct iwl_fw_runtime *fwrt, u32 *extl_clk) struct uefi_cnv_var_eckv *data; int ret = 0; - data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_ECKV_NAME, - "ECKV", sizeof(*data), NULL); + data = iwl_uefi_get_verified_variable_guid(fwrt->trans, + &IWL_EFI_WIFI_BT_GUID, + IWL_UEFI_ECKV_NAME, + "ECKV", sizeof(*data), NULL); if (IS_ERR(data)) return -EINVAL; if (data->revision != IWL_UEFI_ECKV_REVISION) { ret = -EINVAL; - IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WRDD revision:%d\n", + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI ECKV revision:%d\n", data->revision); goto out; } @@ -745,6 +748,10 @@ int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, } *value = data->functions[func]; + + IWL_DEBUG_RADIO(fwrt, + "UEFI: DSM func=%d: value=%d\n", func, *value); + ret = 0; out: kfree(data); @@ -805,3 +812,31 @@ out: kfree(data); return ret; } + +int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt) +{ + struct uefi_cnv_wpfc_data *data __free(kfree); + struct iwl_phy_specific_cfg *filters = &fwrt->phy_filters; + + data = iwl_uefi_get_verified_variable(fwrt->trans, IWL_UEFI_WPFC_NAME, + "WPFC", sizeof(*data), NULL); + if (IS_ERR(data)) + return -EINVAL; + + if (data->revision != 0) { + IWL_DEBUG_RADIO(fwrt, "Unsupported UEFI WPFC revision:%d\n", + data->revision); + return -EINVAL; + } + + BUILD_BUG_ON(ARRAY_SIZE(filters->filter_cfg_chains) != + ARRAY_SIZE(data->chains)); + + for (int i = 0; i < ARRAY_SIZE(filters->filter_cfg_chains); i++) { + filters->filter_cfg_chains[i] = cpu_to_le32(data->chains[i]); + IWL_DEBUG_RADIO(fwrt, "WPFC: chain %d: %u\n", i, data->chains[i]); + } + + IWL_DEBUG_RADIO(fwrt, "Loaded WPFC config from UEFI\n"); + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h index 0c8943a8bd01..5a4c557e47c7 100644 --- a/drivers/net/wireless/intel/iwlwifi/fw/uefi.h +++ b/drivers/net/wireless/intel/iwlwifi/fw/uefi.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright(c) 2021-2024 Intel Corporation + * Copyright(c) 2021-2025 Intel Corporation */ #ifndef __iwl_fw_uefi__ #define __iwl_fw_uefi__ @@ -19,11 +19,12 @@ #define IWL_UEFI_WTAS_NAME L"UefiCnvWlanWTAS" #define IWL_UEFI_SPLC_NAME L"UefiCnvWlanSPLC" #define IWL_UEFI_WRDD_NAME L"UefiCnvWlanWRDD" -#define IWL_UEFI_ECKV_NAME L"UefiCnvWlanECKV" +#define IWL_UEFI_ECKV_NAME L"UefiCnvCommonECKV" #define IWL_UEFI_DSM_NAME L"UefiCnvWlanGeneralCfg" #define IWL_UEFI_WBEM_NAME L"UefiCnvWlanWBEM" #define IWL_UEFI_PUNCTURING_NAME L"UefiCnvWlanPuncturing" #define IWL_UEFI_DSBR_NAME L"UefiCnvCommonDSBR" +#define IWL_UEFI_WPFC_NAME L"WPFC" #define IWL_SGOM_MAP_SIZE 339 @@ -33,7 +34,7 @@ #define IWL_UEFI_EWRD_REVISION 2 #define IWL_UEFI_WGDS_REVISION 3 #define IWL_UEFI_MIN_PPAG_REV 1 -#define IWL_UEFI_MAX_PPAG_REV 3 +#define IWL_UEFI_MAX_PPAG_REV 4 #define IWL_UEFI_MIN_WTAS_REVISION 1 #define IWL_UEFI_MAX_WTAS_REVISION 2 #define IWL_UEFI_SPLC_REVISION 0 @@ -230,6 +231,18 @@ struct uefi_cnv_wlan_dsbr_data { u32 config; } __packed; +/** + * struct uefi_cnv_wpfc_data - BIOS Wi-Fi PHY filter Configuration + * @revision: the revision of the table + * @chains: configuration of each of the chains (a-d) + * + * specific PHY filter configuration + */ +struct uefi_cnv_wpfc_data { + u8 revision; + u32 chains[4]; +} __packed; + /* * This is known to be broken on v4.19 and to work on v5.4. Until we * figure out why this is the case and how to make it work, simply @@ -240,7 +253,8 @@ void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len); u8 *iwl_uefi_get_reduced_power(struct iwl_trans *trans, size_t *len); int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, const u8 *data, size_t len, - struct iwl_pnvm_image *pnvm_data); + struct iwl_pnvm_image *pnvm_data, + __le32 sku_id[3]); void iwl_uefi_get_step_table(struct iwl_trans *trans); int iwl_uefi_handle_tlv_mem_desc(struct iwl_trans *trans, const u8 *data, u32 tlv_len, struct iwl_pnvm_image *pnvm_data); @@ -258,10 +272,11 @@ int iwl_uefi_get_wbem(struct iwl_fw_runtime *fwrt, u32 *value); int iwl_uefi_get_dsm(struct iwl_fw_runtime *fwrt, enum iwl_dsm_funcs func, u32 *value); void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt); -int iwl_uefi_get_uats_table(struct iwl_trans *trans, - struct iwl_fw_runtime *fwrt); +void iwl_uefi_get_uats_table(struct iwl_trans *trans, + struct iwl_fw_runtime *fwrt); int iwl_uefi_get_puncturing(struct iwl_fw_runtime *fwrt); int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value); +int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt); #else /* CONFIG_EFI */ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) { @@ -271,7 +286,8 @@ static inline void *iwl_uefi_get_pnvm(struct iwl_trans *trans, size_t *len) static inline int iwl_uefi_reduce_power_parse(struct iwl_trans *trans, const u8 *data, size_t len, - struct iwl_pnvm_image *pnvm_data) + struct iwl_pnvm_image *pnvm_data, + __le32 sku_id[3]) { return -EOPNOTSUPP; } @@ -352,11 +368,9 @@ void iwl_uefi_get_sgom_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwr { } -static inline -int iwl_uefi_get_uats_table(struct iwl_trans *trans, - struct iwl_fw_runtime *fwrt) +static inline void +iwl_uefi_get_uats_table(struct iwl_trans *trans, struct iwl_fw_runtime *fwrt) { - return 0; } static inline @@ -370,5 +384,10 @@ int iwl_uefi_get_dsbr(struct iwl_fw_runtime *fwrt, u32 *value) { return -ENOENT; } + +static inline int iwl_uefi_get_phy_filters(struct iwl_fw_runtime *fwrt) +{ + return -ENOENT; +} #endif /* CONFIG_EFI */ #endif /* __iwl_fw_uefi__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 2b6a80142aba..91f22ce36d74 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -2,7 +2,7 @@ /* * Copyright (C) 2005-2014, 2018-2021 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #ifndef __IWL_CONFIG_H__ #define __IWL_CONFIG_H__ @@ -115,7 +115,29 @@ static inline u8 num_of_ant(u8 mask) } /** - * struct iwl_base_params - params not likely to change within a device family + * struct iwl_fw_mon_reg - FW monitor register info + * @addr: register address + * @mask: register mask + */ +struct iwl_fw_mon_reg { + u32 addr; + u32 mask; +}; + +/** + * struct iwl_fw_mon_regs - FW monitor registers + * @write_ptr: write pointer register + * @cycle_cnt: cycle count register + * @cur_frag: current fragment in use + */ +struct iwl_fw_mon_regs { + struct iwl_fw_mon_reg write_ptr; + struct iwl_fw_mon_reg cycle_cnt; + struct iwl_fw_mon_reg cur_frag; +}; + +/** + * struct iwl_family_base_params - base parameters for an entire family * @max_ll_items: max number of OTP blocks * @shadow_ram_support: shadow support for OTP memory * @led_compensation: compensate on the led on/off time per HW according @@ -124,12 +146,34 @@ static inline u8 num_of_ant(u8 mask) * @wd_timeout: TX queues watchdog timeout * @max_event_log_size: size of event log buffer size for ucode event logging * @shadow_reg_enable: HW shadow register support + * @apmg_not_supported: there's no APMG * @apmg_wake_up_wa: should the MAC access REQ be asserted when a command * is in flight. This is due to a HW bug in 7260, 3160 and 7265. * @scd_chain_ext_wa: should the chain extension feature in SCD be disabled. * @max_tfd_queue_size: max number of entries in tfd queue. + * @eeprom_size: EEPROM size + * @num_of_queues: number of HW TX queues supported + * @pcie_l1_allowed: PCIe L1 state is allowed + * @pll_cfg: PLL configuration needed + * @nvm_hw_section_num: the ID of the HW NVM section + * @features: hw features, any combination of feature_passlist + * @smem_offset: offset from which the SMEM begins + * @smem_len: the length of SMEM + * @mac_addr_from_csr: read HW address from CSR registers at this offset + * @d3_debug_data_base_addr: base address where D3 debug data is stored + * @d3_debug_data_length: length of the D3 debug data + * @min_ba_txq_size: minimum number of slots required in a TX queue used + * for aggregation + * @min_txq_size: minimum number of slots required in a TX queue + * @gp2_reg_addr: GP2 (timer) register address + * @min_umac_error_event_table: minimum SMEM location of UMAC error table + * @mon_dbgi_regs: monitor DBGI registers + * @mon_dram_regs: monitor DRAM registers + * @mon_smem_regs: monitor SMEM registers + * @ucode_api_max: Highest version of uCode API supported by driver. + * @ucode_api_min: Lowest version of uCode API supported by driver. */ -struct iwl_base_params { +struct iwl_family_base_params { unsigned int wd_timeout; u16 eeprom_size; @@ -140,6 +184,7 @@ struct iwl_base_params { shadow_reg_enable:1, pcie_l1_allowed:1, apmg_wake_up_wa:1, + apmg_not_supported:1, scd_chain_ext_wa:1; u16 num_of_queues; /* def: HW dependent */ @@ -147,6 +192,22 @@ struct iwl_base_params { u8 max_ll_items; u8 led_compensation; + u8 ucode_api_max; + u8 ucode_api_min; + u32 mac_addr_from_csr:10; + u8 nvm_hw_section_num; + netdev_features_t features; + u32 smem_offset; + u32 smem_len; + u32 min_umac_error_event_table; + u32 d3_debug_data_base_addr; + u32 d3_debug_data_length; + u32 min_txq_size; + u32 gp2_reg_addr; + u32 min_ba_txq_size; + const struct iwl_fw_mon_regs mon_dram_regs; + const struct iwl_fw_mon_regs mon_smem_regs; + const struct iwl_fw_mon_regs mon_dbgi_regs; }; /* @@ -238,7 +299,7 @@ struct iwl_pwr_tx_backoff { u32 backoff; }; -enum iwl_cfg_trans_ltr_delay { +enum iwl_mac_cfg_ltr_delay { IWL_CFG_TRANS_LTR_DELAY_NONE = 0, IWL_CFG_TRANS_LTR_DELAY_200US = 1, IWL_CFG_TRANS_LTR_DELAY_2500US = 2, @@ -246,35 +307,33 @@ enum iwl_cfg_trans_ltr_delay { }; /** - * struct iwl_cfg_trans_params - information needed to start the trans + * struct iwl_mac_cfg - information about the MAC-specific device part * * These values are specific to the device ID and do not change when * multiple configs are used for a single device ID. They values are * used, among other things, to boot the NIC so that the HW REV or * RFID can be read before deciding the remaining parameters to use. * - * @base_params: pointer to basic parameters + * @base: pointer to basic parameters * @device_family: the device family * @umac_prph_offset: offset to add to UMAC periphery address * @xtal_latency: power up latency to get the xtal stabilized * @extra_phy_cfg_flags: extra configuration flags to pass to the PHY - * @rf_id: need to read rf_id to determine the firmware image * @gen2: 22000 and on transport operation * @mq_rx_supported: multi-queue rx support * @integrated: discrete or integrated * @low_latency_xtal: use the low latency xtal if supported * @bisr_workaround: BISR hardware workaround (for 22260 series devices) - * @ltr_delay: LTR delay parameter, &enum iwl_cfg_trans_ltr_delay. + * @ltr_delay: LTR delay parameter, &enum iwl_mac_cfg_ltr_delay. * @imr_enabled: use the IMR if supported. */ -struct iwl_cfg_trans_params { - const struct iwl_base_params *base_params; +struct iwl_mac_cfg { + const struct iwl_family_base_params *base; enum iwl_device_family device_family; u32 umac_prph_offset; u32 xtal_latency; u32 extra_phy_cfg_flags; - u32 rf_id:1, - gen2:1, + u32 gen2:1, mq_rx_supported:1, integrated:1, low_latency_xtal:1, @@ -283,36 +342,21 @@ struct iwl_cfg_trans_params { imr_enabled:1; }; -/** - * struct iwl_fw_mon_reg - FW monitor register info - * @addr: register address - * @mask: register mask - */ -struct iwl_fw_mon_reg { - u32 addr; - u32 mask; -}; - -/** - * struct iwl_fw_mon_regs - FW monitor registers - * @write_ptr: write pointer register - * @cycle_cnt: cycle count register - * @cur_frag: current fragment in use +/* + * These sizes were picked according to 8 MSDUs inside 64/256/612 A-MSDUs + * in an A-MPDU, with additional overhead to account for processing time. + * They will be doubled for MACs starting from So/Ty that don't support + * putting multiple frames into a single buffer. */ -struct iwl_fw_mon_regs { - struct iwl_fw_mon_reg write_ptr; - struct iwl_fw_mon_reg cycle_cnt; - struct iwl_fw_mon_reg cur_frag; -}; +#define IWL_NUM_RBDS_NON_HE (64 * 8) +#define IWL_NUM_RBDS_HE (256 * 8) +#define IWL_NUM_RBDS_EHT (512 * 8) /** - * struct iwl_cfg - * @trans: the trans-specific configuration part - * @name: Official name of the device + * struct iwl_rf_cfg * @fw_name_pre: Firmware filename prefix. The api version and extension * (.ucode) will be added to filename before loading from disk. The * filename is constructed as <fw_name_pre>-<api>.ucode. - * @fw_name_mac: MAC name for this config, the remaining pieces of the * name will be generated dynamically * @ucode_api_max: Highest version of uCode API supported by driver. * @ucode_api_min: Lowest version of uCode API supported by driver. @@ -323,34 +367,25 @@ struct iwl_fw_mon_regs { * @non_shared_ant: the antenna that is for WiFi only * @nvm_ver: NVM version * @nvm_calib_ver: NVM calibration version + * @bw_limit: bandwidth limit for this device, if non-zero * @ht_params: point to ht parameters + * @eeprom_params: EEPROM parameters (old devices) + * @thermal_params: Thermal throttling parameters + * @lp_xtal_workaround: low-power crystal workaround needed * @led_mode: 0=blinking, 1=On(RF On)/Off(RF Off) * @rx_with_siso_diversity: 1x1 device with rx antenna diversity * @tx_with_siso_diversity: 1x1 device with tx antenna diversity * @internal_wimax_coex: internal wifi/wimax combo device - * @high_temp: Is this NIC is designated to be in high temperature. * @host_interrupt_operation_mode: device needs host interrupt operation * mode set - * @nvm_hw_section_num: the ID of the HW NVM section - * @mac_addr_from_csr: read HW address from CSR registers at this offset - * @features: hw features, any combination of feature_passlist * @pwr_tx_backoffs: translation table between power limits and backoffs - * @max_tx_agg_size: max TX aggregation size of the ADDBA request/response * @dccm_offset: offset from which DCCM begins * @dccm_len: length of DCCM (including runtime stack CCM) * @dccm2_offset: offset from which the second DCCM begins * @dccm2_len: length of the second DCCM - * @smem_offset: offset from which the SMEM begins - * @smem_len: the length of SMEM * @vht_mu_mimo_supported: VHT MU-MIMO support - * @cdb: CDB support * @nvm_type: see &enum iwl_nvm_type - * @d3_debug_data_base_addr: base address where D3 debug data is stored - * @d3_debug_data_length: length of the D3 debug data - * @min_txq_size: minimum number of slots required in a TX queue * @uhb_supported: ultra high band channels supported - * @min_ba_txq_size: minimum number of slots required in a TX queue which - * based on hardware support (HE - 256, EHT - 1K). * @num_rbds: number of receive buffer descriptors to use * (only used for multi-queue capable devices) * @@ -358,60 +393,38 @@ struct iwl_fw_mon_regs { * API differences in uCode shouldn't be handled here but through TLVs * and/or the uCode API version instead. */ -struct iwl_cfg { - struct iwl_cfg_trans_params trans; +struct iwl_rf_cfg { /* params specific to an individual device within a device family */ - const char *name; const char *fw_name_pre; - const char *fw_name_mac; /* params likely to change within a device family */ - const struct iwl_ht_params *ht_params; + const struct iwl_ht_params ht_params; const struct iwl_eeprom_params *eeprom_params; const struct iwl_pwr_tx_backoff *pwr_tx_backoffs; - const char *default_nvm_file_C_step; const struct iwl_tt_params *thermal_params; enum iwl_led_mode led_mode; enum iwl_nvm_type nvm_type; u32 max_data_size; u32 max_inst_size; - netdev_features_t features; u32 dccm_offset; u32 dccm_len; u32 dccm2_offset; u32 dccm2_len; - u32 smem_offset; - u32 smem_len; u16 nvm_ver; u16 nvm_calib_ver; + u16 bw_limit; u32 rx_with_siso_diversity:1, tx_with_siso_diversity:1, internal_wimax_coex:1, host_interrupt_operation_mode:1, - high_temp:1, - mac_addr_from_csr:10, lp_xtal_workaround:1, - apmg_not_supported:1, vht_mu_mimo_supported:1, - cdb:1, - dbgc_supported:1, uhb_supported:1; u8 valid_tx_ant; u8 valid_rx_ant; u8 non_shared_ant; - u8 nvm_hw_section_num; - u8 max_tx_agg_size; u8 ucode_api_max; u8 ucode_api_min; u16 num_rbds; - u32 min_umac_error_event_table; - u32 d3_debug_data_base_addr; - u32 d3_debug_data_length; - u32 min_txq_size; - u32 gp2_reg_addr; - u32 min_ba_txq_size; - const struct iwl_fw_mon_regs mon_dram_regs; - const struct iwl_fw_mon_regs mon_smem_regs; - const struct iwl_fw_mon_regs mon_dbgi_regs; }; #define IWL_CFG_ANY (~0) @@ -419,8 +432,10 @@ struct iwl_cfg { #define IWL_CFG_MAC_TYPE_PU 0x31 #define IWL_CFG_MAC_TYPE_TH 0x32 #define IWL_CFG_MAC_TYPE_QU 0x33 +#define IWL_CFG_MAC_TYPE_CC 0x34 #define IWL_CFG_MAC_TYPE_QUZ 0x35 #define IWL_CFG_MAC_TYPE_SO 0x37 +#define IWL_CFG_MAC_TYPE_TY 0x42 #define IWL_CFG_MAC_TYPE_SOF 0x43 #define IWL_CFG_MAC_TYPE_MA 0x44 #define IWL_CFG_MAC_TYPE_BZ 0x46 @@ -432,8 +447,6 @@ struct iwl_cfg { #define IWL_CFG_MAC_TYPE_BR 0x4C #define IWL_CFG_MAC_TYPE_DR 0x4D -#define IWL_CFG_RF_TYPE_TH 0x105 -#define IWL_CFG_RF_TYPE_TH1 0x108 #define IWL_CFG_RF_TYPE_JF2 0x105 #define IWL_CFG_RF_TYPE_JF1 0x108 #define IWL_CFG_RF_TYPE_HR2 0x10A @@ -451,12 +464,6 @@ struct iwl_cfg { #define IWL_CFG_RF_ID_HR 0x7 #define IWL_CFG_RF_ID_HR1 0x4 -#define IWL_CFG_NO_160 0x1 -#define IWL_CFG_160 0x0 - -#define IWL_CFG_NO_320 0x1 -#define IWL_CFG_320 0x0 - #define IWL_CFG_CORES_BT 0x0 #define IWL_CFG_CORES_BT_GNSS 0x5 @@ -467,55 +474,133 @@ struct iwl_cfg { #define IWL_CFG_IS_JACKET 0x1 #define IWL_SUBDEVICE_RF_ID(subdevice) ((u16)((subdevice) & 0x00F0) >> 4) -#define IWL_SUBDEVICE_NO_160(subdevice) ((u16)((subdevice) & 0x0200) >> 9) +#define IWL_SUBDEVICE_BW_LIM(subdevice) ((u16)((subdevice) & 0x0200) >> 9) #define IWL_SUBDEVICE_CORES(subdevice) ((u16)((subdevice) & 0x1C00) >> 10) struct iwl_dev_info { + const struct iwl_rf_cfg *cfg; + const char *name; u16 device; u16 subdevice; - u16 mac_type; - u16 rf_type; - u8 mac_step; - u8 rf_step; - u8 rf_id; - u8 no_160; - u8 cores; - u8 cdb; - u8 jacket; - const struct iwl_cfg *cfg; - const char *name; + u32 subdevice_m_l:4, + subdevice_m_h:4, + match_rf_type:1, + rf_type:9, + match_bw_limit:1, + bw_limit:1, + match_rf_step:1, + rf_step:4, + match_rf_id:1, + rf_id:4, + match_cdb:1, + cdb:1; }; #if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) extern const struct iwl_dev_info iwl_dev_info_table[]; extern const unsigned int iwl_dev_info_table_size; const struct iwl_dev_info * -iwl_pci_find_dev_info(u16 device, u16 subsystem_device, - u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, - u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step); +iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 rf_type, u8 cdb, + u8 rf_id, u8 bw_limit, u8 rf_step); extern const struct pci_device_id iwl_hw_card_ids[]; #endif /* * This list declares the config structures for all devices. */ -extern const struct iwl_cfg_trans_params iwl9000_trans_cfg; -extern const struct iwl_cfg_trans_params iwl9560_trans_cfg; -extern const struct iwl_cfg_trans_params iwl9560_long_latency_trans_cfg; -extern const struct iwl_cfg_trans_params iwl9560_shared_clk_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_qu_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_qu_medium_latency_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_qu_long_latency_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_ax200_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_so_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_so_long_latency_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_so_long_latency_imr_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_ma_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_bz_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_gl_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_sc_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_dr_trans_cfg; -extern const struct iwl_cfg_trans_params iwl_br_trans_cfg; +extern const struct iwl_mac_cfg iwl1000_mac_cfg; +extern const struct iwl_mac_cfg iwl5000_mac_cfg; +extern const struct iwl_mac_cfg iwl2000_mac_cfg; +extern const struct iwl_mac_cfg iwl2030_mac_cfg; +extern const struct iwl_mac_cfg iwl105_mac_cfg; +extern const struct iwl_mac_cfg iwl135_mac_cfg; +extern const struct iwl_mac_cfg iwl5150_mac_cfg; +extern const struct iwl_mac_cfg iwl6005_mac_cfg; +extern const struct iwl_mac_cfg iwl6030_mac_cfg; +extern const struct iwl_mac_cfg iwl6000i_mac_cfg; +extern const struct iwl_mac_cfg iwl6050_mac_cfg; +extern const struct iwl_mac_cfg iwl6150_mac_cfg; +extern const struct iwl_mac_cfg iwl6000_mac_cfg; +extern const struct iwl_mac_cfg iwl7000_mac_cfg; +extern const struct iwl_mac_cfg iwl8000_mac_cfg; +extern const struct iwl_mac_cfg iwl9000_mac_cfg; +extern const struct iwl_mac_cfg iwl9560_mac_cfg; +extern const struct iwl_mac_cfg iwl9560_long_latency_mac_cfg; +extern const struct iwl_mac_cfg iwl9560_shared_clk_mac_cfg; +extern const struct iwl_mac_cfg iwl_qu_mac_cfg; +extern const struct iwl_mac_cfg iwl_qu_medium_latency_mac_cfg; +extern const struct iwl_mac_cfg iwl_qu_long_latency_mac_cfg; +extern const struct iwl_mac_cfg iwl_ax200_mac_cfg; +extern const struct iwl_mac_cfg iwl_ty_mac_cfg; +extern const struct iwl_mac_cfg iwl_so_mac_cfg; +extern const struct iwl_mac_cfg iwl_so_long_latency_mac_cfg; +extern const struct iwl_mac_cfg iwl_so_long_latency_imr_mac_cfg; +extern const struct iwl_mac_cfg iwl_ma_mac_cfg; +extern const struct iwl_mac_cfg iwl_bz_mac_cfg; +extern const struct iwl_mac_cfg iwl_gl_mac_cfg; +extern const struct iwl_mac_cfg iwl_sc_mac_cfg; +extern const struct iwl_mac_cfg iwl_dr_mac_cfg; + +extern const char iwl1000_bgn_name[]; +extern const char iwl1000_bg_name[]; +extern const char iwl100_bgn_name[]; +extern const char iwl100_bg_name[]; +extern const char iwl2000_2bgn_name[]; +extern const char iwl2000_2bgn_d_name[]; +extern const char iwl2030_2bgn_name[]; +extern const char iwl105_bgn_name[]; +extern const char iwl105_bgn_d_name[]; +extern const char iwl135_bgn_name[]; +extern const char iwl5300_agn_name[]; +extern const char iwl5100_bgn_name[]; +extern const char iwl5100_abg_name[]; +extern const char iwl5100_agn_name[]; +extern const char iwl5350_agn_name[]; +extern const char iwl5150_agn_name[]; +extern const char iwl5150_abg_name[]; +extern const char iwl6005_2agn_name[]; +extern const char iwl6005_2abg_name[]; +extern const char iwl6005_2bg_name[]; +extern const char iwl6005_2agn_sff_name[]; +extern const char iwl6005_2agn_d_name[]; +extern const char iwl6005_2agn_mow1_name[]; +extern const char iwl6005_2agn_mow2_name[]; +extern const char iwl6030_2agn_name[]; +extern const char iwl6030_2abg_name[]; +extern const char iwl6030_2bgn_name[]; +extern const char iwl6030_2bg_name[]; +extern const char iwl6035_2agn_name[]; +extern const char iwl6035_2agn_sff_name[]; +extern const char iwl1030_bgn_name[]; +extern const char iwl1030_bg_name[]; +extern const char iwl130_bgn_name[]; +extern const char iwl130_bg_name[]; +extern const char iwl6000i_2agn_name[]; +extern const char iwl6000i_2abg_name[]; +extern const char iwl6000i_2bg_name[]; +extern const char iwl6050_2agn_name[]; +extern const char iwl6050_2abg_name[]; +extern const char iwl6150_bgn_name[]; +extern const char iwl6150_bg_name[]; +extern const char iwl6000_3agn_name[]; +extern const char iwl7260_2ac_name[]; +extern const char iwl7260_2n_name[]; +extern const char iwl7260_n_name[]; +extern const char iwl3160_2ac_name[]; +extern const char iwl3160_2n_name[]; +extern const char iwl3160_n_name[]; +extern const char iwl3165_2ac_name[]; +extern const char iwl3168_2ac_name[]; +extern const char iwl7265_2ac_name[]; +extern const char iwl7265_2n_name[]; +extern const char iwl7265_n_name[]; +extern const char iwl8260_2n_name[]; +extern const char iwl8260_2ac_name[]; +extern const char iwl8265_2ac_name[]; +extern const char iwl8275_2ac_name[]; +extern const char iwl4165_2ac_name[]; +extern const char iwl_killer_1435i_name[]; +extern const char iwl_killer_1434_kix_name[]; extern const char iwl9162_name[]; extern const char iwl9260_name[]; extern const char iwl9260_1_name[]; @@ -534,7 +619,6 @@ extern const char iwl9560_killer_1550i_name[]; extern const char iwl9560_killer_1550s_name[]; extern const char iwl_ax200_name[]; extern const char iwl_ax203_name[]; -extern const char iwl_ax204_name[]; extern const char iwl_ax201_name[]; extern const char iwl_ax101_name[]; extern const char iwl_ax200_killer_1650w_name[]; @@ -549,128 +633,84 @@ extern const char iwl_ax211_killer_1675s_name[]; extern const char iwl_ax211_killer_1675i_name[]; extern const char iwl_ax411_killer_1690s_name[]; extern const char iwl_ax411_killer_1690i_name[]; +extern const char iwl_ax210_name[]; extern const char iwl_ax211_name[]; -extern const char iwl_ax221_name[]; -extern const char iwl_ax231_name[]; extern const char iwl_ax411_name[]; -extern const char iwl_bz_name[]; -extern const char iwl_fm_name[]; -extern const char iwl_wh_name[]; -extern const char iwl_gl_name[]; -extern const char iwl_mtp_name[]; -extern const char iwl_sc_name[]; -extern const char iwl_sc2_name[]; -extern const char iwl_sc2f_name[]; -extern const char iwl_dr_name[]; -extern const char iwl_br_name[]; +extern const char iwl_killer_be1750s_name[]; +extern const char iwl_killer_be1750i_name[]; +extern const char iwl_killer_be1750w_name[]; +extern const char iwl_killer_be1750x_name[]; +extern const char iwl_killer_be1790s_name[]; +extern const char iwl_killer_be1790i_name[]; +extern const char iwl_be201_name[]; +extern const char iwl_be200_name[]; +extern const char iwl_be202_name[]; +extern const char iwl_be401_name[]; +extern const char iwl_be213_name[]; +extern const char iwl_killer_be1775s_name[]; +extern const char iwl_killer_be1775i_name[]; +extern const char iwl_be211_name[]; +extern const char iwl_killer_bn1850w2_name[]; +extern const char iwl_killer_bn1850i_name[]; +extern const char iwl_bn201_name[]; +extern const char iwl_be221_name[]; +extern const char iwl_be223_name[]; #if IS_ENABLED(CONFIG_IWLDVM) -extern const struct iwl_cfg iwl5300_agn_cfg; -extern const struct iwl_cfg iwl5100_agn_cfg; -extern const struct iwl_cfg iwl5350_agn_cfg; -extern const struct iwl_cfg iwl5100_bgn_cfg; -extern const struct iwl_cfg iwl5100_abg_cfg; -extern const struct iwl_cfg iwl5150_agn_cfg; -extern const struct iwl_cfg iwl5150_abg_cfg; -extern const struct iwl_cfg iwl6005_2agn_cfg; -extern const struct iwl_cfg iwl6005_2abg_cfg; -extern const struct iwl_cfg iwl6005_2bg_cfg; -extern const struct iwl_cfg iwl6005_2agn_sff_cfg; -extern const struct iwl_cfg iwl6005_2agn_d_cfg; -extern const struct iwl_cfg iwl6005_2agn_mow1_cfg; -extern const struct iwl_cfg iwl6005_2agn_mow2_cfg; -extern const struct iwl_cfg iwl1030_bgn_cfg; -extern const struct iwl_cfg iwl1030_bg_cfg; -extern const struct iwl_cfg iwl6030_2agn_cfg; -extern const struct iwl_cfg iwl6030_2abg_cfg; -extern const struct iwl_cfg iwl6030_2bgn_cfg; -extern const struct iwl_cfg iwl6030_2bg_cfg; -extern const struct iwl_cfg iwl6000i_2agn_cfg; -extern const struct iwl_cfg iwl6000i_2abg_cfg; -extern const struct iwl_cfg iwl6000i_2bg_cfg; -extern const struct iwl_cfg iwl6000_3agn_cfg; -extern const struct iwl_cfg iwl6050_2agn_cfg; -extern const struct iwl_cfg iwl6050_2abg_cfg; -extern const struct iwl_cfg iwl6150_bgn_cfg; -extern const struct iwl_cfg iwl6150_bg_cfg; -extern const struct iwl_cfg iwl1000_bgn_cfg; -extern const struct iwl_cfg iwl1000_bg_cfg; -extern const struct iwl_cfg iwl100_bgn_cfg; -extern const struct iwl_cfg iwl100_bg_cfg; -extern const struct iwl_cfg iwl130_bgn_cfg; -extern const struct iwl_cfg iwl130_bg_cfg; -extern const struct iwl_cfg iwl2000_2bgn_cfg; -extern const struct iwl_cfg iwl2000_2bgn_d_cfg; -extern const struct iwl_cfg iwl2030_2bgn_cfg; -extern const struct iwl_cfg iwl6035_2agn_cfg; -extern const struct iwl_cfg iwl6035_2agn_sff_cfg; -extern const struct iwl_cfg iwl105_bgn_cfg; -extern const struct iwl_cfg iwl105_bgn_d_cfg; -extern const struct iwl_cfg iwl135_bgn_cfg; +extern const struct iwl_rf_cfg iwl5300_agn_cfg; +extern const struct iwl_rf_cfg iwl5350_agn_cfg; +extern const struct iwl_rf_cfg iwl5100_n_cfg; +extern const struct iwl_rf_cfg iwl5100_abg_cfg; +extern const struct iwl_rf_cfg iwl5150_agn_cfg; +extern const struct iwl_rf_cfg iwl5150_abg_cfg; +extern const struct iwl_rf_cfg iwl6005_non_n_cfg; +extern const struct iwl_rf_cfg iwl6005_n_cfg; +extern const struct iwl_rf_cfg iwl6030_n_cfg; +extern const struct iwl_rf_cfg iwl6030_non_n_cfg; +extern const struct iwl_rf_cfg iwl6000i_2agn_cfg; +extern const struct iwl_rf_cfg iwl6000i_non_n_cfg; +extern const struct iwl_rf_cfg iwl6000i_non_n_cfg; +extern const struct iwl_rf_cfg iwl6000_3agn_cfg; +extern const struct iwl_rf_cfg iwl6050_2agn_cfg; +extern const struct iwl_rf_cfg iwl6050_2abg_cfg; +extern const struct iwl_rf_cfg iwl6150_bgn_cfg; +extern const struct iwl_rf_cfg iwl6150_bg_cfg; +extern const struct iwl_rf_cfg iwl1000_bgn_cfg; +extern const struct iwl_rf_cfg iwl1000_bg_cfg; +extern const struct iwl_rf_cfg iwl100_bgn_cfg; +extern const struct iwl_rf_cfg iwl100_bg_cfg; +extern const struct iwl_rf_cfg iwl130_bgn_cfg; +extern const struct iwl_rf_cfg iwl130_bg_cfg; +extern const struct iwl_rf_cfg iwl2000_2bgn_cfg; +extern const struct iwl_rf_cfg iwl2030_2bgn_cfg; +extern const struct iwl_rf_cfg iwl6035_2agn_cfg; +extern const struct iwl_rf_cfg iwl105_bgn_cfg; +extern const struct iwl_rf_cfg iwl135_bgn_cfg; #endif /* CONFIG_IWLDVM */ #if IS_ENABLED(CONFIG_IWLMVM) -extern const struct iwl_ht_params iwl_22000_ht_params; -extern const struct iwl_cfg iwl7260_2ac_cfg; -extern const struct iwl_cfg iwl7260_2ac_cfg_high_temp; -extern const struct iwl_cfg iwl7260_2n_cfg; -extern const struct iwl_cfg iwl7260_n_cfg; -extern const struct iwl_cfg iwl3160_2ac_cfg; -extern const struct iwl_cfg iwl3160_2n_cfg; -extern const struct iwl_cfg iwl3160_n_cfg; -extern const struct iwl_cfg iwl3165_2ac_cfg; -extern const struct iwl_cfg iwl3168_2ac_cfg; -extern const struct iwl_cfg iwl7265_2ac_cfg; -extern const struct iwl_cfg iwl7265_2n_cfg; -extern const struct iwl_cfg iwl7265_n_cfg; -extern const struct iwl_cfg iwl7265d_2ac_cfg; -extern const struct iwl_cfg iwl7265d_2n_cfg; -extern const struct iwl_cfg iwl7265d_n_cfg; -extern const struct iwl_cfg iwl8260_2n_cfg; -extern const struct iwl_cfg iwl8260_2ac_cfg; -extern const struct iwl_cfg iwl8265_2ac_cfg; -extern const struct iwl_cfg iwl8275_2ac_cfg; -extern const struct iwl_cfg iwl4165_2ac_cfg; -extern const struct iwl_cfg iwl9260_2ac_cfg; -extern const struct iwl_cfg iwl9560_qu_b0_jf_b0_cfg; -extern const struct iwl_cfg iwl9560_qu_c0_jf_b0_cfg; -extern const struct iwl_cfg iwl9560_quz_a0_jf_b0_cfg; -extern const struct iwl_cfg iwl9560_2ac_cfg_soc; -extern const struct iwl_cfg iwl_qu_b0_hr1_b0; -extern const struct iwl_cfg iwl_qu_c0_hr1_b0; -extern const struct iwl_cfg iwl_quz_a0_hr1_b0; -extern const struct iwl_cfg iwl_qu_b0_hr_b0; -extern const struct iwl_cfg iwl_qu_c0_hr_b0; -extern const struct iwl_cfg iwl_ax200_cfg_cc; -extern const struct iwl_cfg iwl_ax201_cfg_qu_hr; -extern const struct iwl_cfg iwl_ax201_cfg_qu_c0_hr_b0; -extern const struct iwl_cfg iwl_ax201_cfg_quz_hr; -extern const struct iwl_cfg iwl_ax1650i_cfg_quz_hr; -extern const struct iwl_cfg iwl_ax1650s_cfg_quz_hr; -extern const struct iwl_cfg killer1650s_2ax_cfg_qu_b0_hr_b0; -extern const struct iwl_cfg killer1650i_2ax_cfg_qu_b0_hr_b0; -extern const struct iwl_cfg killer1650s_2ax_cfg_qu_c0_hr_b0; -extern const struct iwl_cfg killer1650i_2ax_cfg_qu_c0_hr_b0; -extern const struct iwl_cfg killer1650x_2ax_cfg; -extern const struct iwl_cfg killer1650w_2ax_cfg; -extern const struct iwl_cfg iwlax210_2ax_cfg_so_jf_b0; -extern const struct iwl_cfg iwlax211_2ax_cfg_so_gf_a0; -extern const struct iwl_cfg iwlax211_2ax_cfg_so_gf_a0_long; -extern const struct iwl_cfg iwlax210_2ax_cfg_ty_gf_a0; -extern const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0; -extern const struct iwl_cfg iwlax411_2ax_cfg_so_gf4_a0_long; - -extern const struct iwl_cfg iwl_cfg_ma; - -extern const struct iwl_cfg iwl_cfg_so_a0_hr_a0; -extern const struct iwl_cfg iwl_cfg_quz_a0_hr_b0; - -extern const struct iwl_cfg iwl_cfg_bz; -extern const struct iwl_cfg iwl_cfg_gl; - -extern const struct iwl_cfg iwl_cfg_sc; -extern const struct iwl_cfg iwl_cfg_sc2; -extern const struct iwl_cfg iwl_cfg_sc2f; -extern const struct iwl_cfg iwl_cfg_dr; -extern const struct iwl_cfg iwl_cfg_br; +extern const struct iwl_rf_cfg iwl7260_cfg; +extern const struct iwl_rf_cfg iwl7260_high_temp_cfg; +extern const struct iwl_rf_cfg iwl3160_cfg; +extern const struct iwl_rf_cfg iwl3165_2ac_cfg; +extern const struct iwl_rf_cfg iwl3168_2ac_cfg; +extern const struct iwl_rf_cfg iwl7265_cfg; +extern const struct iwl_rf_cfg iwl7265d_cfg; +extern const struct iwl_rf_cfg iwl8260_cfg; +extern const struct iwl_rf_cfg iwl8265_cfg; +extern const struct iwl_rf_cfg iwl_rf_jf; +extern const struct iwl_rf_cfg iwl_rf_jf_80mhz; +extern const struct iwl_rf_cfg iwl_rf_hr1; +extern const struct iwl_rf_cfg iwl_rf_hr; +extern const struct iwl_rf_cfg iwl_rf_hr_80mhz; + +extern const struct iwl_rf_cfg iwl_rf_gf; #endif /* CONFIG_IWLMVM */ +#if IS_ENABLED(CONFIG_IWLMLD) +extern const struct iwl_rf_cfg iwl_rf_fm; +extern const struct iwl_rf_cfg iwl_rf_fm_160mhz; +#define iwl_rf_wh iwl_rf_fm +#define iwl_rf_wh_160mhz iwl_rf_fm_160mhz +#define iwl_rf_pe iwl_rf_fm +#endif /* CONFIG_IWLMLD */ + #endif /* __IWL_CONFIG_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h b/drivers/net/wireless/intel/iwlwifi/iwl-context-info-v2.h index cd25a1b9f2ff..8c5c0ea46181 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-context-info-gen3.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-context-info-v2.h @@ -1,9 +1,9 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018, 2020-2024 Intel Corporation + * Copyright (C) 2018, 2020-2025 Intel Corporation */ -#ifndef __iwl_context_info_file_gen3_h__ -#define __iwl_context_info_file_gen3_h__ +#ifndef __iwl_context_info_file_v2_h__ +#define __iwl_context_info_file_v2_h__ #include "iwl-context-info.h" @@ -58,6 +58,7 @@ enum iwl_prph_scratch_mtr_format { * @IWL_PRPH_SCRATCH_RB_SIZE_EXT_16K: 16kB RB size * @IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE: Indicate fw to set SCU_FORCE_ACTIVE * upon reset. + * @IWL_PRPH_SCRATCH_TOP_RESET: request TOP reset */ enum iwl_prph_scratch_flags { IWL_PRPH_SCRATCH_IMR_DEBUG_EN = BIT(1), @@ -74,16 +75,21 @@ enum iwl_prph_scratch_flags { IWL_PRPH_SCRATCH_RB_SIZE_EXT_12K = 9 << 20, IWL_PRPH_SCRATCH_RB_SIZE_EXT_16K = 10 << 20, IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE = BIT(29), + IWL_PRPH_SCRATCH_TOP_RESET = BIT(30), }; /** * enum iwl_prph_scratch_ext_flags - PRPH scratch control ext flags + * @IWL_PRPH_SCRATCH_EXT_EXT_FSEQ: external FSEQ image provided * @IWL_PRPH_SCRATCH_EXT_URM_FW: switch to URM mode based on fw setting * @IWL_PRPH_SCRATCH_EXT_URM_PERM: switch to permanent URM mode + * @IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID: use external 32 KHz clock */ enum iwl_prph_scratch_ext_flags { - IWL_PRPH_SCRATCH_EXT_URM_FW = BIT(4), - IWL_PRPH_SCRATCH_EXT_URM_PERM = BIT(5), + IWL_PRPH_SCRATCH_EXT_EXT_FSEQ = BIT(0), + IWL_PRPH_SCRATCH_EXT_URM_FW = BIT(4), + IWL_PRPH_SCRATCH_EXT_URM_PERM = BIT(5), + IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID = BIT(8), }; /** @@ -200,6 +206,19 @@ struct iwl_prph_scratch_ctrl_cfg { struct iwl_prph_scratch_step_cfg step_cfg; } __packed; /* PERIPH_SCRATCH_CTRL_CFG_S */ +#define IWL_NUM_DRAM_FSEQ_ENTRIES 8 + +/** + * struct iwl_context_info_dram_fseq - images DRAM map (with fseq) + * each entry in the map represents a DRAM chunk of up to 32 KB + * @common: UMAC/LMAC/virtual images + * @fseq_img: FSEQ image DRAM map + */ +struct iwl_context_info_dram_fseq { + struct iwl_context_info_dram_nonfseq common; + __le64 fseq_img[IWL_NUM_DRAM_FSEQ_ENTRIES]; +} __packed; /* PERIPH_SCRATCH_DRAM_MAP_S */ + /** * struct iwl_prph_scratch - peripheral scratch mapping * @ctrl_cfg: control and configuration of prph scratch @@ -213,7 +232,7 @@ struct iwl_prph_scratch { __le32 fseq_override; __le32 step_analog_params; __le32 reserved[8]; - struct iwl_context_info_dram dram; + struct iwl_context_info_dram_fseq dram; } __packed; /* PERIPH_SCRATCH_S */ /** @@ -231,7 +250,7 @@ struct iwl_prph_info { } __packed; /* PERIPH_INFO_S */ /** - * struct iwl_context_info_gen3 - device INIT configuration + * struct iwl_context_info_v2 - device INIT configuration * @version: version of the context information * @size: size of context information in DWs * @config: context in which the peripheral would execute - a subset of @@ -274,7 +293,7 @@ struct iwl_prph_info { * @prph_scratch_size: the size of the peripheral scratch structure in DWs * @reserved: reserved */ -struct iwl_context_info_gen3 { +struct iwl_context_info_v2 { __le16 version; __le16 size; __le32 config; @@ -304,22 +323,22 @@ struct iwl_context_info_gen3 { __le32 reserved; } __packed; /* IPC_CONTEXT_INFO_S */ -int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, - const struct fw_img *fw); -void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive); +int iwl_pcie_ctxt_info_v2_alloc(struct iwl_trans *trans, + const struct iwl_fw *fw, + const struct fw_img *img); +void iwl_pcie_ctxt_info_v2_kick(struct iwl_trans *trans); +void iwl_pcie_ctxt_info_v2_free(struct iwl_trans *trans, bool alive); -int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans, - const struct iwl_pnvm_image *pnvm_payloads, - const struct iwl_ucode_capabilities *capa); -void iwl_trans_pcie_ctx_info_gen3_set_pnvm(struct iwl_trans *trans, - const struct iwl_ucode_capabilities *capa); +int iwl_trans_pcie_ctx_info_v2_load_pnvm(struct iwl_trans *trans, + const struct iwl_pnvm_image *pnvm_payloads, + const struct iwl_ucode_capabilities *capa); +void iwl_trans_pcie_ctx_info_v2_set_pnvm(struct iwl_trans *trans, + const struct iwl_ucode_capabilities *capa); int -iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans, - const struct iwl_pnvm_image *payloads, - const struct iwl_ucode_capabilities *capa); +iwl_trans_pcie_ctx_info_v2_load_reduce_power(struct iwl_trans *trans, + const struct iwl_pnvm_image *payloads, + const struct iwl_ucode_capabilities *capa); void -iwl_trans_pcie_ctx_info_gen3_set_reduce_power(struct iwl_trans *trans, - const struct iwl_ucode_capabilities *capa); -int iwl_trans_pcie_ctx_info_gen3_set_step(struct iwl_trans *trans, - u32 mbx_addr_0_step, u32 mbx_addr_1_step); -#endif /* __iwl_context_info_file_gen3_h__ */ +iwl_trans_pcie_ctx_info_v2_set_reduce_power(struct iwl_trans *trans, + const struct iwl_ucode_capabilities *capa); +#endif /* __iwl_context_info_file_v2_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h b/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h index dfd44fabf237..7ae0fbdef208 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-context-info.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2020, 2022, 2024 Intel Corporation + * Copyright (C) 2018-2020, 2022, 2024-2025 Intel Corporation */ #ifndef __iwl_context_info_file_h__ #define __iwl_context_info_file_h__ @@ -78,13 +78,13 @@ struct iwl_context_info_control { } __packed; /** - * struct iwl_context_info_dram - images DRAM map + * struct iwl_context_info_dram_nonfseq - images DRAM map * each entry in the map represents a DRAM chunk of up to 32 KB * @umac_img: UMAC image DRAM map * @lmac_img: LMAC image DRAM map * @virtual_img: paged image DRAM map */ -struct iwl_context_info_dram { +struct iwl_context_info_dram_nonfseq { __le64 umac_img[IWL_MAX_DRAM_ENTRY]; __le64 lmac_img[IWL_MAX_DRAM_ENTRY]; __le64 virtual_img[IWL_MAX_DRAM_ENTRY]; @@ -177,16 +177,16 @@ struct iwl_context_info { struct iwl_context_info_early_dbg_cfg edbg_cfg; struct iwl_context_info_pnvm_cfg pnvm_cfg; __le32 reserved2[16]; - struct iwl_context_info_dram dram; + struct iwl_context_info_dram_nonfseq dram; __le32 reserved3[16]; -} __packed; +} __packed; /* BOOT_LOADER_CONTEXT_INFO_S */ -int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, const struct fw_img *fw); +int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, const struct fw_img *img); void iwl_pcie_ctxt_info_free(struct iwl_trans *trans); void iwl_pcie_ctxt_info_free_paging(struct iwl_trans *trans); int iwl_pcie_init_fw_sec(struct iwl_trans *trans, const struct fw_img *fw, - struct iwl_context_info_dram *ctxt_dram); + struct iwl_context_info_dram_nonfseq *ctxt_dram); void *iwl_pcie_ctxt_info_dma_alloc_coherent(struct iwl_trans *trans, size_t size, dma_addr_t *phys); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h index be9e464c9b7b..0fd452cb94ae 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-csr.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-csr.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2016 Intel Deutschland GmbH */ @@ -107,6 +107,13 @@ /* GIO Chicken Bits (PCI Express bus link power management) */ #define CSR_GIO_CHICKEN_BITS (CSR_BASE+0x100) +#define CSR_IPC_STATE (CSR_BASE + 0x110) +#define CSR_IPC_STATE_RESET 0x00000030 +#define CSR_IPC_STATE_RESET_NONE 0 +#define CSR_IPC_STATE_RESET_SW_READY 1 +#define CSR_IPC_STATE_RESET_TOP_READY 2 +#define CSR_IPC_STATE_RESET_TOP_FOLLOWER 3 + #define CSR_IPC_SLEEP_CONTROL (CSR_BASE + 0x114) #define CSR_IPC_SLEEP_CONTROL_SUSPEND 0x3 #define CSR_IPC_SLEEP_CONTROL_RESUME 0 @@ -148,6 +155,7 @@ * during a error FW error. */ #define CSR_FUNC_SCRATCH_INIT_VALUE (0x01010101) +#define CSR_FUNC_SCRATCH_POWER_OFF_MASK 0xFFFF /* Bits for CSR_HW_IF_CONFIG_REG */ #define CSR_HW_IF_CONFIG_REG_MSK_MAC_STEP_DASH (0x0000000F) @@ -193,17 +201,19 @@ #define CSR_INT_BIT_RF_KILL (1 << 7) /* HW RFKILL switch GP_CNTRL[27] toggled */ #define CSR_INT_BIT_CT_KILL (1 << 6) /* Critical temp (chip too hot) rfkill */ #define CSR_INT_BIT_SW_RX (1 << 3) /* Rx, command responses */ +#define CSR_INT_BIT_RESET_DONE (1 << 2) /* reset handshake with firmware is done */ #define CSR_INT_BIT_WAKEUP (1 << 1) /* NIC controller waking up (pwr mgmt) */ #define CSR_INT_BIT_ALIVE (1 << 0) /* uCode interrupts once it initializes */ -#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ - CSR_INT_BIT_HW_ERR | \ - CSR_INT_BIT_FH_TX | \ - CSR_INT_BIT_SW_ERR | \ - CSR_INT_BIT_RF_KILL | \ - CSR_INT_BIT_SW_RX | \ - CSR_INT_BIT_WAKEUP | \ - CSR_INT_BIT_ALIVE | \ +#define CSR_INI_SET_MASK (CSR_INT_BIT_FH_RX | \ + CSR_INT_BIT_HW_ERR | \ + CSR_INT_BIT_FH_TX | \ + CSR_INT_BIT_SW_ERR | \ + CSR_INT_BIT_RF_KILL | \ + CSR_INT_BIT_SW_RX | \ + CSR_INT_BIT_WAKEUP | \ + CSR_INT_BIT_RESET_DONE | \ + CSR_INT_BIT_ALIVE | \ CSR_INT_BIT_RX_PERIODIC) /* interrupt flags in FH (flow handler) (PCI busmaster DMA) */ @@ -640,7 +650,7 @@ enum msix_hw_int_causes { * HW address related registers * *****************************************************************************/ -#define CSR_ADDR_BASE(trans) ((trans)->cfg->mac_addr_from_csr) +#define CSR_ADDR_BASE(trans) ((trans)->mac_cfg->base->mac_addr_from_csr) #define CSR_MAC_ADDR0_OTP(trans) (CSR_ADDR_BASE(trans) + 0x00) #define CSR_MAC_ADDR1_OTP(trans) (CSR_ADDR_BASE(trans) + 0x04) #define CSR_MAC_ADDR0_STRAP(trans) (CSR_ADDR_BASE(trans) + 0x08) diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c index 08d990ba8a79..5c8f6dc9a3e0 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-dbg-tlv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include <linux/firmware.h> #include "iwl-drv.h" @@ -503,7 +503,7 @@ void iwl_dbg_tlv_load_bin(struct device *dev, struct iwl_trans *trans) int res; if (!iwlwifi_mod_params.enable_ini || - trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000) + trans->mac_cfg->device_family <= IWL_DEVICE_FAMILY_8000) return; res = firmware_request_nowarn(&fw, yoyo_bin, dev); @@ -603,11 +603,11 @@ static int iwl_dbg_tlv_alloc_fragments(struct iwl_fw_runtime *fwrt, return 0; num_frags = le32_to_cpu(fw_mon_cfg->max_frags_num); - if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { + if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { if (alloc_id != IWL_FW_INI_ALLOCATION_ID_DBGC1) return -EIO; num_frags = 1; - } else if (fwrt->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ && + } else if (fwrt->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ && alloc_id > IWL_FW_INI_ALLOCATION_ID_DBGC3) { return -EIO; } @@ -1241,7 +1241,7 @@ iwl_dbg_tlv_tp_trigger(struct iwl_fw_runtime *fwrt, bool sync, fwrt->trans->dbg.restart_required = false; - if (fwrt->trans->trans_cfg->device_family == + if (fwrt->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000) { fwrt->trans->dbg.restart_required = true; } else if (tp == IWL_FW_INI_TIME_POINT_FW_ASSERT && @@ -1372,15 +1372,15 @@ void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt, switch (tp_id) { case IWL_FW_INI_TIME_POINT_EARLY: iwl_dbg_tlv_init_cfg(fwrt); - iwl_dbg_tlv_apply_config(fwrt, conf_list); iwl_dbg_tlv_update_drams(fwrt); iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); + iwl_dbg_tlv_apply_config(fwrt, conf_list); break; case IWL_FW_INI_TIME_POINT_AFTER_ALIVE: iwl_dbg_tlv_apply_buffers(fwrt); iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); - iwl_dbg_tlv_apply_config(fwrt, conf_list); iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); + iwl_dbg_tlv_apply_config(fwrt, conf_list); break; case IWL_FW_INI_TIME_POINT_PERIODIC: iwl_dbg_tlv_set_periodic_trigs(fwrt); @@ -1390,14 +1390,14 @@ void _iwl_dbg_tlv_time_point(struct iwl_fw_runtime *fwrt, case IWL_FW_INI_TIME_POINT_MISSED_BEACONS: case IWL_FW_INI_TIME_POINT_FW_DHC_NOTIFICATION: iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); - iwl_dbg_tlv_apply_config(fwrt, conf_list); iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, iwl_dbg_tlv_check_fw_pkt); + iwl_dbg_tlv_apply_config(fwrt, conf_list); break; default: iwl_dbg_tlv_send_hcmds(fwrt, hcmd_list); - iwl_dbg_tlv_apply_config(fwrt, conf_list); iwl_dbg_tlv_tp_trigger(fwrt, sync, trig_list, tp_data, NULL); + iwl_dbg_tlv_apply_config(fwrt, conf_list); break; } } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h index bf52c2edaad1..ea32dc88584f 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-debug.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-debug.h @@ -2,7 +2,7 @@ /****************************************************************************** * * Copyright(c) 2003 - 2014 Intel Corporation. All rights reserved. - * Copyright(c) 2018 - 2021, 2024 Intel Corporation + * Copyright(c) 2018 - 2021, 2024-2025 Intel Corporation * * Portions of this file are derived from the ipw3945 project. *****************************************************************************/ @@ -156,6 +156,7 @@ do { \ #define IWL_DL_FW 0x00010000 #define IWL_DL_RF_KILL 0x00020000 #define IWL_DL_TPT 0x00040000 +#define IWL_DL_PTP 0x00080000 /* 0x00F00000 - 0x00100000 */ #define IWL_DL_RATE 0x00100000 #define IWL_DL_CALIB 0x00200000 @@ -165,7 +166,7 @@ do { \ #define IWL_DL_RX 0x01000000 #define IWL_DL_ISR 0x02000000 #define IWL_DL_HT 0x04000000 -#define IWL_DL_EXTERNAL 0x08000000 +#define IWL_DL_EHT 0x08000000 /* 0xF0000000 - 0x10000000 */ #define IWL_DL_11H 0x10000000 #define IWL_DL_STATS 0x20000000 @@ -175,7 +176,6 @@ do { \ #define IWL_DEBUG_INFO(p, f, a...) IWL_DEBUG(p, IWL_DL_INFO, f, ## a) #define IWL_DEBUG_TDLS(p, f, a...) IWL_DEBUG(p, IWL_DL_TDLS, f, ## a) #define IWL_DEBUG_MAC80211(p, f, a...) IWL_DEBUG(p, IWL_DL_MAC80211, f, ## a) -#define IWL_DEBUG_EXTERNAL(p, f, a...) IWL_DEBUG(p, IWL_DL_EXTERNAL, f, ## a) #define IWL_DEBUG_TEMP(p, f, a...) IWL_DEBUG(p, IWL_DL_TEMP, f, ## a) #define IWL_DEBUG_SCAN(p, f, a...) IWL_DEBUG(p, IWL_DL_SCAN, f, ## a) #define IWL_DEBUG_RX(p, f, a...) IWL_DEBUG(p, IWL_DL_RX, f, ## a) @@ -216,5 +216,6 @@ do { \ #define IWL_DEBUG_LAR(p, f, a...) IWL_DEBUG(p, IWL_DL_LAR, f, ## a) #define IWL_DEBUG_FW_INFO(p, f, a...) \ IWL_DEBUG(p, IWL_DL_INFO | IWL_DL_FW, f, ## a) - +#define IWL_DEBUG_PTP(p, f, a...) IWL_DEBUG(p, IWL_DL_PTP, f, ## a) +#define IWL_DEBUG_EHT(p, f, a...) IWL_DEBUG(p, IWL_DL_EHT, f, ## a) #endif diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h index 76166e1b10e5..99789c7cef3b 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-devtrace.h @@ -3,7 +3,7 @@ * * Copyright(c) 2009 - 2014 Intel Corporation. All rights reserved. * Copyright(C) 2016 Intel Deutschland GmbH - * Copyright(c) 2018, 2023 Intel Corporation + * Copyright(c) 2018, 2023, 2025 Intel Corporation *****************************************************************************/ #ifndef __IWLWIFI_DEVICE_TRACE @@ -54,11 +54,11 @@ static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, struct ieee80211_hdr *hdr = NULL; size_t hdr_offset; - if (cmd->cmd != trans->rx_mpdu_cmd) + if (cmd->cmd != trans->conf.rx_mpdu_cmd) return len; hdr_offset = sizeof(struct iwl_cmd_header) + - trans->rx_mpdu_cmd_hdr_size; + trans->conf.rx_mpdu_cmd_hdr_size; if (out_hdr_offset) *out_hdr_offset = hdr_offset; @@ -67,7 +67,8 @@ static inline size_t iwl_rx_trace_len(const struct iwl_trans *trans, if (!ieee80211_is_data(hdr->frame_control)) return len; /* maybe try to identify EAPOL frames? */ - return sizeof(__le32) + sizeof(*cmd) + trans->rx_mpdu_cmd_hdr_size + + return sizeof(__le32) + sizeof(*cmd) + + trans->conf.rx_mpdu_cmd_hdr_size + ieee80211_hdrlen(hdr->frame_control); } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c index d3a65f33097c..9504a0cb8b13 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -75,6 +75,9 @@ struct iwl_drv { enum { DVM_OP_MODE, MVM_OP_MODE, +#if IS_ENABLED(CONFIG_IWLMLD) + MLD_OP_MODE, +#endif }; /* Protects the table contents, i.e. the ops pointer & drv list */ @@ -86,6 +89,9 @@ static struct iwlwifi_opmode_table { } iwlwifi_opmode_table[] = { /* ops set when driver is initialized */ [DVM_OP_MODE] = { .name = "iwldvm", .ops = NULL }, [MVM_OP_MODE] = { .name = "iwlmvm", .ops = NULL }, +#if IS_ENABLED(CONFIG_IWLMLD) + [MLD_OP_MODE] = { .name = "iwlmld", .ops = NULL }, +#endif }; #define IWL_DEFAULT_SCAN_CHANNELS 40 @@ -168,22 +174,88 @@ static inline char iwl_drv_get_step(int step) return 'a' + step; } +static bool iwl_drv_is_wifi7_supported(struct iwl_trans *trans) +{ + return CSR_HW_RFID_TYPE(trans->info.hw_rf_id) >= IWL_CFG_RF_TYPE_FM; +} + const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf) { char mac_step, rf_step; - const char *rf, *cdb; + const char *mac, *rf, *cdb; if (trans->cfg->fw_name_pre) return trans->cfg->fw_name_pre; - if (WARN_ON(!trans->cfg->fw_name_mac)) - return "unconfigured"; + mac_step = iwl_drv_get_step(trans->info.hw_rev_step); - mac_step = iwl_drv_get_step(trans->hw_rev_step); + switch (CSR_HW_REV_TYPE(trans->info.hw_rev)) { + case IWL_CFG_MAC_TYPE_PU: + mac = "9000-pu"; + mac_step = 'b'; + break; + case IWL_CFG_MAC_TYPE_TH: + mac = "9260-th"; + mac_step = 'b'; + break; + case IWL_CFG_MAC_TYPE_QU: + mac = "Qu"; + break; + case IWL_CFG_MAC_TYPE_CC: + /* special case - no RF since it's fixed (discrete) */ + scnprintf(buf, FW_NAME_PRE_BUFSIZE, "iwlwifi-cc-a0"); + return buf; + case IWL_CFG_MAC_TYPE_QUZ: + mac = "QuZ"; + /* all QuZ use A0 firmware */ + mac_step = 'a'; + break; + case IWL_CFG_MAC_TYPE_SO: + case IWL_CFG_MAC_TYPE_SOF: + mac = "so"; + mac_step = 'a'; + break; + case IWL_CFG_MAC_TYPE_TY: + mac = "ty"; + mac_step = 'a'; + break; + case IWL_CFG_MAC_TYPE_MA: + mac = "ma"; + break; + case IWL_CFG_MAC_TYPE_BZ: + case IWL_CFG_MAC_TYPE_BZ_W: + mac = "bz"; + break; + case IWL_CFG_MAC_TYPE_GL: + mac = "gl"; + break; + case IWL_CFG_MAC_TYPE_SC: + mac = "sc"; + break; + case IWL_CFG_MAC_TYPE_SC2: + mac = "sc2"; + break; + case IWL_CFG_MAC_TYPE_SC2F: + mac = "sc2f"; + break; + case IWL_CFG_MAC_TYPE_BR: + mac = "br"; + break; + case IWL_CFG_MAC_TYPE_DR: + mac = "dr"; + break; + default: + return "unknown-mac"; + } - rf_step = iwl_drv_get_step(CSR_HW_RFID_STEP(trans->hw_rf_id)); + rf_step = iwl_drv_get_step(CSR_HW_RFID_STEP(trans->info.hw_rf_id)); - switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { + switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) { + case IWL_CFG_RF_TYPE_JF1: + case IWL_CFG_RF_TYPE_JF2: + rf = "jf"; + rf_step = 'b'; + break; case IWL_CFG_RF_TYPE_HR1: case IWL_CFG_RF_TYPE_HR2: rf = "hr"; @@ -191,29 +263,26 @@ const char *iwl_drv_get_fwname_pre(struct iwl_trans *trans, char *buf) break; case IWL_CFG_RF_TYPE_GF: rf = "gf"; + rf_step = 'a'; break; case IWL_CFG_RF_TYPE_FM: rf = "fm"; break; case IWL_CFG_RF_TYPE_WH: - if (SILICON_Z_STEP == - CSR_HW_RFID_STEP(trans->hw_rf_id)) { - rf = "whtc"; - rf_step = 'a'; - } else { - rf = "wh"; - } + rf = "wh"; + break; + case IWL_CFG_RF_TYPE_PE: + rf = "pe"; break; default: return "unknown-rf"; } - cdb = CSR_HW_RFID_IS_CDB(trans->hw_rf_id) ? "4" : ""; + cdb = CSR_HW_RFID_IS_CDB(trans->info.hw_rf_id) ? "4" : ""; scnprintf(buf, FW_NAME_PRE_BUFSIZE, "iwlwifi-%s-%c0-%s%s-%c0", - trans->cfg->fw_name_mac, mac_step, - rf, cdb, rf_step); + mac, mac_step, rf, cdb, rf_step); return buf; } @@ -222,39 +291,64 @@ IWL_EXPORT_SYMBOL(iwl_drv_get_fwname_pre); static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context); +static void iwl_get_ucode_api_versions(struct iwl_trans *trans, + unsigned int *api_min, + unsigned int *api_max) +{ + const struct iwl_family_base_params *base = trans->mac_cfg->base; + const struct iwl_rf_cfg *cfg = trans->cfg; + + if (!base->ucode_api_max) { + *api_min = cfg->ucode_api_min; + *api_max = cfg->ucode_api_max; + return; + } + + if (!cfg->ucode_api_max) { + *api_min = base->ucode_api_min; + *api_max = base->ucode_api_max; + return; + } + + *api_min = max(cfg->ucode_api_min, base->ucode_api_min); + *api_max = min(cfg->ucode_api_max, base->ucode_api_max); +} + static int iwl_request_firmware(struct iwl_drv *drv, bool first) { - const struct iwl_cfg *cfg = drv->trans->cfg; char _fw_name_pre[FW_NAME_PRE_BUFSIZE]; + unsigned int ucode_api_max, ucode_api_min; const char *fw_name_pre; - if (drv->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000 && - (drv->trans->hw_rev_step != SILICON_B_STEP && - drv->trans->hw_rev_step != SILICON_C_STEP)) { + iwl_get_ucode_api_versions(drv->trans, &ucode_api_min, &ucode_api_max); + + if (drv->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000 && + (drv->trans->info.hw_rev_step != SILICON_B_STEP && + drv->trans->info.hw_rev_step != SILICON_C_STEP)) { IWL_ERR(drv, "Only HW steps B and C are currently supported (0x%0x)\n", - drv->trans->hw_rev); + drv->trans->info.hw_rev); return -EINVAL; } fw_name_pre = iwl_drv_get_fwname_pre(drv->trans, _fw_name_pre); if (first) - drv->fw_index = cfg->ucode_api_max; + drv->fw_index = ucode_api_max; else drv->fw_index--; - if (drv->fw_index < cfg->ucode_api_min) { + if (drv->fw_index < ucode_api_min) { IWL_ERR(drv, "no suitable firmware found!\n"); - if (cfg->ucode_api_min == cfg->ucode_api_max) { + if (ucode_api_min == ucode_api_max) { IWL_ERR(drv, "%s-%d is required\n", fw_name_pre, - cfg->ucode_api_max); + ucode_api_max); } else { IWL_ERR(drv, "minimum version required: %s-%d\n", - fw_name_pre, cfg->ucode_api_min); + fw_name_pre, ucode_api_min); IWL_ERR(drv, "maximum version supported: %s-%d\n", - fw_name_pre, cfg->ucode_api_max); + fw_name_pre, ucode_api_max); } IWL_ERR(drv, @@ -316,6 +410,7 @@ struct iwl_firmware_pieces { size_t dbg_trigger_tlv_len[FW_DBG_TRIGGER_MAX]; struct iwl_fw_dbg_mem_seg_tlv *dbg_mem_tlv; size_t n_mem_tlv; + u32 major; }; static void alloc_sec_data(struct iwl_firmware_pieces *pieces, @@ -957,19 +1052,19 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, break; case IWL_UCODE_TLV_FW_VERSION: { const __le32 *ptr = (const void *)tlv_data; - u32 major, minor; + u32 minor; u8 local_comp; if (tlv_len != sizeof(u32) * 3) goto invalid_tlv_len; - major = le32_to_cpup(ptr++); + pieces->major = le32_to_cpup(ptr++); minor = le32_to_cpup(ptr++); local_comp = le32_to_cpup(ptr); snprintf(drv->fw.fw_version, sizeof(drv->fw.fw_version), - "%u.%08x.%u %s", major, minor, + "%u.%08x.%u %s", pieces->major, minor, local_comp, iwl_reduced_fw_name(drv)); break; } @@ -1181,7 +1276,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(*fseq_ver)) goto invalid_tlv_len; - IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %s\n", + IWL_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n", fseq_ver->version); } break; @@ -1223,7 +1318,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(*dbg_ptrs)) goto invalid_tlv_len; - if (drv->trans->trans_cfg->device_family < + if (drv->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_22000) break; drv->trans->dbg.umac_error_event_table = @@ -1239,7 +1334,7 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv, if (tlv_len != sizeof(*dbg_ptrs)) goto invalid_tlv_len; - if (drv->trans->trans_cfg->device_family < + if (drv->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_22000) break; drv->trans->dbg.lmac_error_event_table[0] = @@ -1361,7 +1456,7 @@ static int iwl_alloc_ucode(struct iwl_drv *drv, static int validate_sec_sizes(struct iwl_drv *drv, struct iwl_firmware_pieces *pieces, - const struct iwl_cfg *cfg) + const struct iwl_rf_cfg *cfg) { IWL_DEBUG_INFO(drv, "f/w package hdr runtime inst size = %zd\n", get_sec_size(pieces, IWL_UCODE_REGULAR, @@ -1468,6 +1563,8 @@ static void _iwl_op_mode_stop(struct iwl_drv *drv) } } +#define IWL_MLD_SUPPORTED_FW_VERSION 97 + /* * iwl_req_fw_callback - callback when firmware was loaded * @@ -1482,14 +1579,15 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) struct iwlwifi_opmode_table *op; int err; struct iwl_firmware_pieces *pieces; - const unsigned int api_max = drv->trans->cfg->ucode_api_max; - const unsigned int api_min = drv->trans->cfg->ucode_api_min; + unsigned int api_min, api_max; size_t trigger_tlv_sz[FW_DBG_TRIGGER_MAX]; u32 api_ver; int i; bool usniffer_images = false; bool failure = true; + iwl_get_ucode_api_versions(drv->trans, &api_min, &api_max); + fw->ucode_capa.max_probe_length = IWL_DEFAULT_MAX_PROBE_LENGTH; fw->ucode_capa.standard_phy_calibration_size = IWL_DEFAULT_STANDARD_PHY_CALIBRATE_TBL_SIZE; @@ -1683,14 +1781,14 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) fw->init_evtlog_size = (pieces->init_evtlog_size - 16)/12; else fw->init_evtlog_size = - drv->trans->trans_cfg->base_params->max_event_log_size; + drv->trans->mac_cfg->base->max_event_log_size; fw->init_errlog_ptr = pieces->init_errlog_ptr; fw->inst_evtlog_ptr = pieces->inst_evtlog_ptr; if (pieces->inst_evtlog_size) fw->inst_evtlog_size = (pieces->inst_evtlog_size - 16)/12; else fw->inst_evtlog_size = - drv->trans->trans_cfg->base_params->max_event_log_size; + drv->trans->mac_cfg->base->max_event_log_size; fw->inst_errlog_ptr = pieces->inst_errlog_ptr; /* @@ -1720,6 +1818,20 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context) break; } +#if IS_ENABLED(CONFIG_IWLMLD) + if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION && + iwl_drv_is_wifi7_supported(drv->trans)) + op = &iwlwifi_opmode_table[MLD_OP_MODE]; +#else + if (pieces->major >= IWL_MLD_SUPPORTED_FW_VERSION && + iwl_drv_is_wifi7_supported(drv->trans)) { + IWL_ERR(drv, + "IWLMLD needs to be compiled to support this firmware\n"); + mutex_unlock(&iwlwifi_opmode_table_mtx); + goto out_unbind; + } +#endif + IWL_INFO(drv, "loaded firmware version %s op_mode %s\n", drv->fw.fw_version, op->name); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h index 854957bdf79d..595300a14639 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-drv.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-drv.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2020-2021, 2023 Intel Corporation + * Copyright (C) 2005-2014, 2020-2021, 2023, 2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH */ #ifndef __iwl_drv_h__ @@ -53,7 +53,7 @@ struct iwl_drv; struct iwl_trans; -struct iwl_cfg; +struct iwl_rf_cfg; /** * iwl_drv_start - start the drv * diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index 5c8f1868db64..0f6de08b7473 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2021, 2023-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2021, 2023-2025 Intel Corporation * Copyright (C) 2015-2017 Intel Deutschland GmbH */ #ifndef __iwl_fh_h__ @@ -71,7 +71,7 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans, unsigned int chnl) { - if (trans->trans_cfg->gen2) { + if (trans->mac_cfg->gen2) { WARN_ON_ONCE(chnl >= 64); return TFH_TFDQ_CBB_TABLE + 8 * chnl; } @@ -378,14 +378,14 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans, * Once the RXF-to-DRAM DMA is active, this flag is immediately turned off. */ #define RFH_GEN_STATUS 0xA09808 -#define RFH_GEN_STATUS_GEN3 0xA07824 +#define RFH_GEN_STATUS_AX210 0xA07824 #define RBD_FETCH_IDLE BIT(29) #define SRAM_DMA_IDLE BIT(30) #define RXF_DMA_IDLE BIT(31) /* DMA configuration */ #define RFH_RXF_DMA_CFG 0xA09820 -#define RFH_RXF_DMA_CFG_GEN3 0xA07880 +#define RFH_RXF_DMA_CFG_AX210 0xA07880 /* RB size */ #define RFH_RXF_DMA_RB_SIZE_MASK (0x000F0000) /* bits 16-19 */ #define RFH_RXF_DMA_RB_SIZE_POS 16 @@ -588,13 +588,12 @@ struct iwl_rb_status { #define TFD_QUEUE_SIZE_MAX (256) -#define TFD_QUEUE_SIZE_MAX_GEN3 (65536) /* cb size is the exponent - 3 */ #define TFD_QUEUE_CB_SIZE(x) (ilog2(x) - 3) #define TFD_QUEUE_SIZE_BC_DUP (64) #define TFD_QUEUE_BC_SIZE (TFD_QUEUE_SIZE_MAX + TFD_QUEUE_SIZE_BC_DUP) -#define TFD_QUEUE_BC_SIZE_GEN3_AX210 1024 -#define TFD_QUEUE_BC_SIZE_GEN3_BZ (1024 * 4) +#define TFD_QUEUE_BC_SIZE_AX210 1024 +#define TFD_QUEUE_BC_SIZE_BZ (1024 * 4) #define IWL_TX_DMA_MASK DMA_BIT_MASK(36) #define IWL_NUM_OF_TBS 20 #define IWL_TFH_NUM_TBS 25 @@ -717,30 +716,19 @@ struct iwl_tfh_tfd { /* Fixed (non-configurable) rx data from phy */ /** - * struct iwlagn_scd_bc_tbl - scheduler byte count table + * struct iwl_bc_tbl_entry - scheduler byte count table entry * base physical address provided by SCD_DRAM_BASE_ADDR * For devices up to 22000: * @tfd_offset: * For devices up to 22000: * 0-12 - tx command byte count * 12-16 - station index - * For 22000: + * For 22000 and on: * 0-12 - tx command byte count * 12-13 - number of 64 byte chunks * 14-16 - reserved */ -struct iwlagn_scd_bc_tbl { - __le16 tfd_offset[TFD_QUEUE_BC_SIZE]; -} __packed; - -/** - * struct iwl_gen3_bc_tbl_entry - scheduler byte count table entry gen3 - * For AX210 and on: - * @tfd_offset: 0-12 - tx command byte count - * 12-13 - number of 64 byte chunks - * 14-16 - reserved - */ -struct iwl_gen3_bc_tbl_entry { +struct iwl_bc_tbl_entry { __le16 tfd_offset; } __packed; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.c b/drivers/net/wireless/intel/iwlwifi/iwl-io.c index 0653ca8b974a..80591809164e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2003-2014, 2018-2022, 2024 Intel Corporation + * Copyright (C) 2003-2014, 2018-2022, 2024-2025 Intel Corporation * Copyright (C) 2015-2016 Intel Deutschland GmbH */ #include <linux/delay.h> @@ -211,13 +211,13 @@ IWL_EXPORT_SYMBOL(iwl_clear_bits_prph); void iwl_force_nmi(struct iwl_trans *trans) { - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000) iwl_write_prph_delay(trans, DEVICE_SET_NMI_REG, DEVICE_SET_NMI_VAL_DRV, 1); - else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + else if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER, UREG_NIC_SET_NMI_DRIVER_NMI_FROM_DRIVER); - else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + else if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ) iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, UREG_DOORBELL_TO_ISR6_NMI_BIT); else @@ -260,7 +260,7 @@ struct reg { static int iwl_dump_rfh(struct iwl_trans *trans, char **buf) { int i, q; - int num_q = trans->num_rx_queues; + int num_q = trans->info.num_rxqs; static const u32 rfh_tbl[] = { RFH_RXF_DMA_CFG, RFH_GEN_CFG, @@ -368,7 +368,7 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf) FH_TSSR_TX_ERROR_REG }; - if (trans->trans_cfg->mq_rx_supported) + if (trans->mac_cfg->mq_rx_supported) return iwl_dump_rfh(trans, buf); #ifdef CONFIG_IWLWIFI_DEBUGFS @@ -423,7 +423,7 @@ static void iwl_dump_host_monitor_block(struct iwl_trans *trans, static void iwl_dump_host_monitor(struct iwl_trans *trans) { - switch (trans->trans_cfg->device_family) { + switch (trans->mac_cfg->device_family) { case IWL_DEVICE_FAMILY_22000: case IWL_DEVICE_FAMILY_AX210: IWL_ERR(trans, "CSR_RESET = 0x%x\n", @@ -445,11 +445,11 @@ static void iwl_dump_host_monitor(struct iwl_trans *trans) int iwl_finish_nic_init(struct iwl_trans *trans) { - const struct iwl_cfg_trans_params *cfg_trans = trans->trans_cfg; + const struct iwl_mac_cfg *mac_cfg = trans->mac_cfg; u32 poll_ready; int err; - if (cfg_trans->bisr_workaround) { + if (mac_cfg->bisr_workaround) { /* ensure the TOP FSM isn't still in previous reset */ mdelay(2); } @@ -458,7 +458,7 @@ int iwl_finish_nic_init(struct iwl_trans *trans) * Set "initialization complete" bit to move adapter from * D0U* --> D0A* (powered-up active) state. */ - if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ | CSR_GP_CNTRL_REG_FLAG_MAC_INIT); @@ -469,7 +469,7 @@ int iwl_finish_nic_init(struct iwl_trans *trans) poll_ready = CSR_GP_CNTRL_REG_FLAG_MAC_CLOCK_READY; } - if (cfg_trans->device_family == IWL_DEVICE_FAMILY_8000) + if (mac_cfg->device_family == IWL_DEVICE_FAMILY_8000) udelay(2); /* @@ -484,7 +484,7 @@ int iwl_finish_nic_init(struct iwl_trans *trans) iwl_dump_host_monitor(trans); } - if (cfg_trans->bisr_workaround) { + if (mac_cfg->bisr_workaround) { /* ensure BISR shift has finished */ udelay(200); } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-io.h b/drivers/net/wireless/intel/iwlwifi/iwl-io.h index 37b3bd62897e..f4833c5fe86e 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-io.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-io.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2018-2021 Intel Corporation + * Copyright (C) 2018-2021, 2025 Intel Corporation */ #ifndef __iwl_io_h__ #define __iwl_io_h__ @@ -64,38 +64,38 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf); */ static inline u32 iwl_umac_prph(struct iwl_trans *trans, u32 ofs) { - return ofs + trans->trans_cfg->umac_prph_offset; + return ofs + trans->mac_cfg->umac_prph_offset; } static inline u32 iwl_read_umac_prph_no_grab(struct iwl_trans *trans, u32 ofs) { return iwl_read_prph_no_grab(trans, ofs + - trans->trans_cfg->umac_prph_offset); + trans->mac_cfg->umac_prph_offset); } static inline u32 iwl_read_umac_prph(struct iwl_trans *trans, u32 ofs) { - return iwl_read_prph(trans, ofs + trans->trans_cfg->umac_prph_offset); + return iwl_read_prph(trans, ofs + trans->mac_cfg->umac_prph_offset); } static inline void iwl_write_umac_prph_no_grab(struct iwl_trans *trans, u32 ofs, u32 val) { - iwl_write_prph_no_grab(trans, ofs + trans->trans_cfg->umac_prph_offset, + iwl_write_prph_no_grab(trans, ofs + trans->mac_cfg->umac_prph_offset, val); } static inline void iwl_write_umac_prph(struct iwl_trans *trans, u32 ofs, u32 val) { - iwl_write_prph(trans, ofs + trans->trans_cfg->umac_prph_offset, val); + iwl_write_prph(trans, ofs + trans->mac_cfg->umac_prph_offset, val); } static inline int iwl_poll_umac_prph_bit(struct iwl_trans *trans, u32 addr, u32 bits, u32 mask, int timeout) { return iwl_poll_prph_bit(trans, addr + - trans->trans_cfg->umac_prph_offset, + trans->mac_cfg->umac_prph_offset, bits, mask, timeout); } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c index 9f7e013252fe..0592f0f59d1c 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -143,6 +143,9 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = { * @NVM_CHANNEL_VALID: channel is usable for this SKU/geo * @NVM_CHANNEL_IBSS: usable as an IBSS channel and deprecated * when %IWL_NVM_SBANDS_FLAGS_LAR enabled. + * @NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY: active scanning allowed and + * AP allowed only in 20 MHz. Valid only + * when %IWL_NVM_SBANDS_FLAGS_LAR enabled. * @NVM_CHANNEL_ACTIVE: active scanning allowed and allows IBSS * when %IWL_NVM_SBANDS_FLAGS_LAR enabled. * @NVM_CHANNEL_RADAR: radar detection required @@ -159,20 +162,21 @@ static struct ieee80211_rate iwl_cfg80211_rates[] = { * @NVM_CHANNEL_AFC: client support connection to UHB AFC AP */ enum iwl_nvm_channel_flags { - NVM_CHANNEL_VALID = BIT(0), - NVM_CHANNEL_IBSS = BIT(1), - NVM_CHANNEL_ACTIVE = BIT(3), - NVM_CHANNEL_RADAR = BIT(4), - NVM_CHANNEL_INDOOR_ONLY = BIT(5), - NVM_CHANNEL_GO_CONCURRENT = BIT(6), - NVM_CHANNEL_UNIFORM = BIT(7), - NVM_CHANNEL_20MHZ = BIT(8), - NVM_CHANNEL_40MHZ = BIT(9), - NVM_CHANNEL_80MHZ = BIT(10), - NVM_CHANNEL_160MHZ = BIT(11), - NVM_CHANNEL_DC_HIGH = BIT(12), - NVM_CHANNEL_VLP = BIT(13), - NVM_CHANNEL_AFC = BIT(14), + NVM_CHANNEL_VALID = BIT(0), + NVM_CHANNEL_IBSS = BIT(1), + NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY = BIT(2), + NVM_CHANNEL_ACTIVE = BIT(3), + NVM_CHANNEL_RADAR = BIT(4), + NVM_CHANNEL_INDOOR_ONLY = BIT(5), + NVM_CHANNEL_GO_CONCURRENT = BIT(6), + NVM_CHANNEL_UNIFORM = BIT(7), + NVM_CHANNEL_20MHZ = BIT(8), + NVM_CHANNEL_40MHZ = BIT(9), + NVM_CHANNEL_80MHZ = BIT(10), + NVM_CHANNEL_160MHZ = BIT(11), + NVM_CHANNEL_DC_HIGH = BIT(12), + NVM_CHANNEL_VLP = BIT(13), + NVM_CHANNEL_AFC = BIT(14), }; /** @@ -332,7 +336,7 @@ static inline void iwl_nvm_print_channel_flags(struct device *dev, u32 level, } static u32 iwl_get_channel_flags(u8 ch_num, int ch_idx, enum nl80211_band band, - u32 nvm_flags, const struct iwl_cfg *cfg) + u32 nvm_flags, const struct iwl_rf_cfg *cfg) { u32 flags = IEEE80211_CHAN_NO_HT40; @@ -399,7 +403,7 @@ static int iwl_init_channel_map(struct iwl_trans *trans, const void * const nvm_ch_flags, u32 sbands_flags, bool v4) { - const struct iwl_cfg *cfg = trans->cfg; + const struct iwl_rf_cfg *cfg = trans->cfg; struct device *dev = trans->dev; int ch_idx; int n_channels = 0; @@ -500,7 +504,7 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans, struct ieee80211_sta_vht_cap *vht_cap, u8 tx_chains, u8 rx_chains) { - const struct iwl_cfg *cfg = trans->cfg; + const struct iwl_rf_cfg *cfg = trans->cfg; int num_rx_ants = num_of_ant(rx_chains); int num_tx_ants = num_of_ant(tx_chains); @@ -513,7 +517,7 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans, IEEE80211_VHT_MAX_AMPDU_1024K << IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_SHIFT; - if (!trans->cfg->ht_params->stbc) + if (!trans->cfg->ht_params.stbc) vht_cap->cap &= ~IEEE80211_VHT_CAP_RXSTBC_MASK; if (data->vht160_supported) @@ -523,7 +527,7 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans, if (cfg->vht_mu_mimo_supported) vht_cap->cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; - if (cfg->ht_params->ldpc) + if (cfg->ht_params.ldpc) vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; if (data->sku_cap_mimo_disabled) { @@ -531,21 +535,21 @@ static void iwl_init_vht_hw_capab(struct iwl_trans *trans, num_tx_ants = 1; } - if (trans->cfg->ht_params->stbc && num_tx_ants > 1) + if (trans->cfg->ht_params.stbc && num_tx_ants > 1) vht_cap->cap |= IEEE80211_VHT_CAP_TXSTBC; else vht_cap->cap |= IEEE80211_VHT_CAP_TX_ANTENNA_PATTERN; switch (iwlwifi_mod_params.amsdu_size) { case IWL_AMSDU_DEF: - if (trans->trans_cfg->mq_rx_supported) + if (trans->mac_cfg->mq_rx_supported) vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; else vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_3895; break; case IWL_AMSDU_2K: - if (trans->trans_cfg->mq_rx_supported) + if (trans->mac_cfg->mq_rx_supported) vht_cap->cap |= IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; else @@ -684,10 +688,13 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = { .has_eht = true, .eht_cap_elem = { .mac_cap_info[0] = + IEEE80211_EHT_MAC_CAP0_EPCS_PRIO_ACCESS | IEEE80211_EHT_MAC_CAP0_OM_CONTROL | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE2 | IEEE80211_EHT_MAC_CAP0_SCS_TRAFFIC_DESC, + .mac_cap_info[1] = + IEEE80211_EHT_MAC_CAP1_UNSOL_EPCS_PRIO_ACCESS, .phy_cap_info[0] = IEEE80211_EHT_PHY_CAP0_242_TONE_RU_GT20MHZ | IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | @@ -913,11 +920,8 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, { bool is_ap = iftype_data->types_mask & (BIT(NL80211_IFTYPE_AP) | BIT(NL80211_IFTYPE_P2P_GO)); - bool no_320; - - no_320 = (!trans->trans_cfg->integrated && - trans->pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB) || - trans->reduced_cap_sku; + bool slow_pcie = (!trans->mac_cfg->integrated && + trans->info.pcie_link_speed < PCI_EXP_LNKSTA_CLS_8_0GB); if (!data->sku_cap_11be_enable || iwlwifi_mod_params.disable_11be) iftype_data->eht_cap.has_eht = false; @@ -944,7 +948,8 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); break; case NL80211_BAND_6GHZ: - if (!no_320) { + if (!trans->reduced_cap_sku && + (!trans->cfg->bw_limit || trans->cfg->bw_limit >= 320)) { iftype_data->eht_cap.eht_cap_elem.phy_cap_info[0] |= IEEE80211_EHT_PHY_CAP0_320MHZ_IN_6GHZ; iftype_data->eht_cap.eht_cap_elem.phy_cap_info[1] |= @@ -986,6 +991,14 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->eht_cap.eht_cap_elem.phy_cap_info[4] |= 0x10; } } + + if (slow_pcie) { + struct ieee80211_eht_mcs_nss_supp *mcs_nss = + &iftype_data->eht_cap.eht_mcs_nss_supp; + + mcs_nss->bw._320.rx_tx_mcs11_max_nss = 0; + mcs_nss->bw._320.rx_tx_mcs13_max_nss = 0; + } } else { struct ieee80211_he_mcs_nss_supp *he_mcs_nss_supp = &iftype_data->he_cap.he_mcs_nss_supp; @@ -1023,11 +1036,11 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, cpu_to_le16(IEEE80211_HE_MCS_NOT_SUPPORTED << 2); } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210 && !is_ap) iftype_data->he_cap.he_cap_elem.phy_cap_info[2] |= IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO; - switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { + switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) { case IWL_CFG_RF_TYPE_GF: case IWL_CFG_RF_TYPE_FM: case IWL_CFG_RF_TYPE_WH: @@ -1039,7 +1052,7 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, break; } - if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && + if (CSR_HW_REV_TYPE(trans->info.hw_rev) == IWL_CFG_MAC_TYPE_GL && iftype_data->eht_cap.has_eht) { iftype_data->eht_cap.eht_cap_elem.mac_cap_info[0] &= ~(IEEE80211_EHT_MAC_CAP0_TRIG_TXOP_SHARING_MODE1 | @@ -1068,13 +1081,13 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->he_cap.he_cap_elem.mac_cap_info[2] |= IEEE80211_HE_MAC_CAP2_BCAST_TWT; - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 && + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000 && !is_ap) { iftype_data->vendor_elems.data = iwl_vendor_caps; iftype_data->vendor_elems.len = ARRAY_SIZE(iwl_vendor_caps); } - if (!trans->cfg->ht_params->stbc) { + if (!trans->cfg->ht_params.stbc) { iftype_data->he_cap.he_cap_elem.phy_cap_info[2] &= ~IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ; iftype_data->he_cap.he_cap_elem.phy_cap_info[7] &= @@ -1086,19 +1099,23 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans, iftype_data->eht_cap.eht_mcs_nss_supp.bw._320.rx_tx_mcs13_max_nss = 0; } - if (trans->no_160) + if (trans->cfg->bw_limit && trans->cfg->bw_limit < 160) iftype_data->he_cap.he_cap_elem.phy_cap_info[0] &= ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; - if (trans->reduced_cap_sku) { + if ((trans->cfg->bw_limit && trans->cfg->bw_limit < 320) || + trans->reduced_cap_sku) { memset(&iftype_data->eht_cap.eht_mcs_nss_supp.bw._320, 0, sizeof(iftype_data->eht_cap.eht_mcs_nss_supp.bw._320)); + iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &= + ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; + } + + if (trans->reduced_cap_sku) { iftype_data->eht_cap.eht_mcs_nss_supp.bw._80.rx_tx_mcs13_max_nss = 0; iftype_data->eht_cap.eht_mcs_nss_supp.bw._160.rx_tx_mcs13_max_nss = 0; iftype_data->eht_cap.eht_cap_elem.phy_cap_info[8] &= ~IEEE80211_EHT_PHY_CAP8_RX_4096QAM_WIDER_BW_DL_OFDMA; - iftype_data->eht_cap.eht_cap_elem.phy_cap_info[2] &= - ~IEEE80211_EHT_PHY_CAP2_SOUNDING_DIM_320MHZ_MASK; } } @@ -1234,7 +1251,7 @@ static void iwl_init_sbands(struct iwl_trans *trans, n_used, n_channels); } -static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, +static int iwl_get_sku(const struct iwl_rf_cfg *cfg, const __le16 *nvm_sw, const __le16 *phy_sku) { if (cfg->nvm_type != IWL_NVM_EXT) @@ -1243,7 +1260,7 @@ static int iwl_get_sku(const struct iwl_cfg *cfg, const __le16 *nvm_sw, return le32_to_cpup((const __le32 *)(phy_sku + SKU_FAMILY_8000)); } -static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw) +static int iwl_get_nvm_version(const struct iwl_rf_cfg *cfg, const __le16 *nvm_sw) { if (cfg->nvm_type != IWL_NVM_EXT) return le16_to_cpup(nvm_sw + NVM_VERSION); @@ -1252,7 +1269,7 @@ static int iwl_get_nvm_version(const struct iwl_cfg *cfg, const __le16 *nvm_sw) NVM_VERSION_EXT_NVM)); } -static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw, +static int iwl_get_radio_cfg(const struct iwl_rf_cfg *cfg, const __le16 *nvm_sw, const __le16 *phy_sku) { if (cfg->nvm_type != IWL_NVM_EXT) @@ -1262,7 +1279,7 @@ static int iwl_get_radio_cfg(const struct iwl_cfg *cfg, const __le16 *nvm_sw, } -static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw) +static int iwl_get_n_hw_addrs(const struct iwl_rf_cfg *cfg, const __le16 *nvm_sw) { int n_hw_addr; @@ -1274,7 +1291,7 @@ static int iwl_get_n_hw_addrs(const struct iwl_cfg *cfg, const __le16 *nvm_sw) return n_hw_addr & N_HW_ADDR_MASK; } -static void iwl_set_radio_cfg(const struct iwl_cfg *cfg, +static void iwl_set_radio_cfg(const struct iwl_rf_cfg *cfg, struct iwl_nvm_data *data, u32 radio_cfg) { @@ -1333,7 +1350,7 @@ static void iwl_set_hw_address_from_csr(struct iwl_trans *trans, } static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, - const struct iwl_cfg *cfg, + const struct iwl_rf_cfg *cfg, struct iwl_nvm_data *data, const __le16 *mac_override, const __be16 *nvm_hw) @@ -1382,11 +1399,12 @@ static void iwl_set_hw_address_family_8000(struct iwl_trans *trans, } static int iwl_set_hw_address(struct iwl_trans *trans, - const struct iwl_cfg *cfg, + const struct iwl_rf_cfg *cfg, struct iwl_nvm_data *data, const __be16 *nvm_hw, const __le16 *mac_override) { - if (cfg->mac_addr_from_csr) { + const struct iwl_mac_cfg *mac_cfg = trans->mac_cfg; + if (mac_cfg->base->mac_addr_from_csr) { iwl_set_hw_address_from_csr(trans, data); } else if (cfg->nvm_type != IWL_NVM_EXT) { const u8 *hw_addr = (const u8 *)(nvm_hw + HW_ADDR); @@ -1416,7 +1434,7 @@ static int iwl_set_hw_address(struct iwl_trans *trans, } static bool -iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const __be16 *nvm_hw) { /* @@ -1428,7 +1446,7 @@ iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg, * in 5GHz otherwise the FW will throw a sysassert when we try * to use them. */ - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) { + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) { /* * Unlike the other sections in the NVM, the hw * section uses big-endian. @@ -1448,7 +1466,7 @@ iwl_nvm_no_wide_in_5ghz(struct iwl_trans *trans, const struct iwl_cfg *cfg, } struct iwl_nvm_data * -iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const struct iwl_mei_nvm *mei_nvm, const struct iwl_fw *fw, u8 tx_ant, u8 rx_ant) { @@ -1512,7 +1530,7 @@ iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, IWL_EXPORT_SYMBOL(iwl_parse_mei_nvm_data); struct iwl_nvm_data * -iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, const __be16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, @@ -1612,7 +1630,7 @@ IWL_EXPORT_SYMBOL(iwl_parse_nvm_data); static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan, int ch_idx, u16 nvm_flags, struct iwl_reg_capa reg_capa, - const struct iwl_cfg *cfg) + const struct iwl_rf_cfg *cfg) { u32 flags = NL80211_RRF_NO_HT40; @@ -1647,6 +1665,10 @@ static u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan, if (nvm_flags & NVM_CHANNEL_INDOOR_ONLY) flags |= NL80211_RRF_NO_OUTDOOR; + if (nvm_flags & NVM_CHANNEL_ALLOW_20MHZ_ACTIVITY && + flags & NL80211_RRF_NO_IR) + flags |= NL80211_RRF_ALLOW_20MHZ_ACTIVITY; + /* Set the GO concurrent flag only in case that NO_IR is set. * Otherwise it is meaningless */ @@ -1723,10 +1745,12 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver) } struct ieee80211_regdomain * -iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, +iwl_parse_nvm_mcc_info(struct iwl_trans *trans, int num_of_ch, __le32 *channels, u16 fw_mcc, u16 geo_info, u32 cap, u8 resp_ver) { + const struct iwl_rf_cfg *cfg = trans->cfg; + struct device *dev = trans->dev; int ch_idx; u16 ch_flags; u32 reg_rule_flags, prev_reg_rule_flags = 0; @@ -1981,8 +2005,8 @@ int iwl_read_external_nvm(struct iwl_trans *trans, le32_to_cpu(dword_buff[3])); /* nvm file validation, dword_buff[2] holds the file version */ - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_8000 && - trans->hw_rev_step == SILICON_C_STEP && + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_8000 && + trans->info.hw_rev_step == SILICON_C_STEP && le32_to_cpu(dword_buff[2]) < 0xE4A) { ret = -EFAULT; goto out; @@ -2049,7 +2073,7 @@ int iwl_read_external_nvm(struct iwl_trans *trans, break; } - iwl_nvm_fixups(trans->hw_id, section_id, temp, section_size); + iwl_nvm_fixups(trans->info.hw_id, section_id, temp, section_size); kfree(nvm_sections[section_id].data); nvm_sections[section_id].data = temp; @@ -2152,7 +2176,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, !!(mac_flags & NVM_MAC_SKU_FLAGS_BAND_5_2_ENABLED); nvm->sku_cap_mimo_disabled = !!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED); - if (CSR_HW_RFID_TYPE(trans->hw_rf_id) >= IWL_CFG_RF_TYPE_FM) + if (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) >= IWL_CFG_RF_TYPE_FM) nvm->sku_cap_11be_enable = true; /* Initialize PHY sku data */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h index 0c6c3fb8c6dd..9ce9fa4e78fd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-parse.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2015, 2018-2024 Intel Corporation + * Copyright (C) 2005-2015, 2018-2025 Intel Corporation * Copyright (C) 2016-2017 Intel Deutschland GmbH */ #ifndef __iwl_nvm_parse_h__ @@ -30,7 +30,7 @@ enum iwl_nvm_sbands_flags { * later with iwl_free_nvm_data(). */ struct iwl_nvm_data * -iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, const __be16 *nvm_hw, const __le16 *nvm_sw, const __le16 *nvm_calib, const __le16 *regulatory, @@ -46,9 +46,17 @@ iwl_parse_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, * accordingly. An ERR_PTR is returned on error. * If not given to the regulatory core, the user is responsible for freeing * the regdomain returned here with kfree. + * + * @trans: the transport + * @num_of_ch: the number of channels + * @channels: channel map + * @fw_mcc: firmware country code + * @geo_info: geo info value + * @cap: capability + * @resp_ver: FW response version */ struct ieee80211_regdomain * -iwl_parse_nvm_mcc_info(struct device *dev, const struct iwl_cfg *cfg, +iwl_parse_nvm_mcc_info(struct iwl_trans *trans, int num_of_ch, __le32 *channels, u16 fw_mcc, u16 geo_info, u32 cap, u8 resp_ver); @@ -87,7 +95,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans, * iwl_parse_mei_nvm_data - parse the mei_nvm_data and get an iwl_nvm_data */ struct iwl_nvm_data * -iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_parse_mei_nvm_data(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const struct iwl_mei_nvm *mei_nvm, const struct iwl_fw *fw, u8 set_tx_ant, u8 set_rx_ant); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c index b3c25acd3691..ec312c90ff85 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-nvm-utils.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2021, 2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2021, 2023, 2025 Intel Corporation * Copyright (C) 2015 Intel Mobile Communications GmbH */ #include <linux/types.h> @@ -42,7 +42,7 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans, enum nl80211_band band, u8 tx_chains, u8 rx_chains) { - const struct iwl_cfg *cfg = trans->cfg; + const struct iwl_rf_cfg *cfg = trans->cfg; int max_bit_rate = 0; tx_chains = hweight8(tx_chains); @@ -53,7 +53,8 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans, if (!(data->sku_cap_11n_enable) || (iwlwifi_mod_params.disable_11n & IWL_DISABLE_HT_ALL) || - !cfg->ht_params) { + /* there are no devices with HT but without HT40 entirely */ + !cfg->ht_params.ht40_bands) { ht_info->ht_supported = false; return; } @@ -64,17 +65,17 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans, ht_info->ht_supported = true; ht_info->cap = IEEE80211_HT_CAP_DSSSCCK40; - if (cfg->ht_params->stbc) { + if (cfg->ht_params.stbc) { ht_info->cap |= (1 << IEEE80211_HT_CAP_RX_STBC_SHIFT); if (tx_chains > 1) ht_info->cap |= IEEE80211_HT_CAP_TX_STBC; } - if (cfg->ht_params->ldpc) + if (cfg->ht_params.ldpc) ht_info->cap |= IEEE80211_HT_CAP_LDPC_CODING; - if (trans->trans_cfg->mq_rx_supported || + if (trans->mac_cfg->mq_rx_supported || iwlwifi_mod_params.amsdu_size >= IWL_AMSDU_8K) ht_info->cap |= IEEE80211_HT_CAP_MAX_AMSDU; @@ -90,13 +91,13 @@ void iwl_init_ht_hw_capab(struct iwl_trans *trans, if (rx_chains >= 3) ht_info->mcs.rx_mask[2] = 0xFF; - if (cfg->ht_params->ht_greenfield_support) + if (cfg->ht_params.ht_greenfield_support) ht_info->cap |= IEEE80211_HT_CAP_GRN_FLD; ht_info->cap |= IEEE80211_HT_CAP_SGI_20; max_bit_rate = MAX_BIT_RATE_20_MHZ; - if (cfg->ht_params->ht40_bands & BIT(band)) { + if (cfg->ht_params.ht40_bands & BIT(band)) { ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; ht_info->cap |= IEEE80211_HT_CAP_SGI_40; max_bit_rate = MAX_BIT_RATE_40_MHZ; diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h index 34eca1a568ea..5dc299296d6d 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-op-mode.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2021, 2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2021, 2024-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015 Intel Deutschland GmbH */ @@ -17,7 +17,7 @@ struct sk_buff; struct iwl_device_cmd; struct iwl_rx_cmd_buffer; struct iwl_fw; -struct iwl_cfg; +struct iwl_rf_cfg; /** * DOC: Operational mode - what is it ? @@ -52,12 +52,20 @@ struct iwl_cfg; * any debug collection must happen synchronously as * the device will be shut down * @IWL_ERR_TYPE_CMD_QUEUE_FULL: command queue was full + * @IWL_ERR_TYPE_TOP_RESET_BY_BT: TOP reset initiated by BT + * @IWL_ERR_TYPE_TOP_FATAL_ERROR: TOP fatal error + * @IWL_ERR_TYPE_TOP_RESET_FAILED: TOP reset failed + * @IWL_ERR_TYPE_DEBUGFS: error/reset indication from debugfs */ enum iwl_fw_error_type { IWL_ERR_TYPE_IRQ, IWL_ERR_TYPE_NMI_FORCED, IWL_ERR_TYPE_RESET_HS_TIMEOUT, IWL_ERR_TYPE_CMD_QUEUE_FULL, + IWL_ERR_TYPE_TOP_RESET_BY_BT, + IWL_ERR_TYPE_TOP_FATAL_ERROR, + IWL_ERR_TYPE_TOP_RESET_FAILED, + IWL_ERR_TYPE_DEBUGFS, }; /** @@ -142,7 +150,7 @@ struct iwl_fw_error_dump_mode { */ struct iwl_op_mode_ops { struct iwl_op_mode *(*start)(struct iwl_trans *trans, - const struct iwl_cfg *cfg, + const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir); void (*stop)(struct iwl_op_mode *op_mode); @@ -242,6 +250,9 @@ static inline void iwl_op_mode_dump_error(struct iwl_op_mode *op_mode, { might_sleep(); + if (WARN_ON(mode->type == IWL_ERR_TYPE_TOP_RESET_BY_BT)) + return; + if (op_mode->ops->dump_error) op_mode->ops->dump_error(op_mode, mode); } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index 49c8507d1a6b..8a40801cf0dd 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2019-2021, 2023-2024 Intel Corporation + * Copyright (C) 2019-2021, 2023-2025 Intel Corporation */ #include <linux/kernel.h> #include <linux/bsearch.h> @@ -15,12 +15,13 @@ #include <linux/dmapool.h> #include "fw/api/commands.h" #include "pcie/internal.h" -#include "iwl-context-info-gen3.h" +#include "iwl-context-info-v2.h" struct iwl_trans_dev_restart_data { struct list_head list; unsigned int restart_count; time64_t last_error; + bool backoff; char name[]; }; @@ -81,14 +82,14 @@ void iwl_trans_free_restart_list(void) struct iwl_trans_reprobe { struct device *dev; - struct work_struct work; + struct delayed_work work; }; static void iwl_trans_reprobe_wk(struct work_struct *wk) { struct iwl_trans_reprobe *reprobe; - reprobe = container_of(wk, typeof(*reprobe), work); + reprobe = container_of(wk, typeof(*reprobe), work.work); if (device_reprobe(reprobe->dev)) dev_err(reprobe->dev, "reprobe failed!\n"); @@ -97,7 +98,32 @@ static void iwl_trans_reprobe_wk(struct work_struct *wk) module_put(THIS_MODULE); } -#define IWL_TRANS_RESET_OK_TIME 180 /* seconds */ +static void iwl_trans_schedule_reprobe(struct iwl_trans *trans, + unsigned int delay_ms) +{ + struct iwl_trans_reprobe *reprobe; + + /* + * get a module reference to avoid doing this while unloading + * anyway and to avoid scheduling a work with code that's + * being removed. + */ + if (!try_module_get(THIS_MODULE)) { + IWL_ERR(trans, "Module is being unloaded - abort\n"); + return; + } + + reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL); + if (!reprobe) { + module_put(THIS_MODULE); + return; + } + reprobe->dev = get_device(trans->dev); + INIT_DELAYED_WORK(&reprobe->work, iwl_trans_reprobe_wk); + schedule_delayed_work(&reprobe->work, msecs_to_jiffies(delay_ms)); +} + +#define IWL_TRANS_RESET_OK_TIME 7 /* seconds */ static enum iwl_reset_mode iwl_trans_determine_restart_mode(struct iwl_trans *trans) @@ -105,18 +131,46 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans) struct iwl_trans_dev_restart_data *data; enum iwl_reset_mode at_least = 0; unsigned int index; - static const enum iwl_reset_mode escalation_list[] = { + static const enum iwl_reset_mode escalation_list_old[] = { IWL_RESET_MODE_SW_RESET, IWL_RESET_MODE_REPROBE, IWL_RESET_MODE_REPROBE, IWL_RESET_MODE_FUNC_RESET, - /* FIXME: add TOP reset */ IWL_RESET_MODE_PROD_RESET, - /* FIXME: add TOP reset */ + }; + static const enum iwl_reset_mode escalation_list_sc[] = { + IWL_RESET_MODE_SW_RESET, + IWL_RESET_MODE_REPROBE, + IWL_RESET_MODE_REPROBE, + IWL_RESET_MODE_FUNC_RESET, + IWL_RESET_MODE_TOP_RESET, + IWL_RESET_MODE_PROD_RESET, + IWL_RESET_MODE_TOP_RESET, IWL_RESET_MODE_PROD_RESET, - /* FIXME: add TOP reset */ + IWL_RESET_MODE_TOP_RESET, IWL_RESET_MODE_PROD_RESET, }; + const enum iwl_reset_mode *escalation_list; + size_t escalation_list_size; + + /* used by TOP fatal error/TOP reset */ + if (trans->restart.mode.type == IWL_ERR_TYPE_TOP_RESET_FAILED) + return IWL_RESET_MODE_PROD_RESET; + + if (trans->request_top_reset) { + trans->request_top_reset = 0; + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) + return IWL_RESET_MODE_TOP_RESET; + return IWL_RESET_MODE_PROD_RESET; + } + + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) { + escalation_list = escalation_list_sc; + escalation_list_size = ARRAY_SIZE(escalation_list_sc); + } else { + escalation_list = escalation_list_old; + escalation_list_size = ARRAY_SIZE(escalation_list_old); + } if (trans->restart.during_reset) at_least = IWL_RESET_MODE_REPROBE; @@ -125,25 +179,39 @@ iwl_trans_determine_restart_mode(struct iwl_trans *trans) if (!data) return at_least; - if (ktime_get_boottime_seconds() - data->last_error >= + if (!data->backoff && + ktime_get_boottime_seconds() - data->last_error >= IWL_TRANS_RESET_OK_TIME) data->restart_count = 0; index = data->restart_count; - if (index >= ARRAY_SIZE(escalation_list)) - index = ARRAY_SIZE(escalation_list) - 1; + if (index >= escalation_list_size) { + index = escalation_list_size - 1; + if (!data->backoff) { + data->backoff = true; + return IWL_RESET_MODE_BACKOFF; + } + data->backoff = false; + } return max(at_least, escalation_list[index]); } +#define IWL_TRANS_TOP_FOLLOWER_WAIT 180 /* ms */ + #define IWL_TRANS_RESET_DELAY (HZ * 60) static void iwl_trans_restart_wk(struct work_struct *wk) { - struct iwl_trans *trans = container_of(wk, typeof(*trans), restart.wk); - struct iwl_trans_reprobe *reprobe; + struct iwl_trans *trans = container_of(wk, typeof(*trans), + restart.wk.work); enum iwl_reset_mode mode; + if (trans->restart.mode.type == IWL_ERR_TYPE_TOP_RESET_BY_BT) { + iwl_trans_schedule_reprobe(trans, IWL_TRANS_TOP_FOLLOWER_WAIT); + return; + } + if (!trans->op_mode) return; @@ -168,35 +236,29 @@ static void iwl_trans_restart_wk(struct work_struct *wk) return; mode = iwl_trans_determine_restart_mode(trans); + if (mode == IWL_RESET_MODE_BACKOFF) { + IWL_ERR(trans, "Too many device errors - delay next reset\n"); + queue_delayed_work(system_unbound_wq, &trans->restart.wk, + IWL_TRANS_RESET_DELAY); + return; + } iwl_trans_inc_restart_count(trans->dev); switch (mode) { + case IWL_RESET_MODE_TOP_RESET: + trans->do_top_reset = 1; + IWL_ERR(trans, "Device error - TOP reset\n"); + fallthrough; case IWL_RESET_MODE_SW_RESET: - IWL_ERR(trans, "Device error - SW reset\n"); + if (mode == IWL_RESET_MODE_SW_RESET) + IWL_ERR(trans, "Device error - SW reset\n"); iwl_trans_opmode_sw_reset(trans, trans->restart.mode.type); break; case IWL_RESET_MODE_REPROBE: IWL_ERR(trans, "Device error - reprobe!\n"); - /* - * get a module reference to avoid doing this while unloading - * anyway and to avoid scheduling a work with code that's - * being removed. - */ - if (!try_module_get(THIS_MODULE)) { - IWL_ERR(trans, "Module is being unloaded - abort\n"); - return; - } - - reprobe = kzalloc(sizeof(*reprobe), GFP_KERNEL); - if (!reprobe) { - module_put(THIS_MODULE); - return; - } - reprobe->dev = get_device(trans->dev); - INIT_WORK(&reprobe->work, iwl_trans_reprobe_wk); - schedule_work(&reprobe->work); + iwl_trans_schedule_reprobe(trans, 0); break; default: iwl_trans_pcie_reset(trans, mode); @@ -206,7 +268,7 @@ static void iwl_trans_restart_wk(struct work_struct *wk) struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, struct device *dev, - const struct iwl_cfg_trans_params *cfg_trans) + const struct iwl_mac_cfg *mac_cfg) { struct iwl_trans *trans; #ifdef CONFIG_LOCKDEP @@ -217,7 +279,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, if (!trans) return NULL; - trans->trans_cfg = cfg_trans; + trans->mac_cfg = mac_cfg; #ifdef CONFIG_LOCKDEP lockdep_init_map(&trans->sync_cmd_lockdep_map, "sync_cmd_lockdep_map", @@ -225,9 +287,8 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, #endif trans->dev = dev; - trans->num_rx_queues = 1; - INIT_WORK(&trans->restart.wk, iwl_trans_restart_wk); + INIT_DELAYED_WORK(&trans->restart.wk, iwl_trans_restart_wk); return trans; } @@ -236,14 +297,18 @@ int iwl_trans_init(struct iwl_trans *trans) { int txcmd_size, txcmd_align; - if (!trans->trans_cfg->gen2) { - txcmd_size = sizeof(struct iwl_tx_cmd); + /* check if name/num_rx_queues were set as a proxy for info being set */ + if (WARN_ON(!trans->info.name || !trans->info.num_rxqs)) + return -EINVAL; + + if (!trans->mac_cfg->gen2) { + txcmd_size = sizeof(struct iwl_tx_cmd_v6); txcmd_align = sizeof(void *); - } else if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { - txcmd_size = sizeof(struct iwl_tx_cmd_gen2); + } else if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { + txcmd_size = sizeof(struct iwl_tx_cmd_v9); txcmd_align = 64; } else { - txcmd_size = sizeof(struct iwl_tx_cmd_gen3); + txcmd_size = sizeof(struct iwl_tx_cmd); txcmd_align = 128; } @@ -251,7 +316,7 @@ int iwl_trans_init(struct iwl_trans *trans) txcmd_size += 36; /* biggest possible 802.11 header */ /* Ensure device TX cmd cannot reach/cross a page boundary in gen2 */ - if (WARN_ON(trans->trans_cfg->gen2 && txcmd_size >= txcmd_align)) + if (WARN_ON(trans->mac_cfg->gen2 && txcmd_size >= txcmd_align)) return -EINVAL; snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), @@ -263,15 +328,12 @@ int iwl_trans_init(struct iwl_trans *trans) if (!trans->dev_cmd_pool) return -ENOMEM; - /* Initialize the wait queue for commands */ - init_waitqueue_head(&trans->wait_command_queue); - return 0; } void iwl_trans_free(struct iwl_trans *trans) { - cancel_work_sync(&trans->restart.wk); + cancel_delayed_work_sync(&trans->restart.wk); kmem_cache_destroy(trans->dev_cmd_pool); } @@ -283,17 +345,7 @@ int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) test_bit(STATUS_RFKILL_OPMODE, &trans->status))) return -ERFKILL; - /* - * We can't test IWL_MVM_STATUS_IN_D3 in mvm->status because this - * bit is set early in the D3 flow, before we send all the commands - * that configure the firmware for D3 operation (power, patterns, ...) - * and we don't want to flag all those with CMD_SEND_IN_D3. - * So use the system_pm_mode instead. The only command sent after - * we set system_pm_mode is D3_CONFIG_CMD, which we now flag with - * CMD_SEND_IN_D3. - */ - if (unlikely(trans->system_pm_mode == IWL_PLAT_PM_MODE_D3 && - !(cmd->flags & CMD_SEND_IN_D3))) + if (unlikely(test_bit(STATUS_SUSPENDED, &trans->status))) return -EHOSTDOWN; if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) @@ -306,7 +358,7 @@ int iwl_trans_send_cmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) if (!(cmd->flags & CMD_ASYNC)) lock_map_acquire_read(&trans->sync_cmd_lockdep_map); - if (trans->wide_cmd_header && !iwl_cmd_groupid(cmd->id)) { + if (trans->conf.wide_cmd_header && !iwl_cmd_groupid(cmd->id)) { if (cmd->id != REPLY_ERROR) cmd->id = DEF_ID(cmd->id); } @@ -350,11 +402,12 @@ const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id) grp = iwl_cmd_groupid(id); cmd = iwl_cmd_opcode(id); - if (!trans->command_groups || grp >= trans->command_groups_size || - !trans->command_groups[grp].arr) + if (!trans->conf.command_groups || + grp >= trans->conf.command_groups_size || + !trans->conf.command_groups[grp].arr) return "UNKNOWN"; - arr = &trans->command_groups[grp]; + arr = &trans->conf.command_groups[grp]; ret = bsearch(&cmd, arr->arr, arr->size, size, iwl_hcmd_names_cmp); if (!ret) return "UNKNOWN"; @@ -362,37 +415,29 @@ const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id) } IWL_EXPORT_SYMBOL(iwl_get_cmd_string); -int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans) +void iwl_trans_op_mode_enter(struct iwl_trans *trans, + struct iwl_op_mode *op_mode) { - int i, j; - const struct iwl_hcmd_arr *arr; + trans->op_mode = op_mode; - for (i = 0; i < trans->command_groups_size; i++) { - arr = &trans->command_groups[i]; - if (!arr->arr) - continue; - for (j = 0; j < arr->size - 1; j++) - if (arr->arr[j].cmd_id > arr->arr[j + 1].cmd_id) - return -1; - } - return 0; -} -IWL_EXPORT_SYMBOL(iwl_cmd_groups_verify_sorted); + if (WARN_ON(trans->conf.n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) + trans->conf.n_no_reclaim_cmds = + ARRAY_SIZE(trans->conf.no_reclaim_cmds); -void iwl_trans_configure(struct iwl_trans *trans, - const struct iwl_trans_config *trans_cfg) -{ - trans->op_mode = trans_cfg->op_mode; + WARN_ON_ONCE(!trans->conf.rx_mpdu_cmd); - iwl_trans_pcie_configure(trans, trans_cfg); - WARN_ON(iwl_cmd_groups_verify_sorted(trans_cfg)); + iwl_trans_pcie_op_mode_enter(trans); } -IWL_EXPORT_SYMBOL(iwl_trans_configure); +IWL_EXPORT_SYMBOL(iwl_trans_op_mode_enter); int iwl_trans_start_hw(struct iwl_trans *trans) { might_sleep(); + clear_bit(STATUS_TRANS_RESET_IN_PROGRESS, &trans->status); + /* opmode may not resume if it detects errors */ + clear_bit(STATUS_SUSPENDED, &trans->status); + return iwl_trans_pcie_start_hw(trans); } IWL_EXPORT_SYMBOL(iwl_trans_start_hw); @@ -403,7 +448,10 @@ void iwl_trans_op_mode_leave(struct iwl_trans *trans) iwl_trans_pcie_op_mode_leave(trans); + cancel_delayed_work_sync(&trans->restart.wk); + trans->op_mode = NULL; + memset(&trans->conf, 0, sizeof(trans->conf)); trans->state = IWL_TRANS_NO_FW; } @@ -480,18 +528,31 @@ IWL_EXPORT_SYMBOL(iwl_trans_dump_data); int iwl_trans_d3_suspend(struct iwl_trans *trans, bool test, bool reset) { + int err; + might_sleep(); - return iwl_trans_pcie_d3_suspend(trans, test, reset); + err = iwl_trans_pcie_d3_suspend(trans, test, reset); + + if (!err) + set_bit(STATUS_SUSPENDED, &trans->status); + + return err; } IWL_EXPORT_SYMBOL(iwl_trans_d3_suspend); int iwl_trans_d3_resume(struct iwl_trans *trans, enum iwl_d3_status *status, bool test, bool reset) { + int err; + might_sleep(); - return iwl_trans_pcie_d3_resume(trans, status, test, reset); + err = iwl_trans_pcie_d3_resume(trans, status, test, reset); + + clear_bit(STATUS_SUSPENDED, &trans->status); + + return err; } IWL_EXPORT_SYMBOL(iwl_trans_d3_resume); @@ -538,38 +599,42 @@ void __releases(nic_access) iwl_trans_release_nic_access(struct iwl_trans *trans) { iwl_trans_pcie_release_nic_access(trans); - __release(nic_access); } IWL_EXPORT_SYMBOL(iwl_trans_release_nic_access); -void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr) +void iwl_trans_fw_alive(struct iwl_trans *trans) { might_sleep(); trans->state = IWL_TRANS_FW_ALIVE; - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) iwl_trans_pcie_gen2_fw_alive(trans); else - iwl_trans_pcie_fw_alive(trans, scd_addr); + iwl_trans_pcie_fw_alive(trans); } IWL_EXPORT_SYMBOL(iwl_trans_fw_alive); -int iwl_trans_start_fw(struct iwl_trans *trans, const struct fw_img *fw, - bool run_in_rfkill) +int iwl_trans_start_fw(struct iwl_trans *trans, const struct iwl_fw *fw, + enum iwl_ucode_type ucode_type, bool run_in_rfkill) { + const struct fw_img *img; int ret; might_sleep(); - WARN_ON_ONCE(!trans->rx_mpdu_cmd); + img = iwl_get_ucode_image(fw, ucode_type); + if (!img) + return -EINVAL; clear_bit(STATUS_FW_ERROR, &trans->status); - if (trans->trans_cfg->gen2) - ret = iwl_trans_pcie_gen2_start_fw(trans, fw, run_in_rfkill); + if (trans->mac_cfg->gen2) + ret = iwl_trans_pcie_gen2_start_fw(trans, fw, img, + run_in_rfkill); else - ret = iwl_trans_pcie_start_fw(trans, fw, run_in_rfkill); + ret = iwl_trans_pcie_start_fw(trans, fw, img, + run_in_rfkill); if (ret == 0) trans->state = IWL_TRANS_FW_STARTED; @@ -610,7 +675,7 @@ void iwl_trans_stop_device(struct iwl_trans *trans) iwl_op_mode_dump_error(trans->op_mode, &mode); } - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) iwl_trans_pcie_gen2_stop_device(trans); else iwl_trans_pcie_stop_device(trans); @@ -629,7 +694,7 @@ int iwl_trans_tx(struct iwl_trans *trans, struct sk_buff *skb, "bad state = %d\n", trans->state)) return -EIO; - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) return iwl_txq_gen2_tx(trans, skb, dev_cmd, queue); return iwl_trans_pcie_tx(trans, skb, dev_cmd, queue); @@ -639,6 +704,9 @@ IWL_EXPORT_SYMBOL(iwl_trans_tx); void iwl_trans_reclaim(struct iwl_trans *trans, int queue, int ssn, struct sk_buff_head *skbs, bool is_flush) { + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return; + if (WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "bad state = %d\n", trans->state)) return; @@ -671,6 +739,9 @@ IWL_EXPORT_SYMBOL(iwl_trans_txq_enable_cfg); int iwl_trans_wait_txq_empty(struct iwl_trans *trans, int queue) { + if (unlikely(test_bit(STATUS_FW_ERROR, &trans->status))) + return -EIO; + if (WARN_ONCE(trans->state != IWL_TRANS_FW_ALIVE, "bad state = %d\n", trans->state)) return -EIO; @@ -756,14 +827,14 @@ int iwl_trans_load_pnvm(struct iwl_trans *trans, const struct iwl_pnvm_image *pnvm_data, const struct iwl_ucode_capabilities *capa) { - return iwl_trans_pcie_ctx_info_gen3_load_pnvm(trans, pnvm_data, capa); + return iwl_trans_pcie_ctx_info_v2_load_pnvm(trans, pnvm_data, capa); } IWL_EXPORT_SYMBOL(iwl_trans_load_pnvm); void iwl_trans_set_pnvm(struct iwl_trans *trans, const struct iwl_ucode_capabilities *capa) { - iwl_trans_pcie_ctx_info_gen3_set_pnvm(trans, capa); + iwl_trans_pcie_ctx_info_v2_set_pnvm(trans, capa); } IWL_EXPORT_SYMBOL(iwl_trans_set_pnvm); @@ -771,7 +842,7 @@ int iwl_trans_load_reduce_power(struct iwl_trans *trans, const struct iwl_pnvm_image *payloads, const struct iwl_ucode_capabilities *capa) { - return iwl_trans_pcie_ctx_info_gen3_load_reduce_power(trans, payloads, + return iwl_trans_pcie_ctx_info_v2_load_reduce_power(trans, payloads, capa); } IWL_EXPORT_SYMBOL(iwl_trans_load_reduce_power); @@ -779,6 +850,6 @@ IWL_EXPORT_SYMBOL(iwl_trans_load_reduce_power); void iwl_trans_set_reduce_power(struct iwl_trans *trans, const struct iwl_ucode_capabilities *capa) { - iwl_trans_pcie_ctx_info_gen3_set_reduce_power(trans, capa); + iwl_trans_pcie_ctx_info_v2_set_reduce_power(trans, capa); } IWL_EXPORT_SYMBOL(iwl_trans_set_reduce_power); diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h index f6234065dbdd..012b1e44bce3 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2023 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -109,16 +109,12 @@ static inline u32 iwl_rx_packet_payload_len(const struct iwl_rx_packet *pkt) * the response. The caller needs to call iwl_free_resp when done. * @CMD_SEND_IN_RFKILL: Send the command even if the NIC is in RF-kill. * @CMD_BLOCK_TXQS: Block TXQs while the comment is executing. - * @CMD_SEND_IN_D3: Allow the command to be sent in D3 mode, relevant to - * SUSPEND and RESUME commands. We are in D3 mode when we set - * trans->system_pm_mode to IWL_PLAT_PM_MODE_D3. */ enum CMD_MODE { CMD_ASYNC = BIT(0), CMD_WANT_SKB = BIT(1), CMD_SEND_IN_RFKILL = BIT(2), CMD_BLOCK_TXQS = BIT(3), - CMD_SEND_IN_D3 = BIT(4), }; #define CMD_MODE_BITS 5 @@ -304,6 +300,10 @@ enum iwl_d3_status { * via iwl_trans_finish_sw_reset() * @STATUS_RESET_PENDING: reset worker was scheduled, but didn't dump * the firmware state yet + * @STATUS_TRANS_RESET_IN_PROGRESS: reset is still in progress, don't + * attempt another reset yet + * @STATUS_SUSPENDED: device is suspended, don't send commands that + * aren't marked accordingly */ enum iwl_trans_status { STATUS_SYNC_HCMD_ACTIVE, @@ -317,6 +317,8 @@ enum iwl_trans_status { STATUS_SUPPRESS_CMD_ERROR_ONCE, STATUS_IN_SW_RESET, STATUS_RESET_PENDING, + STATUS_TRANS_RESET_IN_PROGRESS, + STATUS_SUSPENDED, }; static inline int @@ -328,6 +330,7 @@ iwl_trans_get_rb_size_order(enum iwl_amsdu_size rb_size) case IWL_AMSDU_4K: return get_order(4 * 1024); case IWL_AMSDU_8K: + return get_order(8 * 1024); case IWL_AMSDU_12K: return get_order(16 * 1024); default: @@ -387,7 +390,8 @@ struct iwl_dump_sanitize_ops { /** * struct iwl_trans_config - transport configuration * - * @op_mode: pointer to the upper layer. + * These values should be set before iwl_trans_op_mode_enter(). + * * @cmd_queue: the index of the command queue. * Must be set before start_fw. * @cmd_fifo: the fifo for host commands @@ -398,8 +402,6 @@ struct iwl_dump_sanitize_ops { * @n_no_reclaim_cmds: # of commands in list * @rx_buf_size: RX buffer size needed for A-MSDUs * if unset 4k will be the RX buffer size - * @bc_table_dword: set to true if the BC table expects the byte count to be - * in DWORD (as opposed to bytes) * @scd_set_active: should the transport configure the SCD for HCMD queue * @command_groups: array of command groups, each member is an array of the * commands in the group; for debugging only @@ -410,17 +412,24 @@ struct iwl_dump_sanitize_ops { * @queue_alloc_cmd_ver: queue allocation command version, set to 0 * for using the older SCD_QUEUE_CFG, set to the version of * SCD_QUEUE_CONFIG_CMD otherwise. + * @wide_cmd_header: true when ucode supports wide command header format + * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before + * starting the firmware, used for tracing + * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the + * start of the 802.11 header in the @rx_mpdu_cmd + * @dsbr_urm_fw_dependent: switch to URM based on fw settings + * @dsbr_urm_permanent: switch to URM permanently + * @mbx_addr_0_step: step address data 0 + * @mbx_addr_1_step: step address data 1 + * @ext_32khz_clock_valid: if true, the external 32 KHz clock can be used */ struct iwl_trans_config { - struct iwl_op_mode *op_mode; - u8 cmd_queue; u8 cmd_fifo; - const u8 *no_reclaim_cmds; - unsigned int n_no_reclaim_cmds; + u8 n_no_reclaim_cmds; + u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; enum iwl_amsdu_size rx_buf_size; - bool bc_table_dword; bool scd_set_active; const struct iwl_hcmd_arr *command_groups; int command_groups_size; @@ -428,6 +437,16 @@ struct iwl_trans_config { u8 cb_data_offs; bool fw_reset_handshake; u8 queue_alloc_cmd_ver; + + bool wide_cmd_header; + u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; + + u8 dsbr_urm_fw_dependent:1, + dsbr_urm_permanent:1, + ext_32khz_clock_valid:1; + + u32 mbx_addr_0_step; + u32 mbx_addr_1_step; }; struct iwl_trans_dump_data { @@ -513,23 +532,6 @@ enum iwl_trans_state { */ /** - * enum iwl_plat_pm_mode - platform power management mode - * - * This enumeration describes the device's platform power management - * behavior when in system-wide suspend (i.e WoWLAN). - * - * @IWL_PLAT_PM_MODE_DISABLED: power management is disabled for this - * device. In system-wide suspend mode, it means that the all - * connections will be closed automatically by mac80211 before - * the platform is suspended. - * @IWL_PLAT_PM_MODE_D3: the device goes into D3 mode (i.e. WoWLAN). - */ -enum iwl_plat_pm_mode { - IWL_PLAT_PM_MODE_DISABLED, - IWL_PLAT_PM_MODE_D3, -}; - -/** * enum iwl_ini_cfg_state * @IWL_INI_CFG_STATE_NOT_LOADED: no debug cfg was given * @IWL_INI_CFG_STATE_LOADED: debug cfg was found and loaded @@ -819,103 +821,90 @@ struct iwl_txq { }; /** + * struct iwl_trans_info - transport info for outside use + * @name: the device name + * @max_skb_frags: maximum number of fragments an SKB can have when transmitted. + * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported. + * @hw_rev: the revision data of the HW + * @hw_rev_step: The mac step of the HW + * @hw_rf_id: the device RF ID + * @hw_cnv_id: the device CNV ID + * @hw_crf_id: the device CRF ID + * @hw_wfpm_id: the device wfpm ID + * @hw_id: the ID of the device / sub-device + * Bits 0:15 represent the sub-device ID + * Bits 16:31 represent the device ID. + * @pcie_link_speed: current PCIe link speed (%PCI_EXP_LNKSTA_CLS_*), + * only valid for discrete (not integrated) NICs + * @num_rxqs: number of RX queues allocated by the transport + */ +struct iwl_trans_info { + const char *name; + u32 max_skb_frags; + u32 hw_rev; + u32 hw_rev_step; + u32 hw_rf_id; + u32 hw_crf_id; + u32 hw_cnv_id; + u32 hw_wfpm_id; + u32 hw_id; + u8 pcie_link_speed; + u8 num_rxqs; +}; + +/** * struct iwl_trans - transport common data * * @csme_own: true if we couldn't get ownership on the device * @op_mode: pointer to the op_mode - * @trans_cfg: the trans-specific configuration part + * @mac_cfg: the trans-specific configuration part * @cfg: pointer to the configuration * @drv: pointer to iwl_drv + * @conf: configuration set by the opmode before enter * @state: current device state * @status: a bit-mask of transport status flags * @dev: pointer to struct device * that represents the device - * @max_skb_frags: maximum number of fragments an SKB can have when transmitted. - * 0 indicates that frag SKBs (NETIF_F_SG) aren't supported. - * @hw_rf_id: a u32 with the device RF ID - * @hw_cnv_id: a u32 with the device CNV ID - * @hw_crf_id: a u32 with the device CRF ID - * @hw_wfpm_id: a u32 with the device wfpm ID - * @hw_id: a u32 with the ID of the device / sub-device. - * Set during transport allocation. - * @hw_id_str: a string with info about HW ID. Set during transport allocation. - * @sku_id: the SKU identifier (for PNVM matching) + * @info: device information for use by other layers * @pnvm_loaded: indicates PNVM was loaded - * @hw_rev: the revision data of the HW - * @hw_rev_step: The mac step of the HW * @pm_support: set to true in start_hw if link pm is supported * @ltr_enabled: set to true if the LTR is enabled * @fail_to_parse_pnvm_image: set to true if pnvm parsing failed * @reduce_power_loaded: indicates reduced power section was loaded * @failed_to_load_reduce_power_image: set to true if pnvm loading failed - * @command_groups: pointer to command group name list array - * @command_groups_size: array size of @command_groups - * @wide_cmd_header: true when ucode supports wide command header format - * @wait_command_queue: wait queue for sync commands - * @num_rx_queues: number of RX queues allocated by the transport; - * the transport must set this before calling iwl_drv_start() - * @iml_len: the length of the image loader - * @iml: a pointer to the image loader itself * @dev_cmd_pool: pool for Tx cmd allocation - for internal use only. * The user should use iwl_trans_{alloc,free}_tx_cmd. * @dev_cmd_pool_name: name for the TX command allocation pool * @dbgfs_dir: iwlwifi debugfs base dir for this device * @sync_cmd_lockdep_map: lockdep map for checking sync commands - * @rx_mpdu_cmd: MPDU RX command ID, must be assigned by opmode before - * starting the firmware, used for tracing - * @rx_mpdu_cmd_hdr_size: used for tracing, amount of data before the - * start of the 802.11 header in the @rx_mpdu_cmd * @dbg: additional debug data, see &struct iwl_trans_debug * @init_dram: FW initialization DMA data - * @system_pm_mode: the system-wide power management mode in use. - * This mode is set dynamically, depending on the WoWLAN values - * configured from the userspace at runtime. - * @name: the device name - * @mbx_addr_0_step: step address data 0 - * @mbx_addr_1_step: step address data 1 - * @pcie_link_speed: current PCIe link speed (%PCI_EXP_LNKSTA_CLS_*), - * only valid for discrete (not integrated) NICs - * @invalid_tx_cmd: invalid TX command buffer * @reduced_cap_sku: reduced capability supported SKU - * @no_160: device not supporting 160 MHz * @step_urm: STEP is in URM, no support for MCS>9 in 320 MHz * @restart: restart worker data * @restart.wk: restart worker * @restart.mode: reset/restart error mode information * @restart.during_reset: error occurred during previous software reset - * @me_recheck_wk: worker to recheck WiAMT/CSME presence - * @me_present: WiAMT/CSME is detected as present (1), not present (0) - * or unknown (-1, so can still use it as a boolean safely) * @trans_specific: data for the specific transport this is allocated for/with - * @dsbr_urm_fw_dependent: switch to URM based on fw settings - * @dsbr_urm_permanent: switch to URM permanently + * @request_top_reset: TOP reset was requested, used by the reset + * worker that should be scheduled (with appropriate reason) + * @do_top_reset: indication to the (PCIe) transport/context-info + * to do the TOP reset */ struct iwl_trans { bool csme_own; struct iwl_op_mode *op_mode; - const struct iwl_cfg_trans_params *trans_cfg; - const struct iwl_cfg *cfg; + const struct iwl_mac_cfg *mac_cfg; + const struct iwl_rf_cfg *cfg; struct iwl_drv *drv; + struct iwl_trans_config conf; enum iwl_trans_state state; unsigned long status; struct device *dev; - u32 max_skb_frags; - u32 hw_rev; - u32 hw_rev_step; - u32 hw_rf_id; - u32 hw_crf_id; - u32 hw_cnv_id; - u32 hw_wfpm_id; - u32 hw_id; - char hw_id_str[52]; - u32 sku_id[3]; - bool reduced_cap_sku; - u8 no_160:1, step_urm:1; - u8 dsbr_urm_fw_dependent:1, - dsbr_urm_permanent:1; - - u8 rx_mpdu_cmd, rx_mpdu_cmd_hdr_size; + const struct iwl_trans_info info; + bool reduced_cap_sku; + bool step_urm; bool pm_support; bool ltr_enabled; @@ -924,16 +913,6 @@ struct iwl_trans { u8 reduce_power_loaded:1; u8 failed_to_load_reduce_power_image:1; - const struct iwl_hcmd_arr *command_groups; - int command_groups_size; - bool wide_cmd_header; - - wait_queue_head_t wait_command_queue; - u8 num_rx_queues; - - size_t iml_len; - u8 *iml; - /* The following fields are internal only */ struct kmem_cache *dev_cmd_pool; char dev_cmd_pool_name[50]; @@ -947,24 +926,14 @@ struct iwl_trans { struct iwl_trans_debug dbg; struct iwl_self_init_dram init_dram; - enum iwl_plat_pm_mode system_pm_mode; - - const char *name; - u32 mbx_addr_0_step; - u32 mbx_addr_1_step; - - u8 pcie_link_speed; - - struct iwl_dma_ptr invalid_tx_cmd; - struct { - struct work_struct wk; + struct delayed_work wk; struct iwl_fw_error_dump_mode mode; bool during_reset; } restart; - struct delayed_work me_recheck_wk; - s8 me_present; + u8 request_top_reset:1, + do_top_reset:1; /* pointer to trans specific struct */ /*Ensure that this pointer will always be aligned to sizeof pointer */ @@ -972,19 +941,18 @@ struct iwl_trans { }; const char *iwl_get_cmd_string(struct iwl_trans *trans, u32 id); -int iwl_cmd_groups_verify_sorted(const struct iwl_trans_config *trans); -void iwl_trans_configure(struct iwl_trans *trans, - const struct iwl_trans_config *trans_cfg); +void iwl_trans_op_mode_enter(struct iwl_trans *trans, + struct iwl_op_mode *op_mode); int iwl_trans_start_hw(struct iwl_trans *trans); void iwl_trans_op_mode_leave(struct iwl_trans *trans); -void iwl_trans_fw_alive(struct iwl_trans *trans, u32 scd_addr); +void iwl_trans_fw_alive(struct iwl_trans *trans); -int iwl_trans_start_fw(struct iwl_trans *trans, const struct fw_img *fw, - bool run_in_rfkill); +int iwl_trans_start_fw(struct iwl_trans *trans, const struct iwl_fw *fw, + enum iwl_ucode_type ucode_type, bool run_in_rfkill); void iwl_trans_stop_device(struct iwl_trans *trans); @@ -1147,6 +1115,9 @@ static inline void iwl_trans_schedule_reset(struct iwl_trans *trans, { if (test_bit(STATUS_TRANS_DEAD, &trans->status)) return; + /* clear this on device init, not cleared on any unbind/reprobe */ + if (test_and_set_bit(STATUS_TRANS_RESET_IN_PROGRESS, &trans->status)) + return; trans->restart.mode.type = type; trans->restart.mode.context = IWL_ERR_CONTEXT_WORKER; @@ -1159,7 +1130,7 @@ static inline void iwl_trans_schedule_reset(struct iwl_trans *trans, */ trans->restart.during_reset = test_bit(STATUS_IN_SW_RESET, &trans->status); - queue_work(system_unbound_wq, &trans->restart.wk); + queue_delayed_work(system_unbound_wq, &trans->restart.wk, 0); } static inline void iwl_trans_fw_error(struct iwl_trans *trans, @@ -1184,6 +1155,9 @@ static inline void iwl_trans_opmode_sw_reset(struct iwl_trans *trans, set_bit(STATUS_IN_SW_RESET, &trans->status); + if (WARN_ON(type == IWL_ERR_TYPE_TOP_RESET_BY_BT)) + return; + if (!trans->op_mode->ops->sw_reset || !trans->op_mode->ops->sw_reset(trans->op_mode, type)) clear_bit(STATUS_IN_SW_RESET, &trans->status); @@ -1231,7 +1205,7 @@ static inline void iwl_trans_finish_sw_reset(struct iwl_trans *trans) *****************************************************/ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, struct device *dev, - const struct iwl_cfg_trans_params *cfg_trans); + const struct iwl_mac_cfg *cfg_trans); int iwl_trans_init(struct iwl_trans *trans); void iwl_trans_free(struct iwl_trans *trans); @@ -1242,6 +1216,19 @@ static inline bool iwl_trans_is_hw_error_value(u32 val) void iwl_trans_free_restart_list(void); +static inline u16 iwl_trans_get_num_rbds(struct iwl_trans *trans) +{ + u16 result = trans->cfg->num_rbds; + + /* + * Since AX210 family (So/Ty) the device cannot put mutliple + * frames into the same buffer, so double the value for them. + */ + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + return 2 * result; + return result; +} + /***************************************************** * PCIe handling *****************************************************/ @@ -1253,16 +1240,37 @@ enum iwl_reset_mode { /* upper level modes: */ IWL_RESET_MODE_SW_RESET, IWL_RESET_MODE_REPROBE, + /* TOP reset doesn't require PCIe remove */ + IWL_RESET_MODE_TOP_RESET, /* PCIE level modes: */ IWL_RESET_MODE_REMOVE_ONLY, IWL_RESET_MODE_RESCAN, IWL_RESET_MODE_FUNC_RESET, IWL_RESET_MODE_PROD_RESET, + + /* keep last - special backoff value */ + IWL_RESET_MODE_BACKOFF, }; void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode); +void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans); int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); +/* Internal helper */ +static inline void iwl_trans_set_info(struct iwl_trans *trans, + struct iwl_trans_info *info) +{ + struct iwl_trans_info *write; + + write = (void *)(uintptr_t)&trans->info; + *write = *info; +} + +static inline u16 iwl_trans_get_device_id(struct iwl_trans *trans) +{ + return u32_get_bits(trans->info.hw_id, GENMASK(31, 16)); +} + #endif /* __iwl_trans_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-utils.c b/drivers/net/wireless/intel/iwlwifi/iwl-utils.c index b14ec98e28b6..c5b49851e4b9 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-utils.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-utils.c @@ -4,7 +4,6 @@ */ #include <net/gso.h> #include <linux/ieee80211.h> -#include <net/gso.h> #include <net/ip.h> #include "iwl-drv.h" diff --git a/drivers/net/wireless/intel/iwlwifi/mld/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/Makefile new file mode 100644 index 000000000000..ece66e7a9be4 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/Makefile @@ -0,0 +1,16 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +obj-$(CONFIG_IWLMLD) += iwlmld.o +obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += tests/ + +iwlmld-y += mld.o notif.o mac80211.o fw.o power.o iface.o link.o rx.o mcc.o session-protect.o phy.o +iwlmld-y += scan.o sta.o tx.o coex.o tlc.o agg.o key.o regulatory.o ap.o thermal.o roc.o stats.o +iwlmld-y += low_latency.o mlo.o ptp.o time_sync.o ftm-initiator.o +iwlmld-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o +iwlmld-$(CONFIG_IWLWIFI_LEDS) += led.o +iwlmld-$(CONFIG_PM_SLEEP) += d3.o + +# non-upstream things +iwlmld-$(CONFIG_IWL_VENDOR_CMDS) += vendor-cmd.o +iwlmld-$(CONFIG_IWLMVM_AX_SOFTAP_TESTMODE) += ax-softap-testmode.o + +subdir-ccflags-y += -I$(src)/../ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/agg.c new file mode 100644 index 000000000000..bda488ae9eec --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.c @@ -0,0 +1,672 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "agg.h" +#include "sta.h" +#include "hcmd.h" + +static void +iwl_mld_reorder_release_frames(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct napi_struct *napi, + struct iwl_mld_baid_data *baid_data, + struct iwl_mld_reorder_buffer *reorder_buf, + u16 nssn) +{ + struct iwl_mld_reorder_buf_entry *entries = + &baid_data->entries[reorder_buf->queue * + baid_data->entries_per_queue]; + u16 ssn = reorder_buf->head_sn; + + while (ieee80211_sn_less(ssn, nssn)) { + int index = ssn % baid_data->buf_size; + struct sk_buff_head *skb_list = &entries[index].frames; + struct sk_buff *skb; + + ssn = ieee80211_sn_inc(ssn); + + /* Empty the list. Will have more than one frame for A-MSDU. + * Empty list is valid as well since nssn indicates frames were + * received. + */ + while ((skb = __skb_dequeue(skb_list))) { + iwl_mld_pass_packet_to_mac80211(mld, napi, skb, + reorder_buf->queue, + sta); + reorder_buf->num_stored--; + } + } + reorder_buf->head_sn = nssn; +} + +static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld, + struct napi_struct *napi, + u8 baid, u16 nssn, int queue) +{ + struct iwl_mld_reorder_buffer *reorder_buf; + struct iwl_mld_baid_data *ba_data; + struct ieee80211_link_sta *link_sta; + u32 sta_id; + + IWL_DEBUG_HT(mld, "Frame release notification for BAID %u, NSSN %d\n", + baid, nssn); + + if (WARN_ON_ONCE(baid == IWL_RX_REORDER_DATA_INVALID_BAID || + baid >= ARRAY_SIZE(mld->fw_id_to_ba))) + return; + + rcu_read_lock(); + + ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (!ba_data) { + IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid); + goto out_unlock; + } + + /* pick any STA ID to find the pointer */ + sta_id = ffs(ba_data->sta_mask) - 1; + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) + goto out_unlock; + + reorder_buf = &ba_data->reorder_buf[queue]; + + iwl_mld_reorder_release_frames(mld, link_sta->sta, napi, ba_data, + reorder_buf, nssn); +out_unlock: + rcu_read_unlock(); +} + +void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue) +{ + struct iwl_frame_release *release = (void *)pkt->data; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + + if (IWL_FW_CHECK(mld, pkt_len < sizeof(*release), + "Unexpected frame release notif size %u (expected %zu)\n", + pkt_len, sizeof(*release))) + return; + + iwl_mld_release_frames_from_notif(mld, napi, release->baid, + le16_to_cpu(release->nssn), + queue); +} + +void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, + int queue) +{ + struct iwl_bar_frame_release *release = (void *)pkt->data; + struct iwl_mld_baid_data *baid_data; + unsigned int baid, nssn, sta_id, tid; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + + if (IWL_FW_CHECK(mld, pkt_len < sizeof(*release), + "Unexpected frame release notif size %u (expected %zu)\n", + pkt_len, sizeof(*release))) + return; + + baid = le32_get_bits(release->ba_info, + IWL_BAR_FRAME_RELEASE_BAID_MASK); + nssn = le32_get_bits(release->ba_info, + IWL_BAR_FRAME_RELEASE_NSSN_MASK); + sta_id = le32_get_bits(release->sta_tid, + IWL_BAR_FRAME_RELEASE_STA_MASK); + tid = le32_get_bits(release->sta_tid, + IWL_BAR_FRAME_RELEASE_TID_MASK); + + if (IWL_FW_CHECK(mld, baid >= ARRAY_SIZE(mld->fw_id_to_ba), + "BAR release: invalid BAID (%x)\n", baid)) + return; + + rcu_read_lock(); + baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (!baid_data) { + IWL_DEBUG_HT(mld, + "Got valid BAID %d but not allocated\n", + baid); + goto out_unlock; + } + + if (IWL_FW_CHECK(mld, tid != baid_data->tid || + sta_id > mld->fw->ucode_capa.num_stations || + !(baid_data->sta_mask & BIT(sta_id)), + "BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but BAR release received for sta:%d tid:%d\n", + baid, baid_data->sta_mask, baid_data->tid, sta_id, + tid)) + goto out_unlock; + + IWL_DEBUG_DROP(mld, "Received a BAR, expect packet loss: nssn %d\n", + nssn); + + iwl_mld_release_frames_from_notif(mld, napi, baid, nssn, queue); +out_unlock: + rcu_read_unlock(); +} + +void iwl_mld_del_ba(struct iwl_mld *mld, int queue, + struct iwl_mld_delba_data *data) +{ + struct iwl_mld_baid_data *ba_data; + struct iwl_mld_reorder_buffer *reorder_buf; + struct ieee80211_link_sta *link_sta; + u8 baid = data->baid; + u32 sta_id; + + if (WARN_ONCE(baid >= IWL_MAX_BAID, "invalid BAID: %x\n", baid)) + return; + + rcu_read_lock(); + + ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (WARN_ON_ONCE(!ba_data)) + goto out_unlock; + + /* pick any STA ID to find the pointer */ + sta_id = ffs(ba_data->sta_mask) - 1; + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (WARN_ON_ONCE(IS_ERR_OR_NULL(link_sta) || !link_sta->sta)) + goto out_unlock; + + reorder_buf = &ba_data->reorder_buf[queue]; + + /* release all frames that are in the reorder buffer to the stack */ + iwl_mld_reorder_release_frames(mld, link_sta->sta, NULL, + ba_data, reorder_buf, + ieee80211_sn_add(reorder_buf->head_sn, + ba_data->buf_size)); +out_unlock: + rcu_read_unlock(); +} + +/* Returns true if the MPDU was buffered\dropped, false if it should be passed + * to upper layer. + */ +enum iwl_mld_reorder_result +iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, + int queue, struct ieee80211_sta *sta, + struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc) +{ + struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); + struct iwl_mld_baid_data *baid_data; + struct iwl_mld_reorder_buffer *buffer; + struct iwl_mld_reorder_buf_entry *entries; + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + u32 reorder = le32_to_cpu(desc->reorder_data); + bool amsdu, last_subframe, is_old_sn, is_dup; + u8 tid = ieee80211_get_tid(hdr); + u8 baid; + u16 nssn, sn; + u32 sta_mask = 0; + int index; + u8 link_id; + + baid = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_BAID_MASK); + + /* This also covers the case of receiving a Block Ack Request + * outside a BA session; we'll pass it to mac80211 and that + * then sends a delBA action frame. + * This also covers pure monitor mode, in which case we won't + * have any BA sessions. + */ + if (baid == IWL_RX_REORDER_DATA_INVALID_BAID) + return IWL_MLD_PASS_SKB; + + /* no sta yet */ + if (WARN_ONCE(!sta, + "Got valid BAID without a valid station assigned\n")) + return IWL_MLD_PASS_SKB; + + /* not a data packet */ + if (!ieee80211_is_data_qos(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return IWL_MLD_PASS_SKB; + + if (unlikely(!ieee80211_is_data_present(hdr->frame_control))) + return IWL_MLD_PASS_SKB; + + baid_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (!baid_data) { + IWL_DEBUG_HT(mld, + "Got valid BAID but no baid allocated, bypass re-ordering (BAID=%d reorder=0x%x)\n", + baid, reorder); + return IWL_MLD_PASS_SKB; + } + + for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) + sta_mask |= BIT(mld_link_sta->fw_id); + + /* verify the BAID is correctly mapped to the sta and tid */ + if (IWL_FW_CHECK(mld, + tid != baid_data->tid || + !(sta_mask & baid_data->sta_mask), + "BAID 0x%x is mapped to sta_mask:0x%x tid:%d, but was received for sta_mask:0x%x tid:%d\n", + baid, baid_data->sta_mask, baid_data->tid, + sta_mask, tid)) + return IWL_MLD_PASS_SKB; + + buffer = &baid_data->reorder_buf[queue]; + entries = &baid_data->entries[queue * baid_data->entries_per_queue]; + + is_old_sn = !!(reorder & IWL_RX_MPDU_REORDER_BA_OLD_SN); + + if (!buffer->valid && is_old_sn) + return IWL_MLD_PASS_SKB; + + buffer->valid = true; + + is_dup = !!(desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_DUPLICATE)); + + /* drop any duplicated or outdated packets */ + if (is_dup || is_old_sn) + return IWL_MLD_DROP_SKB; + + sn = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_SN_MASK); + nssn = u32_get_bits(reorder, IWL_RX_MPDU_REORDER_NSSN_MASK); + amsdu = desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU; + last_subframe = desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME; + + /* release immediately if allowed by nssn and no stored frames */ + if (!buffer->num_stored && ieee80211_sn_less(sn, nssn)) { + if (!amsdu || last_subframe) + buffer->head_sn = nssn; + return IWL_MLD_PASS_SKB; + } + + /* release immediately if there are no stored frames, and the sn is + * equal to the head. + * This can happen due to reorder timer, where NSSN is behind head_sn. + * When we released everything, and we got the next frame in the + * sequence, according to the NSSN we can't release immediately, + * while technically there is no hole and we can move forward. + */ + if (!buffer->num_stored && sn == buffer->head_sn) { + if (!amsdu || last_subframe) + buffer->head_sn = ieee80211_sn_inc(buffer->head_sn); + return IWL_MLD_PASS_SKB; + } + + /* put in reorder buffer */ + index = sn % baid_data->buf_size; + __skb_queue_tail(&entries[index].frames, skb); + buffer->num_stored++; + + /* We cannot trust NSSN for AMSDU sub-frames that are not the last. The + * reason is that NSSN advances on the first sub-frame, and may cause + * the reorder buffer to advance before all the sub-frames arrive. + * + * Example: reorder buffer contains SN 0 & 2, and we receive AMSDU with + * SN 1. NSSN for first sub frame will be 3 with the result of driver + * releasing SN 0,1, 2. When sub-frame 1 arrives - reorder buffer is + * already ahead and it will be dropped. + * If the last sub-frame is not on this queue - we will get frame + * release notification with up to date NSSN. + */ + if (!amsdu || last_subframe) + iwl_mld_reorder_release_frames(mld, sta, napi, baid_data, + buffer, nssn); + + return IWL_MLD_BUFFERED_SKB; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_reorder); + +static void iwl_mld_rx_agg_session_expired(struct timer_list *t) +{ + struct iwl_mld_baid_data *data = + from_timer(data, t, session_timer); + struct iwl_mld_baid_data __rcu **rcu_ptr = data->rcu_ptr; + struct iwl_mld_baid_data *ba_data; + struct ieee80211_link_sta *link_sta; + struct iwl_mld_sta *mld_sta; + unsigned long timeout; + unsigned int sta_id; + + rcu_read_lock(); + + ba_data = rcu_dereference(*rcu_ptr); + if (WARN_ON(!ba_data)) + goto unlock; + + if (WARN_ON(!ba_data->timeout)) + goto unlock; + + timeout = ba_data->last_rx_timestamp + + TU_TO_JIFFIES(ba_data->timeout * 2); + if (time_is_after_jiffies(timeout)) { + mod_timer(&ba_data->session_timer, timeout); + goto unlock; + } + + /* timer expired, pick any STA ID to find the pointer */ + sta_id = ffs(ba_data->sta_mask) - 1; + link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]); + + /* sta should be valid unless the following happens: + * The firmware asserts which triggers a reconfig flow, but + * the reconfig fails before we set the pointer to sta into + * the fw_id_to_link_sta pointer table. mac80211 can't stop + * A-MPDU and hence the timer continues to run. Then, the + * timer expires and sta is NULL. + */ + if (IS_ERR_OR_NULL(link_sta) || WARN_ON(!link_sta->sta)) + goto unlock; + + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + ieee80211_rx_ba_timer_expired(mld_sta->vif, link_sta->sta->addr, + ba_data->tid); +unlock: + rcu_read_unlock(); +} + +static int +iwl_mld_stop_ba_in_fw(struct iwl_mld *mld, struct ieee80211_sta *sta, int tid) +{ + struct iwl_rx_baid_cfg_cmd cmd = { + .action = cpu_to_le32(IWL_RX_BAID_ACTION_REMOVE), + .remove.sta_id_mask = + cpu_to_le32(iwl_mld_fw_sta_id_mask(mld, sta)), + .remove.tid = cpu_to_le32(tid), + + }; + int ret; + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + RX_BAID_ALLOCATION_CONFIG_CMD), + &cmd); + if (ret) + return ret; + + IWL_DEBUG_HT(mld, "RX BA Session stopped in fw\n"); + + return ret; +} + +static int +iwl_mld_start_ba_in_fw(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid, u16 ssn, u16 buf_size) +{ + struct iwl_rx_baid_cfg_cmd cmd = { + .action = cpu_to_le32(IWL_RX_BAID_ACTION_ADD), + .alloc.sta_id_mask = + cpu_to_le32(iwl_mld_fw_sta_id_mask(mld, sta)), + .alloc.tid = tid, + .alloc.ssn = cpu_to_le16(ssn), + .alloc.win_size = cpu_to_le16(buf_size), + }; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD), + .flags = CMD_WANT_SKB, + .len[0] = sizeof(cmd), + .data[0] = &cmd, + }; + struct iwl_rx_baid_cfg_resp *resp; + struct iwl_rx_packet *pkt; + u32 resp_len; + int ret, baid; + + BUILD_BUG_ON(sizeof(*resp) != sizeof(baid)); + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) + return ret; + + pkt = hcmd.resp_pkt; + + resp_len = iwl_rx_packet_payload_len(pkt); + if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp), + "BAID_ALLOC_CMD: unexpected response length %d\n", + resp_len)) { + ret = -EIO; + goto out; + } + + IWL_DEBUG_HT(mld, "RX BA Session started in fw\n"); + + resp = (void *)pkt->data; + baid = le32_to_cpu(resp->baid); + + if (IWL_FW_CHECK(mld, baid < 0 || baid >= ARRAY_SIZE(mld->fw_id_to_ba), + "BAID_ALLOC_CMD: invalid BAID response %d\n", baid)) { + ret = -EINVAL; + goto out; + } + + ret = baid; +out: + iwl_free_resp(&hcmd); + return ret; +} + +static void iwl_mld_init_reorder_buffer(struct iwl_mld *mld, + struct iwl_mld_baid_data *data, + u16 ssn) +{ + for (int i = 0; i < mld->trans->info.num_rxqs; i++) { + struct iwl_mld_reorder_buffer *reorder_buf = + &data->reorder_buf[i]; + struct iwl_mld_reorder_buf_entry *entries = + &data->entries[i * data->entries_per_queue]; + + reorder_buf->head_sn = ssn; + reorder_buf->queue = i; + + for (int j = 0; j < data->buf_size; j++) + __skb_queue_head_init(&entries[j].frames); + } +} + +static void iwl_mld_free_reorder_buffer(struct iwl_mld *mld, + struct iwl_mld_baid_data *data) +{ + struct iwl_mld_delba_data delba_data = { + .baid = data->baid, + }; + + iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_NOTIF_DEL_BA, + &delba_data, sizeof(delba_data)); + + for (int i = 0; i < mld->trans->info.num_rxqs; i++) { + struct iwl_mld_reorder_buffer *reorder_buf = + &data->reorder_buf[i]; + struct iwl_mld_reorder_buf_entry *entries = + &data->entries[i * data->entries_per_queue]; + + if (likely(!reorder_buf->num_stored)) + continue; + + /* This shouldn't happen in regular DELBA since the RX queues + * sync internal DELBA notification should trigger a release + * of all frames in the reorder buffer. + */ + WARN_ON(1); + + for (int j = 0; j < data->buf_size; j++) + __skb_queue_purge(&entries[j].frames); + } +} + +int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid, u16 ssn, u16 buf_size, u16 timeout) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_baid_data *baid_data = NULL; + u32 reorder_buf_size = buf_size * sizeof(baid_data->entries[0]); + int ret, baid; + u32 sta_mask; + + lockdep_assert_wiphy(mld->wiphy); + + if (mld->num_rx_ba_sessions >= IWL_MAX_BAID) { + IWL_DEBUG_HT(mld, + "Max num of RX BA sessions reached; blocking new session\n"); + return -ENOSPC; + } + + sta_mask = iwl_mld_fw_sta_id_mask(mld, sta); + if (WARN_ON(!sta_mask)) + return -EINVAL; + + /* sparse doesn't like the __align() so don't check */ +#ifndef __CHECKER__ + /* The division below will be OK if either the cache line size + * can be divided by the entry size (ALIGN will round up) or if + * the entry size can be divided by the cache line size, in which + * case the ALIGN() will do nothing. + */ + BUILD_BUG_ON(SMP_CACHE_BYTES % sizeof(baid_data->entries[0]) && + sizeof(baid_data->entries[0]) % SMP_CACHE_BYTES); +#endif + + /* Upward align the reorder buffer size to fill an entire cache + * line for each queue, to avoid sharing cache lines between + * different queues. + */ + reorder_buf_size = ALIGN(reorder_buf_size, SMP_CACHE_BYTES); + + /* Allocate here so if allocation fails we can bail out early + * before starting the BA session in the firmware + */ + baid_data = kzalloc(sizeof(*baid_data) + + mld->trans->info.num_rxqs * reorder_buf_size, + GFP_KERNEL); + if (!baid_data) + return -ENOMEM; + + /* This division is why we need the above BUILD_BUG_ON(), + * if that doesn't hold then this will not be right. + */ + baid_data->entries_per_queue = + reorder_buf_size / sizeof(baid_data->entries[0]); + + baid = iwl_mld_start_ba_in_fw(mld, sta, tid, ssn, buf_size); + if (baid < 0) { + ret = baid; + goto out_free; + } + + mld->num_rx_ba_sessions++; + mld_sta->tid_to_baid[tid] = baid; + + baid_data->baid = baid; + baid_data->mld = mld; + baid_data->tid = tid; + baid_data->buf_size = buf_size; + baid_data->sta_mask = sta_mask; + baid_data->timeout = timeout; + baid_data->last_rx_timestamp = jiffies; + baid_data->rcu_ptr = &mld->fw_id_to_ba[baid]; + + iwl_mld_init_reorder_buffer(mld, baid_data, ssn); + + timer_setup(&baid_data->session_timer, iwl_mld_rx_agg_session_expired, + 0); + if (timeout) + mod_timer(&baid_data->session_timer, + TU_TO_EXP_TIME(timeout * 2)); + + IWL_DEBUG_HT(mld, "STA mask=0x%x (tid=%d) is assigned to BAID %d\n", + baid_data->sta_mask, tid, baid); + + /* protect the BA data with RCU to cover a case where our + * internal RX sync mechanism will timeout (not that it's + * supposed to happen) and we will free the session data while + * RX is being processed in parallel + */ + WARN_ON(rcu_access_pointer(mld->fw_id_to_ba[baid])); + rcu_assign_pointer(mld->fw_id_to_ba[baid], baid_data); + + return 0; + +out_free: + kfree(baid_data); + return ret; +} + +int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + int baid = mld_sta->tid_to_baid[tid]; + struct iwl_mld_baid_data *baid_data; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* during firmware restart, do not send the command as the firmware no + * longer recognizes the session. instead, only clear the driver BA + * session data. + */ + if (!mld->fw_status.in_hw_restart) { + ret = iwl_mld_stop_ba_in_fw(mld, sta, tid); + if (ret) + return ret; + } + + if (!WARN_ON(mld->num_rx_ba_sessions == 0)) + mld->num_rx_ba_sessions--; + + baid_data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]); + if (WARN_ON(!baid_data)) + return -EINVAL; + + if (timer_pending(&baid_data->session_timer)) + timer_shutdown_sync(&baid_data->session_timer); + + iwl_mld_free_reorder_buffer(mld, baid_data); + + RCU_INIT_POINTER(mld->fw_id_to_ba[baid], NULL); + kfree_rcu(baid_data, rcu_head); + + IWL_DEBUG_HT(mld, "BAID %d is free\n", baid); + + return 0; +} + +int iwl_mld_update_sta_baids(struct iwl_mld *mld, + u32 old_sta_mask, + u32 new_sta_mask) +{ + struct iwl_rx_baid_cfg_cmd cmd = { + .action = cpu_to_le32(IWL_RX_BAID_ACTION_MODIFY), + .modify.old_sta_id_mask = cpu_to_le32(old_sta_mask), + .modify.new_sta_id_mask = cpu_to_le32(new_sta_mask), + }; + u32 cmd_id = WIDE_ID(DATA_PATH_GROUP, RX_BAID_ALLOCATION_CONFIG_CMD); + int baid; + + /* mac80211 will remove sessions later, but we ignore all that */ + if (mld->fw_status.in_hw_restart) + return 0; + + BUILD_BUG_ON(sizeof(struct iwl_rx_baid_cfg_resp) != sizeof(baid)); + + for (baid = 0; baid < ARRAY_SIZE(mld->fw_id_to_ba); baid++) { + struct iwl_mld_baid_data *data; + int ret; + + data = wiphy_dereference(mld->wiphy, mld->fw_id_to_ba[baid]); + if (!data) + continue; + + if (!(data->sta_mask & old_sta_mask)) + continue; + + WARN_ONCE(data->sta_mask != old_sta_mask, + "BAID data for %d corrupted - expected 0x%x found 0x%x\n", + baid, old_sta_mask, data->sta_mask); + + cmd.modify.tid = cpu_to_le32(data->tid); + + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd); + if (ret) + return ret; + data->sta_mask = new_sta_mask; + } + + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/agg.h b/drivers/net/wireless/intel/iwlwifi/mld/agg.h new file mode 100644 index 000000000000..651c80d1c7cd --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/agg.h @@ -0,0 +1,127 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_agg_h__ +#define __iwl_agg_h__ + +#include "mld.h" +#include "fw/api/rx.h" + +/** + * struct iwl_mld_reorder_buffer - per ra/tid/queue reorder buffer + * @head_sn: reorder window head sequence number + * @num_stored: number of MPDUs stored in the buffer + * @queue: queue of this reorder buffer + * @valid: true if reordering is valid for this queue + */ +struct iwl_mld_reorder_buffer { + u16 head_sn; + u16 num_stored; + int queue; + bool valid; +} ____cacheline_aligned_in_smp; + +/** + * struct iwl_mld_reorder_buf_entry - reorder buffer entry per-queue/per-seqno + * @frames: list of skbs stored. a list is necessary because in an A-MSDU, + * all sub-frames share the same sequence number, so they are stored + * together in the same list. + */ +struct iwl_mld_reorder_buf_entry { + struct sk_buff_head frames; +} +#ifndef __CHECKER__ +/* sparse doesn't like this construct: "bad integer constant expression" */ +__aligned(roundup_pow_of_two(sizeof(struct sk_buff_head))) +#endif +; + +/** + * struct iwl_mld_baid_data - Block Ack session data + * @rcu_head: RCU head for freeing this data + * @sta_mask: station mask for the BAID + * @tid: tid of the session + * @baid: baid of the session + * @buf_size: the reorder buffer size as set by the last ADDBA request + * @entries_per_queue: number of buffers per queue, this actually gets + * aligned up to avoid cache line sharing between queues + * @timeout: the timeout value specified in the ADDBA request. + * @last_rx_timestamp: timestamp of the last received packet (in jiffies). This + * value is updated only when the configured @timeout has passed since + * the last update to minimize cache bouncing between RX queues. + * @session_timer: timer is set to expire after 2 * @timeout (since we want + * to minimize the cache bouncing by updating @last_rx_timestamp only once + * after @timeout has passed). If no packets are received within this + * period, it informs mac80211 to initiate delBA flow, terminating the + * BA session. + * @rcu_ptr: BA data RCU protected access + * @mld: mld pointer, needed for timer context + * @reorder_buf: reorder buffer, allocated per queue + * @entries: data + */ +struct iwl_mld_baid_data { + struct rcu_head rcu_head; + u32 sta_mask; + u8 tid; + u8 baid; + u16 buf_size; + u16 entries_per_queue; + u16 timeout; + struct timer_list session_timer; + unsigned long last_rx_timestamp; + struct iwl_mld_baid_data __rcu **rcu_ptr; + struct iwl_mld *mld; + struct iwl_mld_reorder_buffer reorder_buf[IWL_MAX_RX_HW_QUEUES]; + struct iwl_mld_reorder_buf_entry entries[] ____cacheline_aligned_in_smp; +}; + +/** + * struct iwl_mld_delba_data - RX queue sync data for %IWL_MLD_RXQ_NOTIF_DEL_BA + * + * @baid: Block Ack id, used to identify the BA session to be removed + */ +struct iwl_mld_delba_data { + u32 baid; +} __packed; + +/** + * enum iwl_mld_reorder_result - Possible return values for iwl_mld_reorder() + * indicating how the caller should handle the skb based on the result. + * + * @IWL_MLD_PASS_SKB: skb should be passed to upper layer. + * @IWL_MLD_BUFFERED_SKB: skb has been buffered, don't pass it to upper layer. + * @IWL_MLD_DROP_SKB: skb should be dropped and freed by the caller. + */ +enum iwl_mld_reorder_result { + IWL_MLD_PASS_SKB, + IWL_MLD_BUFFERED_SKB, + IWL_MLD_DROP_SKB +}; + +int iwl_mld_ampdu_rx_start(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid, u16 ssn, u16 buf_size, u16 timeout); +int iwl_mld_ampdu_rx_stop(struct iwl_mld *mld, struct ieee80211_sta *sta, + int tid); + +enum iwl_mld_reorder_result +iwl_mld_reorder(struct iwl_mld *mld, struct napi_struct *napi, + int queue, struct ieee80211_sta *sta, + struct sk_buff *skb, struct iwl_rx_mpdu_desc *desc); + +void iwl_mld_handle_frame_release_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue); +void iwl_mld_handle_bar_frame_release_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, + int queue); + +void iwl_mld_del_ba(struct iwl_mld *mld, int queue, + struct iwl_mld_delba_data *data); + +int iwl_mld_update_sta_baids(struct iwl_mld *mld, + u32 old_sta_mask, + u32 new_sta_mask); + +#endif /* __iwl_agg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.c b/drivers/net/wireless/intel/iwlwifi/mld/ap.c new file mode 100644 index 000000000000..26511b49d89a --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include <linux/crc32.h> + +#include <net/mac80211.h> + +#include "ap.h" +#include "hcmd.h" +#include "tx.h" +#include "power.h" +#include "key.h" +#include "phy.h" +#include "iwl-utils.h" + +#include "fw/api/sta.h" + +void iwl_mld_set_tim_idx(struct iwl_mld *mld, __le32 *tim_index, + u8 *beacon, u32 frame_size) +{ + u32 tim_idx; + struct ieee80211_mgmt *mgmt = (void *)beacon; + + /* The index is relative to frame start but we start looking at the + * variable-length part of the beacon. + */ + tim_idx = mgmt->u.beacon.variable - beacon; + + /* Parse variable-length elements of beacon to find WLAN_EID_TIM */ + while ((tim_idx < (frame_size - 2)) && + (beacon[tim_idx] != WLAN_EID_TIM)) + tim_idx += beacon[tim_idx + 1] + 2; + + /* If TIM field was found, set variables */ + if ((tim_idx < (frame_size - 1)) && beacon[tim_idx] == WLAN_EID_TIM) + *tim_index = cpu_to_le32(tim_idx); + else + IWL_WARN(mld, "Unable to find TIM Element in beacon\n"); +} + +u8 iwl_mld_get_rate_flags(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + enum nl80211_band band) +{ + u32 legacy = link->beacon_tx_rate.control[band].legacy; + u32 rate_idx, rate_flags = 0, fw_rate; + + /* if beacon rate was configured try using it */ + if (hweight32(legacy) == 1) { + u32 rate = ffs(legacy) - 1; + struct ieee80211_supported_band *sband = + mld->hw->wiphy->bands[band]; + + rate_idx = sband->bitrates[rate].hw_value; + } else { + rate_idx = iwl_mld_get_lowest_rate(mld, info, vif); + } + + if (rate_idx <= IWL_LAST_CCK_RATE) + rate_flags = IWL_MAC_BEACON_CCK; + + /* Legacy rates are indexed as follows: + * 0 - 3 for CCK and 0 - 7 for OFDM. + */ + fw_rate = (rate_idx >= IWL_FIRST_OFDM_RATE ? + rate_idx - IWL_FIRST_OFDM_RATE : rate_idx); + + return fw_rate | rate_flags; +} + +int iwl_mld_send_beacon_template_cmd(struct iwl_mld *mld, + struct sk_buff *beacon, + struct iwl_mac_beacon_cmd *cmd) +{ + struct iwl_host_cmd hcmd = { + .id = BEACON_TEMPLATE_CMD, + }; + + hcmd.len[0] = sizeof(*cmd); + hcmd.data[0] = cmd; + + hcmd.len[1] = beacon->len; + hcmd.data[1] = beacon->data; + hcmd.dataflags[1] = IWL_HCMD_DFL_DUP; + + return iwl_mld_send_cmd(mld, &hcmd); +} + +static int iwl_mld_fill_beacon_template_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct sk_buff *beacon, + struct iwl_mac_beacon_cmd *cmd, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(beacon); + struct ieee80211_chanctx_conf *ctx; + bool enable_fils; + u16 flags = 0; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + cmd->link_id = cpu_to_le32(mld_link->fw_id); + + ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); + if (WARN_ON(!ctx || !ctx->def.chan)) + return -EINVAL; + + enable_fils = cfg80211_channel_is_psc(ctx->def.chan) || + (ctx->def.chan->band == NL80211_BAND_6GHZ && + ctx->def.width >= NL80211_CHAN_WIDTH_80); + + if (enable_fils) { + flags |= IWL_MAC_BEACON_FILS; + cmd->short_ssid = cpu_to_le32(~crc32_le(~0, vif->cfg.ssid, + vif->cfg.ssid_len)); + } + + cmd->byte_cnt = cpu_to_le16((u16)beacon->len); + + flags |= iwl_mld_get_rate_flags(mld, info, vif, link, + ctx->def.chan->band); + + cmd->flags = cpu_to_le16(flags); + + if (vif->type == NL80211_IFTYPE_AP) { + iwl_mld_set_tim_idx(mld, &cmd->tim_idx, + beacon->data, beacon->len); + + cmd->btwt_offset = + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_S1G_TWT, + beacon->len)); + } + + cmd->csa_offset = + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_CHANNEL_SWITCH, + beacon->len)); + cmd->ecsa_offset = + cpu_to_le32(iwl_find_ie_offset(beacon->data, + WLAN_EID_EXT_CHANSWITCH_ANN, + beacon->len)); + + return 0; +} + +/* The beacon template for the AP/GO/IBSS has changed and needs update */ +int iwl_mld_update_beacon_template(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mac_beacon_cmd cmd = {}; + struct sk_buff *beacon; + int ret; +#ifdef CONFIG_IWLWIFI_DEBUGFS + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); +#endif + + WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC); + + if (IWL_MLD_NON_TRANSMITTING_AP) + return 0; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (mld_vif->beacon_inject_active) { + IWL_DEBUG_INFO(mld, + "Can't update template, beacon injection's active\n"); + return -EBUSY; + } + +#endif + beacon = ieee80211_beacon_get_template(mld->hw, vif, NULL, + link_conf->link_id); + if (!beacon) + return -ENOMEM; + + ret = iwl_mld_fill_beacon_template_cmd(mld, vif, beacon, &cmd, + link_conf); + + if (!ret) + ret = iwl_mld_send_beacon_template_cmd(mld, beacon, &cmd); + + dev_kfree_skb(beacon); + + return ret; +} + +void iwl_mld_free_ap_early_key(struct iwl_mld *mld, + struct ieee80211_key_conf *key, + struct iwl_mld_vif *mld_vif) +{ + struct iwl_mld_link *link; + + if (WARN_ON(key->link_id < 0)) + return; + + link = iwl_mld_link_dereference_check(mld_vif, key->link_id); + if (WARN_ON(!link)) + return; + + for (int i = 0; i < ARRAY_SIZE(link->ap_early_keys); i++) { + if (link->ap_early_keys[i] != key) + continue; + /* Those weren't sent to FW, so should be marked as INVALID */ + if (WARN_ON(key->hw_key_idx != STA_KEY_IDX_INVALID)) + key->hw_key_idx = STA_KEY_IDX_INVALID; + link->ap_early_keys[i] = NULL; + } +} + +int iwl_mld_store_ap_early_key(struct iwl_mld *mld, + struct ieee80211_key_conf *key, + struct iwl_mld_vif *mld_vif) +{ + struct iwl_mld_link *link; + + if (WARN_ON(key->link_id < 0)) + return -EINVAL; + + link = iwl_mld_link_dereference_check(mld_vif, key->link_id); + if (WARN_ON(!link)) + return -EINVAL; + + for (int i = 0; i < ARRAY_SIZE(link->ap_early_keys); i++) { + if (!link->ap_early_keys[i]) { + link->ap_early_keys[i] = key; + return 0; + } + } + + return -ENOSPC; +} + +static int iwl_mld_send_ap_early_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + int ret = 0; + + if (WARN_ON(!link)) + return -EINVAL; + + for (int i = 0; i < ARRAY_SIZE(mld_link->ap_early_keys); i++) { + struct ieee80211_key_conf *key = mld_link->ap_early_keys[i]; + + if (!key) + continue; + + mld_link->ap_early_keys[i] = NULL; + + ret = iwl_mld_add_key(mld, vif, NULL, key); + if (ret) + break; + } + return ret; +} + +int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_chanctx_conf *ctx; + int ret; + + if (vif->type == NL80211_IFTYPE_AP) + iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); + + ret = iwl_mld_update_beacon_template(mld, vif, link); + if (ret) + return ret; + + /* the link should be already activated when assigning chan context, + * and LINK_CONTEXT_MODIFY_EHT_PARAMS is deprecated + */ + ret = iwl_mld_change_link_in_fw(mld, link, + LINK_CONTEXT_MODIFY_ALL & + ~(LINK_CONTEXT_MODIFY_ACTIVE | + LINK_CONTEXT_MODIFY_EHT_PARAMS)); + if (ret) + return ret; + + ret = iwl_mld_add_mcast_sta(mld, vif, link); + if (ret) + return ret; + + ret = iwl_mld_add_bcast_sta(mld, vif, link); + if (ret) + goto rm_mcast; + + /* Those keys were configured by the upper layers before starting the + * AP. Now that it is started and the bcast and mcast sta were added to + * the FW, we can add the keys too. + */ + ret = iwl_mld_send_ap_early_keys(mld, vif, link); + if (ret) + goto rm_bcast; + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) + iwl_mld_vif_update_low_latency(mld, vif, true, + LOW_LATENCY_VIF_TYPE); + + mld_vif->ap_ibss_active = true; + + if (vif->p2p && mld->p2p_device_vif) + return iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, + FW_CTXT_ACTION_MODIFY); + + /* When the channel context was added, the link is not yet active, so + * min_def is always used. Update the PHY again here in case def should + * actually be used. + */ + ctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); + iwl_mld_update_phy_chandef(mld, ctx); + + return 0; +rm_bcast: + iwl_mld_remove_bcast_sta(mld, vif, link); +rm_mcast: + iwl_mld_remove_mcast_sta(mld, vif, link); + return ret; +} + +void iwl_mld_stop_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + mld_vif->ap_ibss_active = false; + + if (vif->p2p && mld->p2p_device_vif) + iwl_mld_mac_fw_action(mld, mld->p2p_device_vif, + FW_CTXT_ACTION_MODIFY); + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) + iwl_mld_vif_update_low_latency(mld, vif, false, + LOW_LATENCY_VIF_TYPE); + + iwl_mld_remove_bcast_sta(mld, vif, link); + + iwl_mld_remove_mcast_sta(mld, vif, link); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ap.h b/drivers/net/wireless/intel/iwlwifi/mld/ap.h new file mode 100644 index 000000000000..4a6f52b9552d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ap.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_ap_h__ +#define __iwl_ap_h__ + +#include "mld.h" +#include "iface.h" + +#include "fw/api/tx.h" + +int iwl_mld_update_beacon_template(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf); + +int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +void iwl_mld_stop_ap_ibss(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_store_ap_early_key(struct iwl_mld *mld, + struct ieee80211_key_conf *key, + struct iwl_mld_vif *mld_vif); + +void iwl_mld_free_ap_early_key(struct iwl_mld *mld, + struct ieee80211_key_conf *key, + struct iwl_mld_vif *mld_vif); + +u8 iwl_mld_get_rate_flags(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + enum nl80211_band band); + +void iwl_mld_set_tim_idx(struct iwl_mld *mld, __le32 *tim_index, + u8 *beacon, u32 frame_size); + +int iwl_mld_send_beacon_template_cmd(struct iwl_mld *mld, + struct sk_buff *beacon, + struct iwl_mac_beacon_cmd *cmd); + +#endif /* __iwl_ap_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.c b/drivers/net/wireless/intel/iwlwifi/mld/coex.c new file mode 100644 index 000000000000..32c727b3b391 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.c @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "fw/api/coex.h" + +#include "coex.h" +#include "mld.h" +#include "hcmd.h" +#include "mlo.h" + +int iwl_mld_send_bt_init_conf(struct iwl_mld *mld) +{ + struct iwl_bt_coex_cmd cmd = { + .mode = cpu_to_le32(BT_COEX_NW), + .enabled_modules = cpu_to_le32(BT_COEX_MPLUT_ENABLED | + BT_COEX_HIGH_BAND_RET), + }; + + return iwl_mld_send_cmd_pdu(mld, BT_CONFIG, &cmd); +} + +void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_bt_coex_profile_notif *notif = (const void *)pkt->data; + const struct iwl_bt_coex_profile_notif zero_notif = {}; + /* zeroed structure means that BT is OFF */ + bool bt_is_active = memcmp(notif, &zero_notif, sizeof(*notif)); + + mld->last_bt_notif = *notif; + IWL_DEBUG_INFO(mld, "BT was turned %s\n", bt_is_active ? "ON" : "OFF"); + + iwl_mld_emlsr_check_bt(mld); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/coex.h b/drivers/net/wireless/intel/iwlwifi/mld/coex.h new file mode 100644 index 000000000000..a77c5dc9613c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/coex.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_coex_h__ +#define __iwl_mld_coex_h__ + +#include "mld.h" + +int iwl_mld_send_bt_init_conf(struct iwl_mld *mld); + +void iwl_mld_handle_bt_coex_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +#endif /* __iwl_mld_coex_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/constants.h b/drivers/net/wireless/intel/iwlwifi/mld/constants.h new file mode 100644 index 000000000000..2a59b29b75cb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/constants.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_constants_h__ +#define __iwl_mld_constants_h__ + +#define IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD 4 +#define IWL_MLD_MISSED_BEACONS_THRESHOLD 8 +#define IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG 19 +#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS 5 +#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH 15 +#define IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED 11 +#define IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH -72 + +#define IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT (100 * USEC_PER_MSEC) +#define IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT (100 * USEC_PER_MSEC) +#define IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT (10 * USEC_PER_MSEC) +#define IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT (10 * USEC_PER_MSEC) +#define IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT (2 * 1024) /* defined in TU */ +#define IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT (40 * 1024) /* defined in TU */ + +#define IWL_MLD_UAPSD_RX_DATA_TIMEOUT (50 * USEC_PER_MSEC) +#define IWL_MLD_UAPSD_TX_DATA_TIMEOUT (50 * USEC_PER_MSEC) + +#define IWL_MLD_PS_SNOOZE_INTERVAL 25 +#define IWL_MLD_PS_SNOOZE_INTERVAL 25 +#define IWL_MLD_PS_SNOOZE_WINDOW 50 + +#define IWL_MLD_PS_SNOOZE_HEAVY_TX_THLD_PACKETS 30 +#define IWL_MLD_PS_SNOOZE_HEAVY_RX_THLD_PACKETS 20 + +#define IWL_MLD_PS_HEAVY_TX_THLD_PERCENT 50 +#define IWL_MLD_PS_HEAVY_RX_THLD_PERCENT 50 +#define IWL_MLD_PS_HEAVY_TX_THLD_PACKETS 20 +#define IWL_MLD_PS_HEAVY_RX_THLD_PACKETS 8 + +#define IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC 30 +#define IWL_MLD_SCAN_EXPIRE_TIME_SEC 20 + +#define IWL_MLD_TPT_COUNT_WINDOW (5 * HZ) + +/* OMI reduced BW thresholds (channel load percentage) */ +#define IWL_MLD_OMI_ENTER_CHAN_LOAD 10 +#define IWL_MLD_OMI_EXIT_CHAN_LOAD_160 20 +#define IWL_MLD_OMI_EXIT_CHAN_LOAD_320 30 +/* time (in milliseconds) to let AP "settle" the OMI */ +#define IWL_MLD_OMI_AP_SETTLE_DELAY 27 +/* time (in milliseconds) to not enter OMI reduced BW after leaving */ +#define IWL_MLD_OMI_EXIT_PROTECTION 5000 + +#define IWL_MLD_DIS_RANDOM_FW_ID false +#define IWL_MLD_D3_DEBUG false +#define IWL_MLD_NON_TRANSMITTING_AP false +#define IWL_MLD_6GHZ_PASSIVE_SCAN_TIMEOUT 3000 /* in seconds */ +#define IWL_MLD_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT 60 /* in seconds */ +#define IWL_MLD_CONN_LISTEN_INTERVAL 10 +#define IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE 0 +#define IWL_MLD_AUTO_EML_ENABLE true + +#define IWL_MLD_HIGH_RSSI_THRESH_20MHZ -67 +#define IWL_MLD_LOW_RSSI_THRESH_20MHZ -72 +#define IWL_MLD_HIGH_RSSI_THRESH_40MHZ -64 +#define IWL_MLD_LOW_RSSI_THRESH_40MHZ -72 +#define IWL_MLD_HIGH_RSSI_THRESH_80MHZ -61 +#define IWL_MLD_LOW_RSSI_THRESH_80MHZ -72 +#define IWL_MLD_HIGH_RSSI_THRESH_160MHZ -58 +#define IWL_MLD_LOW_RSSI_THRESH_160MHZ -72 + +#define IWL_MLD_ENTER_EMLSR_TPT_THRESH 400 +#define IWL_MLD_EXIT_EMLSR_CHAN_LOAD 2 /* in percentage */ + +#define IWL_MLD_FTM_INITIATOR_ALGO IWL_TOF_ALGO_TYPE_MAX_LIKE +#define IWL_MLD_FTM_INITIATOR_DYNACK true +#define IWL_MLD_FTM_LMR_FEEDBACK_TERMINATE false +#define IWL_MLD_FTM_TEST_INCORRECT_SAC false +#define IWL_MLD_FTM_R2I_MAX_REP 7 +#define IWL_MLD_FTM_I2R_MAX_REP 7 +#define IWL_MLD_FTM_R2I_MAX_STS 1 +#define IWL_MLD_FTM_I2R_MAX_STS 1 +#define IWL_MLD_FTM_R2I_MAX_TOTAL_LTF 3 +#define IWL_MLD_FTM_I2R_MAX_TOTAL_LTF 3 +#define IWL_MLD_FTM_RESP_NDP_SUPPORT true +#define IWL_MLD_FTM_RESP_LMR_FEEDBACK_SUPPORT true +#define IWL_MLD_FTM_NON_TB_MIN_TIME_BETWEEN_MSR 7 +#define IWL_MLD_FTM_NON_TB_MAX_TIME_BETWEEN_MSR 1000 + +#endif /* __iwl_mld_constants_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.c b/drivers/net/wireless/intel/iwlwifi/mld/d3.c new file mode 100644 index 000000000000..339b148d6793 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.c @@ -0,0 +1,1998 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "mld.h" + +#include "d3.h" +#include "power.h" +#include "hcmd.h" +#include "iface.h" +#include "mcc.h" +#include "sta.h" +#include "mlo.h" + +#include "fw/api/d3.h" +#include "fw/api/offload.h" +#include "fw/api/sta.h" +#include "fw/dbg.h" + +#include <net/ipv6.h> +#include <net/addrconf.h> +#include <linux/bitops.h> + +/** + * enum iwl_mld_d3_notif - d3 notifications + * @IWL_D3_NOTIF_WOWLAN_INFO: WOWLAN_INFO_NOTIF is expected/was received + * @IWL_D3_NOTIF_WOWLAN_WAKE_PKT: WOWLAN_WAKE_PKT_NOTIF is expected/was received + * @IWL_D3_NOTIF_PROT_OFFLOAD: PROT_OFFLOAD_NOTIF is expected/was received + * @IWL_D3_ND_MATCH_INFO: OFFLOAD_MATCH_INFO_NOTIF is expected/was received + * @IWL_D3_NOTIF_D3_END_NOTIF: D3_END_NOTIF is expected/was received + */ +enum iwl_mld_d3_notif { + IWL_D3_NOTIF_WOWLAN_INFO = BIT(0), + IWL_D3_NOTIF_WOWLAN_WAKE_PKT = BIT(1), + IWL_D3_NOTIF_PROT_OFFLOAD = BIT(2), + IWL_D3_ND_MATCH_INFO = BIT(3), + IWL_D3_NOTIF_D3_END_NOTIF = BIT(4) +}; + +struct iwl_mld_resume_key_iter_data { + struct iwl_mld *mld; + struct iwl_mld_wowlan_status *wowlan_status; + u32 num_keys, gtk_cipher, igtk_cipher, bigtk_cipher; + bool unhandled_cipher; +}; + +struct iwl_mld_suspend_key_iter_data { + struct iwl_wowlan_rsc_tsc_params_cmd *rsc; + bool have_rsc; + int gtks; + int found_gtk_idx[4]; + __le32 gtk_cipher; + __le32 igtk_cipher; + __le32 bigtk_cipher; +}; + +struct iwl_mld_mcast_key_data { + u8 key[WOWLAN_KEY_MAX_SIZE]; + u8 len; + u8 flags; + u8 id; + union { + struct { + struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT]; + struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT]; + } gtk; + struct { + struct ieee80211_key_seq cmac_gmac_seq; + } igtk_bigtk; + }; + +}; + +/** + * struct iwl_mld_wowlan_status - contains wowlan status data from + * all wowlan notifications + * @wakeup_reasons: wakeup reasons, see &enum iwl_wowlan_wakeup_reason + * @replay_ctr: GTK rekey replay counter + * @pattern_number: number of the matched patterns on packets + * @last_qos_seq: QoS sequence counter of offloaded tid + * @num_of_gtk_rekeys: number of GTK rekeys during D3 + * @tid_offloaded_tx: tid used by the firmware to transmit data packets + * while in wowlan + * @wake_packet: wakeup packet received + * @wake_packet_length: wake packet length + * @wake_packet_bufsize: wake packet bufsize + * @gtk: data of the last two used gtk's by the FW upon resume + * @igtk: data of the last used igtk by the FW upon resume + * @bigtk: data of the last two used gtk's by the FW upon resume + * @ptk: last seq numbers per tid passed by the FW, + * holds both in tkip and aes formats + */ +struct iwl_mld_wowlan_status { + u32 wakeup_reasons; + u64 replay_ctr; + u16 pattern_number; + u16 last_qos_seq; + u32 num_of_gtk_rekeys; + u8 tid_offloaded_tx; + u8 *wake_packet; + u32 wake_packet_length; + u32 wake_packet_bufsize; + struct iwl_mld_mcast_key_data gtk[WOWLAN_GTK_KEYS_NUM]; + struct iwl_mld_mcast_key_data igtk; + struct iwl_mld_mcast_key_data bigtk[WOWLAN_BIGTK_KEYS_NUM]; + struct { + struct ieee80211_key_seq aes_seq[IWL_MAX_TID_COUNT]; + struct ieee80211_key_seq tkip_seq[IWL_MAX_TID_COUNT]; + + } ptk; +}; + +#define NETDETECT_QUERY_BUF_LEN \ + (sizeof(struct iwl_scan_offload_profile_match) * \ + IWL_SCAN_MAX_PROFILES_V2) + +/** + * struct iwl_mld_netdetect_res - contains netdetect results from + * match_info_notif + * @matched_profiles: bitmap of matched profiles, referencing the + * matches passed in the scan offload request + * @matches: array of match information, one for each match + */ +struct iwl_mld_netdetect_res { + u32 matched_profiles; + u8 matches[NETDETECT_QUERY_BUF_LEN]; +}; + +/** + * struct iwl_mld_resume_data - d3 resume flow data + * @notifs_expected: bitmap of expected notifications from fw, + * see &enum iwl_mld_d3_notif + * @notifs_received: bitmap of received notifications from fw, + * see &enum iwl_mld_d3_notif + * @d3_end_flags: bitmap of flags from d3_end_notif + * @notif_handling_err: error handling one of the resume notifications + * @wowlan_status: wowlan status data from all wowlan notifications + * @netdetect_res: contains netdetect results from match_info_notif + */ +struct iwl_mld_resume_data { + u32 notifs_expected; + u32 notifs_received; + u32 d3_end_flags; + bool notif_handling_err; + struct iwl_mld_wowlan_status *wowlan_status; + struct iwl_mld_netdetect_res *netdetect_res; +}; + +#define IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT \ + (IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET | \ + IWL_WOWLAN_WAKEUP_BY_PATTERN | \ + IWL_WAKEUP_BY_PATTERN_IPV4_TCP_SYN |\ + IWL_WAKEUP_BY_PATTERN_IPV4_TCP_SYN_WILDCARD |\ + IWL_WAKEUP_BY_PATTERN_IPV6_TCP_SYN |\ + IWL_WAKEUP_BY_PATTERN_IPV6_TCP_SYN_WILDCARD) + +#define IWL_WOWLAN_OFFLOAD_TID 0 + +void iwl_mld_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; + + lockdep_assert_wiphy(mld->wiphy); + + wowlan_data->rekey_data.kek_len = data->kek_len; + wowlan_data->rekey_data.kck_len = data->kck_len; + memcpy(wowlan_data->rekey_data.kek, data->kek, data->kek_len); + memcpy(wowlan_data->rekey_data.kck, data->kck, data->kck_len); + wowlan_data->rekey_data.akm = data->akm & 0xFF; + wowlan_data->rekey_data.replay_ctr = + cpu_to_le64(be64_to_cpup((const __be64 *)data->replay_ctr)); + wowlan_data->rekey_data.valid = true; +} + +#if IS_ENABLED(CONFIG_IPV6) +void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; + struct inet6_ifaddr *ifa; + int idx = 0; + + memset(wowlan_data->tentative_addrs, 0, + sizeof(wowlan_data->tentative_addrs)); + + read_lock_bh(&idev->lock); + list_for_each_entry(ifa, &idev->addr_list, if_list) { + wowlan_data->target_ipv6_addrs[idx] = ifa->addr; + if (ifa->flags & IFA_F_TENTATIVE) + __set_bit(idx, wowlan_data->tentative_addrs); + idx++; + if (idx >= IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX) + break; + } + read_unlock_bh(&idev->lock); + + wowlan_data->num_target_ipv6_addrs = idx; +} +#endif + +enum rt_status { + FW_ALIVE, + FW_NEEDS_RESET, + FW_ERROR, +}; + +static enum rt_status iwl_mld_check_err_tables(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + u32 err_id; + + /* check for lmac1 error */ + if (iwl_fwrt_read_err_table(mld->trans, + mld->trans->dbg.lmac_error_event_table[0], + &err_id)) { + if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) { + struct cfg80211_wowlan_wakeup wakeup = { + .rfkill_release = true, + }; + ieee80211_report_wowlan_wakeup(vif, &wakeup, + GFP_KERNEL); + + return FW_NEEDS_RESET; + } + return FW_ERROR; + } + + /* check if we have lmac2 set and check for error */ + if (iwl_fwrt_read_err_table(mld->trans, + mld->trans->dbg.lmac_error_event_table[1], + NULL)) + return FW_ERROR; + + /* check for umac error */ + if (iwl_fwrt_read_err_table(mld->trans, + mld->trans->dbg.umac_error_event_table, + NULL)) + return FW_ERROR; + + return FW_ALIVE; +} + +static bool iwl_mld_fw_needs_restart(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + enum rt_status rt_status = iwl_mld_check_err_tables(mld, vif); + + if (rt_status == FW_ALIVE) + return false; + + if (rt_status == FW_ERROR) { + IWL_ERR(mld, "FW Error occurred during suspend\n"); + iwl_fwrt_dump_error_logs(&mld->fwrt); + iwl_dbg_tlv_time_point(&mld->fwrt, + IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); + } + + return true; +} + +static int +iwl_mld_netdetect_config(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct cfg80211_wowlan *wowlan) +{ + int ret; + struct cfg80211_sched_scan_request *netdetect_cfg = + wowlan->nd_config; + struct ieee80211_scan_ies ies = {}; + + ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_SCHED, true); + if (ret) + return ret; + + ret = iwl_mld_sched_scan_start(mld, vif, netdetect_cfg, &ies, + IWL_MLD_SCAN_NETDETECT); + return ret; +} + +static void +iwl_mld_le64_to_tkip_seq(__le64 le_pn, struct ieee80211_key_seq *seq) +{ + u64 pn = le64_to_cpu(le_pn); + + seq->tkip.iv16 = (u16)pn; + seq->tkip.iv32 = (u32)(pn >> 16); +} + +static void +iwl_mld_le64_to_aes_seq(__le64 le_pn, struct ieee80211_key_seq *seq) +{ + u64 pn = le64_to_cpu(le_pn); + + seq->ccmp.pn[0] = pn >> 40; + seq->ccmp.pn[1] = pn >> 32; + seq->ccmp.pn[2] = pn >> 24; + seq->ccmp.pn[3] = pn >> 16; + seq->ccmp.pn[4] = pn >> 8; + seq->ccmp.pn[5] = pn; +} + +static void +iwl_mld_convert_gtk_resume_seq(struct iwl_mld_mcast_key_data *gtk_data, + const struct iwl_wowlan_all_rsc_tsc_v5 *sc, + int rsc_idx) +{ + struct ieee80211_key_seq *aes_seq = gtk_data->gtk.aes_seq; + struct ieee80211_key_seq *tkip_seq = gtk_data->gtk.tkip_seq; + + if (rsc_idx >= ARRAY_SIZE(sc->mcast_rsc)) + return; + + /* We store both the TKIP and AES representations coming from the + * FW because we decode the data from there before we iterate + * the keys and know which type is used. + */ + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + iwl_mld_le64_to_tkip_seq(sc->mcast_rsc[rsc_idx][tid], + &tkip_seq[tid]); + iwl_mld_le64_to_aes_seq(sc->mcast_rsc[rsc_idx][tid], + &aes_seq[tid]); + } +} + +static void +iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + const struct iwl_wowlan_gtk_status_v3 *gtk_data, + const struct iwl_wowlan_all_rsc_tsc_v5 *sc) +{ + int status_idx = 0; + + BUILD_BUG_ON(sizeof(wowlan_status->gtk[0].key) < + sizeof(gtk_data[0].key)); + BUILD_BUG_ON(ARRAY_SIZE(wowlan_status->gtk) < WOWLAN_GTK_KEYS_NUM); + + for (int notif_idx = 0; notif_idx < ARRAY_SIZE(wowlan_status->gtk); + notif_idx++) { + int rsc_idx; + + if (!(gtk_data[notif_idx].key_len)) + continue; + + wowlan_status->gtk[status_idx].len = + gtk_data[notif_idx].key_len; + wowlan_status->gtk[status_idx].flags = + gtk_data[notif_idx].key_flags; + wowlan_status->gtk[status_idx].id = + wowlan_status->gtk[status_idx].flags & + IWL_WOWLAN_GTK_IDX_MASK; + memcpy(wowlan_status->gtk[status_idx].key, + gtk_data[notif_idx].key, + sizeof(gtk_data[notif_idx].key)); + + /* The rsc for both gtk keys are stored in gtk[0]->sc->mcast_rsc + * The gtk ids can be any two numbers between 0 and 3, + * the id_map maps between the key id and the index in sc->mcast + */ + rsc_idx = + sc->mcast_key_id_map[wowlan_status->gtk[status_idx].id]; + iwl_mld_convert_gtk_resume_seq(&wowlan_status->gtk[status_idx], + sc, rsc_idx); + + /* if it's as long as the TKIP encryption key, copy MIC key */ + if (wowlan_status->gtk[status_idx].len == + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY) + memcpy(wowlan_status->gtk[status_idx].key + + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + gtk_data[notif_idx].tkip_mic_key, + sizeof(gtk_data[notif_idx].tkip_mic_key)); + status_idx++; + } +} + +static void +iwl_mld_convert_ptk_resume_seq(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + const struct iwl_wowlan_all_rsc_tsc_v5 *sc) +{ + struct ieee80211_key_seq *aes_seq = wowlan_status->ptk.aes_seq; + struct ieee80211_key_seq *tkip_seq = wowlan_status->ptk.tkip_seq; + + BUILD_BUG_ON(ARRAY_SIZE(sc->ucast_rsc) != IWL_MAX_TID_COUNT); + + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + iwl_mld_le64_to_aes_seq(sc->ucast_rsc[tid], &aes_seq[tid]); + iwl_mld_le64_to_tkip_seq(sc->ucast_rsc[tid], &tkip_seq[tid]); + } +} + +static void +iwl_mld_convert_mcast_ipn(struct iwl_mld_mcast_key_data *key_status, + const struct iwl_wowlan_igtk_status *key) +{ + struct ieee80211_key_seq *seq = + &key_status->igtk_bigtk.cmac_gmac_seq; + u8 ipn_len = ARRAY_SIZE(key->ipn); + + BUILD_BUG_ON(ipn_len != ARRAY_SIZE(seq->aes_gmac.pn)); + BUILD_BUG_ON(ipn_len != ARRAY_SIZE(seq->aes_cmac.pn)); + BUILD_BUG_ON(offsetof(struct ieee80211_key_seq, aes_gmac) != + offsetof(struct ieee80211_key_seq, aes_cmac)); + + /* mac80211 expects big endian for memcmp() to work, convert. + * We don't have the key cipher yet so copy to both to cmac and gmac + */ + for (int i = 0; i < ipn_len; i++) { + seq->aes_gmac.pn[i] = key->ipn[ipn_len - i - 1]; + seq->aes_cmac.pn[i] = key->ipn[ipn_len - i - 1]; + } +} + +static void +iwl_mld_convert_igtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status, + const struct iwl_wowlan_igtk_status *igtk) +{ + BUILD_BUG_ON(sizeof(wowlan_status->igtk.key) < sizeof(igtk->key)); + + if (!igtk->key_len) + return; + + wowlan_status->igtk.len = igtk->key_len; + wowlan_status->igtk.flags = igtk->key_flags; + wowlan_status->igtk.id = + u32_get_bits(igtk->key_flags, + IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) + + WOWLAN_IGTK_MIN_INDEX; + + memcpy(wowlan_status->igtk.key, igtk->key, sizeof(igtk->key)); + iwl_mld_convert_mcast_ipn(&wowlan_status->igtk, igtk); +} + +static void +iwl_mld_convert_bigtk_resume_data(struct iwl_mld_wowlan_status *wowlan_status, + const struct iwl_wowlan_igtk_status *bigtk) +{ + int status_idx = 0; + + BUILD_BUG_ON(ARRAY_SIZE(wowlan_status->bigtk) < WOWLAN_BIGTK_KEYS_NUM); + + for (int notif_idx = 0; notif_idx < WOWLAN_BIGTK_KEYS_NUM; + notif_idx++) { + if (!bigtk[notif_idx].key_len) + continue; + + wowlan_status->bigtk[status_idx].len = bigtk[notif_idx].key_len; + wowlan_status->bigtk[status_idx].flags = + bigtk[notif_idx].key_flags; + wowlan_status->bigtk[status_idx].id = + u32_get_bits(bigtk[notif_idx].key_flags, + IWL_WOWLAN_IGTK_BIGTK_IDX_MASK) + + WOWLAN_BIGTK_MIN_INDEX; + + BUILD_BUG_ON(sizeof(wowlan_status->bigtk[status_idx].key) < + sizeof(bigtk[notif_idx].key)); + memcpy(wowlan_status->bigtk[status_idx].key, + bigtk[notif_idx].key, sizeof(bigtk[notif_idx].key)); + iwl_mld_convert_mcast_ipn(&wowlan_status->bigtk[status_idx], + &bigtk[notif_idx]); + status_idx++; + } +} + +static bool +iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + struct iwl_rx_packet *pkt) +{ + const struct iwl_wowlan_info_notif *notif = (void *)pkt->data; + u32 expected_len, len = iwl_rx_packet_payload_len(pkt); + + expected_len = sizeof(*notif); + + if (IWL_FW_CHECK(mld, len < expected_len, + "Invalid wowlan_info_notif (expected=%ud got=%ud)\n", + expected_len, len)) + return true; + + if (IWL_FW_CHECK(mld, notif->tid_offloaded_tx != IWL_WOWLAN_OFFLOAD_TID, + "Invalid tid_offloaded_tx %d\n", + wowlan_status->tid_offloaded_tx)) + return true; + + iwl_mld_convert_gtk_resume_data(mld, wowlan_status, notif->gtk, + ¬if->gtk[0].sc); + iwl_mld_convert_ptk_resume_seq(mld, wowlan_status, ¬if->gtk[0].sc); + /* only one igtk is passed by FW */ + iwl_mld_convert_igtk_resume_data(wowlan_status, ¬if->igtk[0]); + iwl_mld_convert_bigtk_resume_data(wowlan_status, notif->bigtk); + + wowlan_status->replay_ctr = le64_to_cpu(notif->replay_ctr); + wowlan_status->pattern_number = le16_to_cpu(notif->pattern_number); + + wowlan_status->tid_offloaded_tx = notif->tid_offloaded_tx; + wowlan_status->last_qos_seq = le16_to_cpu(notif->qos_seq_ctr); + wowlan_status->num_of_gtk_rekeys = + le32_to_cpu(notif->num_of_gtk_rekeys); + wowlan_status->wakeup_reasons = le32_to_cpu(notif->wakeup_reasons); + return false; + /* TODO: mlo_links (task=MLO)*/ +} + +static bool +iwl_mld_handle_wake_pkt_notif(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + struct iwl_rx_packet *pkt) +{ + const struct iwl_wowlan_wake_pkt_notif *notif = (void *)pkt->data; + u32 actual_size, len = iwl_rx_packet_payload_len(pkt); + u32 expected_size = le32_to_cpu(notif->wake_packet_length); + + if (IWL_FW_CHECK(mld, len < sizeof(*notif), + "Invalid WoWLAN wake packet notification (expected size=%zu got=%u)\n", + sizeof(*notif), len)) + return true; + + if (IWL_FW_CHECK(mld, !(wowlan_status->wakeup_reasons & + IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT), + "Got wake packet but wakeup reason is %x\n", + wowlan_status->wakeup_reasons)) + return true; + + actual_size = len - offsetof(struct iwl_wowlan_wake_pkt_notif, + wake_packet); + + /* actual_size got the padding from the notification, remove it. */ + if (expected_size < actual_size) + actual_size = expected_size; + wowlan_status->wake_packet = kmemdup(notif->wake_packet, actual_size, + GFP_ATOMIC); + if (!wowlan_status->wake_packet) + return true; + + wowlan_status->wake_packet_length = expected_size; + wowlan_status->wake_packet_bufsize = actual_size; + + return false; +} + +static void +iwl_mld_set_wake_packet(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct iwl_mld_wowlan_status *wowlan_status, + struct cfg80211_wowlan_wakeup *wakeup, + struct sk_buff **_pkt) +{ + int pkt_bufsize = wowlan_status->wake_packet_bufsize; + int expected_pktlen = wowlan_status->wake_packet_length; + const u8 *pktdata = wowlan_status->wake_packet; + const struct ieee80211_hdr *hdr = (const void *)pktdata; + int truncated = expected_pktlen - pkt_bufsize; + + if (ieee80211_is_data(hdr->frame_control)) { + int hdrlen = ieee80211_hdrlen(hdr->frame_control); + int ivlen = 0, icvlen = 4; /* also FCS */ + + struct sk_buff *pkt = alloc_skb(pkt_bufsize, GFP_KERNEL); + *_pkt = pkt; + if (!pkt) + return; + + skb_put_data(pkt, pktdata, hdrlen); + pktdata += hdrlen; + pkt_bufsize -= hdrlen; + + /* if truncated, FCS/ICV is (partially) gone */ + if (truncated >= icvlen) { + truncated -= icvlen; + icvlen = 0; + } else { + icvlen -= truncated; + truncated = 0; + } + + pkt_bufsize -= ivlen + icvlen; + pktdata += ivlen; + + skb_put_data(pkt, pktdata, pkt_bufsize); + + if (ieee80211_data_to_8023(pkt, vif->addr, vif->type)) + return; + wakeup->packet = pkt->data; + wakeup->packet_present_len = pkt->len; + wakeup->packet_len = pkt->len - truncated; + wakeup->packet_80211 = false; + } else { + int fcslen = 4; + + if (truncated >= 4) { + truncated -= 4; + fcslen = 0; + } else { + fcslen -= truncated; + truncated = 0; + } + pkt_bufsize -= fcslen; + wakeup->packet = wowlan_status->wake_packet; + wakeup->packet_present_len = pkt_bufsize; + wakeup->packet_len = expected_pktlen - truncated; + wakeup->packet_80211 = true; + } +} + +static void +iwl_mld_report_wowlan_wakeup(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status) +{ + struct sk_buff *pkt = NULL; + struct cfg80211_wowlan_wakeup wakeup = { + .pattern_idx = -1, + }; + u32 reasons = wowlan_status->wakeup_reasons; + + if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { + ieee80211_report_wowlan_wakeup(vif, NULL, GFP_KERNEL); + return; + } + + pm_wakeup_event(mld->dev, 0); + + if (reasons & IWL_WOWLAN_WAKEUP_BY_MAGIC_PACKET) + wakeup.magic_pkt = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) + wakeup.pattern_idx = + wowlan_status->pattern_number; + + if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH | + IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)) + wakeup.disconnect = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE) + wakeup.gtk_rekey_failure = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) + wakeup.rfkill_release = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_EAPOL_REQUEST) + wakeup.eap_identity_req = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_FOUR_WAY_HANDSHAKE) + wakeup.four_way_handshake = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_LINK_LOSS) + wakeup.tcp_connlost = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_SIGNATURE_TABLE) + wakeup.tcp_nomoretokens = true; + + if (reasons & IWL_WOWLAN_WAKEUP_BY_REM_WAKE_WAKEUP_PACKET) + wakeup.tcp_match = true; + + if (reasons & IWL_WAKEUP_BY_11W_UNPROTECTED_DEAUTH_OR_DISASSOC) + wakeup.unprot_deauth_disassoc = true; + + if (wowlan_status->wake_packet) + iwl_mld_set_wake_packet(mld, vif, wowlan_status, &wakeup, &pkt); + + ieee80211_report_wowlan_wakeup(vif, &wakeup, GFP_KERNEL); + kfree_skb(pkt); +} + +static void +iwl_mld_set_key_rx_seq_tids(struct ieee80211_key_conf *key, + struct ieee80211_key_seq *seq) +{ + int tid; + + for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) + ieee80211_set_key_rx_seq(key, tid, &seq[tid]); +} + +static void +iwl_mld_set_key_rx_seq(struct ieee80211_key_conf *key, + struct iwl_mld_mcast_key_data *key_data) +{ + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + iwl_mld_set_key_rx_seq_tids(key, + key_data->gtk.aes_seq); + break; + case WLAN_CIPHER_SUITE_TKIP: + iwl_mld_set_key_rx_seq_tids(key, + key_data->gtk.tkip_seq); + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + /* igtk/bigtk ciphers*/ + ieee80211_set_key_rx_seq(key, 0, + &key_data->igtk_bigtk.cmac_gmac_seq); + break; + default: + WARN_ON(1); + } +} + +static void +iwl_mld_d3_update_mcast_key(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status, + struct ieee80211_key_conf *key, + struct iwl_mld_mcast_key_data *key_data) +{ + if (key->keyidx != key_data->id && + (key->keyidx < 4 || key->keyidx > 5)) { + IWL_ERR(mld, + "Unexpected keyId mismatch. Old keyId:%d, New keyId:%d\n", + key->keyidx, key_data->id); + return; + } + + /* All installed keys are sent by the FW, even weren't + * rekeyed during D3. + * We remove an existing key if it has the same index as + * a new key and a rekey has occurred during d3 + */ + if (wowlan_status->num_of_gtk_rekeys && key_data->len) { + if (key->keyidx == 4 || key->keyidx == 5) { + struct iwl_mld_vif *mld_vif = + iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link; + int link_id = vif->active_links ? + __ffs(vif->active_links) : 0; + + mld_link = iwl_mld_link_dereference_check(mld_vif, + link_id); + if (WARN_ON(!mld_link)) + return; + + if (mld_link->igtk == key) + mld_link->igtk = NULL; + mld->num_igtks--; + } + + ieee80211_remove_key(key); + return; + } + + iwl_mld_set_key_rx_seq(key, key_data); +} + +static void +iwl_mld_update_ptk_rx_seq(struct iwl_mld *mld, + struct iwl_mld_wowlan_status *wowlan_status, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + bool is_tkip) +{ + struct iwl_mld_sta *mld_sta = + iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_ptk_pn *mld_ptk_pn = + wiphy_dereference(mld->wiphy, + mld_sta->ptk_pn[key->keyidx]); + + iwl_mld_set_key_rx_seq_tids(key, is_tkip ? + wowlan_status->ptk.tkip_seq : + wowlan_status->ptk.aes_seq); + if (is_tkip) + return; + + if (WARN_ON(!mld_ptk_pn)) + return; + + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + for (int i = 1; i < mld->trans->info.num_rxqs; i++) + memcpy(mld_ptk_pn->q[i].pn[tid], + wowlan_status->ptk.aes_seq[tid].ccmp.pn, + IEEE80211_CCMP_PN_LEN); + } +} + +static void +iwl_mld_resume_keys_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mld_resume_key_iter_data *data = _data; + struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status; + u8 status_idx; + + /* TODO: check key link id (task=MLO) */ + if (data->unhandled_cipher) + return; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + /* ignore WEP completely, nothing to do */ + return; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_TKIP: + if (sta) { + iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status, + sta, key, + key->cipher == + WLAN_CIPHER_SUITE_TKIP); + return; + } + + if (WARN_ON(data->gtk_cipher && + data->gtk_cipher != key->cipher)) + return; + + data->gtk_cipher = key->cipher; + status_idx = key->keyidx == wowlan_status->gtk[1].id; + iwl_mld_d3_update_mcast_key(data->mld, vif, wowlan_status, key, + &wowlan_status->gtk[status_idx]); + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + if (key->keyidx == 4 || key->keyidx == 5) { + if (WARN_ON(data->igtk_cipher && + data->igtk_cipher != key->cipher)) + return; + + data->igtk_cipher = key->cipher; + iwl_mld_d3_update_mcast_key(data->mld, vif, + wowlan_status, + key, &wowlan_status->igtk); + } + if (key->keyidx == 6 || key->keyidx == 7) { + if (WARN_ON(data->bigtk_cipher && + data->bigtk_cipher != key->cipher)) + return; + + data->bigtk_cipher = key->cipher; + status_idx = key->keyidx == wowlan_status->bigtk[1].id; + iwl_mld_d3_update_mcast_key(data->mld, vif, + wowlan_status, key, + &wowlan_status->bigtk[status_idx]); + } + break; + default: + data->unhandled_cipher = true; + return; + } + data->num_keys++; +} + +static bool +iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif, + struct iwl_mld *mld, + struct iwl_mld_mcast_key_data *key_data, + struct ieee80211_bss_conf *link_conf, + u32 cipher) +{ + struct ieee80211_key_conf *key_config; + struct { + struct ieee80211_key_conf conf; + u8 key[WOWLAN_KEY_MAX_SIZE]; + } conf = { + .conf.cipher = cipher, + .conf.keyidx = key_data->id, + }; + int link_id = vif->active_links ? __ffs(vif->active_links) : -1; + + BUILD_BUG_ON(WLAN_KEY_LEN_CCMP != WLAN_KEY_LEN_GCMP); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_CCMP); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_GCMP_256); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_TKIP); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_128); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_BIP_GMAC_256); + BUILD_BUG_ON(sizeof(conf.key) < WLAN_KEY_LEN_AES_CMAC); + BUILD_BUG_ON(sizeof(conf.key) < sizeof(key_data->key)); + + if (!key_data->len) + return true; + + switch (cipher) { + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + conf.conf.keylen = WLAN_KEY_LEN_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + conf.conf.keylen = WLAN_KEY_LEN_GCMP_256; + break; + case WLAN_CIPHER_SUITE_TKIP: + conf.conf.keylen = WLAN_KEY_LEN_TKIP; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_128; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + conf.conf.keylen = WLAN_KEY_LEN_BIP_GMAC_256; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + conf.conf.keylen = WLAN_KEY_LEN_AES_CMAC; + break; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + conf.conf.keylen = WLAN_KEY_LEN_BIP_CMAC_256; + break; + default: + WARN_ON(1); + } + + memcpy(conf.conf.key, key_data->key, conf.conf.keylen); + key_config = ieee80211_gtk_rekey_add(vif, &conf.conf, link_id); + if (IS_ERR(key_config)) + return false; + + iwl_mld_set_key_rx_seq(key_config, key_data); + + /* The FW holds only one igtk so we keep track of the valid one */ + if (key_config->keyidx == 4 || key_config->keyidx == 5) { + struct iwl_mld_link *mld_link = + iwl_mld_link_from_mac80211(link_conf); + mld_link->igtk = key_config; + mld->num_igtks++; + } + return true; +} + +static bool +iwl_mld_add_all_rekeys(struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status, + struct iwl_mld_resume_key_iter_data *key_iter_data, + struct ieee80211_bss_conf *link_conf) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(wowlan_status->gtk); i++) + if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->gtk[i], + link_conf, + key_iter_data->gtk_cipher)) + return false; + + if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->igtk, + link_conf, key_iter_data->igtk_cipher)) + return false; + + for (i = 0; i < ARRAY_SIZE(wowlan_status->bigtk); i++) + if (!iwl_mld_add_mcast_rekey(vif, key_iter_data->mld, + &wowlan_status->bigtk[i], + link_conf, + key_iter_data->bigtk_cipher)) + return false; + + return true; +} + +static bool +iwl_mld_update_sec_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status) +{ + int link_id = vif->active_links ? __ffs(vif->active_links) : 0; + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + __be64 replay_ctr = cpu_to_be64(wowlan_status->replay_ctr); + struct iwl_mld_resume_key_iter_data key_iter_data = { + .mld = mld, + .wowlan_status = wowlan_status, + }; + + if (WARN_ON(!link_conf)) + return false; + + ieee80211_iter_keys(mld->hw, vif, iwl_mld_resume_keys_iter, + &key_iter_data); + + if (key_iter_data.unhandled_cipher) + return false; + + IWL_DEBUG_WOWLAN(mld, + "Number of installed keys: %d, Number of rekeys: %d\n", + key_iter_data.num_keys, + wowlan_status->num_of_gtk_rekeys); + + if (!key_iter_data.num_keys || !wowlan_status->num_of_gtk_rekeys) + return true; + + iwl_mld_add_all_rekeys(vif, wowlan_status, &key_iter_data, + link_conf); + + ieee80211_gtk_rekey_notify(vif, link_conf->bssid, + (void *)&replay_ctr, GFP_KERNEL); + /* TODO: MLO rekey (task=MLO) */ + return true; +} + +static bool +iwl_mld_process_wowlan_status(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_wowlan_status *wowlan_status) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_sta *ap_sta = mld_vif->ap_sta; + struct iwl_mld_txq *mld_txq; + + iwl_mld_report_wowlan_wakeup(mld, vif, wowlan_status); + + if (WARN_ON(!ap_sta)) + return false; + + mld_txq = + iwl_mld_txq_from_mac80211(ap_sta->txq[wowlan_status->tid_offloaded_tx]); + + /* Update the pointers of the Tx queue that may have moved during + * suspend if the firmware sent frames. + * The firmware stores last-used value, we store next value. + */ + WARN_ON(!mld_txq->status.allocated); + iwl_trans_set_q_ptrs(mld->trans, mld_txq->fw_id, + (wowlan_status->last_qos_seq + + 0x10) >> 4); + + if (!iwl_mld_update_sec_keys(mld, vif, wowlan_status)) + return false; + + if (wowlan_status->wakeup_reasons & + (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | + IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH | + IWL_WOWLAN_WAKEUP_BY_GTK_REKEY_FAILURE)) + return false; + + return true; +} + +static bool +iwl_mld_netdetect_match_info_handler(struct iwl_mld *mld, + struct iwl_mld_resume_data *resume_data, + struct iwl_rx_packet *pkt) +{ + struct iwl_mld_netdetect_res *results = resume_data->netdetect_res; + const struct iwl_scan_offload_match_info *notif = (void *)pkt->data; + u32 len = iwl_rx_packet_payload_len(pkt); + + if (IWL_FW_CHECK(mld, !mld->netdetect, + "Got scan match info notif when mld->netdetect==%d\n", + mld->netdetect)) + return true; + + if (IWL_FW_CHECK(mld, len < sizeof(*notif), + "Invalid scan offload match notif of length: %d\n", + len)) + return true; + + if (IWL_FW_CHECK(mld, resume_data->wowlan_status->wakeup_reasons != + IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS, + "Ignore scan match info: unexpected wakeup reason (expected=0x%x got=0x%x)\n", + IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS, + resume_data->wowlan_status->wakeup_reasons)) + return true; + + results->matched_profiles = le32_to_cpu(notif->matched_profiles); + IWL_DEBUG_WOWLAN(mld, "number of matched profiles=%u\n", + results->matched_profiles); + + if (results->matched_profiles) + memcpy(results->matches, notif->matches, + NETDETECT_QUERY_BUF_LEN); + + /* No scan should be active at this point */ + mld->scan.status = 0; + memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status)); + return false; +} + +static void +iwl_mld_set_netdetect_info(struct iwl_mld *mld, + const struct cfg80211_sched_scan_request *netdetect_cfg, + struct cfg80211_wowlan_nd_info *netdetect_info, + struct iwl_mld_netdetect_res *netdetect_res, + unsigned long matched_profiles) +{ + int i; + + for_each_set_bit(i, &matched_profiles, netdetect_cfg->n_match_sets) { + struct cfg80211_wowlan_nd_match *match; + int idx, j, n_channels = 0; + struct iwl_scan_offload_profile_match *matches = + (void *)netdetect_res->matches; + + for (int k = 0; k < SCAN_OFFLOAD_MATCHING_CHANNELS_LEN; k++) + n_channels += + hweight8(matches[i].matching_channels[k]); + match = kzalloc(struct_size(match, channels, n_channels), + GFP_KERNEL); + if (!match) + return; + + netdetect_info->matches[netdetect_info->n_matches] = match; + netdetect_info->n_matches++; + + /* We inverted the order of the SSIDs in the scan + * request, so invert the index here. + */ + idx = netdetect_cfg->n_match_sets - i - 1; + match->ssid.ssid_len = + netdetect_cfg->match_sets[idx].ssid.ssid_len; + memcpy(match->ssid.ssid, + netdetect_cfg->match_sets[idx].ssid.ssid, + match->ssid.ssid_len); + + if (netdetect_cfg->n_channels < n_channels) + continue; + + for_each_set_bit(j, + (unsigned long *)&matches[i].matching_channels[0], + sizeof(matches[i].matching_channels)) { + match->channels[match->n_channels] = + netdetect_cfg->channels[j]->center_freq; + match->n_channels++; + } + } +} + +static void +iwl_mld_process_netdetect_res(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_resume_data *resume_data) +{ + struct cfg80211_wowlan_nd_info *netdetect_info = NULL; + const struct cfg80211_sched_scan_request *netdetect_cfg; + struct cfg80211_wowlan_wakeup wakeup = { + .pattern_idx = -1, + }; + struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; + unsigned long matched_profiles; + u32 wakeup_reasons; + int n_matches; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld->wiphy->wowlan_config || + !mld->wiphy->wowlan_config->nd_config)) { + IWL_DEBUG_WOWLAN(mld, + "Netdetect isn't configured on resume flow\n"); + goto out; + } + + netdetect_cfg = mld->wiphy->wowlan_config->nd_config; + wakeup_reasons = resume_data->wowlan_status->wakeup_reasons; + + if (wakeup_reasons & IWL_WOWLAN_WAKEUP_BY_RFKILL_DEASSERTED) + wakeup.rfkill_release = true; + + if (wakeup_reasons != IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) + goto out; + + if (!resume_data->netdetect_res->matched_profiles) { + IWL_DEBUG_WOWLAN(mld, + "Netdetect results aren't valid\n"); + wakeup_report = NULL; + goto out; + } + + matched_profiles = resume_data->netdetect_res->matched_profiles; + if (!netdetect_cfg->n_match_sets) { + IWL_DEBUG_WOWLAN(mld, + "No netdetect match sets are configured\n"); + goto out; + } + n_matches = hweight_long(matched_profiles); + netdetect_info = kzalloc(struct_size(netdetect_info, matches, + n_matches), GFP_KERNEL); + if (netdetect_info) + iwl_mld_set_netdetect_info(mld, netdetect_cfg, netdetect_info, + resume_data->netdetect_res, + matched_profiles); + + wakeup.net_detect = netdetect_info; + out: + ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL); + if (netdetect_info) { + for (int i = 0; i < netdetect_info->n_matches; i++) + kfree(netdetect_info->matches[i]); + kfree(netdetect_info); + } +} + +static bool iwl_mld_handle_d3_notif(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mld_resume_data *resume_data = data; + struct iwl_mld *mld = + container_of(notif_wait, struct iwl_mld, notif_wait); + + switch (WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) { + case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION): { + if (resume_data->notifs_received & IWL_D3_NOTIF_WOWLAN_INFO) { + IWL_DEBUG_WOWLAN(mld, + "got additional wowlan_info notif\n"); + break; + } + resume_data->notif_handling_err = + iwl_mld_handle_wowlan_info_notif(mld, + resume_data->wowlan_status, + pkt); + resume_data->notifs_received |= IWL_D3_NOTIF_WOWLAN_INFO; + + if (resume_data->wowlan_status->wakeup_reasons & + IWL_WOWLAN_WAKEUP_REASON_HAS_WAKEUP_PKT) + resume_data->notifs_expected |= + IWL_D3_NOTIF_WOWLAN_WAKE_PKT; + break; + } + case WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_WAKE_PKT_NOTIFICATION): { + if (resume_data->notifs_received & + IWL_D3_NOTIF_WOWLAN_WAKE_PKT) { + /* We shouldn't get two wake packet notifications */ + IWL_DEBUG_WOWLAN(mld, + "Got additional wowlan wake packet notification\n"); + break; + } + resume_data->notif_handling_err = + iwl_mld_handle_wake_pkt_notif(mld, + resume_data->wowlan_status, + pkt); + resume_data->notifs_received |= IWL_D3_NOTIF_WOWLAN_WAKE_PKT; + break; + } + case WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF): { + if (resume_data->notifs_received & IWL_D3_ND_MATCH_INFO) { + IWL_ERR(mld, + "Got additional netdetect match info\n"); + break; + } + + resume_data->notif_handling_err = + iwl_mld_netdetect_match_info_handler(mld, resume_data, + pkt); + resume_data->notifs_received |= IWL_D3_ND_MATCH_INFO; + break; + } + case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): { + struct iwl_d3_end_notif *notif = (void *)pkt->data; + + resume_data->d3_end_flags = le32_to_cpu(notif->flags); + resume_data->notifs_received |= IWL_D3_NOTIF_D3_END_NOTIF; + break; + } + default: + WARN_ON(1); + } + + return resume_data->notifs_received == resume_data->notifs_expected; +} + +#define IWL_MLD_D3_NOTIF_TIMEOUT (HZ / 3) + +static int iwl_mld_wait_d3_notif(struct iwl_mld *mld, + struct iwl_mld_resume_data *resume_data, + bool with_wowlan) +{ + static const u16 wowlan_resume_notif[] = { + WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_INFO_NOTIFICATION), + WIDE_ID(PROT_OFFLOAD_GROUP, WOWLAN_WAKE_PKT_NOTIFICATION), + WIDE_ID(SCAN_GROUP, OFFLOAD_MATCH_INFO_NOTIF), + WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION) + }; + static const u16 d3_resume_notif[] = { + WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION) + }; + struct iwl_notification_wait wait_d3_notif; + enum iwl_d3_status d3_status; + int ret; + + if (with_wowlan) + iwl_init_notification_wait(&mld->notif_wait, &wait_d3_notif, + wowlan_resume_notif, + ARRAY_SIZE(wowlan_resume_notif), + iwl_mld_handle_d3_notif, + resume_data); + else + iwl_init_notification_wait(&mld->notif_wait, &wait_d3_notif, + d3_resume_notif, + ARRAY_SIZE(d3_resume_notif), + iwl_mld_handle_d3_notif, + resume_data); + + ret = iwl_trans_d3_resume(mld->trans, &d3_status, false, false); + if (ret || d3_status != IWL_D3_STATUS_ALIVE) { + if (d3_status != IWL_D3_STATUS_ALIVE) { + IWL_INFO(mld, "Device was reset during suspend\n"); + ret = -ENOENT; + } else { + IWL_ERR(mld, "Transport resume failed\n"); + } + iwl_remove_notification(&mld->notif_wait, &wait_d3_notif); + return ret; + } + + ret = iwl_wait_notification(&mld->notif_wait, &wait_d3_notif, + IWL_MLD_D3_NOTIF_TIMEOUT); + if (ret) + IWL_ERR(mld, "Couldn't get the d3 notif %d\n", ret); + + if (resume_data->notif_handling_err) + ret = -EIO; + + return ret; +} + +int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld) +{ + struct iwl_d3_manager_config d3_cfg_cmd_data = {}; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan suspend flow\n"); + + iwl_mld_low_latency_stop(mld); + + /* This will happen if iwl_mld_supsend failed with FW error */ + if (mld->trans->state == IWL_TRANS_NO_FW && + test_bit(STATUS_FW_ERROR, &mld->trans->status)) + return -ENODEV; + + ret = iwl_mld_update_device_power(mld, true); + if (ret) { + IWL_ERR(mld, + "d3 suspend: couldn't send power_device %d\n", ret); + goto out; + } + + ret = iwl_mld_send_cmd_pdu(mld, D3_CONFIG_CMD, + &d3_cfg_cmd_data); + if (ret) { + IWL_ERR(mld, + "d3 suspend: couldn't send D3_CONFIG_CMD %d\n", ret); + goto out; + } + + ret = iwl_trans_d3_suspend(mld->trans, false, false); + if (ret) { + IWL_ERR(mld, "d3 suspend: trans_d3_suspend failed %d\n", ret); + } else { + /* Async notification might send hcmds, which is not allowed in suspend */ + iwl_mld_cancel_async_notifications(mld); + mld->fw_status.in_d3 = true; + } + + out: + if (ret) { + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); + } + + return ret; +} + +int iwl_mld_no_wowlan_resume(struct iwl_mld *mld) +{ + struct iwl_mld_resume_data resume_data = { + .notifs_expected = + IWL_D3_NOTIF_D3_END_NOTIF, + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + IWL_DEBUG_WOWLAN(mld, "Starting the no wowlan resume flow\n"); + + mld->fw_status.in_d3 = false; + iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); + + if (iwl_mld_fw_needs_restart(mld, NULL)) + ret = -ENODEV; + else + ret = iwl_mld_wait_d3_notif(mld, &resume_data, false); + + if (!ret && (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE)) + return -ENODEV; + + if (ret) { + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); + return ret; + } + iwl_mld_low_latency_restart(mld); + + return iwl_mld_update_device_power(mld, false); +} + +static void +iwl_mld_aes_seq_to_le64_pn(struct ieee80211_key_conf *key, + __le64 *key_rsc) +{ + for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { + struct ieee80211_key_seq seq; + u8 *pn = key->cipher == WLAN_CIPHER_SUITE_CCMP ? seq.ccmp.pn : + seq.gcmp.pn; + + ieee80211_get_key_rx_seq(key, i, &seq); + key_rsc[i] = cpu_to_le64((u64)pn[5] | + ((u64)pn[4] << 8) | + ((u64)pn[3] << 16) | + ((u64)pn[2] << 24) | + ((u64)pn[1] << 32) | + ((u64)pn[0] << 40)); + } +} + +static void +iwl_mld_suspend_set_ucast_pn(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, __le64 *key_rsc) +{ + struct iwl_mld_sta *mld_sta = + iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_ptk_pn *mld_ptk_pn; + + if (WARN_ON(key->keyidx >= ARRAY_SIZE(mld_sta->ptk_pn))) + return; + + mld_ptk_pn = wiphy_dereference(mld->wiphy, + mld_sta->ptk_pn[key->keyidx]); + if (WARN_ON(!mld_ptk_pn)) + return; + + for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + struct ieee80211_key_seq seq; + u8 *max_pn = seq.ccmp.pn; + + /* get the PN from mac80211, used on the default queue */ + ieee80211_get_key_rx_seq(key, tid, &seq); + + /* and use the internal data for all queues */ + for (int que = 1; que < mld->trans->info.num_rxqs; que++) { + u8 *cur_pn = mld_ptk_pn->q[que].pn[tid]; + + if (memcmp(max_pn, cur_pn, IEEE80211_CCMP_PN_LEN) < 0) + max_pn = cur_pn; + } + key_rsc[tid] = cpu_to_le64((u64)max_pn[5] | + ((u64)max_pn[4] << 8) | + ((u64)max_pn[3] << 16) | + ((u64)max_pn[2] << 24) | + ((u64)max_pn[1] << 32) | + ((u64)max_pn[0] << 40)); + } +} + +static void +iwl_mld_suspend_convert_tkip_ipn(struct ieee80211_key_conf *key, + __le64 *rsc) +{ + struct ieee80211_key_seq seq; + + for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { + ieee80211_get_key_rx_seq(key, i, &seq); + rsc[i] = + cpu_to_le64(((u64)seq.tkip.iv32 << 16) | + seq.tkip.iv16); + } +} + +static void +iwl_mld_suspend_key_data_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_suspend_key_iter_data *data = _data; + __le64 *key_rsc; + __le32 cipher = 0; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_CCMP: + cipher = cpu_to_le32(STA_KEY_FLG_CCM); + fallthrough; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + if (!cipher) + cipher = cpu_to_le32(STA_KEY_FLG_GCMP); + fallthrough; + case WLAN_CIPHER_SUITE_TKIP: + if (!cipher) + cipher = cpu_to_le32(STA_KEY_FLG_TKIP); + if (sta) { + key_rsc = data->rsc->ucast_rsc; + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + iwl_mld_suspend_convert_tkip_ipn(key, key_rsc); + else + iwl_mld_suspend_set_ucast_pn(mld, sta, key, + key_rsc); + + data->have_rsc = true; + return; + } + /* We're iterating from old to new, there're 4 possible + * gtk ids, and only the last two keys matter + */ + if (WARN_ON(data->gtks >= + ARRAY_SIZE(data->found_gtk_idx))) + return; + + if (WARN_ON(key->keyidx >= + ARRAY_SIZE(data->rsc->mcast_key_id_map))) + return; + data->gtk_cipher = cipher; + data->found_gtk_idx[data->gtks] = key->keyidx; + key_rsc = data->rsc->mcast_rsc[data->gtks % 2]; + data->rsc->mcast_key_id_map[key->keyidx] = + data->gtks % 2; + + if (data->gtks >= 2) { + int prev = data->gtks % 2; + int prev_idx = data->found_gtk_idx[prev]; + + data->rsc->mcast_key_id_map[prev_idx] = + IWL_MCAST_KEY_MAP_INVALID; + } + + if (key->cipher == WLAN_CIPHER_SUITE_TKIP) + iwl_mld_suspend_convert_tkip_ipn(key, key_rsc); + else + iwl_mld_aes_seq_to_le64_pn(key, key_rsc); + + data->gtks++; + data->have_rsc = true; + break; + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + cipher = cpu_to_le32(STA_KEY_FLG_GCMP); + fallthrough; + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + if (!cipher) + cipher = cpu_to_le32(STA_KEY_FLG_CCM); + if (key->keyidx == 4 || key->keyidx == 5) + data->igtk_cipher = cipher; + + if (key->keyidx == 6 || key->keyidx == 7) + data->bigtk_cipher = cipher; + + break; + } +} + +static int +iwl_mld_send_kek_kck_cmd(struct iwl_mld *mld, + struct iwl_mld_vif *mld_vif, + struct iwl_mld_suspend_key_iter_data data, + int ap_sta_id) +{ + struct iwl_wowlan_kek_kck_material_cmd_v4 kek_kck_cmd = {}; + struct iwl_mld_rekey_data *rekey_data = + &mld_vif->wowlan_data.rekey_data; + + memcpy(kek_kck_cmd.kck, rekey_data->kck, + rekey_data->kck_len); + kek_kck_cmd.kck_len = cpu_to_le16(rekey_data->kck_len); + memcpy(kek_kck_cmd.kek, rekey_data->kek, + rekey_data->kek_len); + kek_kck_cmd.kek_len = cpu_to_le16(rekey_data->kek_len); + kek_kck_cmd.replay_ctr = rekey_data->replay_ctr; + kek_kck_cmd.akm = cpu_to_le32(rekey_data->akm); + kek_kck_cmd.sta_id = cpu_to_le32(ap_sta_id); + kek_kck_cmd.gtk_cipher = data.gtk_cipher; + kek_kck_cmd.igtk_cipher = data.igtk_cipher; + kek_kck_cmd.bigtk_cipher = data.bigtk_cipher; + + IWL_DEBUG_WOWLAN(mld, "setting akm %d\n", + rekey_data->akm); + + return iwl_mld_send_cmd_pdu(mld, WOWLAN_KEK_KCK_MATERIAL, + &kek_kck_cmd); +} + +static int +iwl_mld_suspend_send_security_cmds(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_vif *mld_vif, + int ap_sta_id) +{ + struct iwl_mld_suspend_key_iter_data data = {}; + int ret; + + data.rsc = kzalloc(sizeof(*data.rsc), GFP_KERNEL); + if (!data.rsc) + return -ENOMEM; + + memset(data.rsc->mcast_key_id_map, IWL_MCAST_KEY_MAP_INVALID, + ARRAY_SIZE(data.rsc->mcast_key_id_map)); + + data.rsc->sta_id = cpu_to_le32(ap_sta_id); + ieee80211_iter_keys(mld->hw, vif, + iwl_mld_suspend_key_data_iter, + &data); + + if (data.have_rsc) + ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_TSC_RSC_PARAM, + data.rsc); + else + ret = 0; + + if (!ret && mld_vif->wowlan_data.rekey_data.valid) + ret = iwl_mld_send_kek_kck_cmd(mld, mld_vif, data, ap_sta_id); + + kfree(data.rsc); + + return ret; +} + +static void +iwl_mld_set_wowlan_config_cmd(struct iwl_mld *mld, + struct cfg80211_wowlan *wowlan, + struct iwl_wowlan_config_cmd *wowlan_config_cmd, + struct ieee80211_sta *ap_sta) +{ + wowlan_config_cmd->is_11n_connection = + ap_sta->deflink.ht_cap.ht_supported; + wowlan_config_cmd->flags = ENABLE_L3_FILTERING | + ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING; + + if (ap_sta->mfp) + wowlan_config_cmd->flags |= IS_11W_ASSOC; + + if (wowlan->disconnect) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | + IWL_WOWLAN_WAKEUP_LINK_CHANGE); + if (wowlan->magic_pkt) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); + if (wowlan->gtk_rekey_failure) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); + if (wowlan->eap_identity_req) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); + if (wowlan->four_way_handshake) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); + if (wowlan->n_patterns) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); + + if (wowlan->rfkill_release) + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); + + if (wowlan->any) { + wowlan_config_cmd->wakeup_filter |= + cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | + IWL_WOWLAN_WAKEUP_LINK_CHANGE | + IWL_WOWLAN_WAKEUP_RX_FRAME | + IWL_WOWLAN_WAKEUP_BCN_FILTERING); + } +} + +static int iwl_mld_send_patterns(struct iwl_mld *mld, + struct cfg80211_wowlan *wowlan, + int ap_sta_id) +{ + struct iwl_wowlan_patterns_cmd *pattern_cmd; + struct iwl_host_cmd cmd = { + .id = WOWLAN_PATTERNS, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + int ret; + + if (!wowlan->n_patterns) + return 0; + + cmd.len[0] = struct_size(pattern_cmd, patterns, wowlan->n_patterns); + + pattern_cmd = kzalloc(cmd.len[0], GFP_KERNEL); + if (!pattern_cmd) + return -ENOMEM; + + pattern_cmd->n_patterns = wowlan->n_patterns; + pattern_cmd->sta_id = ap_sta_id; + + for (int i = 0; i < wowlan->n_patterns; i++) { + int mask_len = DIV_ROUND_UP(wowlan->patterns[i].pattern_len, 8); + + pattern_cmd->patterns[i].pattern_type = + WOWLAN_PATTERN_TYPE_BITMASK; + + memcpy(&pattern_cmd->patterns[i].u.bitmask.mask, + wowlan->patterns[i].mask, mask_len); + memcpy(&pattern_cmd->patterns[i].u.bitmask.pattern, + wowlan->patterns[i].pattern, + wowlan->patterns[i].pattern_len); + pattern_cmd->patterns[i].u.bitmask.mask_size = mask_len; + pattern_cmd->patterns[i].u.bitmask.pattern_size = + wowlan->patterns[i].pattern_len; + } + + cmd.data[0] = pattern_cmd; + ret = iwl_mld_send_cmd(mld, &cmd); + kfree(pattern_cmd); + return ret; +} + +static int +iwl_mld_send_proto_offload(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u8 ap_sta_id) +{ + struct iwl_proto_offload_cmd_v4 *cmd __free(kfree); + struct iwl_host_cmd hcmd = { + .id = PROT_OFFLOAD_CONFIG_CMD, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + .len[0] = sizeof(*cmd), + }; + u32 enabled = 0; + + cmd = kzalloc(hcmd.len[0], GFP_KERNEL); + +#if IS_ENABLED(CONFIG_IPV6) + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_wowlan_data *wowlan_data = &mld_vif->wowlan_data; + struct iwl_ns_config *nsc; + struct iwl_targ_addr *addrs; + int n_nsc, n_addrs; + int i, c; + int num_skipped = 0; + + nsc = cmd->ns_config; + n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; + addrs = cmd->targ_addrs; + n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; + + /* For each address we have (and that will fit) fill a target + * address struct and combine for NS offload structs with the + * solicited node addresses. + */ + for (i = 0, c = 0; + i < wowlan_data->num_target_ipv6_addrs && + i < n_addrs && c < n_nsc; i++) { + int j; + struct in6_addr solicited_addr; + + /* Because ns is offloaded skip tentative address to avoid + * violating RFC4862. + */ + if (test_bit(i, wowlan_data->tentative_addrs)) { + num_skipped++; + continue; + } + + addrconf_addr_solict_mult(&wowlan_data->target_ipv6_addrs[i], + &solicited_addr); + for (j = 0; j < c; j++) + if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, + &solicited_addr) == 0) + break; + if (j == c) + c++; + addrs[i].addr = wowlan_data->target_ipv6_addrs[i]; + addrs[i].config_num = cpu_to_le32(j); + nsc[j].dest_ipv6_addr = solicited_addr; + memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); + } + + if (wowlan_data->num_target_ipv6_addrs - num_skipped) + enabled |= IWL_D3_PROTO_IPV6_VALID; + + cmd->num_valid_ipv6_addrs = cpu_to_le32(i - num_skipped); + if (enabled & IWL_D3_PROTO_IPV6_VALID) + enabled |= IWL_D3_PROTO_OFFLOAD_NS; +#endif + + if (vif->cfg.arp_addr_cnt) { + enabled |= IWL_D3_PROTO_OFFLOAD_ARP | IWL_D3_PROTO_IPV4_VALID; + cmd->common.host_ipv4_addr = vif->cfg.arp_addr_list[0]; + ether_addr_copy(cmd->common.arp_mac_addr, vif->addr); + } + + enabled |= IWL_D3_PROTO_OFFLOAD_BTM; + cmd->common.enabled = cpu_to_le32(enabled); + cmd->sta_id = cpu_to_le32(ap_sta_id); + hcmd.data[0] = cmd; + return iwl_mld_send_cmd(mld, &hcmd); +} + +static int +iwl_mld_wowlan_config(struct iwl_mld *mld, struct ieee80211_vif *bss_vif, + struct cfg80211_wowlan *wowlan) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_vif); + struct ieee80211_sta *ap_sta = mld_vif->ap_sta; + struct iwl_wowlan_config_cmd wowlan_config_cmd = { + .offloading_tid = IWL_WOWLAN_OFFLOAD_TID, + }; + u32 sta_id_mask; + int ap_sta_id, ret; + int link_id = iwl_mld_get_primary_link(bss_vif); + struct ieee80211_bss_conf *link_conf; + + ret = iwl_mld_block_emlsr_sync(mld, bss_vif, + IWL_MLD_EMLSR_BLOCKED_WOWLAN, link_id); + if (ret) + return ret; + + link_conf = link_conf_dereference_protected(bss_vif, link_id); + + if (WARN_ON(!ap_sta || !link_conf)) + return -EINVAL; + + sta_id_mask = iwl_mld_fw_sta_id_mask(mld, ap_sta); + if (WARN_ON(hweight32(sta_id_mask) != 1)) + return -EINVAL; + + ap_sta_id = __ffs(sta_id_mask); + wowlan_config_cmd.sta_id = ap_sta_id; + + ret = iwl_mld_ensure_queue(mld, + ap_sta->txq[wowlan_config_cmd.offloading_tid]); + if (ret) + return ret; + + iwl_mld_set_wowlan_config_cmd(mld, wowlan, + &wowlan_config_cmd, ap_sta); + ret = iwl_mld_send_cmd_pdu(mld, WOWLAN_CONFIGURATION, + &wowlan_config_cmd); + if (ret) + return ret; + + ret = iwl_mld_suspend_send_security_cmds(mld, bss_vif, mld_vif, + ap_sta_id); + if (ret) + return ret; + + ret = iwl_mld_send_patterns(mld, wowlan, ap_sta_id); + if (ret) + return ret; + + ret = iwl_mld_send_proto_offload(mld, bss_vif, ap_sta_id); + if (ret) + return ret; + + iwl_mld_enable_beacon_filter(mld, link_conf, true); + return iwl_mld_update_mac_power(mld, bss_vif, true); +} + +int iwl_mld_wowlan_suspend(struct iwl_mld *mld, struct cfg80211_wowlan *wowlan) +{ + struct ieee80211_vif *bss_vif; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!wowlan)) + return 1; + + IWL_DEBUG_WOWLAN(mld, "Starting the wowlan suspend flow\n"); + + bss_vif = iwl_mld_get_bss_vif(mld); + if (WARN_ON(!bss_vif)) + return 1; + + if (!bss_vif->cfg.assoc) { + int ret; + /* If we're not associated, this must be netdetect */ + if (WARN_ON(!wowlan->nd_config)) + return 1; + + ret = iwl_mld_netdetect_config(mld, bss_vif, wowlan); + if (!ret) + mld->netdetect = true; + + return ret; + } + + return iwl_mld_wowlan_config(mld, bss_vif, wowlan); +} + +/* Returns 0 on success, 1 if an error occurred in firmware during d3, + * A negative value is expected only in unrecovreable cases. + */ +int iwl_mld_wowlan_resume(struct iwl_mld *mld) +{ + struct ieee80211_vif *bss_vif; + struct ieee80211_bss_conf *link_conf; + struct iwl_mld_netdetect_res netdetect_res; + struct iwl_mld_resume_data resume_data = { + .notifs_expected = + IWL_D3_NOTIF_WOWLAN_INFO | + IWL_D3_NOTIF_D3_END_NOTIF, + .netdetect_res = &netdetect_res, + }; + int link_id; + int ret; + bool fw_err = false; + + lockdep_assert_wiphy(mld->wiphy); + + IWL_DEBUG_WOWLAN(mld, "Starting the wowlan resume flow\n"); + + if (!mld->fw_status.in_d3) { + IWL_DEBUG_WOWLAN(mld, + "Device_powered_off() was called during wowlan\n"); + goto err; + } + + mld->fw_status.in_d3 = false; + mld->scan.last_start_time_jiffies = jiffies; + + bss_vif = iwl_mld_get_bss_vif(mld); + if (WARN_ON(!bss_vif)) + goto err; + + /* We can't have several links upon wowlan entry, + * this is enforced in the suspend flow. + */ + WARN_ON(hweight16(bss_vif->active_links) > 1); + link_id = bss_vif->active_links ? __ffs(bss_vif->active_links) : 0; + link_conf = link_conf_dereference_protected(bss_vif, link_id); + + if (WARN_ON(!link_conf)) + goto err; + + iwl_fw_dbg_read_d3_debug_data(&mld->fwrt); + + if (iwl_mld_fw_needs_restart(mld, bss_vif)) { + fw_err = true; + goto err; + } + + resume_data.wowlan_status = kzalloc(sizeof(*resume_data.wowlan_status), + GFP_KERNEL); + if (!resume_data.wowlan_status) + return -1; + + if (mld->netdetect) + resume_data.notifs_expected |= IWL_D3_ND_MATCH_INFO; + + ret = iwl_mld_wait_d3_notif(mld, &resume_data, true); + if (ret) { + IWL_ERR(mld, "Couldn't get the d3 notifs %d\n", ret); + fw_err = true; + goto err; + } + + if (resume_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE) { + mld->fw_status.in_hw_restart = true; + goto process_wakeup_results; + } + + iwl_mld_update_changed_regdomain(mld); + iwl_mld_update_mac_power(mld, bss_vif, false); + iwl_mld_enable_beacon_filter(mld, link_conf, false); + iwl_mld_update_device_power(mld, false); + + if (mld->netdetect) + ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_NETDETECT, false); + + process_wakeup_results: + if (mld->netdetect) { + iwl_mld_process_netdetect_res(mld, bss_vif, &resume_data); + mld->netdetect = false; + } else { + bool keep_connection = + iwl_mld_process_wowlan_status(mld, bss_vif, + resume_data.wowlan_status); + + /* EMLSR state will be cleared if the connection is not kept */ + if (keep_connection) + iwl_mld_unblock_emlsr(mld, bss_vif, + IWL_MLD_EMLSR_BLOCKED_WOWLAN); + else + ieee80211_resume_disconnect(bss_vif); + } + + goto out; + + err: + if (fw_err) { + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); + } + + mld->fw_status.in_hw_restart = true; + ret = 1; + out: + if (resume_data.wowlan_status) { + kfree(resume_data.wowlan_status->wake_packet); + kfree(resume_data.wowlan_status); + } + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/d3.h b/drivers/net/wireless/intel/iwlwifi/mld/d3.h new file mode 100644 index 000000000000..618d6fb3c796 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/d3.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_d3_h__ +#define __iwl_mld_d3_h__ + +#include "fw/api/d3.h" + +struct iwl_mld_rekey_data { + bool valid; + u8 kck[NL80211_KCK_EXT_LEN]; + u8 kek[NL80211_KEK_EXT_LEN]; + size_t kck_len; + size_t kek_len; + __le64 replay_ctr; + u32 akm; +}; + +/** + * struct iwl_mld_wowlan_data - data used by the wowlan suspend flow + * + * @target_ipv6_addrs: IPv6 addresses on this interface for offload + * @tentative_addrs: bitmap of tentative IPv6 addresses in @target_ipv6_addrs + * @num_target_ipv6_addrs: number of @target_ipv6_addrs + * @rekey_data: security key data used for rekeying during D3 + */ +struct iwl_mld_wowlan_data { +#if IS_ENABLED(CONFIG_IPV6) + struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; + unsigned long tentative_addrs[BITS_TO_LONGS(IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX)]; + int num_target_ipv6_addrs; +#endif + struct iwl_mld_rekey_data rekey_data; +}; + +int iwl_mld_no_wowlan_resume(struct iwl_mld *mld); +int iwl_mld_no_wowlan_suspend(struct iwl_mld *mld); +int iwl_mld_wowlan_suspend(struct iwl_mld *mld, + struct cfg80211_wowlan *wowlan); +int iwl_mld_wowlan_resume(struct iwl_mld *mld); +void iwl_mld_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data); +#if IS_ENABLED(CONFIG_IPV6) +void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct inet6_dev *idev); +#endif + +#endif /* __iwl_mld_d3_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c new file mode 100644 index 000000000000..352da8aa7898 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.c @@ -0,0 +1,1104 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "mld.h" +#include "debugfs.h" +#include "iwl-io.h" +#include "hcmd.h" +#include "iface.h" +#include "sta.h" +#include "tlc.h" +#include "power.h" +#include "notif.h" +#include "ap.h" +#include "iwl-utils.h" +#include "scan.h" +#ifdef CONFIG_THERMAL +#include "thermal.h" +#endif + +#include "fw/api/rs.h" +#include "fw/api/dhc.h" +#include "fw/api/rfi.h" +#include "fw/dhc-utils.h" +#include <linux/dmi.h> + +#define MLD_DEBUGFS_READ_FILE_OPS(name, bufsz) \ + _MLD_DEBUGFS_READ_FILE_OPS(name, bufsz, struct iwl_mld) + +#define MLD_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ + debugfs_create_file(alias, mode, parent, mld, \ + &iwl_dbgfs_##name##_ops) +#define MLD_DEBUGFS_ADD_FILE(name, parent, mode) \ + MLD_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +static bool iwl_mld_dbgfs_fw_cmd_disabled(struct iwl_mld *mld) +{ +#ifdef CONFIG_PM_SLEEP + return !mld->fw_status.running || mld->fw_status.in_d3; +#else + return !mld->fw_status.running; +#endif /* CONFIG_PM_SLEEP */ +} + +static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mld *mld, + char *buf, size_t count) +{ + /* If the firmware is not running, silently succeed since there is + * no data to clear. + */ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return 0; + + iwl_fw_dbg_clear_monitor_buf(&mld->fwrt); + + return count; +} + +static ssize_t iwl_dbgfs_fw_nmi_write(struct iwl_mld *mld, char *buf, + size_t count) +{ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + IWL_ERR(mld, "Triggering an NMI from debugfs\n"); + + if (count == 6 && !strcmp(buf, "nolog\n")) + mld->fw_status.do_not_dump_once = true; + + iwl_force_nmi(mld->trans); + + return count; +} + +static ssize_t iwl_dbgfs_fw_restart_write(struct iwl_mld *mld, char *buf, + size_t count) +{ + int __maybe_unused ret; + + if (!iwlwifi_mod_params.fw_restart) + return -EPERM; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + if (count == 6 && !strcmp(buf, "nolog\n")) { + mld->fw_status.do_not_dump_once = true; + set_bit(STATUS_SUPPRESS_CMD_ERROR_ONCE, &mld->trans->status); + } + + /* take the return value to make compiler happy - it will + * fail anyway + */ + ret = iwl_mld_send_cmd_empty(mld, WIDE_ID(LONG_GROUP, REPLY_ERROR)); + + return count; +} + +static ssize_t iwl_dbgfs_send_echo_cmd_write(struct iwl_mld *mld, char *buf, + size_t count) +{ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + return iwl_mld_send_cmd_empty(mld, ECHO_CMD) ?: count; +} + +struct iwl_mld_sniffer_apply { + struct iwl_mld *mld; + const u8 *bssid; + u16 aid; +}; + +static bool iwl_mld_sniffer_apply(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mld_sniffer_apply *apply = data; + + apply->mld->monitor.cur_aid = cpu_to_le16(apply->aid); + memcpy(apply->mld->monitor.cur_bssid, apply->bssid, + sizeof(apply->mld->monitor.cur_bssid)); + + return true; +} + +static ssize_t +iwl_dbgfs_he_sniffer_params_write(struct iwl_mld *mld, char *buf, + size_t count) +{ + struct iwl_notification_wait wait; + struct iwl_he_monitor_cmd he_mon_cmd = {}; + struct iwl_mld_sniffer_apply apply = { + .mld = mld, + }; + u16 wait_cmds[] = { + WIDE_ID(DATA_PATH_GROUP, HE_AIR_SNIFFER_CONFIG_CMD), + }; + u32 aid; + int ret; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + if (!mld->monitor.on) + return -ENODEV; + + ret = sscanf(buf, "%x %2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx", &aid, + &he_mon_cmd.bssid[0], &he_mon_cmd.bssid[1], + &he_mon_cmd.bssid[2], &he_mon_cmd.bssid[3], + &he_mon_cmd.bssid[4], &he_mon_cmd.bssid[5]); + if (ret != 7) + return -EINVAL; + + he_mon_cmd.aid = cpu_to_le16(aid); + + apply.aid = aid; + apply.bssid = (void *)he_mon_cmd.bssid; + + /* Use the notification waiter to get our function triggered + * in sequence with other RX. This ensures that frames we get + * on the RX queue _before_ the new configuration is applied + * still have mld->cur_aid pointing to the old AID, and that + * frames on the RX queue _after_ the firmware processed the + * new configuration (and sent the response, synchronously) + * get mld->cur_aid correctly set to the new AID. + */ + iwl_init_notification_wait(&mld->notif_wait, &wait, + wait_cmds, ARRAY_SIZE(wait_cmds), + iwl_mld_sniffer_apply, &apply); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + HE_AIR_SNIFFER_CONFIG_CMD), + &he_mon_cmd); + + /* no need to really wait, we already did anyway */ + iwl_remove_notification(&mld->notif_wait, &wait); + + return ret ?: count; +} + +static ssize_t +iwl_dbgfs_he_sniffer_params_read(struct iwl_mld *mld, char *buf, size_t count) +{ + return scnprintf(buf, count, + "%d %02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx\n", + le16_to_cpu(mld->monitor.cur_aid), + mld->monitor.cur_bssid[0], mld->monitor.cur_bssid[1], + mld->monitor.cur_bssid[2], mld->monitor.cur_bssid[3], + mld->monitor.cur_bssid[4], mld->monitor.cur_bssid[5]); +} + +/* The size computation is as follows: + * each number needs at most 3 characters, number of rows is the size of + * the table; So, need 5 chars for the "freq: " part and each tuple afterwards + * needs 6 characters for numbers and 5 for the punctuation around. 32 bytes + * for feature support message. + */ +#define IWL_RFI_DDR_BUF_SIZE (IWL_RFI_DDR_LUT_INSTALLED_SIZE *\ + (5 + IWL_RFI_DDR_LUT_ENTRY_CHANNELS_NUM *\ + (6 + 5)) + 32) +#define IWL_RFI_DLVR_BUF_SIZE (IWL_RFI_DLVR_LUT_INSTALLED_SIZE *\ + (5 + IWL_RFI_DLVR_LUT_ENTRY_CHANNELS_NUM *\ + (6 + 5)) + 32) +#define IWL_RFI_DESENSE_BUF_SIZE IWL_RFI_DDR_BUF_SIZE + +/* Extra 32 for "DDR and DLVR table" message */ +#define IWL_RFI_BUF_SIZE (IWL_RFI_DDR_BUF_SIZE + IWL_RFI_DLVR_BUF_SIZE +\ + IWL_RFI_DESENSE_BUF_SIZE + 32) + +static size_t iwl_mld_dump_tas_resp(struct iwl_dhc_tas_status_resp *resp, + size_t count, u8 *buf) +{ + const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = { + [TAS_DISABLED_DUE_TO_BIOS] = + "Due To BIOS", + [TAS_DISABLED_DUE_TO_SAR_6DBM] = + "Due To SAR Limit Less Than 6 dBm", + [TAS_DISABLED_REASON_INVALID] = + "N/A", + [TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID] = + "Due to table source invalid" + }; + const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = { + [TAS_DYNA_INACTIVE] = "INACTIVE", + [TAS_DYNA_INACTIVE_MVM_MODE] = + "inactive due to mvm mode", + [TAS_DYNA_INACTIVE_TRIGGER_MODE] = + "inactive due to trigger mode", + [TAS_DYNA_INACTIVE_BLOCK_LISTED] = + "inactive due to block listed", + [TAS_DYNA_INACTIVE_UHB_NON_US] = + "inactive due to uhb non US", + [TAS_DYNA_ACTIVE] = "ACTIVE", + }; + ssize_t pos = 0; + + if (resp->header.version != 1) { + pos += scnprintf(buf + pos, count - pos, + "Unsupported TAS response version:%d", + resp->header.version); + return pos; + } + + pos += scnprintf(buf + pos, count - pos, "TAS Report\n"); + switch (resp->tas_config_info.table_source) { + case BIOS_SOURCE_NONE: + pos += scnprintf(buf + pos, count - pos, + "BIOS SOURCE NONE "); + break; + case BIOS_SOURCE_ACPI: + pos += scnprintf(buf + pos, count - pos, + "BIOS SOURCE ACPI "); + break; + case BIOS_SOURCE_UEFI: + pos += scnprintf(buf + pos, count - pos, + "BIOS SOURCE UEFI "); + break; + default: + pos += scnprintf(buf + pos, count - pos, + "BIOS SOURCE UNKNOWN (%d) ", + resp->tas_config_info.table_source); + break; + } + + pos += scnprintf(buf + pos, count - pos, + "revision is: %d data is: 0x%08x\n", + resp->tas_config_info.table_revision, + resp->tas_config_info.value); + pos += scnprintf(buf + pos, count - pos, "Current MCC: 0x%x\n", + le16_to_cpu(resp->curr_mcc)); + + pos += scnprintf(buf + pos, count - pos, "Block list entries:"); + for (int i = 0; i < ARRAY_SIZE(resp->mcc_block_list); i++) + pos += scnprintf(buf + pos, count - pos, " 0x%x", + le16_to_cpu(resp->mcc_block_list[i])); + + pos += scnprintf(buf + pos, count - pos, + "\nDo TAS Support Dual Radio?: %s\n", + hweight8(resp->valid_radio_mask) > 1 ? + "TRUE" : "FALSE"); + + for (int i = 0; i < ARRAY_SIZE(resp->tas_status_radio); i++) { + int tmp; + unsigned long dynamic_status; + + if (!(resp->valid_radio_mask & BIT(i))) + continue; + + pos += scnprintf(buf + pos, count - pos, + "TAS report for radio:%d\n", i + 1); + pos += scnprintf(buf + pos, count - pos, + "Static status: %sabled\n", + resp->tas_status_radio[i].static_status ? + "En" : "Dis"); + if (!resp->tas_status_radio[i].static_status) { + u8 static_disable_reason = + resp->tas_status_radio[i].static_disable_reason; + + pos += scnprintf(buf + pos, count - pos, + "\tStatic Disabled Reason: "); + if (static_disable_reason >= TAS_DISABLED_REASON_MAX) { + pos += scnprintf(buf + pos, count - pos, + "unsupported value (%d)\n", + static_disable_reason); + continue; + } + + pos += scnprintf(buf + pos, count - pos, + "%s (%d)\n", + tas_dis_reason[static_disable_reason], + static_disable_reason); + continue; + } + + pos += scnprintf(buf + pos, count - pos, "\tANT A %s and ", + (resp->tas_status_radio[i].dynamic_status_ant_a + & BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF"); + + pos += scnprintf(buf + pos, count - pos, "ANT B %s for ", + (resp->tas_status_radio[i].dynamic_status_ant_b + & BIT(TAS_DYNA_ACTIVE)) ? "ON" : "OFF"); + + switch (resp->tas_status_radio[i].band) { + case PHY_BAND_5: + pos += scnprintf(buf + pos, count - pos, "HB\n"); + break; + case PHY_BAND_24: + pos += scnprintf(buf + pos, count - pos, "LB\n"); + break; + case PHY_BAND_6: + pos += scnprintf(buf + pos, count - pos, "UHB\n"); + break; + default: + pos += scnprintf(buf + pos, count - pos, + "Unsupported band (%d)\n", + resp->tas_status_radio[i].band); + break; + } + + pos += scnprintf(buf + pos, count - pos, + "Is near disconnection?: %s\n", + resp->tas_status_radio[i].near_disconnection ? + "True" : "False"); + + pos += scnprintf(buf + pos, count - pos, + "Dynamic status antenna A:\n"); + dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_a; + for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) { + pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n", + tas_current_status[tmp], tmp); + } + pos += scnprintf(buf + pos, count - pos, + "\nDynamic status antenna B:\n"); + dynamic_status = resp->tas_status_radio[i].dynamic_status_ant_b; + for_each_set_bit(tmp, &dynamic_status, TAS_DYNA_STATUS_MAX) { + pos += scnprintf(buf + pos, count - pos, "\t%s (%d)\n", + tas_current_status[tmp], tmp); + } + + tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_a); + pos += scnprintf(buf + pos, count - pos, + "Max antenna A regulatory pwr limit (dBm): %d.%03d\n", + tmp / 8, 125 * (tmp % 8)); + tmp = le16_to_cpu(resp->tas_status_radio[i].max_reg_pwr_limit_ant_b); + pos += scnprintf(buf + pos, count - pos, + "Max antenna B regulatory pwr limit (dBm): %d.%03d\n", + tmp / 8, 125 * (tmp % 8)); + + tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_a); + pos += scnprintf(buf + pos, count - pos, + "Antenna A SAR limit (dBm): %d.%03d\n", + tmp / 8, 125 * (tmp % 8)); + tmp = le16_to_cpu(resp->tas_status_radio[i].sar_limit_ant_b); + pos += scnprintf(buf + pos, count - pos, + "Antenna B SAR limit (dBm): %d.%03d\n", + tmp / 8, 125 * (tmp % 8)); + } + + return pos; +} + +static ssize_t iwl_dbgfs_tas_get_status_read(struct iwl_mld *mld, char *buf, + size_t count) +{ + struct iwl_dhc_cmd cmd = { + .index_and_mask = cpu_to_le32(DHC_TABLE_TOOLS | + DHC_TARGET_UMAC | + DHC_TOOLS_UMAC_GET_TAS_STATUS), + }; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(LEGACY_GROUP, DEBUG_HOST_COMMAND), + .flags = CMD_WANT_SKB, + .len[0] = sizeof(cmd), + .data[0] = &cmd, + }; + struct iwl_dhc_tas_status_resp *resp = NULL; + u32 resp_len = 0; + ssize_t pos = 0; + u32 status; + int ret; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) + return ret; + + pos += scnprintf(buf + pos, count - pos, "\nOEM name: %s\n", + dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); + pos += scnprintf(buf + pos, count - pos, + "\tVendor In Approved List: %s\n", + iwl_is_tas_approved() ? "YES" : "NO"); + + status = iwl_dhc_resp_status(mld->fwrt.fw, hcmd.resp_pkt); + if (status != 1) { + pos += scnprintf(buf + pos, count - pos, + "response status is not success: %d\n", + status); + goto out; + } + + resp = iwl_dhc_resp_data(mld->fwrt.fw, hcmd.resp_pkt, &resp_len); + if (IS_ERR(resp) || resp_len != sizeof(*resp)) { + pos += scnprintf(buf + pos, count - pos, + "Invalid size for TAS response (%u instead of %zd)\n", + resp_len, sizeof(*resp)); + goto out; + } + + pos += iwl_mld_dump_tas_resp(resp, count - pos, buf + pos); + +out: + iwl_free_resp(&hcmd); + return pos; +} + +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_nmi, 10); +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_restart, 10); +WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(he_sniffer_params, 32); +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(fw_dbg_clear, 10); +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(send_echo_cmd, 8); +WIPHY_DEBUGFS_READ_FILE_OPS_MLD(tas_get_status, 2048); + +static ssize_t iwl_dbgfs_wifi_6e_enable_read(struct iwl_mld *mld, + size_t count, u8 *buf) +{ + int err; + u32 value; + + err = iwl_bios_get_dsm(&mld->fwrt, DSM_FUNC_ENABLE_6E, &value); + if (err) + return err; + + return scnprintf(buf, count, "0x%08x\n", value); +} + +MLD_DEBUGFS_READ_FILE_OPS(wifi_6e_enable, 64); + +static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mld *mld, + char *buf, size_t count) +{ + struct iwl_op_mode *opmode = container_of((void *)mld, + struct iwl_op_mode, + op_mode_specific); + struct iwl_rx_cmd_buffer rxb = {}; + struct iwl_rx_packet *pkt; + int n_bytes = count / 2; + int ret = -EINVAL; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + rxb._page = alloc_pages(GFP_KERNEL, 0); + if (!rxb._page) + return -ENOMEM; + pkt = rxb_addr(&rxb); + + ret = hex2bin(page_address(rxb._page), buf, n_bytes); + if (ret) + goto out; + + /* avoid invalid memory access and malformed packet */ + if (n_bytes < sizeof(*pkt) || + n_bytes != sizeof(*pkt) + iwl_rx_packet_payload_len(pkt)) + goto out; + + local_bh_disable(); + iwl_mld_rx(opmode, NULL, &rxb); + local_bh_enable(); + ret = 0; + +out: + iwl_free_rxb(&rxb); + + return ret ?: count; +} + +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(inject_packet, 512); + +#ifdef CONFIG_THERMAL + +static ssize_t iwl_dbgfs_stop_ctdp_write(struct iwl_mld *mld, + char *buf, size_t count) +{ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, + CTDP_CMD_OPERATION_STOP) ? : count; +} + +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(stop_ctdp, 8); + +static ssize_t iwl_dbgfs_start_ctdp_write(struct iwl_mld *mld, + char *buf, size_t count) +{ + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + return iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, + CTDP_CMD_OPERATION_START) ? : count; +} + +WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(start_ctdp, 8); + +#endif /* CONFIG_THERMAL */ + +void +iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) +{ + /* Add debugfs files here */ + + MLD_DEBUGFS_ADD_FILE(fw_nmi, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(fw_restart, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(wifi_6e_enable, debugfs_dir, 0400); + MLD_DEBUGFS_ADD_FILE(he_sniffer_params, debugfs_dir, 0600); + MLD_DEBUGFS_ADD_FILE(fw_dbg_clear, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(send_echo_cmd, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(tas_get_status, debugfs_dir, 0400); +#ifdef CONFIG_THERMAL + MLD_DEBUGFS_ADD_FILE(start_ctdp, debugfs_dir, 0200); + MLD_DEBUGFS_ADD_FILE(stop_ctdp, debugfs_dir, 0200); +#endif + MLD_DEBUGFS_ADD_FILE(inject_packet, debugfs_dir, 0200); + + debugfs_create_bool("rx_ts_ptp", 0600, debugfs_dir, + &mld->monitor.ptp_time); + + /* Create a symlink with mac80211. It will be removed when mac80211 + * exits (before the opmode exits which removes the target.) + */ + if (!IS_ERR(debugfs_dir)) { + char buf[100]; + + snprintf(buf, 100, "../../%pd2", debugfs_dir->d_parent); + debugfs_create_symlink("iwlwifi", mld->wiphy->debugfsdir, + buf); + } +} + +#define VIF_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + WIPHY_DEBUGFS_WRITE_FILE_OPS(vif_##name, bufsz, vif) + +#define VIF_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz) \ + IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(vif_##name, bufsz, vif) \ + +#define VIF_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ + debugfs_create_file(alias, mode, parent, vif, \ + &iwl_dbgfs_vif_##name##_ops) +#define VIF_DEBUGFS_ADD_FILE(name, parent, mode) \ + VIF_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +static ssize_t iwl_dbgfs_vif_bf_params_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + struct ieee80211_vif *vif = data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int link_id = vif->active_links ? __ffs(vif->active_links) : 0; + struct ieee80211_bss_conf *link_conf; + int val; + + if (!strncmp("bf_enable_beacon_filter=", buf, 24)) { + if (sscanf(buf + 24, "%d", &val) != 1) + return -EINVAL; + } else { + return -EINVAL; + } + + if (val != 0 && val != 1) + return -EINVAL; + + link_conf = link_conf_dereference_protected(vif, link_id); + if (WARN_ON(!link_conf)) + return -ENODEV; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + mld_vif->disable_bf = !val; + + if (val) + return iwl_mld_enable_beacon_filter(mld, link_conf, + false) ?: count; + else + return iwl_mld_disable_beacon_filter(mld, vif) ?: count; +} + +static ssize_t iwl_dbgfs_vif_pm_params_write(struct iwl_mld *mld, + char *buf, + size_t count, void *data) +{ + struct ieee80211_vif *vif = data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int val; + + if (!strncmp("use_ps_poll=", buf, 12)) { + if (sscanf(buf + 12, "%d", &val) != 1) + return -EINVAL; + } else { + return -EINVAL; + } + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + mld_vif->use_ps_poll = val; + + return iwl_mld_update_mac_power(mld, vif, false) ?: count; +} + +static ssize_t iwl_dbgfs_vif_low_latency_write(struct iwl_mld *mld, + char *buf, size_t count, + void *data) +{ + struct ieee80211_vif *vif = data; + u8 value; + int ret; + + ret = kstrtou8(buf, 0, &value); + if (ret) + return ret; + + if (value > 1) + return -EINVAL; + + iwl_mld_vif_update_low_latency(mld, vif, value, LOW_LATENCY_DEBUGFS); + + return count; +} + +static ssize_t iwl_dbgfs_vif_low_latency_read(struct ieee80211_vif *vif, + size_t count, char *buf) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + char format[] = "traffic=%d\ndbgfs=%d\nvif_type=%d\nactual=%d\n"; + u8 ll_causes; + + if (WARN_ON(count < sizeof(format))) + return -EINVAL; + + ll_causes = READ_ONCE(mld_vif->low_latency_causes); + + /* all values in format are boolean so the size of format is enough + * for holding the result string + */ + return scnprintf(buf, count, format, + !!(ll_causes & LOW_LATENCY_TRAFFIC), + !!(ll_causes & LOW_LATENCY_DEBUGFS), + !!(ll_causes & LOW_LATENCY_VIF_TYPE), + !!(ll_causes)); +} + +VIF_DEBUGFS_WRITE_FILE_OPS(pm_params, 32); +VIF_DEBUGFS_WRITE_FILE_OPS(bf_params, 32); +VIF_DEBUGFS_READ_WRITE_FILE_OPS(low_latency, 45); + +static int +_iwl_dbgfs_inject_beacon_ie(struct iwl_mld *mld, struct ieee80211_vif *vif, + char *bin, ssize_t len, + bool restore) +{ + struct iwl_mld_vif *mld_vif; + struct iwl_mld_link *mld_link; + struct iwl_mac_beacon_cmd beacon_cmd = {}; + int n_bytes = len / 2; + + /* Element len should be represented by u8 */ + if (n_bytes >= U8_MAX) + return -EINVAL; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + if (!vif) + return -EINVAL; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + mld_vif->beacon_inject_active = true; + mld->hw->extra_beacon_tailroom = n_bytes; + + for_each_mld_vif_valid_link(mld_vif, mld_link) { + u32 offset; + struct ieee80211_tx_info *info; + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + struct ieee80211_chanctx_conf *ctx = + wiphy_dereference(mld->wiphy, link_conf->chanctx_conf); + struct sk_buff *beacon = + ieee80211_beacon_get_template(mld->hw, vif, + NULL, link_id); + + if (!beacon) + return -EINVAL; + + if (!restore && (WARN_ON(!n_bytes || !bin) || + hex2bin(skb_put_zero(beacon, n_bytes), + bin, n_bytes))) { + dev_kfree_skb(beacon); + return -EINVAL; + } + + info = IEEE80211_SKB_CB(beacon); + + beacon_cmd.flags = + cpu_to_le16(iwl_mld_get_rate_flags(mld, info, vif, + link_conf, + ctx->def.chan->band)); + beacon_cmd.byte_cnt = cpu_to_le16((u16)beacon->len); + beacon_cmd.link_id = + cpu_to_le32(mld_link->fw_id); + + iwl_mld_set_tim_idx(mld, &beacon_cmd.tim_idx, + beacon->data, beacon->len); + + offset = iwl_find_ie_offset(beacon->data, + WLAN_EID_S1G_TWT, + beacon->len); + + beacon_cmd.btwt_offset = cpu_to_le32(offset); + + iwl_mld_send_beacon_template_cmd(mld, beacon, &beacon_cmd); + dev_kfree_skb(beacon); + } + + if (restore) + mld_vif->beacon_inject_active = false; + + return 0; +} + +static ssize_t +iwl_dbgfs_vif_inject_beacon_ie_write(struct iwl_mld *mld, + char *buf, size_t count, + void *data) +{ + struct ieee80211_vif *vif = data; + int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, buf, + count, false); + + mld->hw->extra_beacon_tailroom = 0; + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie, 512); + +static ssize_t +iwl_dbgfs_vif_inject_beacon_ie_restore_write(struct iwl_mld *mld, + char *buf, + size_t count, + void *data) +{ + struct ieee80211_vif *vif = data; + int ret = _iwl_dbgfs_inject_beacon_ie(mld, vif, NULL, + 0, true); + + mld->hw->extra_beacon_tailroom = 0; + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(inject_beacon_ie_restore, 512); + +static ssize_t +iwl_dbgfs_vif_twt_setup_write(struct iwl_mld *mld, char *buf, size_t count, + void *data) +{ + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(IWL_ALWAYS_LONG_GROUP, DEBUG_HOST_COMMAND), + }; + struct ieee80211_vif *vif = data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_dhc_cmd *cmd __free(kfree) = NULL; + struct iwl_dhc_twt_operation *dhc_twt_cmd; + u64 target_wake_time; + u32 twt_operation, interval_exp, interval_mantissa, min_wake_duration; + u8 trigger, flow_type, flow_id, protection, tenth_param; + u8 twt_request = 1, broadcast = 0; + int ret; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + ret = sscanf(buf, "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu", + &twt_operation, &target_wake_time, &interval_exp, + &interval_mantissa, &min_wake_duration, &trigger, + &flow_type, &flow_id, &protection, &tenth_param); + + /* the new twt_request parameter is optional for station */ + if ((ret != 9 && ret != 10) || + (ret == 10 && vif->type != NL80211_IFTYPE_STATION && + tenth_param == 1)) + return -EINVAL; + + /* The 10th parameter: + * In STA mode - the TWT type (broadcast or individual) + * In AP mode - the role (0 responder, 2 unsolicited) + */ + if (ret == 10) { + if (vif->type == NL80211_IFTYPE_STATION) + broadcast = tenth_param; + else + twt_request = tenth_param; + } + + cmd = kzalloc(sizeof(*cmd) + sizeof(*dhc_twt_cmd), GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + dhc_twt_cmd = (void *)cmd->data; + dhc_twt_cmd->mac_id = cpu_to_le32(mld_vif->fw_id); + dhc_twt_cmd->twt_operation = cpu_to_le32(twt_operation); + dhc_twt_cmd->target_wake_time = cpu_to_le64(target_wake_time); + dhc_twt_cmd->interval_exp = cpu_to_le32(interval_exp); + dhc_twt_cmd->interval_mantissa = cpu_to_le32(interval_mantissa); + dhc_twt_cmd->min_wake_duration = cpu_to_le32(min_wake_duration); + dhc_twt_cmd->trigger = trigger; + dhc_twt_cmd->flow_type = flow_type; + dhc_twt_cmd->flow_id = flow_id; + dhc_twt_cmd->protection = protection; + dhc_twt_cmd->twt_request = twt_request; + dhc_twt_cmd->negotiation_type = broadcast ? 3 : 0; + + cmd->length = cpu_to_le32(sizeof(*dhc_twt_cmd) >> 2); + cmd->index_and_mask = + cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC | + DHC_INT_UMAC_TWT_OPERATION); + + hcmd.len[0] = sizeof(*cmd) + sizeof(*dhc_twt_cmd); + hcmd.data[0] = cmd; + + ret = iwl_mld_send_cmd(mld, &hcmd); + + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(twt_setup, 256); + +static ssize_t +iwl_dbgfs_vif_twt_operation_write(struct iwl_mld *mld, char *buf, size_t count, + void *data) +{ + struct ieee80211_vif *vif = data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_twt_operation_cmd twt_cmd = {}; + int link_id = vif->active_links ? __ffs(vif->active_links) : 0; + struct iwl_mld_link *mld_link = iwl_mld_link_dereference_check(mld_vif, + link_id); + int ret; + + if (WARN_ON(!mld_link)) + return -ENODEV; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + if (hweight16(vif->active_links) > 1) + return -EOPNOTSUPP; + + ret = sscanf(buf, + "%u %llu %u %u %u %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu %hhu", + &twt_cmd.twt_operation, &twt_cmd.target_wake_time, + &twt_cmd.interval_exponent, &twt_cmd.interval_mantissa, + &twt_cmd.minimum_wake_duration, &twt_cmd.trigger, + &twt_cmd.flow_type, &twt_cmd.flow_id, + &twt_cmd.twt_protection, &twt_cmd.ndp_paging_indicator, + &twt_cmd.responder_pm_mode, &twt_cmd.negotiation_type, + &twt_cmd.twt_request, &twt_cmd.implicit, + &twt_cmd.twt_group_assignment, &twt_cmd.twt_channel, + &twt_cmd.restricted_info_present, &twt_cmd.dl_bitmap_valid, + &twt_cmd.ul_bitmap_valid, &twt_cmd.dl_tid_bitmap, + &twt_cmd.ul_tid_bitmap); + + if (ret != 21) + return -EINVAL; + + twt_cmd.link_id = cpu_to_le32(mld_link->fw_id); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, TWT_OPERATION_CMD), + &twt_cmd); + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(twt_operation, 256); + +static ssize_t iwl_dbgfs_vif_int_mlo_scan_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + struct ieee80211_vif *vif = data; + u32 action; + int ret; + + if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) + return -EINVAL; + + if (kstrtou32(buf, 0, &action)) + return -EINVAL; + + if (action == 0) { + ret = iwl_mld_scan_stop(mld, IWL_MLD_SCAN_INT_MLO, false); + } else if (action == 1) { + iwl_mld_int_mlo_scan(mld, vif); + ret = 0; + } else { + ret = -EINVAL; + } + + return ret ?: count; +} + +VIF_DEBUGFS_WRITE_FILE_OPS(int_mlo_scan, 32); + +void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct dentry *mld_vif_dbgfs = + debugfs_create_dir("iwlmld", vif->debugfs_dir); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + char target[3 * 3 + 11 + (NL80211_WIPHY_NAME_MAXLEN + 1) + + (7 + IFNAMSIZ + 1) + 6 + 1]; + char name[7 + IFNAMSIZ + 1]; + + /* Create symlink for convenience pointing to interface specific + * debugfs entries for the driver. For example, under + * /sys/kernel/debug/iwlwifi/0000\:02\:00.0/iwlmld/ + * find + * netdev:wlan0 -> ../../../ieee80211/phy0/netdev:wlan0/iwlmld/ + */ + snprintf(name, sizeof(name), "%pd", vif->debugfs_dir); + snprintf(target, sizeof(target), "../../../%pd3/iwlmld", + vif->debugfs_dir); + if (!mld_vif->dbgfs_slink) + mld_vif->dbgfs_slink = + debugfs_create_symlink(name, mld->debugfs_dir, target); + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM && + vif->type == NL80211_IFTYPE_STATION) { + VIF_DEBUGFS_ADD_FILE(pm_params, mld_vif_dbgfs, 0200); + VIF_DEBUGFS_ADD_FILE(bf_params, mld_vif_dbgfs, 0200); + } + + if (vif->type == NL80211_IFTYPE_AP) { + VIF_DEBUGFS_ADD_FILE(inject_beacon_ie, mld_vif_dbgfs, 0200); + VIF_DEBUGFS_ADD_FILE(inject_beacon_ie_restore, + mld_vif_dbgfs, 0200); + } + + VIF_DEBUGFS_ADD_FILE(low_latency, mld_vif_dbgfs, 0600); + VIF_DEBUGFS_ADD_FILE(twt_setup, mld_vif_dbgfs, 0200); + VIF_DEBUGFS_ADD_FILE(twt_operation, mld_vif_dbgfs, 0200); + VIF_DEBUGFS_ADD_FILE(int_mlo_scan, mld_vif_dbgfs, 0200); +} +#define LINK_DEBUGFS_WRITE_FILE_OPS(name, bufsz) \ + WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf) + +#define LINK_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ + debugfs_create_file(alias, mode, parent, link_conf, \ + &iwl_dbgfs_link_##name##_ops) +#define LINK_DEBUGFS_ADD_FILE(name, parent, mode) \ + LINK_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct dentry *dir) +{ + struct dentry *mld_link_dir; + + mld_link_dir = debugfs_lookup("iwlmld", dir); + + /* For non-MLO vifs, the dir of deflink is the same as the vif's one. + * so if iwlmld dir already exists, this means that this is deflink. + * If not, this is a per-link dir of a MLO vif, add in it the iwlmld + * dir. + */ + if (!mld_link_dir) + mld_link_dir = debugfs_create_dir("iwlmld", dir); +} + +static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, + size_t count, void *data, bool v3) +{ + struct ieee80211_link_sta *link_sta = data; + struct iwl_mld_link_sta *mld_link_sta; + u32 rate; + u32 partial = false; + char pretty_rate[100]; + int ret; + u8 fw_sta_id; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (WARN_ON(!mld_link_sta)) + return -EINVAL; + + fw_sta_id = mld_link_sta->fw_id; + + if (sscanf(buf, "%i %i", &rate, &partial) == 0) + return -EINVAL; + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + /* input is in FW format (v2 or v3) so convert to v3 */ + rate = iwl_v3_rate_from_v2_v3(cpu_to_le32(rate), v3); + rate = le32_to_cpu(iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3)); + + ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, + partial ? IWL_TLC_DEBUG_PARTIAL_FIXED_RATE : + IWL_TLC_DEBUG_FIXED_RATE, + rate); + + rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), rate); + + IWL_DEBUG_RATE(mld, "sta_id %d rate %s partial: %d, ret:%d\n", + fw_sta_id, pretty_rate, partial, ret); + + return ret ? : count; +} + +static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, false); +} + +static ssize_t iwl_dbgfs_fixed_rate_v3_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + return _iwl_dbgfs_fixed_rate_write(mld, buf, count, data, true); +} + +static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf, + size_t count, void *data) +{ + struct ieee80211_link_sta *link_sta = data; + struct iwl_mld_link_sta *mld_link_sta; + u32 type, value; + int ret; + u8 fw_sta_id; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (WARN_ON(!mld_link_sta)) + return -EINVAL; + + fw_sta_id = mld_link_sta->fw_id; + + if (sscanf(buf, "%i %i", &type, &value) != 2) { + IWL_DEBUG_RATE(mld, "usage <type> <value>\n"); + return -EINVAL; + } + + if (iwl_mld_dbgfs_fw_cmd_disabled(mld)) + return -EIO; + + ret = iwl_mld_send_tlc_dhc(mld, fw_sta_id, type, value); + + return ret ? : count; +} + +#define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \ + debugfs_create_file(alias, mode, parent, link_sta, \ + &iwl_dbgfs_##name##_ops) +#define LINK_STA_DEBUGFS_ADD_FILE(name, parent, mode) \ + LINK_STA_DEBUGFS_ADD_FILE_ALIAS(#name, name, parent, mode) + +#define LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(name, bufsz) \ + WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, link_sta) + +LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(tlc_dhc, 64); +LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate, 64); +LINK_STA_WIPHY_DEBUGFS_WRITE_OPS(fixed_rate_v3, 64); + +void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir) +{ + LINK_STA_DEBUGFS_ADD_FILE(fixed_rate, dir, 0200); + LINK_STA_DEBUGFS_ADD_FILE(fixed_rate_v3, dir, 0200); + LINK_STA_DEBUGFS_ADD_FILE(tlc_dhc, dir, 0200); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h new file mode 100644 index 000000000000..eeba35342ba1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/debugfs.h @@ -0,0 +1,244 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "iface.h" +#include "sta.h" + +#define MLD_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ +struct dbgfs_##name##_data { \ + argtype *arg; \ + bool read_done; \ + ssize_t rlen; \ + char buf[buflen]; \ +}; \ +static int _iwl_dbgfs_##name##_open(struct inode *inode, \ + struct file *file) \ +{ \ + struct dbgfs_##name##_data *data; \ + \ + if ((file->f_flags & O_ACCMODE) == O_RDWR) \ + return -EOPNOTSUPP; \ + \ + data = kzalloc(sizeof(*data), GFP_KERNEL); \ + if (!data) \ + return -ENOMEM; \ + \ + data->read_done = false; \ + data->arg = inode->i_private; \ + file->private_data = data; \ + \ + return 0; \ +} + +#define MLD_DEBUGFS_READ_WRAPPER(name) \ +static ssize_t _iwl_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + \ + if (!data->read_done) { \ + data->read_done = true; \ + data->rlen = iwl_dbgfs_##name##_read(data->arg, \ + sizeof(data->buf),\ + data->buf); \ + } \ + \ + if (data->rlen < 0) \ + return data->rlen; \ + return simple_read_from_buffer(user_buf, count, ppos, \ + data->buf, data->rlen); \ +} + +static int _iwl_dbgfs_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +#define _MLD_DEBUGFS_READ_FILE_OPS(name, buflen, argtype) \ +MLD_DEBUGFS_OPEN_WRAPPER(name, buflen, argtype) \ +MLD_DEBUGFS_READ_WRAPPER(name) \ +static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = _iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ +} + +#define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \ +static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \ + struct file *file, char *buf, \ + size_t count, void *data) \ +{ \ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \ + return iwl_dbgfs_##name##_write(mld, buf, count, data); \ +} + +static inline struct iwl_mld * +iwl_mld_from_link_sta(struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_vif *vif = + iwl_mld_sta_from_mac80211(link_sta->sta)->vif; + return iwl_mld_vif_from_mac80211(vif)->mld; +} + +static inline struct iwl_mld * +iwl_mld_from_bss_conf(struct ieee80211_bss_conf *link) +{ + return iwl_mld_vif_from_mac80211(link->vif)->mld; +} + +static inline struct iwl_mld *iwl_mld_from_vif(struct ieee80211_vif *vif) +{ + return iwl_mld_vif_from_mac80211(vif)->mld; +} + +#define WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype) \ +WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \ +static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct ieee80211_##objtype *arg = file->private_data; \ + struct iwl_mld *mld = iwl_mld_from_##objtype(arg); \ + char buf[bufsz] = {}; \ + \ + return wiphy_locked_debugfs_write(mld->wiphy, file, \ + buf, sizeof(buf), \ + user_buf, count, \ + iwl_dbgfs_##name##_write_handler, \ + arg); \ +} + +#define WIPHY_DEBUGFS_WRITE_FILE_OPS(name, bufsz, objtype) \ + WIPHY_DEBUGFS_WRITE_WRAPPER(name, bufsz, objtype) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = __iwl_dbgfs_##name##_write, \ + .open = simple_open, \ + .llseek = generic_file_llseek, \ + } + +#define WIPHY_DEBUGFS_READ_HANDLER_WRAPPER_MLD(name) \ +static ssize_t iwl_dbgfs_##name##_read_handler(struct wiphy *wiphy, \ + struct file *file, char *buf, \ + size_t count, void *data) \ +{ \ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \ + return iwl_dbgfs_##name##_read(mld, buf, count); \ +} + +#define WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name) \ +static ssize_t iwl_dbgfs_##name##_write_handler(struct wiphy *wiphy, \ + struct file *file, char *buf, \ + size_t count, void *data) \ +{ \ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); \ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); \ + return iwl_dbgfs_##name##_write(mld, buf, count); \ +} + +#define WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ +WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER_MLD(name) \ +static ssize_t __iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + struct iwl_mld *mld = data->arg; \ + \ + return wiphy_locked_debugfs_write(mld->wiphy, file, \ + data->buf, sizeof(data->buf), \ + user_buf, count, \ + iwl_dbgfs_##name##_write_handler, \ + NULL); \ +} + +#define WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \ +WIPHY_DEBUGFS_READ_HANDLER_WRAPPER_MLD(name) \ +static ssize_t __iwl_dbgfs_##name##_read(struct file *file, \ + char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + struct iwl_mld *mld = data->arg; \ + \ + if (!data->read_done) { \ + data->read_done = true; \ + data->rlen = wiphy_locked_debugfs_read(mld->wiphy, \ + file, data->buf, sizeof(data->buf), \ + user_buf, count, ppos, \ + iwl_dbgfs_##name##_read_handler, NULL); \ + return data->rlen; \ + } \ + \ + if (data->rlen < 0) \ + return data->rlen; \ + return simple_read_from_buffer(user_buf, count, ppos, \ + data->buf, data->rlen); \ +} + +#define WIPHY_DEBUGFS_READ_FILE_OPS_MLD(name, bufsz) \ + MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \ + WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .read = __iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ + } + +#define WIPHY_DEBUGFS_WRITE_FILE_OPS_MLD(name, bufsz) \ + MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \ + WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = __iwl_dbgfs_##name##_write, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ + } + +#define WIPHY_DEBUGFS_READ_WRITE_FILE_OPS_MLD(name, bufsz) \ + MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct iwl_mld) \ + WIPHY_DEBUGFS_WRITE_WRAPPER_MLD(name) \ + WIPHY_DEBUGFS_READ_WRAPPER_MLD(name) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = __iwl_dbgfs_##name##_write, \ + .read = __iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ + } + +#define WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype) \ +WIPHY_DEBUGFS_WRITE_HANDLER_WRAPPER(name) \ +static ssize_t _iwl_dbgfs_##name##_write(struct file *file, \ + const char __user *user_buf, \ + size_t count, loff_t *ppos) \ +{ \ + struct dbgfs_##name##_data *data = file->private_data; \ + struct ieee80211_##objtype *arg = data->arg; \ + struct iwl_mld *mld = iwl_mld_from_##objtype(arg); \ + char buf[bufsz] = {}; \ + \ + return wiphy_locked_debugfs_write(mld->wiphy, file, \ + buf, sizeof(buf), \ + user_buf, count, \ + iwl_dbgfs_##name##_write_handler, \ + arg); \ +} + +#define IEEE80211_WIPHY_DEBUGFS_READ_WRITE_FILE_OPS(name, bufsz, objtype) \ + MLD_DEBUGFS_OPEN_WRAPPER(name, bufsz, struct ieee80211_##objtype) \ + WIPHY_DEBUGFS_WRITE_WRAPPER_IEEE80211(name, bufsz, objtype) \ + MLD_DEBUGFS_READ_WRAPPER(name) \ + static const struct file_operations iwl_dbgfs_##name##_ops = { \ + .write = _iwl_dbgfs_##name##_write, \ + .read = _iwl_dbgfs_##name##_read, \ + .open = _iwl_dbgfs_##name##_open, \ + .llseek = generic_file_llseek, \ + .release = _iwl_dbgfs_release, \ + } diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c new file mode 100644 index 000000000000..f77ba21a174d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2025 Intel Corporation + */ +#include <linux/etherdevice.h> +#include <linux/math64.h> +#include <net/cfg80211.h> +#include "mld.h" +#include "iface.h" +#include "phy.h" +#include "iwl-io.h" +#include "iwl-prph.h" +#include "constants.h" +#include "fw/api/location.h" +#include "ftm-initiator.h" + +static void iwl_mld_ftm_cmd_common(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_tof_range_req_cmd *cmd, + struct cfg80211_pmsr_request *req) +{ + int i; + + cmd->initiator_flags = + cpu_to_le32(IWL_TOF_INITIATOR_FLAGS_MACADDR_RANDOM | + IWL_TOF_INITIATOR_FLAGS_NON_ASAP_SUPPORT); + cmd->request_id = req->cookie; + cmd->num_of_ap = req->n_peers; + + /* Use a large value for "no timeout". Don't use the maximum value + * because of fw limitations. + */ + if (req->timeout) + cmd->req_timeout_ms = cpu_to_le32(min(req->timeout, 0xfffff)); + else + cmd->req_timeout_ms = cpu_to_le32(0xfffff); + + memcpy(cmd->macaddr_template, req->mac_addr, ETH_ALEN); + for (i = 0; i < ETH_ALEN; i++) + cmd->macaddr_mask[i] = ~req->mac_addr_mask[i]; + + if (vif->cfg.assoc) { + memcpy(cmd->range_req_bssid, vif->bss_conf.bssid, ETH_ALEN); + + /* AP's TSF is only relevant if associated */ + for (i = 0; i < req->n_peers; i++) { + if (req->peers[i].report_ap_tsf) { + struct iwl_mld_vif *mld_vif = + iwl_mld_vif_from_mac80211(vif); + + cmd->tsf_mac_id = cpu_to_le32(mld_vif->fw_id); + return; + } + } + } else { + eth_broadcast_addr(cmd->range_req_bssid); + } + + /* Don't report AP's TSF */ + cmd->tsf_mac_id = cpu_to_le32(0xff); +} + +static int +iwl_mld_ftm_set_target_chandef(struct iwl_mld *mld, + struct cfg80211_pmsr_request_peer *peer, + struct iwl_tof_range_req_ap_entry *target) +{ + u32 freq = peer->chandef.chan->center_freq; + + target->channel_num = ieee80211_frequency_to_channel(freq); + + switch (peer->chandef.width) { + case NL80211_CHAN_WIDTH_20_NOHT: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_LEGACY; + target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + break; + case NL80211_CHAN_WIDTH_20: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT; + target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS; + break; + case NL80211_CHAN_WIDTH_40: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT; + target->format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS; + break; + case NL80211_CHAN_WIDTH_80: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_VHT; + target->format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS; + break; + case NL80211_CHAN_WIDTH_160: + target->format_bw = IWL_LOCATION_FRAME_FORMAT_HE; + target->format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS; + break; + default: + IWL_ERR(mld, "Unsupported BW in FTM request (%d)\n", + peer->chandef.width); + return -EINVAL; +} + + /* non EDCA based measurement must use HE preamble */ + if (peer->ftm.trigger_based || peer->ftm.non_trigger_based) + target->format_bw |= IWL_LOCATION_FRAME_FORMAT_HE; + + target->ctrl_ch_position = + (peer->chandef.width > NL80211_CHAN_WIDTH_20) ? + iwl_mld_get_fw_ctrl_pos(&peer->chandef) : 0; + + target->band = iwl_mld_nl80211_band_to_fw(peer->chandef.chan->band); + return 0; +} + +#define FTM_SET_FLAG(flag) (target->initiator_ap_flags |= \ + cpu_to_le32(IWL_INITIATOR_AP_FLAGS_##flag)) + +static void +iwl_mld_ftm_set_target_flags(struct iwl_mld *mld, + struct cfg80211_pmsr_request_peer *peer, + struct iwl_tof_range_req_ap_entry *target) +{ + target->initiator_ap_flags = cpu_to_le32(0); + + if (peer->ftm.asap) + FTM_SET_FLAG(ASAP); + + if (peer->ftm.request_lci) + FTM_SET_FLAG(LCI_REQUEST); + + if (peer->ftm.request_civicloc) + FTM_SET_FLAG(CIVIC_REQUEST); + + if (IWL_MLD_FTM_INITIATOR_DYNACK) + FTM_SET_FLAG(DYN_ACK); + + if (IWL_MLD_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_LINEAR_REG) + FTM_SET_FLAG(ALGO_LR); + else if (IWL_MLD_FTM_INITIATOR_ALGO == IWL_TOF_ALGO_TYPE_FFT) + FTM_SET_FLAG(ALGO_FFT); + + if (peer->ftm.trigger_based) + FTM_SET_FLAG(TB); + else if (peer->ftm.non_trigger_based) + FTM_SET_FLAG(NON_TB); + + if ((peer->ftm.trigger_based || peer->ftm.non_trigger_based) && + peer->ftm.lmr_feedback) + FTM_SET_FLAG(LMR_FEEDBACK); +} + +static void iwl_mld_ftm_set_sta(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_pmsr_request_peer *peer, + struct iwl_tof_range_req_ap_entry *target) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + u32 sta_id_mask; + + target->sta_id = IWL_INVALID_STA; + + /* TODO: add ftm_unprotected debugfs support */ + + if (!vif->cfg.assoc || !mld_vif->ap_sta) + return; + + sta_id_mask = iwl_mld_fw_sta_id_mask(mld, mld_vif->ap_sta); + if (WARN_ON(hweight32(sta_id_mask) != 1)) + return; + + target->sta_id = __ffs(sta_id_mask); + + if (mld_vif->ap_sta->mfp && + (peer->ftm.trigger_based || peer->ftm.non_trigger_based)) + FTM_SET_FLAG(PMF); +} + +static int +iwl_mld_ftm_set_target(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_pmsr_request_peer *peer, + struct iwl_tof_range_req_ap_entry *target) +{ + u32 i2r_max_sts; + int ret; + + ret = iwl_mld_ftm_set_target_chandef(mld, peer, target); + if (ret) + return ret; + + memcpy(target->bssid, peer->addr, ETH_ALEN); + target->burst_period = cpu_to_le16(peer->ftm.burst_period); + target->samples_per_burst = peer->ftm.ftms_per_burst; + target->num_of_bursts = peer->ftm.num_bursts_exp; + iwl_mld_ftm_set_target_flags(mld, peer, target); + iwl_mld_ftm_set_sta(mld, vif, peer, target); + + /* TODO: add secured ranging support */ + + i2r_max_sts = IWL_MLD_FTM_I2R_MAX_STS > 1 ? 1 : + IWL_MLD_FTM_I2R_MAX_STS; + + target->r2i_ndp_params = IWL_MLD_FTM_R2I_MAX_REP | + (IWL_MLD_FTM_R2I_MAX_STS << IWL_LOCATION_MAX_STS_POS) | + (IWL_MLD_FTM_R2I_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS); + target->i2r_ndp_params = IWL_MLD_FTM_I2R_MAX_REP | + (i2r_max_sts << IWL_LOCATION_MAX_STS_POS) | + (IWL_MLD_FTM_I2R_MAX_TOTAL_LTF << IWL_LOCATION_TOTAL_LTF_POS); + + if (peer->ftm.non_trigger_based) { + target->min_time_between_msr = + cpu_to_le16(IWL_MLD_FTM_NON_TB_MIN_TIME_BETWEEN_MSR); + target->burst_period = + cpu_to_le16(IWL_MLD_FTM_NON_TB_MAX_TIME_BETWEEN_MSR); + } else { + target->min_time_between_msr = cpu_to_le16(0); + } + + /* TODO: Beacon interval is currently unknown, so use the common value + * of 100 TUs. + */ + target->beacon_interval = cpu_to_le16(100); + + return 0; +} + +int iwl_mld_ftm_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_pmsr_request *req) +{ + struct iwl_tof_range_req_cmd cmd; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), + .dataflags[0] = IWL_HCMD_DFL_DUP, + .data[0] = &cmd, + .len[0] = sizeof(cmd), + }; + u8 i; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (mld->ftm_initiator.req) + return -EBUSY; + + if (req->n_peers > ARRAY_SIZE(cmd.ap)) + return -EINVAL; + + memset(&cmd, 0, sizeof(cmd)); + + iwl_mld_ftm_cmd_common(mld, vif, (void *)&cmd, req); + + for (i = 0; i < cmd.num_of_ap; i++) { + struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; + struct iwl_tof_range_req_ap_entry *target = &cmd.ap[i]; + + ret = iwl_mld_ftm_set_target(mld, vif, peer, target); + if (ret) + return ret; + } + + /* TODO: get the status from the response*/ + ret = iwl_mld_send_cmd(mld, &hcmd); + if (!ret) { + mld->ftm_initiator.req = req; + mld->ftm_initiator.req_wdev = ieee80211_vif_to_wdev(vif); + } + + return ret; +} + +static void iwl_mld_ftm_reset(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + mld->ftm_initiator.req = NULL; + mld->ftm_initiator.req_wdev = NULL; + memset(mld->ftm_initiator.responses, 0, + sizeof(mld->ftm_initiator.responses)); +} + +static int iwl_mld_ftm_range_resp_valid(struct iwl_mld *mld, u8 request_id, + u8 num_of_aps) +{ + if (IWL_FW_CHECK(mld, request_id != (u8)mld->ftm_initiator.req->cookie, + "Request ID mismatch, got %u, active %u\n", + request_id, (u8)mld->ftm_initiator.req->cookie)) + return -EINVAL; + + if (IWL_FW_CHECK(mld, num_of_aps > mld->ftm_initiator.req->n_peers || + num_of_aps > IWL_TOF_MAX_APS, + "FTM range response: invalid num of APs (%u)\n", + num_of_aps)) + return -EINVAL; + + return 0; +} + +static int iwl_mld_ftm_find_peer(struct cfg80211_pmsr_request *req, + const u8 *addr) +{ + for (int i = 0; i < req->n_peers; i++) { + struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; + + if (ether_addr_equal_unaligned(peer->addr, addr)) + return i; + } + + return -ENOENT; +} + +static void iwl_mld_debug_range_resp(struct iwl_mld *mld, u8 index, + struct cfg80211_pmsr_result *res) +{ + s64 rtt_avg = div_s64(res->ftm.rtt_avg * 100, 6666); + + IWL_DEBUG_INFO(mld, "entry %d\n", index); + IWL_DEBUG_INFO(mld, "\tstatus: %d\n", res->status); + IWL_DEBUG_INFO(mld, "\tBSSID: %pM\n", res->addr); + IWL_DEBUG_INFO(mld, "\thost time: %llu\n", res->host_time); + IWL_DEBUG_INFO(mld, "\tburst index: %d\n", res->ftm.burst_index); + IWL_DEBUG_INFO(mld, "\tsuccess num: %u\n", res->ftm.num_ftmr_successes); + IWL_DEBUG_INFO(mld, "\trssi: %d\n", res->ftm.rssi_avg); + IWL_DEBUG_INFO(mld, "\trssi spread: %d\n", res->ftm.rssi_spread); + IWL_DEBUG_INFO(mld, "\trtt: %lld\n", res->ftm.rtt_avg); + IWL_DEBUG_INFO(mld, "\trtt var: %llu\n", res->ftm.rtt_variance); + IWL_DEBUG_INFO(mld, "\trtt spread: %llu\n", res->ftm.rtt_spread); + IWL_DEBUG_INFO(mld, "\tdistance: %lld\n", rtt_avg); +} + +void iwl_mld_handle_ftm_resp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_tof_range_rsp_ntfy *fw_resp = (void *)pkt->data; + u8 num_of_aps, last_in_batch; + + if (IWL_FW_CHECK(mld, !mld->ftm_initiator.req, + "FTM response without a pending request\n")) + return; + + if (iwl_mld_ftm_range_resp_valid(mld, fw_resp->request_id, + fw_resp->num_of_aps)) + return; + + num_of_aps = fw_resp->num_of_aps; + last_in_batch = fw_resp->last_report; + + IWL_DEBUG_INFO(mld, "Range response received\n"); + IWL_DEBUG_INFO(mld, "request id: %llu, num of entries: %u\n", + mld->ftm_initiator.req->cookie, num_of_aps); + + for (int i = 0; i < num_of_aps; i++) { + struct cfg80211_pmsr_result result = {}; + struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap; + int peer_idx; + + fw_ap = &fw_resp->ap[i]; + result.final = fw_ap->last_burst; + result.ap_tsf = le32_to_cpu(fw_ap->start_tsf); + result.ap_tsf_valid = 1; + + peer_idx = iwl_mld_ftm_find_peer(mld->ftm_initiator.req, + fw_ap->bssid); + if (peer_idx < 0) { + IWL_WARN(mld, + "Unknown address (%pM, target #%d) in FTM response\n", + fw_ap->bssid, i); + continue; + } + + switch (fw_ap->measure_status) { + case IWL_TOF_ENTRY_SUCCESS: + result.status = NL80211_PMSR_STATUS_SUCCESS; + break; + case IWL_TOF_ENTRY_TIMING_MEASURE_TIMEOUT: + result.status = NL80211_PMSR_STATUS_TIMEOUT; + break; + case IWL_TOF_ENTRY_NO_RESPONSE: + result.status = NL80211_PMSR_STATUS_FAILURE; + result.ftm.failure_reason = + NL80211_PMSR_FTM_FAILURE_NO_RESPONSE; + break; + case IWL_TOF_ENTRY_REQUEST_REJECTED: + result.status = NL80211_PMSR_STATUS_FAILURE; + result.ftm.failure_reason = + NL80211_PMSR_FTM_FAILURE_PEER_BUSY; + result.ftm.busy_retry_time = fw_ap->refusal_period; + break; + default: + result.status = NL80211_PMSR_STATUS_FAILURE; + result.ftm.failure_reason = + NL80211_PMSR_FTM_FAILURE_UNSPECIFIED; + break; + } + memcpy(result.addr, fw_ap->bssid, ETH_ALEN); + + /* TODO: convert the timestamp from the result to systime */ + result.host_time = ktime_get_boottime_ns(); + + result.type = NL80211_PMSR_TYPE_FTM; + result.ftm.burst_index = mld->ftm_initiator.responses[peer_idx]; + mld->ftm_initiator.responses[peer_idx]++; + result.ftm.rssi_avg = fw_ap->rssi; + result.ftm.rssi_avg_valid = 1; + result.ftm.rssi_spread = fw_ap->rssi_spread; + result.ftm.rssi_spread_valid = 1; + result.ftm.rtt_avg = (s32)le32_to_cpu(fw_ap->rtt); + result.ftm.rtt_avg_valid = 1; + result.ftm.rtt_variance = le32_to_cpu(fw_ap->rtt_variance); + result.ftm.rtt_variance_valid = 1; + result.ftm.rtt_spread = le32_to_cpu(fw_ap->rtt_spread); + result.ftm.rtt_spread_valid = 1; + + cfg80211_pmsr_report(mld->ftm_initiator.req_wdev, + mld->ftm_initiator.req, + &result, GFP_KERNEL); + + if (fw_has_api(&mld->fw->ucode_capa, + IWL_UCODE_TLV_API_FTM_RTT_ACCURACY)) + IWL_DEBUG_INFO(mld, "RTT confidence: %u\n", + fw_ap->rttConfidence); + + iwl_mld_debug_range_resp(mld, i, &result); + } + + if (last_in_batch) { + cfg80211_pmsr_complete(mld->ftm_initiator.req_wdev, + mld->ftm_initiator.req, + GFP_KERNEL); + iwl_mld_ftm_reset(mld); + } +} + +void iwl_mld_ftm_restart_cleanup(struct iwl_mld *mld) +{ + struct cfg80211_pmsr_result result = { + .status = NL80211_PMSR_STATUS_FAILURE, + .final = 1, + .host_time = ktime_get_boottime_ns(), + .type = NL80211_PMSR_TYPE_FTM, + }; + + if (!mld->ftm_initiator.req) + return; + + for (int i = 0; i < mld->ftm_initiator.req->n_peers; i++) { + memcpy(result.addr, mld->ftm_initiator.req->peers[i].addr, + ETH_ALEN); + + cfg80211_pmsr_report(mld->ftm_initiator.req_wdev, + mld->ftm_initiator.req, + &result, GFP_KERNEL); + } + + cfg80211_pmsr_complete(mld->ftm_initiator.req_wdev, + mld->ftm_initiator.req, GFP_KERNEL); + iwl_mld_ftm_reset(mld); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h new file mode 100644 index 000000000000..3fab25a52508 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ftm-initiator.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2025 Intel Corporation + */ +#ifndef __iwl_mld_ftm_initiator_h__ +#define __iwl_mld_ftm_initiator_h__ + +/** + * struct ftm_initiator_data - FTM initiator data + * + * @req: a pointer to cfg80211 FTM request + * @req_wdev: a pointer to the wdev that requested the current FTM request + * @responses: the number of responses received for the current FTM session. + * Used for tracking the burst index in a periodic request. + */ +struct ftm_initiator_data { + struct cfg80211_pmsr_request *req; + struct wireless_dev *req_wdev; + int responses[IWL_TOF_MAX_APS]; +}; + +int iwl_mld_ftm_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_pmsr_request *req); + +void iwl_mld_handle_ftm_resp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_ftm_restart_cleanup(struct iwl_mld *mld); + +#endif /* __iwl_mld_ftm_initiator_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/fw.c b/drivers/net/wireless/intel/iwlwifi/mld/fw.c new file mode 100644 index 000000000000..73ed8d5cab43 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/fw.c @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "mld.h" + +#include "fw/api/alive.h" +#include "fw/api/scan.h" +#include "fw/api/rx.h" +#include "phy.h" +#include "fw/dbg.h" +#include "fw/pnvm.h" +#include "hcmd.h" +#include "power.h" +#include "mcc.h" +#include "led.h" +#include "coex.h" +#include "regulatory.h" +#include "thermal.h" + +static int iwl_mld_send_tx_ant_cfg(struct iwl_mld *mld) +{ + struct iwl_tx_ant_cfg_cmd cmd; + + lockdep_assert_wiphy(mld->wiphy); + + cmd.valid = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld)); + + IWL_DEBUG_FW(mld, "select valid tx ant: %u\n", cmd.valid); + + return iwl_mld_send_cmd_pdu(mld, TX_ANT_CONFIGURATION_CMD, &cmd); +} + +static int iwl_mld_send_rss_cfg_cmd(struct iwl_mld *mld) +{ + struct iwl_rss_config_cmd cmd = { + .flags = cpu_to_le32(IWL_RSS_ENABLE), + .hash_mask = BIT(IWL_RSS_HASH_TYPE_IPV4_TCP) | + BIT(IWL_RSS_HASH_TYPE_IPV4_UDP) | + BIT(IWL_RSS_HASH_TYPE_IPV4_PAYLOAD) | + BIT(IWL_RSS_HASH_TYPE_IPV6_TCP) | + BIT(IWL_RSS_HASH_TYPE_IPV6_UDP) | + BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD), + }; + + lockdep_assert_wiphy(mld->wiphy); + + /* Do not direct RSS traffic to Q 0 which is our fallback queue */ + for (int i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++) + cmd.indirection_table[i] = + 1 + (i % (mld->trans->info.num_rxqs - 1)); + netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key)); + + return iwl_mld_send_cmd_pdu(mld, RSS_CONFIG_CMD, &cmd); +} + +static int iwl_mld_config_scan(struct iwl_mld *mld) +{ + struct iwl_scan_config cmd = { + .tx_chains = cpu_to_le32(iwl_mld_get_valid_tx_ant(mld)), + .rx_chains = cpu_to_le32(iwl_mld_get_valid_rx_ant(mld)) + }; + + return iwl_mld_send_cmd_pdu(mld, WIDE_ID(LONG_GROUP, SCAN_CFG_CMD), + &cmd); +} + +static void iwl_mld_alive_imr_data(struct iwl_trans *trans, + const struct iwl_imr_alive_info *imr_info) +{ + struct iwl_imr_data *imr_data = &trans->dbg.imr_data; + + imr_data->imr_enable = le32_to_cpu(imr_info->enabled); + imr_data->imr_size = le32_to_cpu(imr_info->size); + imr_data->imr2sram_remainbyte = imr_data->imr_size; + imr_data->imr_base_addr = imr_info->base_addr; + imr_data->imr_curr_addr = le64_to_cpu(imr_data->imr_base_addr); + + if (imr_data->imr_enable) + return; + + for (int i = 0; i < ARRAY_SIZE(trans->dbg.active_regions); i++) { + struct iwl_fw_ini_region_tlv *reg; + + if (!trans->dbg.active_regions[i]) + continue; + + reg = (void *)trans->dbg.active_regions[i]->data; + + /* We have only one DRAM IMR region, so we + * can break as soon as we find the first + * one. + */ + if (reg->type == IWL_FW_INI_REGION_DRAM_IMR) { + trans->dbg.unsupported_region_msk |= BIT(i); + break; + } + } +} + +struct iwl_mld_alive_data { + __le32 sku_id[3]; + bool valid; +}; + +static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *data) +{ + unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); + unsigned int expected_sz; + struct iwl_mld *mld = + container_of(notif_wait, struct iwl_mld, notif_wait); + struct iwl_trans *trans = mld->trans; + u32 version = iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP, + UCODE_ALIVE_NTFY, 0); + struct iwl_mld_alive_data *alive_data = data; + struct iwl_alive_ntf *palive; + struct iwl_umac_alive *umac; + struct iwl_lmac_alive *lmac1; + struct iwl_lmac_alive *lmac2 = NULL; + u32 lmac_error_event_table; + u32 umac_error_table; + u16 status; + + switch (version) { + case 6: + case 7: + expected_sz = sizeof(struct iwl_alive_ntf_v6); + break; + case 8: + expected_sz = sizeof(struct iwl_alive_ntf); + break; + default: + return false; + } + + if (pkt_len != expected_sz) + return false; + + palive = (void *)pkt->data; + + iwl_mld_alive_imr_data(trans, &palive->imr); + + umac = &palive->umac_data; + lmac1 = &palive->lmac_data[0]; + lmac2 = &palive->lmac_data[1]; + status = le16_to_cpu(palive->status); + + BUILD_BUG_ON(sizeof(alive_data->sku_id) != + sizeof(palive->sku_id.data)); + memcpy(alive_data->sku_id, palive->sku_id.data, + sizeof(palive->sku_id.data)); + + IWL_DEBUG_FW(mld, "Got sku_id: 0x0%x 0x0%x 0x0%x\n", + le32_to_cpu(alive_data->sku_id[0]), + le32_to_cpu(alive_data->sku_id[1]), + le32_to_cpu(alive_data->sku_id[2])); + + lmac_error_event_table = + le32_to_cpu(lmac1->dbg_ptrs.error_event_table_ptr); + iwl_fw_lmac1_set_alive_err_table(trans, lmac_error_event_table); + + if (lmac2) + trans->dbg.lmac_error_event_table[1] = + le32_to_cpu(lmac2->dbg_ptrs.error_event_table_ptr); + + umac_error_table = le32_to_cpu(umac->dbg_ptrs.error_info_addr) & + ~FW_ADDR_CACHE_CONTROL; + + if (umac_error_table >= trans->mac_cfg->base->min_umac_error_event_table) + iwl_fw_umac_set_alive_err_table(trans, umac_error_table); + else + IWL_ERR(mld, "Not valid error log pointer 0x%08X\n", + umac_error_table); + + alive_data->valid = status == IWL_ALIVE_STATUS_OK; + + IWL_DEBUG_FW(mld, + "Alive ucode status 0x%04x revision 0x%01X 0x%01X\n", + status, lmac1->ver_type, lmac1->ver_subtype); + + if (lmac2) + IWL_DEBUG_FW(mld, "Alive ucode CDB\n"); + + IWL_DEBUG_FW(mld, + "UMAC version: Major - 0x%x, Minor - 0x%x\n", + le32_to_cpu(umac->umac_major), + le32_to_cpu(umac->umac_minor)); + + if (version >= 7) + IWL_DEBUG_FW(mld, "FW alive flags 0x%x\n", + le16_to_cpu(palive->flags)); + + if (version >= 8) + IWL_DEBUG_FW(mld, "platform_id 0x%llx\n", + le64_to_cpu(palive->platform_id)); + + iwl_fwrt_update_fw_versions(&mld->fwrt, lmac1, umac); + + return true; +} + +#define MLD_ALIVE_TIMEOUT (2 * HZ) +#define MLD_INIT_COMPLETE_TIMEOUT (2 * HZ) + +static void iwl_mld_print_alive_notif_timeout(struct iwl_mld *mld) +{ + struct iwl_trans *trans = mld->trans; + struct iwl_pc_data *pc_data; + u8 count; + + IWL_ERR(mld, + "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", + iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS), + iwl_read_umac_prph(trans, + UMAG_SB_CPU_2_STATUS)); +#define IWL_FW_PRINT_REG_INFO(reg_name) \ + IWL_ERR(mld, #reg_name ": 0x%x\n", iwl_read_umac_prph(trans, reg_name)) + + IWL_FW_PRINT_REG_INFO(WFPM_LMAC1_PD_NOTIFICATION); + + IWL_FW_PRINT_REG_INFO(HPM_SECONDARY_DEVICE_STATE); + + /* print OTP info */ + IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_ADDR); + IWL_FW_PRINT_REG_INFO(WFPM_MAC_OTP_CFG7_DATA); +#undef IWL_FW_PRINT_REG_INFO + + pc_data = trans->dbg.pc_data; + for (count = 0; count < trans->dbg.num_pc; count++, pc_data++) + IWL_ERR(mld, "%s: 0x%x\n", pc_data->pc_name, + pc_data->pc_address); +} + +static int iwl_mld_load_fw_wait_alive(struct iwl_mld *mld, + struct iwl_mld_alive_data *alive_data) +{ + static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY }; + struct iwl_notification_wait alive_wait; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + iwl_init_notification_wait(&mld->notif_wait, &alive_wait, + alive_cmd, ARRAY_SIZE(alive_cmd), + iwl_alive_fn, alive_data); + + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_EARLY, NULL); + + ret = iwl_trans_start_fw(mld->trans, mld->fw, IWL_UCODE_REGULAR, true); + if (ret) { + iwl_remove_notification(&mld->notif_wait, &alive_wait); + return ret; + } + + ret = iwl_wait_notification(&mld->notif_wait, &alive_wait, + MLD_ALIVE_TIMEOUT); + + if (ret) { + if (ret == -ETIMEDOUT) + iwl_fw_dbg_error_collect(&mld->fwrt, + FW_DBG_TRIGGER_ALIVE_TIMEOUT); + iwl_mld_print_alive_notif_timeout(mld); + return ret; + } + + if (!alive_data->valid) { + IWL_ERR(mld, "Loaded firmware is not valid!\n"); + return -EIO; + } + + iwl_trans_fw_alive(mld->trans); + + return 0; +} + +static int iwl_mld_run_fw_init_sequence(struct iwl_mld *mld) +{ + struct iwl_notification_wait init_wait; + struct iwl_init_extended_cfg_cmd init_cfg = { + .init_flags = cpu_to_le32(BIT(IWL_INIT_PHY)), + }; + struct iwl_mld_alive_data alive_data = {}; + static const u16 init_complete[] = { + INIT_COMPLETE_NOTIF, + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_load_fw_wait_alive(mld, &alive_data); + if (ret) + return ret; + + ret = iwl_pnvm_load(mld->trans, &mld->notif_wait, + &mld->fw->ucode_capa, alive_data.sku_id); + if (ret) { + IWL_ERR(mld, "Timeout waiting for PNVM load %d\n", ret); + return ret; + } + + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, + NULL); + + iwl_init_notification_wait(&mld->notif_wait, + &init_wait, + init_complete, + ARRAY_SIZE(init_complete), + NULL, NULL); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(SYSTEM_GROUP, INIT_EXTENDED_CFG_CMD), + &init_cfg); + if (ret) { + IWL_ERR(mld, "Failed to send init config command: %d\n", ret); + iwl_remove_notification(&mld->notif_wait, &init_wait); + return ret; + } + + ret = iwl_mld_send_phy_cfg_cmd(mld); + if (ret) { + IWL_ERR(mld, "Failed to send PHY config command: %d\n", ret); + iwl_remove_notification(&mld->notif_wait, &init_wait); + return ret; + } + + ret = iwl_wait_notification(&mld->notif_wait, &init_wait, + MLD_INIT_COMPLETE_TIMEOUT); + if (ret) { + IWL_ERR(mld, "Failed to get INIT_COMPLETE %d\n", ret); + return ret; + } + + return 0; +} + +int iwl_mld_load_fw(struct iwl_mld *mld) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_trans_start_hw(mld->trans); + if (ret) + return ret; + + ret = iwl_mld_run_fw_init_sequence(mld); + if (ret) + goto err; + + ret = iwl_mld_init_mcc(mld); + if (ret) + goto err; + + mld->fw_status.running = true; + + return 0; +err: + iwl_mld_stop_fw(mld); + return ret; +} + +void iwl_mld_stop_fw(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + iwl_abort_notification_waits(&mld->notif_wait); + + iwl_fw_dbg_stop_sync(&mld->fwrt); + + iwl_trans_stop_device(mld->trans); + + /* HW is stopped, no more coming RX. Cancel all notifications in + * case they were sent just before stopping the HW. + */ + iwl_mld_cancel_async_notifications(mld); + + mld->fw_status.running = false; +} + +static void iwl_mld_restart_disconnect_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + if (vif->type == NL80211_IFTYPE_STATION) + ieee80211_hw_restart_disconnect(vif); +} + +void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags) +{ + u32 error_log_size = mld->fw->ucode_capa.error_log_size; + struct iwl_fw_error_recovery_cmd recovery_cmd = { + .flags = cpu_to_le32(flags), + }; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(SYSTEM_GROUP, FW_ERROR_RECOVERY_CMD), + .flags = CMD_WANT_SKB, + .data = {&recovery_cmd, }, + .len = {sizeof(recovery_cmd), }, + }; + int ret; + + /* no error log was defined in TLV */ + if (!error_log_size) + return; + + if (flags & ERROR_RECOVERY_UPDATE_DB) { + /* no buf was allocated upon NIC error */ + if (!mld->error_recovery_buf) + return; + + cmd.data[1] = mld->error_recovery_buf; + cmd.len[1] = error_log_size; + cmd.dataflags[1] = IWL_HCMD_DFL_NOCOPY; + recovery_cmd.buf_size = cpu_to_le32(error_log_size); + } + + ret = iwl_mld_send_cmd(mld, &cmd); + + /* we no longer need the recovery buffer */ + kfree(mld->error_recovery_buf); + mld->error_recovery_buf = NULL; + + if (ret) { + IWL_ERR(mld, "Failed to send recovery cmd %d\n", ret); + return; + } + + if (flags & ERROR_RECOVERY_UPDATE_DB) { + struct iwl_rx_packet *pkt = cmd.resp_pkt; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + u32 resp; + + if (IWL_FW_CHECK(mld, pkt_len != sizeof(resp), + "Unexpected recovery cmd response size %u (expected %zu)\n", + pkt_len, sizeof(resp))) + goto out; + + resp = le32_to_cpup((__le32 *)cmd.resp_pkt->data); + if (!resp) + goto out; + + IWL_ERR(mld, + "Failed to send recovery cmd blob was invalid %d\n", + resp); + + ieee80211_iterate_interfaces(mld->hw, 0, + iwl_mld_restart_disconnect_iter, + NULL); + } + +out: + iwl_free_resp(&cmd); +} + +static int iwl_mld_config_fw(struct iwl_mld *mld) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + iwl_fw_disable_dbg_asserts(&mld->fwrt); + iwl_get_shared_mem_conf(&mld->fwrt); + + ret = iwl_mld_send_tx_ant_cfg(mld); + if (ret) + return ret; + + ret = iwl_mld_send_bt_init_conf(mld); + if (ret) + return ret; + + ret = iwl_set_soc_latency(&mld->fwrt); + if (ret) + return ret; + + iwl_mld_configure_lari(mld); + + ret = iwl_mld_config_temp_report_ths(mld); + if (ret) + return ret; + +#ifdef CONFIG_THERMAL + ret = iwl_mld_config_ctdp(mld, mld->cooling_dev.cur_state, + CTDP_CMD_OPERATION_START); + if (ret) + return ret; +#endif + + ret = iwl_configure_rxq(&mld->fwrt); + if (ret) + return ret; + + ret = iwl_mld_send_rss_cfg_cmd(mld); + if (ret) + return ret; + + ret = iwl_mld_config_scan(mld); + if (ret) + return ret; + + ret = iwl_mld_update_device_power(mld, false); + if (ret) + return ret; + + if (mld->fw_status.in_hw_restart) { + iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_UPDATE_DB); + iwl_mld_time_sync_fw_config(mld); + } + + iwl_mld_led_config_fw(mld); + + ret = iwl_mld_init_ppag(mld); + if (ret) + return ret; + + ret = iwl_mld_init_sar(mld); + if (ret) + return ret; + + ret = iwl_mld_init_sgom(mld); + if (ret) + return ret; + + iwl_mld_init_tas(mld); + iwl_mld_init_uats(mld); + + return 0; +} + +int iwl_mld_start_fw(struct iwl_mld *mld) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_load_fw(mld); + if (IWL_FW_CHECK(mld, ret, "Failed to start firmware %d\n", ret)) { + iwl_fw_dbg_error_collect(&mld->fwrt, FW_DBG_TRIGGER_DRIVER); + return ret; + } + + IWL_DEBUG_INFO(mld, "uCode started.\n"); + + ret = iwl_mld_config_fw(mld); + if (ret) + goto error; + + return 0; + +error: + iwl_mld_stop_fw(mld); + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h new file mode 100644 index 000000000000..64a8d4248324 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/hcmd.h @@ -0,0 +1,56 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_hcmd_h__ +#define __iwl_mld_hcmd_h__ + +static inline int iwl_mld_send_cmd(struct iwl_mld *mld, struct iwl_host_cmd *cmd) +{ + /* No commands, including the d3 related commands, should be sent + * after entering d3 + */ +#ifdef CONFIG_PM_SLEEP + if (WARN_ON(mld->fw_status.in_d3)) + return -EIO; +#endif + + if (!(cmd->flags & CMD_ASYNC)) + lockdep_assert_wiphy(mld->wiphy); + + /* Devices that need to shutdown immediately on rfkill are not + * supported, so we can send all the cmds in rfkill + */ + cmd->flags |= CMD_SEND_IN_RFKILL; + + return iwl_trans_send_cmd(mld->trans, cmd); +} + +static inline int +__iwl_mld_send_cmd_with_flags_pdu(struct iwl_mld *mld, u32 id, + u32 flags, const void *data, u16 len) +{ + struct iwl_host_cmd cmd = { + .id = id, + .len = { data ? len : 0, }, + .data = { data, }, + .flags = flags, + }; + + return iwl_mld_send_cmd(mld, &cmd); +} + +#define _iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len, \ + ignored...) \ + __iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len) +#define iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, len...) \ + _iwl_mld_send_cmd_with_flags_pdu(mld, id, flags, data, ##len, \ + sizeof(*(data))) + +#define iwl_mld_send_cmd_pdu(mld, id, ...) \ + iwl_mld_send_cmd_with_flags_pdu(mld, id, 0, __VA_ARGS__) + +#define iwl_mld_send_cmd_empty(mld, id) \ + iwl_mld_send_cmd_with_flags_pdu(mld, id, 0, NULL, 0) + +#endif /* __iwl_mld_hcmd_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.c b/drivers/net/wireless/intel/iwlwifi/mld/iface.c new file mode 100644 index 000000000000..235b55e0fe59 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.c @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <net/cfg80211.h> + +#include "iface.h" +#include "hcmd.h" +#include "key.h" +#include "mlo.h" +#include "mac80211.h" + +#include "fw/api/context.h" +#include "fw/api/mac.h" +#include "fw/api/time-event.h" +#include "fw/api/datapath.h" + +/* Cleanup function for struct iwl_mld_vif, will be called in restart */ +void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + struct iwl_mld_link *link; + + mld_vif->emlsr.blocked_reasons &= ~IWL_MLD_EMLSR_BLOCKED_ROC; + + if (mld_vif->aux_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &mld_vif->aux_sta); + + /* EMLSR is turned back on during recovery */ + vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; + + if (mld_vif->roc_activity != ROC_NUM_ACTIVITIES) + ieee80211_remain_on_channel_expired(mld->hw); + + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; + + for_each_mld_vif_valid_link(mld_vif, link) { + iwl_mld_cleanup_link(mld_vif->mld, link); + + /* Correctly allocated primary link in non-MLO mode */ + if (!ieee80211_vif_is_mld(vif) && + link_id == 0 && link == &mld_vif->deflink) + continue; + + if (vif->active_links & BIT(link_id)) + continue; + + /* Should not happen as link removal should always succeed */ + WARN_ON(1); + if (link != &mld_vif->deflink) + kfree_rcu(link, rcu_head); + RCU_INIT_POINTER(mld_vif->link[link_id], NULL); + } + + ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL); + + CLEANUP_STRUCT(mld_vif); +} + +static int iwl_mld_send_mac_cmd(struct iwl_mld *mld, + struct iwl_mac_config_cmd *cmd) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), + cmd); + if (ret) + IWL_ERR(mld, "Failed to send MAC_CONFIG_CMD ret = %d\n", ret); + + return ret; +} + +int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif) +{ + switch (vif->type) { + case NL80211_IFTYPE_STATION: + return vif->p2p ? FW_MAC_TYPE_P2P_STA : FW_MAC_TYPE_BSS_STA; + case NL80211_IFTYPE_AP: + return FW_MAC_TYPE_GO; + case NL80211_IFTYPE_MONITOR: + return FW_MAC_TYPE_LISTENER; + case NL80211_IFTYPE_P2P_DEVICE: + return FW_MAC_TYPE_P2P_DEVICE; + case NL80211_IFTYPE_ADHOC: + return FW_MAC_TYPE_IBSS; + default: + WARN_ON_ONCE(1); + } + return FW_MAC_TYPE_BSS_STA; +} + +static bool iwl_mld_is_nic_ack_enabled(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + const struct ieee80211_supported_band *sband; + const struct ieee80211_sta_he_cap *own_he_cap; + + lockdep_assert_wiphy(mld->wiphy); + + /* This capability is the same for all bands, + * so take it from one of them. + */ + sband = mld->hw->wiphy->bands[NL80211_BAND_2GHZ]; + own_he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); + + return own_he_cap && (own_he_cap->he_cap_elem.mac_cap_info[2] & + IEEE80211_HE_MAC_CAP2_ACK_EN); +} + +static void iwl_mld_set_he_support(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd, + int cmd_ver) +{ + if (vif->type == NL80211_IFTYPE_AP) { + if (cmd_ver == 2) + cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1); + else + cmd->wifi_gen.he_ap_support = 1; + } else { + if (cmd_ver == 2) + cmd->wifi_gen_v2.he_support = cpu_to_le16(1); + else + cmd->wifi_gen.he_support = 1; + } +} + +/* fill the common part for all interface types */ +static void iwl_mld_mac_cmd_fill_common(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd, + u32 action) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf; + unsigned int link_id; + int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, + MAC_CONFIG_CMD), 0); + + lockdep_assert_wiphy(mld->wiphy); + + cmd->id_and_color = cpu_to_le32(mld_vif->fw_id); + cmd->action = cpu_to_le32(action); + + cmd->mac_type = + cpu_to_le32(iwl_mld_mac80211_iftype_to_fw(vif)); + + memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN); + + if (iwlwifi_mod_params.disable_11ax) + return; + + cmd->nic_not_ack_enabled = + cpu_to_le32(!iwl_mld_is_nic_ack_enabled(mld, vif)); + + /* If we have MLO enabled, then the firmware needs to enable + * address translation for the station(s) we add. That depends + * on having EHT enabled in firmware, which in turn depends on + * mac80211 in the code below. + * However, mac80211 doesn't enable HE/EHT until it has parsed + * the association response successfully, so just skip all that + * and enable both when we have MLO. + */ + if (ieee80211_vif_is_mld(vif)) { + iwl_mld_set_he_support(mld, vif, cmd, cmd_ver); + if (cmd_ver == 2) + cmd->wifi_gen_v2.eht_support = cpu_to_le32(1); + else + cmd->wifi_gen.eht_support = 1; + return; + } + + for_each_vif_active_link(vif, link_conf, link_id) { + if (!link_conf->he_support) + continue; + + iwl_mld_set_he_support(mld, vif, cmd, cmd_ver); + + /* EHT, if supported, was already set above */ + break; + } +} + +static void iwl_mld_fill_mac_cmd_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, u32 action, + struct iwl_mac_config_cmd *cmd) +{ + struct ieee80211_bss_conf *link; + u32 twt_policy = 0; + int link_id; + + lockdep_assert_wiphy(mld->wiphy); + + WARN_ON(vif->type != NL80211_IFTYPE_STATION); + + /* We always want to hear MCAST frames, if we're not authorized yet, + * we'll drop them. + */ + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_GRP); + + /* Adding a MAC ctxt with is_assoc set is not allowed in fw + * (and shouldn't happen) + */ + if (vif->cfg.assoc && action != FW_CTXT_ACTION_ADD) { + cmd->client.is_assoc = 1; + + if (!iwl_mld_vif_from_mac80211(vif)->authorized) + cmd->client.data_policy |= + cpu_to_le16(COEX_HIGH_PRIORITY_ENABLE); + } else { + /* Allow beacons to pass through as long as we are not + * associated + */ + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); + } + + cmd->client.assoc_id = cpu_to_le16(vif->cfg.aid); + + if (ieee80211_vif_is_mld(vif)) { + u16 esr_transition_timeout = + u16_get_bits(vif->cfg.eml_cap, + IEEE80211_EML_CAP_TRANSITION_TIMEOUT); + + cmd->client.esr_transition_timeout = + min_t(u16, IEEE80211_EML_CAP_TRANSITION_TIMEOUT_128TU, + esr_transition_timeout); + cmd->client.medium_sync_delay = + cpu_to_le16(vif->cfg.eml_med_sync_delay); + } + + for_each_vif_active_link(vif, link, link_id) { + if (!link->he_support) + continue; + + if (link->twt_requester) + twt_policy |= TWT_SUPPORTED; + if (link->twt_protected) + twt_policy |= PROTECTED_TWT_SUPPORTED; + if (link->twt_broadcast) + twt_policy |= BROADCAST_TWT_SUPPORTED; + } + + if (!iwlwifi_mod_params.disable_11ax) + cmd->client.data_policy |= cpu_to_le16(twt_policy); + + if (vif->probe_req_reg && vif->cfg.assoc && vif->p2p) + cmd->filter_flags |= + cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); +} + +static void iwl_mld_fill_mac_cmd_ap(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld->wiphy); + + WARN_ON(vif->type != NL80211_IFTYPE_AP); + + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_PROBE_REQ); + + /* in AP mode, pass beacons from other APs (needed for ht protection). + * When there're no any associated station, which means that we are not + * TXing anyway, don't ask FW to pass beacons to prevent unnecessary + * wake-ups. + */ + if (mld_vif->num_associated_stas) + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON); +} + +static void iwl_mld_go_iterator(void *_data, u8 *mac, struct ieee80211_vif *vif) +{ + bool *go_active = _data; + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO && + iwl_mld_vif_from_mac80211(vif)->ap_ibss_active) + *go_active = true; +} + +static bool iwl_mld_p2p_dev_has_extended_disc(struct iwl_mld *mld) +{ + bool go_active = false; + + /* This flag should be set to true when the P2P Device is + * discoverable and there is at least a P2P GO. Setting + * this flag will allow the P2P Device to be discoverable on other + * channels in addition to its listen channel. + * Note that this flag should not be set in other cases as it opens the + * Rx filters on all MAC and increases the number of interrupts. + */ + ieee80211_iterate_active_interfaces(mld->hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + iwl_mld_go_iterator, &go_active); + + return go_active; +} + +static void iwl_mld_fill_mac_cmd_p2p_dev(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd) +{ + bool ext_disc = iwl_mld_p2p_dev_has_extended_disc(mld); + + lockdep_assert_wiphy(mld->wiphy); + + /* Override the filter flags to accept all management frames. This is + * needed to support both P2P device discovery using probe requests and + * P2P service discovery using action frames + */ + cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT); + + if (ext_disc) + cmd->p2p_dev.is_disc_extended = cpu_to_le32(1); +} + +static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_config_cmd *cmd) +{ + lockdep_assert_wiphy(mld->wiphy); + + WARN_ON(vif->type != NL80211_IFTYPE_ADHOC); + + cmd->filter_flags |= cpu_to_le32(MAC_CFG_FILTER_ACCEPT_BEACON | + MAC_CFG_FILTER_ACCEPT_PROBE_REQ | + MAC_CFG_FILTER_ACCEPT_GRP); +} + +static int +iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mac_config_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .id_and_color = cpu_to_le32(mld_vif->fw_id), + }; + + return iwl_mld_send_mac_cmd(mld, &cmd); +} + +int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, + u32 action) +{ + struct iwl_mac_config_cmd cmd = {}; + + lockdep_assert_wiphy(mld->wiphy); + + if (action == FW_CTXT_ACTION_REMOVE) + return iwl_mld_rm_mac_from_fw(mld, vif); + + iwl_mld_mac_cmd_fill_common(mld, vif, &cmd, action); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + iwl_mld_fill_mac_cmd_sta(mld, vif, action, &cmd); + break; + case NL80211_IFTYPE_AP: + iwl_mld_fill_mac_cmd_ap(mld, vif, &cmd); + break; + case NL80211_IFTYPE_MONITOR: + cmd.filter_flags = + cpu_to_le32(MAC_CFG_FILTER_PROMISC | + MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT | + MAC_CFG_FILTER_ACCEPT_BEACON | + MAC_CFG_FILTER_ACCEPT_PROBE_REQ | + MAC_CFG_FILTER_ACCEPT_GRP); + break; + case NL80211_IFTYPE_P2P_DEVICE: + iwl_mld_fill_mac_cmd_p2p_dev(mld, vif, &cmd); + break; + case NL80211_IFTYPE_ADHOC: + iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd); + break; + default: + WARN(1, "not supported yet\n"); + return -EOPNOTSUPP; + } + + return iwl_mld_send_mac_cmd(mld, &cmd); +} + +IWL_MLD_ALLOC_FN(vif, vif) + +/* Constructor function for struct iwl_mld_vif */ +static int +iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + mld_vif->mld = mld; + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; + + ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif); + if (ret) + return ret; + + if (!mld->fw_status.in_hw_restart) { + wiphy_work_init(&mld_vif->emlsr.unblock_tpt_wk, + iwl_mld_emlsr_unblock_tpt_wk); + wiphy_delayed_work_init(&mld_vif->emlsr.check_tpt_wk, + iwl_mld_emlsr_check_tpt); + wiphy_delayed_work_init(&mld_vif->emlsr.prevent_done_wk, + iwl_mld_emlsr_prevent_done_wk); + wiphy_delayed_work_init(&mld_vif->emlsr.tmp_non_bss_done_wk, + iwl_mld_emlsr_tmp_non_bss_done_wk); + } + iwl_mld_init_internal_sta(&mld_vif->aux_sta); + + return 0; +} + +int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_init_vif(mld, vif); + if (ret) + return ret; + + ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_ADD); + if (ret) + RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL); + + return ret; +} + +int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_REMOVE); + + if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->fw_id_to_vif))) + return -EINVAL; + + RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL); + + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF, + mld_vif->fw_id); + + return ret; +} + +void iwl_mld_set_vif_associated(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct ieee80211_bss_conf *link; + unsigned int link_id; + + for_each_vif_active_link(vif, link, link_id) { + if (iwl_mld_link_set_associated(mld, vif, link)) + IWL_ERR(mld, "failed to update link %d\n", link_id); + } + + iwl_mld_recalc_multicast_filter(mld); +} + +static void iwl_mld_get_fw_id_bss_bitmap_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + u8 *fw_id_bitmap = _data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) + return; + + *fw_id_bitmap |= BIT(mld_vif->fw_id); +} + +u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld) +{ + u8 fw_id_bitmap = 0; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_SKIP_SDATA_NOT_IN_DRIVER, + iwl_mld_get_fw_id_bss_bitmap_iter, + &fw_id_bitmap); + + return fw_id_bitmap; +} + +void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_probe_resp_data_notif *notif = (void *)pkt->data; + struct iwl_probe_resp_data *old_data, *new_data; + struct ieee80211_vif *vif; + struct iwl_mld_link *mld_link; + + IWL_DEBUG_INFO(mld, "Probe response data notif: noa %d, csa %d\n", + notif->noa_active, notif->csa_counter); + + if (IWL_FW_CHECK(mld, le32_to_cpu(notif->mac_id) >= + ARRAY_SIZE(mld->fw_id_to_vif), + "mac id is invalid: %d\n", + le32_to_cpu(notif->mac_id))) + return; + + vif = wiphy_dereference(mld->wiphy, + mld->fw_id_to_vif[le32_to_cpu(notif->mac_id)]); + + /* the firmware gives us the mac_id (and not the link_id), mac80211 + * gets a vif and not a link, bottom line, this flow is not MLD ready + * yet. + */ + if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif)) + return; + + if (notif->csa_counter != IWL_PROBE_RESP_DATA_NO_CSA && + notif->csa_counter >= 1) + ieee80211_beacon_set_cntdwn(vif, notif->csa_counter); + + if (!vif->p2p) + return; + + mld_link = &iwl_mld_vif_from_mac80211(vif)->deflink; + + new_data = kzalloc(sizeof(*new_data), GFP_KERNEL); + if (!new_data) + return; + + memcpy(&new_data->notif, notif, sizeof(new_data->notif)); + + /* noa_attr contains 1 reserved byte, need to substruct it */ + new_data->noa_len = sizeof(struct ieee80211_vendor_ie) + + sizeof(new_data->notif.noa_attr) - 1; + + /* + * If it's a one time NoA, only one descriptor is needed, + * adjust the length according to len_low. + */ + if (new_data->notif.noa_attr.len_low == + sizeof(struct ieee80211_p2p_noa_desc) + 2) + new_data->noa_len -= sizeof(struct ieee80211_p2p_noa_desc); + + old_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data); + rcu_assign_pointer(mld_link->probe_resp_data, new_data); + + if (old_data) + kfree_rcu(old_data, rcu_head); +} + +void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_uapsd_misbehaving_ap_notif *notif = (void *)pkt->data; + struct ieee80211_vif *vif; + + if (IWL_FW_CHECK(mld, notif->mac_id >= ARRAY_SIZE(mld->fw_id_to_vif), + "mac id is invalid: %d\n", notif->mac_id)) + return; + + vif = wiphy_dereference(mld->wiphy, mld->fw_id_to_vif[notif->mac_id]); + + if (WARN_ON(!vif) || ieee80211_vif_is_mld(vif)) + return; + + IWL_WARN(mld, "uapsd misbehaving AP: %pM\n", vif->bss_conf.bssid); +} + +void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_datapath_monitor_notif *notif = (void *)pkt->data; + struct ieee80211_bss_conf *link; + struct ieee80211_supported_band *sband; + const struct ieee80211_sta_he_cap *he_cap; + struct ieee80211_vif *vif; + struct iwl_mld_vif *mld_vif; + + if (notif->type != cpu_to_le32(IWL_DP_MON_NOTIF_TYPE_EXT_CCA)) + return; + + link = iwl_mld_fw_id_to_link_conf(mld, notif->link_id); + if (WARN_ON(!link)) + return; + + vif = link->vif; + if (WARN_ON(!vif) || vif->type != NL80211_IFTYPE_STATION || + !vif->cfg.assoc) + return; + + if (!link->chanreq.oper.chan || + link->chanreq.oper.chan->band != NL80211_BAND_2GHZ || + link->chanreq.oper.width < NL80211_CHAN_WIDTH_40) + return; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + + /* this shouldn't happen *again*, ignore it */ + if (mld_vif->cca_40mhz_workaround != CCA_40_MHZ_WA_NONE) + return; + + mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RECONNECT; + + /* + * This capability manipulation isn't really ideal, but it's the + * easiest choice - otherwise we'd have to do some major changes + * in mac80211 to support this, which isn't worth it. This does + * mean that userspace may have outdated information, but that's + * actually not an issue at all. + */ + sband = mld->wiphy->bands[NL80211_BAND_2GHZ]; + + WARN_ON(!sband->ht_cap.ht_supported); + WARN_ON(!(sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)); + sband->ht_cap.cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); + + if (he_cap) { + /* we know that ours is writable */ + struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap; + + WARN_ON(!he->has_he); + WARN_ON(!(he->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G)); + he->he_cap_elem.phy_cap_info[0] &= + ~IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + } + + ieee80211_disconnect(vif, true); +} + +void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct ieee80211_supported_band *sband; + const struct ieee80211_sta_he_cap *he_cap; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_NONE) + return; + + /* Now we are just reconnecting with the new capabilities, + * but remember to reset the capabilities when we disconnect for real + */ + if (mld_vif->cca_40mhz_workaround == CCA_40_MHZ_WA_RECONNECT) { + mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_RESET; + return; + } + + /* Now cca_40mhz_workaround == CCA_40_MHZ_WA_RESET */ + + sband = mld->wiphy->bands[NL80211_BAND_2GHZ]; + + sband->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; + + he_cap = ieee80211_get_he_iftype_cap_vif(sband, vif); + + if (he_cap) { + /* we know that ours is writable */ + struct ieee80211_sta_he_cap *he = (void *)(uintptr_t)he_cap; + + he->he_cap_elem.phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G; + } + + mld_vif->cca_40mhz_workaround = CCA_40_MHZ_WA_NONE; +} + +struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld) +{ + unsigned long fw_id_bitmap = iwl_mld_get_fw_bss_vifs_ids(mld); + int fw_id; + + if (hweight8(fw_id_bitmap) != 1) + return NULL; + + fw_id = __ffs(fw_id_bitmap); + + return wiphy_dereference(mld->wiphy, + mld->fw_id_to_vif[fw_id]); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/iface.h b/drivers/net/wireless/intel/iwlwifi/mld/iface.h new file mode 100644 index 000000000000..49e2ce65557d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/iface.h @@ -0,0 +1,238 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_iface_h__ +#define __iwl_mld_iface_h__ + +#include <net/mac80211.h> + +#include "link.h" +#include "session-protect.h" +#include "d3.h" +#include "fw/api/time-event.h" + +enum iwl_mld_cca_40mhz_wa_status { + CCA_40_MHZ_WA_NONE, + CCA_40_MHZ_WA_RESET, + CCA_40_MHZ_WA_RECONNECT, +}; + +/** + * enum iwl_mld_emlsr_blocked - defines reasons for which EMLSR is blocked + * + * These blocks are applied/stored per-VIF. + * + * @IWL_MLD_EMLSR_BLOCKED_PREVENTION: Prevent repeated EMLSR enter/exit + * @IWL_MLD_EMLSR_BLOCKED_WOWLAN: WOWLAN is preventing EMLSR + * @IWL_MLD_EMLSR_BLOCKED_ROC: remain-on-channel is preventing EMLSR + * @IWL_MLD_EMLSR_BLOCKED_NON_BSS: An active non-BSS interface's link is + * preventing EMLSR + * @IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS: An expected active non-BSS interface's + * link is preventing EMLSR. This is a temporary blocking that is set when + * there is an indication that a non-BSS interface is to be added. + * @IWL_MLD_EMLSR_BLOCKED_TPT: throughput is too low to make EMLSR worthwhile + */ +enum iwl_mld_emlsr_blocked { + IWL_MLD_EMLSR_BLOCKED_PREVENTION = 0x1, + IWL_MLD_EMLSR_BLOCKED_WOWLAN = 0x2, + IWL_MLD_EMLSR_BLOCKED_ROC = 0x4, + IWL_MLD_EMLSR_BLOCKED_NON_BSS = 0x8, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS = 0x10, + IWL_MLD_EMLSR_BLOCKED_TPT = 0x20, +}; + +/** + * enum iwl_mld_emlsr_exit - defines reasons for exiting EMLSR + * + * Reasons to exit EMLSR may be either link specific or even specific to a + * combination of links. + * + * @IWL_MLD_EMLSR_EXIT_BLOCK: Exit due to a block reason being set + * @IWL_MLD_EMLSR_EXIT_MISSED_BEACON: Exit due to missed beacons + * @IWL_MLD_EMLSR_EXIT_FAIL_ENTRY: FW failed to enter EMLSR + * @IWL_MLD_EMLSR_EXIT_CSA: EMLSR prevented due to channel switch on link + * @IWL_MLD_EMLSR_EXIT_EQUAL_BAND: EMLSR prevented as both links share the band + * @IWL_MLD_EMLSR_EXIT_LOW_RSSI: Link RSSI is unsuitable for EMLSR + * @IWL_MLD_EMLSR_EXIT_LINK_USAGE: Exit EMLSR due to low TPT on secondary link + * @IWL_MLD_EMLSR_EXIT_BT_COEX: Exit EMLSR due to BT coexistence + * @IWL_MLD_EMLSR_EXIT_CHAN_LOAD: Exit EMLSR because the primary channel is not + * loaded enough to justify EMLSR. + * @IWL_MLD_EMLSR_EXIT_RFI: Exit EMLSR due to RFI + * @IWL_MLD_EMLSR_EXIT_FW_REQUEST: Exit EMLSR because the FW requested it + * @IWL_MLD_EMLSR_EXIT_INVALID: internal exit reason due to invalid data + */ +enum iwl_mld_emlsr_exit { + IWL_MLD_EMLSR_EXIT_BLOCK = 0x1, + IWL_MLD_EMLSR_EXIT_MISSED_BEACON = 0x2, + IWL_MLD_EMLSR_EXIT_FAIL_ENTRY = 0x4, + IWL_MLD_EMLSR_EXIT_CSA = 0x8, + IWL_MLD_EMLSR_EXIT_EQUAL_BAND = 0x10, + IWL_MLD_EMLSR_EXIT_LOW_RSSI = 0x20, + IWL_MLD_EMLSR_EXIT_LINK_USAGE = 0x40, + IWL_MLD_EMLSR_EXIT_BT_COEX = 0x80, + IWL_MLD_EMLSR_EXIT_CHAN_LOAD = 0x100, + IWL_MLD_EMLSR_EXIT_RFI = 0x200, + IWL_MLD_EMLSR_EXIT_FW_REQUEST = 0x400, + IWL_MLD_EMLSR_EXIT_INVALID = 0x800, +}; + +/** + * struct iwl_mld_emlsr - per-VIF data about EMLSR operation + * + * @primary: The current primary link + * @selected_primary: Primary link as selected during the last link selection + * @selected_links: Links as selected during the last link selection + * @blocked_reasons: Reasons preventing EMLSR from being enabled + * @last_exit_reason: Reason for the last EMLSR exit + * @last_exit_ts: Time of the last EMLSR exit (if @last_exit_reason is non-zero) + * @exit_repeat_count: Number of times EMLSR was exited for the same reason + * @unblock_tpt_wk: Unblock EMLSR because the throughput limit was reached + * @check_tpt_wk: a worker to check if IWL_MLD_EMLSR_BLOCKED_TPT should be + * added, for example if there is no longer enough traffic. + * @prevent_done_wk: Worker to remove %IWL_MLD_EMLSR_BLOCKED_PREVENTION + * @tmp_non_bss_done_wk: Worker to remove %IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS + */ +struct iwl_mld_emlsr { + struct_group(zeroed_on_not_authorized, + u8 primary; + + u8 selected_primary; + u16 selected_links; + + enum iwl_mld_emlsr_blocked blocked_reasons; + + enum iwl_mld_emlsr_exit last_exit_reason; + unsigned long last_exit_ts; + u8 exit_repeat_count; + ); + + struct wiphy_work unblock_tpt_wk; + struct wiphy_delayed_work check_tpt_wk; + + struct wiphy_delayed_work prevent_done_wk; + struct wiphy_delayed_work tmp_non_bss_done_wk; +}; + +/** + * struct iwl_mld_vif - virtual interface (MAC context) configuration parameters + * + * @fw_id: fw id of the mac context. + * @session_protect: session protection parameters + * @ap_sta: pointer to AP sta, for easier access to it. + * Relevant only for STA vifs. + * @authorized: indicates the AP station was set to authorized + * @bigtks: BIGTKs of the AP, for beacon protection. + * Only valid for STA. (FIXME: needs to be per link) + * @num_associated_stas: number of associated STAs. Relevant only for AP mode. + * @ap_ibss_active: whether the AP/IBSS was started + * @cca_40mhz_workaround: When we are connected in 2.4 GHz and 40 MHz, and the + * environment is too loaded, we work around this by reconnecting to the + * same AP with 20 MHz. This manages the status of the workaround. + * @beacon_inject_active: indicates an active debugfs beacon ie injection + * @low_latency_causes: bit flags, indicating the causes for low-latency, + * see @iwl_mld_low_latency_cause. + * @ps_disabled: indicates that PS is disabled for this interface + * @mld: pointer to the mld structure. + * @deflink: default link data, for use in non-MLO, + * @link: reference to link data for each valid link, for use in MLO. + * @emlsr: information related to EMLSR + * @wowlan_data: data used by the wowlan suspend flow + * @use_ps_poll: use ps_poll frames + * @disable_bf: disable beacon filter + * @dbgfs_slink: debugfs symlink for this interface + * @roc_activity: the id of the roc_activity running. Relevant for STA and + * p2p device only. Set to %ROC_NUM_ACTIVITIES when not in use. + * @aux_sta: station used for remain on channel. Used in P2P device. + */ +struct iwl_mld_vif { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + u8 fw_id; + struct iwl_mld_session_protect session_protect; + struct ieee80211_sta *ap_sta; + bool authorized; + struct ieee80211_key_conf __rcu *bigtks[2]; + u8 num_associated_stas; + bool ap_ibss_active; + enum iwl_mld_cca_40mhz_wa_status cca_40mhz_workaround; +#ifdef CONFIG_IWLWIFI_DEBUGFS + bool beacon_inject_active; +#endif + u8 low_latency_causes; + bool ps_disabled; + ); + /* And here fields that survive a fw restart */ + struct iwl_mld *mld; + struct iwl_mld_link deflink; + struct iwl_mld_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + + struct iwl_mld_emlsr emlsr; + +#ifdef CONFIG_PM_SLEEP + struct iwl_mld_wowlan_data wowlan_data; +#endif +#ifdef CONFIG_IWLWIFI_DEBUGFS + bool use_ps_poll; + bool disable_bf; + struct dentry *dbgfs_slink; +#endif + enum iwl_roc_activity roc_activity; + struct iwl_mld_int_sta aux_sta; +}; + +static inline struct iwl_mld_vif * +iwl_mld_vif_from_mac80211(struct ieee80211_vif *vif) +{ + return (void *)vif->drv_priv; +} + +#define iwl_mld_link_dereference_check(mld_vif, link_id) \ + rcu_dereference_check((mld_vif)->link[link_id], \ + lockdep_is_held(&mld_vif->mld->wiphy->mtx)) + +#define for_each_mld_vif_valid_link(mld_vif, mld_link) \ + for (int link_id = 0; link_id < ARRAY_SIZE((mld_vif)->link); \ + link_id++) \ + if ((mld_link = iwl_mld_link_dereference_check(mld_vif, link_id))) + +/* Retrieve pointer to mld link from mac80211 structures */ +static inline struct iwl_mld_link * +iwl_mld_link_from_mac80211(struct ieee80211_bss_conf *bss_conf) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); + + return iwl_mld_link_dereference_check(mld_vif, bss_conf->link_id); +} + +int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif); + +/* Cleanup function for struct iwl_mld_vif, will be called in restart */ +void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif); +int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif, + u32 action); +int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif); +int iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif); +void iwl_mld_set_vif_associated(struct iwl_mld *mld, + struct ieee80211_vif *vif); +u8 iwl_mld_get_fw_bss_vifs_ids(struct iwl_mld *mld); +void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_handle_datapath_monitor_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_handle_uapsd_misbehaving_ap_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_reset_cca_40mhz_workaround(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +static inline bool iwl_mld_vif_low_latency(const struct iwl_mld_vif *mld_vif) +{ + return !!mld_vif->low_latency_causes; +} + +struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld); + +#endif /* __iwl_mld_iface_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.c b/drivers/net/wireless/intel/iwlwifi/mld/key.c new file mode 100644 index 000000000000..0eff13e5ffd5 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include "key.h" +#include "iface.h" +#include "sta.h" +#include "fw/api/datapath.h" + +static u32 iwl_mld_get_key_flags(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool pairwise = key->flags & IEEE80211_KEY_FLAG_PAIRWISE; + bool igtk = key->keyidx == 4 || key->keyidx == 5; + u32 flags = 0; + + if (!pairwise) + flags |= IWL_SEC_KEY_FLAG_MCAST_KEY; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_TKIP: + flags |= IWL_SEC_KEY_FLAG_CIPHER_TKIP; + break; + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_CCMP: + flags |= IWL_SEC_KEY_FLAG_CIPHER_CCMP; + break; + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + flags |= IWL_SEC_KEY_FLAG_KEY_SIZE; + fallthrough; + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + flags |= IWL_SEC_KEY_FLAG_CIPHER_GCMP; + break; + } + + if (!sta && vif->type == NL80211_IFTYPE_STATION) + sta = mld_vif->ap_sta; + + /* If we are installing an iGTK (in AP or STA mode), we need to tell + * the firmware this key will en/decrypt MGMT frames. + * Same goes if we are installing a pairwise key for an MFP station. + * In case we're installing a groupwise key (which is not an iGTK), + * then, we will not use this key for MGMT frames. + */ + if ((sta && sta->mfp && pairwise) || igtk) + flags |= IWL_SEC_KEY_FLAG_MFP; + + if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU) + flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU; + + return flags; +} + +static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_link_sta *link_sta; + int sta_id; + + lockdep_assert_wiphy(mld->wiphy); + + /* AP group keys are per link and should be on the mcast/bcast STA */ + if (vif->type == NL80211_IFTYPE_AP && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) { + struct iwl_mld_link *link = NULL; + + if (key->link_id >= 0) + link = iwl_mld_link_dereference_check(mld_vif, + key->link_id); + + if (WARN_ON(!link)) + return 0; + + /* In this stage we should have both the bcast and mcast STAs */ + if (WARN_ON(link->bcast_sta.sta_id == IWL_INVALID_STA || + link->mcast_sta.sta_id == IWL_INVALID_STA)) + return 0; + + /* IGTK/BIGTK to bcast STA */ + if (key->keyidx >= 4) + return BIT(link->bcast_sta.sta_id); + + /* GTK for data to mcast STA */ + return BIT(link->mcast_sta.sta_id); + } + + /* for client mode use the AP STA also for group keys */ + if (!sta && vif->type == NL80211_IFTYPE_STATION) + sta = mld_vif->ap_sta; + + /* STA should be non-NULL now */ + if (WARN_ON(!sta)) + return 0; + + /* Key is not per-link, get the full sta mask */ + if (key->link_id < 0) + return iwl_mld_fw_sta_id_mask(mld, sta); + + /* The link_sta shouldn't be NULL now, but this is checked in + * iwl_mld_fw_sta_id_mask + */ + link_sta = link_sta_dereference_check(sta, key->link_id); + + sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + if (sta_id < 0) + return 0; + + return BIT(sta_id); +} + +static int iwl_mld_add_key_to_fw(struct iwl_mld *mld, u32 sta_mask, + u32 key_flags, struct ieee80211_key_conf *key) +{ + struct iwl_sec_key_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .u.add.sta_mask = cpu_to_le32(sta_mask), + .u.add.key_id = cpu_to_le32(key->keyidx), + .u.add.key_flags = cpu_to_le32(key_flags), + .u.add.tx_seq = cpu_to_le64(atomic64_read(&key->tx_pn)), + }; + bool tkip = key->cipher == WLAN_CIPHER_SUITE_TKIP; + int max_key_len = sizeof(cmd.u.add.key); + + if (WARN_ON(!sta_mask)) + return -EINVAL; + + if (WARN_ON(key->keylen > max_key_len)) + return -EINVAL; + + memcpy(cmd.u.add.key, key->key, key->keylen); + + if (tkip) { + memcpy(cmd.u.add.tkip_mic_rx_key, + key->key + NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, + 8); + memcpy(cmd.u.add.tkip_mic_tx_key, + key->key + NL80211_TKIP_DATA_OFFSET_TX_MIC_KEY, + 8); + } + + return iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), + &cmd); +} + +static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask, + u32 key_flags, u32 keyidx) +{ + struct iwl_sec_key_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .u.remove.sta_mask = cpu_to_le32(sta_mask), + .u.remove.key_id = cpu_to_le32(keyidx), + .u.remove.key_flags = cpu_to_le32(key_flags), + }; + + if (WARN_ON(!sta_mask)) + return; + + iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd); +} + +void iwl_mld_remove_key(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key); + u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld->wiphy); + + if (!sta_mask) + return; + + if (key->keyidx == 4 || key->keyidx == 5) { + struct iwl_mld_link *mld_link; + unsigned int link_id = 0; + + /* set to -1 for non-MLO right now */ + if (key->link_id >= 0) + link_id = key->link_id; + + mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); + if (WARN_ON(!mld_link)) + return; + + if (mld_link->igtk == key) + mld_link->igtk = NULL; + + mld->num_igtks--; + } + + iwl_mld_remove_key_from_fw(mld, sta_mask, key_flags, key->keyidx); + + /* no longer in HW */ + key->hw_key_idx = STA_KEY_IDX_INVALID; +} + +int iwl_mld_add_key(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + u32 sta_mask = iwl_mld_get_key_sta_mask(mld, vif, sta, key); + u32 key_flags = iwl_mld_get_key_flags(mld, vif, sta, key); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link = NULL; + bool igtk = key->keyidx == 4 || key->keyidx == 5; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (!sta_mask) + return -EINVAL; + + if (igtk) { + if (mld->num_igtks == IWL_MAX_NUM_IGTKS) + return -EOPNOTSUPP; + + u8 link_id = 0; + + /* set to -1 for non-MLO right now */ + if (key->link_id >= 0) + link_id = key->link_id; + + mld_link = iwl_mld_link_dereference_check(mld_vif, link_id); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (mld_link->igtk) { + IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n", + mld_link->igtk->keyidx); + iwl_mld_remove_key(mld, vif, sta, mld_link->igtk); + } + + WARN_ON(mld_link->igtk); + } + + ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key); + if (ret) + return ret; + + if (mld_link) { + mld_link->igtk = key; + mld->num_igtks++; + } + + /* We don't really need this, but need it to be not invalid, + * so we will know if the key is in fw. + */ + key->hw_key_idx = 0; + + return 0; +} + +struct remove_ap_keys_iter_data { + u8 link_id; + struct ieee80211_sta *sta; +}; + +static void iwl_mld_remove_ap_keys_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct remove_ap_keys_iter_data *data = _data; + + if (key->hw_key_idx == STA_KEY_IDX_INVALID) + return; + + /* All the pairwise keys should have been removed by now */ + if (WARN_ON(sta)) + return; + + if (key->link_id >= 0 && key->link_id != data->link_id) + return; + + iwl_mld_remove_key(mld, vif, data->sta, key); +} + +void iwl_mld_remove_ap_keys(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned int link_id) +{ + struct remove_ap_keys_iter_data iter_data = { + .link_id = link_id, + .sta = sta, + }; + + if (WARN_ON_ONCE(vif->type != NL80211_IFTYPE_STATION)) + return; + + ieee80211_iter_keys(mld->hw, vif, + iwl_mld_remove_ap_keys_iter, + &iter_data); +} + +struct iwl_mvm_sta_key_update_data { + struct ieee80211_sta *sta; + u32 old_sta_mask; + u32 new_sta_mask; + int err; +}; + +static void iwl_mld_update_sta_key_iter(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, + void *_data) +{ + struct iwl_mvm_sta_key_update_data *data = _data; + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_sec_key_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_MODIFY), + .u.modify.old_sta_mask = cpu_to_le32(data->old_sta_mask), + .u.modify.new_sta_mask = cpu_to_le32(data->new_sta_mask), + .u.modify.key_id = cpu_to_le32(key->keyidx), + .u.modify.key_flags = + cpu_to_le32(iwl_mld_get_key_flags(mld, vif, sta, key)), + }; + int err; + + /* only need to do this for pairwise keys (link_id == -1) */ + if (sta != data->sta || key->link_id >= 0) + return; + + err = iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), + &cmd); + + if (err) + data->err = err; +} + +int iwl_mld_update_sta_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 old_sta_mask, + u32 new_sta_mask) +{ + struct iwl_mvm_sta_key_update_data data = { + .sta = sta, + .old_sta_mask = old_sta_mask, + .new_sta_mask = new_sta_mask, + }; + + ieee80211_iter_keys(mld->hw, vif, iwl_mld_update_sta_key_iter, + &data); + return data.err; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/key.h b/drivers/net/wireless/intel/iwlwifi/mld/key.h new file mode 100644 index 000000000000..a68ea48913be --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/key.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_key_h__ +#define __iwl_mld_key_h__ + +#include "mld.h" +#include <net/mac80211.h> +#include "fw/api/sta.h" + +void iwl_mld_remove_key(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +int iwl_mld_add_key(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key); +void iwl_mld_remove_ap_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + unsigned int link_id); + +int iwl_mld_update_sta_keys(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 old_sta_mask, + u32 new_sta_mask); + +static inline void +iwl_mld_cleanup_keys_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key, void *data) +{ + key->hw_key_idx = STA_KEY_IDX_INVALID; +} + +#endif /* __iwl_mld_key_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/led.c b/drivers/net/wireless/intel/iwlwifi/mld/led.c new file mode 100644 index 000000000000..a37b32cbc6e6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/led.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include <linux/leds.h> +#include <net/mac80211.h> + +#include "fw/api/led.h" +#include "mld.h" +#include "led.h" +#include "hcmd.h" + +static void iwl_mld_send_led_fw_cmd(struct iwl_mld *mld, bool on) +{ + struct iwl_led_cmd led_cmd = { + .status = cpu_to_le32(on), + }; + int err; + + if (WARN_ON(!mld->fw_status.running)) + return; + + err = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(LONG_GROUP, + LEDS_CMD), + CMD_ASYNC, &led_cmd); + + if (err) + IWL_WARN(mld, "LED command failed: %d\n", err); +} + +static void iwl_led_brightness_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + struct iwl_mld *mld = container_of(led_cdev, struct iwl_mld, led); + + if (!mld->fw_status.running) + return; + + iwl_mld_send_led_fw_cmd(mld, brightness > 0); +} + +int iwl_mld_leds_init(struct iwl_mld *mld) +{ + int mode = iwlwifi_mod_params.led_mode; + int ret; + + switch (mode) { + case IWL_LED_BLINK: + IWL_ERR(mld, "Blink led mode not supported, used default\n"); + fallthrough; + case IWL_LED_DEFAULT: + case IWL_LED_RF_STATE: + mode = IWL_LED_RF_STATE; + break; + case IWL_LED_DISABLE: + IWL_INFO(mld, "Led disabled\n"); + return 0; + default: + return -EINVAL; + } + + mld->led.name = kasprintf(GFP_KERNEL, "%s-led", + wiphy_name(mld->hw->wiphy)); + if (!mld->led.name) + return -ENOMEM; + + mld->led.brightness_set = iwl_led_brightness_set; + mld->led.max_brightness = 1; + + if (mode == IWL_LED_RF_STATE) + mld->led.default_trigger = + ieee80211_get_radio_led_name(mld->hw); + + ret = led_classdev_register(mld->trans->dev, &mld->led); + if (ret) { + kfree(mld->led.name); + mld->led.name = NULL; + IWL_INFO(mld, "Failed to enable led\n"); + } + + return ret; +} + +void iwl_mld_led_config_fw(struct iwl_mld *mld) +{ + if (!mld->led.name) + return; + + iwl_mld_send_led_fw_cmd(mld, mld->led.brightness > 0); +} + +void iwl_mld_leds_exit(struct iwl_mld *mld) +{ + if (!mld->led.name) + return; + + led_classdev_unregister(&mld->led); + kfree(mld->led.name); + mld->led.name = NULL; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/led.h b/drivers/net/wireless/intel/iwlwifi/mld/led.h new file mode 100644 index 000000000000..0954e214e73d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/led.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_led_h__ +#define __iwl_mld_led_h__ + +#include "mld.h" + +#ifdef CONFIG_IWLWIFI_LEDS +int iwl_mld_leds_init(struct iwl_mld *mld); +void iwl_mld_leds_exit(struct iwl_mld *mld); +void iwl_mld_led_config_fw(struct iwl_mld *mld); +#else +static inline int iwl_mld_leds_init(struct iwl_mld *mld) +{ + return 0; +} + +static inline void iwl_mld_leds_exit(struct iwl_mld *mld) +{ +} + +static inline void iwl_mld_led_config_fw(struct iwl_mld *mld) +{ +} +#endif + +#endif /* __iwl_mld_led_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.c b/drivers/net/wireless/intel/iwlwifi/mld/link.c new file mode 100644 index 000000000000..d0f56189ad3f --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.c @@ -0,0 +1,1214 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "constants.h" +#include "link.h" +#include "iface.h" +#include "mlo.h" +#include "hcmd.h" +#include "phy.h" +#include "fw/api/rs.h" +#include "fw/api/txq.h" +#include "fw/api/mac.h" + +#include "fw/api/context.h" +#include "fw/dbg.h" + +static int iwl_mld_send_link_cmd(struct iwl_mld *mld, + struct iwl_link_config_cmd *cmd, + enum iwl_ctxt_action action) +{ + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + cmd->action = cpu_to_le32(action); + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD), + cmd); + if (ret) + IWL_ERR(mld, "Failed to send LINK_CONFIG_CMD (action:%d): %d\n", + action, ret); + return ret; +} + +static int iwl_mld_add_link_to_fw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_vif *vif = link_conf->vif; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(link_conf); + struct iwl_link_config_cmd cmd = {}; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!link)) + return -EINVAL; + + cmd.link_id = cpu_to_le32(link->fw_id); + cmd.mac_id = cpu_to_le32(mld_vif->fw_id); + cmd.spec_link_id = link_conf->link_id; + cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID); + + ether_addr_copy(cmd.local_link_addr, link_conf->addr); + + if (vif->type == NL80211_IFTYPE_ADHOC && link_conf->bssid) + ether_addr_copy(cmd.ibss_bssid_addr, link_conf->bssid); + + return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD); +} + +/* Get the basic rates of the used band and add the mandatory ones */ +static void iwl_mld_fill_rates(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *chan_ctx, + __le32 *cck_rates, __le32 *ofdm_rates) +{ + struct cfg80211_chan_def *chandef = + iwl_mld_get_chandef_from_chanctx(mld, chan_ctx); + struct ieee80211_supported_band *sband = + mld->hw->wiphy->bands[chandef->chan->band]; + unsigned long basic = link->basic_rates; + int lowest_present_ofdm = 100; + int lowest_present_cck = 100; + u32 cck = 0; + u32 ofdm = 0; + int i; + + for_each_set_bit(i, &basic, BITS_PER_LONG) { + int hw = sband->bitrates[i].hw_value; + + if (hw >= IWL_FIRST_OFDM_RATE) { + ofdm |= BIT(hw - IWL_FIRST_OFDM_RATE); + if (lowest_present_ofdm > hw) + lowest_present_ofdm = hw; + } else { + BUILD_BUG_ON(IWL_FIRST_CCK_RATE != 0); + + cck |= BIT(hw); + if (lowest_present_cck > hw) + lowest_present_cck = hw; + } + } + + /* Now we've got the basic rates as bitmaps in the ofdm and cck + * variables. This isn't sufficient though, as there might not + * be all the right rates in the bitmap. E.g. if the only basic + * rates are 5.5 Mbps and 11 Mbps, we still need to add 1 Mbps + * and 6 Mbps because the 802.11-2007 standard says in 9.6: + * + * [...] a STA responding to a received frame shall transmit + * its Control Response frame [...] at the highest rate in the + * BSSBasicRateSet parameter that is less than or equal to the + * rate of the immediately previous frame in the frame exchange + * sequence ([...]) and that is of the same modulation class + * ([...]) as the received frame. If no rate contained in the + * BSSBasicRateSet parameter meets these conditions, then the + * control frame sent in response to a received frame shall be + * transmitted at the highest mandatory rate of the PHY that is + * less than or equal to the rate of the received frame, and + * that is of the same modulation class as the received frame. + * + * As a consequence, we need to add all mandatory rates that are + * lower than all of the basic rates to these bitmaps. + */ + + if (lowest_present_ofdm > IWL_RATE_24M_INDEX) + ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE; + if (lowest_present_ofdm > IWL_RATE_12M_INDEX) + ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE; + /* 6M already there or needed so always add */ + ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE; + + /* CCK is a bit more complex with DSSS vs. HR/DSSS vs. ERP. + * Note, however: + * - if no CCK rates are basic, it must be ERP since there must + * be some basic rates at all, so they're OFDM => ERP PHY + * (or we're in 5 GHz, and the cck bitmap will never be used) + * - if 11M is a basic rate, it must be ERP as well, so add 5.5M + * - if 5.5M is basic, 1M and 2M are mandatory + * - if 2M is basic, 1M is mandatory + * - if 1M is basic, that's the only valid ACK rate. + * As a consequence, it's not as complicated as it sounds, just add + * any lower rates to the ACK rate bitmap. + */ + if (lowest_present_cck > IWL_RATE_11M_INDEX) + cck |= IWL_RATE_BIT_MSK(11) >> IWL_FIRST_CCK_RATE; + if (lowest_present_cck > IWL_RATE_5M_INDEX) + cck |= IWL_RATE_BIT_MSK(5) >> IWL_FIRST_CCK_RATE; + if (lowest_present_cck > IWL_RATE_2M_INDEX) + cck |= IWL_RATE_BIT_MSK(2) >> IWL_FIRST_CCK_RATE; + /* 1M already there or needed so always add */ + cck |= IWL_RATE_BIT_MSK(1) >> IWL_FIRST_CCK_RATE; + + *cck_rates = cpu_to_le32((u32)cck); + *ofdm_rates = cpu_to_le32((u32)ofdm); +} + +static void iwl_mld_fill_protection_flags(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + __le32 *protection_flags) +{ + u8 protection_mode = link->ht_operation_mode & + IEEE80211_HT_OP_MODE_PROTECTION; + u8 ht_flag = LINK_PROT_FLG_HT_PROT | LINK_PROT_FLG_FAT_PROT; + + IWL_DEBUG_RATE(mld, "HT protection mode: %d\n", protection_mode); + + if (link->use_cts_prot) + *protection_flags |= cpu_to_le32(LINK_PROT_FLG_TGG_PROTECT); + + /* See section 9.23.3.1 of IEEE 80211-2012. + * Nongreenfield HT STAs Present is not supported. + */ + switch (protection_mode) { + case IEEE80211_HT_OP_MODE_PROTECTION_NONE: + break; + case IEEE80211_HT_OP_MODE_PROTECTION_NONMEMBER: + case IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED: + *protection_flags |= cpu_to_le32(ht_flag); + break; + case IEEE80211_HT_OP_MODE_PROTECTION_20MHZ: + /* Protect when channel wider than 20MHz */ + if (link->chanreq.oper.width > NL80211_CHAN_WIDTH_20) + *protection_flags |= cpu_to_le32(ht_flag); + break; + } +} + +static u8 iwl_mld_mac80211_ac_to_fw_ac(enum ieee80211_ac_numbers ac) +{ + static const u8 mac80211_ac_to_fw[] = { + AC_VO, + AC_VI, + AC_BE, + AC_BK + }; + + return mac80211_ac_to_fw[ac]; +} + +static void iwl_mld_fill_qos_params(struct ieee80211_bss_conf *link, + struct iwl_ac_qos *ac, __le32 *qos_flags) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + /* no need to check mld_link since it is done in the caller */ + + for (int mac_ac = 0; mac_ac < IEEE80211_NUM_ACS; mac_ac++) { + u8 txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(mac_ac); + u8 fw_ac = iwl_mld_mac80211_ac_to_fw_ac(mac_ac); + + ac[fw_ac].cw_min = + cpu_to_le16(mld_link->queue_params[mac_ac].cw_min); + ac[fw_ac].cw_max = + cpu_to_le16(mld_link->queue_params[mac_ac].cw_max); + ac[fw_ac].edca_txop = + cpu_to_le16(mld_link->queue_params[mac_ac].txop * 32); + ac[fw_ac].aifsn = mld_link->queue_params[mac_ac].aifs; + ac[fw_ac].fifos_mask = BIT(txf); + } + + if (link->qos) + *qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA); + + if (link->chanreq.oper.width != NL80211_CHAN_WIDTH_20_NOHT) + *qos_flags |= cpu_to_le32(MAC_QOS_FLG_TGN); +} + +static bool iwl_mld_fill_mu_edca(struct iwl_mld *mld, + const struct iwl_mld_link *mld_link, + struct iwl_he_backoff_conf *trig_based_txf) +{ + for (int mac_ac = 0; mac_ac < IEEE80211_NUM_ACS; mac_ac++) { + const struct ieee80211_he_mu_edca_param_ac_rec *mu_edca = + &mld_link->queue_params[mac_ac].mu_edca_param_rec; + u8 fw_ac = iwl_mld_mac80211_ac_to_fw_ac(mac_ac); + + if (!mld_link->queue_params[mac_ac].mu_edca) + return false; + + trig_based_txf[fw_ac].cwmin = + cpu_to_le16(mu_edca->ecw_min_max & 0xf); + trig_based_txf[fw_ac].cwmax = + cpu_to_le16((mu_edca->ecw_min_max & 0xf0) >> 4); + trig_based_txf[fw_ac].aifsn = + cpu_to_le16(mu_edca->aifsn & 0xf); + trig_based_txf[fw_ac].mu_time = + cpu_to_le16(mu_edca->mu_edca_timer); + } + return true; +} + +static u8 iwl_mld_sta_rx_bw_to_fw(enum ieee80211_sta_rx_bandwidth bw) +{ + switch (bw) { + default: /* potential future values not supported by this hw/driver */ + case IEEE80211_STA_RX_BW_20: + return IWL_LINK_MODIFY_BW_20; + case IEEE80211_STA_RX_BW_40: + return IWL_LINK_MODIFY_BW_40; + case IEEE80211_STA_RX_BW_80: + return IWL_LINK_MODIFY_BW_80; + case IEEE80211_STA_RX_BW_160: + return IWL_LINK_MODIFY_BW_160; + case IEEE80211_STA_RX_BW_320: + return IWL_LINK_MODIFY_BW_320; + } +} + +static int _iwl_mld_change_link_in_fw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + enum ieee80211_sta_rx_bandwidth bw, + u32 changes) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct ieee80211_vif *vif = link->vif; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_chanctx_conf *chan_ctx; + struct iwl_link_config_cmd cmd = {}; + u32 flags = 0; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + cmd.link_id = cpu_to_le32(mld_link->fw_id); + cmd.spec_link_id = link->link_id; + cmd.mac_id = cpu_to_le32(mld_vif->fw_id); + + chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); + + cmd.phy_id = cpu_to_le32(chan_ctx ? + iwl_mld_phy_from_mac80211(chan_ctx)->fw_id : + FW_CTXT_ID_INVALID); + + ether_addr_copy(cmd.local_link_addr, link->addr); + + cmd.active = cpu_to_le32(mld_link->active); + + if ((changes & LINK_CONTEXT_MODIFY_ACTIVE) && !mld_link->active && + mld_link->silent_deactivation) { + /* We are de-activating a link that is having CSA with + * immediate quiet in EMLSR. Tell the firmware not to send any + * frame. + */ + cmd.block_tx = 1; + mld_link->silent_deactivation = false; + } + + if (vif->type == NL80211_IFTYPE_ADHOC && link->bssid) + ether_addr_copy(cmd.ibss_bssid_addr, link->bssid); + + /* Channel context is needed to get the rates */ + if (chan_ctx) + iwl_mld_fill_rates(mld, link, chan_ctx, &cmd.cck_rates, + &cmd.ofdm_rates); + + cmd.cck_short_preamble = cpu_to_le32(link->use_short_preamble); + cmd.short_slot = cpu_to_le32(link->use_short_slot); + + iwl_mld_fill_protection_flags(mld, link, &cmd.protection_flags); + + iwl_mld_fill_qos_params(link, cmd.ac, &cmd.qos_flags); + + cmd.bi = cpu_to_le32(link->beacon_int); + cmd.dtim_interval = cpu_to_le32(link->beacon_int * link->dtim_period); + + if (changes & LINK_CONTEXT_MODIFY_BANDWIDTH) + cmd.modify_bandwidth = iwl_mld_sta_rx_bw_to_fw(bw); + + /* Configure HE parameters only if HE is supported, and only after + * the parameters are set in mac80211 (meaning after assoc) + */ + if (!link->he_support || iwlwifi_mod_params.disable_11ax || + (vif->type == NL80211_IFTYPE_STATION && !vif->cfg.assoc)) { + changes &= ~LINK_CONTEXT_MODIFY_HE_PARAMS; + goto send_cmd; + } + + /* ap_sta may be NULL if we're disconnecting */ + if (mld_vif->ap_sta) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_check(mld_vif->ap_sta, + link->link_id); + + if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he && + link_sta->he_cap.he_cap_elem.mac_cap_info[5] & + IEEE80211_HE_MAC_CAP5_OM_CTRL_UL_MU_DATA_DIS_RX) + cmd.ul_mu_data_disable = 1; + } + + cmd.htc_trig_based_pkt_ext = link->htc_trig_based_pkt_ext; + + if (link->uora_exists) { + cmd.rand_alloc_ecwmin = link->uora_ocw_range & 0x7; + cmd.rand_alloc_ecwmax = (link->uora_ocw_range >> 3) & 0x7; + } + + if (iwl_mld_fill_mu_edca(mld, mld_link, cmd.trig_based_txf)) + flags |= LINK_FLG_MU_EDCA_CW; + + cmd.bss_color = link->he_bss_color.color; + + if (!link->he_bss_color.enabled) + flags |= LINK_FLG_BSS_COLOR_DIS; + + cmd.frame_time_rts_th = cpu_to_le16(link->frame_time_rts_th); + + /* Block 26-tone RU OFDMA transmissions */ + if (mld_link->he_ru_2mhz_block) + flags |= LINK_FLG_RU_2MHZ_BLOCK; + + if (link->nontransmitted) { + ether_addr_copy(cmd.ref_bssid_addr, link->transmitter_bssid); + cmd.bssid_index = link->bssid_index; + } + + /* The only EHT parameter is puncturing, and starting from PHY cmd + * version 6 - it is sent there. For older versions of the PHY cmd, + * puncturing is not needed at all. + */ + if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS)) + changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS; + +send_cmd: + cmd.modify_mask = cpu_to_le32(changes); + cmd.flags = cpu_to_le32(flags); + + return iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY); +} + +int iwl_mld_change_link_in_fw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + u32 changes) +{ + if (WARN_ON(changes & LINK_CONTEXT_MODIFY_BANDWIDTH)) + changes &= ~LINK_CONTEXT_MODIFY_BANDWIDTH; + + return _iwl_mld_change_link_in_fw(mld, link, 0, changes); +} + +int iwl_mld_change_link_omi_bw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + enum ieee80211_sta_rx_bandwidth bw) +{ + return _iwl_mld_change_link_in_fw(mld, link, bw, + LINK_CONTEXT_MODIFY_BANDWIDTH); +} + +int iwl_mld_activate_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link || mld_link->active)) + return -EINVAL; + + mld_link->rx_omi.exit_ts = jiffies; + mld_link->active = true; + + ret = iwl_mld_change_link_in_fw(mld, link, + LINK_CONTEXT_MODIFY_ACTIVE); + if (ret) + mld_link->active = false; + + return ret; +} + +void iwl_mld_deactivate_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct iwl_probe_resp_data *probe_data; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link || !mld_link->active)) + return; + + iwl_mld_cancel_session_protection(mld, link->vif, link->link_id); + + /* If we deactivate the link, we will probably remove it, or switch + * channel. In both cases, the CSA or Notice of Absence information is + * now irrelevant. Remove the data here. + */ + probe_data = wiphy_dereference(mld->wiphy, mld_link->probe_resp_data); + RCU_INIT_POINTER(mld_link->probe_resp_data, NULL); + if (probe_data) + kfree_rcu(probe_data, rcu_head); + + mld_link->active = false; + + iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ACTIVE); + + /* Now that the link is not active in FW, we don't expect any new + * notifications for it. Cancel the ones that are already pending + */ + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_LINK, + mld_link->fw_id); +} + +static void +iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + struct iwl_link_config_cmd cmd = {}; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return; + + cmd.link_id = cpu_to_le32(mld_link->fw_id); + cmd.spec_link_id = link->link_id; + cmd.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID); + + iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE); +} + +static void iwl_mld_omi_bw_update(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + struct iwl_mld_link *mld_link, + struct ieee80211_link_sta *link_sta, + enum ieee80211_sta_rx_bandwidth bw, + bool ap_update) +{ + enum ieee80211_sta_rx_bandwidth apply_bw; + + mld_link->rx_omi.desired_bw = bw; + + /* Can't update OMI while already in progress, desired_bw was + * set so on FW notification the worker will see the change + * and apply new the new desired bw. + */ + if (mld_link->rx_omi.bw_in_progress) + return; + + if (bw == IEEE80211_STA_RX_BW_MAX) + apply_bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width); + else + apply_bw = bw; + + if (!ap_update) { + /* The update isn't due to AP tracking after leaving OMI, + * where the AP could increase BW and then we must tell + * it that we can do the increased BW as well, if we did + * update the chandef. + * In this case, if we want MAX, then we will need to send + * a new OMI to the AP if it increases its own bandwidth as + * we can (due to internal and FW limitations, and being + * worried the AP might break) only send to what we're doing + * at the moment. In this case, set last_max_bw; otherwise + * if we really want to decrease our bandwidth set it to 0 + * to indicate no updates are needed if the AP changes. + */ + if (bw != IEEE80211_STA_RX_BW_MAX) + mld_link->rx_omi.last_max_bw = apply_bw; + else + mld_link->rx_omi.last_max_bw = 0; + } else { + /* Otherwise, if we're already trying to do maximum and + * the AP is changing, set last_max_bw to the new max the + * AP is using, we'll only get to this code path if the + * new bandwidth of the AP is bigger than what we sent it + * previously. This avoids repeatedly sending updates if + * it changes bandwidth, only doing it once on an increase. + */ + mld_link->rx_omi.last_max_bw = apply_bw; + } + + if (ieee80211_prepare_rx_omi_bw(link_sta, bw)) { + mld_link->rx_omi.bw_in_progress = apply_bw; + iwl_mld_change_link_omi_bw(mld, link_conf, apply_bw); + } +} + +static void iwl_mld_omi_bw_finished_work(struct wiphy *wiphy, + struct wiphy_work *work) +{ + struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_link *mld_link = + container_of(work, typeof(*mld_link), rx_omi.finished_work.work); + enum ieee80211_sta_rx_bandwidth desired_bw, switched_to_bw; + struct ieee80211_vif *vif = mld_link->vif; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + + if (!mld_vif->ap_sta) + return; + + link_sta = wiphy_dereference(mld->wiphy, + mld_vif->ap_sta->link[mld_link->link_id]); + if (WARN_ON_ONCE(!link_sta)) + return; + + link_conf = link_conf_dereference_protected(vif, link_sta->link_id); + if (WARN_ON_ONCE(!link_conf)) + return; + + if (WARN_ON(!mld_link->rx_omi.bw_in_progress)) + return; + + desired_bw = mld_link->rx_omi.desired_bw; + switched_to_bw = mld_link->rx_omi.bw_in_progress; + + ieee80211_finalize_rx_omi_bw(link_sta); + mld_link->rx_omi.bw_in_progress = 0; + + if (desired_bw != switched_to_bw) + iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, + desired_bw, false); +} + +static struct ieee80211_vif * +iwl_mld_get_omi_bw_reduction_pointers(struct iwl_mld *mld, + struct ieee80211_link_sta **link_sta, + struct iwl_mld_link **mld_link) +{ + struct iwl_mld_vif *mld_vif; + struct ieee80211_vif *vif; + int n_link_stas = 0; + + *link_sta = NULL; + + if (mld->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC) + return NULL; + + vif = iwl_mld_get_bss_vif(mld); + if (!vif) + return NULL; + + for (int i = 0; i < ARRAY_SIZE(mld->fw_id_to_link_sta); i++) { + struct ieee80211_link_sta *tmp; + + tmp = wiphy_dereference(mld->wiphy, mld->fw_id_to_link_sta[i]); + if (IS_ERR_OR_NULL(tmp)) + continue; + + n_link_stas++; + *link_sta = tmp; + } + + /* can't do anything if we have TDLS peers or EMLSR */ + if (n_link_stas != 1) + return NULL; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + *mld_link = iwl_mld_link_dereference_check(mld_vif, + (*link_sta)->link_id); + if (WARN_ON(!*mld_link)) + return NULL; + + return vif; +} + +void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + enum ieee80211_sta_rx_bandwidth bw) +{ + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link *mld_link; + struct ieee80211_vif *vif; + + vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); + if (!vif) + return; + + if (WARN_ON(link_conf->vif != vif)) + return; + + /* This is 0 if we requested an OMI BW reduction and don't want to + * be sending an OMI when the AP's bandwidth changes. + */ + if (!mld_link->rx_omi.last_max_bw) + return; + + /* We only need to tell the AP if it increases BW over what we last + * told it we were using, if it reduces then our last OMI to it will + * not get used anyway (e.g. we said we want 160 but it's doing 80.) + */ + if (bw < mld_link->rx_omi.last_max_bw) + return; + + iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, true); +} + +void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link *mld_link; + struct ieee80211_vif *vif; + + vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); + if (IWL_FW_CHECK(mld, !vif, "unexpected OMI notification\n")) + return; + + if (IWL_FW_CHECK(mld, !mld_link->rx_omi.bw_in_progress, + "OMI notification when not requested\n")) + return; + + wiphy_delayed_work_queue(mld->hw->wiphy, + &mld_link->rx_omi.finished_work, + msecs_to_jiffies(IWL_MLD_OMI_AP_SETTLE_DELAY)); +} + +void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld) +{ + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link *mld_link; + struct ieee80211_vif *vif; + + vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); + if (!vif) + return; + + link_conf = link_conf_dereference_protected(vif, link_sta->link_id); + if (WARN_ON_ONCE(!link_conf)) + return; + + if (!link_conf->he_support) + return; + + mld_link->rx_omi.exit_ts = jiffies; + + iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, + IEEE80211_STA_RX_BW_MAX, false); +} + +void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld) +{ + enum ieee80211_sta_rx_bandwidth bw = IEEE80211_STA_RX_BW_MAX; + struct ieee80211_chanctx_conf *chanctx; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct cfg80211_chan_def chandef; + struct iwl_mld_link *mld_link; + struct iwl_mld_vif *mld_vif; + struct ieee80211_vif *vif; + struct iwl_mld_phy *phy; + u16 punctured; + int exit_thr; + + /* not allowed in CAM mode */ + if (iwlmld_mod_params.power_scheme == IWL_POWER_SCHEME_CAM) + return; + + /* must have one BSS connection (no P2P), no TDLS, nor EMLSR */ + vif = iwl_mld_get_omi_bw_reduction_pointers(mld, &link_sta, &mld_link); + if (!vif) + return; + + link_conf = link_conf_dereference_protected(vif, link_sta->link_id); + if (WARN_ON_ONCE(!link_conf)) + return; + + if (!link_conf->he_support) + return; + + chanctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); + if (WARN_ON(!chanctx)) + return; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + if (!mld_vif->authorized) + goto apply; + + /* must not be in low-latency mode */ + if (iwl_mld_vif_low_latency(mld_vif)) + goto apply; + + chandef = link_conf->chanreq.oper; + + switch (chandef.width) { + case NL80211_CHAN_WIDTH_320: + exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_320; + break; + case NL80211_CHAN_WIDTH_160: + exit_thr = IWL_MLD_OMI_EXIT_CHAN_LOAD_160; + break; + default: + /* since we reduce to 80 MHz, must have more to start with */ + goto apply; + } + + /* not to be done if primary 80 MHz is punctured */ + if (cfg80211_chandef_primary(&chandef, NL80211_CHAN_WIDTH_80, + &punctured) < 0 || + punctured != 0) + goto apply; + + phy = iwl_mld_phy_from_mac80211(chanctx); + + if (phy->channel_load_by_us > exit_thr) { + /* send OMI for max bandwidth */ + goto apply; + } + + if (phy->channel_load_by_us > IWL_MLD_OMI_ENTER_CHAN_LOAD) { + /* no changes between enter/exit thresholds */ + return; + } + + if (time_is_after_jiffies(mld_link->rx_omi.exit_ts + + msecs_to_jiffies(IWL_MLD_OMI_EXIT_PROTECTION))) + return; + + /* reduce bandwidth to 80 MHz to save power */ + bw = IEEE80211_STA_RX_BW_80; +apply: + iwl_mld_omi_bw_update(mld, link_conf, mld_link, link_sta, bw, false); +} + +IWL_MLD_ALLOC_FN(link, bss_conf) + +/* Constructor function for struct iwl_mld_link */ +static int +iwl_mld_init_link(struct iwl_mld *mld, struct ieee80211_bss_conf *link, + struct iwl_mld_link *mld_link) +{ + mld_link->vif = link->vif; + mld_link->link_id = link->link_id; + + iwl_mld_init_internal_sta(&mld_link->bcast_sta); + iwl_mld_init_internal_sta(&mld_link->mcast_sta); + iwl_mld_init_internal_sta(&mld_link->mon_sta); + + if (!mld->fw_status.in_hw_restart) + wiphy_delayed_work_init(&mld_link->rx_omi.finished_work, + iwl_mld_omi_bw_finished_work); + + return iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link); +} + +/* Initializes the link structure, maps fw id to the ieee80211_bss_conf, and + * adds a link to the fw + */ +int iwl_mld_add_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); + bool is_deflink = bss_conf == &bss_conf->vif->bss_conf; + int ret; + + if (!link) { + if (is_deflink) + link = &mld_vif->deflink; + else + link = kzalloc(sizeof(*link), GFP_KERNEL); + } else { + WARN_ON(!mld->fw_status.in_hw_restart); + } + + ret = iwl_mld_init_link(mld, bss_conf, link); + if (ret) + goto free; + + rcu_assign_pointer(mld_vif->link[bss_conf->link_id], link); + + ret = iwl_mld_add_link_to_fw(mld, bss_conf); + if (ret) { + RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); + RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); + goto free; + } + + return ret; + +free: + if (!is_deflink) + kfree(link); + return ret; +} + +/* Remove link from fw, unmap the bss_conf, and destroy the link structure */ +void iwl_mld_remove_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(bss_conf->vif); + struct iwl_mld_link *link = iwl_mld_link_from_mac80211(bss_conf); + bool is_deflink = link == &mld_vif->deflink; + + if (WARN_ON(!link || link->active)) + return; + + iwl_mld_rm_link_from_fw(mld, bss_conf); + /* Continue cleanup on failure */ + + if (!is_deflink) + kfree_rcu(link, rcu_head); + + RCU_INIT_POINTER(mld_vif->link[bss_conf->link_id], NULL); + + wiphy_delayed_work_cancel(mld->wiphy, &link->rx_omi.finished_work); + + if (WARN_ON(link->fw_id >= mld->fw->ucode_capa.num_links)) + return; + + RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link->fw_id], NULL); +} + +void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_missed_beacons_notif *notif = (const void *)pkt->data; + union iwl_dbg_tlv_tp_data tp_data = { .fw_pkt = pkt }; + u32 link_id = le32_to_cpu(notif->link_id); + u32 missed_bcon = le32_to_cpu(notif->consec_missed_beacons); + u32 missed_bcon_since_rx = + le32_to_cpu(notif->consec_missed_beacons_since_last_rx); + u32 scnd_lnk_bcn_lost = + le32_to_cpu(notif->consec_missed_beacons_other_link); + struct ieee80211_bss_conf *link_conf = + iwl_mld_fw_id_to_link_conf(mld, link_id); + u32 bss_param_ch_cnt_link_id; + struct ieee80211_vif *vif; + + if (WARN_ON(!link_conf)) + return; + + vif = link_conf->vif; + bss_param_ch_cnt_link_id = link_conf->bss_param_ch_cnt_link_id; + + IWL_DEBUG_INFO(mld, + "missed bcn link_id=%u, %u consecutive=%u\n", + link_id, missed_bcon, missed_bcon_since_rx); + + if (WARN_ON(!vif)) + return; + + mld->trans->dbg.dump_file_name_ext_valid = true; + snprintf(mld->trans->dbg.dump_file_name_ext, IWL_FW_INI_MAX_NAME, + "LinkId_%d_MacType_%d", link_id, + iwl_mld_mac80211_iftype_to_fw(vif)); + + iwl_dbg_tlv_time_point(&mld->fwrt, + IWL_FW_INI_TIME_POINT_MISSED_BEACONS, &tp_data); + + if (missed_bcon >= IWL_MLD_MISSED_BEACONS_THRESHOLD_LONG) { + if (missed_bcon_since_rx >= + IWL_MLD_MISSED_BEACONS_SINCE_RX_THOLD) { + ieee80211_connection_loss(vif); + return; + } + IWL_WARN(mld, + "missed beacons exceeds threshold, but receiving data. Stay connected, Expect bugs.\n"); + return; + } + + if (missed_bcon_since_rx > IWL_MLD_MISSED_BEACONS_THRESHOLD) { + ieee80211_cqm_beacon_loss_notify(vif, GFP_ATOMIC); + + /* try to switch links, no-op if we don't have MLO */ + iwl_mld_int_mlo_scan(mld, vif); + } + + /* no more logic if we're not in EMLSR */ + if (hweight16(vif->active_links) <= 1) + return; + + /* We are processing a notification before link activation */ + if (le32_to_cpu(notif->other_link_id) == FW_CTXT_ID_INVALID) + return; + + /* Exit EMLSR if we lost more than + * IWL_MLD_MISSED_BEACONS_EXIT_ESR_THRESH beacons on boths links + * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH on current link. + * OR more than IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED + * on current link and the link's bss_param_ch_count has changed on + * the other link's beacon. + */ + if ((missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS && + scnd_lnk_bcn_lost >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_2_LINKS) || + missed_bcon >= IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH || + (bss_param_ch_cnt_link_id != link_id && + missed_bcon >= + IWL_MLD_BCN_LOSS_EXIT_ESR_THRESH_BSS_PARAM_CHANGED)) { + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON, + iwl_mld_get_primary_link(vif)); + } +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif); + +bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 removed_link_id) +{ + struct iwl_missed_beacons_notif *notif = (void *)pkt->data; + + if (le32_to_cpu(notif->other_link_id) == removed_link_id) { + /* Second link is being removed. Don't cancel the notification, + * but mark second link as invalid. + */ + notif->other_link_id = cpu_to_le32(FW_CTXT_ID_INVALID); + } + + /* If the primary link is removed, cancel the notification */ + return le32_to_cpu(notif->link_id) == removed_link_id; +} + +int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + return iwl_mld_change_link_in_fw(mld, link, LINK_CONTEXT_MODIFY_ALL & + ~(LINK_CONTEXT_MODIFY_ACTIVE | + LINK_CONTEXT_MODIFY_EHT_PARAMS)); +} + +struct iwl_mld_rssi_to_grade { + s8 rssi[2]; + u16 grade; +}; + +#define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \ + { \ + .rssi = {_lb, _hb_uhb}, \ + .grade = _grade \ + } + +/* + * This array must be sorted by increasing RSSI for proper functionality. + * The grades are actually estimated throughput, represented as fixed-point + * with a scale factor of 1/10. + */ +static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = { + RSSI_TO_GRADE_LINE(-85, -89, 172), + RSSI_TO_GRADE_LINE(-83, -86, 344), + RSSI_TO_GRADE_LINE(-82, -85, 516), + RSSI_TO_GRADE_LINE(-80, -83, 688), + RSSI_TO_GRADE_LINE(-77, -79, 1032), + RSSI_TO_GRADE_LINE(-73, -76, 1376), + RSSI_TO_GRADE_LINE(-70, -74, 1548), + RSSI_TO_GRADE_LINE(-69, -72, 1720), + RSSI_TO_GRADE_LINE(-65, -68, 2064), + RSSI_TO_GRADE_LINE(-61, -66, 2294), + RSSI_TO_GRADE_LINE(-58, -61, 2580), + RSSI_TO_GRADE_LINE(-55, -58, 2868), + RSSI_TO_GRADE_LINE(-46, -55, 3098), + RSSI_TO_GRADE_LINE(-43, -54, 3442) +}; + +#define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade) + +#define DEFAULT_CHAN_LOAD_2GHZ 30 +#define DEFAULT_CHAN_LOAD_5GHZ 15 +#define DEFAULT_CHAN_LOAD_6GHZ 0 + +/* Factors calculation is done with fixed-point with a scaling factor of 1/256 */ +#define SCALE_FACTOR 256 +#define MAX_CHAN_LOAD 256 + +static unsigned int +iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf) +{ + enum nl80211_chan_width chan_width = + link_conf->chanreq.oper.width; + int mhz = nl80211_chan_width_to_mhz(chan_width); + unsigned int n_subchannels; + + if (WARN_ONCE(mhz < 20 || mhz > 320, + "Invalid channel width : (%d)\n", mhz)) + return 1; + + /* total number of subchannels */ + n_subchannels = mhz / 20; + + /* No puncturing if less than 80 MHz */ + if (mhz >= 80) + n_subchannels -= hweight16(link_conf->chanreq.oper.punctured); + + return n_subchannels; +} + +static int +iwl_mld_get_chan_load_from_element(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + struct ieee80211_vif *vif = link_conf->vif; + const struct cfg80211_bss_ies *ies; + const struct element *bss_load_elem = NULL; + const struct ieee80211_bss_load_elem *bss_load; + + guard(rcu)(); + + if (ieee80211_vif_link_active(vif, link_conf->link_id)) + ies = rcu_dereference(link_conf->bss->beacon_ies); + else + ies = rcu_dereference(link_conf->bss->ies); + + if (ies) + bss_load_elem = cfg80211_find_elem(WLAN_EID_QBSS_LOAD, + ies->data, ies->len); + + if (!bss_load_elem || + bss_load_elem->datalen != sizeof(*bss_load)) + return -EINVAL; + + bss_load = (const void *)bss_load_elem->data; + + return bss_load->channel_util; +} + +static unsigned int +iwl_mld_get_chan_load_by_us(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + bool expect_active_link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + struct ieee80211_chanctx_conf *chan_ctx; + struct iwl_mld_phy *phy; + + if (!mld_link || !mld_link->active) { + WARN_ON(expect_active_link); + return 0; + } + + if (WARN_ONCE(!rcu_access_pointer(mld_link->chan_ctx), + "Active link (%u) without channel ctxt assigned!\n", + link_conf->link_id)) + return 0; + + chan_ctx = wiphy_dereference(mld->wiphy, mld_link->chan_ctx); + phy = iwl_mld_phy_from_mac80211(chan_ctx); + + return phy->channel_load_by_us; +} + +/* Returns error if the channel utilization element is invalid/unavailable */ +int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + bool expect_active_link) +{ + int chan_load; + unsigned int chan_load_by_us; + + /* get overall load */ + chan_load = iwl_mld_get_chan_load_from_element(mld, link_conf); + if (chan_load < 0) + return chan_load; + + chan_load_by_us = iwl_mld_get_chan_load_by_us(mld, link_conf, + expect_active_link); + + /* channel load by us is given in percentage */ + chan_load_by_us = + NORMALIZE_PERCENT_TO_255(chan_load_by_us); + + /* Use only values that firmware sends that can possibly be valid */ + if (chan_load_by_us <= chan_load) + chan_load -= chan_load_by_us; + + return chan_load; +} + +static unsigned int +iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf) +{ + enum nl80211_band band = link_conf->chanreq.oper.chan->band; + + switch (band) { + case NL80211_BAND_2GHZ: + return DEFAULT_CHAN_LOAD_2GHZ; + case NL80211_BAND_5GHZ: + return DEFAULT_CHAN_LOAD_5GHZ; + case NL80211_BAND_6GHZ: + return DEFAULT_CHAN_LOAD_6GHZ; + default: + WARN_ON(1); + return 0; + } +} + +unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + int chan_load; + + chan_load = iwl_mld_get_chan_load_by_others(mld, link_conf, false); + if (chan_load >= 0) + return chan_load; + + /* No information from the element, take the defaults */ + chan_load = iwl_mld_get_default_chan_load(link_conf); + + /* The defaults are given in percentage */ + return NORMALIZE_PERCENT_TO_255(chan_load); +} + +static unsigned int +iwl_mld_get_avail_chan_load(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf); +} + +/* This function calculates the grade of a link. Returns 0 in error case */ +unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + enum nl80211_band band; + int rssi_idx; + s32 link_rssi; + unsigned int grade = MAX_GRADE; + + if (WARN_ON_ONCE(!link_conf)) + return 0; + + band = link_conf->chanreq.oper.chan->band; + if (WARN_ONCE(band != NL80211_BAND_2GHZ && + band != NL80211_BAND_5GHZ && + band != NL80211_BAND_6GHZ, + "Invalid band (%u)\n", band)) + return 0; + + link_rssi = MBM_TO_DBM(link_conf->bss->signal); + /* + * For 6 GHz the RSSI of the beacons is lower than + * the RSSI of the data. + */ + if (band == NL80211_BAND_6GHZ && link_rssi) + link_rssi += 4; + + rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1; + + /* No valid RSSI - take the lowest grade */ + if (!link_rssi) + link_rssi = rssi_to_grade_map[0].rssi[rssi_idx]; + + IWL_DEBUG_EHT(mld, + "Calculating grade of link %d: band = %d, bandwidth = %d, punctured subchannels =0x%x RSSI = %d\n", + link_conf->link_id, band, + link_conf->chanreq.oper.width, + link_conf->chanreq.oper.punctured, link_rssi); + + /* Get grade based on RSSI */ + for (int i = 0; i < ARRAY_SIZE(rssi_to_grade_map); i++) { + const struct iwl_mld_rssi_to_grade *line = + &rssi_to_grade_map[i]; + + if (link_rssi > line->rssi[rssi_idx]) + continue; + grade = line->grade; + break; + } + + /* Apply the channel load and puncturing factors */ + grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) / SCALE_FACTOR; + grade = grade * iwl_mld_get_n_subchannels(link_conf); + + IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade); + + return grade; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_get_link_grade); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/link.h b/drivers/net/wireless/intel/iwlwifi/mld/link.h new file mode 100644 index 000000000000..39f04aae5579 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/link.h @@ -0,0 +1,153 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_link_h__ +#define __iwl_mld_link_h__ + +#include <net/mac80211.h> + +#include "mld.h" +#include "sta.h" + +/** + * struct iwl_probe_resp_data - data for NoA/CSA updates + * @rcu_head: used for freeing the data on update + * @notif: notification data + * @noa_len: length of NoA attribute, calculated from the notification + */ +struct iwl_probe_resp_data { + struct rcu_head rcu_head; + struct iwl_probe_resp_data_notif notif; + int noa_len; +}; + +/** + * struct iwl_mld_link - link configuration parameters + * + * @rcu_head: RCU head for freeing this data. + * @fw_id: the fw id of the link. + * @active: if the link is active or not. + * @queue_params: QoS data from mac80211. This is updated with a call to + * drv_conf_tx per each AC, and then notified once with BSS_CHANGED_QOS. + * So we store it here and then send one link cmd for all the ACs. + * @chan_ctx: pointer to the channel context assigned to the link. If a link + * has an assigned channel context it means that it is active. + * @he_ru_2mhz_block: 26-tone RU OFDMA transmissions should be blocked. + * @igtk: fw can only have one IGTK at a time, whereas mac80211 can have two. + * This tracks the one IGTK that currently exists in FW. + * @vif: the vif this link belongs to + * @bcast_sta: station used for broadcast packets. Used in AP, GO and IBSS. + * @mcast_sta: station used for multicast packets. Used in AP, GO and IBSS. + * @mon_sta: station used for TX injection in monitor interface. + * @link_id: over the air link ID + * @ap_early_keys: The firmware cannot install keys before bcast/mcast STAs, + * but higher layers work differently, so we store the keys here for + * later installation. + * @silent_deactivation: next deactivation needs to be silent. + * @probe_resp_data: data from FW notification to store NOA related data to be + * inserted into probe response. + * @rx_omi: data for BW reduction with OMI + * @rx_omi.bw_in_progress: update is in progress (indicates target BW) + * @rx_omi.exit_ts: timestamp of last exit + * @rx_omi.finished_work: work for the delayed reaction to the firmware saying + * the change was applied, and for then applying a new mode if it was + * updated while waiting for firmware/AP settle delay. + * @rx_omi.desired_bw: desired bandwidth + * @rx_omi.last_max_bw: last maximum BW used by firmware, for AP BW changes + */ +struct iwl_mld_link { + struct rcu_head rcu_head; + + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + u8 fw_id; + bool active; + struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; + struct ieee80211_chanctx_conf __rcu *chan_ctx; + bool he_ru_2mhz_block; + struct ieee80211_key_conf *igtk; + ); + /* And here fields that survive a fw restart */ + struct ieee80211_vif *vif; + struct iwl_mld_int_sta bcast_sta; + struct iwl_mld_int_sta mcast_sta; + struct iwl_mld_int_sta mon_sta; + u8 link_id; + + struct { + struct wiphy_delayed_work finished_work; + unsigned long exit_ts; + enum ieee80211_sta_rx_bandwidth bw_in_progress, + desired_bw, + last_max_bw; + } rx_omi; + + /* we can only have 2 GTK + 2 IGTK + 2 BIGTK active at a time */ + struct ieee80211_key_conf *ap_early_keys[6]; + bool silent_deactivation; + struct iwl_probe_resp_data __rcu *probe_resp_data; +}; + +/* Cleanup function for struct iwl_mld_link, will be called in restart */ +static inline void +iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link) +{ + struct iwl_probe_resp_data *probe_data; + + probe_data = wiphy_dereference(mld->wiphy, link->probe_resp_data); + RCU_INIT_POINTER(link->probe_resp_data, NULL); + if (probe_data) + kfree_rcu(probe_data, rcu_head); + + CLEANUP_STRUCT(link); + if (link->bcast_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &link->bcast_sta); + if (link->mcast_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &link->mcast_sta); + if (link->mon_sta.sta_id != IWL_INVALID_STA) + iwl_mld_free_internal_sta(mld, &link->mon_sta); +} + +/* Convert a percentage from [0,100] to [0,255] */ +#define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * 256 / 100) + +int iwl_mld_add_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf); +void iwl_mld_remove_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *bss_conf); +int iwl_mld_activate_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *link); +void iwl_mld_deactivate_link(struct iwl_mld *mld, + struct ieee80211_bss_conf *link); +int iwl_mld_change_link_omi_bw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + enum ieee80211_sta_rx_bandwidth bw); +int iwl_mld_change_link_in_fw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, u32 changes); +void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +bool iwl_mld_cancel_missed_beacon_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 removed_link_id); +int iwl_mld_link_set_associated(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf); + +unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf); + +int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + bool expect_active_link); +void iwl_mld_handle_omi_status_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_leave_omi_bw_reduction(struct iwl_mld *mld); +void iwl_mld_check_omi_bw_reduction(struct iwl_mld *mld); +void iwl_mld_omi_ap_changed_bw(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + enum ieee80211_sta_rx_bandwidth bw); + +#endif /* __iwl_mld_link_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c new file mode 100644 index 000000000000..f7faa87b8ba6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "mld.h" +#include "iface.h" +#include "low_latency.h" +#include "hcmd.h" +#include "power.h" +#include "mlo.h" + +#define MLD_LL_WK_INTERVAL_MSEC 500 +#define MLD_LL_PERIOD (HZ * MLD_LL_WK_INTERVAL_MSEC / 1000) +#define MLD_LL_ACTIVE_WK_PERIOD (HZ * 10) + +/* packets/MLD_LL_WK_PERIOD seconds */ +#define MLD_LL_ENABLE_THRESH 100 + +static bool iwl_mld_calc_low_latency(struct iwl_mld *mld, + unsigned long timestamp) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + bool global_low_latency = false; + u8 num_rx_q = mld->trans->info.num_rxqs; + + for (int mac_id = 0; mac_id < NUM_MAC_INDEX_DRIVER; mac_id++) { + u32 total_vo_vi_pkts = 0; + bool ll_period_expired; + + /* If it's not initialized yet, it means we have not yet + * received/transmitted any vo/vi packet on this MAC. + */ + if (!ll->window_start[mac_id]) + continue; + + ll_period_expired = + time_after(timestamp, ll->window_start[mac_id] + + MLD_LL_ACTIVE_WK_PERIOD); + + if (ll_period_expired) + ll->window_start[mac_id] = timestamp; + + for (int q = 0; q < num_rx_q; q++) { + struct iwl_mld_low_latency_packets_counters *counters = + &mld->low_latency.pkts_counters[q]; + + spin_lock_bh(&counters->lock); + + total_vo_vi_pkts += counters->vo_vi[mac_id]; + + if (ll_period_expired) + counters->vo_vi[mac_id] = 0; + + spin_unlock_bh(&counters->lock); + } + + /* enable immediately with enough packets but defer + * disabling only if the low-latency period expired and + * below threshold. + */ + if (total_vo_vi_pkts > MLD_LL_ENABLE_THRESH) + mld->low_latency.result[mac_id] = true; + else if (ll_period_expired) + mld->low_latency.result[mac_id] = false; + + global_low_latency |= mld->low_latency.result[mac_id]; + } + + return global_low_latency; +} + +static void iwl_mld_low_latency_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = _data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool prev = mld_vif->low_latency_causes & LOW_LATENCY_TRAFFIC; + bool low_latency; + + if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld->low_latency.result))) + return; + + low_latency = mld->low_latency.result[mld_vif->fw_id]; + + if (prev != low_latency) + iwl_mld_vif_update_low_latency(mld, vif, low_latency, + LOW_LATENCY_TRAFFIC); +} + +static void iwl_mld_low_latency_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld *mld = container_of(wk, struct iwl_mld, + low_latency.work.work); + unsigned long timestamp = jiffies; + bool low_latency_active; + + if (mld->fw_status.in_hw_restart) + return; + + /* It is assumed that the work was scheduled only after checking + * at least MLD_LL_PERIOD has passed since the last update. + */ + + low_latency_active = iwl_mld_calc_low_latency(mld, timestamp); + + /* Update the timestamp now after the low-latency calculation */ + mld->low_latency.timestamp = timestamp; + + /* If low-latency is active we need to force re-evaluation after + * 10 seconds, so that we can disable low-latency when + * the low-latency traffic ends. + * + * Otherwise, we don't need to run the work because there is nothing to + * disable. + * + * Note that this has no impact on the regular scheduling of the + * updates triggered by traffic - those happen whenever the + * MLD_LL_PERIOD timeout expire. + */ + if (low_latency_active) + wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work, + MLD_LL_ACTIVE_WK_PERIOD); + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_low_latency_iter, mld); +} + +int iwl_mld_low_latency_init(struct iwl_mld *mld) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + unsigned long ts = jiffies; + + ll->pkts_counters = kcalloc(mld->trans->info.num_rxqs, + sizeof(*ll->pkts_counters), GFP_KERNEL); + if (!ll->pkts_counters) + return -ENOMEM; + + for (int q = 0; q < mld->trans->info.num_rxqs; q++) + spin_lock_init(&ll->pkts_counters[q].lock); + + wiphy_delayed_work_init(&ll->work, iwl_mld_low_latency_wk); + + ll->timestamp = ts; + + /* The low-latency window_start will be initialized per-MAC on + * the first vo/vi packet received/transmitted. + */ + + return 0; +} + +void iwl_mld_low_latency_free(struct iwl_mld *mld) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + + kfree(ll->pkts_counters); + ll->pkts_counters = NULL; +} + +void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + + ll->timestamp = jiffies; + + memset(ll->window_start, 0, sizeof(ll->window_start)); + memset(ll->result, 0, sizeof(ll->result)); + + for (int q = 0; q < mld->trans->info.num_rxqs; q++) + memset(ll->pkts_counters[q].vo_vi, 0, + sizeof(ll->pkts_counters[q].vo_vi)); +} + +static int iwl_mld_send_low_latency_cmd(struct iwl_mld *mld, bool low_latency, + u16 mac_id) +{ + struct iwl_mac_low_latency_cmd cmd = { + .mac_id = cpu_to_le32(mac_id) + }; + u16 cmd_id = WIDE_ID(MAC_CONF_GROUP, LOW_LATENCY_CMD); + int ret; + + if (low_latency) { + /* Currently we don't care about the direction */ + cmd.low_latency_rx = 1; + cmd.low_latency_tx = 1; + } + + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd); + if (ret) + IWL_ERR(mld, "Failed to send low latency command\n"); + + return ret; +} + +static void iwl_mld_vif_set_low_latency(struct iwl_mld_vif *mld_vif, bool set, + enum iwl_mld_low_latency_cause cause) +{ + if (set) + mld_vif->low_latency_causes |= cause; + else + mld_vif->low_latency_causes &= ~cause; +} + +void iwl_mld_vif_update_low_latency(struct iwl_mld *mld, + struct ieee80211_vif *vif, + bool low_latency, + enum iwl_mld_low_latency_cause cause) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool prev; + + prev = iwl_mld_vif_low_latency(mld_vif); + iwl_mld_vif_set_low_latency(mld_vif, low_latency, cause); + + low_latency = iwl_mld_vif_low_latency(mld_vif); + if (low_latency == prev) + return; + + if (iwl_mld_send_low_latency_cmd(mld, low_latency, mld_vif->fw_id)) { + /* revert to previous low-latency state */ + iwl_mld_vif_set_low_latency(mld_vif, prev, cause); + return; + } + + if (low_latency) + iwl_mld_leave_omi_bw_reduction(mld); + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_CLIENT) + return; + + iwl_mld_update_mac_power(mld, vif, false); + + if (low_latency) + iwl_mld_retry_emlsr(mld, vif); +} + +static bool iwl_mld_is_vo_vi_pkt(struct ieee80211_hdr *hdr) +{ + u8 tid; + static const u8 tid_to_mac80211_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO, + }; + + if (!hdr || !ieee80211_is_data_qos(hdr->frame_control)) + return false; + + tid = ieee80211_get_tid(hdr); + if (tid >= IWL_MAX_TID_COUNT) + return false; + + return tid_to_mac80211_ac[tid] < IEEE80211_AC_VI; +} + +void iwl_mld_low_latency_update_counters(struct iwl_mld *mld, + struct ieee80211_hdr *hdr, + struct ieee80211_sta *sta, + u8 queue) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + struct iwl_mld_low_latency_packets_counters *counters; + unsigned long ts = jiffies ? jiffies : 1; + u8 fw_id = mld_vif->fw_id; + + /* we should have failed op mode init if NULL */ + if (WARN_ON_ONCE(!mld->low_latency.pkts_counters)) + return; + + if (WARN_ON_ONCE(fw_id >= ARRAY_SIZE(counters->vo_vi) || + queue >= mld->trans->info.num_rxqs)) + return; + + if (mld->low_latency.stopped) + return; + + if (!iwl_mld_is_vo_vi_pkt(hdr)) + return; + + counters = &mld->low_latency.pkts_counters[queue]; + + spin_lock_bh(&counters->lock); + counters->vo_vi[fw_id]++; + spin_unlock_bh(&counters->lock); + + /* Initialize the window_start on the first vo/vi packet */ + if (!mld->low_latency.window_start[fw_id]) + mld->low_latency.window_start[fw_id] = ts; + + if (time_is_before_jiffies(mld->low_latency.timestamp + MLD_LL_PERIOD)) + wiphy_delayed_work_queue(mld->wiphy, &mld->low_latency.work, + 0); +} + +void iwl_mld_low_latency_stop(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + mld->low_latency.stopped = true; + + wiphy_delayed_work_cancel(mld->wiphy, &mld->low_latency.work); +} + +void iwl_mld_low_latency_restart(struct iwl_mld *mld) +{ + struct iwl_mld_low_latency *ll = &mld->low_latency; + bool low_latency = false; + unsigned long ts = jiffies; + + lockdep_assert_wiphy(mld->wiphy); + + ll->timestamp = ts; + mld->low_latency.stopped = false; + + for (int mac = 0; mac < NUM_MAC_INDEX_DRIVER; mac++) { + ll->window_start[mac] = 0; + low_latency |= ll->result[mac]; + + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { + spin_lock_bh(&ll->pkts_counters[q].lock); + ll->pkts_counters[q].vo_vi[mac] = 0; + spin_unlock_bh(&ll->pkts_counters[q].lock); + } + } + + /* if low latency is active, force re-evaluation to cover the case of + * no traffic. + */ + if (low_latency) + wiphy_delayed_work_queue(mld->wiphy, &ll->work, MLD_LL_PERIOD); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h new file mode 100644 index 000000000000..f59684d235af --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/low_latency.h @@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_low_latency_h__ +#define __iwl_mld_low_latency_h__ + +/** + * struct iwl_mld_low_latency_packets_counters - Packets counters + * @lock: synchronize the counting in data path against the worker + * @vo_vi: per-mac, counts the number of TX and RX voice and video packets + */ +struct iwl_mld_low_latency_packets_counters { + spinlock_t lock; + u32 vo_vi[NUM_MAC_INDEX_DRIVER]; +} ____cacheline_aligned_in_smp; + +/** + * enum iwl_mld_low_latency_cause - low-latency set causes + * + * @LOW_LATENCY_TRAFFIC: indicates low-latency traffic was detected + * @LOW_LATENCY_DEBUGFS: low-latency mode set from debugfs + * @LOW_LATENCY_VIF_TYPE: low-latency mode set because of vif type (AP) + */ +enum iwl_mld_low_latency_cause { + LOW_LATENCY_TRAFFIC = BIT(0), + LOW_LATENCY_DEBUGFS = BIT(1), + LOW_LATENCY_VIF_TYPE = BIT(2), +}; + +/** + * struct iwl_mld_low_latency - Manage low-latency detection and activation. + * @work: this work is used to detect low-latency by monitoring the number of + * voice and video packets transmitted in a period of time. If the + * threshold is reached, low-latency is activated. When active, + * it is deactivated if the threshold is not reached within a + * 10-second period. + * @timestamp: timestamp of the last update. + * @window_start: per-mac, timestamp of the start of the current window. when + * the window is over, the counters are reset. + * @pkts_counters: per-queue array voice/video packet counters + * @result: per-mac latest low-latency result + * @stopped: if true, ignore the requests to update the counters + */ +struct iwl_mld_low_latency { + struct wiphy_delayed_work work; + unsigned long timestamp; + unsigned long window_start[NUM_MAC_INDEX_DRIVER]; + struct iwl_mld_low_latency_packets_counters *pkts_counters; + bool result[NUM_MAC_INDEX_DRIVER]; + bool stopped; +}; + +int iwl_mld_low_latency_init(struct iwl_mld *mld); +void iwl_mld_low_latency_free(struct iwl_mld *mld); +void iwl_mld_low_latency_restart_cleanup(struct iwl_mld *mld); +void iwl_mld_vif_update_low_latency(struct iwl_mld *mld, + struct ieee80211_vif *vif, + bool low_latency, + enum iwl_mld_low_latency_cause cause); +void iwl_mld_low_latency_update_counters(struct iwl_mld *mld, + struct ieee80211_hdr *hdr, + struct ieee80211_sta *sta, + u8 queue); +void iwl_mld_low_latency_stop(struct iwl_mld *mld); +void iwl_mld_low_latency_restart(struct iwl_mld *mld); + +#endif /* __iwl_mld_low_latency_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c new file mode 100644 index 000000000000..4ba050397632 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.c @@ -0,0 +1,2712 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include <net/mac80211.h> +#include <linux/ip.h> + +#include "mld.h" +#include "mac80211.h" +#include "phy.h" +#include "iface.h" +#include "power.h" +#include "sta.h" +#include "agg.h" +#include "scan.h" +#include "d3.h" +#include "tlc.h" +#include "key.h" +#include "ap.h" +#include "tx.h" +#include "roc.h" +#include "mlo.h" +#include "stats.h" +#include "ftm-initiator.h" +#include "low_latency.h" +#include "fw/api/scan.h" +#include "fw/api/context.h" +#include "fw/api/filter.h" +#include "fw/api/sta.h" +#include "fw/api/tdls.h" +#ifdef CONFIG_PM_SLEEP +#include "fw/api/d3.h" +#endif /* CONFIG_PM_SLEEP */ +#include "iwl-trans.h" + +#define IWL_MLD_LIMITS(ap) \ + { \ + .max = 2, \ + .types = BIT(NL80211_IFTYPE_STATION), \ + }, \ + { \ + .max = 1, \ + .types = ap | \ + BIT(NL80211_IFTYPE_P2P_CLIENT) | \ + BIT(NL80211_IFTYPE_P2P_GO), \ + }, \ + { \ + .max = 1, \ + .types = BIT(NL80211_IFTYPE_P2P_DEVICE), \ + } + +static const struct ieee80211_iface_limit iwl_mld_limits[] = { + IWL_MLD_LIMITS(0) +}; + +static const struct ieee80211_iface_limit iwl_mld_limits_ap[] = { + IWL_MLD_LIMITS(BIT(NL80211_IFTYPE_AP)) +}; + +static const struct ieee80211_iface_combination +iwl_mld_iface_combinations[] = { + { + .num_different_channels = 2, + .max_interfaces = 4, + .limits = iwl_mld_limits, + .n_limits = ARRAY_SIZE(iwl_mld_limits), + }, + { + .num_different_channels = 1, + .max_interfaces = 4, + .limits = iwl_mld_limits_ap, + .n_limits = ARRAY_SIZE(iwl_mld_limits_ap), + }, +}; + +static const u8 if_types_ext_capa_sta[] = { + [0] = WLAN_EXT_CAPA1_EXT_CHANNEL_SWITCHING, + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF | + WLAN_EXT_CAPA8_MAX_MSDU_IN_AMSDU_LSB, + [8] = WLAN_EXT_CAPA9_MAX_MSDU_IN_AMSDU_MSB, + [9] = WLAN_EXT_CAPA10_TWT_REQUESTER_SUPPORT, +}; + +#define IWL_MLD_EMLSR_CAPA (IEEE80211_EML_CAP_EMLSR_SUPP | \ + IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_32US << \ + __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \ + IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \ + __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)) +#define IWL_MLD_CAPA_OPS (FIELD_PREP_CONST( \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \ + IEEE80211_MLD_CAP_OP_LINK_RECONF_SUPPORT) + +static const struct wiphy_iftype_ext_capab iftypes_ext_capa[] = { + { + .iftype = NL80211_IFTYPE_STATION, + .extended_capabilities = if_types_ext_capa_sta, + .extended_capabilities_mask = if_types_ext_capa_sta, + .extended_capabilities_len = sizeof(if_types_ext_capa_sta), + /* relevant only if EHT is supported */ + .eml_capabilities = IWL_MLD_EMLSR_CAPA, + .mld_capa_and_ops = IWL_MLD_CAPA_OPS, + }, +}; + +static void iwl_mld_hw_set_addresses(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + int num_addrs = 1; + + /* Extract MAC address */ + memcpy(mld->addresses[0].addr, mld->nvm_data->hw_addr, ETH_ALEN); + wiphy->addresses = mld->addresses; + wiphy->n_addresses = 1; + + /* Extract additional MAC addresses if available */ + if (mld->nvm_data->n_hw_addrs > 1) + num_addrs = min(mld->nvm_data->n_hw_addrs, + IWL_MLD_MAX_ADDRESSES); + + for (int i = 1; i < num_addrs; i++) { + memcpy(mld->addresses[i].addr, + mld->addresses[i - 1].addr, + ETH_ALEN); + mld->addresses[i].addr[ETH_ALEN - 1]++; + wiphy->n_addresses++; + } +} + +static void iwl_mld_hw_set_channels(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + struct ieee80211_supported_band *bands = mld->nvm_data->bands; + + wiphy->bands[NL80211_BAND_2GHZ] = &bands[NL80211_BAND_2GHZ]; + wiphy->bands[NL80211_BAND_5GHZ] = &bands[NL80211_BAND_5GHZ]; + + if (bands[NL80211_BAND_6GHZ].n_channels) + wiphy->bands[NL80211_BAND_6GHZ] = &bands[NL80211_BAND_6GHZ]; +} + +static void iwl_mld_hw_set_security(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + static const u32 mld_ciphers[] = { + WLAN_CIPHER_SUITE_WEP40, + WLAN_CIPHER_SUITE_WEP104, + WLAN_CIPHER_SUITE_TKIP, + WLAN_CIPHER_SUITE_CCMP, + WLAN_CIPHER_SUITE_GCMP, + WLAN_CIPHER_SUITE_GCMP_256, + WLAN_CIPHER_SUITE_AES_CMAC, + WLAN_CIPHER_SUITE_BIP_GMAC_128, + WLAN_CIPHER_SUITE_BIP_GMAC_256 + }; + + hw->wiphy->n_cipher_suites = ARRAY_SIZE(mld_ciphers); + hw->wiphy->cipher_suites = mld_ciphers; + + ieee80211_hw_set(hw, MFP_CAPABLE); + wiphy_ext_feature_set(hw->wiphy, + NL80211_EXT_FEATURE_BEACON_PROTECTION); +} + +static void iwl_mld_hw_set_antennas(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + + wiphy->available_antennas_tx = iwl_mld_get_valid_tx_ant(mld); + wiphy->available_antennas_rx = iwl_mld_get_valid_rx_ant(mld); +} + +static void iwl_mld_hw_set_pm(struct iwl_mld *mld) +{ +#ifdef CONFIG_PM_SLEEP + struct wiphy *wiphy = mld->wiphy; + + if (!device_can_wakeup(mld->trans->dev)) + return; + + mld->wowlan.flags |= WIPHY_WOWLAN_MAGIC_PKT | + WIPHY_WOWLAN_DISCONNECT | + WIPHY_WOWLAN_EAP_IDENTITY_REQ | + WIPHY_WOWLAN_RFKILL_RELEASE | + WIPHY_WOWLAN_NET_DETECT | + WIPHY_WOWLAN_SUPPORTS_GTK_REKEY | + WIPHY_WOWLAN_GTK_REKEY_FAILURE | + WIPHY_WOWLAN_4WAY_HANDSHAKE; + + mld->wowlan.n_patterns = IWL_WOWLAN_MAX_PATTERNS; + mld->wowlan.pattern_min_len = IWL_WOWLAN_MIN_PATTERN_LEN; + mld->wowlan.pattern_max_len = IWL_WOWLAN_MAX_PATTERN_LEN; + mld->wowlan.max_nd_match_sets = IWL_SCAN_MAX_PROFILES_V2; + + wiphy->wowlan = &mld->wowlan; +#endif /* CONFIG_PM_SLEEP */ +} + +static void iwl_mac_hw_set_radiotap(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + + hw->radiotap_mcs_details |= IEEE80211_RADIOTAP_MCS_HAVE_FEC | + IEEE80211_RADIOTAP_MCS_HAVE_STBC; + + hw->radiotap_vht_details |= IEEE80211_RADIOTAP_VHT_KNOWN_STBC | + IEEE80211_RADIOTAP_VHT_KNOWN_BEAMFORMED; + + hw->radiotap_timestamp.units_pos = + IEEE80211_RADIOTAP_TIMESTAMP_UNIT_US | + IEEE80211_RADIOTAP_TIMESTAMP_SPOS_PLCP_SIG_ACQ; + + /* this is the case for CCK frames, it's better (only 8) for OFDM */ + hw->radiotap_timestamp.accuracy = 22; +} + +static void iwl_mac_hw_set_flags(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + + ieee80211_hw_set(hw, USES_RSS); + ieee80211_hw_set(hw, HANDLES_QUIET_CSA); + ieee80211_hw_set(hw, AP_LINK_PS); + ieee80211_hw_set(hw, SIGNAL_DBM); + ieee80211_hw_set(hw, SPECTRUM_MGMT); + ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); + ieee80211_hw_set(hw, WANT_MONITOR_VIF); + ieee80211_hw_set(hw, SUPPORTS_PS); + ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); + ieee80211_hw_set(hw, AMPDU_AGGREGATION); + ieee80211_hw_set(hw, CONNECTION_MONITOR); + ieee80211_hw_set(hw, CHANCTX_STA_CSA); + ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); + ieee80211_hw_set(hw, SUPPORTS_CLONED_SKBS); + ieee80211_hw_set(hw, NEEDS_UNIQUE_STA_ADDR); + ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); + ieee80211_hw_set(hw, BUFF_MMPDU_TXQ); + ieee80211_hw_set(hw, STA_MMPDU_TXQ); + ieee80211_hw_set(hw, TX_AMSDU); + ieee80211_hw_set(hw, TX_FRAG_LIST); + ieee80211_hw_set(hw, TX_AMPDU_SETUP_IN_HW); + ieee80211_hw_set(hw, HAS_RATE_CONTROL); + ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); + ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); + ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); + ieee80211_hw_set(hw, TDLS_WIDER_BW); +} + +static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + struct wiphy *wiphy = hw->wiphy; + const struct iwl_ucode_capabilities *ucode_capa = &mld->fw->ucode_capa; + + snprintf(wiphy->fw_version, + sizeof(wiphy->fw_version), + "%.31s", mld->fw->fw_version); + + wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | + BIT(NL80211_IFTYPE_P2P_CLIENT) | + BIT(NL80211_IFTYPE_AP) | + BIT(NL80211_IFTYPE_P2P_GO) | + BIT(NL80211_IFTYPE_P2P_DEVICE) | + BIT(NL80211_IFTYPE_ADHOC); + + wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR | + NL80211_FEATURE_ND_RANDOM_MAC_ADDR | + NL80211_FEATURE_HT_IBSS | + NL80211_FEATURE_P2P_GO_CTWIN | + NL80211_FEATURE_LOW_PRIORITY_SCAN | + NL80211_FEATURE_P2P_GO_OPPPS | + NL80211_FEATURE_AP_MODE_CHAN_WIDTH_CHANGE | + NL80211_FEATURE_SUPPORTS_WMM_ADMISSION | + NL80211_FEATURE_TX_POWER_INSERTION | + NL80211_FEATURE_DS_PARAM_SET_IE_IN_PROBES; + + wiphy->flags |= WIPHY_FLAG_IBSS_RSN | + WIPHY_FLAG_AP_UAPSD | + WIPHY_FLAG_HAS_CHANNEL_SWITCH | + WIPHY_FLAG_SPLIT_SCAN_6GHZ | + WIPHY_FLAG_SUPPORTS_TDLS | + WIPHY_FLAG_SUPPORTS_EXT_KEK_KCK; + + if (mld->nvm_data->sku_cap_11be_enable && + !iwlwifi_mod_params.disable_11ax && + !iwlwifi_mod_params.disable_11be) + wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + + /* the firmware uses u8 for num of iterations, but 0xff is saved for + * infinite loop, so the maximum number of iterations is actually 254. + */ + wiphy->max_sched_scan_plan_iterations = 254; + wiphy->max_sched_scan_ie_len = iwl_mld_scan_max_template_size(); + wiphy->max_scan_ie_len = iwl_mld_scan_max_template_size(); + wiphy->max_sched_scan_ssids = PROBE_OPTION_MAX; + wiphy->max_scan_ssids = PROBE_OPTION_MAX; + wiphy->max_sched_scan_plans = IWL_MAX_SCHED_SCAN_PLANS; + wiphy->max_sched_scan_reqs = 1; + wiphy->max_sched_scan_plan_interval = U16_MAX; + wiphy->max_match_sets = IWL_SCAN_MAX_PROFILES_V2; + + wiphy->max_remain_on_channel_duration = 10000; + + wiphy->hw_version = mld->trans->info.hw_id; + + wiphy->hw_timestamp_max_peers = 1; + + wiphy->iface_combinations = iwl_mld_iface_combinations; + wiphy->n_iface_combinations = ARRAY_SIZE(iwl_mld_iface_combinations); + + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_DFS_CONCURRENT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BEACON_RATE_LEGACY); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SCAN_START_TIME); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_BSS_PARENT_TSF); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACCEPT_BCAST_PROBE_RESP); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_FILS_MAX_CHANNEL_TIME); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_OCE_PROBE_REQ_HIGH_TX_RATE); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER); + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_SPP_AMSDU_SUPPORT); + + if (fw_has_capa(ucode_capa, IWL_UCODE_TLV_CAPA_PROTECTED_TWT)) + wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_PROTECTED_TWT); + + wiphy->iftype_ext_capab = NULL; + wiphy->num_iftype_ext_capab = 0; + + if (!iwlwifi_mod_params.disable_11ax) { + wiphy->iftype_ext_capab = iftypes_ext_capa; + wiphy->num_iftype_ext_capab = ARRAY_SIZE(iftypes_ext_capa); + + ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, SUPPORTS_ONLY_HE_MULTI_BSSID); + } + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) + wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; + else + wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT; +} + +static void iwl_mac_hw_set_misc(struct iwl_mld *mld) +{ + struct ieee80211_hw *hw = mld->hw; + + hw->queues = IEEE80211_NUM_ACS; + + hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; + hw->netdev_features |= mld->trans->mac_cfg->base->features; + + hw->max_tx_fragments = mld->trans->info.max_skb_frags; + hw->max_listen_interval = IWL_MLD_CONN_LISTEN_INTERVAL; + + hw->uapsd_max_sp_len = IEEE80211_WMM_IE_STA_QOSINFO_SP_ALL; + hw->uapsd_queues = IEEE80211_WMM_IE_STA_QOSINFO_AC_VO | + IEEE80211_WMM_IE_STA_QOSINFO_AC_VI | + IEEE80211_WMM_IE_STA_QOSINFO_AC_BK | + IEEE80211_WMM_IE_STA_QOSINFO_AC_BE; + + hw->chanctx_data_size = sizeof(struct iwl_mld_phy); + hw->vif_data_size = sizeof(struct iwl_mld_vif); + hw->sta_data_size = sizeof(struct iwl_mld_sta); + hw->txq_data_size = sizeof(struct iwl_mld_txq); + + /* TODO: Remove this division when IEEE80211_MAX_AMPDU_BUF_EHT size + * is supported. + * Note: ensure that IWL_DEFAULT_QUEUE_SIZE_EHT is updated accordingly. + */ + hw->max_rx_aggregation_subframes = IEEE80211_MAX_AMPDU_BUF_EHT / 2; +} + +static int iwl_mld_hw_verify_preconditions(struct iwl_mld *mld) +{ + int ratecheck; + + /* check for rates version 3 */ + ratecheck = + (iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11) + + (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP, + TLC_MNG_UPDATE_NOTIF, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mld->fw, LEGACY_GROUP, + REPLY_RX_MPDU_CMD, 0) >= 6) + + (iwl_fw_lookup_notif_ver(mld->fw, DATA_PATH_GROUP, + RX_NO_DATA_NOTIF, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, TX_CMD, 0) >= 9); + + if (ratecheck != 0 && ratecheck != 5) { + IWL_ERR(mld, "Firmware has inconsistent rates\n"); + return -EINVAL; + } + + /* 11ax is expected to be enabled for all supported devices */ + if (WARN_ON(!mld->nvm_data->sku_cap_11ax_enable)) + return -EINVAL; + + /* LAR is expected to be enabled for all supported devices */ + if (WARN_ON(!mld->nvm_data->lar_enabled)) + return -EINVAL; + + /* All supported devices are currently using version 3 of the cmd. + * Since version 3, IWL_SCAN_MAX_PROFILES_V2 shall be used where + * necessary. + */ + if (WARN_ON(iwl_fw_lookup_cmd_ver(mld->fw, + SCAN_OFFLOAD_UPDATE_PROFILES_CMD, + IWL_FW_CMD_VER_UNKNOWN) != 3)) + return -EINVAL; + + return 0; +} + +int iwl_mld_register_hw(struct iwl_mld *mld) +{ + /* verify once essential preconditions required for setting + * the hw capabilities + */ + if (iwl_mld_hw_verify_preconditions(mld)) + return -EINVAL; + + iwl_mld_hw_set_addresses(mld); + iwl_mld_hw_set_channels(mld); + iwl_mld_hw_set_security(mld); + iwl_mld_hw_set_pm(mld); + iwl_mld_hw_set_antennas(mld); + iwl_mac_hw_set_radiotap(mld); + iwl_mac_hw_set_flags(mld); + iwl_mac_hw_set_wiphy(mld); + iwl_mac_hw_set_misc(mld); + + SET_IEEE80211_DEV(mld->hw, mld->trans->dev); + + return ieee80211_register_hw(mld->hw); +} + +static void +iwl_mld_mac80211_tx(struct ieee80211_hw *hw, + struct ieee80211_tx_control *control, struct sk_buff *skb) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_sta *sta = control->sta; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + u32 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + /* In AP mode, mgmt frames are sent on the bcast station, + * so the FW can't translate the MLD addr to the link addr. Do it here + */ + if (ieee80211_is_mgmt(hdr->frame_control) && sta && + link_id != IEEE80211_LINK_UNSPECIFIED && + !ieee80211_is_probe_resp(hdr->frame_control)) { + /* translate MLD addresses to LINK addresses */ + struct ieee80211_link_sta *link_sta = + rcu_dereference(sta->link[link_id]); + struct ieee80211_bss_conf *link_conf = + rcu_dereference(info->control.vif->link_conf[link_id]); + struct ieee80211_mgmt *mgmt; + + if (WARN_ON(!link_sta || !link_conf)) { + ieee80211_free_txskb(hw, skb); + return; + } + + mgmt = (void *)hdr; + memcpy(mgmt->da, link_sta->addr, ETH_ALEN); + memcpy(mgmt->sa, link_conf->addr, ETH_ALEN); + memcpy(mgmt->bssid, link_conf->bssid, ETH_ALEN); + } + + iwl_mld_tx_skb(mld, skb, NULL); +} + +static void +iwl_mld_restart_cleanup(struct iwl_mld *mld) +{ + iwl_cleanup_mld(mld); + + ieee80211_iterate_interfaces(mld->hw, IEEE80211_IFACE_ITER_ACTIVE, + iwl_mld_cleanup_vif, NULL); + + ieee80211_iterate_stations_atomic(mld->hw, + iwl_mld_cleanup_sta, NULL); + + iwl_mld_ftm_restart_cleanup(mld); +} + +static +int iwl_mld_mac80211_start(struct ieee80211_hw *hw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + bool in_d3 = false; + int ret = 0; + + lockdep_assert_wiphy(mld->wiphy); + +#ifdef CONFIG_PM_SLEEP + /* Unless the host goes into hibernate the FW always stays on and + * the d3_resume flow is used. When wowlan is configured, mac80211 + * would call it's resume callback and the wowlan_resume flow + * would be used. + */ + + in_d3 = mld->fw_status.in_d3; + if (in_d3) { + /* mac80211 already cleaned up the state, no need for cleanup */ + ret = iwl_mld_no_wowlan_resume(mld); + if (ret) + iwl_mld_stop_fw(mld); + } +#endif /* CONFIG_PM_SLEEP */ + + if (mld->fw_status.in_hw_restart) { + iwl_mld_stop_fw(mld); + iwl_mld_restart_cleanup(mld); + } + + if (!in_d3 || ret) { + ret = iwl_mld_start_fw(mld); + if (ret) + goto error; + } + + mld->scan.last_start_time_jiffies = jiffies; + + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_POST_INIT, + NULL); + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_POINT_PERIODIC, + NULL); + + return 0; + +error: + /* If we failed to restart the hw, there is nothing useful + * we can do but indicate we are no longer in restart. + */ + mld->fw_status.in_hw_restart = false; + + return ret; +} + +static +void iwl_mld_mac80211_stop(struct ieee80211_hw *hw, bool suspend) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + lockdep_assert_wiphy(mld->wiphy); + + wiphy_work_cancel(mld->wiphy, &mld->add_txqs_wk); + + /* if the suspend flow fails the fw is in error. Stop it here, and it + * will be started upon wakeup + */ + if (!suspend || + (IS_ENABLED(CONFIG_PM_SLEEP) && iwl_mld_no_wowlan_suspend(mld))) + iwl_mld_stop_fw(mld); + + /* Clear in_hw_restart flag when stopping the hw, as mac80211 won't + * execute the restart. + */ + mld->fw_status.in_hw_restart = false; + + /* We shouldn't have any UIDs still set. Loop over all the UIDs to + * make sure there's nothing left there and warn if any is found. + */ + for (int i = 0; i < ARRAY_SIZE(mld->scan.uid_status); i++) + if (WARN_ONCE(mld->scan.uid_status[i], + "UMAC scan UID %d status was not cleaned (0x%x 0x%x)\n", + i, mld->scan.uid_status[i], mld->scan.status)) + mld->scan.uid_status[i] = 0; +} + +static +int iwl_mld_mac80211_config(struct ieee80211_hw *hw, u32 changed) +{ + return 0; +} + +static +int iwl_mld_mac80211_add_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* Construct mld_vif, add it to fw, and map its ID to ieee80211_vif */ + ret = iwl_mld_add_vif(mld, vif); + if (ret) + return ret; + + /* + * Add the default link, but not if this is an MLD vif as that implies + * the HW is restarting and it will be configured by change_vif_links. + */ + if (!ieee80211_vif_is_mld(vif)) + ret = iwl_mld_add_link(mld, &vif->bss_conf); + if (ret) + goto err; + + if (vif->type == NL80211_IFTYPE_STATION) { + vif->driver_flags |= IEEE80211_VIF_REMOVE_AP_AFTER_DISASSOC; + if (!vif->p2p) + vif->driver_flags |= IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI; + } + + if (vif->p2p || iwl_fw_lookup_cmd_ver(mld->fw, PHY_CONTEXT_CMD, 0) < 5) + vif->driver_flags |= IEEE80211_VIF_IGNORE_OFDMA_WIDER_BW; + + /* + * For an MLD vif (in restart) we may not have a link; delay the call + * the initial change_vif_links. + */ + if (vif->type == NL80211_IFTYPE_STATION && + !ieee80211_vif_is_mld(vif)) + iwl_mld_update_mac_power(mld, vif, false); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + mld->monitor.on = true; + ieee80211_hw_set(mld->hw, RX_INCLUDES_FCS); + } + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + mld->p2p_device_vif = vif; + + return 0; + +err: + iwl_mld_rm_vif(mld, vif); + return ret; +} + +static +void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + lockdep_assert_wiphy(mld->wiphy); + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) + vif->driver_flags &= ~(IEEE80211_VIF_BEACON_FILTER | + IEEE80211_VIF_SUPPORTS_CQM_RSSI); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + __clear_bit(IEEE80211_HW_RX_INCLUDES_FCS, mld->hw->flags); + mld->monitor.on = false; + } + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + mld->p2p_device_vif = NULL; + + iwl_mld_remove_link(mld, &vif->bss_conf); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink); + iwl_mld_vif_from_mac80211(vif)->dbgfs_slink = NULL; +#endif + + iwl_mld_rm_vif(mld, vif); +} + +struct iwl_mld_mc_iter_data { + struct iwl_mld *mld; + int port_id; +}; + +static void iwl_mld_mc_iface_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_mc_iter_data *mc_data = data; + struct iwl_mld *mld = mc_data->mld; + struct iwl_mcast_filter_cmd *cmd = mld->mcast_filter_cmd; + struct iwl_host_cmd hcmd = { + .id = MCAST_FILTER_CMD, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + int ret, len; + + /* If we don't have free ports, mcast frames will be dropped */ + if (WARN_ON_ONCE(mc_data->port_id >= MAX_PORT_ID_NUM)) + return; + + if (vif->type != NL80211_IFTYPE_STATION || !vif->cfg.assoc) + return; + + cmd->port_id = mc_data->port_id++; + ether_addr_copy(cmd->bssid, vif->bss_conf.bssid); + len = roundup(sizeof(*cmd) + cmd->count * ETH_ALEN, 4); + + hcmd.len[0] = len; + hcmd.data[0] = cmd; + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) + IWL_ERR(mld, "mcast filter cmd error. ret=%d\n", ret); +} + +void iwl_mld_recalc_multicast_filter(struct iwl_mld *mld) +{ + struct iwl_mld_mc_iter_data iter_data = { + .mld = mld, + }; + + if (WARN_ON_ONCE(!mld->mcast_filter_cmd)) + return; + + ieee80211_iterate_active_interfaces(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_mc_iface_iterator, + &iter_data); +} + +static u64 +iwl_mld_mac80211_prepare_multicast(struct ieee80211_hw *hw, + struct netdev_hw_addr_list *mc_list) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mcast_filter_cmd *cmd; + struct netdev_hw_addr *addr; + int addr_count = netdev_hw_addr_list_count(mc_list); + bool pass_all = addr_count > MAX_MCAST_FILTERING_ADDRESSES; + int len; + + if (pass_all) + addr_count = 0; + + /* len must be a multiple of 4 */ + len = roundup(sizeof(*cmd) + addr_count * ETH_ALEN, 4); + cmd = kzalloc(len, GFP_ATOMIC); + if (!cmd) + return 0; + + if (pass_all) { + cmd->pass_all = 1; + goto out; + } + + netdev_hw_addr_list_for_each(addr, mc_list) { + IWL_DEBUG_MAC80211(mld, "mcast addr (%d): %pM\n", + cmd->count, addr->addr); + ether_addr_copy(&cmd->addr_list[cmd->count * ETH_ALEN], + addr->addr); + cmd->count++; + } + +out: + return (u64)(unsigned long)cmd; +} + +static +void iwl_mld_mac80211_configure_filter(struct ieee80211_hw *hw, + unsigned int changed_flags, + unsigned int *total_flags, + u64 multicast) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mcast_filter_cmd *cmd = (void *)(unsigned long)multicast; + + /* Replace previous configuration */ + kfree(mld->mcast_filter_cmd); + mld->mcast_filter_cmd = cmd; + + if (!cmd) + goto out; + + if (changed_flags & FIF_ALLMULTI) + cmd->pass_all = !!(*total_flags & FIF_ALLMULTI); + + if (cmd->pass_all) + cmd->count = 0; + + iwl_mld_recalc_multicast_filter(mld); +out: + *total_flags = 0; +} + +static +void iwl_mld_mac80211_wake_tx_queue(struct ieee80211_hw *hw, + struct ieee80211_txq *txq) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + + if (likely(mld_txq->status.allocated) || !txq->sta) { + iwl_mld_tx_from_txq(mld, txq); + return; + } + + /* We don't support TSPEC tids. %IEEE80211_NUM_TIDS is for mgmt */ + if (txq->tid != IEEE80211_NUM_TIDS && txq->tid >= IWL_MAX_TID_COUNT) { + IWL_DEBUG_MAC80211(mld, "TID %d is not supported\n", txq->tid); + return; + } + + /* The worker will handle any packets we leave on the txq now */ + + spin_lock_bh(&mld->add_txqs_lock); + /* The list is being deleted only after the queue is fully allocated. */ + if (list_empty(&mld_txq->list) && + /* recheck under lock, otherwise it can be added twice */ + !mld_txq->status.allocated) { + list_add_tail(&mld_txq->list, &mld->txqs_to_add); + wiphy_work_queue(mld->wiphy, &mld->add_txqs_wk); + } + spin_unlock_bh(&mld->add_txqs_lock); +} + +static void iwl_mld_teardown_tdls_peers(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { + struct ieee80211_link_sta *link_sta; + struct iwl_mld_sta *mld_sta; + + link_sta = wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[i]); + if (IS_ERR_OR_NULL(link_sta)) + continue; + + if (!link_sta->sta->tdls) + continue; + + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + + ieee80211_tdls_oper_request(mld_sta->vif, link_sta->addr, + NL80211_TDLS_TEARDOWN, + WLAN_REASON_TDLS_TEARDOWN_UNSPECIFIED, + GFP_KERNEL); + } +} + +static +int iwl_mld_add_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + int fw_id = iwl_mld_allocate_fw_phy_id(mld); + int ret; + + if (fw_id < 0) + return fw_id; + + phy->mld = mld; + phy->fw_id = fw_id; + phy->chandef = *iwl_mld_get_chandef_from_chanctx(mld, ctx); + + ret = iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_ADD); + if (ret) { + mld->used_phy_ids &= ~BIT(phy->fw_id); + return ret; + } + + if (hweight8(mld->used_phy_ids) > 1) + iwl_mld_teardown_tdls_peers(mld); + + return 0; +} + +static +void iwl_mld_remove_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + + iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_REMOVE); + mld->used_phy_ids &= ~BIT(phy->fw_id); +} + +static +void iwl_mld_change_chanctx(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, u32 changed) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + struct cfg80211_chan_def *chandef = + iwl_mld_get_chandef_from_chanctx(mld, ctx); + + /* We don't care about these */ + if (!(changed & ~(IEEE80211_CHANCTX_CHANGE_RX_CHAINS | + IEEE80211_CHANCTX_CHANGE_RADAR | + IEEE80211_CHANCTX_CHANGE_CHANNEL))) + return; + + /* Check if a FW update is required */ + + if (changed & IEEE80211_CHANCTX_CHANGE_AP) + goto update; + + if (chandef->chan == phy->chandef.chan && + chandef->center_freq1 == phy->chandef.center_freq1 && + chandef->punctured == phy->chandef.punctured) { + /* Check if we are toggling between HT and non-HT, no-op */ + if (phy->chandef.width == chandef->width || + (phy->chandef.width <= NL80211_CHAN_WIDTH_20 && + chandef->width <= NL80211_CHAN_WIDTH_20)) + return; + } +update: + + iwl_mld_update_phy_chandef(mld, ctx); +} + +static u8 +iwl_mld_chandef_get_primary_80(struct cfg80211_chan_def *chandef) +{ + int data_start; + int control_start; + int bw; + + if (chandef->width == NL80211_CHAN_WIDTH_320) + bw = 320; + else if (chandef->width == NL80211_CHAN_WIDTH_160) + bw = 160; + else + return 0; + + /* data is bw wide so the start is half the width */ + data_start = chandef->center_freq1 - bw / 2; + /* control is 20Mhz width */ + control_start = chandef->chan->center_freq - 10; + + return (control_start - data_start) / 80; +} + +static bool iwl_mld_can_activate_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_sta *mld_sta; + struct iwl_mld_link_sta *link_sta; + + /* In association, we activate the assoc link before adding the STA. */ + if (!mld_vif->ap_sta || !vif->cfg.assoc) + return true; + + mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta); + + /* When switching links, we need to wait with the activation until the + * STA was added to the FW. It'll be activated in + * iwl_mld_update_link_stas + */ + link_sta = wiphy_dereference(mld->wiphy, mld_sta->link[link->link_id]); + + /* In restart we can have a link_sta that doesn't exist in FW yet */ + return link_sta && link_sta->in_fw; +} + +static +int iwl_mld_assign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + unsigned int n_active = iwl_mld_count_active_links(mld, vif); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + /* if the assigned one was not counted yet, count it now */ + if (!rcu_access_pointer(mld_link->chan_ctx)) { + n_active++; + + /* Track addition of non-BSS link */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) { + ret = iwl_mld_emlsr_check_non_bss_block(mld, 1); + if (ret) + return ret; + } + } + + /* for AP, mac parameters such as HE support are updated at this stage. */ + if (vif->type == NL80211_IFTYPE_AP) { + ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + + if (ret) { + IWL_ERR(mld, "failed to update MAC %pM\n", vif->addr); + return -EINVAL; + } + } + + rcu_assign_pointer(mld_link->chan_ctx, ctx); + + if (n_active > 1) { + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + iwl_mld_leave_omi_bw_reduction(mld); + + /* Indicate to mac80211 that EML is enabled */ + vif->driver_flags |= IEEE80211_VIF_EML_ACTIVE; + + if (vif->active_links & BIT(mld_vif->emlsr.selected_links)) + mld_vif->emlsr.primary = mld_vif->emlsr.selected_primary; + else + mld_vif->emlsr.primary = __ffs(vif->active_links); + + iwl_dbg_tlv_time_point(&mld->fwrt, IWL_FW_INI_TIME_ESR_LINK_UP, + NULL); + } + + /* First send the link command with the phy context ID. + * Now that we have the phy, we know the band so also the rates + */ + ret = iwl_mld_change_link_in_fw(mld, link, + LINK_CONTEXT_MODIFY_RATES_INFO); + if (ret) + goto err; + + /* TODO: Initialize rate control for the AP station, since we might be + * doing a link switch here - we cannot initialize it before since + * this needs the phy context assigned (and in FW?), and we cannot + * do it later because it needs to be initialized as soon as we're + * able to TX on the link, i.e. when active. (task=link-switch) + */ + + /* Now activate the link */ + if (iwl_mld_can_activate_link(mld, vif, link)) { + ret = iwl_mld_activate_link(mld, link); + if (ret) + goto err; + } + + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link); + + if (vif->type == NL80211_IFTYPE_MONITOR) { + ret = iwl_mld_add_mon_sta(mld, vif, link); + if (ret) + goto deactivate_link; + + mld->monitor.p80 = + iwl_mld_chandef_get_primary_80(&vif->bss_conf.chanreq.oper); + } + + return 0; + +deactivate_link: + if (mld_link->active) + iwl_mld_deactivate_link(mld, link); +err: + RCU_INIT_POINTER(mld_link->chan_ctx, NULL); + return ret; +} + +static +void iwl_mld_unassign_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + unsigned int n_active = iwl_mld_count_active_links(mld, vif); + + if (WARN_ON(!mld_link)) + return; + + /* Track removal of non-BSS link */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) + iwl_mld_emlsr_check_non_bss_block(mld, -1); + + iwl_mld_deactivate_link(mld, link); + + if (vif->type == NL80211_IFTYPE_MONITOR) + iwl_mld_remove_mon_sta(mld, vif, link); + + if (n_active > 1) { + /* Indicate to mac80211 that EML is disabled */ + vif->driver_flags &= ~IEEE80211_VIF_EML_ACTIVE; + + iwl_dbg_tlv_time_point(&mld->fwrt, + IWL_FW_INI_TIME_ESR_LINK_DOWN, + NULL); + } + + RCU_INIT_POINTER(mld_link->chan_ctx, NULL); + + /* in the non-MLO case, remove/re-add the link to clean up FW state. + * In MLO, it'll be done in drv_change_vif_link + */ + if (!ieee80211_vif_is_mld(vif) && !mld_vif->ap_sta && + !WARN_ON_ONCE(vif->cfg.assoc) && + vif->type != NL80211_IFTYPE_AP && !mld->fw_status.in_hw_restart) { + iwl_mld_remove_link(mld, link); + iwl_mld_add_link(mld, link); + } +} + +static +int iwl_mld_mac80211_set_rts_threshold(struct ieee80211_hw *hw, u32 value) +{ + return 0; +} + +static void +iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + u64 changes) +{ + u32 link_changes = 0; + + if (changes & BSS_CHANGED_ERP_SLOT) + link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; + + if (changes & (BSS_CHANGED_ERP_CTS_PROT | BSS_CHANGED_HT)) + link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS; + + if (changes & (BSS_CHANGED_QOS | BSS_CHANGED_BANDWIDTH)) + link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; + + if (changes & BSS_CHANGED_HE_BSS_COLOR) + link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; + + if (link_changes) + iwl_mld_change_link_in_fw(mld, link, link_changes); + + if (changes & BSS_CHANGED_BEACON) + iwl_mld_update_beacon_template(mld, vif, link); +} + +static +u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + u64 changes) +{ + u32 link_changes = 0; + bool has_he, has_eht; + + if (changes & BSS_CHANGED_QOS && vif->cfg.assoc && link_conf->qos) + link_changes |= LINK_CONTEXT_MODIFY_QOS_PARAMS; + + if (changes & (BSS_CHANGED_ERP_PREAMBLE | BSS_CHANGED_BASIC_RATES | + BSS_CHANGED_ERP_SLOT)) + link_changes |= LINK_CONTEXT_MODIFY_RATES_INFO; + + if (changes & (BSS_CHANGED_HT | BSS_CHANGED_ERP_CTS_PROT)) + link_changes |= LINK_CONTEXT_MODIFY_PROTECT_FLAGS; + + /* TODO: task=MLO check mac80211's HE flags and if command is needed + * every time there's a link change. Currently used flags are + * BSS_CHANGED_HE_OBSS_PD and BSS_CHANGED_HE_BSS_COLOR. + */ + has_he = link_conf->he_support && !iwlwifi_mod_params.disable_11ax; + has_eht = link_conf->eht_support && !iwlwifi_mod_params.disable_11be; + + if (vif->cfg.assoc && (has_he || has_eht)) { + IWL_DEBUG_MAC80211(mld, "Associated in HE mode\n"); + link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS; + } + + return link_changes; +} + +static void +iwl_mld_mac80211_link_info_changed_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + u64 changes) +{ + u32 link_changes = iwl_mld_link_changed_mapping(mld, vif, link_conf, + changes); + + if (link_changes) + iwl_mld_change_link_in_fw(mld, link_conf, link_changes); + + if (changes & BSS_CHANGED_TPE) + iwl_mld_send_ap_tx_power_constraint_cmd(mld, vif, link_conf); + + if (changes & BSS_CHANGED_BEACON_INFO) + iwl_mld_update_mac_power(mld, vif, false); + + /* The firmware will wait quite a while after association before it + * starts filtering the beacons. We can safely enable beacon filtering + * upon CQM configuration, even if we didn't get a beacon yet. + */ + if (changes & (BSS_CHANGED_CQM | BSS_CHANGED_BEACON_INFO)) + iwl_mld_enable_beacon_filter(mld, link_conf, false); + + /* If we have used OMI before to reduce bandwidth to 80 MHz and then + * increased to 160 MHz again, and then the AP changes to 320 MHz, it + * will think that we're limited to 160 MHz right now. Update it by + * requesting a new OMI bandwidth. + */ + if (changes & BSS_CHANGED_BANDWIDTH) { + enum ieee80211_sta_rx_bandwidth bw; + + bw = ieee80211_chan_width_to_rx_bw(link_conf->chanreq.oper.width); + + iwl_mld_omi_ap_changed_bw(mld, link_conf, bw); + + } + + if (changes & BSS_CHANGED_BANDWIDTH) + iwl_mld_retry_emlsr(mld, vif); +} + +static int iwl_mld_update_mu_groups(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mu_group_mgmt_cmd cmd = {}; + + BUILD_BUG_ON(sizeof(cmd.membership_status) != + sizeof(link_conf->mu_group.membership)); + BUILD_BUG_ON(sizeof(cmd.user_position) != + sizeof(link_conf->mu_group.position)); + + memcpy(cmd.membership_status, link_conf->mu_group.membership, + WLAN_MEMBERSHIP_LEN); + memcpy(cmd.user_position, link_conf->mu_group.position, + WLAN_USER_POSITION_LEN); + + return iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + UPDATE_MU_GROUPS_CMD), + &cmd); +} + +static void +iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + u64 changes) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + switch (vif->type) { + case NL80211_IFTYPE_STATION: + iwl_mld_mac80211_link_info_changed_sta(mld, vif, link_conf, + changes); + break; + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + iwl_mld_link_info_changed_ap_ibss(mld, vif, link_conf, + changes); + break; + case NL80211_IFTYPE_MONITOR: + /* The firmware tracks this on its own in STATION mode, but + * obviously not in sniffer mode. + */ + if (changes & BSS_CHANGED_MU_GROUPS) + iwl_mld_update_mu_groups(mld, link_conf); + break; + default: + /* shouldn't happen */ + WARN_ON_ONCE(1); + } + + /* We now know our BSSID, we can configure the MAC context with + * eht_support if needed. + */ + if (changes & BSS_CHANGED_BSSID) + iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + + if (changes & BSS_CHANGED_TXPOWER) + iwl_mld_set_tx_power(mld, link_conf, link_conf->txpower); +} + +static void +iwl_mld_smps_workaround(struct iwl_mld *mld, struct ieee80211_vif *vif, bool enable) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + bool workaround_required = + iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) < 2; + + if (!workaround_required) + return; + + /* Send the device-level power commands since the + * firmware checks the POWER_TABLE_CMD's POWER_SAVE_EN bit to + * determine SMPS mode. + */ + if (mld_vif->ps_disabled == !enable) + return; + + mld_vif->ps_disabled = !enable; + + iwl_mld_update_device_power(mld, false); +} + +static +void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u64 changes) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (changes & BSS_CHANGED_ASSOC) { + ret = iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY); + if (ret) + IWL_ERR(mld, "failed to update context\n"); + + if (vif->cfg.assoc) { + /* Clear statistics to get clean beacon counter, and + * ask for periodic statistics, as they are needed for + * link selection and RX OMI decisions. + */ + iwl_mld_clear_stats_in_fw(mld); + iwl_mld_request_periodic_fw_stats(mld, true); + + iwl_mld_set_vif_associated(mld, vif); + } else { + iwl_mld_request_periodic_fw_stats(mld, false); + } + } + + if (changes & BSS_CHANGED_PS) { + iwl_mld_smps_workaround(mld, vif, vif->cfg.ps); + iwl_mld_update_mac_power(mld, vif, false); + } + + /* TODO: task=MLO BSS_CHANGED_MLD_VALID_LINKS/CHANGED_MLD_TTLM */ +} + +static int +iwl_mld_mac80211_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_scan_request *hw_req) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + if (WARN_ON(!hw_req->req.n_channels || + hw_req->req.n_channels > + mld->fw->ucode_capa.n_scan_channels)) + return -EINVAL; + + ret = iwl_mld_regular_scan_start(mld, vif, &hw_req->req, &hw_req->ies); + if (!ret) { + /* We will be busy with scanning, so the counters may not reflect the + * reality. Stop checking the counters until the scan ends + */ + iwl_mld_start_ignoring_tpt_updates(mld); + } + + return ret; +} + +static void +iwl_mld_mac80211_cancel_hw_scan(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* Due to a race condition, it's possible that mac80211 asks + * us to stop a hw_scan when it's already stopped. This can + * happen, for instance, if we stopped the scan ourselves, + * called ieee80211_scan_completed() and the userspace called + * cancel scan before ieee80211_scan_work() could run. + * To handle that, simply return if the scan is not running. + */ + if (mld->scan.status & IWL_MLD_SCAN_REGULAR) { + iwl_mld_scan_stop(mld, IWL_MLD_SCAN_REGULAR, true); + /* Scan is over, we can check again the tpt counters */ + iwl_mld_stop_ignoring_tpt_updates(mld); + } +} + +static int +iwl_mld_mac80211_sched_scan_start(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + return iwl_mld_sched_scan_start(mld, vif, req, ies, IWL_MLD_SCAN_SCHED); +} + +static int +iwl_mld_mac80211_sched_scan_stop(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* Due to a race condition, it's possible that mac80211 asks + * us to stop a sched_scan when it's already stopped. This + * can happen, for instance, if we stopped the scan ourselves, + * called ieee80211_sched_scan_stopped() and the userspace called + * stop sched scan before ieee80211_sched_scan_stopped_work() + * could run. To handle this, simply return if the scan is + * not running. + */ + if (!(mld->scan.status & IWL_MLD_SCAN_SCHED)) + return 0; + + return iwl_mld_scan_stop(mld, IWL_MLD_SCAN_SCHED, false); +} + +static void +iwl_mld_restart_complete_vif(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf; + struct iwl_mld *mld = data; + int link_id; + + for_each_vif_active_link(vif, link_conf, link_id) { + enum ieee80211_sta_rx_bandwidth bw; + struct iwl_mld_link *mld_link; + + mld_link = wiphy_dereference(mld->wiphy, + mld_vif->link[link_id]); + + if (WARN_ON_ONCE(!mld_link)) + continue; + + bw = mld_link->rx_omi.bw_in_progress; + if (bw) + iwl_mld_change_link_omi_bw(mld, link_conf, bw); + } +} + +static void +iwl_mld_mac80211_reconfig_complete(struct ieee80211_hw *hw, + enum ieee80211_reconfig_type reconfig_type) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + switch (reconfig_type) { + case IEEE80211_RECONFIG_TYPE_RESTART: + mld->fw_status.in_hw_restart = false; + iwl_mld_send_recovery_cmd(mld, ERROR_RECOVERY_END_OF_RECOVERY); + + ieee80211_iterate_interfaces(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_restart_complete_vif, mld); + + iwl_trans_finish_sw_reset(mld->trans); + /* no need to lock, adding in parallel would schedule too */ + if (!list_empty(&mld->txqs_to_add)) + wiphy_work_queue(mld->wiphy, &mld->add_txqs_wk); + + IWL_INFO(mld, "restart completed\n"); + break; + case IEEE80211_RECONFIG_TYPE_SUSPEND: + break; + } +} + +static +void iwl_mld_mac80211_mgd_prepare_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + u32 duration = IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS; + + /* After a successful association the connection is etalibeshed + * and we can rely on the quota to send the disassociation frame. + */ + if (info->was_assoc) + return; + + if (info->duration > duration) + duration = info->duration; + + iwl_mld_schedule_session_protection(mld, vif, duration, + IWL_MLD_SESSION_PROTECTION_MIN_TIME_MS, + info->link_id); +} + +static +void iwl_mld_mac_mgd_complete_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_prep_tx_info *info) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* Successful authentication is the only case that requires to let + * the session protection go. We'll need it for the upcoming + * association. For all the other cases, we need to cancel the session + * protection. + * After successful association the connection is established and + * further mgd tx can rely on the quota. + */ + if (info->success && info->subtype == IEEE80211_STYPE_AUTH) + return; + + /* The firmware will be on medium after we configure the vif as + * associated. Removing the session protection allows the firmware + * to stop being on medium. In order to ensure the continuity of our + * presence on medium, we need first to configure the vif as associated + * and only then, remove the session protection. + * Currently, mac80211 calls vif_cfg_changed() first and then, + * drv_mgd_complete_tx(). Ensure that this assumption stays true by + * a warning. + */ + WARN_ON(info->success && + (info->subtype == IEEE80211_STYPE_ASSOC_REQ || + info->subtype == IEEE80211_STYPE_REASSOC_REQ) && + !vif->cfg.assoc); + + iwl_mld_cancel_session_protection(mld, vif, info->link_id); +} + +static int +iwl_mld_mac80211_conf_tx(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id, u16 ac, + const struct ieee80211_tx_queue_params *params) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link; + + lockdep_assert_wiphy(mld->wiphy); + + link = iwl_mld_link_dereference_check(mld_vif, link_id); + if (!link) + return -EINVAL; + + link->queue_params[ac] = *params; + + /* No need to update right away, we'll get BSS_CHANGED_QOS + * The exception is P2P_DEVICE interface which needs immediate update. + */ + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) + iwl_mld_change_link_in_fw(mld, &vif->bss_conf, + LINK_CONTEXT_MODIFY_QOS_PARAMS); + + return 0; +} + +static void iwl_mld_set_uapsd(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + vif->driver_flags &= ~IEEE80211_VIF_SUPPORTS_UAPSD; + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + if (vif->p2p && + !(iwlwifi_mod_params.uapsd_disable & IWL_DISABLE_UAPSD_P2P_CLIENT)) + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; + + if (!vif->p2p && + !(iwlwifi_mod_params.uapsd_disable & IWL_DISABLE_UAPSD_BSS)) + vif->driver_flags |= IEEE80211_VIF_SUPPORTS_UAPSD; +} + +int iwl_mld_tdls_sta_count(struct iwl_mld *mld) +{ + int count = 0; + + lockdep_assert_wiphy(mld->wiphy); + + for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { + struct ieee80211_link_sta *link_sta; + + link_sta = wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[i]); + if (IS_ERR_OR_NULL(link_sta)) + continue; + + if (!link_sta->sta->tdls) + continue; + + count++; + } + + return count; +} + +static void iwl_mld_check_he_obss_narrow_bw_ru_iter(struct wiphy *wiphy, + struct cfg80211_bss *bss, + void *_data) +{ + bool *tolerated = _data; + const struct cfg80211_bss_ies *ies; + const struct element *elem; + + rcu_read_lock(); + ies = rcu_dereference(bss->ies); + elem = cfg80211_find_elem(WLAN_EID_EXT_CAPABILITY, ies->data, + ies->len); + + if (!elem || elem->datalen < 10 || + !(elem->data[10] & + WLAN_EXT_CAPA10_OBSS_NARROW_BW_RU_TOLERANCE_SUPPORT)) { + *tolerated = false; + } + rcu_read_unlock(); +} + +static void +iwl_mld_check_he_obss_narrow_bw_ru(struct iwl_mld *mld, + struct iwl_mld_link *mld_link, + struct ieee80211_bss_conf *link_conf) +{ + bool tolerated = true; + + if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan)) + return; + + if (!(link_conf->chanreq.oper.chan->flags & IEEE80211_CHAN_RADAR)) { + mld_link->he_ru_2mhz_block = false; + return; + } + + cfg80211_bss_iter(mld->wiphy, &link_conf->chanreq.oper, + iwl_mld_check_he_obss_narrow_bw_ru_iter, &tolerated); + + /* If there is at least one AP on radar channel that cannot + * tolerate 26-tone RU UL OFDMA transmissions using HE TB PPDU. + */ + mld_link->he_ru_2mhz_block = !tolerated; +} + +static void iwl_mld_link_set_2mhz_block(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + struct iwl_mld_link *mld_link = + iwl_mld_link_from_mac80211(link_conf); + + if (WARN_ON(!link_conf || !mld_link)) + continue; + + if (link_sta->he_cap.has_he) + iwl_mld_check_he_obss_narrow_bw_ru(mld, mld_link, + link_conf); + } +} + +static int iwl_mld_move_sta_state_up(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int tdls_count = 0; + int ret; + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) { + if (sta->tdls) { + if (vif->p2p || hweight8(mld->used_phy_ids) != 1) + return -EBUSY; + + tdls_count = iwl_mld_tdls_sta_count(mld); + if (tdls_count >= IWL_TDLS_STA_COUNT) + return -EBUSY; + } + + /* + * If this is the first STA (i.e. the AP) it won't do + * anything, otherwise must leave for any new STA on + * any other interface, or for TDLS, etc. + * Need to call this _before_ adding the STA so it can + * look up the one STA to use to ask mac80211 to leave + * OMI; in the unlikely event that adding the new STA + * then fails we'll just re-enter OMI later (via the + * statistics notification handling.) + */ + iwl_mld_leave_omi_bw_reduction(mld); + + ret = iwl_mld_add_sta(mld, sta, vif, STATION_TYPE_PEER); + if (ret) + return ret; + + /* just added first TDLS STA, so disable PM */ + if (sta->tdls && tdls_count == 0) + iwl_mld_update_mac_power(mld, vif, false); + + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) + mld_vif->ap_sta = sta; + + /* Initialize TLC here already - this really tells + * the firmware only what the supported legacy rates are + * (may be) since it's initialized already from what the + * AP advertised in the beacon/probe response. This will + * allow the firmware to send auth/assoc frames with one + * of the supported rates already, rather than having to + * use a mandatory rate. + * If we're the AP, we'll just assume mandatory rates at + * this point, but we know nothing about the STA anyway. + */ + iwl_mld_config_tlc(mld, vif, sta); + + return ret; + } else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_AUTH) { + iwl_mld_set_uapsd(mld, vif); + return 0; + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) { + ret = iwl_mld_update_all_link_stations(mld, sta); + + if (vif->type == NL80211_IFTYPE_STATION) + iwl_mld_link_set_2mhz_block(mld, vif, sta); + /* Now the link_sta's capabilities are set, update the FW */ + iwl_mld_config_tlc(mld, vif, sta); + + if (vif->type == NL80211_IFTYPE_AP) { + /* Update MAC_CFG_FILTER_ACCEPT_BEACON if at least + * one sta is associated + */ + if (++mld_vif->num_associated_stas == 1) + ret = iwl_mld_mac_fw_action(mld, vif, + FW_CTXT_ACTION_MODIFY); + } + + return ret; + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) { + ret = 0; + + if (!sta->tdls) { + mld_vif->authorized = true; + + /* Ensure any block due to a non-BSS link is synced */ + iwl_mld_emlsr_check_non_bss_block(mld, 0); + + /* Block EMLSR until a certain throughput it reached */ + if (!mld->fw_status.in_hw_restart && + IWL_MLD_ENTER_EMLSR_TPT_THRESH > 0) + iwl_mld_block_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_TPT, + 0); + + /* clear COEX_HIGH_PRIORITY_ENABLE */ + ret = iwl_mld_mac_fw_action(mld, vif, + FW_CTXT_ACTION_MODIFY); + if (ret) + return ret; + iwl_mld_smps_workaround(mld, vif, vif->cfg.ps); + } + + /* MFP is set by default before the station is authorized. + * Clear it here in case it's not used. + */ + if (!sta->mfp) + ret = iwl_mld_update_all_link_stations(mld, sta); + + /* We can use wide bandwidth now, not only 20 MHz */ + iwl_mld_config_tlc(mld, vif, sta); + + return ret; + } else { + return -EINVAL; + } +} + +static int iwl_mld_move_sta_state_down(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (old_state == IEEE80211_STA_AUTHORIZED && + new_state == IEEE80211_STA_ASSOC) { + if (!sta->tdls) { + mld_vif->authorized = false; + + memset(&mld_vif->emlsr.zeroed_on_not_authorized, 0, + sizeof(mld_vif->emlsr.zeroed_on_not_authorized)); + + wiphy_delayed_work_cancel(mld->wiphy, + &mld_vif->emlsr.prevent_done_wk); + wiphy_delayed_work_cancel(mld->wiphy, + &mld_vif->emlsr.tmp_non_bss_done_wk); + wiphy_work_cancel(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk); + wiphy_delayed_work_cancel(mld->wiphy, + &mld_vif->emlsr.check_tpt_wk); + + iwl_mld_reset_cca_40mhz_workaround(mld, vif); + iwl_mld_smps_workaround(mld, vif, true); + } + + /* once we move into assoc state, need to update the FW to + * stop using wide bandwidth + */ + iwl_mld_config_tlc(mld, vif, sta); + } else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) { + if (vif->type == NL80211_IFTYPE_AP && + !WARN_ON(!mld_vif->num_associated_stas)) { + /* Update MAC_CFG_FILTER_ACCEPT_BEACON if the last sta + * is disassociating + */ + if (--mld_vif->num_associated_stas == 0) + iwl_mld_mac_fw_action(mld, vif, + FW_CTXT_ACTION_MODIFY); + } + } else if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_NONE) { + /* nothing */ + } else if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) { + iwl_mld_remove_sta(mld, sta); + + if (sta->tdls && iwl_mld_tdls_sta_count(mld) == 0) { + /* just removed last TDLS STA, so enable PM */ + iwl_mld_update_mac_power(mld, vif, false); + } + } else { + return -EINVAL; + } + return 0; +} + +static int iwl_mld_mac80211_sta_state(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + IWL_DEBUG_MAC80211(mld, "station %pM state change %d->%d\n", + sta->addr, old_state, new_state); + + mld_sta->sta_state = new_state; + + if (old_state < new_state) + return iwl_mld_move_sta_state_up(mld, vif, sta, old_state, + new_state); + else + return iwl_mld_move_sta_state_down(mld, vif, sta, old_state, + new_state); +} + +static void iwl_mld_mac80211_flush(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u32 queues, bool drop) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* Make sure we're done with the deferred traffic before flushing */ + iwl_mld_add_txq_list(mld); + + for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { + struct ieee80211_link_sta *link_sta = + wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[i]); + + if (IS_ERR_OR_NULL(link_sta)) + continue; + + /* Check that the sta belongs to the given vif */ + if (vif && vif != iwl_mld_sta_from_mac80211(link_sta->sta)->vif) + continue; + + if (drop) + iwl_mld_flush_sta_txqs(mld, link_sta->sta); + else + iwl_mld_wait_sta_txqs_empty(mld, link_sta->sta); + } +} + +static void iwl_mld_mac80211_flush_sta(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + iwl_mld_flush_sta_txqs(mld, sta); +} + +static int +iwl_mld_mac80211_ampdu_action(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_ampdu_params *params) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_sta *sta = params->sta; + enum ieee80211_ampdu_mlme_action action = params->action; + u16 tid = params->tid; + u16 ssn = params->ssn; + u16 buf_size = params->buf_size; + u16 timeout = params->timeout; + int ret; + + IWL_DEBUG_HT(mld, "A-MPDU action on addr %pM tid: %d action: %d\n", + sta->addr, tid, action); + + switch (action) { + case IEEE80211_AMPDU_RX_START: + ret = iwl_mld_ampdu_rx_start(mld, sta, tid, ssn, buf_size, + timeout); + break; + case IEEE80211_AMPDU_RX_STOP: + ret = iwl_mld_ampdu_rx_stop(mld, sta, tid); + break; + default: + /* The mac80211 TX_AMPDU_SETUP_IN_HW flag is set for all + * devices, since all support TX A-MPDU offload in hardware. + * Therefore, no TX action should be requested here. + */ + WARN_ON_ONCE(1); + return -EINVAL; + } + + return ret; +} + +static bool iwl_mld_can_hw_csum(struct sk_buff *skb) +{ + u8 protocol = ip_hdr(skb)->protocol; + + return protocol == IPPROTO_TCP || protocol == IPPROTO_UDP; +} + +static bool iwl_mld_mac80211_can_aggregate(struct ieee80211_hw *hw, + struct sk_buff *head, + struct sk_buff *skb) +{ + if (!IS_ENABLED(CONFIG_INET)) + return false; + + /* For now don't aggregate IPv6 in AMSDU */ + if (skb->protocol != htons(ETH_P_IP)) + return false; + + /* Allow aggregation only if both frames have the same HW csum offload + * capability, ensuring consistent HW or SW csum handling in A-MSDU. + */ + return iwl_mld_can_hw_csum(skb) == iwl_mld_can_hw_csum(head); +} + +static void iwl_mld_mac80211_sync_rx_queues(struct ieee80211_hw *hw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + iwl_mld_sync_rx_queues(mld, IWL_MLD_RXQ_EMPTY, NULL, 0); +} + +static void iwl_mld_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + u32 changed) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + if (changed & (IEEE80211_RC_BW_CHANGED | + IEEE80211_RC_SUPP_RATES_CHANGED | + IEEE80211_RC_NSS_CHANGED)) { + struct ieee80211_bss_conf *link = + link_conf_dereference_check(vif, link_sta->link_id); + + if (WARN_ON(!link)) + return; + + iwl_mld_config_tlc_link(mld, vif, link, link_sta); + } +} + +#ifdef CONFIG_PM_SLEEP +static void iwl_mld_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + device_set_wakeup_enable(mld->trans->dev, enabled); +} + +/* Returns 0 on success. 1 if failed to suspend with wowlan: + * If the circumstances didn't satisfy the conditions for suspension + * with wowlan, mac80211 would use the no_wowlan flow. + * If an error had occurred we update the trans status and state here + * and the result will be stopping the FW. + */ +static int +iwl_mld_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + iwl_fw_runtime_suspend(&mld->fwrt); + + ret = iwl_mld_wowlan_suspend(mld, wowlan); + if (ret) { + if (ret < 0) { + mld->trans->state = IWL_TRANS_NO_FW; + set_bit(STATUS_FW_ERROR, &mld->trans->status); + } + return 1; + } + + if (iwl_mld_no_wowlan_suspend(mld)) + return 1; + + return 0; +} + +static int iwl_mld_resume(struct ieee80211_hw *hw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + ret = iwl_mld_wowlan_resume(mld); + if (ret) + return ret; + + iwl_fw_runtime_resume(&mld->fwrt); + + iwl_mld_low_latency_restart(mld); + + return 0; +} +#endif + +static int iwl_mld_alloc_ptk_pn(struct iwl_mld *mld, + struct iwl_mld_sta *mld_sta, + struct ieee80211_key_conf *key, + struct iwl_mld_ptk_pn **ptk_pn) +{ + u8 num_rx_queues = mld->trans->info.num_rxqs; + int keyidx = key->keyidx; + struct ieee80211_key_seq seq; + + if (WARN_ON(keyidx >= ARRAY_SIZE(mld_sta->ptk_pn))) + return -EINVAL; + + WARN_ON(rcu_access_pointer(mld_sta->ptk_pn[keyidx])); + *ptk_pn = kzalloc(struct_size(*ptk_pn, q, num_rx_queues), + GFP_KERNEL); + if (!*ptk_pn) + return -ENOMEM; + + for (u8 tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { + ieee80211_get_key_rx_seq(key, tid, &seq); + for (u8 q = 0; q < num_rx_queues; q++) + memcpy((*ptk_pn)->q[q].pn[tid], seq.ccmp.pn, + IEEE80211_CCMP_PN_LEN); + } + + rcu_assign_pointer(mld_sta->ptk_pn[keyidx], *ptk_pn); + + return 0; +} + +static int iwl_mld_set_key_add(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_sta *mld_sta = + sta ? iwl_mld_sta_from_mac80211(sta) : NULL; + struct iwl_mld_ptk_pn *ptk_pn = NULL; + int keyidx = key->keyidx; + int ret; + + /* Will be set to 0 if added successfully */ + key->hw_key_idx = STA_KEY_IDX_INVALID; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_WEP40: + case WLAN_CIPHER_SUITE_WEP104: + IWL_DEBUG_MAC80211(mld, "Use SW encryption for WEP\n"); + return -EOPNOTSUPP; + case WLAN_CIPHER_SUITE_TKIP: + if (vif->type == NL80211_IFTYPE_STATION) { + key->flags |= IEEE80211_KEY_FLAG_PUT_MIC_SPACE; + break; + } + IWL_DEBUG_MAC80211(mld, "Use SW encryption for TKIP\n"); + return -EOPNOTSUPP; + case WLAN_CIPHER_SUITE_CCMP: + case WLAN_CIPHER_SUITE_GCMP: + case WLAN_CIPHER_SUITE_GCMP_256: + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + break; + default: + return -EOPNOTSUPP; + } + + if (vif->type == NL80211_IFTYPE_STATION && + (keyidx == 6 || keyidx == 7)) + rcu_assign_pointer(mld_vif->bigtks[keyidx - 6], key); + + /* After exiting from RFKILL, hostapd configures GTK/ITGK before the + * AP is started, but those keys can't be sent to the FW before the + * MCAST/BCAST STAs are added to it (which happens upon AP start). + * Store it here to be sent later when the AP is started. + */ + if ((vif->type == NL80211_IFTYPE_ADHOC || + vif->type == NL80211_IFTYPE_AP) && !sta && + !mld_vif->ap_ibss_active) + return iwl_mld_store_ap_early_key(mld, key, mld_vif); + + if (!mld->fw_status.in_hw_restart && mld_sta && + key->flags & IEEE80211_KEY_FLAG_PAIRWISE && + (key->cipher == WLAN_CIPHER_SUITE_CCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) { + ret = iwl_mld_alloc_ptk_pn(mld, mld_sta, key, &ptk_pn); + if (ret) + return ret; + } + + IWL_DEBUG_MAC80211(mld, "set hwcrypto key (sta:%pM, id:%d)\n", + sta ? sta->addr : NULL, keyidx); + + ret = iwl_mld_add_key(mld, vif, sta, key); + if (ret) { + IWL_WARN(mld, "set key failed (%d)\n", ret); + if (ptk_pn) { + RCU_INIT_POINTER(mld_sta->ptk_pn[keyidx], NULL); + kfree(ptk_pn); + } + + return -EOPNOTSUPP; + } + + return 0; +} + +static void iwl_mld_set_key_remove(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_sta *mld_sta = + sta ? iwl_mld_sta_from_mac80211(sta) : NULL; + int keyidx = key->keyidx; + + if (vif->type == NL80211_IFTYPE_STATION && + (keyidx == 6 || keyidx == 7)) + RCU_INIT_POINTER(mld_vif->bigtks[keyidx - 6], NULL); + + if (mld_sta && key->flags & IEEE80211_KEY_FLAG_PAIRWISE && + (key->cipher == WLAN_CIPHER_SUITE_CCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP || + key->cipher == WLAN_CIPHER_SUITE_GCMP_256)) { + struct iwl_mld_ptk_pn *ptk_pn; + + if (WARN_ON(keyidx >= ARRAY_SIZE(mld_sta->ptk_pn))) + return; + + ptk_pn = wiphy_dereference(mld->wiphy, + mld_sta->ptk_pn[keyidx]); + RCU_INIT_POINTER(mld_sta->ptk_pn[keyidx], NULL); + if (!WARN_ON(!ptk_pn)) + kfree_rcu(ptk_pn, rcu_head); + } + + /* if this key was stored to be added later to the FW - free it here */ + if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + iwl_mld_free_ap_early_key(mld, key, mld_vif); + + /* We already removed it */ + if (key->hw_key_idx == STA_KEY_IDX_INVALID) + return; + + IWL_DEBUG_MAC80211(mld, "disable hwcrypto key\n"); + + iwl_mld_remove_key(mld, vif, sta, key); +} + +static int iwl_mld_mac80211_set_key(struct ieee80211_hw *hw, + enum set_key_cmd cmd, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct ieee80211_key_conf *key) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + switch (cmd) { + case SET_KEY: + ret = iwl_mld_set_key_add(mld, vif, sta, key); + if (ret) + ret = -EOPNOTSUPP; + break; + case DISABLE_KEY: + iwl_mld_set_key_remove(mld, vif, sta, key); + ret = 0; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int +iwl_mld_pre_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link = + iwl_mld_link_dereference_check(mld_vif, chsw->link_id); + u8 primary; + int selected; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + IWL_DEBUG_MAC80211(mld, "pre CSA to freq %d\n", + chsw->chandef.center_freq1); + + if (!iwl_mld_emlsr_active(vif)) + return 0; + + primary = iwl_mld_get_primary_link(vif); + + /* stay on the primary link unless it is undergoing a CSA with quiet */ + if (chsw->link_id == primary && chsw->block_tx) + selected = iwl_mld_get_other_link(vif, primary); + else + selected = primary; + + /* Remember to tell the firmware that this link can't tx + * Note that this logic seems to be unrelated to emlsr, but it + * really is needed only when emlsr is active. When we have a + * single link, the firmware will handle all this on its own. + * In multi-link scenarios, we can learn about the CSA from + * another link and this logic is too complex for the firmware + * to track. + * Since we want to de-activate the link that got a CSA with mode=1, + * we need to tell the firmware not to send any frame on that link + * as the firmware may not be aware that link is under a CSA + * with mode=1 (no Tx allowed). + */ + mld_link->silent_deactivation = chsw->block_tx; + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_CSA, selected); + + return 0; +} + +static void +iwl_mld_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_channel_switch *chsw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + /* By implementing this operation, we prevent mac80211 from + * starting its own channel switch timer, so that we can call + * ieee80211_chswitch_done() ourselves at the right time + * (Upon receiving the channel_switch_start notification from the fw) + */ + IWL_DEBUG_MAC80211(mld, + "dummy channel switch op\n"); +} + +static int +iwl_mld_post_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + + lockdep_assert_wiphy(mld->wiphy); + + WARN_ON(mld_link->silent_deactivation); + + return 0; +} + +static void +iwl_mld_abort_channel_switch(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + + IWL_DEBUG_MAC80211(mld, + "abort channel switch op\n"); + mld_link->silent_deactivation = false; +} + +static int +iwl_mld_switch_vif_chanctx_swap(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + iwl_mld_unassign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].old_ctx); + iwl_mld_remove_chanctx(hw, vifs[0].old_ctx); + + ret = iwl_mld_add_chanctx(hw, vifs[0].new_ctx); + if (ret) { + IWL_ERR(mld, "failed to add new_ctx during channel switch\n"); + goto out_reassign; + } + + ret = iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].new_ctx); + if (ret) { + IWL_ERR(mld, + "failed to assign new_ctx during channel switch\n"); + goto out_remove; + } + + return 0; + + out_remove: + iwl_mld_remove_chanctx(hw, vifs[0].new_ctx); + out_reassign: + if (iwl_mld_add_chanctx(hw, vifs[0].old_ctx)) { + IWL_ERR(mld, "failed to add old_ctx after failure\n"); + return ret; + } + + if (iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].old_ctx)) + IWL_ERR(mld, "failed to reassign old_ctx after failure\n"); + + return ret; +} + +static int +iwl_mld_switch_vif_chanctx_reassign(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int ret; + + iwl_mld_unassign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].old_ctx); + ret = iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].new_ctx); + if (ret) { + IWL_ERR(mld, + "failed to assign new_ctx during channel switch\n"); + goto out_reassign; + } + + return 0; + +out_reassign: + if (iwl_mld_assign_vif_chanctx(hw, vifs[0].vif, vifs[0].link_conf, + vifs[0].old_ctx)) + IWL_ERR(mld, "failed to reassign old_ctx after failure\n"); + + return ret; +} + +static int +iwl_mld_switch_vif_chanctx(struct ieee80211_hw *hw, + struct ieee80211_vif_chanctx_switch *vifs, + int n_vifs, + enum ieee80211_chanctx_switch_mode mode) +{ + int ret; + + /* we only support a single-vif right now */ + if (n_vifs > 1) + return -EOPNOTSUPP; + + switch (mode) { + case CHANCTX_SWMODE_SWAP_CONTEXTS: + ret = iwl_mld_switch_vif_chanctx_swap(hw, vifs); + break; + case CHANCTX_SWMODE_REASSIGN_VIF: + ret = iwl_mld_switch_vif_chanctx_reassign(hw, vifs); + break; + default: + ret = -EOPNOTSUPP; + break; + } + + return ret; +} + +static void iwl_mld_sta_pre_rcu_remove(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link_sta *mld_link_sta; + u8 link_id; + + lockdep_assert_wiphy(mld->wiphy); + + /* This is called before mac80211 does RCU synchronisation, + * so here we already invalidate our internal RCU-protected + * station pointer. The rest of the code will thus no longer + * be able to find the station this way, and we don't rely + * on further RCU synchronisation after the sta_state() + * callback deleted the station. + */ + for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) + RCU_INIT_POINTER(mld->fw_id_to_link_sta[mld_link_sta->fw_id], + NULL); + + if (sta == mld_vif->ap_sta) + mld_vif->ap_sta = NULL; +} + +static void +iwl_mld_mac80211_mgd_protect_tdls_discover(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + unsigned int link_id) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_bss_conf *link_conf; + u32 duration; + int ret; + + link_conf = wiphy_dereference(hw->wiphy, vif->link_conf[link_id]); + if (WARN_ON_ONCE(!link_conf)) + return; + + /* Protect the session to hear the TDLS setup response on the channel */ + + duration = 2 * link_conf->dtim_period * link_conf->beacon_int; + + ret = iwl_mld_start_session_protection(mld, vif, duration, duration, + link_id, HZ / 5); + if (ret) + IWL_ERR(mld, + "Failed to start session protection for TDLS: %d\n", + ret); +} + +static bool iwl_mld_can_activate_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 desired_links) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + int n_links = hweight16(desired_links); + + /* Check if HW supports the wanted number of links */ + return n_links <= iwl_mld_max_active_links(mld, vif); +} + +static int +iwl_mld_change_vif_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + u16 old_links, u16 new_links, + struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct ieee80211_bss_conf *link_conf; + u16 removed = old_links & ~new_links; + u16 added = new_links & ~old_links; + int err; + + lockdep_assert_wiphy(mld->wiphy); + + /* + * No bits designate non-MLO mode. We can handle MLO exit/enter by + * simply mapping that to link ID zero internally. + * Note that mac80211 does such a non-MLO to MLO switch during restart + * if it was in MLO before. In that case, we do not have a link to + * remove. + */ + if (old_links == 0 && !mld->fw_status.in_hw_restart) + removed |= BIT(0); + + if (new_links == 0) + added |= BIT(0); + + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + if (removed & BIT(i) && !WARN_ON(!old[i])) + iwl_mld_remove_link(mld, old[i]); + } + + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + if (added & BIT(i)) { + link_conf = link_conf_dereference_protected(vif, i); + if (!link_conf) { + err = -EINVAL; + goto remove_added_links; + } + + err = iwl_mld_add_link(mld, link_conf); + if (err) + goto remove_added_links; + } + } + + /* + * Ensure we always have a valid primary_link. When using multiple + * links the proper value is set in assign_vif_chanctx. + */ + mld_vif->emlsr.primary = new_links ? __ffs(new_links) : 0; + + /* + * Special MLO restart case. We did not have a link when the interface + * was added, so do the power configuration now. + */ + if (old_links == 0 && mld->fw_status.in_hw_restart) + iwl_mld_update_mac_power(mld, vif, false); + + return 0; + +remove_added_links: + for (int i = 0; i < IEEE80211_MLD_MAX_NUM_LINKS; i++) { + if (!(added & BIT(i))) + continue; + + link_conf = link_conf_dereference_protected(vif, i); + if (!link_conf || !iwl_mld_link_from_mac80211(link_conf)) + continue; + + iwl_mld_remove_link(mld, link_conf); + } + + if (WARN_ON(!iwl_mld_error_before_recovery(mld))) + return err; + + /* reconfig will fix us anyway */ + return 0; +} + +static int iwl_mld_change_sta_links(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + return iwl_mld_update_link_stas(mld, vif, sta, old_links, new_links); +} + +static int iwl_mld_mac80211_join_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + return iwl_mld_start_ap_ibss(hw, vif, &vif->bss_conf); +} + +static void iwl_mld_mac80211_leave_ibss(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + return iwl_mld_stop_ap_ibss(hw, vif, &vif->bss_conf); +} + +static int iwl_mld_mac80211_tx_last_beacon(struct ieee80211_hw *hw) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + return mld->ibss_manager; +} + +#define IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT (5 * HZ) + +static void iwl_mld_vif_iter_emlsr_block_tmp_non_bss(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS, + iwl_mld_get_primary_link(vif)); + if (ret) + return; + + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.tmp_non_bss_done_wk, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS_TIMEOUT); +} + +static void iwl_mld_prep_add_interface(struct ieee80211_hw *hw, + enum nl80211_iftype type) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + IWL_DEBUG_MAC80211(mld, "prep_add_interface: type=%u\n", type); + + if (!(type == NL80211_IFTYPE_AP || + type == NL80211_IFTYPE_P2P_GO || + type == NL80211_IFTYPE_P2P_CLIENT)) + return; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_block_tmp_non_bss, + NULL); +} + +static int iwl_mld_set_hw_timestamp(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_set_hw_timestamp *hwts) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + u32 protocols = 0; + + /* HW timestamping is only supported for a specific station */ + if (!hwts->macaddr) + return -EOPNOTSUPP; + + if (hwts->enable) + protocols = + IWL_TIME_SYNC_PROTOCOL_TM | IWL_TIME_SYNC_PROTOCOL_FTM; + + return iwl_mld_time_sync_config(mld, hwts->macaddr, protocols); +} + +static int iwl_mld_start_pmsr(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_pmsr_request *request) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + + return iwl_mld_ftm_start(mld, vif, request); +} + +const struct ieee80211_ops iwl_mld_hw_ops = { + .tx = iwl_mld_mac80211_tx, + .start = iwl_mld_mac80211_start, + .stop = iwl_mld_mac80211_stop, + .config = iwl_mld_mac80211_config, + .add_interface = iwl_mld_mac80211_add_interface, + .remove_interface = iwl_mld_mac80211_remove_interface, + .conf_tx = iwl_mld_mac80211_conf_tx, + .prepare_multicast = iwl_mld_mac80211_prepare_multicast, + .configure_filter = iwl_mld_mac80211_configure_filter, + .reconfig_complete = iwl_mld_mac80211_reconfig_complete, + .wake_tx_queue = iwl_mld_mac80211_wake_tx_queue, + .add_chanctx = iwl_mld_add_chanctx, + .remove_chanctx = iwl_mld_remove_chanctx, + .change_chanctx = iwl_mld_change_chanctx, + .assign_vif_chanctx = iwl_mld_assign_vif_chanctx, + .unassign_vif_chanctx = iwl_mld_unassign_vif_chanctx, + .set_rts_threshold = iwl_mld_mac80211_set_rts_threshold, + .link_info_changed = iwl_mld_mac80211_link_info_changed, + .vif_cfg_changed = iwl_mld_mac80211_vif_cfg_changed, + .set_key = iwl_mld_mac80211_set_key, + .hw_scan = iwl_mld_mac80211_hw_scan, + .cancel_hw_scan = iwl_mld_mac80211_cancel_hw_scan, + .sched_scan_start = iwl_mld_mac80211_sched_scan_start, + .sched_scan_stop = iwl_mld_mac80211_sched_scan_stop, + .mgd_prepare_tx = iwl_mld_mac80211_mgd_prepare_tx, + .mgd_complete_tx = iwl_mld_mac_mgd_complete_tx, + .sta_state = iwl_mld_mac80211_sta_state, + .sta_statistics = iwl_mld_mac80211_sta_statistics, + .flush = iwl_mld_mac80211_flush, + .flush_sta = iwl_mld_mac80211_flush_sta, + .ampdu_action = iwl_mld_mac80211_ampdu_action, + .can_aggregate_in_amsdu = iwl_mld_mac80211_can_aggregate, + .sync_rx_queues = iwl_mld_mac80211_sync_rx_queues, + .link_sta_rc_update = iwl_mld_sta_rc_update, + .start_ap = iwl_mld_start_ap_ibss, + .stop_ap = iwl_mld_stop_ap_ibss, + .pre_channel_switch = iwl_mld_pre_channel_switch, + .channel_switch = iwl_mld_channel_switch, + .post_channel_switch = iwl_mld_post_channel_switch, + .abort_channel_switch = iwl_mld_abort_channel_switch, + .switch_vif_chanctx = iwl_mld_switch_vif_chanctx, + .sta_pre_rcu_remove = iwl_mld_sta_pre_rcu_remove, + .remain_on_channel = iwl_mld_start_roc, + .cancel_remain_on_channel = iwl_mld_cancel_roc, + .can_activate_links = iwl_mld_can_activate_links, + .change_vif_links = iwl_mld_change_vif_links, + .change_sta_links = iwl_mld_change_sta_links, +#ifdef CONFIG_PM_SLEEP + .suspend = iwl_mld_suspend, + .resume = iwl_mld_resume, + .set_wakeup = iwl_mld_set_wakeup, + .set_rekey_data = iwl_mld_set_rekey_data, +#if IS_ENABLED(CONFIG_IPV6) + .ipv6_addr_change = iwl_mld_ipv6_addr_change, +#endif /* IS_ENABLED(CONFIG_IPV6) */ +#endif /* CONFIG_PM_SLEEP */ +#ifdef CONFIG_IWLWIFI_DEBUGFS + .vif_add_debugfs = iwl_mld_add_vif_debugfs, + .link_add_debugfs = iwl_mld_add_link_debugfs, + .link_sta_add_debugfs = iwl_mld_add_link_sta_debugfs, +#endif + .mgd_protect_tdls_discover = iwl_mld_mac80211_mgd_protect_tdls_discover, + .join_ibss = iwl_mld_mac80211_join_ibss, + .leave_ibss = iwl_mld_mac80211_leave_ibss, + .tx_last_beacon = iwl_mld_mac80211_tx_last_beacon, + .prep_add_interface = iwl_mld_prep_add_interface, + .set_hw_timestamp = iwl_mld_set_hw_timestamp, + .start_pmsr = iwl_mld_start_pmsr, +}; diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h new file mode 100644 index 000000000000..aad04d7b2617 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mac80211.h @@ -0,0 +1,13 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_mac80211_h__ +#define __iwl_mld_mac80211_h__ + +#include "mld.h" + +int iwl_mld_register_hw(struct iwl_mld *mld); +void iwl_mld_recalc_multicast_filter(struct iwl_mld *mld); + +#endif /* __iwl_mld_mac80211_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.c b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c new file mode 100644 index 000000000000..19cb562e7a73 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include <net/cfg80211.h> +#include <net/mac80211.h> + +#include <fw/dbg.h> +#include <iwl-nvm-parse.h> + +#include "mld.h" +#include "hcmd.h" +#include "mcc.h" + +/* It is the caller's responsibility to free the pointer returned here */ +static struct iwl_mcc_update_resp_v8 * +iwl_mld_parse_mcc_update_resp_v8(const struct iwl_rx_packet *pkt) +{ + const struct iwl_mcc_update_resp_v8 *mcc_resp_v8 = (const void *)pkt->data; + int n_channels = __le32_to_cpu(mcc_resp_v8->n_channels); + struct iwl_mcc_update_resp_v8 *resp_cp; + int notif_len = struct_size(resp_cp, channels, n_channels); + + if (iwl_rx_packet_payload_len(pkt) != notif_len) + return ERR_PTR(-EINVAL); + + resp_cp = kmemdup(mcc_resp_v8, notif_len, GFP_KERNEL); + if (!resp_cp) + return ERR_PTR(-ENOMEM); + + return resp_cp; +} + +/* It is the caller's responsibility to free the pointer returned here */ +static struct iwl_mcc_update_resp_v8 * +iwl_mld_parse_mcc_update_resp_v5_v6(const struct iwl_rx_packet *pkt) +{ + const struct iwl_mcc_update_resp_v4 *mcc_resp_v4 = (const void *)pkt->data; + struct iwl_mcc_update_resp_v8 *resp_cp; + int n_channels = __le32_to_cpu(mcc_resp_v4->n_channels); + int resp_len; + + if (iwl_rx_packet_payload_len(pkt) != + struct_size(mcc_resp_v4, channels, n_channels)) + return ERR_PTR(-EINVAL); + + resp_len = struct_size(resp_cp, channels, n_channels); + resp_cp = kzalloc(resp_len, GFP_KERNEL); + if (!resp_cp) + return ERR_PTR(-ENOMEM); + + resp_cp->status = mcc_resp_v4->status; + resp_cp->mcc = mcc_resp_v4->mcc; + resp_cp->cap = cpu_to_le32(le16_to_cpu(mcc_resp_v4->cap)); + resp_cp->source_id = mcc_resp_v4->source_id; + resp_cp->geo_info = mcc_resp_v4->geo_info; + resp_cp->n_channels = mcc_resp_v4->n_channels; + memcpy(resp_cp->channels, mcc_resp_v4->channels, + n_channels * sizeof(__le32)); + + return resp_cp; +} + +/* It is the caller's responsibility to free the pointer returned here */ +static struct iwl_mcc_update_resp_v8 * +iwl_mld_update_mcc(struct iwl_mld *mld, const char *alpha2, + enum iwl_mcc_source src_id) +{ + int resp_ver = iwl_fw_lookup_notif_ver(mld->fw, LONG_GROUP, + MCC_UPDATE_CMD, 0); + struct iwl_mcc_update_cmd mcc_update_cmd = { + .mcc = cpu_to_le16(alpha2[0] << 8 | alpha2[1]), + .source_id = (u8)src_id, + }; + struct iwl_mcc_update_resp_v8 *resp_cp; + struct iwl_rx_packet *pkt; + struct iwl_host_cmd cmd = { + .id = MCC_UPDATE_CMD, + .flags = CMD_WANT_SKB, + .data = { &mcc_update_cmd }, + .len[0] = sizeof(mcc_update_cmd), + }; + int ret; + u16 mcc; + + IWL_DEBUG_LAR(mld, "send MCC update to FW with '%c%c' src = %d\n", + alpha2[0], alpha2[1], src_id); + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) + return ERR_PTR(ret); + + pkt = cmd.resp_pkt; + + /* For Wifi-7 radios, we get version 8 + * For Wifi-6E radios, we get version 6 + * For Wifi-6 radios, we get version 5, but 5, 6, and 4 are compatible. + */ + switch (resp_ver) { + case 5: + case 6: + resp_cp = iwl_mld_parse_mcc_update_resp_v5_v6(pkt); + break; + case 8: + resp_cp = iwl_mld_parse_mcc_update_resp_v8(pkt); + break; + default: + IWL_FW_CHECK_FAILED(mld, "Unknown MCC_UPDATE_CMD version %d\n", resp_ver); + resp_cp = ERR_PTR(-EINVAL); + } + + if (IS_ERR(resp_cp)) + goto exit; + + mcc = le16_to_cpu(resp_cp->mcc); + + IWL_FW_CHECK(mld, !mcc, "mcc can't be 0: %d\n", mcc); + + IWL_DEBUG_LAR(mld, + "MCC response status: 0x%x. new MCC: 0x%x ('%c%c')\n", + le32_to_cpu(resp_cp->status), mcc, mcc >> 8, mcc & 0xff); + +exit: + iwl_free_resp(&cmd); + return resp_cp; +} + +/* It is the caller's responsibility to free the pointer returned here */ +struct ieee80211_regdomain * +iwl_mld_get_regdomain(struct iwl_mld *mld, + const char *alpha2, + enum iwl_mcc_source src_id, + bool *changed) +{ + struct ieee80211_regdomain *regd = NULL; + struct iwl_mcc_update_resp_v8 *resp; + u8 resp_ver = iwl_fw_lookup_notif_ver(mld->fw, IWL_ALWAYS_LONG_GROUP, + MCC_UPDATE_CMD, 0); + + IWL_DEBUG_LAR(mld, "Getting regdomain data for %s from FW\n", alpha2); + + lockdep_assert_wiphy(mld->wiphy); + + resp = iwl_mld_update_mcc(mld, alpha2, src_id); + if (IS_ERR(resp)) { + IWL_DEBUG_LAR(mld, "Could not get update from FW %ld\n", + PTR_ERR(resp)); + resp = NULL; + goto out; + } + + if (changed) { + u32 status = le32_to_cpu(resp->status); + + *changed = (status == MCC_RESP_NEW_CHAN_PROFILE || + status == MCC_RESP_ILLEGAL); + } + IWL_DEBUG_LAR(mld, "MCC update response version: %d\n", resp_ver); + + regd = iwl_parse_nvm_mcc_info(mld->trans, + __le32_to_cpu(resp->n_channels), + resp->channels, + __le16_to_cpu(resp->mcc), + __le16_to_cpu(resp->geo_info), + le32_to_cpu(resp->cap), resp_ver); + + if (IS_ERR(regd)) { + IWL_DEBUG_LAR(mld, "Could not get parse update from FW %ld\n", + PTR_ERR(regd)); + goto out; + } + + IWL_DEBUG_LAR(mld, "setting alpha2 from FW to %s (0x%x, 0x%x) src=%d\n", + regd->alpha2, regd->alpha2[0], + regd->alpha2[1], resp->source_id); + + mld->mcc_src = resp->source_id; + + if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing, + le16_to_cpu(resp->mcc))) + ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING); + else + __clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING, mld->hw->flags); + +out: + kfree(resp); + return regd; +} + +/* It is the caller's responsibility to free the pointer returned here */ +static struct ieee80211_regdomain * +iwl_mld_get_current_regdomain(struct iwl_mld *mld, + bool *changed) +{ + return iwl_mld_get_regdomain(mld, "ZZ", + MCC_SOURCE_GET_CURRENT, changed); +} + +void iwl_mld_update_changed_regdomain(struct iwl_mld *mld) +{ + struct ieee80211_regdomain *regd; + bool changed; + + regd = iwl_mld_get_current_regdomain(mld, &changed); + + if (IS_ERR_OR_NULL(regd)) + return; + + if (changed) + regulatory_set_wiphy_regd(mld->wiphy, regd); + kfree(regd); +} + +static int iwl_mld_apply_last_mcc(struct iwl_mld *mld, + const char *alpha2) +{ + struct ieee80211_regdomain *regd; + u32 used_src; + bool changed; + int ret; + + /* save the last source in case we overwrite it below */ + used_src = mld->mcc_src; + + /* Notify the firmware we support wifi location updates */ + regd = iwl_mld_get_current_regdomain(mld, NULL); + if (!IS_ERR_OR_NULL(regd)) + kfree(regd); + + /* Now set our last stored MCC and source */ + regd = iwl_mld_get_regdomain(mld, alpha2, used_src, + &changed); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + + /* update cfg80211 if the regdomain was changed */ + if (changed) + ret = regulatory_set_wiphy_regd_sync(mld->wiphy, regd); + else + ret = 0; + + kfree(regd); + return ret; +} + +int iwl_mld_init_mcc(struct iwl_mld *mld) +{ + const struct ieee80211_regdomain *r; + struct ieee80211_regdomain *regd; + char mcc[3]; + int retval; + + /* try to replay the last set MCC to FW */ + r = wiphy_dereference(mld->wiphy, mld->wiphy->regd); + + if (r) + return iwl_mld_apply_last_mcc(mld, r->alpha2); + + regd = iwl_mld_get_current_regdomain(mld, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + + if (!iwl_bios_get_mcc(&mld->fwrt, mcc)) { + kfree(regd); + regd = iwl_mld_get_regdomain(mld, mcc, MCC_SOURCE_BIOS, NULL); + if (IS_ERR_OR_NULL(regd)) + return -EIO; + } + + retval = regulatory_set_wiphy_regd_sync(mld->wiphy, regd); + + kfree(regd); + return retval; +} + +static void iwl_mld_find_assoc_vif_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + bool *assoc = data; + + if (vif->type == NL80211_IFTYPE_STATION && + vif->cfg.assoc) + *assoc = true; +} + +static bool iwl_mld_is_a_vif_assoc(struct iwl_mld *mld) +{ + bool assoc = false; + + ieee80211_iterate_active_interfaces_atomic(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_find_assoc_vif_iterator, + &assoc); + return assoc; +} + +void iwl_mld_handle_update_mcc(struct iwl_mld *mld, struct iwl_rx_packet *pkt) +{ + struct iwl_mcc_chub_notif *notif = (void *)pkt->data; + enum iwl_mcc_source src; + char mcc[3]; + struct ieee80211_regdomain *regd; + bool changed; + + lockdep_assert_wiphy(mld->wiphy); + + if (iwl_mld_is_a_vif_assoc(mld) && + notif->source_id == MCC_SOURCE_WIFI) { + IWL_DEBUG_LAR(mld, "Ignore mcc update while associated\n"); + return; + } + + mcc[0] = le16_to_cpu(notif->mcc) >> 8; + mcc[1] = le16_to_cpu(notif->mcc) & 0xff; + mcc[2] = '\0'; + src = notif->source_id; + + IWL_DEBUG_LAR(mld, + "RX: received chub update mcc cmd (mcc '%s' src %d)\n", + mcc, src); + regd = iwl_mld_get_regdomain(mld, mcc, src, &changed); + if (IS_ERR_OR_NULL(regd)) + return; + + if (changed) + regulatory_set_wiphy_regd(mld->hw->wiphy, regd); + kfree(regd); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mcc.h b/drivers/net/wireless/intel/iwlwifi/mld/mcc.h new file mode 100644 index 000000000000..2b31e5b5e2ed --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mcc.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_mcc_h__ +#define __iwl_mld_mcc_h__ + +int iwl_mld_init_mcc(struct iwl_mld *mld); +void iwl_mld_handle_update_mcc(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +void iwl_mld_update_changed_regdomain(struct iwl_mld *mld); +struct ieee80211_regdomain * +iwl_mld_get_regdomain(struct iwl_mld *mld, + const char *alpha2, + enum iwl_mcc_source src_id, + bool *changed); + +#endif /* __iwl_mld_mcc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.c b/drivers/net/wireless/intel/iwlwifi/mld/mld.c new file mode 100644 index 000000000000..8cdd960c5245 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.c @@ -0,0 +1,748 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <linux/rtnetlink.h> +#include <net/mac80211.h> + +#include "fw/api/rx.h" +#include "fw/api/datapath.h" +#include "fw/api/commands.h" +#include "fw/api/offload.h" +#include "fw/api/coex.h" +#include "fw/dbg.h" +#include "fw/uefi.h" + +#include "mld.h" +#include "mlo.h" +#include "mac80211.h" +#include "led.h" +#include "scan.h" +#include "tx.h" +#include "sta.h" +#include "regulatory.h" +#include "thermal.h" +#include "low_latency.h" +#include "hcmd.h" +#include "fw/api/location.h" + +#include "iwl-nvm-parse.h" + +#define DRV_DESCRIPTION "Intel(R) MLD wireless driver for Linux" +MODULE_DESCRIPTION(DRV_DESCRIPTION); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS("IWLWIFI"); + +static const struct iwl_op_mode_ops iwl_mld_ops; + +static int __init iwl_mld_init(void) +{ + int ret = iwl_opmode_register("iwlmld", &iwl_mld_ops); + + if (ret) + pr_err("Unable to register MLD op_mode: %d\n", ret); + + return ret; +} +module_init(iwl_mld_init); + +static void __exit iwl_mld_exit(void) +{ + iwl_opmode_deregister("iwlmld"); +} +module_exit(iwl_mld_exit); + +static void iwl_mld_hw_set_regulatory(struct iwl_mld *mld) +{ + struct wiphy *wiphy = mld->wiphy; + + wiphy->regulatory_flags |= REGULATORY_WIPHY_SELF_MANAGED; + wiphy->regulatory_flags |= REGULATORY_ENABLE_RELAX_NO_IR; +} + +VISIBLE_IF_IWLWIFI_KUNIT +void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, + const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, + struct ieee80211_hw *hw, struct dentry *dbgfs_dir) +{ + mld->dev = trans->dev; + mld->trans = trans; + mld->cfg = cfg; + mld->fw = fw; + mld->hw = hw; + mld->wiphy = hw->wiphy; + mld->debugfs_dir = dbgfs_dir; + + iwl_notification_wait_init(&mld->notif_wait); + + /* Setup async RX handling */ + spin_lock_init(&mld->async_handlers_lock); + wiphy_work_init(&mld->async_handlers_wk, + iwl_mld_async_handlers_wk); + + /* Dynamic Queue Allocation */ + spin_lock_init(&mld->add_txqs_lock); + INIT_LIST_HEAD(&mld->txqs_to_add); + wiphy_work_init(&mld->add_txqs_wk, iwl_mld_add_txqs_wk); + + /* Setup RX queues sync wait queue */ + init_waitqueue_head(&mld->rxq_sync.waitq); +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_construct_mld); + +static void __acquires(&mld->wiphy->mtx) +iwl_mld_fwrt_dump_start(void *ctx) +{ + struct iwl_mld *mld = ctx; + + wiphy_lock(mld->wiphy); +} + +static void __releases(&mld->wiphy->mtx) +iwl_mld_fwrt_dump_end(void *ctx) +{ + struct iwl_mld *mld = ctx; + + wiphy_unlock(mld->wiphy); +} + +static bool iwl_mld_d3_debug_enable(void *ctx) +{ + return IWL_MLD_D3_DEBUG; +} + +static int iwl_mld_fwrt_send_hcmd(void *ctx, struct iwl_host_cmd *host_cmd) +{ + struct iwl_mld *mld = (struct iwl_mld *)ctx; + int ret; + + wiphy_lock(mld->wiphy); + ret = iwl_mld_send_cmd(mld, host_cmd); + wiphy_unlock(mld->wiphy); + + return ret; +} + +static const struct iwl_fw_runtime_ops iwl_mld_fwrt_ops = { + .dump_start = iwl_mld_fwrt_dump_start, + .dump_end = iwl_mld_fwrt_dump_end, + .send_hcmd = iwl_mld_fwrt_send_hcmd, + .d3_debug_enable = iwl_mld_d3_debug_enable, +}; + +static void +iwl_mld_construct_fw_runtime(struct iwl_mld *mld, struct iwl_trans *trans, + const struct iwl_fw *fw, + struct dentry *debugfs_dir) +{ + iwl_fw_runtime_init(&mld->fwrt, trans, fw, &iwl_mld_fwrt_ops, mld, + NULL, NULL, debugfs_dir); + + iwl_fw_set_current_image(&mld->fwrt, IWL_UCODE_REGULAR); +} + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_legacy_names[] = { + HCMD_NAME(UCODE_ALIVE_NTFY), + HCMD_NAME(INIT_COMPLETE_NOTIF), + HCMD_NAME(PHY_CONTEXT_CMD), + HCMD_NAME(SCAN_CFG_CMD), + HCMD_NAME(SCAN_REQ_UMAC), + HCMD_NAME(SCAN_ABORT_UMAC), + HCMD_NAME(SCAN_COMPLETE_UMAC), + HCMD_NAME(TX_CMD), + HCMD_NAME(TXPATH_FLUSH), + HCMD_NAME(LEDS_CMD), + HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION), + HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION), + HCMD_NAME(SCAN_OFFLOAD_UPDATE_PROFILES_CMD), + HCMD_NAME(POWER_TABLE_CMD), + HCMD_NAME(PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION), + HCMD_NAME(BEACON_NOTIFICATION), + HCMD_NAME(BEACON_TEMPLATE_CMD), + HCMD_NAME(TX_ANT_CONFIGURATION_CMD), + HCMD_NAME(REDUCE_TX_POWER_CMD), + HCMD_NAME(MISSED_BEACONS_NOTIFICATION), + HCMD_NAME(MAC_PM_POWER_TABLE), + HCMD_NAME(MFUART_LOAD_NOTIFICATION), + HCMD_NAME(RSS_CONFIG_CMD), + HCMD_NAME(SCAN_ITERATION_COMPLETE_UMAC), + HCMD_NAME(REPLY_RX_MPDU_CMD), + HCMD_NAME(BA_NOTIF), + HCMD_NAME(MCC_UPDATE_CMD), + HCMD_NAME(MCC_CHUB_UPDATE_CMD), + HCMD_NAME(MCAST_FILTER_CMD), + HCMD_NAME(REPLY_BEACON_FILTERING_CMD), + HCMD_NAME(PROT_OFFLOAD_CONFIG_CMD), + HCMD_NAME(MATCH_FOUND_NOTIFICATION), + HCMD_NAME(WOWLAN_PATTERNS), + HCMD_NAME(WOWLAN_CONFIGURATION), + HCMD_NAME(WOWLAN_TSC_RSC_PARAM), + HCMD_NAME(WOWLAN_KEK_KCK_MATERIAL), + HCMD_NAME(DEBUG_HOST_COMMAND), + HCMD_NAME(LDBG_CONFIG_CMD), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_system_names[] = { + HCMD_NAME(SHARED_MEM_CFG_CMD), + HCMD_NAME(SOC_CONFIGURATION_CMD), + HCMD_NAME(INIT_EXTENDED_CFG_CMD), + HCMD_NAME(FW_ERROR_RECOVERY_CMD), + HCMD_NAME(RFI_CONFIG_CMD), + HCMD_NAME(RFI_GET_FREQ_TABLE_CMD), + HCMD_NAME(SYSTEM_STATISTICS_CMD), + HCMD_NAME(SYSTEM_STATISTICS_END_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_reg_and_nvm_names[] = { + HCMD_NAME(LARI_CONFIG_CHANGE), + HCMD_NAME(NVM_GET_INFO), + HCMD_NAME(TAS_CONFIG), + HCMD_NAME(SAR_OFFSET_MAPPING_TABLE_CMD), + HCMD_NAME(MCC_ALLOWED_AP_TYPE_CMD), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_debug_names[] = { + HCMD_NAME(HOST_EVENT_CFG), + HCMD_NAME(DBGC_SUSPEND_RESUME), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_mac_conf_names[] = { + HCMD_NAME(LOW_LATENCY_CMD), + HCMD_NAME(SESSION_PROTECTION_CMD), + HCMD_NAME(MAC_CONFIG_CMD), + HCMD_NAME(LINK_CONFIG_CMD), + HCMD_NAME(STA_CONFIG_CMD), + HCMD_NAME(AUX_STA_CMD), + HCMD_NAME(STA_REMOVE_CMD), + HCMD_NAME(ROC_CMD), + HCMD_NAME(MISSED_BEACONS_NOTIF), + HCMD_NAME(EMLSR_TRANS_FAIL_NOTIF), + HCMD_NAME(ROC_NOTIF), + HCMD_NAME(CHANNEL_SWITCH_ERROR_NOTIF), + HCMD_NAME(SESSION_PROTECTION_NOTIF), + HCMD_NAME(PROBE_RESPONSE_DATA_NOTIF), + HCMD_NAME(CHANNEL_SWITCH_START_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_data_path_names[] = { + HCMD_NAME(TRIGGER_RX_QUEUES_NOTIF_CMD), + HCMD_NAME(WNM_PLATFORM_PTM_REQUEST_CMD), + HCMD_NAME(WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD), + HCMD_NAME(RFH_QUEUE_CONFIG_CMD), + HCMD_NAME(TLC_MNG_CONFIG_CMD), + HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD), + HCMD_NAME(SCD_QUEUE_CONFIG_CMD), + HCMD_NAME(OMI_SEND_STATUS_NOTIF), + HCMD_NAME(ESR_MODE_NOTIF), + HCMD_NAME(MONITOR_NOTIF), + HCMD_NAME(TLC_MNG_UPDATE_NOTIF), + HCMD_NAME(MU_GROUP_MGMT_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_location_names[] = { + HCMD_NAME(TOF_RANGE_REQ_CMD), + HCMD_NAME(TOF_RANGE_RESPONSE_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_phy_names[] = { + HCMD_NAME(CMD_DTS_MEASUREMENT_TRIGGER_WIDE), + HCMD_NAME(CTDP_CONFIG_CMD), + HCMD_NAME(TEMP_REPORTING_THRESHOLDS_CMD), + HCMD_NAME(PER_CHAIN_LIMIT_OFFSET_CMD), + HCMD_NAME(CT_KILL_NOTIFICATION), + HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_statistics_names[] = { + HCMD_NAME(STATISTICS_OPER_NOTIF), + HCMD_NAME(STATISTICS_OPER_PART1_NOTIF), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_prot_offload_names[] = { + HCMD_NAME(WOWLAN_WAKE_PKT_NOTIFICATION), + HCMD_NAME(WOWLAN_INFO_NOTIFICATION), + HCMD_NAME(D3_END_NOTIFICATION), +}; + +/* Please keep this array *SORTED* by hex value. + * Access is done through binary search + */ +static const struct iwl_hcmd_names iwl_mld_coex_names[] = { + HCMD_NAME(PROFILE_NOTIF), +}; + +VISIBLE_IF_IWLWIFI_KUNIT +const struct iwl_hcmd_arr iwl_mld_groups[] = { + [LEGACY_GROUP] = HCMD_ARR(iwl_mld_legacy_names), + [LONG_GROUP] = HCMD_ARR(iwl_mld_legacy_names), + [SYSTEM_GROUP] = HCMD_ARR(iwl_mld_system_names), + [MAC_CONF_GROUP] = HCMD_ARR(iwl_mld_mac_conf_names), + [DATA_PATH_GROUP] = HCMD_ARR(iwl_mld_data_path_names), + [LOCATION_GROUP] = HCMD_ARR(iwl_mld_location_names), + [REGULATORY_AND_NVM_GROUP] = HCMD_ARR(iwl_mld_reg_and_nvm_names), + [DEBUG_GROUP] = HCMD_ARR(iwl_mld_debug_names), + [PHY_OPS_GROUP] = HCMD_ARR(iwl_mld_phy_names), + [STATISTICS_GROUP] = HCMD_ARR(iwl_mld_statistics_names), + [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mld_prot_offload_names), + [BT_COEX_GROUP] = HCMD_ARR(iwl_mld_coex_names), +}; +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_groups); + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +const unsigned int global_iwl_mld_goups_size = ARRAY_SIZE(iwl_mld_groups); +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(global_iwl_mld_goups_size); +#endif + +static void +iwl_mld_configure_trans(struct iwl_op_mode *op_mode) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + static const u8 no_reclaim_cmds[] = {TX_CMD}; + struct iwl_trans *trans = mld->trans; + u32 eckv_value; + + iwl_bios_setup_step(trans, &mld->fwrt); + iwl_uefi_get_step_table(trans); + + if (iwl_bios_get_eckv(&mld->fwrt, &eckv_value)) + IWL_DEBUG_RADIO(mld, "ECKV table doesn't exist in BIOS\n"); + else + trans->conf.ext_32khz_clock_valid = !!eckv_value; + + trans->conf.rx_buf_size = iwl_amsdu_size_to_rxb_size(); + trans->conf.command_groups = iwl_mld_groups; + trans->conf.command_groups_size = ARRAY_SIZE(iwl_mld_groups); + trans->conf.fw_reset_handshake = true; + trans->conf.queue_alloc_cmd_ver = + iwl_fw_lookup_cmd_ver(mld->fw, WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD), + 0); + trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info, + driver_data[2]); + BUILD_BUG_ON(sizeof(no_reclaim_cmds) > + sizeof(trans->conf.no_reclaim_cmds)); + memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds, + sizeof(no_reclaim_cmds)); + trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + + trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD; + trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); + trans->conf.wide_cmd_header = true; + + iwl_trans_op_mode_enter(trans, op_mode); +} + +/* + ***************************************************** + * op mode ops functions + ***************************************************** + */ + +#define NUM_FW_LOAD_RETRIES 3 +static struct iwl_op_mode * +iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, + const struct iwl_fw *fw, struct dentry *dbgfs_dir) +{ + struct ieee80211_hw *hw; + struct iwl_op_mode *op_mode; + struct iwl_mld *mld; + int ret; + + /* Allocate and initialize a new hardware device */ + hw = ieee80211_alloc_hw(sizeof(struct iwl_op_mode) + + sizeof(struct iwl_mld), + &iwl_mld_hw_ops); + if (!hw) + return ERR_PTR(-ENOMEM); + + op_mode = hw->priv; + + op_mode->ops = &iwl_mld_ops; + + mld = IWL_OP_MODE_GET_MLD(op_mode); + + iwl_construct_mld(mld, trans, cfg, fw, hw, dbgfs_dir); + + /* we'll verify later it matches between commands */ + mld->fw_rates_ver_3 = iwl_fw_lookup_cmd_ver(mld->fw, TX_CMD, 0) >= 11; + + iwl_mld_construct_fw_runtime(mld, trans, fw, dbgfs_dir); + + iwl_mld_get_bios_tables(mld); + iwl_uefi_get_sgom_table(trans, &mld->fwrt); + mld->bios_enable_puncturing = iwl_uefi_get_puncturing(&mld->fwrt); + + iwl_mld_hw_set_regulatory(mld); + + /* Configure transport layer with the opmode specific params */ + iwl_mld_configure_trans(op_mode); + + /* needed for regulatory init */ + rtnl_lock(); + /* Needed for sending commands */ + wiphy_lock(mld->wiphy); + + for (int i = 0; i < NUM_FW_LOAD_RETRIES; i++) { + ret = iwl_mld_load_fw(mld); + if (!ret) + break; + } + + if (!ret) { + mld->nvm_data = iwl_get_nvm(mld->trans, mld->fw, 0, 0); + if (IS_ERR(mld->nvm_data)) { + IWL_ERR(mld, "Failed to read NVM: %d\n", ret); + ret = PTR_ERR(mld->nvm_data); + } + } + + if (ret) { + wiphy_unlock(mld->wiphy); + rtnl_unlock(); + goto err; + } + + /* We are about to stop the FW. Notifications may require an + * operational FW, so handle them all here before we stop. + */ + wiphy_work_flush(mld->wiphy, &mld->async_handlers_wk); + + iwl_mld_stop_fw(mld); + + wiphy_unlock(mld->wiphy); + rtnl_unlock(); + + ret = iwl_mld_leds_init(mld); + if (ret) + goto free_nvm; + + ret = iwl_mld_alloc_scan_cmd(mld); + if (ret) + goto leds_exit; + + ret = iwl_mld_low_latency_init(mld); + if (ret) + goto free_scan_cmd; + + ret = iwl_mld_register_hw(mld); + if (ret) + goto low_latency_free; + + iwl_mld_toggle_tx_ant(mld, &mld->mgmt_tx_ant); + + iwl_mld_add_debugfs_files(mld, dbgfs_dir); + iwl_mld_thermal_initialize(mld); + + iwl_mld_ptp_init(mld); + + return op_mode; + +low_latency_free: + iwl_mld_low_latency_free(mld); +free_scan_cmd: + kfree(mld->scan.cmd); +leds_exit: + iwl_mld_leds_exit(mld); +free_nvm: + kfree(mld->nvm_data); +err: + iwl_trans_op_mode_leave(mld->trans); + ieee80211_free_hw(mld->hw); + return ERR_PTR(ret); +} + +static void +iwl_op_mode_mld_stop(struct iwl_op_mode *op_mode) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + iwl_mld_ptp_remove(mld); + iwl_mld_leds_exit(mld); + + iwl_mld_thermal_exit(mld); + + wiphy_lock(mld->wiphy); + iwl_mld_low_latency_stop(mld); + iwl_mld_deinit_time_sync(mld); + wiphy_unlock(mld->wiphy); + + ieee80211_unregister_hw(mld->hw); + + iwl_fw_runtime_free(&mld->fwrt); + iwl_mld_low_latency_free(mld); + + iwl_trans_op_mode_leave(mld->trans); + + kfree(mld->nvm_data); + kfree(mld->scan.cmd); + kfree(mld->error_recovery_buf); + kfree(mld->mcast_filter_cmd); + + ieee80211_free_hw(mld->hw); +} + +static void iwl_mld_queue_state_change(struct iwl_op_mode *op_mode, + int hw_queue, bool queue_full) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + struct ieee80211_txq *txq; + struct iwl_mld_sta *mld_sta; + struct iwl_mld_txq *mld_txq; + + rcu_read_lock(); + + txq = rcu_dereference(mld->fw_id_to_txq[hw_queue]); + if (!txq) { + rcu_read_unlock(); + + if (queue_full) { + /* An internal queue is not expected to become full */ + IWL_WARN(mld, + "Internal hw_queue %d is full! stopping all queues\n", + hw_queue); + /* Stop all queues, as an internal queue is not + * mapped to a mac80211 one + */ + ieee80211_stop_queues(mld->hw); + } else { + ieee80211_wake_queues(mld->hw); + } + + return; + } + + mld_txq = iwl_mld_txq_from_mac80211(txq); + mld_sta = txq->sta ? iwl_mld_sta_from_mac80211(txq->sta) : NULL; + + mld_txq->status.stop_full = queue_full; + + if (!queue_full && mld_sta && + mld_sta->sta_state != IEEE80211_STA_NOTEXIST) { + local_bh_disable(); + iwl_mld_tx_from_txq(mld, txq); + local_bh_enable(); + } + + rcu_read_unlock(); +} + +static void +iwl_mld_queue_full(struct iwl_op_mode *op_mode, int hw_queue) +{ + iwl_mld_queue_state_change(op_mode, hw_queue, true); +} + +static void +iwl_mld_queue_not_full(struct iwl_op_mode *op_mode, int hw_queue) +{ + iwl_mld_queue_state_change(op_mode, hw_queue, false); +} + +static bool +iwl_mld_set_hw_rfkill_state(struct iwl_op_mode *op_mode, bool state) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + iwl_mld_set_hwkill(mld, state); + + return false; +} + +static void +iwl_mld_free_skb(struct iwl_op_mode *op_mode, struct sk_buff *skb) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]); + ieee80211_free_txskb(mld->hw, skb); +} + +static void iwl_mld_read_error_recovery_buffer(struct iwl_mld *mld) +{ + u32 src_size = mld->fw->ucode_capa.error_log_size; + u32 src_addr = mld->fw->ucode_capa.error_log_addr; + u8 *recovery_buf; + int ret; + + /* no recovery buffer size defined in a TLV */ + if (!src_size) + return; + + recovery_buf = kzalloc(src_size, GFP_ATOMIC); + if (!recovery_buf) + return; + + ret = iwl_trans_read_mem_bytes(mld->trans, src_addr, + recovery_buf, src_size); + if (ret) { + IWL_ERR(mld, "Failed to read error recovery buffer (%d)\n", + ret); + kfree(recovery_buf); + return; + } + + mld->error_recovery_buf = recovery_buf; +} + +static void iwl_mld_restart_nic(struct iwl_mld *mld) +{ + iwl_mld_read_error_recovery_buffer(mld); + + mld->fwrt.trans->dbg.restart_required = false; + + ieee80211_restart_hw(mld->hw); +} + +static void +iwl_mld_nic_error(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + bool trans_dead = test_bit(STATUS_TRANS_DEAD, &mld->trans->status); + + if (type == IWL_ERR_TYPE_CMD_QUEUE_FULL) + IWL_ERR(mld, "Command queue full!\n"); + else if (!trans_dead && !mld->fw_status.do_not_dump_once) + iwl_fwrt_dump_error_logs(&mld->fwrt); + + mld->fw_status.do_not_dump_once = false; + + /* It is necessary to abort any os scan here because mac80211 requires + * having the scan cleared before restarting. + * We'll reset the scan_status to NONE in restart cleanup in + * the next drv_start() call from mac80211. If ieee80211_hw_restart + * isn't called scan status will stay busy. + */ + iwl_mld_report_scan_aborted(mld); + + /* + * This should be first thing before trying to collect any + * data to avoid endless loops if any HW error happens while + * collecting debug data. + * It might not actually be true that we'll restart, but the + * setting doesn't matter if we're going to be unbound either. + */ + if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT) + mld->fw_status.in_hw_restart = true; +} + +static void iwl_mld_dump_error(struct iwl_op_mode *op_mode, + struct iwl_fw_error_dump_mode *mode) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + /* if we come in from opmode we have the mutex held */ + if (mode->context == IWL_ERR_CONTEXT_FROM_OPMODE) { + lockdep_assert_wiphy(mld->wiphy); + iwl_fw_error_collect(&mld->fwrt); + } else { + wiphy_lock(mld->wiphy); + if (mode->context != IWL_ERR_CONTEXT_ABORT) + iwl_fw_error_collect(&mld->fwrt); + wiphy_unlock(mld->wiphy); + } +} + +static bool iwl_mld_sw_reset(struct iwl_op_mode *op_mode, + enum iwl_fw_error_type type) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + /* SW reset can happen for TOP error w/o NIC error, so + * also abort scan here and set in_hw_restart, when we + * had a NIC error both were already done. + */ + iwl_mld_report_scan_aborted(mld); + mld->fw_status.in_hw_restart = true; + + /* Do restart only in the following conditions are met: + * - we consider the FW as running + * - The trigger that brought us here is defined as one that requires + * a restart (in the debug TLVs) + */ + if (!mld->fw_status.running || !mld->fwrt.trans->dbg.restart_required) + return false; + + iwl_mld_restart_nic(mld); + return true; +} + +static void +iwl_mld_time_point(struct iwl_op_mode *op_mode, + enum iwl_fw_ini_time_point tp_id, + union iwl_dbg_tlv_tp_data *tp_data) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + iwl_dbg_tlv_time_point(&mld->fwrt, tp_id, tp_data); +} + +#ifdef CONFIG_PM_SLEEP +static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) +{ + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + + wiphy_lock(mld->wiphy); + iwl_mld_stop_fw(mld); + mld->fw_status.in_d3 = false; + wiphy_unlock(mld->wiphy); +} +#else +static void iwl_mld_device_powered_off(struct iwl_op_mode *op_mode) +{} +#endif + +static const struct iwl_op_mode_ops iwl_mld_ops = { + .start = iwl_op_mode_mld_start, + .stop = iwl_op_mode_mld_stop, + .rx = iwl_mld_rx, + .rx_rss = iwl_mld_rx_rss, + .queue_full = iwl_mld_queue_full, + .queue_not_full = iwl_mld_queue_not_full, + .hw_rf_kill = iwl_mld_set_hw_rfkill_state, + .free_skb = iwl_mld_free_skb, + .nic_error = iwl_mld_nic_error, + .dump_error = iwl_mld_dump_error, + .sw_reset = iwl_mld_sw_reset, + .time_point = iwl_mld_time_point, + .device_powered_off = pm_sleep_ptr(iwl_mld_device_powered_off), +}; + +struct iwl_mld_mod_params iwlmld_mod_params = { + .power_scheme = IWL_POWER_SCHEME_BPS, +}; + +module_param_named(power_scheme, iwlmld_mod_params.power_scheme, int, 0444); +MODULE_PARM_DESC(power_scheme, + "power management scheme: 1-active, 2-balanced, default: 2"); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mld.h b/drivers/net/wireless/intel/iwlwifi/mld/mld.h new file mode 100644 index 000000000000..1a2c44f44eff --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mld.h @@ -0,0 +1,586 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_h__ +#define __iwl_mld_h__ + +#include <linux/leds.h> +#include <net/mac80211.h> + +#include "iwl-trans.h" +#include "iwl-op-mode.h" +#include "fw/runtime.h" +#include "fw/notif-wait.h" +#include "fw/api/commands.h" +#include "fw/api/scan.h" +#include "fw/api/mac-cfg.h" +#include "fw/api/mac.h" +#include "fw/api/phy-ctxt.h" +#include "fw/api/datapath.h" +#include "fw/api/rx.h" +#include "fw/api/rs.h" +#include "fw/api/context.h" +#include "fw/api/coex.h" +#include "fw/api/location.h" + +#include "fw/dbg.h" + +#include "notif.h" +#include "scan.h" +#include "rx.h" +#include "thermal.h" +#include "low_latency.h" +#include "constants.h" +#include "ptp.h" +#include "time_sync.h" +#include "ftm-initiator.h" + +/** + * DOC: Introduction + * + * iwlmld is an operation mode (a.k.a. op_mode) for Intel wireless devices. + * It is used for devices that ship after 2024 which typically support + * the WiFi-7 features. MLD stands for multi-link device. Note that there are + * devices that do not support WiFi-7 or even WiFi 6E and yet use iwlmld, but + * the firmware APIs used in this driver are WiFi-7 compatible. + * + * In the architecture of iwlwifi, an op_mode is a layer that translates + * mac80211's APIs into commands for the firmware and, of course, notifications + * from the firmware to mac80211's APIs. An op_mode must implement the + * interface defined in iwl-op-mode.h to interact with the transport layer + * which allows to send and receive data to the device, start the hardware, + * etc... + */ + +/** + * DOC: Locking policy + * + * iwlmld has a very simple locking policy: it doesn't have any mutexes. It + * relies on cfg80211's wiphy->mtx and takes the lock when needed. All the + * control flows originating from mac80211 already acquired the lock, so that + * part is trivial, but also notifications that are received from the firmware + * and handled asynchronously are handled only after having taken the lock. + * This is described in notif.c. + * There are spin_locks needed to synchronize with the data path, around the + * allocation of the queues, for example. + */ + +/** + * DOC: Debugfs + * + * iwlmld adds its share of debugfs hooks and its handlers are synchronized + * with the wiphy_lock using wiphy_locked_debugfs. This avoids races against + * resources deletion while the debugfs hook is being used. + */ + +/** + * DOC: Main resources + * + * iwlmld is designed with the life cycle of the resource in mind. The + * resources are: + * + * - struct iwl_mld (matches mac80211's struct ieee80211_hw) + * + * - struct iwl_mld_vif (matches macu80211's struct ieee80211_vif) + * iwl_mld_vif contains an array of pointers to struct iwl_mld_link + * which describe the links for this vif. + * + * - struct iwl_mld_sta (matches mac80211's struct ieee80211_sta) + * iwl_mld_sta contains an array of points to struct iwl_mld_link_sta + * which describes the link stations for this station + * + * Each object has properties that can survive a firmware reset or not. + * Asynchronous firmware notifications can declare themselves as dependent on a + * certain instance of those resources and that means that the notifications + * will be cancelled once the instance is destroyed. + */ + +#define IWL_MLD_MAX_ADDRESSES 5 + +/** + * struct iwl_mld - MLD op mode + * + * @fw_id_to_bss_conf: maps a fw id of a link to the corresponding + * ieee80211_bss_conf. + * @fw_id_to_vif: maps a fw id of a MAC context to the corresponding + * ieee80211_vif. Mapping is valid only when the MAC exists in the fw. + * @fw_id_to_txq: maps a fw id of a txq to the corresponding + * ieee80211_txq. + * @used_phy_ids: a bitmap of the phy IDs used. If a bit is set, it means + * that the index of this bit is already used as a PHY id. + * @num_igtks: the number if iGTKs that were sent to the FW. + * @monitor: monitor related data + * @monitor.on: does a monitor vif exist (singleton hence bool) + * @monitor.ampdu_ref: the id of the A-MPDU for sniffer + * @monitor.ampdu_toggle: the state of the previous packet to track A-MPDU + * @monitor.cur_aid: current association id tracked by the sniffer + * @monitor.cur_bssid: current bssid tracked by the sniffer + * @monitor.ptp_time: set the Rx mactime using the device's PTP clock time + * @monitor.p80: primary channel position relative to he whole bandwidth, in + * steps of 80 MHz + * @fw_id_to_link_sta: maps a fw id of a sta to the corresponding + * ieee80211_link_sta. This is not cleaned up on restart since we want to + * preserve the fw sta ids during a restart (for SN/PN restoring). + * FW ids of internal stations will be mapped to ERR_PTR, and will be + * re-allocated during a restart, so make sure to free it in restart + * cleanup using iwl_mld_free_internal_sta + * @netdetect: indicates the FW is in suspend mode with netdetect configured + * @p2p_device_vif: points to the p2p device vif if exists + * @dev: pointer to device struct. For printing purposes + * @trans: pointer to the transport layer + * @cfg: pointer to the device configuration + * @fw: a pointer to the fw object + * @hw: pointer to the hw object. + * @wiphy: a pointer to the wiphy struct, for easier access to it. + * @nvm_data: pointer to the nvm_data that includes all our capabilities + * @fwrt: fw runtime data + * @debugfs_dir: debugfs directory + * @notif_wait: notification wait related data. + * @async_handlers_list: a list of all async RX handlers. When a notifciation + * with an async handler is received, it is added to this list. + * When &async_handlers_wk runs - it runs these handlers one by one. + * @async_handlers_lock: a lock for &async_handlers_list. Sync + * &async_handlers_wk and RX notifcation path. + * @async_handlers_wk: A work to run all async RX handlers from + * &async_handlers_list. + * @ct_kill_exit_wk: worker to exit thermal kill + * @fw_status: bitmap of fw status bits + * @running: true if the firmware is running + * @do_not_dump_once: true if firmware dump must be prevented once + * @in_d3: indicates FW is in suspend mode and should be resumed + * @in_hw_restart: indicates that we are currently in restart flow. + * rather than restarted. Should be unset upon restart. + * @radio_kill: bitmap of radio kill status + * @radio_kill.hw: radio is killed by hw switch + * @radio_kill.ct: radio is killed because the device it too hot + * @power_budget_mw: maximum cTDP power budget as defined for this system and + * device + * @addresses: device MAC addresses. + * @scan: instance of the scan object + * @wowlan: WoWLAN support data. + * @led: the led device + * @mcc_src: the source id of the MCC, comes from the firmware + * @bios_enable_puncturing: is puncturing enabled by bios + * @fw_id_to_ba: maps a fw (BA) id to a corresponding Block Ack session data. + * @num_rx_ba_sessions: tracks the number of active Rx Block Ack (BA) sessions. + * the driver ensures that new BA sessions are blocked once the maximum + * supported by the firmware is reached, preventing firmware asserts. + * @rxq_sync: manages RX queue sync state + * @txqs_to_add: a list of &ieee80211_txq's to allocate in &add_txqs_wk + * @add_txqs_wk: a worker to allocate txqs. + * @add_txqs_lock: to lock the &txqs_to_add list. + * @error_recovery_buf: pointer to the recovery buffer that will be read + * from firmware upon fw/hw error and sent back to the firmware in + * reconfig flow (after NIC reset). + * @mcast_filter_cmd: pointer to the multicast filter command. + * @mgmt_tx_ant: stores the last TX antenna index; used for setting + * TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure). + * @fw_rates_ver_3: FW rates are in version 3 + * @low_latency: low-latency manager. + * @tzone: thermal zone device's data + * @cooling_dev: cooling device's related data + * @ibss_manager: in IBSS mode (only one vif can be active), indicates what + * firmware indicated about having transmitted the last beacon, i.e. + * being IBSS manager for that time and needing to respond to probe + * requests + * @ptp_data: data of the PTP clock + * @time_sync: time sync data. + * @ftm_initiator: FTM initiator data + * @last_bt_notif: last received BT Coex notif + */ +struct iwl_mld { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + struct ieee80211_bss_conf __rcu *fw_id_to_bss_conf[IWL_FW_MAX_LINK_ID + 1]; + struct ieee80211_vif __rcu *fw_id_to_vif[NUM_MAC_INDEX_DRIVER]; + struct ieee80211_txq __rcu *fw_id_to_txq[IWL_MAX_TVQM_QUEUES]; + u8 used_phy_ids: NUM_PHY_CTX; + u8 num_igtks; + struct { + bool on; + u32 ampdu_ref; + bool ampdu_toggle; + u8 p80; +#ifdef CONFIG_IWLWIFI_DEBUGFS + __le16 cur_aid; + u8 cur_bssid[ETH_ALEN]; + bool ptp_time; +#endif + } monitor; +#ifdef CONFIG_PM_SLEEP + bool netdetect; +#endif /* CONFIG_PM_SLEEP */ + struct ieee80211_vif *p2p_device_vif; + struct iwl_bt_coex_profile_notif last_bt_notif; + ); + struct ieee80211_link_sta __rcu *fw_id_to_link_sta[IWL_STATION_COUNT_MAX]; + /* And here fields that survive a fw restart */ + struct device *dev; + struct iwl_trans *trans; + const struct iwl_rf_cfg *cfg; + const struct iwl_fw *fw; + struct ieee80211_hw *hw; + struct wiphy *wiphy; + struct iwl_nvm_data *nvm_data; + struct iwl_fw_runtime fwrt; + struct dentry *debugfs_dir; + struct iwl_notif_wait_data notif_wait; + struct list_head async_handlers_list; + spinlock_t async_handlers_lock; + struct wiphy_work async_handlers_wk; + struct wiphy_delayed_work ct_kill_exit_wk; + + struct { + u32 running:1, + do_not_dump_once:1, +#ifdef CONFIG_PM_SLEEP + in_d3:1, +#endif + in_hw_restart:1; + + } fw_status; + + struct { + u32 hw:1, + ct:1; + } radio_kill; + + u32 power_budget_mw; + + struct mac_address addresses[IWL_MLD_MAX_ADDRESSES]; + struct iwl_mld_scan scan; +#ifdef CONFIG_PM_SLEEP + struct wiphy_wowlan_support wowlan; +#endif /* CONFIG_PM_SLEEP */ +#ifdef CONFIG_IWLWIFI_LEDS + struct led_classdev led; +#endif + enum iwl_mcc_source mcc_src; + bool bios_enable_puncturing; + + struct iwl_mld_baid_data __rcu *fw_id_to_ba[IWL_MAX_BAID]; + u8 num_rx_ba_sessions; + + struct iwl_mld_rx_queues_sync rxq_sync; + + struct list_head txqs_to_add; + struct wiphy_work add_txqs_wk; + spinlock_t add_txqs_lock; + + u8 *error_recovery_buf; + struct iwl_mcast_filter_cmd *mcast_filter_cmd; + + u8 mgmt_tx_ant; + + bool fw_rates_ver_3; + + struct iwl_mld_low_latency low_latency; + + bool ibss_manager; +#ifdef CONFIG_THERMAL + struct thermal_zone_device *tzone; + struct iwl_mld_cooling_device cooling_dev; +#endif + + struct ptp_data ptp_data; + + struct iwl_mld_time_sync_data __rcu *time_sync; + + struct ftm_initiator_data ftm_initiator; +}; + +/* memset the part of the struct that requires cleanup on restart */ +#define CLEANUP_STRUCT(_ptr) \ + memset((void *)&(_ptr)->zeroed_on_hw_restart, 0, \ + sizeof((_ptr)->zeroed_on_hw_restart)) + +/* Cleanup function for struct iwl_mld, will be called in restart */ +static inline void +iwl_cleanup_mld(struct iwl_mld *mld) +{ + CLEANUP_STRUCT(mld); + CLEANUP_STRUCT(&mld->scan); + +#ifdef CONFIG_PM_SLEEP + mld->fw_status.in_d3 = false; +#endif + + iwl_mld_low_latency_restart_cleanup(mld); +} + +enum iwl_power_scheme { + IWL_POWER_SCHEME_CAM = 1, + IWL_POWER_SCHEME_BPS, +}; + +/** + * struct iwl_mld_mod_params - module parameters for iwlmld + * @power_scheme: one of enum iwl_power_scheme + */ +struct iwl_mld_mod_params { + int power_scheme; +}; + +extern struct iwl_mld_mod_params iwlmld_mod_params; + +/* Extract MLD priv from op_mode */ +#define IWL_OP_MODE_GET_MLD(_iwl_op_mode) \ + ((struct iwl_mld *)(_iwl_op_mode)->op_mode_specific) + +#define IWL_MAC80211_GET_MLD(_hw) \ + IWL_OP_MODE_GET_MLD((struct iwl_op_mode *)((_hw)->priv)) + +#ifdef CONFIG_IWLWIFI_DEBUGFS +void +iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir); +#else +static inline void +iwl_mld_add_debugfs_files(struct iwl_mld *mld, struct dentry *debugfs_dir) +{} +#endif + +int iwl_mld_load_fw(struct iwl_mld *mld); +void iwl_mld_stop_fw(struct iwl_mld *mld); +int iwl_mld_start_fw(struct iwl_mld *mld); +void iwl_mld_send_recovery_cmd(struct iwl_mld *mld, u32 flags); + +static inline void iwl_mld_set_ctkill(struct iwl_mld *mld, bool state) +{ + mld->radio_kill.ct = state; + + wiphy_rfkill_set_hw_state(mld->wiphy, + mld->radio_kill.hw || mld->radio_kill.ct); +} + +static inline void iwl_mld_set_hwkill(struct iwl_mld *mld, bool state) +{ + mld->radio_kill.hw = state; + + wiphy_rfkill_set_hw_state(mld->wiphy, + mld->radio_kill.hw || mld->radio_kill.ct); +} + +static inline u8 iwl_mld_get_valid_tx_ant(const struct iwl_mld *mld) +{ + u8 tx_ant = mld->fw->valid_tx_ant; + + if (mld->nvm_data && mld->nvm_data->valid_tx_ant) + tx_ant &= mld->nvm_data->valid_tx_ant; + + return tx_ant; +} + +static inline u8 iwl_mld_get_valid_rx_ant(const struct iwl_mld *mld) +{ + u8 rx_ant = mld->fw->valid_rx_ant; + + if (mld->nvm_data && mld->nvm_data->valid_rx_ant) + rx_ant &= mld->nvm_data->valid_rx_ant; + + return rx_ant; +} + +static inline u8 iwl_mld_nl80211_band_to_fw(enum nl80211_band band) +{ + switch (band) { + case NL80211_BAND_2GHZ: + return PHY_BAND_24; + case NL80211_BAND_5GHZ: + return PHY_BAND_5; + case NL80211_BAND_6GHZ: + return PHY_BAND_6; + default: + WARN_ONCE(1, "Unsupported band (%u)\n", band); + return PHY_BAND_5; + } +} + +static inline u8 iwl_mld_phy_band_to_nl80211(u8 phy_band) +{ + switch (phy_band) { + case PHY_BAND_24: + return NL80211_BAND_2GHZ; + case PHY_BAND_5: + return NL80211_BAND_5GHZ; + case PHY_BAND_6: + return NL80211_BAND_6GHZ; + default: + WARN_ONCE(1, "Unsupported phy band (%u)\n", phy_band); + return NL80211_BAND_5GHZ; + } +} + +static inline int +iwl_mld_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, + enum nl80211_band band) +{ + int format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; + bool is_lb = band == NL80211_BAND_2GHZ; + + if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) + return is_lb ? rate + IWL_FIRST_OFDM_RATE : rate; + + /* CCK is not allowed in 5 GHz */ + return is_lb ? rate : -1; +} + +extern const struct ieee80211_ops iwl_mld_hw_ops; + +/** + * enum iwl_rx_handler_context: context for Rx handler + * @RX_HANDLER_SYNC: this means that it will be called in the Rx path + * which can't acquire the wiphy->mutex. + * @RX_HANDLER_ASYNC: If the handler needs to hold wiphy->mutex + * (and only in this case!), it should be set as ASYNC. In that case, + * it will be called from a worker with wiphy->mutex held. + */ +enum iwl_rx_handler_context { + RX_HANDLER_SYNC, + RX_HANDLER_ASYNC, +}; + +/** + * struct iwl_rx_handler: handler for FW notification + * @val_fn: input validation function. + * @sizes: an array that mapps a version to the expected size. + * @fn: the function is called when notification is handled + * @cmd_id: command id + * @n_sizes: number of elements in &sizes. + * @context: see &iwl_rx_handler_context + * @obj_type: the type of the object that this handler is related to. + * See &iwl_mld_object_type. Use IWL_MLD_OBJECT_TYPE_NONE if not related. + * @cancel: function to cancel the notification. valid only if obj_type is not + * IWL_MLD_OBJECT_TYPE_NONE. + */ +struct iwl_rx_handler { + union { + bool (*val_fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt); + const struct iwl_notif_struct_size *sizes; + }; + void (*fn)(struct iwl_mld *mld, struct iwl_rx_packet *pkt); + u16 cmd_id; + u8 n_sizes; + u8 context; + enum iwl_mld_object_type obj_type; + bool (*cancel)(struct iwl_mld *mld, struct iwl_rx_packet *pkt, + u32 obj_id); +}; + +/** + * struct iwl_notif_struct_size: map a notif ver to the expected size + * + * @size: the size to expect + * @ver: the version of the notification + */ +struct iwl_notif_struct_size { + u32 size:24, ver:8; +}; + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +extern const struct iwl_hcmd_arr iwl_mld_groups[]; +extern const unsigned int global_iwl_mld_goups_size; +extern const struct iwl_rx_handler iwl_mld_rx_handlers[]; +extern const unsigned int iwl_mld_rx_handlers_num; + +bool +iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + const struct iwl_rx_mpdu_desc *mpdu_desc, + struct ieee80211_rx_status *rx_status, int queue); + +void iwl_construct_mld(struct iwl_mld *mld, struct iwl_trans *trans, + const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, + struct ieee80211_hw *hw, struct dentry *dbgfs_dir); +#endif + +#define IWL_MLD_INVALID_FW_ID 0xff + +#define IWL_MLD_ALLOC_FN(_type, _mac80211_type) \ +static int \ +iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \ + u8 *fw_id, \ + struct ieee80211_##_mac80211_type *mac80211_ptr) \ +{ \ + u8 rand = IWL_MLD_DIS_RANDOM_FW_ID ? 0 : get_random_u8(); \ + u8 arr_sz = ARRAY_SIZE(mld->fw_id_to_##_mac80211_type); \ + if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \ + struct ieee80211_link_sta)) \ + arr_sz = mld->fw->ucode_capa.num_stations; \ + if (__builtin_types_compatible_p(typeof(*mac80211_ptr), \ + struct ieee80211_bss_conf)) \ + arr_sz = mld->fw->ucode_capa.num_links; \ + for (int i = 0; i < arr_sz; i++) { \ + u8 idx = (i + rand) % arr_sz; \ + if (rcu_access_pointer(mld->fw_id_to_##_mac80211_type[idx])) \ + continue; \ + IWL_DEBUG_INFO(mld, "Allocated at index %d / %d\n", idx, arr_sz); \ + *fw_id = idx; \ + rcu_assign_pointer(mld->fw_id_to_##_mac80211_type[idx], mac80211_ptr); \ + return 0; \ + } \ + return -ENOSPC; \ +} + +static inline struct ieee80211_bss_conf * +iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id) +{ + if (IWL_FW_CHECK(mld, fw_link_id >= mld->fw->ucode_capa.num_links, + "Invalid fw_link_id: %d\n", fw_link_id)) + return NULL; + + return wiphy_dereference(mld->wiphy, + mld->fw_id_to_bss_conf[fw_link_id]); +} + +#define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024) + +void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); +void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct dentry *dir); +void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct dentry *dir); + +/* Utilities */ + +static inline u8 iwl_mld_mac80211_ac_to_fw_tx_fifo(enum ieee80211_ac_numbers ac) +{ + static const u8 mac80211_ac_to_fw_tx_fifo[] = { + IWL_BZ_EDCA_TX_FIFO_VO, + IWL_BZ_EDCA_TX_FIFO_VI, + IWL_BZ_EDCA_TX_FIFO_BE, + IWL_BZ_EDCA_TX_FIFO_BK, + IWL_BZ_TRIG_TX_FIFO_VO, + IWL_BZ_TRIG_TX_FIFO_VI, + IWL_BZ_TRIG_TX_FIFO_BE, + IWL_BZ_TRIG_TX_FIFO_BK, + }; + return mac80211_ac_to_fw_tx_fifo[ac]; +} + +static inline u32 +iwl_mld_get_lmac_id(struct iwl_mld *mld, enum nl80211_band band) +{ + if (!fw_has_capa(&mld->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CDB_SUPPORT) || + band == NL80211_BAND_2GHZ) + return IWL_LMAC_24G_INDEX; + return IWL_LMAC_5G_INDEX; +} + +/* Check if we had an error, but reconfig flow didn't start yet */ +static inline bool iwl_mld_error_before_recovery(struct iwl_mld *mld) +{ + return mld->fw_status.in_hw_restart && + !iwl_trans_fw_running(mld->trans); +} + +int iwl_mld_tdls_sta_count(struct iwl_mld *mld); + +#endif /* __iwl_mld_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.c b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c new file mode 100644 index 000000000000..dba5379ed009 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.c @@ -0,0 +1,1241 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include "mlo.h" +#include "phy.h" + +/* Block reasons helper */ +#define HANDLE_EMLSR_BLOCKED_REASONS(HOW) \ + HOW(PREVENTION) \ + HOW(WOWLAN) \ + HOW(ROC) \ + HOW(NON_BSS) \ + HOW(TMP_NON_BSS) \ + HOW(TPT) + +static const char * +iwl_mld_get_emlsr_blocked_string(enum iwl_mld_emlsr_blocked blocked) +{ + /* Using switch without "default" will warn about missing entries */ + switch (blocked) { +#define REASON_CASE(x) case IWL_MLD_EMLSR_BLOCKED_##x: return #x; + HANDLE_EMLSR_BLOCKED_REASONS(REASON_CASE) +#undef REASON_CASE + } + + return "ERROR"; +} + +static void iwl_mld_print_emlsr_blocked(struct iwl_mld *mld, u32 mask) +{ +#define NAME_FMT(x) "%s" +#define NAME_PR(x) (mask & IWL_MLD_EMLSR_BLOCKED_##x) ? "[" #x "]" : "", + IWL_DEBUG_INFO(mld, + "EMLSR blocked = " HANDLE_EMLSR_BLOCKED_REASONS(NAME_FMT) + " (0x%x)\n", + HANDLE_EMLSR_BLOCKED_REASONS(NAME_PR) + mask); +#undef NAME_FMT +#undef NAME_PR +} + +/* Exit reasons helper */ +#define HANDLE_EMLSR_EXIT_REASONS(HOW) \ + HOW(BLOCK) \ + HOW(MISSED_BEACON) \ + HOW(FAIL_ENTRY) \ + HOW(CSA) \ + HOW(EQUAL_BAND) \ + HOW(LOW_RSSI) \ + HOW(LINK_USAGE) \ + HOW(BT_COEX) \ + HOW(CHAN_LOAD) \ + HOW(RFI) \ + HOW(FW_REQUEST) \ + HOW(INVALID) + +static const char * +iwl_mld_get_emlsr_exit_string(enum iwl_mld_emlsr_exit exit) +{ + /* Using switch without "default" will warn about missing entries */ + switch (exit) { +#define REASON_CASE(x) case IWL_MLD_EMLSR_EXIT_##x: return #x; + HANDLE_EMLSR_EXIT_REASONS(REASON_CASE) +#undef REASON_CASE + } + + return "ERROR"; +} + +static void iwl_mld_print_emlsr_exit(struct iwl_mld *mld, u32 mask) +{ +#define NAME_FMT(x) "%s" +#define NAME_PR(x) (mask & IWL_MLD_EMLSR_EXIT_##x) ? "[" #x "]" : "", + IWL_DEBUG_INFO(mld, + "EMLSR exit = " HANDLE_EMLSR_EXIT_REASONS(NAME_FMT) + " (0x%x)\n", + HANDLE_EMLSR_EXIT_REASONS(NAME_PR) + mask); +#undef NAME_FMT +#undef NAME_PR +} + +void iwl_mld_emlsr_prevent_done_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + emlsr.prevent_done_wk.work); + struct ieee80211_vif *vif = + container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); + + if (WARN_ON(!(mld_vif->emlsr.blocked_reasons & + IWL_MLD_EMLSR_BLOCKED_PREVENTION))) + return; + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_PREVENTION); +} + +void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy, + struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + emlsr.tmp_non_bss_done_wk.work); + struct ieee80211_vif *vif = + container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); + + if (WARN_ON(!(mld_vif->emlsr.blocked_reasons & + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS))) + return; + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_TMP_NON_BSS); +} + +#define IWL_MLD_TRIGGER_LINK_SEL_TIME (HZ * IWL_MLD_TRIGGER_LINK_SEL_TIME_SEC) +#define IWL_MLD_SCAN_EXPIRE_TIME (HZ * IWL_MLD_SCAN_EXPIRE_TIME_SEC) + +/* Exit reasons that can cause longer EMLSR prevention */ +#define IWL_MLD_PREVENT_EMLSR_REASONS (IWL_MLD_EMLSR_EXIT_MISSED_BEACON | \ + IWL_MLD_EMLSR_EXIT_LINK_USAGE | \ + IWL_MLD_EMLSR_EXIT_FW_REQUEST) +#define IWL_MLD_PREVENT_EMLSR_TIMEOUT (HZ * 400) + +#define IWL_MLD_EMLSR_PREVENT_SHORT (HZ * 300) +#define IWL_MLD_EMLSR_PREVENT_LONG (HZ * 600) + +static void iwl_mld_check_emlsr_prevention(struct iwl_mld *mld, + struct iwl_mld_vif *mld_vif, + enum iwl_mld_emlsr_exit reason) +{ + unsigned long delay; + + /* + * Reset the counter if more than 400 seconds have passed between one + * exit and the other, or if we exited due to a different reason. + * Will also reset the counter after the long prevention is done. + */ + if (time_after(jiffies, mld_vif->emlsr.last_exit_ts + + IWL_MLD_PREVENT_EMLSR_TIMEOUT) || + mld_vif->emlsr.last_exit_reason != reason) + mld_vif->emlsr.exit_repeat_count = 0; + + mld_vif->emlsr.last_exit_reason = reason; + mld_vif->emlsr.last_exit_ts = jiffies; + mld_vif->emlsr.exit_repeat_count++; + + /* + * Do not add a prevention when the reason was a block. For a block, + * EMLSR will be enabled again on unblock. + */ + if (reason == IWL_MLD_EMLSR_EXIT_BLOCK) + return; + + /* Set prevention for a minimum of 30 seconds */ + mld_vif->emlsr.blocked_reasons |= IWL_MLD_EMLSR_BLOCKED_PREVENTION; + delay = IWL_MLD_TRIGGER_LINK_SEL_TIME; + + /* Handle repeats for reasons that can cause long prevention */ + if (mld_vif->emlsr.exit_repeat_count > 1 && + reason & IWL_MLD_PREVENT_EMLSR_REASONS) { + if (mld_vif->emlsr.exit_repeat_count == 2) + delay = IWL_MLD_EMLSR_PREVENT_SHORT; + else + delay = IWL_MLD_EMLSR_PREVENT_LONG; + + /* + * The timeouts are chosen so that this will not happen, i.e. + * IWL_MLD_EMLSR_PREVENT_LONG > IWL_MLD_PREVENT_EMLSR_TIMEOUT + */ + WARN_ON(mld_vif->emlsr.exit_repeat_count > 3); + } + + IWL_DEBUG_INFO(mld, + "Preventing EMLSR for %ld seconds due to %u exits with the reason = %s (0x%x)\n", + delay / HZ, mld_vif->emlsr.exit_repeat_count, + iwl_mld_get_emlsr_exit_string(reason), reason); + + wiphy_delayed_work_queue(mld->wiphy, + &mld_vif->emlsr.prevent_done_wk, delay); +} + +static void iwl_mld_clear_avg_chan_load_iter(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + void *dat) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + + /* It is ok to do it for all chanctx (and not only for the ones that + * belong to the EMLSR vif) since EMLSR is not allowed if there is + * another vif. + */ + phy->avg_channel_load_not_by_us = 0; +} + +static int _iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_exit exit, u8 link_to_keep, + bool sync) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + u16 new_active_links; + int ret = 0; + + lockdep_assert_wiphy(mld->wiphy); + + /* On entry failure need to exit anyway, even if entered from debugfs */ + if (exit != IWL_MLD_EMLSR_EXIT_FAIL_ENTRY && !IWL_MLD_AUTO_EML_ENABLE) + return 0; + + /* Ignore exit request if EMLSR is not active */ + if (!iwl_mld_emlsr_active(vif)) + return 0; + + if (WARN_ON(!ieee80211_vif_is_mld(vif) || !mld_vif->authorized)) + return 0; + + if (WARN_ON(!(vif->active_links & BIT(link_to_keep)))) + link_to_keep = __ffs(vif->active_links); + + new_active_links = BIT(link_to_keep); + IWL_DEBUG_INFO(mld, + "Exiting EMLSR. reason = %s (0x%x). Current active links=0x%x, new active links = 0x%x\n", + iwl_mld_get_emlsr_exit_string(exit), exit, + vif->active_links, new_active_links); + + if (sync) + ret = ieee80211_set_active_links(vif, new_active_links); + else + ieee80211_set_active_links_async(vif, new_active_links); + + /* Update latest exit reason and check EMLSR prevention */ + iwl_mld_check_emlsr_prevention(mld, mld_vif, exit); + + /* channel_load_not_by_us is invalid when in EMLSR. + * Clear it so wrong values won't be used. + */ + ieee80211_iter_chan_contexts_atomic(mld->hw, + iwl_mld_clear_avg_chan_load_iter, + NULL); + + return ret; +} + +void iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_exit exit, u8 link_to_keep) +{ + _iwl_mld_exit_emlsr(mld, vif, exit, link_to_keep, false); +} + +static int _iwl_mld_emlsr_block(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, + u8 link_to_keep, bool sync) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld->wiphy); + + if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif)) + return 0; + + if (mld_vif->emlsr.blocked_reasons & reason) + return 0; + + mld_vif->emlsr.blocked_reasons |= reason; + + IWL_DEBUG_INFO(mld, + "Blocking EMLSR mode. reason = %s (0x%x)\n", + iwl_mld_get_emlsr_blocked_string(reason), reason); + iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons); + + if (reason == IWL_MLD_EMLSR_BLOCKED_TPT) + wiphy_delayed_work_cancel(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk); + + return _iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_BLOCK, + link_to_keep, sync); +} + +void iwl_mld_block_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, u8 link_to_keep) +{ + _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, false); +} + +int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, u8 link_to_keep) +{ + return _iwl_mld_emlsr_block(mld, vif, reason, link_to_keep, true); +} + +static void _iwl_mld_select_links(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld->wiphy); + + if (!IWL_MLD_AUTO_EML_ENABLE || !iwl_mld_vif_has_emlsr_cap(vif)) + return; + + if (!(mld_vif->emlsr.blocked_reasons & reason)) + return; + + mld_vif->emlsr.blocked_reasons &= ~reason; + + IWL_DEBUG_INFO(mld, + "Unblocking EMLSR mode. reason = %s (0x%x)\n", + iwl_mld_get_emlsr_blocked_string(reason), reason); + iwl_mld_print_emlsr_blocked(mld, mld_vif->emlsr.blocked_reasons); + + if (reason == IWL_MLD_EMLSR_BLOCKED_TPT) + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk, + round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW)); + + if (mld_vif->emlsr.blocked_reasons) + return; + + IWL_DEBUG_INFO(mld, "EMLSR is unblocked\n"); + iwl_mld_int_mlo_scan(mld, vif); +} + +static void +iwl_mld_vif_iter_emlsr_mode_notif(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + const struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + enum iwl_mvm_fw_esr_recommendation action; + const struct iwl_esr_mode_notif *notif = NULL; + + if (iwl_fw_lookup_notif_ver(mld_vif->mld->fw, DATA_PATH_GROUP, + ESR_MODE_NOTIF, 0) > 1) { + notif = (void *)data; + action = le32_to_cpu(notif->action); + } else { + const struct iwl_esr_mode_notif_v1 *notif_v1 = (void *)data; + + action = le32_to_cpu(notif_v1->action); + } + + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + switch (action) { + case ESR_RECOMMEND_LEAVE: + if (notif) + IWL_DEBUG_INFO(mld_vif->mld, + "FW recommend leave reason = 0x%x\n", + le32_to_cpu(notif->leave_reason_mask)); + + iwl_mld_exit_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_EXIT_FW_REQUEST, + iwl_mld_get_primary_link(vif)); + break; + case ESR_FORCE_LEAVE: + if (notif) + IWL_DEBUG_INFO(mld_vif->mld, + "FW force leave reason = 0x%x\n", + le32_to_cpu(notif->leave_reason_mask)); + fallthrough; + case ESR_RECOMMEND_ENTER: + default: + IWL_WARN(mld_vif->mld, "Unexpected EMLSR notification: %d\n", + action); + } +} + +void iwl_mld_handle_emlsr_mode_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_mode_notif, + pkt->data); +} + +static void +iwl_mld_vif_iter_disconnect_emlsr(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + ieee80211_connection_loss(vif); +} + +void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_esr_trans_fail_notif *notif = (const void *)pkt->data; + u32 fw_link_id = le32_to_cpu(notif->link_id); + struct ieee80211_bss_conf *bss_conf = + iwl_mld_fw_id_to_link_conf(mld, fw_link_id); + + IWL_DEBUG_INFO(mld, "Failed to %s EMLSR on link %d (FW: %d), reason %d\n", + le32_to_cpu(notif->activation) ? "enter" : "exit", + bss_conf ? bss_conf->link_id : -1, + le32_to_cpu(notif->link_id), + le32_to_cpu(notif->err_code)); + + if (IWL_FW_CHECK(mld, !bss_conf, + "FW reported failure to %sactivate EMLSR on a non-existing link: %d\n", + le32_to_cpu(notif->activation) ? "" : "de", + fw_link_id)) { + ieee80211_iterate_active_interfaces_mtx( + mld->hw, IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_disconnect_emlsr, NULL); + return; + } + + /* Disconnect if we failed to deactivate a link */ + if (!le32_to_cpu(notif->activation)) { + ieee80211_connection_loss(bss_conf->vif); + return; + } + + /* + * We failed to activate the second link, go back to the link specified + * by the firmware as that is the one that is still valid now. + */ + iwl_mld_exit_emlsr(mld, bss_conf->vif, IWL_MLD_EMLSR_EXIT_FAIL_ENTRY, + bss_conf->link_id); +} + +/* Active non-station link tracking */ +static void iwl_mld_count_non_bss_links(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int *count = _data; + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) + return; + + *count += iwl_mld_count_active_links(mld_vif->mld, vif); +} + +struct iwl_mld_update_emlsr_block_data { + bool block; + int result; +}; + +static void +iwl_mld_vif_iter_update_emlsr_non_bss_block(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_update_emlsr_block_data *data = _data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + if (data->block) { + ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_NON_BSS, + iwl_mld_get_primary_link(vif)); + if (ret) + data->result = ret; + } else { + iwl_mld_unblock_emlsr(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_NON_BSS); + } +} + +int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld, + int pending_link_changes) +{ + /* An active link of a non-station vif blocks EMLSR. Upon activation + * block EMLSR on the bss vif. Upon deactivation, check if this link + * was the last non-station link active, and if so unblock the bss vif + */ + struct iwl_mld_update_emlsr_block_data block_data = {}; + int count = pending_link_changes; + + /* No need to count if we are activating a non-BSS link */ + if (count <= 0) + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_count_non_bss_links, + &count); + + /* + * We could skip updating it if the block change did not change (and + * pending_link_changes is non-zero). + */ + block_data.block = !!count; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_update_emlsr_non_bss_block, + &block_data); + + return block_data.result; +} + +#define EMLSR_SEC_LINK_MIN_PERC 10 +#define EMLSR_MIN_TX 3000 +#define EMLSR_MIN_RX 400 + +void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + emlsr.check_tpt_wk.work); + struct ieee80211_vif *vif = + container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); + struct iwl_mld *mld = mld_vif->mld; + struct iwl_mld_sta *mld_sta; + struct iwl_mld_link *sec_link; + unsigned long total_tx = 0, total_rx = 0; + unsigned long sec_link_tx = 0, sec_link_rx = 0; + u8 sec_link_tx_perc, sec_link_rx_perc; + s8 sec_link_id; + + if (!iwl_mld_vif_has_emlsr_cap(vif) || !mld_vif->ap_sta) + return; + + mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta); + + /* We only count for the AP sta in a MLO connection */ + if (!mld_sta->mpdu_counters) + return; + + /* This wk should only run when the TPT blocker isn't set. + * When the blocker is set, the decision to remove it, as well as + * clearing the counters is done in DP (to avoid having a wk every + * 5 seconds when idle. When the blocker is unset, we are not idle anyway) + */ + if (WARN_ON(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT)) + return; + /* + * TPT is unblocked, need to check if the TPT criteria is still met. + * + * If EMLSR is active, then we also need to check the secondar link + * requirements. + */ + if (iwl_mld_emlsr_active(vif)) { + sec_link_id = iwl_mld_get_other_link(vif, iwl_mld_get_primary_link(vif)); + sec_link = iwl_mld_link_dereference_check(mld_vif, sec_link_id); + if (WARN_ON_ONCE(!sec_link)) + return; + /* We need the FW ID here */ + sec_link_id = sec_link->fw_id; + } else { + sec_link_id = -1; + } + + /* Sum up RX and TX MPDUs from the different queues/links */ + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { + struct iwl_mld_per_q_mpdu_counter *queue_counter = + &mld_sta->mpdu_counters[q]; + + spin_lock_bh(&queue_counter->lock); + + /* The link IDs that doesn't exist will contain 0 */ + for (int link = 0; + link < ARRAY_SIZE(queue_counter->per_link); + link++) { + total_tx += queue_counter->per_link[link].tx; + total_rx += queue_counter->per_link[link].rx; + } + + if (sec_link_id != -1) { + sec_link_tx += queue_counter->per_link[sec_link_id].tx; + sec_link_rx += queue_counter->per_link[sec_link_id].rx; + } + + memset(queue_counter->per_link, 0, + sizeof(queue_counter->per_link)); + + spin_unlock_bh(&queue_counter->lock); + } + + IWL_DEBUG_INFO(mld, "total Tx MPDUs: %ld. total Rx MPDUs: %ld\n", + total_tx, total_rx); + + /* If we don't have enough MPDUs - exit EMLSR */ + if (total_tx < IWL_MLD_ENTER_EMLSR_TPT_THRESH && + total_rx < IWL_MLD_ENTER_EMLSR_TPT_THRESH) { + iwl_mld_block_emlsr(mld, vif, IWL_MLD_EMLSR_BLOCKED_TPT, + iwl_mld_get_primary_link(vif)); + return; + } + + /* EMLSR is not active */ + if (sec_link_id == -1) + return; + + IWL_DEBUG_INFO(mld, "Secondary Link %d: Tx MPDUs: %ld. Rx MPDUs: %ld\n", + sec_link_id, sec_link_tx, sec_link_rx); + + /* Calculate the percentage of the secondary link TX/RX */ + sec_link_tx_perc = total_tx ? sec_link_tx * 100 / total_tx : 0; + sec_link_rx_perc = total_rx ? sec_link_rx * 100 / total_rx : 0; + + /* + * The TX/RX percentage is checked only if it exceeds the required + * minimum. In addition, RX is checked only if the TX check failed. + */ + if ((total_tx > EMLSR_MIN_TX && + sec_link_tx_perc < EMLSR_SEC_LINK_MIN_PERC) || + (total_rx > EMLSR_MIN_RX && + sec_link_rx_perc < EMLSR_SEC_LINK_MIN_PERC)) { + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LINK_USAGE, + iwl_mld_get_primary_link(vif)); + return; + } + + /* Check again when the next window ends */ + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk, + round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW)); +} + +void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld_vif *mld_vif = container_of(wk, struct iwl_mld_vif, + emlsr.unblock_tpt_wk); + struct ieee80211_vif *vif = + container_of((void *)mld_vif, struct ieee80211_vif, drv_priv); + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_TPT); +} + +/* + * Link selection + */ + +s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld, + const struct cfg80211_chan_def *chandef, + bool low) +{ + if (WARN_ON(chandef->chan->band != NL80211_BAND_2GHZ && + chandef->chan->band != NL80211_BAND_5GHZ && + chandef->chan->band != NL80211_BAND_6GHZ)) + return S8_MAX; + +#define RSSI_THRESHOLD(_low, _bw) \ + (_low) ? IWL_MLD_LOW_RSSI_THRESH_##_bw##MHZ \ + : IWL_MLD_HIGH_RSSI_THRESH_##_bw##MHZ + + switch (chandef->width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + /* 320 MHz has the same thresholds as 20 MHz */ + case NL80211_CHAN_WIDTH_320: + return RSSI_THRESHOLD(low, 20); + case NL80211_CHAN_WIDTH_40: + return RSSI_THRESHOLD(low, 40); + case NL80211_CHAN_WIDTH_80: + return RSSI_THRESHOLD(low, 80); + case NL80211_CHAN_WIDTH_160: + return RSSI_THRESHOLD(low, 160); + default: + WARN_ON(1); + return S8_MAX; + } +#undef RSSI_THRESHOLD +} + +#define IWL_MLD_BT_COEX_DISABLE_EMLSR_RSSI_THRESH -69 +#define IWL_MLD_BT_COEX_ENABLE_EMLSR_RSSI_THRESH -63 +#define IWL_MLD_BT_COEX_WIFI_LOSS_THRESH 7 + +VISIBLE_IF_IWLWIFI_KUNIT +bool +iwl_mld_bt_allows_emlsr(struct iwl_mld *mld, struct ieee80211_bss_conf *link, + bool check_entry) +{ + int bt_penalty, rssi_thresh; + s32 link_rssi; + + if (WARN_ON_ONCE(!link->bss)) + return false; + + link_rssi = MBM_TO_DBM(link->bss->signal); + rssi_thresh = check_entry ? + IWL_MLD_BT_COEX_ENABLE_EMLSR_RSSI_THRESH : + IWL_MLD_BT_COEX_DISABLE_EMLSR_RSSI_THRESH; + /* No valid RSSI - force to take low rssi */ + if (!link_rssi) + link_rssi = rssi_thresh - 1; + + if (link_rssi > rssi_thresh) + bt_penalty = max(mld->last_bt_notif.wifi_loss_mid_high_rssi[PHY_BAND_24][0], + mld->last_bt_notif.wifi_loss_mid_high_rssi[PHY_BAND_24][1]); + else + bt_penalty = max(mld->last_bt_notif.wifi_loss_low_rssi[PHY_BAND_24][0], + mld->last_bt_notif.wifi_loss_low_rssi[PHY_BAND_24][1]); + + IWL_DEBUG_EHT(mld, "BT penalty for link-id %0X is %d\n", + link->link_id, bt_penalty); + return bt_penalty < IWL_MLD_BT_COEX_WIFI_LOSS_THRESH; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_bt_allows_emlsr); + +static u32 +iwl_mld_emlsr_disallowed_with_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *link, + bool primary) +{ + struct wiphy *wiphy = mld->wiphy; + struct ieee80211_bss_conf *conf; + u32 ret = 0; + + conf = wiphy_dereference(wiphy, vif->link_conf[link->link_id]); + if (WARN_ON_ONCE(!conf)) + return IWL_MLD_EMLSR_EXIT_INVALID; + + if (link->chandef->chan->band == NL80211_BAND_2GHZ && + !iwl_mld_bt_allows_emlsr(mld, conf, true)) + ret |= IWL_MLD_EMLSR_EXIT_BT_COEX; + + if (link->signal < + iwl_mld_get_emlsr_rssi_thresh(mld, link->chandef, false)) + ret |= IWL_MLD_EMLSR_EXIT_LOW_RSSI; + + if (conf->csa_active) + ret |= IWL_MLD_EMLSR_EXIT_CSA; + + if (ret) { + IWL_DEBUG_INFO(mld, + "Link %d is not allowed for EMLSR as %s\n", + link->link_id, + primary ? "primary" : "secondary"); + iwl_mld_print_emlsr_exit(mld, ret); + } + + return ret; +} + +static u8 +iwl_mld_set_link_sel_data(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *data, + unsigned long usable_links, + u8 *best_link_idx) +{ + u8 n_data = 0; + u16 max_grade = 0; + unsigned long link_id; + + /* + * TODO: don't select links that weren't discovered in the last scan + * This requires mac80211 (or cfg80211) changes to forward/track when + * a BSS was last updated. cfg80211 already tracks this information but + * it is not exposed within the kernel. + */ + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_protected(vif, link_id); + + if (WARN_ON_ONCE(!link_conf)) + continue; + + /* Ignore any BSS that was not seen in the last MLO scan */ + if (ktime_before(link_conf->bss->ts_boottime, + mld->scan.last_mlo_scan_time)) + continue; + + data[n_data].link_id = link_id; + data[n_data].chandef = &link_conf->chanreq.oper; + data[n_data].signal = MBM_TO_DBM(link_conf->bss->signal); + data[n_data].grade = iwl_mld_get_link_grade(mld, link_conf); + + if (n_data == 0 || data[n_data].grade > max_grade) { + max_grade = data[n_data].grade; + *best_link_idx = n_data; + } + n_data++; + } + + return n_data; +} + +static u32 +iwl_mld_get_min_chan_load_thresh(struct ieee80211_chanctx_conf *chanctx) +{ + const struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(chanctx); + + switch (phy->chandef.width) { + case NL80211_CHAN_WIDTH_320: + case NL80211_CHAN_WIDTH_160: + return 5; + case NL80211_CHAN_WIDTH_80: + return 7; + default: + break; + } + return 10; +} + +static bool +iwl_mld_channel_load_allows_emlsr(struct iwl_mld *mld, + struct ieee80211_vif *vif, + const struct iwl_mld_link_sel_data *a, + const struct iwl_mld_link_sel_data *b) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link_a = + iwl_mld_link_dereference_check(mld_vif, a->link_id); + struct ieee80211_chanctx_conf *chanctx_a = NULL; + u32 bw_a, bw_b, ratio; + u32 primary_load_perc; + + if (!link_a || !link_a->active) { + IWL_DEBUG_EHT(mld, "Primary link is not active. Can't enter EMLSR\n"); + return false; + } + + chanctx_a = wiphy_dereference(mld->wiphy, link_a->chan_ctx); + + if (WARN_ON(!chanctx_a)) + return false; + + primary_load_perc = + iwl_mld_phy_from_mac80211(chanctx_a)->avg_channel_load_not_by_us; + + IWL_DEBUG_EHT(mld, "Average channel load not by us: %u\n", primary_load_perc); + + if (primary_load_perc < iwl_mld_get_min_chan_load_thresh(chanctx_a)) { + IWL_DEBUG_EHT(mld, "Channel load is below the minimum threshold\n"); + return false; + } + + if (iwl_mld_vif_low_latency(mld_vif)) { + IWL_DEBUG_EHT(mld, "Low latency vif, EMLSR is allowed\n"); + return true; + } + + if (a->chandef->width <= b->chandef->width) + return true; + + bw_a = cfg80211_chandef_get_width(a->chandef); + bw_b = cfg80211_chandef_get_width(b->chandef); + ratio = bw_a / bw_b; + + switch (ratio) { + case 2: + return primary_load_perc > 25; + case 4: + return primary_load_perc > 40; + case 8: + case 16: + return primary_load_perc > 50; + } + + return false; +} + +VISIBLE_IF_IWLWIFI_KUNIT u32 +iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *a, + struct iwl_mld_link_sel_data *b) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + u32 reason_mask = 0; + + /* Per-link considerations */ + reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, a, true); + if (reason_mask) + return reason_mask; + + reason_mask = iwl_mld_emlsr_disallowed_with_link(mld, vif, b, false); + if (reason_mask) + return reason_mask; + + if (a->chandef->chan->band == b->chandef->chan->band) { + const struct cfg80211_chan_def *c_low = a->chandef; + const struct cfg80211_chan_def *c_high = b->chandef; + u32 c_low_upper_edge, c_high_lower_edge; + + if (c_low->chan->center_freq > c_high->chan->center_freq) + swap(c_low, c_high); + + c_low_upper_edge = c_low->chan->center_freq + + cfg80211_chandef_get_width(c_low) / 2; + c_high_lower_edge = c_high->chan->center_freq - + cfg80211_chandef_get_width(c_high) / 2; + + if (a->chandef->chan->band == NL80211_BAND_5GHZ && + c_low_upper_edge <= 5330 && c_high_lower_edge >= 5490) { + /* This case is fine - HW/FW can deal with it, there's + * enough separation between the two channels. + */ + } else { + reason_mask |= IWL_MLD_EMLSR_EXIT_EQUAL_BAND; + } + } + if (!iwl_mld_channel_load_allows_emlsr(mld, vif, a, b)) + reason_mask |= IWL_MLD_EMLSR_EXIT_CHAN_LOAD; + + if (reason_mask) { + IWL_DEBUG_INFO(mld, + "Links %d and %d are not a valid pair for EMLSR\n", + a->link_id, b->link_id); + IWL_DEBUG_INFO(mld, + "Links bandwidth are: %d and %d\n", + nl80211_chan_width_to_mhz(a->chandef->width), + nl80211_chan_width_to_mhz(b->chandef->width)); + iwl_mld_print_emlsr_exit(mld, reason_mask); + } + + return reason_mask; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_emlsr_pair_state); + +/* Calculation is done with fixed-point with a scaling factor of 1/256 */ +#define SCALE_FACTOR 256 + +/* + * Returns the combined grade of two given links. + * Returns 0 if EMLSR is not allowed with these 2 links. + */ +static +unsigned int iwl_mld_get_emlsr_grade(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *a, + struct iwl_mld_link_sel_data *b, + u8 *primary_id) +{ + struct ieee80211_bss_conf *primary_conf; + struct wiphy *wiphy = ieee80211_vif_to_wdev(vif)->wiphy; + unsigned int primary_load; + + lockdep_assert_wiphy(wiphy); + + /* a is always primary, b is always secondary */ + if (b->grade > a->grade) + swap(a, b); + + *primary_id = a->link_id; + + if (iwl_mld_emlsr_pair_state(vif, a, b)) + return 0; + + primary_conf = wiphy_dereference(wiphy, vif->link_conf[*primary_id]); + + if (WARN_ON_ONCE(!primary_conf)) + return 0; + + primary_load = iwl_mld_get_chan_load(mld, primary_conf); + + /* The more the primary link is loaded, the more worthwhile EMLSR becomes */ + return a->grade + ((b->grade * primary_load) / SCALE_FACTOR); +} + +static void _iwl_mld_select_links(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_mld_link_sel_data data[IEEE80211_MLD_MAX_NUM_LINKS]; + struct iwl_mld_link_sel_data *best_link; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int max_active_links = iwl_mld_max_active_links(mld, vif); + u16 new_active, usable_links = ieee80211_vif_usable_links(vif); + u8 best_idx, new_primary, n_data; + u16 max_grade; + + lockdep_assert_wiphy(mld->wiphy); + + if (!mld_vif->authorized || hweight16(usable_links) <= 1) + return; + + if (WARN(ktime_before(mld->scan.last_mlo_scan_time, + ktime_sub_ns(ktime_get_boottime_ns(), + 5ULL * NSEC_PER_SEC)), + "Last MLO scan was too long ago, can't select links\n")) + return; + + /* The logic below is simple and not suited for more than 2 links */ + WARN_ON_ONCE(max_active_links > 2); + + n_data = iwl_mld_set_link_sel_data(mld, vif, data, usable_links, + &best_idx); + + if (!n_data) { + IWL_DEBUG_EHT(mld, + "Couldn't find a valid grade for any link!\n"); + return; + } + + /* Default to selecting the single best link */ + best_link = &data[best_idx]; + new_primary = best_link->link_id; + new_active = BIT(best_link->link_id); + max_grade = best_link->grade; + + /* If EMLSR is not possible, activate the best link */ + if (max_active_links == 1 || n_data == 1 || + !iwl_mld_vif_has_emlsr_cap(vif) || !IWL_MLD_AUTO_EML_ENABLE || + mld_vif->emlsr.blocked_reasons) + goto set_active; + + /* Try to find the best link combination */ + for (u8 a = 0; a < n_data; a++) { + for (u8 b = a + 1; b < n_data; b++) { + u8 best_in_pair; + u16 emlsr_grade = + iwl_mld_get_emlsr_grade(mld, vif, + &data[a], &data[b], + &best_in_pair); + + /* + * Prefer (new) EMLSR combination to prefer EMLSR over + * a single link. + */ + if (emlsr_grade < max_grade) + continue; + + max_grade = emlsr_grade; + new_primary = best_in_pair; + new_active = BIT(data[a].link_id) | + BIT(data[b].link_id); + } + } + +set_active: + IWL_DEBUG_INFO(mld, "Link selection result: 0x%x. Primary = %d\n", + new_active, new_primary); + + mld_vif->emlsr.selected_primary = new_primary; + mld_vif->emlsr.selected_links = new_active; + + ieee80211_set_active_links_async(vif, new_active); +} + +static void iwl_mld_vif_iter_select_links(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + + _iwl_mld_select_links(mld, vif); +} + +void iwl_mld_select_links(struct iwl_mld *mld) +{ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_select_links, + NULL); +} + +static void iwl_mld_emlsr_check_bt_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + const struct iwl_bt_coex_profile_notif zero_notif = {}; + struct iwl_mld *mld = mld_vif->mld; + struct ieee80211_bss_conf *link; + unsigned int link_id; + const struct iwl_bt_coex_profile_notif *notif = &mld->last_bt_notif; + + if (!iwl_mld_vif_has_emlsr_cap(vif)) + return; + + /* zeroed structure means that BT is OFF */ + if (!memcmp(notif, &zero_notif, sizeof(*notif))) { + iwl_mld_retry_emlsr(mld, vif); + return; + } + + for_each_vif_active_link(vif, link, link_id) { + bool emlsr_active, emlsr_allowed; + + if (WARN_ON(!link->chanreq.oper.chan)) + continue; + + if (link->chanreq.oper.chan->band != NL80211_BAND_2GHZ) + continue; + + emlsr_active = iwl_mld_emlsr_active(vif); + emlsr_allowed = iwl_mld_bt_allows_emlsr(mld, link, + !emlsr_active); + if (emlsr_allowed && !emlsr_active) { + iwl_mld_retry_emlsr(mld, vif); + return; + } + + if (!emlsr_allowed && emlsr_active) { + iwl_mld_exit_emlsr(mld, vif, + IWL_MLD_EMLSR_EXIT_BT_COEX, + iwl_mld_get_primary_link(vif)); + return; + } + } +} + +void iwl_mld_emlsr_check_bt(struct iwl_mld *mld) +{ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_emlsr_check_bt_iter, + NULL); +} + +struct iwl_mld_chan_load_data { + struct iwl_mld_phy *phy; + u32 prev_chan_load_not_by_us; +}; + +static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_chan_load_data *data = _data; + const struct iwl_mld_phy *phy = data->phy; + struct ieee80211_chanctx_conf *chanctx = + container_of((const void *)phy, struct ieee80211_chanctx_conf, + drv_priv); + struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld; + struct ieee80211_bss_conf *prim_link; + unsigned int prim_link_id; + + prim_link_id = iwl_mld_get_primary_link(vif); + prim_link = link_conf_dereference_protected(vif, prim_link_id); + + if (WARN_ON(!prim_link)) + return; + + if (chanctx != rcu_access_pointer(prim_link->chanctx_conf)) + return; + + if (iwl_mld_emlsr_active(vif)) { + int chan_load = iwl_mld_get_chan_load_by_others(mld, prim_link, + true); + + if (chan_load < 0) + return; + + /* chan_load is in range [0,255] */ + if (chan_load < NORMALIZE_PERCENT_TO_255(IWL_MLD_EXIT_EMLSR_CHAN_LOAD)) + iwl_mld_exit_emlsr(mld, vif, + IWL_MLD_EMLSR_EXIT_CHAN_LOAD, + prim_link_id); + } else { + u32 old_chan_load = data->prev_chan_load_not_by_us; + u32 new_chan_load = phy->avg_channel_load_not_by_us; + u32 min_thresh = iwl_mld_get_min_chan_load_thresh(chanctx); + +#define THRESHOLD_CROSSED(threshold) \ + (old_chan_load <= (threshold) && new_chan_load > (threshold)) + + if (THRESHOLD_CROSSED(min_thresh) || THRESHOLD_CROSSED(25) || + THRESHOLD_CROSSED(40) || THRESHOLD_CROSSED(50)) + iwl_mld_retry_emlsr(mld, vif); +#undef THRESHOLD_CROSSED + } +} + +void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw, + struct iwl_mld_phy *phy, + u32 prev_chan_load_not_by_us) +{ + struct iwl_mld_chan_load_data data = { + .phy = phy, + .prev_chan_load_not_by_us = prev_chan_load_not_by_us, + }; + + ieee80211_iterate_active_interfaces_mtx(hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_chan_load_update_iter, + &data); +} + +void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (!iwl_mld_vif_has_emlsr_cap(vif) || iwl_mld_emlsr_active(vif) || + mld_vif->emlsr.blocked_reasons) + return; + + iwl_mld_int_mlo_scan(mld, vif); +} + +static void iwl_mld_ignore_tpt_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld *mld = mld_vif->mld; + struct iwl_mld_sta *mld_sta; + bool *start = (void *)data; + + /* check_tpt_wk is only used when TPT block isn't set */ + if (mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT || + !IWL_MLD_AUTO_EML_ENABLE || !mld_vif->ap_sta) + return; + + mld_sta = iwl_mld_sta_from_mac80211(mld_vif->ap_sta); + + /* We only count for the AP sta in a MLO connection */ + if (!mld_sta->mpdu_counters) + return; + + if (*start) { + wiphy_delayed_work_cancel(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk); + IWL_DEBUG_EHT(mld, "TPT check disabled\n"); + return; + } + + /* Clear the counters so we start from the beginning */ + for (int q = 0; q < mld->trans->info.num_rxqs; q++) { + struct iwl_mld_per_q_mpdu_counter *queue_counter = + &mld_sta->mpdu_counters[q]; + + spin_lock_bh(&queue_counter->lock); + + memset(queue_counter->per_link, 0, + sizeof(queue_counter->per_link)); + + spin_unlock_bh(&queue_counter->lock); + } + + /* Schedule the check in 5 seconds */ + wiphy_delayed_work_queue(mld_vif->mld->wiphy, + &mld_vif->emlsr.check_tpt_wk, + round_jiffies_relative(IWL_MLD_TPT_COUNT_WINDOW)); + IWL_DEBUG_EHT(mld, "TPT check enabled\n"); +} + +void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld) +{ + bool start = true; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_ignore_tpt_iter, + &start); +} + +void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld) +{ + bool start = false; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_ignore_tpt_iter, + &start); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/mlo.h b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h new file mode 100644 index 000000000000..9afa3d6ea649 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/mlo.h @@ -0,0 +1,173 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_mlo_h__ +#define __iwl_mld_mlo_h__ + +#include <linux/ieee80211.h> +#include <linux/types.h> +#include <net/mac80211.h> +#include "iwl-config.h" +#include "iwl-trans.h" +#include "iface.h" +#include "phy.h" + +struct iwl_mld; + +void iwl_mld_emlsr_prevent_done_wk(struct wiphy *wiphy, struct wiphy_work *wk); +void iwl_mld_emlsr_tmp_non_bss_done_wk(struct wiphy *wiphy, + struct wiphy_work *wk); + +static inline bool iwl_mld_emlsr_active(struct ieee80211_vif *vif) +{ + /* Set on phy context activation, so should be a good proxy */ + return !!(vif->driver_flags & IEEE80211_VIF_EML_ACTIVE); +} + +static inline bool iwl_mld_vif_has_emlsr_cap(struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + /* We only track/permit EMLSR state once authorized */ + if (!mld_vif->authorized) + return false; + + /* No EMLSR on dual radio devices */ + return ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION && + ieee80211_vif_is_mld(vif) && + vif->cfg.eml_cap & IEEE80211_EML_CAP_EMLSR_SUPP && + !CSR_HW_RFID_IS_CDB(mld_vif->mld->trans->info.hw_rf_id); +} + +static inline int +iwl_mld_max_active_links(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + if (vif->type == NL80211_IFTYPE_AP) + return mld->fw->ucode_capa.num_beacons; + + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_STATION) + return IWL_FW_MAX_ACTIVE_LINKS_NUM; + + /* For now, do not accept more links on other interface types */ + return 1; +} + +static inline int +iwl_mld_count_active_links(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *mld_link; + int n_active = 0; + + for_each_mld_vif_valid_link(mld_vif, mld_link) { + if (rcu_access_pointer(mld_link->chan_ctx)) + n_active++; + } + + return n_active; +} + +static inline u8 iwl_mld_get_primary_link(struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + lockdep_assert_wiphy(mld_vif->mld->wiphy); + + if (!ieee80211_vif_is_mld(vif) || WARN_ON(!vif->active_links)) + return 0; + + /* In AP mode, there is no primary link */ + if (vif->type == NL80211_IFTYPE_AP) + return __ffs(vif->active_links); + + if (iwl_mld_emlsr_active(vif) && + !WARN_ON(!(BIT(mld_vif->emlsr.primary) & vif->active_links))) + return mld_vif->emlsr.primary; + + return __ffs(vif->active_links); +} + +/* + * For non-MLO/single link, this will return the deflink/single active link, + * respectively + */ +static inline u8 iwl_mld_get_other_link(struct ieee80211_vif *vif, u8 link_id) +{ + switch (hweight16(vif->active_links)) { + case 0: + return 0; + default: + WARN_ON(1); + fallthrough; + case 1: + return __ffs(vif->active_links); + case 2: + return __ffs(vif->active_links & ~BIT(link_id)); + } +} + +s8 iwl_mld_get_emlsr_rssi_thresh(struct iwl_mld *mld, + const struct cfg80211_chan_def *chandef, + bool low); + +/* EMLSR block/unblock and exit */ +void iwl_mld_block_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, u8 link_to_keep); +int iwl_mld_block_emlsr_sync(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason, u8 link_to_keep); +void iwl_mld_unblock_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_blocked reason); +void iwl_mld_exit_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif, + enum iwl_mld_emlsr_exit exit, u8 link_to_keep); + +int iwl_mld_emlsr_check_non_bss_block(struct iwl_mld *mld, + int pending_link_changes); + +void iwl_mld_handle_emlsr_mode_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_handle_emlsr_trans_fail_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_emlsr_check_tpt(struct wiphy *wiphy, struct wiphy_work *wk); +void iwl_mld_emlsr_unblock_tpt_wk(struct wiphy *wiphy, struct wiphy_work *wk); + +void iwl_mld_select_links(struct iwl_mld *mld); + +void iwl_mld_emlsr_check_bt(struct iwl_mld *mld); + +void iwl_mld_emlsr_check_chan_load(struct ieee80211_hw *hw, + struct iwl_mld_phy *phy, + u32 prev_chan_load_not_by_us); + +/** + * iwl_mld_retry_emlsr - Retry entering EMLSR + * @mld: MLD context + * @vif: VIF to retry EMLSR on + * + * Retry entering EMLSR on the given VIF. + * Use this if one of the parameters that can prevent EMLSR has changed. + */ +void iwl_mld_retry_emlsr(struct iwl_mld *mld, struct ieee80211_vif *vif); + +struct iwl_mld_link_sel_data { + u8 link_id; + const struct cfg80211_chan_def *chandef; + s32 signal; + u16 grade; +}; + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +u32 iwl_mld_emlsr_pair_state(struct ieee80211_vif *vif, + struct iwl_mld_link_sel_data *a, + struct iwl_mld_link_sel_data *b); + +bool iwl_mld_bt_allows_emlsr(struct iwl_mld *mld, + struct ieee80211_bss_conf *link, + bool entry_criteria); +#endif + +void iwl_mld_start_ignoring_tpt_updates(struct iwl_mld *mld); +void iwl_mld_stop_ignoring_tpt_updates(struct iwl_mld *mld); + +#endif /* __iwl_mld_mlo_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.c b/drivers/net/wireless/intel/iwlwifi/mld/notif.c new file mode 100644 index 000000000000..c0e62d46aba6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.c @@ -0,0 +1,722 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "mld.h" +#include "notif.h" +#include "scan.h" +#include "iface.h" +#include "mlo.h" +#include "iwl-trans.h" +#include "fw/file.h" +#include "fw/dbg.h" +#include "fw/api/cmdhdr.h" +#include "fw/api/mac-cfg.h" +#include "session-protect.h" +#include "fw/api/time-event.h" +#include "fw/api/tx.h" +#include "fw/api/rs.h" +#include "fw/api/offload.h" +#include "fw/api/stats.h" +#include "fw/api/rfi.h" +#include "fw/api/coex.h" + +#include "mcc.h" +#include "link.h" +#include "tx.h" +#include "rx.h" +#include "tlc.h" +#include "agg.h" +#include "mac80211.h" +#include "thermal.h" +#include "roc.h" +#include "stats.h" +#include "coex.h" +#include "time_sync.h" +#include "ftm-initiator.h" + +/* Please use this in an increasing order of the versions */ +#define CMD_VER_ENTRY(_ver, _struct) \ + { .size = sizeof(struct _struct), .ver = _ver }, +#define CMD_VERSIONS(name, ...) \ + static const struct iwl_notif_struct_size \ + iwl_notif_struct_sizes_##name[] = { __VA_ARGS__ }; + +#define RX_HANDLER_NO_OBJECT(_grp, _cmd, _name, _context) \ + {.cmd_id = WIDE_ID(_grp, _cmd), \ + .context = _context, \ + .fn = iwl_mld_handle_##_name, \ + .sizes = iwl_notif_struct_sizes_##_name, \ + .n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name), \ + }, + +/* Use this for Rx handlers that do not need notification validation */ +#define RX_HANDLER_NO_VAL(_grp, _cmd, _name, _context) \ + {.cmd_id = WIDE_ID(_grp, _cmd), \ + .context = _context, \ + .fn = iwl_mld_handle_##_name, \ + }, + +#define RX_HANDLER_VAL_FN(_grp, _cmd, _name, _context) \ + { .cmd_id = WIDE_ID(_grp, _cmd), \ + .context = _context, \ + .fn = iwl_mld_handle_##_name, \ + .val_fn = iwl_mld_validate_##_name, \ + }, + +#define DEFINE_SIMPLE_CANCELLATION(name, notif_struct, id_member) \ +static bool iwl_mld_cancel_##name##_notif(struct iwl_mld *mld, \ + struct iwl_rx_packet *pkt, \ + u32 obj_id) \ +{ \ + const struct notif_struct *notif = (const void *)pkt->data; \ + \ + return obj_id == _Generic((notif)->id_member, \ + __le32: le32_to_cpu((notif)->id_member), \ + __le16: le16_to_cpu((notif)->id_member), \ + u8: (notif)->id_member); \ +} + +static bool iwl_mld_always_cancel(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u32 obj_id) +{ + return true; +} + +/* Currently only defined for the RX_HANDLER_SIZES options. Use this for + * notifications that belong to a specific object, and that should be + * canceled when the object is removed + */ +#define RX_HANDLER_OF_OBJ(_grp, _cmd, _name, _obj_type) \ + {.cmd_id = WIDE_ID(_grp, _cmd), \ + /* Only async handlers can be canceled */ \ + .context = RX_HANDLER_ASYNC, \ + .fn = iwl_mld_handle_##_name, \ + .sizes = iwl_notif_struct_sizes_##_name, \ + .n_sizes = ARRAY_SIZE(iwl_notif_struct_sizes_##_name), \ + .obj_type = IWL_MLD_OBJECT_TYPE_##_obj_type, \ + .cancel = iwl_mld_cancel_##_name, \ + }, + +#define RX_HANDLER_OF_LINK(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, LINK) \ + +#define RX_HANDLER_OF_VIF(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, VIF) \ + +#define RX_HANDLER_OF_STA(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, STA) \ + +#define RX_HANDLER_OF_ROC(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, ROC) + +#define RX_HANDLER_OF_SCAN(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, SCAN) + +#define RX_HANDLER_OF_FTM_REQ(_grp, _cmd, _name) \ + RX_HANDLER_OF_OBJ(_grp, _cmd, _name, FTM_REQ) + +static void iwl_mld_handle_mfuart_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_mfuart_load_notif *mfuart_notif = (void *)pkt->data; + + IWL_DEBUG_INFO(mld, + "MFUART: installed ver: 0x%08x, external ver: 0x%08x\n", + le32_to_cpu(mfuart_notif->installed_ver), + le32_to_cpu(mfuart_notif->external_ver)); + IWL_DEBUG_INFO(mld, + "MFUART: status: 0x%08x, duration: 0x%08x image size: 0x%08x\n", + le32_to_cpu(mfuart_notif->status), + le32_to_cpu(mfuart_notif->duration), + le32_to_cpu(mfuart_notif->image_size)); +} + +static void iwl_mld_mu_mimo_iface_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf; + unsigned int link_id = 0; + + if (WARN(hweight16(vif->active_links) > 1, + "no support for this notif while in EMLSR 0x%x\n", + vif->active_links)) + return; + + if (ieee80211_vif_is_mld(vif)) { + link_id = __ffs(vif->active_links); + bss_conf = link_conf_dereference_check(vif, link_id); + } + + if (!WARN_ON(!bss_conf) && bss_conf->mu_mimo_owner) { + const struct iwl_mu_group_mgmt_notif *notif = _data; + + BUILD_BUG_ON(sizeof(notif->membership_status) != + WLAN_MEMBERSHIP_LEN); + BUILD_BUG_ON(sizeof(notif->user_position) != + WLAN_USER_POSITION_LEN); + + /* MU-MIMO Group Id action frame is little endian. We treat + * the data received from firmware as if it came from the + * action frame, so no conversion is needed. + */ + ieee80211_update_mu_groups(vif, link_id, + (u8 *)¬if->membership_status, + (u8 *)¬if->user_position); + } +} + +/* This handler is called in SYNC mode because it needs to be serialized with + * Rx as specified in ieee80211_update_mu_groups()'s documentation. + */ +static void iwl_mld_handle_mu_mimo_grp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_mu_group_mgmt_notif *notif = (void *)pkt->data; + + ieee80211_iterate_active_interfaces_atomic(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_mu_mimo_iface_iterator, + notif); +} + +static void +iwl_mld_handle_channel_switch_start_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_channel_switch_start_notif *notif = (void *)pkt->data; + u32 link_id = le32_to_cpu(notif->link_id); + struct ieee80211_bss_conf *link_conf = + iwl_mld_fw_id_to_link_conf(mld, link_id); + struct ieee80211_vif *vif; + + if (WARN_ON(!link_conf)) + return; + + vif = link_conf->vif; + + IWL_DEBUG_INFO(mld, + "CSA Start Notification with vif type: %d, link_id: %d\n", + vif->type, + link_conf->link_id); + + switch (vif->type) { + case NL80211_IFTYPE_AP: + /* We don't support canceling a CSA as it was advertised + * by the AP itself + */ + if (!link_conf->csa_active) + return; + + ieee80211_csa_finish(vif, link_conf->link_id); + break; + case NL80211_IFTYPE_STATION: + if (!link_conf->csa_active) { + /* Either unexpected cs notif or mac80211 chose to + * ignore, for example in channel switch to same channel + */ + struct iwl_cancel_channel_switch_cmd cmd = { + .id = cpu_to_le32(link_id), + }; + + if (iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, + CANCEL_CHANNEL_SWITCH_CMD), + &cmd)) + IWL_ERR(mld, + "Failed to cancel the channel switch\n"); + return; + } + + ieee80211_chswitch_done(vif, true, link_conf->link_id); + break; + + default: + WARN(1, "CSA on invalid vif type: %d", vif->type); + } +} + +static void +iwl_mld_handle_channel_switch_error_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_channel_switch_error_notif *notif = (void *)pkt->data; + struct ieee80211_bss_conf *link_conf; + struct ieee80211_vif *vif; + u32 link_id = le32_to_cpu(notif->link_id); + u32 csa_err_mask = le32_to_cpu(notif->csa_err_mask); + + link_conf = iwl_mld_fw_id_to_link_conf(mld, link_id); + if (WARN_ON(!link_conf)) + return; + + vif = link_conf->vif; + + IWL_DEBUG_INFO(mld, "FW reports CSA error: id=%u, csa_err_mask=%u\n", + link_id, csa_err_mask); + + if (csa_err_mask & (CS_ERR_COUNT_ERROR | + CS_ERR_LONG_DELAY_AFTER_CS | + CS_ERR_TX_BLOCK_TIMER_EXPIRED)) + ieee80211_channel_switch_disconnect(vif); +} + +static void iwl_mld_handle_beacon_notification(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_extended_beacon_notif *beacon = (void *)pkt->data; + + mld->ibss_manager = !!beacon->ibss_mgr_status; +} + +/** + * DOC: Notification versioning + * + * The firmware's notifications change from time to time. In order to + * differentiate between different versions of the same notification, the + * firmware advertises the version of each notification. + * Here are listed all the notifications that are supported. Several versions + * of the same notification can be allowed at the same time: + * + * CMD_VERSION(my_multi_version_notif, + * CMD_VER_ENTRY(1, iwl_my_multi_version_notif_ver1) + * CMD_VER_ENTRY(2, iwl_my_multi_version_notif_ver2) + * + * etc... + * + * The driver will enforce that the notification coming from the firmware + * has its version listed here and it'll also enforce that the firmware sent + * at least enough bytes to cover the structure listed in the CMD_VER_ENTRY. + */ + +CMD_VERSIONS(scan_complete_notif, + CMD_VER_ENTRY(1, iwl_umac_scan_complete)) +CMD_VERSIONS(scan_iter_complete_notif, + CMD_VER_ENTRY(2, iwl_umac_scan_iter_complete_notif)) +CMD_VERSIONS(mfuart_notif, + CMD_VER_ENTRY(2, iwl_mfuart_load_notif)) +CMD_VERSIONS(update_mcc, + CMD_VER_ENTRY(1, iwl_mcc_chub_notif)) +CMD_VERSIONS(session_prot_notif, + CMD_VER_ENTRY(3, iwl_session_prot_notif)) +CMD_VERSIONS(missed_beacon_notif, + CMD_VER_ENTRY(5, iwl_missed_beacons_notif)) +CMD_VERSIONS(tx_resp_notif, + CMD_VER_ENTRY(8, iwl_tx_resp) + CMD_VER_ENTRY(9, iwl_tx_resp)) +CMD_VERSIONS(compressed_ba_notif, + CMD_VER_ENTRY(5, iwl_compressed_ba_notif) + CMD_VER_ENTRY(6, iwl_compressed_ba_notif) + CMD_VER_ENTRY(7, iwl_compressed_ba_notif)) +CMD_VERSIONS(tlc_notif, + CMD_VER_ENTRY(3, iwl_tlc_update_notif) + CMD_VER_ENTRY(4, iwl_tlc_update_notif)) +CMD_VERSIONS(mu_mimo_grp_notif, + CMD_VER_ENTRY(1, iwl_mu_group_mgmt_notif)) +CMD_VERSIONS(channel_switch_start_notif, + CMD_VER_ENTRY(3, iwl_channel_switch_start_notif)) +CMD_VERSIONS(channel_switch_error_notif, + CMD_VER_ENTRY(2, iwl_channel_switch_error_notif)) +CMD_VERSIONS(ct_kill_notif, + CMD_VER_ENTRY(2, ct_kill_notif)) +CMD_VERSIONS(temp_notif, + CMD_VER_ENTRY(2, iwl_dts_measurement_notif)) +CMD_VERSIONS(roc_notif, + CMD_VER_ENTRY(1, iwl_roc_notif)) +CMD_VERSIONS(probe_resp_data_notif, + CMD_VER_ENTRY(1, iwl_probe_resp_data_notif)) +CMD_VERSIONS(datapath_monitor_notif, + CMD_VER_ENTRY(1, iwl_datapath_monitor_notif)) +CMD_VERSIONS(stats_oper_notif, + CMD_VER_ENTRY(3, iwl_system_statistics_notif_oper)) +CMD_VERSIONS(stats_oper_part1_notif, + CMD_VER_ENTRY(4, iwl_system_statistics_part1_notif_oper)) +CMD_VERSIONS(bt_coex_notif, + CMD_VER_ENTRY(1, iwl_bt_coex_profile_notif)) +CMD_VERSIONS(beacon_notification, + CMD_VER_ENTRY(6, iwl_extended_beacon_notif)) +CMD_VERSIONS(emlsr_mode_notif, + CMD_VER_ENTRY(1, iwl_esr_mode_notif_v1) + CMD_VER_ENTRY(2, iwl_esr_mode_notif)) +CMD_VERSIONS(emlsr_trans_fail_notif, + CMD_VER_ENTRY(1, iwl_esr_trans_fail_notif)) +CMD_VERSIONS(uapsd_misbehaving_ap_notif, + CMD_VER_ENTRY(1, iwl_uapsd_misbehaving_ap_notif)) +CMD_VERSIONS(time_msmt_notif, + CMD_VER_ENTRY(1, iwl_time_msmt_notify)) +CMD_VERSIONS(time_sync_confirm_notif, + CMD_VER_ENTRY(1, iwl_time_msmt_cfm_notify)) +CMD_VERSIONS(omi_status_notif, + CMD_VER_ENTRY(1, iwl_omi_send_status_notif)) +CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(9, iwl_tof_range_rsp_ntfy)) + +DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id) +DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id) +DEFINE_SIMPLE_CANCELLATION(channel_switch_start, + iwl_channel_switch_start_notif, link_id) +DEFINE_SIMPLE_CANCELLATION(channel_switch_error, + iwl_channel_switch_error_notif, link_id) +DEFINE_SIMPLE_CANCELLATION(datapath_monitor, iwl_datapath_monitor_notif, + link_id) +DEFINE_SIMPLE_CANCELLATION(roc, iwl_roc_notif, activity) +DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid) +DEFINE_SIMPLE_CANCELLATION(probe_resp_data, iwl_probe_resp_data_notif, + mac_id) +DEFINE_SIMPLE_CANCELLATION(uapsd_misbehaving_ap, iwl_uapsd_misbehaving_ap_notif, + mac_id) +#define iwl_mld_cancel_omi_status_notif iwl_mld_always_cancel +DEFINE_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id) + +/** + * DOC: Handlers for fw notifications + * + * Here are listed the notifications IDs (including the group ID), the handler + * of the notification and how it should be called: + * + * - RX_HANDLER_SYNC: will be called as part of the Rx path + * - RX_HANDLER_ASYNC: will be handled in a working with the wiphy_lock held + * + * This means that if the firmware sends two notifications A and B in that + * order and notification A is RX_HANDLER_ASYNC and notification is + * RX_HANDLER_SYNC, the handler of B will likely be called before the handler + * of A. + * + * This list should be in order of frequency for performance purposes. + * The handler can be one from two contexts, see &iwl_rx_handler_context + * + * A handler can declare that it relies on a specific object in which case it + * can be cancelled in case the object is deleted. In order to use this + * mechanism, a cancellation function is needed. The cancellation function must + * receive an object id (the index of that object in the firmware) and a + * notification payload. It'll return true if that specific notification should + * be cancelled upon the obliteration of the specific instance of the object. + * + * DEFINE_SIMPLE_CANCELLATION allows to easily create a cancellation function + * that wills simply return true if a given object id matches the object id in + * the firmware notification. + */ + +VISIBLE_IF_IWLWIFI_KUNIT +const struct iwl_rx_handler iwl_mld_rx_handlers[] = { + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, TX_CMD, tx_resp_notif, + RX_HANDLER_SYNC) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BA_NOTIF, compressed_ba_notif, + RX_HANDLER_SYNC) + RX_HANDLER_OF_SCAN(LEGACY_GROUP, SCAN_COMPLETE_UMAC, + scan_complete_notif) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, SCAN_ITERATION_COMPLETE_UMAC, + scan_iter_complete_notif, + RX_HANDLER_SYNC) + RX_HANDLER_NO_VAL(LEGACY_GROUP, MATCH_FOUND_NOTIFICATION, + match_found_notif, RX_HANDLER_SYNC) + + RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_NOTIF, + stats_oper_notif, RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF, + stats_oper_part1_notif, RX_HANDLER_ASYNC) + + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MFUART_LOAD_NOTIFICATION, + mfuart_notif, RX_HANDLER_SYNC) + + RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, DTS_MEASUREMENT_NOTIF_WIDE, + temp_notif, RX_HANDLER_ASYNC) + RX_HANDLER_OF_LINK(MAC_CONF_GROUP, SESSION_PROTECTION_NOTIF, + session_prot_notif) + RX_HANDLER_OF_LINK(MAC_CONF_GROUP, MISSED_BEACONS_NOTIF, + missed_beacon_notif) + RX_HANDLER_OF_STA(DATA_PATH_GROUP, TLC_MNG_UPDATE_NOTIF, tlc_notif) + RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_START_NOTIF, + channel_switch_start_notif) + RX_HANDLER_OF_LINK(MAC_CONF_GROUP, CHANNEL_SWITCH_ERROR_NOTIF, + channel_switch_error_notif) + RX_HANDLER_OF_ROC(MAC_CONF_GROUP, ROC_NOTIF, roc_notif) + RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, MU_GROUP_MGMT_NOTIF, + mu_mimo_grp_notif, RX_HANDLER_SYNC) + RX_HANDLER_OF_VIF(MAC_CONF_GROUP, PROBE_RESPONSE_DATA_NOTIF, + probe_resp_data_notif) + RX_HANDLER_NO_OBJECT(PHY_OPS_GROUP, CT_KILL_NOTIFICATION, + ct_kill_notif, RX_HANDLER_ASYNC) + RX_HANDLER_OF_LINK(DATA_PATH_GROUP, MONITOR_NOTIF, + datapath_monitor_notif) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, MCC_CHUB_UPDATE_CMD, update_mcc, + RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(BT_COEX_GROUP, PROFILE_NOTIF, + bt_coex_notif, RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, BEACON_NOTIFICATION, + beacon_notification, RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(DATA_PATH_GROUP, ESR_MODE_NOTIF, + emlsr_mode_notif, RX_HANDLER_ASYNC) + RX_HANDLER_NO_OBJECT(MAC_CONF_GROUP, EMLSR_TRANS_FAIL_NOTIF, + emlsr_trans_fail_notif, RX_HANDLER_ASYNC) + RX_HANDLER_OF_VIF(LEGACY_GROUP, PSM_UAPSD_AP_MISBEHAVING_NOTIFICATION, + uapsd_misbehaving_ap_notif) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, + WNM_80211V_TIMING_MEASUREMENT_NOTIFICATION, + time_msmt_notif, RX_HANDLER_SYNC) + RX_HANDLER_NO_OBJECT(LEGACY_GROUP, + WNM_80211V_TIMING_MEASUREMENT_CONFIRM_NOTIFICATION, + time_sync_confirm_notif, RX_HANDLER_ASYNC) + RX_HANDLER_OF_LINK(DATA_PATH_GROUP, OMI_SEND_STATUS_NOTIF, + omi_status_notif) + RX_HANDLER_OF_FTM_REQ(LOCATION_GROUP, TOF_RANGE_RESPONSE_NOTIF, + ftm_resp_notif) +}; +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers); + +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +const unsigned int iwl_mld_rx_handlers_num = ARRAY_SIZE(iwl_mld_rx_handlers); +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers_num); +#endif + +static bool +iwl_mld_notif_is_valid(struct iwl_mld *mld, struct iwl_rx_packet *pkt, + const struct iwl_rx_handler *handler) +{ + unsigned int size = iwl_rx_packet_payload_len(pkt); + size_t notif_ver; + + /* If n_sizes == 0, it indicates that a validation function may be used + * or that no validation is required. + */ + if (!handler->n_sizes) { + if (handler->val_fn) + return handler->val_fn(mld, pkt); + return true; + } + + notif_ver = iwl_fw_lookup_notif_ver(mld->fw, + iwl_cmd_groupid(handler->cmd_id), + iwl_cmd_opcode(handler->cmd_id), + IWL_FW_CMD_VER_UNKNOWN); + + for (int i = 0; i < handler->n_sizes; i++) { + if (handler->sizes[i].ver != notif_ver) + continue; + + if (IWL_FW_CHECK(mld, size < handler->sizes[i].size, + "unexpected notification 0x%04x size %d, need %d\n", + handler->cmd_id, size, handler->sizes[i].size)) + return false; + return true; + } + + IWL_FW_CHECK_FAILED(mld, + "notif 0x%04x ver %zu missing expected size, use version %u size\n", + handler->cmd_id, notif_ver, + handler->sizes[handler->n_sizes - 1].ver); + + return size < handler->sizes[handler->n_sizes - 1].size; +} + +struct iwl_async_handler_entry { + struct list_head list; + struct iwl_rx_cmd_buffer rxb; + const struct iwl_rx_handler *rx_h; +}; + +static void +iwl_mld_log_async_handler_op(struct iwl_mld *mld, const char *op, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + + IWL_DEBUG_HC(mld, + "%s async handler for notif %s (%.2x.%2x, seq 0x%x)\n", + op, iwl_get_cmd_string(mld->trans, + WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)), + pkt->hdr.group_id, pkt->hdr.cmd, + le16_to_cpu(pkt->hdr.sequence)); +} + +static void iwl_mld_rx_notif(struct iwl_mld *mld, + struct iwl_rx_cmd_buffer *rxb, + struct iwl_rx_packet *pkt) +{ + for (int i = 0; i < ARRAY_SIZE(iwl_mld_rx_handlers); i++) { + const struct iwl_rx_handler *rx_h = &iwl_mld_rx_handlers[i]; + struct iwl_async_handler_entry *entry; + + if (rx_h->cmd_id != WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd)) + continue; + + if (!iwl_mld_notif_is_valid(mld, pkt, rx_h)) + return; + + if (rx_h->context == RX_HANDLER_SYNC) { + rx_h->fn(mld, pkt); + break; + } + + entry = kzalloc(sizeof(*entry), GFP_ATOMIC); + /* we can't do much... */ + if (!entry) + return; + + /* Set the async handler entry */ + entry->rxb._page = rxb_steal_page(rxb); + entry->rxb._offset = rxb->_offset; + entry->rxb._rx_page_order = rxb->_rx_page_order; + + entry->rx_h = rx_h; + + /* Add it to the list and queue the work */ + spin_lock(&mld->async_handlers_lock); + list_add_tail(&entry->list, &mld->async_handlers_list); + spin_unlock(&mld->async_handlers_lock); + + wiphy_work_queue(mld->hw->wiphy, + &mld->async_handlers_wk); + + iwl_mld_log_async_handler_op(mld, "Queued", rxb); + break; + } + + iwl_notification_wait_notify(&mld->notif_wait, pkt); +} + +void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); + + if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD))) + iwl_mld_rx_mpdu(mld, napi, rxb, 0); + else if (cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE)) + iwl_mld_handle_frame_release_notif(mld, napi, pkt, 0); + else if (cmd_id == WIDE_ID(LEGACY_GROUP, BAR_FRAME_RELEASE)) + iwl_mld_handle_bar_frame_release_notif(mld, napi, pkt, 0); + else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP, + RX_QUEUES_NOTIFICATION))) + iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0); + else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, RX_NO_DATA_NOTIF)) + iwl_mld_rx_monitor_no_data(mld, napi, pkt, 0); + else + iwl_mld_rx_notif(mld, rxb, pkt); +} + +void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, unsigned int queue) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mld *mld = IWL_OP_MODE_GET_MLD(op_mode); + u16 cmd_id = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); + + if (unlikely(queue >= mld->trans->info.num_rxqs)) + return; + + if (likely(cmd_id == WIDE_ID(LEGACY_GROUP, REPLY_RX_MPDU_CMD))) + iwl_mld_rx_mpdu(mld, napi, rxb, queue); + else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP, + RX_QUEUES_NOTIFICATION))) + iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, queue); + else if (unlikely(cmd_id == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))) + iwl_mld_handle_frame_release_notif(mld, napi, pkt, queue); +} + +void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds) +{ + struct iwl_async_handler_entry *entry, *tmp; + + spin_lock_bh(&mld->async_handlers_lock); + list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { + bool match = false; + + for (int i = 0; i < n_cmds; i++) { + if (entry->rx_h->cmd_id == cmds[i]) { + match = true; + break; + } + } + + if (!match) + continue; + + iwl_mld_log_async_handler_op(mld, "Delete", &entry->rxb); + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } + spin_unlock_bh(&mld->async_handlers_lock); +} + +void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld *mld = + container_of(wk, struct iwl_mld, async_handlers_wk); + struct iwl_async_handler_entry *entry, *tmp; + LIST_HEAD(local_list); + + /* Sync with Rx path with a lock. Remove all the entries from this + * list, add them to a local one (lock free), and then handle them. + */ + spin_lock_bh(&mld->async_handlers_lock); + list_splice_init(&mld->async_handlers_list, &local_list); + spin_unlock_bh(&mld->async_handlers_lock); + + list_for_each_entry_safe(entry, tmp, &local_list, list) { + iwl_mld_log_async_handler_op(mld, "Handle", &entry->rxb); + entry->rx_h->fn(mld, rxb_addr(&entry->rxb)); + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } +} + +void iwl_mld_cancel_async_notifications(struct iwl_mld *mld) +{ + struct iwl_async_handler_entry *entry, *tmp; + + lockdep_assert_wiphy(mld->wiphy); + + wiphy_work_cancel(mld->wiphy, &mld->async_handlers_wk); + + spin_lock_bh(&mld->async_handlers_lock); + list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { + iwl_mld_log_async_handler_op(mld, "Purged", &entry->rxb); + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } + spin_unlock_bh(&mld->async_handlers_lock); +} + +void iwl_mld_cancel_notifications_of_object(struct iwl_mld *mld, + enum iwl_mld_object_type obj_type, + u32 obj_id) +{ + struct iwl_async_handler_entry *entry, *tmp; + LIST_HEAD(cancel_list); + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(obj_type == IWL_MLD_OBJECT_TYPE_NONE)) + return; + + /* Sync with RX path and remove matching entries from the async list */ + spin_lock_bh(&mld->async_handlers_lock); + list_for_each_entry_safe(entry, tmp, &mld->async_handlers_list, list) { + const struct iwl_rx_handler *rx_h = entry->rx_h; + + if (rx_h->obj_type != obj_type || WARN_ON(!rx_h->cancel)) + continue; + + if (rx_h->cancel(mld, rxb_addr(&entry->rxb), obj_id)) { + iwl_mld_log_async_handler_op(mld, "Cancel", &entry->rxb); + list_del(&entry->list); + list_add_tail(&entry->list, &cancel_list); + } + } + + spin_unlock_bh(&mld->async_handlers_lock); + + /* Free the matching entries outside of the spinlock */ + list_for_each_entry_safe(entry, tmp, &cancel_list, list) { + iwl_free_rxb(&entry->rxb); + list_del(&entry->list); + kfree(entry); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/notif.h b/drivers/net/wireless/intel/iwlwifi/mld/notif.h new file mode 100644 index 000000000000..adcdd9dec192 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/notif.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_notif_h__ +#define __iwl_mld_notif_h__ + +struct iwl_mld; + +void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb); + +void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, unsigned int queue); + +void iwl_mld_async_handlers_wk(struct wiphy *wiphy, struct wiphy_work *wk); + +void iwl_mld_cancel_async_notifications(struct iwl_mld *mld); + +enum iwl_mld_object_type { + IWL_MLD_OBJECT_TYPE_NONE, + IWL_MLD_OBJECT_TYPE_LINK, + IWL_MLD_OBJECT_TYPE_STA, + IWL_MLD_OBJECT_TYPE_VIF, + IWL_MLD_OBJECT_TYPE_ROC, + IWL_MLD_OBJECT_TYPE_SCAN, + IWL_MLD_OBJECT_TYPE_FTM_REQ, +}; + +void iwl_mld_cancel_notifications_of_object(struct iwl_mld *mld, + enum iwl_mld_object_type obj_type, + u32 obj_id); +void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds); + +#endif /* __iwl_mld_notif_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.c b/drivers/net/wireless/intel/iwlwifi/mld/phy.c new file mode 100644 index 000000000000..d5a32ee56b92 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <net/mac80211.h> + +#include "phy.h" +#include "hcmd.h" +#include "fw/api/phy-ctxt.h" + +int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld) +{ + int id; + unsigned long used = mld->used_phy_ids; + + for_each_clear_bit(id, &used, NUM_PHY_CTX) { + mld->used_phy_ids |= BIT(id); + return id; + } + + return -ENOSPC; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_fw_phy_id); + +struct iwl_mld_chanctx_usage_data { + struct iwl_mld *mld; + struct ieee80211_chanctx_conf *ctx; + bool use_def; +}; + +static bool iwl_mld_chanctx_fils_enabled(struct ieee80211_vif *vif, + struct ieee80211_chanctx_conf *ctx) +{ + if (vif->type != NL80211_IFTYPE_AP) + return false; + + return cfg80211_channel_is_psc(ctx->def.chan) || + (ctx->def.chan->band == NL80211_BAND_6GHZ && + ctx->def.width >= NL80211_CHAN_WIDTH_80); +} + +static void iwl_mld_chanctx_usage_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_chanctx_usage_data *data = _data; + struct ieee80211_bss_conf *link_conf; + int link_id; + + for_each_vif_active_link(vif, link_conf, link_id) { + if (rcu_access_pointer(link_conf->chanctx_conf) != data->ctx) + continue; + + if (vif->type == NL80211_IFTYPE_AP && link_conf->ftm_responder) + data->use_def = true; + + if (iwl_mld_chanctx_fils_enabled(vif, data->ctx)) + data->use_def = true; + } +} + +struct cfg80211_chan_def * +iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld_chanctx_usage_data data = { + .mld = mld, + .ctx = ctx, + }; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_chanctx_usage_iter, + &data); + + return data.use_def ? &ctx->def : &ctx->min_def; +} + +static u8 +iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width) +{ + switch (width) { + case NL80211_CHAN_WIDTH_20_NOHT: + case NL80211_CHAN_WIDTH_20: + return IWL_PHY_CHANNEL_MODE20; + case NL80211_CHAN_WIDTH_40: + return IWL_PHY_CHANNEL_MODE40; + case NL80211_CHAN_WIDTH_80: + return IWL_PHY_CHANNEL_MODE80; + case NL80211_CHAN_WIDTH_160: + return IWL_PHY_CHANNEL_MODE160; + case NL80211_CHAN_WIDTH_320: + return IWL_PHY_CHANNEL_MODE320; + default: + WARN(1, "Invalid channel width=%u", width); + return IWL_PHY_CHANNEL_MODE20; + } +} + +/* Maps the driver specific control channel position (relative to the center + * freq) definitions to the fw values + */ +u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef) +{ + int offs = chandef->chan->center_freq - chandef->center_freq1; + int abs_offs = abs(offs); + u8 ret; + + if (offs == 0) { + /* The FW is expected to check the control channel position only + * when in HT/VHT and the channel width is not 20MHz. Return + * this value as the default one. + */ + return 0; + } + + /* this results in a value 0-7, i.e. fitting into 0b0111 */ + ret = (abs_offs - 10) / 20; + /* But we need the value to be in 0b1011 because 0b0100 is + * IWL_PHY_CTRL_POS_ABOVE, so shift bit 2 up to land in + * IWL_PHY_CTRL_POS_OFFS_EXT (0b1000) + */ + ret = (ret & IWL_PHY_CTRL_POS_OFFS_MSK) | + ((ret & BIT(2)) << 1); + /* and add the above bit */ + ret |= (offs > 0) * IWL_PHY_CTRL_POS_ABOVE; + + return ret; +} + +int iwl_mld_phy_fw_action(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx, u32 action) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + struct cfg80211_chan_def *chandef = &phy->chandef; + struct iwl_phy_context_cmd cmd = { + .id_and_color = cpu_to_le32(phy->fw_id), + .action = cpu_to_le32(action), + .puncture_mask = cpu_to_le16(chandef->punctured), + /* Channel info */ + .ci.channel = cpu_to_le32(chandef->chan->hw_value), + .ci.band = iwl_mld_nl80211_band_to_fw(chandef->chan->band), + .ci.width = iwl_mld_nl80211_width_to_fw(chandef->width), + .ci.ctrl_pos = iwl_mld_get_fw_ctrl_pos(chandef), + }; + int ret; + + if (ctx->ap.chan) { + cmd.sbb_bandwidth = + iwl_mld_nl80211_width_to_fw(ctx->ap.width); + cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap); + } + + ret = iwl_mld_send_cmd_pdu(mld, PHY_CONTEXT_CMD, &cmd); + if (ret) + IWL_ERR(mld, "Failed to send PHY_CONTEXT_CMD ret = %d\n", ret); + + return ret; +} + +static u32 iwl_mld_get_phy_config(struct iwl_mld *mld) +{ + u32 phy_config = ~(FW_PHY_CFG_TX_CHAIN | + FW_PHY_CFG_RX_CHAIN); + u32 valid_rx_ant = iwl_mld_get_valid_rx_ant(mld); + u32 valid_tx_ant = iwl_mld_get_valid_tx_ant(mld); + + phy_config |= valid_tx_ant << FW_PHY_CFG_TX_CHAIN_POS | + valid_rx_ant << FW_PHY_CFG_RX_CHAIN_POS; + + return mld->fw->phy_config & phy_config; +} + +int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld) +{ + const struct iwl_tlv_calib_ctrl *default_calib = + &mld->fw->default_calib[IWL_UCODE_REGULAR]; + struct iwl_phy_cfg_cmd_v3 cmd = { + .phy_cfg = cpu_to_le32(iwl_mld_get_phy_config(mld)), + .calib_control.event_trigger = default_calib->event_trigger, + .calib_control.flow_trigger = default_calib->flow_trigger, + .phy_specific_cfg = mld->fwrt.phy_filters, + }; + + IWL_INFO(mld, "Sending Phy CFG command: 0x%x\n", cmd.phy_cfg); + + return iwl_mld_send_cmd_pdu(mld, PHY_CONFIGURATION_CMD, &cmd); +} + +void iwl_mld_update_phy_chandef(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + struct cfg80211_chan_def *chandef = + iwl_mld_get_chandef_from_chanctx(mld, ctx); + + phy->chandef = *chandef; + iwl_mld_phy_fw_action(mld, ctx, FW_CTXT_ACTION_MODIFY); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/phy.h b/drivers/net/wireless/intel/iwlwifi/mld/phy.h new file mode 100644 index 000000000000..0deaf179f07c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/phy.h @@ -0,0 +1,60 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_phy_h__ +#define __iwl_mld_phy_h__ + +#include "mld.h" + +/** + * struct iwl_mld_phy - PHY configuration parameters + * + * @fw_id: fw id of the phy. + * @chandef: the last chandef that mac80211 configured the driver + * with. Used to detect a no-op when the chanctx changes. + * @channel_load_by_us: channel load on this channel caused by + * the NIC itself, as indicated by firmware + * @avg_channel_load_not_by_us: averaged channel load on this channel caused by + * others. This value is invalid when in EMLSR (due to FW limitations) + * @mld: pointer to the MLD context + */ +struct iwl_mld_phy { + /* Add here fields that need clean up on hw restart */ + struct_group(zeroed_on_hw_restart, + u8 fw_id; + struct cfg80211_chan_def chandef; + ); + /* And here fields that survive a hw restart */ + u32 channel_load_by_us; + u32 avg_channel_load_not_by_us; + struct iwl_mld *mld; +}; + +static inline struct iwl_mld_phy * +iwl_mld_phy_from_mac80211(struct ieee80211_chanctx_conf *channel) +{ + return (void *)channel->drv_priv; +} + +/* Cleanup function for struct iwl_mld_phy, will be called in restart */ +static inline void +iwl_mld_cleanup_phy(struct iwl_mld *mld, struct iwl_mld_phy *phy) +{ + CLEANUP_STRUCT(phy); +} + +int iwl_mld_allocate_fw_phy_id(struct iwl_mld *mld); +int iwl_mld_phy_fw_action(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx, u32 action); +struct cfg80211_chan_def * +iwl_mld_get_chandef_from_chanctx(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx); +u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef); + +int iwl_mld_send_phy_cfg_cmd(struct iwl_mld *mld); + +void iwl_mld_update_phy_chandef(struct iwl_mld *mld, + struct ieee80211_chanctx_conf *ctx); + +#endif /* __iwl_mld_phy_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.c b/drivers/net/wireless/intel/iwlwifi/mld/power.c new file mode 100644 index 000000000000..8cc276041360 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.c @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <net/mac80211.h> + +#include "mld.h" +#include "hcmd.h" +#include "power.h" +#include "iface.h" +#include "link.h" +#include "constants.h" + +static void iwl_mld_vif_ps_iterator(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + bool *ps_enable = (bool *)data; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (vif->type != NL80211_IFTYPE_STATION) + return; + + *ps_enable &= !mld_vif->ps_disabled; +} + +int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3) +{ + struct iwl_device_power_cmd cmd = {}; + bool enable_ps = false; + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) { + enable_ps = true; + + /* Disable power save if any STA interface has + * power save turned off + */ + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_ps_iterator, + &enable_ps); + } + + if (enable_ps) + cmd.flags |= + cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK); + + if (d3) + cmd.flags |= + cpu_to_le16(DEVICE_POWER_FLAGS_NO_SLEEP_TILL_D3_MSK); + + IWL_DEBUG_POWER(mld, + "Sending device power command with flags = 0x%X\n", + cmd.flags); + + return iwl_mld_send_cmd_pdu(mld, POWER_TABLE_CMD, &cmd); +} + +int iwl_mld_enable_beacon_filter(struct iwl_mld *mld, + const struct ieee80211_bss_conf *link_conf, + bool d3) +{ + struct iwl_beacon_filter_cmd cmd = { + IWL_BF_CMD_CONFIG_DEFAULTS, + .bf_enable_beacon_filter = cpu_to_le32(1), + .ba_enable_beacon_abort = cpu_to_le32(1), + }; + + if (ieee80211_vif_type_p2p(link_conf->vif) != NL80211_IFTYPE_STATION) + return 0; + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (iwl_mld_vif_from_mac80211(link_conf->vif)->disable_bf) + return 0; +#endif + + if (link_conf->cqm_rssi_thold) { + cmd.bf_energy_delta = + cpu_to_le32(link_conf->cqm_rssi_hyst); + /* fw uses an absolute value for this */ + cmd.bf_roaming_state = + cpu_to_le32(-link_conf->cqm_rssi_thold); + } + + if (d3) + cmd.ba_escape_timer = cpu_to_le32(IWL_BA_ESCAPE_TIMER_D3); + + return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD, + &cmd); +} + +int iwl_mld_disable_beacon_filter(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_beacon_filter_cmd cmd = {}; + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION) + return 0; + + return iwl_mld_send_cmd_pdu(mld, REPLY_BEACON_FILTERING_CMD, + &cmd); +} + +static bool iwl_mld_power_is_radar(struct iwl_mld *mld, + const struct ieee80211_bss_conf *link_conf) +{ + const struct ieee80211_chanctx_conf *chanctx_conf; + + chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf); + + if (WARN_ON(!chanctx_conf)) + return false; + + return chanctx_conf->def.chan->flags & IEEE80211_CHAN_RADAR; +} + +static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld, + struct iwl_mld_link *link, + struct iwl_mac_power_cmd *cmd, + bool ps_poll) +{ + bool tid_found = false; + + cmd->rx_data_timeout_uapsd = + cpu_to_le32(IWL_MLD_UAPSD_RX_DATA_TIMEOUT); + cmd->tx_data_timeout_uapsd = + cpu_to_le32(IWL_MLD_UAPSD_TX_DATA_TIMEOUT); + + /* set advanced pm flag with no uapsd ACs to enable ps-poll */ + if (ps_poll) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK); + return; + } + + for (enum ieee80211_ac_numbers ac = IEEE80211_AC_VO; + ac <= IEEE80211_AC_BK; + ac++) { + if (!link->queue_params[ac].uapsd) + continue; + + cmd->flags |= + cpu_to_le16(POWER_FLAGS_ADVANCE_PM_ENA_MSK | + POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK); + + cmd->uapsd_ac_flags |= BIT(ac); + + /* QNDP TID - the highest TID with no admission control */ + if (!tid_found && !link->queue_params[ac].acm) { + tid_found = true; + switch (ac) { + case IEEE80211_AC_VO: + cmd->qndp_tid = 6; + break; + case IEEE80211_AC_VI: + cmd->qndp_tid = 5; + break; + case IEEE80211_AC_BE: + cmd->qndp_tid = 0; + break; + case IEEE80211_AC_BK: + cmd->qndp_tid = 1; + break; + } + } + } + + if (cmd->uapsd_ac_flags == (BIT(IEEE80211_AC_VO) | + BIT(IEEE80211_AC_VI) | + BIT(IEEE80211_AC_BE) | + BIT(IEEE80211_AC_BK))) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_SNOOZE_ENA_MSK); + cmd->snooze_interval = cpu_to_le16(IWL_MLD_PS_SNOOZE_INTERVAL); + cmd->snooze_window = cpu_to_le16(IWL_MLD_PS_SNOOZE_WINDOW); + } + + cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len; +} + +static void +iwl_mld_power_config_skip_dtim(struct iwl_mld *mld, + const struct ieee80211_bss_conf *link_conf, + struct iwl_mac_power_cmd *cmd) +{ + unsigned int dtimper_tu; + unsigned int dtimper; + unsigned int skip; + + dtimper = link_conf->dtim_period ?: 1; + dtimper_tu = dtimper * link_conf->beacon_int; + + if (dtimper >= 10 || iwl_mld_power_is_radar(mld, link_conf)) + return; + + if (WARN_ON(!dtimper_tu)) + return; + + /* configure skip over dtim up to 900 TU DTIM interval */ + skip = max_t(int, 1, 900 / dtimper_tu); + + cmd->skip_dtim_periods = skip; + cmd->flags |= cpu_to_le16(POWER_FLAGS_SKIP_OVER_DTIM_MSK); +} + +#define POWER_KEEP_ALIVE_PERIOD_SEC 25 +static void iwl_mld_power_build_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mac_power_cmd *cmd, + bool d3) +{ + int dtimper, bi; + int keep_alive; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct ieee80211_bss_conf *link_conf = &vif->bss_conf; + struct iwl_mld_link *link = &mld_vif->deflink; + bool ps_poll = false; + + cmd->id_and_color = cpu_to_le32(mld_vif->fw_id); + + if (ieee80211_vif_is_mld(vif)) { + int link_id; + + if (WARN_ON(!vif->active_links)) + return; + + /* The firmware consumes one single configuration for the vif + * and can't differentiate between links, just pick the lowest + * link_id's configuration and use that. + */ + link_id = __ffs(vif->active_links); + link_conf = link_conf_dereference_check(vif, link_id); + link = iwl_mld_link_dereference_check(mld_vif, link_id); + + if (WARN_ON(!link_conf || !link)) + return; + } + dtimper = link_conf->dtim_period; + bi = link_conf->beacon_int; + + /* Regardless of power management state the driver must set + * keep alive period. FW will use it for sending keep alive NDPs + * immediately after association. Check that keep alive period + * is at least 3 * DTIM + */ + keep_alive = DIV_ROUND_UP(ieee80211_tu_to_usec(3 * dtimper * bi), + USEC_PER_SEC); + keep_alive = max(keep_alive, POWER_KEEP_ALIVE_PERIOD_SEC); + cmd->keep_alive_seconds = cpu_to_le16(keep_alive); + + if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_SAVE_ENA_MSK); + + if (!vif->cfg.ps || iwl_mld_tdls_sta_count(mld) > 0) + return; + + cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK); + + if (iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0) >= 2) + cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK); + + /* firmware supports LPRX for beacons at rate 1 Mbps or 6 Mbps only */ + if (link_conf->beacon_rate && + (link_conf->beacon_rate->bitrate == 10 || + link_conf->beacon_rate->bitrate == 60)) { + cmd->flags |= cpu_to_le16(POWER_FLAGS_LPRX_ENA_MSK); + cmd->lprx_rssi_threshold = POWER_LPRX_RSSI_THRESHOLD; + } + + if (d3) { + iwl_mld_power_config_skip_dtim(mld, link_conf, cmd); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_WOWLAN_PS_TX_DATA_TIMEOUT); + } else if (iwl_mld_vif_low_latency(mld_vif) && vif->p2p) { + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_SHORT_PS_TX_DATA_TIMEOUT); + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_SHORT_PS_RX_DATA_TIMEOUT); + } else { + cmd->rx_data_timeout = + cpu_to_le32(IWL_MLD_DEFAULT_PS_RX_DATA_TIMEOUT); + cmd->tx_data_timeout = + cpu_to_le32(IWL_MLD_DEFAULT_PS_TX_DATA_TIMEOUT); + } + + /* uAPSD is only enabled for specific certifications. For those cases, + * mac80211 will allow uAPSD. Always call iwl_mld_power_configure_uapsd + * which will look at what mac80211 is saying. + */ +#ifdef CONFIG_IWLWIFI_DEBUGFS + ps_poll = mld_vif->use_ps_poll; +#endif + iwl_mld_power_configure_uapsd(mld, link, cmd, ps_poll); +} + +int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif, + bool d3) +{ + struct iwl_mac_power_cmd cmd = {}; + + iwl_mld_power_build_cmd(mld, vif, &cmd, d3); + + return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd); +} + +static void +iwl_mld_tpe_sta_cmd_data(struct iwl_txpower_constraints_cmd *cmd, + const struct ieee80211_bss_conf *link) +{ + u8 i; + + /* NOTE: the 0 here is IEEE80211_TPE_CAT_6GHZ_DEFAULT, + * we fully ignore IEEE80211_TPE_CAT_6GHZ_SUBORDINATE + */ + + BUILD_BUG_ON(ARRAY_SIZE(cmd->psd_pwr) != + ARRAY_SIZE(link->tpe.psd_local[0].power)); + + /* if not valid, mac80211 puts default (max value) */ + for (i = 0; i < ARRAY_SIZE(cmd->psd_pwr); i++) + cmd->psd_pwr[i] = min(link->tpe.psd_local[0].power[i], + link->tpe.psd_reg_client[0].power[i]); + + BUILD_BUG_ON(ARRAY_SIZE(cmd->eirp_pwr) != + ARRAY_SIZE(link->tpe.max_local[0].power)); + + for (i = 0; i < ARRAY_SIZE(cmd->eirp_pwr); i++) + cmd->eirp_pwr[i] = min(link->tpe.max_local[0].power[i], + link->tpe.max_reg_client[0].power[i]); +} + +void +iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_txpower_constraints_cmd cmd = {}; + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (!mld_link->active) + return; + + if (link->chanreq.oper.chan->band != NL80211_BAND_6GHZ) + return; + + cmd.link_id = cpu_to_le16(mld_link->fw_id); + memset(cmd.psd_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.psd_pwr)); + memset(cmd.eirp_pwr, DEFAULT_TPE_TX_POWER, sizeof(cmd.eirp_pwr)); + + if (vif->type == NL80211_IFTYPE_AP) { + cmd.ap_type = cpu_to_le16(IWL_6GHZ_AP_TYPE_VLP); + } else if (link->power_type == IEEE80211_REG_UNSET_AP) { + return; + } else { + cmd.ap_type = cpu_to_le16(link->power_type - 1); + iwl_mld_tpe_sta_cmd_data(&cmd, link); + } + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(PHY_OPS_GROUP, + AP_TX_POWER_CONSTRAINTS_CMD), + &cmd); + if (ret) + IWL_ERR(mld, + "failed to send AP_TX_POWER_CONSTRAINTS_CMD (%d)\n", + ret); +} + +int iwl_mld_set_tx_power(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + s16 tx_power) +{ + u32 cmd_id = REDUCE_TX_POWER_CMD; + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf); + u16 u_tx_power = tx_power == IWL_DEFAULT_MAX_TX_POWER ? + IWL_DEV_MAX_TX_POWER : 8 * tx_power; + struct iwl_dev_tx_power_cmd cmd = { + /* Those fields sit on the same place for v9 and v10 */ + .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_LINK), + .common.pwr_restriction = cpu_to_le16(u_tx_power), + }; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + int len = sizeof(cmd.common); + + if (WARN_ON(!mld_link)) + return -ENODEV; + + cmd.common.link_id = cpu_to_le32(mld_link->fw_id); + + if (cmd_ver == 10) + len += sizeof(cmd.v10); + else if (cmd_ver == 9) + len += sizeof(cmd.v9); + + return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/power.h b/drivers/net/wireless/intel/iwlwifi/mld/power.h new file mode 100644 index 000000000000..05ce27bef106 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/power.h @@ -0,0 +1,33 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_power_h__ +#define __iwl_mld_power_h__ + +#include <net/mac80211.h> + +#include "mld.h" + +int iwl_mld_update_device_power(struct iwl_mld *mld, bool d3); + +int iwl_mld_enable_beacon_filter(struct iwl_mld *mld, + const struct ieee80211_bss_conf *link_conf, + bool d3); + +int iwl_mld_disable_beacon_filter(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif, + bool d3); + +void +iwl_mld_send_ap_tx_power_constraint_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_set_tx_power(struct iwl_mld *mld, + struct ieee80211_bss_conf *link_conf, + s16 tx_power); + +#endif /* __iwl_mld_power_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.c b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c new file mode 100644 index 000000000000..5ee38fc168c1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.c @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2025 Intel Corporation + */ + +#include "mld.h" +#include "iwl-debug.h" +#include "hcmd.h" +#include "ptp.h" +#include <linux/timekeeping.h> + +/* The scaled_ppm parameter is ppm (parts per million) with a 16-bit fractional + * part, which means that a value of 1 in one of those fields actually means + * 2^-16 ppm, and 2^16=65536 is 1 ppm. + */ +#define PTP_SCALE_FACTOR 65536000000ULL + +#define IWL_PTP_GP2_WRAP 0x100000000ULL +#define IWL_PTP_WRAP_TIME (3600 * HZ) +#define IWL_PTP_WRAP_THRESHOLD_USEC (5000) + +static int iwl_mld_get_systime(struct iwl_mld *mld, u32 *gp2) +{ + *gp2 = iwl_read_prph(mld->trans, mld->trans->mac_cfg->base->gp2_reg_addr); + + if (*gp2 == 0x5a5a5a5a) + return -EINVAL; + + return 0; +} + +static void iwl_mld_ptp_update_new_read(struct iwl_mld *mld, u32 gp2) +{ + IWL_DEBUG_PTP(mld, "PTP: last_gp2=%u, new gp2 read=%u\n", + mld->ptp_data.last_gp2, gp2); + + /* If the difference is above the threshold, assume it's a wraparound. + * Otherwise assume it's an old read and ignore it. + */ + if (gp2 < mld->ptp_data.last_gp2) { + if (mld->ptp_data.last_gp2 - gp2 < + IWL_PTP_WRAP_THRESHOLD_USEC) { + IWL_DEBUG_PTP(mld, + "PTP: ignore old read (gp2=%u, last_gp2=%u)\n", + gp2, mld->ptp_data.last_gp2); + return; + } + + mld->ptp_data.wrap_counter++; + IWL_DEBUG_PTP(mld, + "PTP: wraparound detected (new counter=%u)\n", + mld->ptp_data.wrap_counter); + } + + mld->ptp_data.last_gp2 = gp2; + schedule_delayed_work(&mld->ptp_data.dwork, IWL_PTP_WRAP_TIME); +} + +u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns) +{ + struct ptp_data *data = &mld->ptp_data; + u64 scale_time_gp2_ns = mld->ptp_data.scale_update_gp2 * NSEC_PER_USEC; + u64 res; + u64 diff; + s64 scaled_diff; + + lockdep_assert_held(&data->lock); + + iwl_mld_ptp_update_new_read(mld, + div64_u64(base_time_ns, NSEC_PER_USEC)); + + base_time_ns = base_time_ns + + (data->wrap_counter * IWL_PTP_GP2_WRAP * NSEC_PER_USEC); + + /* It is possible that a GP2 timestamp was received from fw before the + * last scale update. + */ + if (base_time_ns < scale_time_gp2_ns) { + diff = scale_time_gp2_ns - base_time_ns; + scaled_diff = -mul_u64_u64_div_u64(diff, + data->scaled_freq, + PTP_SCALE_FACTOR); + } else { + diff = base_time_ns - scale_time_gp2_ns; + scaled_diff = mul_u64_u64_div_u64(diff, + data->scaled_freq, + PTP_SCALE_FACTOR); + } + + IWL_DEBUG_PTP(mld, "base_time=%llu diff ns=%llu scaled_diff_ns=%lld\n", + (unsigned long long)base_time_ns, + (unsigned long long)diff, (long long)scaled_diff); + + res = data->scale_update_adj_time_ns + data->delta + scaled_diff; + + IWL_DEBUG_PTP(mld, "scale_update_ns=%llu delta=%lld adj=%llu\n", + (unsigned long long)data->scale_update_adj_time_ns, + (long long)data->delta, (unsigned long long)res); + return res; +} + +static int iwl_mld_ptp_gettime(struct ptp_clock_info *ptp, + struct timespec64 *ts) +{ + struct iwl_mld *mld = container_of(ptp, struct iwl_mld, + ptp_data.ptp_clock_info); + struct ptp_data *data = &mld->ptp_data; + u32 gp2; + u64 ns; + + if (iwl_mld_get_systime(mld, &gp2)) { + IWL_DEBUG_PTP(mld, "PTP: gettime: failed to read systime\n"); + return -EIO; + } + + spin_lock_bh(&data->lock); + ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC); + spin_unlock_bh(&data->lock); + + *ts = ns_to_timespec64(ns); + return 0; +} + +static int iwl_mld_ptp_adjtime(struct ptp_clock_info *ptp, s64 delta) +{ + struct iwl_mld *mld = container_of(ptp, struct iwl_mld, + ptp_data.ptp_clock_info); + struct ptp_data *data = &mld->ptp_data; + + spin_lock_bh(&data->lock); + data->delta += delta; + IWL_DEBUG_PTP(mld, "delta=%lld, new delta=%lld\n", (long long)delta, + (long long)data->delta); + spin_unlock_bh(&data->lock); + return 0; +} + +static int iwl_mld_ptp_adjfine(struct ptp_clock_info *ptp, long scaled_ppm) +{ + struct iwl_mld *mld = container_of(ptp, struct iwl_mld, + ptp_data.ptp_clock_info); + struct ptp_data *data = &mld->ptp_data; + u32 gp2; + + /* Must call iwl_mld_ptp_get_adj_time() before updating + * data->scale_update_gp2 or data->scaled_freq since + * scale_update_adj_time_ns should reflect the previous scaled_freq. + */ + if (iwl_mld_get_systime(mld, &gp2)) { + IWL_DEBUG_PTP(mld, "adjfine: failed to read systime\n"); + return -EBUSY; + } + + spin_lock_bh(&data->lock); + data->scale_update_adj_time_ns = + iwl_mld_ptp_get_adj_time(mld, gp2 * NSEC_PER_USEC); + data->scale_update_gp2 = gp2; + + /* scale_update_adj_time_ns now relects the configured delta, the + * wrap_counter and the previous scaled frequency. Thus delta and + * wrap_counter should be reset, and the scale frequency is updated + * to the new frequency. + */ + data->delta = 0; + data->wrap_counter = 0; + data->scaled_freq = PTP_SCALE_FACTOR + scaled_ppm; + IWL_DEBUG_PTP(mld, "adjfine: scaled_ppm=%ld new=%llu\n", + scaled_ppm, (unsigned long long)data->scaled_freq); + spin_unlock_bh(&data->lock); + return 0; +} + +static void iwl_mld_ptp_work(struct work_struct *wk) +{ + struct iwl_mld *mld = container_of(wk, struct iwl_mld, + ptp_data.dwork.work); + struct ptp_data *data = &mld->ptp_data; + u32 gp2; + + spin_lock_bh(&data->lock); + if (!iwl_mld_get_systime(mld, &gp2)) + iwl_mld_ptp_update_new_read(mld, gp2); + else + IWL_DEBUG_PTP(mld, "PTP work: failed to read GP2\n"); + spin_unlock_bh(&data->lock); +} + +static int +iwl_mld_get_crosstimestamp_fw(struct iwl_mld *mld, u32 *gp2, u64 *sys_time) +{ + struct iwl_synced_time_cmd synced_time_cmd = { + .operation = cpu_to_le32(IWL_SYNCED_TIME_OPERATION_READ_BOTH) + }; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(DATA_PATH_GROUP, WNM_PLATFORM_PTM_REQUEST_CMD), + .flags = CMD_WANT_SKB, + .data[0] = &synced_time_cmd, + .len[0] = sizeof(synced_time_cmd), + }; + struct iwl_synced_time_rsp *resp; + struct iwl_rx_packet *pkt; + int ret; + u64 gp2_10ns; + + wiphy_lock(mld->wiphy); + ret = iwl_mld_send_cmd(mld, &cmd); + wiphy_unlock(mld->wiphy); + if (ret) + return ret; + + pkt = cmd.resp_pkt; + + if (iwl_rx_packet_payload_len(pkt) != sizeof(*resp)) { + IWL_DEBUG_PTP(mld, "PTP: Invalid PTM command response\n"); + iwl_free_resp(&cmd); + return -EIO; + } + + resp = (void *)pkt->data; + + gp2_10ns = (u64)le32_to_cpu(resp->gp2_timestamp_hi) << 32 | + le32_to_cpu(resp->gp2_timestamp_lo); + *gp2 = div_u64(gp2_10ns, 100); + + *sys_time = (u64)le32_to_cpu(resp->platform_timestamp_hi) << 32 | + le32_to_cpu(resp->platform_timestamp_lo); + + iwl_free_resp(&cmd); + return ret; +} + +static int +iwl_mld_phc_get_crosstimestamp(struct ptp_clock_info *ptp, + struct system_device_crosststamp *xtstamp) +{ + struct iwl_mld *mld = container_of(ptp, struct iwl_mld, + ptp_data.ptp_clock_info); + struct ptp_data *data = &mld->ptp_data; + int ret = 0; + /* Raw value read from GP2 register in usec */ + u32 gp2; + /* GP2 value in ns*/ + s64 gp2_ns; + /* System (wall) time */ + ktime_t sys_time; + + memset(xtstamp, 0, sizeof(struct system_device_crosststamp)); + + ret = iwl_mld_get_crosstimestamp_fw(mld, &gp2, &sys_time); + if (ret) { + IWL_DEBUG_PTP(mld, + "PTP: fw get_crosstimestamp failed (ret=%d)\n", + ret); + return ret; + } + + spin_lock_bh(&data->lock); + gp2_ns = iwl_mld_ptp_get_adj_time(mld, (u64)gp2 * NSEC_PER_USEC); + spin_unlock_bh(&data->lock); + + IWL_DEBUG_PTP(mld, + "Got Sync Time: GP2:%u, last_GP2: %u, GP2_ns: %lld, sys_time: %lld\n", + gp2, mld->ptp_data.last_gp2, gp2_ns, (s64)sys_time); + + /* System monotonic raw time is not used */ + xtstamp->device = ns_to_ktime(gp2_ns); + xtstamp->sys_realtime = sys_time; + + return ret; +} + +void iwl_mld_ptp_init(struct iwl_mld *mld) +{ + if (WARN_ON(mld->ptp_data.ptp_clock)) + return; + + spin_lock_init(&mld->ptp_data.lock); + INIT_DELAYED_WORK(&mld->ptp_data.dwork, iwl_mld_ptp_work); + + mld->ptp_data.ptp_clock_info.owner = THIS_MODULE; + mld->ptp_data.ptp_clock_info.gettime64 = iwl_mld_ptp_gettime; + mld->ptp_data.ptp_clock_info.max_adj = 0x7fffffff; + mld->ptp_data.ptp_clock_info.adjtime = iwl_mld_ptp_adjtime; + mld->ptp_data.ptp_clock_info.adjfine = iwl_mld_ptp_adjfine; + mld->ptp_data.scaled_freq = PTP_SCALE_FACTOR; + mld->ptp_data.ptp_clock_info.getcrosststamp = + iwl_mld_phc_get_crosstimestamp; + + /* Give a short 'friendly name' to identify the PHC clock */ + snprintf(mld->ptp_data.ptp_clock_info.name, + sizeof(mld->ptp_data.ptp_clock_info.name), + "%s", "iwlwifi-PTP"); + + mld->ptp_data.ptp_clock = + ptp_clock_register(&mld->ptp_data.ptp_clock_info, mld->dev); + + if (IS_ERR_OR_NULL(mld->ptp_data.ptp_clock)) { + IWL_ERR(mld, "Failed to register PHC clock (%ld)\n", + PTR_ERR(mld->ptp_data.ptp_clock)); + mld->ptp_data.ptp_clock = NULL; + } else { + IWL_INFO(mld, "Registered PHC clock: %s, with index: %d\n", + mld->ptp_data.ptp_clock_info.name, + ptp_clock_index(mld->ptp_data.ptp_clock)); + } +} + +void iwl_mld_ptp_remove(struct iwl_mld *mld) +{ + if (mld->ptp_data.ptp_clock) { + IWL_INFO(mld, "Unregistering PHC clock: %s, with index: %d\n", + mld->ptp_data.ptp_clock_info.name, + ptp_clock_index(mld->ptp_data.ptp_clock)); + + ptp_clock_unregister(mld->ptp_data.ptp_clock); + mld->ptp_data.ptp_clock = NULL; + mld->ptp_data.last_gp2 = 0; + mld->ptp_data.wrap_counter = 0; + cancel_delayed_work_sync(&mld->ptp_data.dwork); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/ptp.h b/drivers/net/wireless/intel/iwlwifi/mld/ptp.h new file mode 100644 index 000000000000..f3d18dd304e5 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/ptp.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2025 Intel Corporation + */ +#ifndef __iwl_mld_ptp_h__ +#define __iwl_mld_ptp_h__ + +#include <linux/ptp_clock_kernel.h> + +/** + * struct ptp_data - PTP hardware clock data + * + * @ptp_clock: struct ptp_clock pointer returned by the ptp_clock_register() + * function. + * @ptp_clock_info: struct ptp_clock_info that describes a PTP hardware clock + * @lock: protects the time adjustments data + * @delta: delta between hardware clock and ptp clock in nanoseconds + * @scale_update_gp2: GP2 time when the scale was last updated + * @scale_update_adj_time_ns: adjusted time when the scale was last updated, + * in nanoseconds + * @scaled_freq: clock frequency offset, scaled to 65536000000 + * @last_gp2: the last GP2 reading from the hardware, used for tracking GP2 + * wraparounds + * @wrap_counter: number of wraparounds since scale_update_adj_time_ns + * @dwork: worker scheduled every 1 hour to detect workarounds + */ +struct ptp_data { + struct ptp_clock *ptp_clock; + struct ptp_clock_info ptp_clock_info; + + spinlock_t lock; + s64 delta; + u32 scale_update_gp2; + u64 scale_update_adj_time_ns; + u64 scaled_freq; + u32 last_gp2; + u32 wrap_counter; + struct delayed_work dwork; +}; + +void iwl_mld_ptp_init(struct iwl_mld *mld); +void iwl_mld_ptp_remove(struct iwl_mld *mld); +u64 iwl_mld_ptp_get_adj_time(struct iwl_mld *mld, u64 base_time_ns); + +#endif /* __iwl_mld_ptp_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c new file mode 100644 index 000000000000..326c300470ea --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.c @@ -0,0 +1,393 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include <linux/dmi.h> + +#include "fw/regulatory.h" +#include "fw/acpi.h" +#include "fw/uefi.h" + +#include "regulatory.h" +#include "mld.h" +#include "hcmd.h" + +void iwl_mld_get_bios_tables(struct iwl_mld *mld) +{ + int ret; + + iwl_acpi_get_guid_lock_status(&mld->fwrt); + + ret = iwl_bios_get_ppag_table(&mld->fwrt); + if (ret < 0) { + IWL_DEBUG_RADIO(mld, + "PPAG BIOS table invalid or unavailable. (%d)\n", + ret); + } + + ret = iwl_bios_get_wrds_table(&mld->fwrt); + if (ret < 0) { + IWL_DEBUG_RADIO(mld, + "WRDS SAR BIOS table invalid or unavailable. (%d)\n", + ret); + + /* If not available, don't fail and don't bother with EWRD and + * WGDS + */ + + if (!iwl_bios_get_wgds_table(&mld->fwrt)) { + /* If basic SAR is not available, we check for WGDS, + * which should *not* be available either. If it is + * available, issue an error, because we can't use SAR + * Geo without basic SAR. + */ + IWL_ERR(mld, "BIOS contains WGDS but no WRDS\n"); + } + + } else { + ret = iwl_bios_get_ewrd_table(&mld->fwrt); + /* If EWRD is not available, we can still use + * WRDS, so don't fail. + */ + if (ret < 0) + IWL_DEBUG_RADIO(mld, + "EWRD SAR BIOS table invalid or unavailable. (%d)\n", + ret); + + ret = iwl_bios_get_wgds_table(&mld->fwrt); + if (ret < 0) + IWL_DEBUG_RADIO(mld, + "Geo SAR BIOS table invalid or unavailable. (%d)\n", + ret); + /* we don't fail if the table is not available */ + } + + iwl_uefi_get_uats_table(mld->trans, &mld->fwrt); + + iwl_bios_get_phy_filters(&mld->fwrt); +} + +static int iwl_mld_geo_sar_init(struct iwl_mld *mld) +{ + u32 cmd_id = WIDE_ID(PHY_OPS_GROUP, PER_CHAIN_LIMIT_OFFSET_CMD); + union iwl_geo_tx_power_profiles_cmd cmd; + u16 len; + u32 n_bands; + __le32 sk = cpu_to_le32(0); + int ret; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + + BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, ops) != + offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, ops)); + + cmd.v4.ops = cpu_to_le32(IWL_PER_CHAIN_OFFSET_SET_TABLES); + + /* Only set to South Korea if the table revision is 1 */ + if (mld->fwrt.geo_rev == 1) + sk = cpu_to_le32(1); + + if (cmd_ver == 5) { + len = sizeof(cmd.v5); + n_bands = ARRAY_SIZE(cmd.v5.table[0]); + cmd.v5.table_revision = sk; + } else if (cmd_ver == 4) { + len = sizeof(cmd.v4); + n_bands = ARRAY_SIZE(cmd.v4.table[0]); + cmd.v4.table_revision = sk; + } else { + return -EOPNOTSUPP; + } + + BUILD_BUG_ON(offsetof(struct iwl_geo_tx_power_profiles_cmd_v4, table) != + offsetof(struct iwl_geo_tx_power_profiles_cmd_v5, table)); + /* the table is at the same position for all versions, so set use v4 */ + ret = iwl_sar_geo_fill_table(&mld->fwrt, &cmd.v4.table[0][0], + n_bands, BIOS_GEO_MAX_PROFILE_NUM); + + /* It is a valid scenario to not support SAR, or miss wgds table, + * but in that case there is no need to send the command. + */ + if (ret) + return 0; + + return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len); +} + +int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b) +{ + u32 cmd_id = REDUCE_TX_POWER_CMD; + struct iwl_dev_tx_power_cmd cmd = { + .common.set_mode = cpu_to_le32(IWL_TX_POWER_MODE_SET_CHAINS), + }; + __le16 *per_chain; + int ret; + u16 len = sizeof(cmd.common); + u32 n_subbands; + u8 cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, + IWL_FW_CMD_VER_UNKNOWN); + + if (cmd_ver == 10) { + len += sizeof(cmd.v10); + n_subbands = IWL_NUM_SUB_BANDS_V2; + per_chain = &cmd.v10.per_chain[0][0][0]; + cmd.v10.flags = + cpu_to_le32(mld->fwrt.reduced_power_flags); + } else if (cmd_ver == 9) { + len += sizeof(cmd.v9); + n_subbands = IWL_NUM_SUB_BANDS_V1; + per_chain = &cmd.v9.per_chain[0][0]; + } else { + return -EOPNOTSUPP; + } + + /* TODO: CDB - support IWL_NUM_CHAIN_TABLES_V2 */ + ret = iwl_sar_fill_profile(&mld->fwrt, per_chain, + IWL_NUM_CHAIN_TABLES, + n_subbands, prof_a, prof_b); + /* return on error or if the profile is disabled (positive number) */ + if (ret) + return ret; + + return iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, len); +} + +int iwl_mld_init_sar(struct iwl_mld *mld) +{ + int chain_a_prof = 1; + int chain_b_prof = 1; + int ret; + + /* If no profile was chosen by the user yet, choose profile 1 (WRDS) as + * default for both chains + */ + if (mld->fwrt.sar_chain_a_profile && mld->fwrt.sar_chain_b_profile) { + chain_a_prof = mld->fwrt.sar_chain_a_profile; + chain_b_prof = mld->fwrt.sar_chain_b_profile; + } + + ret = iwl_mld_config_sar_profile(mld, chain_a_prof, chain_b_prof); + if (ret < 0) + return ret; + + if (ret) + return 0; + + return iwl_mld_geo_sar_init(mld); +} + +int iwl_mld_init_sgom(struct iwl_mld *mld) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, + SAR_OFFSET_MAPPING_TABLE_CMD), + .data[0] = &mld->fwrt.sgom_table, + .len[0] = sizeof(mld->fwrt.sgom_table), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + + if (!mld->fwrt.sgom_enabled) { + IWL_DEBUG_RADIO(mld, "SGOM table is disabled\n"); + return 0; + } + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) + IWL_ERR(mld, + "failed to send SAR_OFFSET_MAPPING_CMD (%d)\n", ret); + + return ret; +} + +static int iwl_mld_ppag_send_cmd(struct iwl_mld *mld) +{ + union iwl_ppag_table_cmd cmd = {}; + int ret, len; + + ret = iwl_fill_ppag_table(&mld->fwrt, &cmd, &len); + /* Not supporting PPAG table is a valid scenario */ + if (ret < 0) + return 0; + + IWL_DEBUG_RADIO(mld, "Sending PER_PLATFORM_ANT_GAIN_CMD\n"); + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, + PER_PLATFORM_ANT_GAIN_CMD), + &cmd, len); + if (ret < 0) + IWL_ERR(mld, "failed to send PER_PLATFORM_ANT_GAIN_CMD (%d)\n", + ret); + + return ret; +} + +int iwl_mld_init_ppag(struct iwl_mld *mld) +{ + /* no need to read the table, done in INIT stage */ + + if (!(iwl_is_ppag_approved(&mld->fwrt))) + return 0; + + return iwl_mld_ppag_send_cmd(mld); +} + +void iwl_mld_configure_lari(struct iwl_mld *mld) +{ + struct iwl_fw_runtime *fwrt = &mld->fwrt; + struct iwl_lari_config_change_cmd cmd = { + .config_bitmap = iwl_get_lari_config_bitmap(fwrt), + }; + int ret; + u32 value; + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_11AX_ENABLEMENT, &value); + if (!ret) + cmd.oem_11ax_allow_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_UNII4_CHAN, &value); + if (!ret) + cmd.oem_unii4_allow_bitmap = + cpu_to_le32(value &= DSM_UNII4_ALLOW_BITMAP); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ACTIVATE_CHANNEL, &value); + if (!ret) + cmd.chan_state_active_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_6E, &value); + if (!ret) + cmd.oem_uhb_allow_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_FORCE_DISABLE_CHANNELS, &value); + if (!ret) + cmd.force_disable_channels_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENERGY_DETECTION_THRESHOLD, + &value); + if (!ret) + cmd.edt_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_wbem(fwrt, &value); + if (!ret) + cmd.oem_320mhz_allow_bitmap = cpu_to_le32(value); + + ret = iwl_bios_get_dsm(fwrt, DSM_FUNC_ENABLE_11BE, &value); + if (!ret) + cmd.oem_11be_allow_bitmap = cpu_to_le32(value); + + if (!cmd.config_bitmap && + !cmd.oem_uhb_allow_bitmap && + !cmd.oem_11ax_allow_bitmap && + !cmd.oem_unii4_allow_bitmap && + !cmd.chan_state_active_bitmap && + !cmd.force_disable_channels_bitmap && + !cmd.edt_bitmap && + !cmd.oem_320mhz_allow_bitmap && + !cmd.oem_11be_allow_bitmap) + return; + + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, config_bitmap=0x%x, oem_11ax_allow_bitmap=0x%x\n", + le32_to_cpu(cmd.config_bitmap), + le32_to_cpu(cmd.oem_11ax_allow_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, oem_unii4_allow_bitmap=0x%x, chan_state_active_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_unii4_allow_bitmap), + le32_to_cpu(cmd.chan_state_active_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, oem_uhb_allow_bitmap=0x%x, force_disable_channels_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_uhb_allow_bitmap), + le32_to_cpu(cmd.force_disable_channels_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, edt_bitmap=0x%x, oem_320mhz_allow_bitmap=0x%x\n", + le32_to_cpu(cmd.edt_bitmap), + le32_to_cpu(cmd.oem_320mhz_allow_bitmap)); + IWL_DEBUG_RADIO(mld, + "sending LARI_CONFIG_CHANGE, oem_11be_allow_bitmap=0x%x\n", + le32_to_cpu(cmd.oem_11be_allow_bitmap)); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(REGULATORY_AND_NVM_GROUP, + LARI_CONFIG_CHANGE), &cmd); + if (ret) + IWL_DEBUG_RADIO(mld, + "Failed to send LARI_CONFIG_CHANGE (%d)\n", + ret); +} + +void iwl_mld_init_uats(struct iwl_mld *mld) +{ + int ret; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(REGULATORY_AND_NVM_GROUP, + MCC_ALLOWED_AP_TYPE_CMD), + .data[0] = &mld->fwrt.uats_table, + .len[0] = sizeof(mld->fwrt.uats_table), + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + + if (!mld->fwrt.uats_valid) + return; + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) + IWL_ERR(mld, "failed to send MCC_ALLOWED_AP_TYPE_CMD (%d)\n", + ret); +} + +void iwl_mld_init_tas(struct iwl_mld *mld) +{ + int ret; + struct iwl_tas_data data = {}; + struct iwl_tas_config_cmd cmd = {}; + u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG); + + BUILD_BUG_ON(ARRAY_SIZE(data.block_list_array) != + IWL_WTAS_BLACK_LIST_MAX); + BUILD_BUG_ON(ARRAY_SIZE(cmd.block_list_array) != + IWL_WTAS_BLACK_LIST_MAX); + + if (!fw_has_capa(&mld->fw->ucode_capa, IWL_UCODE_TLV_CAPA_TAS_CFG)) { + IWL_DEBUG_RADIO(mld, "TAS not enabled in FW\n"); + return; + } + + ret = iwl_bios_get_tas_table(&mld->fwrt, &data); + if (ret < 0) { + IWL_DEBUG_RADIO(mld, + "TAS table invalid or unavailable. (%d)\n", + ret); + return; + } + + if (!iwl_is_tas_approved()) { + IWL_DEBUG_RADIO(mld, + "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", + dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); + if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array, + &data.block_list_size, + IWL_MCC_US)) || + (!iwl_add_mcc_to_tas_block_list(data.block_list_array, + &data.block_list_size, + IWL_MCC_CANADA))) { + IWL_DEBUG_RADIO(mld, + "Unable to add US/Canada to TAS block list, disabling TAS\n"); + return; + } + } else { + IWL_DEBUG_RADIO(mld, + "System vendor '%s' is in the approved list.\n", + dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); + } + + cmd.block_list_size = cpu_to_le16(data.block_list_size); + for (u8 i = 0; i < data.block_list_size; i++) + cmd.block_list_array[i] = + cpu_to_le16(data.block_list_array[i]); + cmd.tas_config_info.table_source = data.table_source; + cmd.tas_config_info.table_revision = data.table_revision; + cmd.tas_config_info.value = cpu_to_le32(data.tas_selection); + + ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd); + if (ret) + IWL_DEBUG_RADIO(mld, "failed to send TAS_CONFIG (%d)\n", ret); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h new file mode 100644 index 000000000000..3b01c645adda --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/regulatory.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_regulatory_h__ +#define __iwl_mld_regulatory_h__ + +#include "mld.h" + +void iwl_mld_get_bios_tables(struct iwl_mld *mld); +void iwl_mld_configure_lari(struct iwl_mld *mld); +void iwl_mld_init_uats(struct iwl_mld *mld); +void iwl_mld_init_tas(struct iwl_mld *mld); + +int iwl_mld_init_ppag(struct iwl_mld *mld); + +int iwl_mld_init_sgom(struct iwl_mld *mld); + +int iwl_mld_init_sar(struct iwl_mld *mld); + +int iwl_mld_config_sar_profile(struct iwl_mld *mld, int prof_a, int prof_b); + +#endif /* __iwl_mld_regulatory_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.c b/drivers/net/wireless/intel/iwlwifi/mld/roc.c new file mode 100644 index 000000000000..e85f45bce79a --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.c @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 - 2025 Intel Corporation + */ +#include <net/cfg80211.h> +#include <net/mac80211.h> + +#include "mld.h" +#include "roc.h" +#include "hcmd.h" +#include "iface.h" +#include "sta.h" +#include "mlo.h" + +#include "fw/api/context.h" +#include "fw/api/time-event.h" + +#define AUX_ROC_MAX_DELAY MSEC_TO_TU(200) + +static void +iwl_mld_vif_iter_emlsr_block_roc(void *data, u8 *mac, struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int *result = data; + int ret; + + ret = iwl_mld_block_emlsr_sync(mld_vif->mld, vif, + IWL_MLD_EMLSR_BLOCKED_ROC, + iwl_mld_get_primary_link(vif)); + if (ret) + *result = ret; +} + +struct iwl_mld_roc_iter_data { + enum iwl_roc_activity activity; + struct ieee80211_vif *vif; + bool found; +}; + +static void iwl_mld_find_roc_vif_iter(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_roc_iter_data *roc_data = data; + + if (mld_vif->roc_activity != roc_data->activity) + return; + + /* The FW supports one ROC of each type simultaneously */ + if (WARN_ON(roc_data->found)) { + roc_data->vif = NULL; + return; + } + + roc_data->found = true; + roc_data->vif = vif; +} + +static struct ieee80211_vif * +iwl_mld_find_roc_vif(struct iwl_mld *mld, enum iwl_roc_activity activity) +{ + struct iwl_mld_roc_iter_data roc_data = { + .activity = activity, + .found = false, + }; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_find_roc_vif_iter, + &roc_data); + + return roc_data.vif; +} + +int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel *channel, int duration, + enum ieee80211_roc_type type) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_int_sta *aux_sta = &mld_vif->aux_sta; + struct iwl_roc_req cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + }; + u8 ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); + u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd); + enum iwl_roc_activity activity; + int ret = 0; + + lockdep_assert_wiphy(mld->wiphy); + + if (vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION) { + IWL_ERR(mld, "NOT SUPPORTED: ROC on vif->type %d\n", + vif->type); + + return -EOPNOTSUPP; + } + + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + switch (type) { + case IEEE80211_ROC_TYPE_NORMAL: + activity = ROC_ACTIVITY_P2P_DISC; + break; + case IEEE80211_ROC_TYPE_MGMT_TX: + activity = ROC_ACTIVITY_P2P_NEG; + break; + default: + WARN_ONCE(1, "Got an invalid P2P ROC type\n"); + return -EINVAL; + } + } else { + activity = ROC_ACTIVITY_HOTSPOT; + } + + /* The FW supports one ROC of each type simultaneously */ + if (WARN_ON(iwl_mld_find_roc_vif(mld, activity))) + return -EBUSY; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_block_roc, + &ret); + if (ret) + return ret; + + ret = iwl_mld_add_aux_sta(mld, aux_sta); + if (ret) + return ret; + + cmd.activity = cpu_to_le32(activity); + cmd.sta_id = cpu_to_le32(aux_sta->sta_id); + cmd.channel_info.channel = cpu_to_le32(channel->hw_value); + cmd.channel_info.band = iwl_mld_nl80211_band_to_fw(channel->band); + cmd.channel_info.width = IWL_PHY_CHANNEL_MODE20; + cmd.max_delay = cpu_to_le32(AUX_ROC_MAX_DELAY); + cmd.duration = cpu_to_le32(MSEC_TO_TU(duration)); + + memcpy(cmd.node_addr, vif->addr, ETH_ALEN); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + &cmd, cmd_len); + if (ret) { + IWL_ERR(mld, "Couldn't send the ROC_CMD\n"); + return ret; + } + + mld_vif->roc_activity = activity; + + return 0; +} + +static void +iwl_mld_vif_iter_emlsr_unblock_roc(void *data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + iwl_mld_unblock_emlsr(mld_vif->mld, vif, IWL_MLD_EMLSR_BLOCKED_ROC); +} + +static void iwl_mld_destroy_roc(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_vif *mld_vif) +{ + mld_vif->roc_activity = ROC_NUM_ACTIVITIES; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_vif_iter_emlsr_unblock_roc, + NULL); + + /* wait until every tx has seen that roc_activity has been reset */ + synchronize_net(); + /* from here, no new tx will be added + * we can flush the Tx on the queues + */ + + iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id); + + iwl_mld_remove_aux_sta(mld, vif); +} + +int iwl_mld_cancel_roc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) +{ + struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw); + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_roc_req cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + }; + u8 ver = iwl_fw_lookup_cmd_ver(mld->fw, + WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); + u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(cmd); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION)) + return -EOPNOTSUPP; + + /* No roc activity running it's probably already done */ + if (mld_vif->roc_activity == ROC_NUM_ACTIVITIES) + return 0; + + cmd.activity = cpu_to_le32(mld_vif->roc_activity); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), + &cmd, cmd_len); + if (ret) + IWL_ERR(mld, "Couldn't send the command to cancel the ROC\n"); + + /* We may have raced with the firmware expiring the ROC instance at + * this very moment. In that case, we can have a notification in the + * async processing queue. However, none can arrive _after_ this as + * ROC_CMD was sent synchronously, i.e. we waited for a response and + * the firmware cannot refer to this ROC after the response. Thus, + * if we just cancel the notification (if there's one) we'll be at a + * clean state for any possible next ROC. + */ + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_ROC, + mld_vif->roc_activity); + + iwl_mld_destroy_roc(mld, vif, mld_vif); + + return 0; +} + +void iwl_mld_handle_roc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_roc_notif *notif = (void *)pkt->data; + u32 activity = le32_to_cpu(notif->activity); + struct iwl_mld_vif *mld_vif; + struct ieee80211_vif *vif; + + vif = iwl_mld_find_roc_vif(mld, activity); + if (WARN_ON(!vif)) + return; + + mld_vif = iwl_mld_vif_from_mac80211(vif); + /* It is possible that the ROC was canceled + * but the notification was already fired. + */ + if (mld_vif->roc_activity != activity) + return; + + if (le32_to_cpu(notif->success) && + le32_to_cpu(notif->started)) { + /* We had a successful start */ + ieee80211_ready_on_channel(mld->hw); + } else { + /* ROC was not successful, tell the firmware to remove it */ + if (le32_to_cpu(notif->started)) + iwl_mld_cancel_roc(mld->hw, vif); + else + iwl_mld_destroy_roc(mld, vif, mld_vif); + /* we need to let know mac80211 about end OR + * an unsuccessful start + */ + ieee80211_remain_on_channel_expired(mld->hw); + } +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/roc.h b/drivers/net/wireless/intel/iwlwifi/mld/roc.h new file mode 100644 index 000000000000..985d7f20a3d7 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/roc.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_roc_h__ +#define __iwl_mld_roc_h__ + +#include <net/mac80211.h> + +int iwl_mld_start_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_channel *channel, int duration, + enum ieee80211_roc_type type); + +int iwl_mld_cancel_roc(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); + +void iwl_mld_handle_roc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +#endif /* __iwl_mld_roc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/rx.c new file mode 100644 index 000000000000..ce0093d5c638 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.c @@ -0,0 +1,2071 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include <net/mac80211.h> +#include <kunit/static_stub.h> + +#include "mld.h" +#include "sta.h" +#include "agg.h" +#include "rx.h" +#include "hcmd.h" +#include "iface.h" +#include "time_sync.h" +#include "fw/dbg.h" +#include "fw/api/rx.h" + +/* stores relevant PHY data fields extracted from iwl_rx_mpdu_desc */ +struct iwl_mld_rx_phy_data { + enum iwl_rx_phy_info_type info_type; + __le32 data0; + __le32 data1; + __le32 data2; + __le32 data3; + __le32 eht_data4; + __le32 data5; + __le16 data4; + bool first_subframe; + bool with_data; + __le32 rx_vec[4]; + u32 rate_n_flags; + u32 gp2_on_air_rise; + u16 phy_info; + u8 energy_a, energy_b; +}; + +static void +iwl_mld_fill_phy_data(struct iwl_mld *mld, + struct iwl_rx_mpdu_desc *desc, + struct iwl_mld_rx_phy_data *phy_data) +{ + phy_data->phy_info = le16_to_cpu(desc->phy_info); + phy_data->rate_n_flags = iwl_v3_rate_from_v2_v3(desc->v3.rate_n_flags, + mld->fw_rates_ver_3); + phy_data->gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise); + phy_data->energy_a = desc->v3.energy_a; + phy_data->energy_b = desc->v3.energy_b; + phy_data->data0 = desc->v3.phy_data0; + phy_data->data1 = desc->v3.phy_data1; + phy_data->data2 = desc->v3.phy_data2; + phy_data->data3 = desc->v3.phy_data3; + phy_data->data4 = desc->phy_data4; + phy_data->eht_data4 = desc->phy_eht_data4; + phy_data->data5 = desc->v3.phy_data5; + phy_data->with_data = true; +} + +static inline int iwl_mld_check_pn(struct iwl_mld *mld, struct sk_buff *skb, + int queue, struct ieee80211_sta *sta) +{ + struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); + struct ieee80211_rx_status *stats = IEEE80211_SKB_RXCB(skb); + struct iwl_mld_sta *mld_sta; + struct iwl_mld_ptk_pn *ptk_pn; + int res; + u8 tid, keyidx; + u8 pn[IEEE80211_CCMP_PN_LEN]; + u8 *extiv; + + /* multicast and non-data only arrives on default queue; avoid checking + * for default queue - we don't want to replicate all the logic that's + * necessary for checking the PN on fragmented frames, leave that + * to mac80211 + */ + if (queue == 0 || !ieee80211_is_data(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return 0; + + if (!(stats->flag & RX_FLAG_DECRYPTED)) + return 0; + + /* if we are here - this for sure is either CCMP or GCMP */ + if (!sta) { + IWL_DEBUG_DROP(mld, + "expected hw-decrypted unicast frame for station\n"); + return -1; + } + + mld_sta = iwl_mld_sta_from_mac80211(sta); + + extiv = (u8 *)hdr + ieee80211_hdrlen(hdr->frame_control); + keyidx = extiv[3] >> 6; + + ptk_pn = rcu_dereference(mld_sta->ptk_pn[keyidx]); + if (!ptk_pn) + return -1; + + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = ieee80211_get_tid(hdr); + else + tid = 0; + + /* we don't use HCCA/802.11 QoS TSPECs, so drop such frames */ + if (tid >= IWL_MAX_TID_COUNT) + return -1; + + /* load pn */ + pn[0] = extiv[7]; + pn[1] = extiv[6]; + pn[2] = extiv[5]; + pn[3] = extiv[4]; + pn[4] = extiv[1]; + pn[5] = extiv[0]; + + res = memcmp(pn, ptk_pn->q[queue].pn[tid], IEEE80211_CCMP_PN_LEN); + if (res < 0) + return -1; + if (!res && !(stats->flag & RX_FLAG_ALLOW_SAME_PN)) + return -1; + + memcpy(ptk_pn->q[queue].pn[tid], pn, IEEE80211_CCMP_PN_LEN); + stats->flag |= RX_FLAG_PN_VALIDATED; + + return 0; +} + +/* iwl_mld_pass_packet_to_mac80211 - passes the packet for mac80211 */ +void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, + struct napi_struct *napi, + struct sk_buff *skb, int queue, + struct ieee80211_sta *sta) +{ + KUNIT_STATIC_STUB_REDIRECT(iwl_mld_pass_packet_to_mac80211, + mld, napi, skb, queue, sta); + + if (unlikely(iwl_mld_check_pn(mld, skb, queue, sta))) { + kfree_skb(skb); + return; + } + + ieee80211_rx_napi(mld->hw, sta, skb, napi); +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_pass_packet_to_mac80211); + +static void iwl_mld_fill_signal(struct iwl_mld *mld, + struct ieee80211_rx_status *rx_status, + struct iwl_mld_rx_phy_data *phy_data) +{ + u32 rate_n_flags = phy_data->rate_n_flags; + int energy_a = phy_data->energy_a; + int energy_b = phy_data->energy_b; + int max_energy; + + energy_a = energy_a ? -energy_a : S8_MIN; + energy_b = energy_b ? -energy_b : S8_MIN; + max_energy = max(energy_a, energy_b); + + IWL_DEBUG_STATS(mld, "energy in A %d B %d, and max %d\n", + energy_a, energy_b, max_energy); + + rx_status->signal = max_energy; + rx_status->chains = + (rate_n_flags & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS; + rx_status->chain_signal[0] = energy_a; + rx_status->chain_signal[1] = energy_b; +} + +static void +iwl_mld_decode_he_phy_ru_alloc(struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_radiotap_he *he, + struct ieee80211_radiotap_he_mu *he_mu, + struct ieee80211_rx_status *rx_status) +{ + /* Unfortunately, we have to leave the mac80211 data + * incorrect for the case that we receive an HE-MU + * transmission and *don't* have the HE phy data (due + * to the bits being used for TSF). This shouldn't + * happen though as management frames where we need + * the TSF/timers are not be transmitted in HE-MU. + */ + u8 ru = le32_get_bits(phy_data->data1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK); + u32 rate_n_flags = phy_data->rate_n_flags; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; + u8 offs = 0; + + rx_status->bw = RATE_INFO_BW_HE_RU; + + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN); + + switch (ru) { + case 0 ... 36: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_26; + offs = ru; + break; + case 37 ... 52: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_52; + offs = ru - 37; + break; + case 53 ... 60: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + offs = ru - 53; + break; + case 61 ... 64: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_242; + offs = ru - 61; + break; + case 65 ... 66: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_484; + offs = ru - 65; + break; + case 67: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_996; + break; + case 68: + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_2x996; + break; + } + he->data2 |= le16_encode_bits(offs, + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET); + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_RU_OFFSET_KNOWN); + if (phy_data->data1 & cpu_to_le32(IWL_RX_PHY_DATA1_HE_RU_ALLOC_SEC80)) + he->data2 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRISEC_80_SEC); + +#define CHECK_BW(bw) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_ ## bw ## MHZ != \ + RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS); \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_ ## bw ## MHZ != \ + RATE_MCS_CHAN_WIDTH_##bw >> RATE_MCS_CHAN_WIDTH_POS) + CHECK_BW(20); + CHECK_BW(40); + CHECK_BW(80); + CHECK_BW(160); + + if (he_mu) + he_mu->flags2 |= + le16_encode_bits(u32_get_bits(rate_n_flags, + RATE_MCS_CHAN_WIDTH_MSK), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); + else if (he_type == RATE_MCS_HE_TYPE_TRIG) + he->data6 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) | + le16_encode_bits(u32_get_bits(rate_n_flags, + RATE_MCS_CHAN_WIDTH_MSK), + IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW); +} + +static void +iwl_mld_decode_he_mu_ext(struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_radiotap_he_mu *he_mu) +{ + u32 phy_data2 = le32_to_cpu(phy_data->data2); + u32 phy_data3 = le32_to_cpu(phy_data->data3); + u16 phy_data4 = le16_to_cpu(phy_data->data4); + u32 rate_n_flags = phy_data->rate_n_flags; + + if (u32_get_bits(phy_data4, IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CRC_OK)) { + he_mu->flags1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU_KNOWN); + + he_mu->flags1 |= + le16_encode_bits(u32_get_bits(phy_data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_CH1_CTR_RU), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH1_CTR_26T_RU); + + he_mu->ru_ch1[0] = u32_get_bits(phy_data2, + IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU0); + he_mu->ru_ch1[1] = u32_get_bits(phy_data3, + IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU1); + he_mu->ru_ch1[2] = u32_get_bits(phy_data2, + IWL_RX_PHY_DATA2_HE_MU_EXT_CH1_RU2); + he_mu->ru_ch1[3] = u32_get_bits(phy_data3, + IWL_RX_PHY_DATA3_HE_MU_EXT_CH1_RU3); + } + + if (u32_get_bits(phy_data4, IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CRC_OK) && + (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) != RATE_MCS_CHAN_WIDTH_20) { + he_mu->flags1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_RU_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_CH2_CTR_26T_RU_KNOWN); + + he_mu->flags2 |= + le16_encode_bits(u32_get_bits(phy_data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_CH2_CTR_RU), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_CH2_CTR_26T_RU); + + he_mu->ru_ch2[0] = u32_get_bits(phy_data2, + IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU0); + he_mu->ru_ch2[1] = u32_get_bits(phy_data3, + IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU1); + he_mu->ru_ch2[2] = u32_get_bits(phy_data2, + IWL_RX_PHY_DATA2_HE_MU_EXT_CH2_RU2); + he_mu->ru_ch2[3] = u32_get_bits(phy_data3, + IWL_RX_PHY_DATA3_HE_MU_EXT_CH2_RU3); + } +} + +static void +iwl_mld_decode_he_phy_data(struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_radiotap_he *he, + struct ieee80211_radiotap_he_mu *he_mu, + struct ieee80211_rx_status *rx_status, + int queue) +{ + switch (phy_data->info_type) { + case IWL_RX_PHY_INFO_TYPE_NONE: + case IWL_RX_PHY_INFO_TYPE_CCK: + case IWL_RX_PHY_INFO_TYPE_OFDM_LGCY: + case IWL_RX_PHY_INFO_TYPE_HT: + case IWL_RX_PHY_INFO_TYPE_VHT_SU: + case IWL_RX_PHY_INFO_TYPE_VHT_MU: + case IWL_RX_PHY_INFO_TYPE_EHT_MU: + case IWL_RX_PHY_INFO_TYPE_EHT_TB: + case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT: + return; + case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT: + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE2_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE3_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE4_KNOWN); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2, + IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE1), + IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE1); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2, + IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE2), + IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE2); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2, + IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE3), + IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE3); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data2, + IWL_RX_PHY_DATA2_HE_TB_EXT_SPTL_REUSE4), + IEEE80211_RADIOTAP_HE_DATA4_TB_SPTL_REUSE4); + fallthrough; + case IWL_RX_PHY_INFO_TYPE_HE_SU: + case IWL_RX_PHY_INFO_TYPE_HE_MU: + case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_HE_TB: + /* HE common */ + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_LDPC_XSYMSEG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DOPPLER_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_BSS_COLOR_KNOWN); + he->data2 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_PRE_FEC_PAD_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_PE_DISAMBIG_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXOP_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_NUM_LTF_SYMS_KNOWN); + he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_BSS_COLOR_MASK), + IEEE80211_RADIOTAP_HE_DATA3_BSS_COLOR); + if (phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB && + phy_data->info_type != IWL_RX_PHY_INFO_TYPE_HE_TB_EXT) { + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_UL_DL_KNOWN); + he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_UPLINK), + IEEE80211_RADIOTAP_HE_DATA3_UL_DL); + } + he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_LDPC_EXT_SYM), + IEEE80211_RADIOTAP_HE_DATA3_LDPC_XSYMSEG); + he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_PRE_FEC_PAD_MASK), + IEEE80211_RADIOTAP_HE_DATA5_PRE_FEC_PAD); + he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_PE_DISAMBIG), + IEEE80211_RADIOTAP_HE_DATA5_PE_DISAMBIG); + he->data5 |= le16_encode_bits(le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_HE_LTF_NUM_MASK), + IEEE80211_RADIOTAP_HE_DATA5_NUM_LTF_SYMS); + he->data6 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_TXOP_DUR_MASK), + IEEE80211_RADIOTAP_HE_DATA6_TXOP); + he->data6 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_DOPPLER), + IEEE80211_RADIOTAP_HE_DATA6_DOPPLER); + break; + } + + switch (phy_data->info_type) { + case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_HE_MU: + case IWL_RX_PHY_INFO_TYPE_HE_SU: + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_SPTL_REUSE_KNOWN); + he->data4 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_SPATIAL_REUSE_MASK), + IEEE80211_RADIOTAP_HE_DATA4_SU_MU_SPTL_REUSE); + break; + default: + /* nothing here */ + break; + } + + switch (phy_data->info_type) { + case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: + he_mu->flags1 |= + le16_encode_bits(le16_get_bits(phy_data->data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_DCM), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM); + he_mu->flags1 |= + le16_encode_bits(le16_get_bits(phy_data->data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_SIGB_MCS_MASK), + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS); + he_mu->flags2 |= + le16_encode_bits(le16_get_bits(phy_data->data4, + IWL_RX_PHY_DATA4_HE_MU_EXT_PREAMBLE_PUNC_TYPE_MASK), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW); + iwl_mld_decode_he_mu_ext(phy_data, he_mu); + fallthrough; + case IWL_RX_PHY_INFO_TYPE_HE_MU: + he_mu->flags2 |= + le16_encode_bits(le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_HE_MU_SIBG_SYM_OR_USER_NUM_MASK), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_SYMS_USERS); + he_mu->flags2 |= + le16_encode_bits(le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_HE_MU_SIGB_COMPRESSION), + IEEE80211_RADIOTAP_HE_MU_FLAGS2_SIG_B_COMP); + fallthrough; + case IWL_RX_PHY_INFO_TYPE_HE_TB: + case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT: + iwl_mld_decode_he_phy_ru_alloc(phy_data, he, he_mu, rx_status); + break; + case IWL_RX_PHY_INFO_TYPE_HE_SU: + he->data1 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BEAM_CHANGE_KNOWN); + he->data3 |= le16_encode_bits(le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_HE_BEAM_CHNG), + IEEE80211_RADIOTAP_HE_DATA3_BEAM_CHANGE); + break; + default: + /* nothing */ + break; + } +} + +static void iwl_mld_rx_he(struct iwl_mld *mld, struct sk_buff *skb, + struct iwl_mld_rx_phy_data *phy_data, + int queue) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_radiotap_he *he = NULL; + struct ieee80211_radiotap_he_mu *he_mu = NULL; + u32 rate_n_flags = phy_data->rate_n_flags; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; + u8 ltf; + static const struct ieee80211_radiotap_he known = { + .data1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_DATA_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_DATA_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_STBC_KNOWN | + IEEE80211_RADIOTAP_HE_DATA1_CODING_KNOWN), + .data2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA2_GI_KNOWN | + IEEE80211_RADIOTAP_HE_DATA2_TXBF_KNOWN), + }; + static const struct ieee80211_radiotap_he_mu mu_known = { + .flags1 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_MCS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_DCM_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_SYMS_USERS_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS1_SIG_B_COMP_KNOWN), + .flags2 = cpu_to_le16(IEEE80211_RADIOTAP_HE_MU_FLAGS2_PUNC_FROM_SIG_A_BW_KNOWN | + IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW_KNOWN), + }; + u16 phy_info = phy_data->phy_info; + + he = skb_put_data(skb, &known, sizeof(known)); + rx_status->flag |= RX_FLAG_RADIOTAP_HE; + + if (phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU || + phy_data->info_type == IWL_RX_PHY_INFO_TYPE_HE_MU_EXT) { + he_mu = skb_put_data(skb, &mu_known, sizeof(mu_known)); + rx_status->flag |= RX_FLAG_RADIOTAP_HE_MU; + } + + /* report the AMPDU-EOF bit on single frames */ + if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_HE_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + + if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) + iwl_mld_decode_he_phy_data(phy_data, he, he_mu, rx_status, + queue); + + /* update aggregation data for monitor sake on default queue */ + if (!queue && (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) && + (phy_info & IWL_RX_MPDU_PHY_AMPDU) && phy_data->first_subframe) { + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->data0 & cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + + if (he_type == RATE_MCS_HE_TYPE_EXT_SU && + rate_n_flags & RATE_MCS_HE_106T_MSK) { + rx_status->bw = RATE_INFO_BW_HE_RU; + rx_status->he_ru = NL80211_RATE_INFO_HE_RU_ALLOC_106; + } + + /* actually data is filled in mac80211 */ + if (he_type == RATE_MCS_HE_TYPE_SU || + he_type == RATE_MCS_HE_TYPE_EXT_SU) + he->data1 |= + cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA1_BW_RU_ALLOC_KNOWN); + +#define CHECK_TYPE(F) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \ + (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS)) + + CHECK_TYPE(SU); + CHECK_TYPE(EXT_SU); + CHECK_TYPE(MU); + CHECK_TYPE(TRIG); + + he->data1 |= cpu_to_le16(he_type >> RATE_MCS_HE_TYPE_POS); + + if (rate_n_flags & RATE_MCS_BF_MSK) + he->data5 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA5_TXBF); + + switch ((rate_n_flags & RATE_MCS_HE_GI_LTF_MSK) >> + RATE_MCS_HE_GI_LTF_POS) { + case 0: + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + if (he_type == RATE_MCS_HE_TYPE_MU) + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + else + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X; + break; + case 1: + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + break; + case 2: + if (he_type == RATE_MCS_HE_TYPE_TRIG) { + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + } else { + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + } + break; + case 3: + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + break; + case 4: + rx_status->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + break; + default: + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN; + } + + he->data5 |= le16_encode_bits(ltf, + IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE); +} + +static void iwl_mld_decode_lsig(struct sk_buff *skb, + struct iwl_mld_rx_phy_data *phy_data) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_radiotap_lsig *lsig; + + switch (phy_data->info_type) { + case IWL_RX_PHY_INFO_TYPE_HT: + case IWL_RX_PHY_INFO_TYPE_VHT_SU: + case IWL_RX_PHY_INFO_TYPE_VHT_MU: + case IWL_RX_PHY_INFO_TYPE_HE_TB_EXT: + case IWL_RX_PHY_INFO_TYPE_HE_SU: + case IWL_RX_PHY_INFO_TYPE_HE_MU: + case IWL_RX_PHY_INFO_TYPE_HE_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_HE_TB: + case IWL_RX_PHY_INFO_TYPE_EHT_MU: + case IWL_RX_PHY_INFO_TYPE_EHT_TB: + case IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT: + case IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT: + lsig = skb_put(skb, sizeof(*lsig)); + lsig->data1 = cpu_to_le16(IEEE80211_RADIOTAP_LSIG_DATA1_LENGTH_KNOWN); + lsig->data2 = le16_encode_bits(le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_LSIG_LEN_MASK), + IEEE80211_RADIOTAP_LSIG_DATA2_LENGTH); + rx_status->flag |= RX_FLAG_RADIOTAP_LSIG; + break; + default: + break; + } +} + +/* Put a TLV on the skb and return data pointer + * + * Also pad the len to 4 and zero out all data part + */ +static void * +iwl_mld_radiotap_put_tlv(struct sk_buff *skb, u16 type, u16 len) +{ + struct ieee80211_radiotap_tlv *tlv; + + tlv = skb_put(skb, sizeof(*tlv)); + tlv->type = cpu_to_le16(type); + tlv->len = cpu_to_le16(len); + return skb_put_zero(skb, ALIGN(len, 4)); +} + +#define LE32_DEC_ENC(value, dec_bits, enc_bits) \ + le32_encode_bits(le32_get_bits(value, dec_bits), enc_bits) + +#define IWL_MLD_ENC_USIG_VALUE_MASK(usig, in_value, dec_bits, enc_bits) do { \ + typeof(enc_bits) _enc_bits = enc_bits; \ + typeof(usig) _usig = usig; \ + (_usig)->mask |= cpu_to_le32(_enc_bits); \ + (_usig)->value |= LE32_DEC_ENC(in_value, dec_bits, _enc_bits); \ +} while (0) + +#define __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \ + eht->data[(rt_data)] |= \ + (cpu_to_le32 \ + (IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru ## _KNOWN) | \ + LE32_DEC_ENC(data ## fw_data, \ + IWL_RX_PHY_DATA ## fw_data ## _EHT_MU_EXT_RU_ALLOC_ ## fw_ru, \ + IEEE80211_RADIOTAP_EHT_DATA ## rt_data ## _RU_ALLOC_CC_ ## rt_ru)) + +#define _IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) \ + __IWL_MLD_ENC_EHT_RU(rt_data, rt_ru, fw_data, fw_ru) + +#define IEEE80211_RADIOTAP_RU_DATA_1_1_1 1 +#define IEEE80211_RADIOTAP_RU_DATA_2_1_1 2 +#define IEEE80211_RADIOTAP_RU_DATA_1_1_2 2 +#define IEEE80211_RADIOTAP_RU_DATA_2_1_2 2 +#define IEEE80211_RADIOTAP_RU_DATA_1_2_1 3 +#define IEEE80211_RADIOTAP_RU_DATA_2_2_1 3 +#define IEEE80211_RADIOTAP_RU_DATA_1_2_2 3 +#define IEEE80211_RADIOTAP_RU_DATA_2_2_2 4 + +#define IWL_RX_RU_DATA_A1 2 +#define IWL_RX_RU_DATA_A2 2 +#define IWL_RX_RU_DATA_B1 2 +#define IWL_RX_RU_DATA_B2 4 +#define IWL_RX_RU_DATA_C1 3 +#define IWL_RX_RU_DATA_C2 3 +#define IWL_RX_RU_DATA_D1 4 +#define IWL_RX_RU_DATA_D2 4 + +#define IWL_MLD_ENC_EHT_RU(rt_ru, fw_ru) \ + _IWL_MLD_ENC_EHT_RU(IEEE80211_RADIOTAP_RU_DATA_ ## rt_ru, \ + rt_ru, \ + IWL_RX_RU_DATA_ ## fw_ru, \ + fw_ru) + +static void iwl_mld_decode_eht_ext_mu(struct iwl_mld *mld, + struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) +{ + if (phy_data->with_data) { + __le32 data1 = phy_data->data1; + __le32 data2 = phy_data->data2; + __le32 data3 = phy_data->data3; + __le32 data4 = phy_data->eht_data4; + __le32 data5 = phy_data->data5; + u32 phy_bw = phy_data->rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_MU_PUNC_CH_CODE, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data4, + IWL_RX_PHY_DATA4_EHT_MU_EXT_SIGB_MCS, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS); + IWL_MLD_ENC_USIG_VALUE_MASK + (usig, data1, IWL_RX_PHY_DATA1_EHT_MU_NUM_SIG_SYM_USIGA2, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); + + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) | + LE32_DEC_ENC(data5, IWL_RX_PHY_DATA5_EHT_MU_STA_ID_USR, + IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NR_NON_OFDMA_USERS_M); + eht->data[7] |= LE32_DEC_ENC + (data5, IWL_RX_PHY_DATA5_EHT_MU_NUM_USR_NON_OFDMA, + IEEE80211_RADIOTAP_EHT_DATA7_NUM_OF_NON_OFDMA_USERS); + + /* + * Hardware labels the content channels/RU allocation values + * as follows: + * Content Channel 1 Content Channel 2 + * 20 MHz: A1 + * 40 MHz: A1 B1 + * 80 MHz: A1 C1 B1 D1 + * 160 MHz: A1 C1 A2 C2 B1 D1 B2 D2 + * 320 MHz: A1 C1 A2 C2 A3 C3 A4 C4 B1 D1 B2 D2 B3 D3 B4 D4 + * + * However firmware can only give us A1-D2, so the higher + * frequencies are missing. + */ + + switch (phy_bw) { + case RATE_MCS_CHAN_WIDTH_320: + /* additional values are missing in RX metadata */ + fallthrough; + case RATE_MCS_CHAN_WIDTH_160: + /* content channel 1 */ + IWL_MLD_ENC_EHT_RU(1_2_1, A2); + IWL_MLD_ENC_EHT_RU(1_2_2, C2); + /* content channel 2 */ + IWL_MLD_ENC_EHT_RU(2_2_1, B2); + IWL_MLD_ENC_EHT_RU(2_2_2, D2); + fallthrough; + case RATE_MCS_CHAN_WIDTH_80: + /* content channel 1 */ + IWL_MLD_ENC_EHT_RU(1_1_2, C1); + /* content channel 2 */ + IWL_MLD_ENC_EHT_RU(2_1_2, D1); + fallthrough; + case RATE_MCS_CHAN_WIDTH_40: + /* content channel 2 */ + IWL_MLD_ENC_EHT_RU(2_1_1, B1); + fallthrough; + case RATE_MCS_CHAN_WIDTH_20: + IWL_MLD_ENC_EHT_RU(1_1_1, A1); + break; + } + } else { + __le32 usig_a1 = phy_data->rx_vec[0]; + __le32 usig_a2 = phy_data->rx_vec[1]; + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG1_MU_B20_B24_DISREGARD); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_VALIDATE, + IEEE80211_RADIOTAP_EHT_USIG1_MU_B25_VALIDATE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PPDU_TYPE, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B0_B1_PPDU_TYPE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B2_VALIDATE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PUNC_CHANNEL, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B3_B7_PUNCTURED_INFO); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B8, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B8_VALIDATE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_SIG_MCS, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B9_B10_SIG_MCS); + IWL_MLD_ENC_USIG_VALUE_MASK + (usig, usig_a2, IWL_RX_USIG_A2_EHT_SIG_SYM_NUM, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B11_B15_EHT_SIG_SYMBOLS); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_CRC_OK, + IEEE80211_RADIOTAP_EHT_USIG2_MU_B16_B19_CRC); + } +} + +static void iwl_mld_decode_eht_ext_tb(struct iwl_mld *mld, + struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) +{ + if (phy_data->with_data) { + __le32 data5 = phy_data->data5; + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TYPE_AND_COMP, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE1, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1); + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, data5, + IWL_RX_PHY_DATA5_EHT_TB_SPATIAL_REUSE2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2); + } else { + __le32 usig_a1 = phy_data->rx_vec[0]; + __le32 usig_a2 = phy_data->rx_vec[1]; + + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a1, + IWL_RX_USIG_A1_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG1_TB_B20_B25_DISREGARD); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_PPDU_TYPE, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B0_B1_PPDU_TYPE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_USIG2_VALIDATE_B2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B2_VALIDATE); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_1, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B3_B6_SPATIAL_REUSE_1); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_SPATIAL_REUSE_2, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B7_B10_SPATIAL_REUSE_2); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_TRIG_USIG2_DISREGARD, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B11_B15_DISREGARD); + IWL_MLD_ENC_USIG_VALUE_MASK(usig, usig_a2, + IWL_RX_USIG_A2_EHT_CRC_OK, + IEEE80211_RADIOTAP_EHT_USIG2_TB_B16_B19_CRC); + } +} + +static void iwl_mld_decode_eht_ru(struct iwl_mld *mld, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht) +{ + u32 ru = le32_get_bits(eht->data[8], + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); + enum nl80211_eht_ru_alloc nl_ru; + + /* Using D1.5 Table 9-53a - Encoding of PS160 and RU Allocation subfields + * in an EHT variant User Info field + */ + + switch (ru) { + case 0 ... 36: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_26; + break; + case 37 ... 52: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52; + break; + case 53 ... 60: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106; + break; + case 61 ... 64: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_242; + break; + case 65 ... 66: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484; + break; + case 67: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996; + break; + case 68: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996; + break; + case 69: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_4x996; + break; + case 70 ... 81: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_52P26; + break; + case 82 ... 89: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_106P26; + break; + case 90 ... 93: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_484P242; + break; + case 94 ... 95: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484; + break; + case 96 ... 99: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_996P484P242; + break; + case 100 ... 103: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_2x996P484; + break; + case 104: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996; + break; + case 105 ... 106: + nl_ru = NL80211_RATE_INFO_EHT_RU_ALLOC_3x996P484; + break; + default: + return; + } + + rx_status->bw = RATE_INFO_BW_EHT_RU; + rx_status->eht.ru = nl_ru; +} + +static void iwl_mld_decode_eht_phy_data(struct iwl_mld *mld, + struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status, + struct ieee80211_radiotap_eht *eht, + struct ieee80211_radiotap_eht_usig *usig) + +{ + __le32 data0 = phy_data->data0; + __le32 data1 = phy_data->data1; + __le32 usig_a1 = phy_data->rx_vec[0]; + u8 info_type = phy_data->info_type; + + /* Not in EHT range */ + if (info_type < IWL_RX_PHY_INFO_TYPE_EHT_MU || + info_type > IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT) + return; + + usig->common |= cpu_to_le32 + (IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL_KNOWN | + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR_KNOWN); + if (phy_data->with_data) { + usig->common |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_EHT_UPLINK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); + usig->common |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_EHT_BSS_COLOR_MASK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR); + } else { + usig->common |= LE32_DEC_ENC(usig_a1, + IWL_RX_USIG_A1_UL_FLAG, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_UL_DL); + usig->common |= LE32_DEC_ENC(usig_a1, + IWL_RX_USIG_A1_BSS_COLOR, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_BSS_COLOR); + } + + usig->common |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_CHECKED); + usig->common |= + LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_VALIDATE, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_VALIDATE_BITS_OK); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_SPATIAL_REUSE); + eht->data[0] |= LE32_DEC_ENC(data0, + IWL_RX_PHY_DATA0_ETH_SPATIAL_REUSE_MASK, + IEEE80211_RADIOTAP_EHT_DATA0_SPATIAL_REUSE); + + /* All RU allocating size/index is in TB format */ + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_RU_ALLOC_TB_FMT); + eht->data[8] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PS160, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_PS_160); + eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B0, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B0); + eht->data[8] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_RU_ALLOC_B1_B7, + IEEE80211_RADIOTAP_EHT_DATA8_RU_ALLOC_TB_FMT_B7_B1); + + iwl_mld_decode_eht_ru(mld, rx_status, eht); + + /* We only get here in case of IWL_RX_MPDU_PHY_TSF_OVERLOAD is set + * which is on only in case of monitor mode so no need to check monitor + * mode + */ + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRIMARY_80); + eht->data[1] |= + le32_encode_bits(mld->monitor.p80, + IEEE80211_RADIOTAP_EHT_DATA1_PRIMARY_80); + + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP_KNOWN); + if (phy_data->with_data) + usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_TXOP_DUR_MASK, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + else + usig->common |= LE32_DEC_ENC(usig_a1, IWL_RX_USIG_A1_TXOP_DURATION, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_TXOP); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_LDPC_EXTRA_SYM_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_LDPC_EXT_SYM, + IEEE80211_RADIOTAP_EHT_DATA0_LDPC_EXTRA_SYM_OM); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PRE_PADD_FACOR_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PRE_FEC_PAD_MASK, + IEEE80211_RADIOTAP_EHT_DATA0_PRE_PADD_FACOR_OM); + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_PE_DISAMBIGUITY_OM); + eht->data[0] |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PE_DISAMBIG, + IEEE80211_RADIOTAP_EHT_DATA0_PE_DISAMBIGUITY_OM); + + /* TODO: what about IWL_RX_PHY_DATA0_EHT_BW320_SLOT */ + + if (!le32_get_bits(data0, IWL_RX_PHY_DATA0_EHT_SIGA_CRC_OK)) + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BAD_USIG_CRC); + + usig->common |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER_KNOWN); + usig->common |= LE32_DEC_ENC(data0, IWL_RX_PHY_DATA0_EHT_PHY_VER, + IEEE80211_RADIOTAP_EHT_USIG_COMMON_PHY_VER); + + /* + * TODO: what about TB - IWL_RX_PHY_DATA1_EHT_TB_PILOT_TYPE, + * IWL_RX_PHY_DATA1_EHT_TB_LOW_SS + */ + + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_EHT_LTF); + eht->data[0] |= LE32_DEC_ENC(data1, IWL_RX_PHY_DATA1_EHT_SIG_LTF_NUM, + IEEE80211_RADIOTAP_EHT_DATA0_EHT_LTF); + + if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB_EXT || + info_type == IWL_RX_PHY_INFO_TYPE_EHT_TB) + iwl_mld_decode_eht_ext_tb(mld, phy_data, rx_status, eht, usig); + + if (info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU_EXT || + info_type == IWL_RX_PHY_INFO_TYPE_EHT_MU) + iwl_mld_decode_eht_ext_mu(mld, phy_data, rx_status, eht, usig); +} + +static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb, + struct iwl_mld_rx_phy_data *phy_data, + int queue) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_radiotap_eht *eht; + struct ieee80211_radiotap_eht_usig *usig; + size_t eht_len = sizeof(*eht); + + u32 rate_n_flags = phy_data->rate_n_flags; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; + /* EHT and HE have the same values for LTF */ + u8 ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN; + u16 phy_info = phy_data->phy_info; + u32 bw; + + /* u32 for 1 user_info */ + if (phy_data->with_data) + eht_len += sizeof(u32); + + eht = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT, eht_len); + + usig = iwl_mld_radiotap_put_tlv(skb, IEEE80211_RADIOTAP_EHT_USIG, + sizeof(*usig)); + rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; + usig->common |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW_KNOWN); + + /* specific handling for 320MHz */ + bw = u32_get_bits(rate_n_flags, RATE_MCS_CHAN_WIDTH_MSK); + if (bw == RATE_MCS_CHAN_WIDTH_320_VAL) + bw += le32_get_bits(phy_data->data0, + IWL_RX_PHY_DATA0_EHT_BW320_SLOT); + + usig->common |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USIG_COMMON_BW, bw)); + + /* report the AMPDU-EOF bit on single frames */ + if (!queue && !(phy_info & IWL_RX_MPDU_PHY_AMPDU)) { + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT_KNOWN; + if (phy_data->data0 & + cpu_to_le32(IWL_RX_PHY_DATA0_EHT_DELIM_EOF)) + rx_status->flag |= RX_FLAG_AMPDU_EOF_BIT; + } + + if (phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) + iwl_mld_decode_eht_phy_data(mld, phy_data, rx_status, eht, usig); + +#define CHECK_TYPE(F) \ + BUILD_BUG_ON(IEEE80211_RADIOTAP_HE_DATA1_FORMAT_ ## F != \ + (RATE_MCS_HE_TYPE_ ## F >> RATE_MCS_HE_TYPE_POS)) + + CHECK_TYPE(SU); + CHECK_TYPE(EXT_SU); + CHECK_TYPE(MU); + CHECK_TYPE(TRIG); + + switch (u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK)) { + case 0: + if (he_type == RATE_MCS_HE_TYPE_TRIG) { + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_1X; + } else { + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + } + break; + case 1: + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_1_6; + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_2X; + break; + case 2: + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + if (he_type == RATE_MCS_HE_TYPE_TRIG) + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2; + else + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_0_8; + break; + case 3: + if (he_type != RATE_MCS_HE_TYPE_TRIG) { + ltf = IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_4X; + rx_status->eht.gi = NL80211_RATE_INFO_EHT_GI_3_2; + } + break; + default: + /* nothing here */ + break; + } + + if (ltf != IEEE80211_RADIOTAP_HE_DATA5_LTF_SIZE_UNKNOWN) { + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_GI); + eht->data[0] |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_LTF, + ltf) | + FIELD_PREP(IEEE80211_RADIOTAP_EHT_DATA0_GI, + rx_status->eht.gi)); + } + + if (!phy_data->with_data) { + eht->known |= cpu_to_le32(IEEE80211_RADIOTAP_EHT_KNOWN_NSS_S | + IEEE80211_RADIOTAP_EHT_KNOWN_BEAMFORMED_S); + eht->data[7] |= + le32_encode_bits(le32_get_bits(phy_data->rx_vec[2], + RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK), + IEEE80211_RADIOTAP_EHT_DATA7_NSS_S); + if (rate_n_flags & RATE_MCS_BF_MSK) + eht->data[7] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_DATA7_BEAMFORMED_S); + } else { + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_CODING_KNOWN | + IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_KNOWN_O | + IEEE80211_RADIOTAP_EHT_USER_INFO_DATA_FOR_USER); + + if (rate_n_flags & RATE_MCS_BF_MSK) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_BEAMFORMING_O); + + if (rate_n_flags & RATE_MCS_LDPC_MSK) + eht->user_info[0] |= + cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_CODING); + + eht->user_info[0] |= cpu_to_le32 + (FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_MCS, + u32_get_bits(rate_n_flags, + RATE_VHT_MCS_RATE_CODE_MSK)) | + FIELD_PREP(IEEE80211_RADIOTAP_EHT_USER_INFO_NSS_O, + u32_get_bits(rate_n_flags, + RATE_MCS_NSS_MSK))); + } +} + +#ifdef CONFIG_IWLWIFI_DEBUGFS +static void iwl_mld_add_rtap_sniffer_config(struct iwl_mld *mld, + struct sk_buff *skb) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + struct ieee80211_radiotap_vendor_content *radiotap; + const u16 vendor_data_len = sizeof(mld->monitor.cur_aid); + + if (!mld->monitor.cur_aid) + return; + + radiotap = + iwl_mld_radiotap_put_tlv(skb, + IEEE80211_RADIOTAP_VENDOR_NAMESPACE, + sizeof(*radiotap) + vendor_data_len); + + /* Intel OUI */ + radiotap->oui[0] = 0xf6; + radiotap->oui[1] = 0x54; + radiotap->oui[2] = 0x25; + /* radiotap sniffer config sub-namespace */ + radiotap->oui_subtype = 1; + radiotap->vendor_type = 0; + + /* fill the data now */ + memcpy(radiotap->data, &mld->monitor.cur_aid, + sizeof(mld->monitor.cur_aid)); + + rx_status->flag |= RX_FLAG_RADIOTAP_TLV_AT_END; +} +#endif + +static void iwl_mld_rx_fill_status(struct iwl_mld *mld, struct sk_buff *skb, + struct iwl_mld_rx_phy_data *phy_data, + int queue) +{ + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); + u32 format = phy_data->rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + u32 rate_n_flags = phy_data->rate_n_flags; + u8 stbc = u32_get_bits(rate_n_flags, RATE_MCS_STBC_MSK); + bool is_sgi = rate_n_flags & RATE_MCS_SGI_MSK; + + phy_data->info_type = IWL_RX_PHY_INFO_TYPE_NONE; + + if (phy_data->phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD) + phy_data->info_type = + le32_get_bits(phy_data->data1, + IWL_RX_PHY_DATA1_INFO_TYPE_MASK); + + /* set the preamble flag if appropriate */ + if (format == RATE_MCS_MOD_TYPE_CCK && + phy_data->phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) + rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; + + iwl_mld_fill_signal(mld, rx_status, phy_data); + + /* This may be overridden by iwl_mld_rx_he() to HE_RU */ + switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + break; + case RATE_MCS_CHAN_WIDTH_40: + rx_status->bw = RATE_INFO_BW_40; + break; + case RATE_MCS_CHAN_WIDTH_80: + rx_status->bw = RATE_INFO_BW_80; + break; + case RATE_MCS_CHAN_WIDTH_160: + rx_status->bw = RATE_INFO_BW_160; + break; + case RATE_MCS_CHAN_WIDTH_320: + rx_status->bw = RATE_INFO_BW_320; + break; + } + + /* must be before L-SIG data */ + if (format == RATE_MCS_MOD_TYPE_HE) + iwl_mld_rx_he(mld, skb, phy_data, queue); + + iwl_mld_decode_lsig(skb, phy_data); + + rx_status->device_timestamp = phy_data->gp2_on_air_rise; + + /* using TLV format and must be after all fixed len fields */ + if (format == RATE_MCS_MOD_TYPE_EHT) + iwl_mld_rx_eht(mld, skb, phy_data, queue); + +#ifdef CONFIG_IWLWIFI_DEBUGFS + if (unlikely(mld->monitor.on)) { + iwl_mld_add_rtap_sniffer_config(mld, skb); + + if (mld->monitor.ptp_time) { + u64 adj_time = + iwl_mld_ptp_get_adj_time(mld, + phy_data->gp2_on_air_rise * + NSEC_PER_USEC); + + rx_status->mactime = div64_u64(adj_time, NSEC_PER_USEC); + rx_status->flag |= RX_FLAG_MACTIME_IS_RTAP_TS64; + rx_status->flag &= ~RX_FLAG_MACTIME; + } + } +#endif + + if (format != RATE_MCS_MOD_TYPE_CCK && is_sgi) + rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; + + if (rate_n_flags & RATE_MCS_LDPC_MSK) + rx_status->enc_flags |= RX_ENC_FLAG_LDPC; + + switch (format) { + case RATE_MCS_MOD_TYPE_HT: + rx_status->encoding = RX_ENC_HT; + rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags); + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; + break; + case RATE_MCS_MOD_TYPE_VHT: + case RATE_MCS_MOD_TYPE_HE: + case RATE_MCS_MOD_TYPE_EHT: + if (format == RATE_MCS_MOD_TYPE_VHT) { + rx_status->encoding = RX_ENC_VHT; + } else if (format == RATE_MCS_MOD_TYPE_HE) { + rx_status->encoding = RX_ENC_HE; + rx_status->he_dcm = + !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK); + } else if (format == RATE_MCS_MOD_TYPE_EHT) { + rx_status->encoding = RX_ENC_EHT; + } + + rx_status->nss = u32_get_bits(rate_n_flags, + RATE_MCS_NSS_MSK) + 1; + rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK; + rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; + break; + default: { + int rate = + iwl_mld_legacy_hw_idx_to_mac80211_idx(rate_n_flags, + rx_status->band); + + /* valid rate */ + if (rate >= 0 && rate <= 0xFF) { + rx_status->rate_idx = rate; + break; + } + + /* invalid rate */ + rx_status->rate_idx = 0; + + if (net_ratelimit()) + IWL_ERR(mld, "invalid rate_n_flags=0x%x, band=%d\n", + rate_n_flags, rx_status->band); + break; + } + } +} + +/* iwl_mld_create_skb adds the rxb to a new skb */ +static int iwl_mld_build_rx_skb(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_hdr *hdr, u16 len, + u8 crypt_len, struct iwl_rx_cmd_buffer *rxb) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; + unsigned int headlen, fraglen, pad_len = 0; + unsigned int hdrlen = ieee80211_hdrlen(hdr->frame_control); + u8 mic_crc_len = u8_get_bits(desc->mac_flags1, + IWL_RX_MPDU_MFLG1_MIC_CRC_LEN_MASK) << 1; + + if (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { + len -= 2; + pad_len = 2; + } + + /* For non monitor interface strip the bytes the RADA might not have + * removed (it might be disabled, e.g. for mgmt frames). As a monitor + * interface cannot exist with other interfaces, this removal is safe + * and sufficient, in monitor mode there's no decryption being done. + */ + if (len > mic_crc_len && !ieee80211_hw_check(mld->hw, RX_INCLUDES_FCS)) + len -= mic_crc_len; + + /* If frame is small enough to fit in skb->head, pull it completely. + * If not, only pull ieee80211_hdr (including crypto if present, and + * an additional 8 bytes for SNAP/ethertype, see below) so that + * splice() or TCP coalesce are more efficient. + * + * Since, in addition, ieee80211_data_to_8023() always pull in at + * least 8 bytes (possibly more for mesh) we can do the same here + * to save the cost of doing it later. That still doesn't pull in + * the actual IP header since the typical case has a SNAP header. + * If the latter changes (there are efforts in the standards group + * to do so) we should revisit this and ieee80211_data_to_8023(). + */ + headlen = (len <= skb_tailroom(skb)) ? len : hdrlen + crypt_len + 8; + + /* The firmware may align the packet to DWORD. + * The padding is inserted after the IV. + * After copying the header + IV skip the padding if + * present before copying packet data. + */ + hdrlen += crypt_len; + + if (unlikely(headlen < hdrlen)) + return -EINVAL; + + /* Since data doesn't move data while putting data on skb and that is + * the only way we use, data + len is the next place that hdr would + * be put + */ + skb_set_mac_header(skb, skb->len); + skb_put_data(skb, hdr, hdrlen); + skb_put_data(skb, (u8 *)hdr + hdrlen + pad_len, headlen - hdrlen); + + if (skb->ip_summed == CHECKSUM_COMPLETE) { + struct { + u8 hdr[6]; + __be16 type; + } __packed *shdr = (void *)((u8 *)hdr + hdrlen + pad_len); + + if (unlikely(headlen - hdrlen < sizeof(*shdr) || + !ether_addr_equal(shdr->hdr, rfc1042_header) || + (shdr->type != htons(ETH_P_IP) && + shdr->type != htons(ETH_P_ARP) && + shdr->type != htons(ETH_P_IPV6) && + shdr->type != htons(ETH_P_8021Q) && + shdr->type != htons(ETH_P_PAE) && + shdr->type != htons(ETH_P_TDLS)))) + skb->ip_summed = CHECKSUM_NONE; + } + + fraglen = len - headlen; + + if (fraglen) { + int offset = (u8 *)hdr + headlen + pad_len - + (u8 *)rxb_addr(rxb) + rxb_offset(rxb); + + skb_add_rx_frag(skb, 0, rxb_steal_page(rxb), offset, + fraglen, rxb->truesize); + } + + return 0; +} + +/* returns true if a packet is a duplicate or invalid tid and + * should be dropped. Updates AMSDU PN tracking info + */ +VISIBLE_IF_IWLWIFI_KUNIT +bool +iwl_mld_is_dup(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + const struct iwl_rx_mpdu_desc *mpdu_desc, + struct ieee80211_rx_status *rx_status, int queue) +{ + struct iwl_mld_sta *mld_sta; + struct iwl_mld_rxq_dup_data *dup_data; + u8 tid, sub_frame_idx; + + if (WARN_ON(!sta)) + return false; + + mld_sta = iwl_mld_sta_from_mac80211(sta); + + if (WARN_ON_ONCE(!mld_sta->dup_data)) + return false; + + dup_data = &mld_sta->dup_data[queue]; + + /* Drop duplicate 802.11 retransmissions + * (IEEE 802.11-2020: 10.3.2.14 "Duplicate detection and recovery") + */ + if (ieee80211_is_ctl(hdr->frame_control) || + ieee80211_is_any_nullfunc(hdr->frame_control) || + is_multicast_ether_addr(hdr->addr1)) + return false; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + /* frame has qos control */ + tid = ieee80211_get_tid(hdr); + if (tid >= IWL_MAX_TID_COUNT) + return true; + } else { + tid = IWL_MAX_TID_COUNT; + } + + /* If this wasn't a part of an A-MSDU the sub-frame index will be 0 */ + sub_frame_idx = mpdu_desc->amsdu_info & + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; + + if (IWL_FW_CHECK(mld, + sub_frame_idx > 0 && + !(mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU), + "got sub_frame_idx=%d but A-MSDU flag is not set\n", + sub_frame_idx)) + return true; + + if (unlikely(ieee80211_has_retry(hdr->frame_control) && + dup_data->last_seq[tid] == hdr->seq_ctrl && + dup_data->last_sub_frame_idx[tid] >= sub_frame_idx)) + return true; + + /* Allow same PN as the first subframe for following sub frames */ + if (dup_data->last_seq[tid] == hdr->seq_ctrl && + sub_frame_idx > dup_data->last_sub_frame_idx[tid]) + rx_status->flag |= RX_FLAG_ALLOW_SAME_PN; + + dup_data->last_seq[tid] = hdr->seq_ctrl; + dup_data->last_sub_frame_idx[tid] = sub_frame_idx; + + rx_status->flag |= RX_FLAG_DUP_VALIDATED; + + return false; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_is_dup); + +static void iwl_mld_update_last_rx_timestamp(struct iwl_mld *mld, u8 baid) +{ + unsigned long now = jiffies; + unsigned long timeout; + struct iwl_mld_baid_data *ba_data; + + ba_data = rcu_dereference(mld->fw_id_to_ba[baid]); + if (!ba_data) { + IWL_DEBUG_HT(mld, "BAID %d not found in map\n", baid); + return; + } + + if (!ba_data->timeout) + return; + + /* To minimize cache bouncing between RX queues, avoid frequent updates + * to last_rx_timestamp. update it only when the timeout period has + * passed. The worst-case scenario is the session expiring after + * approximately 2 * timeout, which is negligible (the update is + * atomic). + */ + timeout = TU_TO_JIFFIES(ba_data->timeout); + if (time_is_before_jiffies(ba_data->last_rx_timestamp + timeout)) + ba_data->last_rx_timestamp = now; +} + +/* Processes received packets for a station. + * Sets *drop to true if the packet should be dropped. + * Returns the station if found, or NULL otherwise. + */ +static struct ieee80211_sta * +iwl_mld_rx_with_sta(struct iwl_mld *mld, struct ieee80211_hdr *hdr, + struct sk_buff *skb, + const struct iwl_rx_mpdu_desc *mpdu_desc, + const struct iwl_rx_packet *pkt, int queue, bool *drop) +{ + struct ieee80211_sta *sta = NULL; + struct ieee80211_link_sta *link_sta = NULL; + struct ieee80211_rx_status *rx_status; + u8 baid; + + if (mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_SRC_STA_FOUND)) { + u8 sta_id = le32_get_bits(mpdu_desc->status, + IWL_RX_MPDU_STATUS_STA_ID); + + if (IWL_FW_CHECK(mld, + sta_id >= mld->fw->ucode_capa.num_stations, + "rx_mpdu: invalid sta_id %d\n", sta_id)) + return NULL; + + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (!IS_ERR_OR_NULL(link_sta)) + sta = link_sta->sta; + } else if (!is_multicast_ether_addr(hdr->addr2)) { + /* Passing NULL is fine since we prevent two stations with the + * same address from being added. + */ + sta = ieee80211_find_sta_by_ifaddr(mld->hw, hdr->addr2, NULL); + } + + /* we may not have any station yet */ + if (!sta) + return NULL; + + rx_status = IEEE80211_SKB_RXCB(skb); + + if (link_sta && sta->valid_links) { + rx_status->link_valid = true; + rx_status->link_id = link_sta->link_id; + } + + /* fill checksum */ + if (ieee80211_is_data(hdr->frame_control) && + pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) { + u16 hwsum = be16_to_cpu(mpdu_desc->v3.raw_xsum); + + skb->ip_summed = CHECKSUM_COMPLETE; + skb->csum = csum_unfold(~(__force __sum16)hwsum); + } + + if (iwl_mld_is_dup(mld, sta, hdr, mpdu_desc, rx_status, queue)) { + IWL_DEBUG_DROP(mld, "Dropping duplicate packet 0x%x\n", + le16_to_cpu(hdr->seq_ctrl)); + *drop = true; + return NULL; + } + + baid = le32_get_bits(mpdu_desc->reorder_data, + IWL_RX_MPDU_REORDER_BAID_MASK); + if (baid != IWL_RX_REORDER_DATA_INVALID_BAID) + iwl_mld_update_last_rx_timestamp(mld, baid); + + if (link_sta && ieee80211_is_data(hdr->frame_control)) { + u8 sub_frame_idx = mpdu_desc->amsdu_info & + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; + + /* 0 means not an A-MSDU, and 1 means a new A-MSDU */ + if (!sub_frame_idx || sub_frame_idx == 1) + iwl_mld_count_mpdu_rx(link_sta, queue, 1); + + if (!is_multicast_ether_addr(hdr->addr1)) + iwl_mld_low_latency_update_counters(mld, hdr, sta, + queue); + } + + return sta; +} + +#define KEY_IDX_LEN 2 + +static int iwl_mld_rx_mgmt_prot(struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + struct ieee80211_rx_status *rx_status, + u32 mpdu_status, + u32 mpdu_len) +{ + struct wireless_dev *wdev; + struct iwl_mld_sta *mld_sta; + struct iwl_mld_vif *mld_vif; + u8 keyidx; + struct ieee80211_key_conf *key; + const u8 *frame = (void *)hdr; + + if ((mpdu_status & IWL_RX_MPDU_STATUS_SEC_MASK) == + IWL_RX_MPDU_STATUS_SEC_NONE) + return 0; + + /* For non-beacon, we don't really care. But beacons may + * be filtered out, and we thus need the firmware's replay + * detection, otherwise beacons the firmware previously + * filtered could be replayed, or something like that, and + * it can filter a lot - though usually only if nothing has + * changed. + */ + if (!ieee80211_is_beacon(hdr->frame_control)) + return 0; + + if (!sta) + return -1; + + mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + + /* key mismatch - will also report !MIC_OK but we shouldn't count it */ + if (!(mpdu_status & IWL_RX_MPDU_STATUS_KEY_VALID)) + goto report; + + /* good cases */ + if (likely(mpdu_status & IWL_RX_MPDU_STATUS_MIC_OK && + !(mpdu_status & IWL_RX_MPDU_STATUS_REPLAY_ERROR))) { + rx_status->flag |= RX_FLAG_DECRYPTED; + return 0; + } + + /* both keys will have the same cipher and MIC length, use + * whichever one is available + */ + key = rcu_dereference(mld_vif->bigtks[0]); + if (!key) { + key = rcu_dereference(mld_vif->bigtks[1]); + if (!key) + goto report; + } + + if (mpdu_len < key->icv_len + IEEE80211_GMAC_PN_LEN + KEY_IDX_LEN) + goto report; + + /* get the real key ID */ + keyidx = frame[mpdu_len - key->icv_len - IEEE80211_GMAC_PN_LEN - KEY_IDX_LEN]; + /* and if that's the other key, look it up */ + if (keyidx != key->keyidx) { + /* shouldn't happen since firmware checked, but be safe + * in case the MIC length is wrong too, for example + */ + if (keyidx != 6 && keyidx != 7) + return -1; + + key = rcu_dereference(mld_vif->bigtks[keyidx - 6]); + if (!key) + goto report; + } + + /* Report status to mac80211 */ + if (!(mpdu_status & IWL_RX_MPDU_STATUS_MIC_OK)) + ieee80211_key_mic_failure(key); + else if (mpdu_status & IWL_RX_MPDU_STATUS_REPLAY_ERROR) + ieee80211_key_replay(key); +report: + wdev = ieee80211_vif_to_wdev(mld_sta->vif); + if (wdev->netdev) + cfg80211_rx_unprot_mlme_mgmt(wdev->netdev, (void *)hdr, + mpdu_len); + + return -1; +} + +static int iwl_mld_rx_crypto(struct iwl_mld *mld, + struct ieee80211_sta *sta, + struct ieee80211_hdr *hdr, + struct ieee80211_rx_status *rx_status, + struct iwl_rx_mpdu_desc *desc, int queue, + u32 pkt_flags, u8 *crypto_len) +{ + u32 status = le32_to_cpu(desc->status); + + if (unlikely(ieee80211_is_mgmt(hdr->frame_control) && + !ieee80211_has_protected(hdr->frame_control))) + return iwl_mld_rx_mgmt_prot(sta, hdr, rx_status, status, + le16_to_cpu(desc->mpdu_len)); + + if (!ieee80211_has_protected(hdr->frame_control) || + (status & IWL_RX_MPDU_STATUS_SEC_MASK) == + IWL_RX_MPDU_STATUS_SEC_NONE) + return 0; + + switch (status & IWL_RX_MPDU_STATUS_SEC_MASK) { + case IWL_RX_MPDU_STATUS_SEC_CCM: + case IWL_RX_MPDU_STATUS_SEC_GCM: + BUILD_BUG_ON(IEEE80211_CCMP_PN_LEN != IEEE80211_GCMP_PN_LEN); + if (!(status & IWL_RX_MPDU_STATUS_MIC_OK)) { + IWL_DEBUG_DROP(mld, + "Dropping packet, bad MIC (CCM/GCM)\n"); + return -1; + } + + rx_status->flag |= RX_FLAG_DECRYPTED | RX_FLAG_MIC_STRIPPED; + *crypto_len = IEEE80211_CCMP_HDR_LEN; + return 0; + case IWL_RX_MPDU_STATUS_SEC_TKIP: + if (!(status & IWL_RX_MPDU_STATUS_ICV_OK)) + return -1; + + if (!(status & RX_MPDU_RES_STATUS_MIC_OK)) + rx_status->flag |= RX_FLAG_MMIC_ERROR; + + if (pkt_flags & FH_RSCSR_RADA_EN) { + rx_status->flag |= RX_FLAG_ICV_STRIPPED; + rx_status->flag |= RX_FLAG_MMIC_STRIPPED; + } + + *crypto_len = IEEE80211_TKIP_IV_LEN; + rx_status->flag |= RX_FLAG_DECRYPTED; + return 0; + default: + break; + } + + return 0; +} + +static void iwl_mld_rx_update_ampdu_ref(struct iwl_mld *mld, + struct iwl_mld_rx_phy_data *phy_data, + struct ieee80211_rx_status *rx_status) +{ + bool toggle_bit = + phy_data->phy_info & IWL_RX_MPDU_PHY_AMPDU_TOGGLE; + + rx_status->flag |= RX_FLAG_AMPDU_DETAILS; + /* Toggle is switched whenever new aggregation starts. Make + * sure ampdu_reference is never 0 so we can later use it to + * see if the frame was really part of an A-MPDU or not. + */ + if (toggle_bit != mld->monitor.ampdu_toggle) { + mld->monitor.ampdu_ref++; + if (mld->monitor.ampdu_ref == 0) + mld->monitor.ampdu_ref++; + mld->monitor.ampdu_toggle = toggle_bit; + phy_data->first_subframe = true; + } + rx_status->ampdu_reference = mld->monitor.ampdu_ref; +} + +static void +iwl_mld_fill_rx_status_band_freq(struct ieee80211_rx_status *rx_status, + u8 band, u8 channel) +{ + rx_status->band = iwl_mld_phy_band_to_nl80211(band); + rx_status->freq = ieee80211_channel_to_frequency(channel, + rx_status->band); +} + +void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue) +{ + struct iwl_rx_packet *pkt = rxb_addr(rxb); + struct iwl_mld_rx_phy_data phy_data = {}; + struct iwl_rx_mpdu_desc *mpdu_desc = (void *)pkt->data; + struct ieee80211_sta *sta; + struct ieee80211_hdr *hdr; + struct sk_buff *skb; + size_t mpdu_desc_size = sizeof(*mpdu_desc); + bool drop = false; + u8 crypto_len = 0, band; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + u32 mpdu_len; + enum iwl_mld_reorder_result reorder_res; + struct ieee80211_rx_status *rx_status; + + if (unlikely(mld->fw_status.in_hw_restart)) + return; + + if (IWL_FW_CHECK(mld, pkt_len < mpdu_desc_size, + "Bad REPLY_RX_MPDU_CMD size (%d)\n", pkt_len)) + return; + + mpdu_len = le16_to_cpu(mpdu_desc->mpdu_len); + + if (IWL_FW_CHECK(mld, mpdu_len + mpdu_desc_size > pkt_len, + "FW lied about packet len (%d)\n", pkt_len)) + return; + + /* Don't use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); + if (!skb) { + IWL_ERR(mld, "alloc_skb failed\n"); + return; + } + + hdr = (void *)(pkt->data + mpdu_desc_size); + + iwl_mld_fill_phy_data(mld, mpdu_desc, &phy_data); + + if (mpdu_desc->mac_flags2 & IWL_RX_MPDU_MFLG2_PAD) { + /* If the device inserted padding it means that (it thought) + * the 802.11 header wasn't a multiple of 4 bytes long. In + * this case, reserve two bytes at the start of the SKB to + * align the payload properly in case we end up copying it. + */ + skb_reserve(skb, 2); + } + + rx_status = IEEE80211_SKB_RXCB(skb); + + /* this is needed early */ + band = u8_get_bits(mpdu_desc->mac_phy_band, + IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK); + iwl_mld_fill_rx_status_band_freq(rx_status, band, + mpdu_desc->v3.channel); + + + rcu_read_lock(); + + sta = iwl_mld_rx_with_sta(mld, hdr, skb, mpdu_desc, pkt, queue, &drop); + if (drop) + goto drop; + + /* update aggregation data for monitor sake on default queue */ + if (!queue && (phy_data.phy_info & IWL_RX_MPDU_PHY_AMPDU)) + iwl_mld_rx_update_ampdu_ref(mld, &phy_data, rx_status); + + /* Keep packets with CRC errors (and with overrun) for monitor mode + * (otherwise the firmware discards them) but mark them as bad. + */ + if (!(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_CRC_OK)) || + !(mpdu_desc->status & cpu_to_le32(IWL_RX_MPDU_STATUS_OVERRUN_OK))) { + IWL_DEBUG_RX(mld, "Bad CRC or FIFO: 0x%08X.\n", + le32_to_cpu(mpdu_desc->status)); + rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; + } + + if (likely(!(phy_data.phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { + rx_status->mactime = + le64_to_cpu(mpdu_desc->v3.tsf_on_air_rise); + + /* TSF as indicated by the firmware is at INA time */ + rx_status->flag |= RX_FLAG_MACTIME_PLCP_START; + } + + /* management stuff on default queue */ + if (!queue && unlikely(ieee80211_is_beacon(hdr->frame_control) || + ieee80211_is_probe_resp(hdr->frame_control))) { + rx_status->boottime_ns = ktime_get_boottime_ns(); + + if (mld->scan.pass_all_sched_res == + SCHED_SCAN_PASS_ALL_STATE_ENABLED) + mld->scan.pass_all_sched_res = + SCHED_SCAN_PASS_ALL_STATE_FOUND; + } + + iwl_mld_rx_fill_status(mld, skb, &phy_data, queue); + + if (iwl_mld_rx_crypto(mld, sta, hdr, rx_status, mpdu_desc, queue, + le32_to_cpu(pkt->len_n_flags), &crypto_len)) + goto drop; + + if (iwl_mld_build_rx_skb(mld, skb, hdr, mpdu_len, crypto_len, rxb)) + goto drop; + + /* time sync frame is saved and will be released later when the + * notification with the timestamps arrives. + */ + if (iwl_mld_time_sync_frame(mld, skb, hdr->addr2)) + goto out; + + reorder_res = iwl_mld_reorder(mld, napi, queue, sta, skb, mpdu_desc); + switch (reorder_res) { + case IWL_MLD_PASS_SKB: + break; + case IWL_MLD_DROP_SKB: + goto drop; + case IWL_MLD_BUFFERED_SKB: + goto out; + default: + WARN_ON(1); + goto drop; + } + + iwl_mld_pass_packet_to_mac80211(mld, napi, skb, queue, sta); + + goto out; + +drop: + kfree_skb(skb); +out: + rcu_read_unlock(); +} + +#define SYNC_RX_QUEUE_TIMEOUT (HZ) +void iwl_mld_sync_rx_queues(struct iwl_mld *mld, + enum iwl_mld_internal_rxq_notif_type type, + const void *notif_payload, u32 notif_payload_size) +{ + u8 num_rx_queues = mld->trans->info.num_rxqs; + struct { + struct iwl_rxq_sync_cmd sync_cmd; + struct iwl_mld_internal_rxq_notif notif; + } __packed cmd = { + .sync_cmd.rxq_mask = cpu_to_le32(BIT(num_rx_queues) - 1), + .sync_cmd.count = + cpu_to_le32(sizeof(struct iwl_mld_internal_rxq_notif) + + notif_payload_size), + .notif.type = type, + .notif.cookie = mld->rxq_sync.cookie, + }; + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD), + .data[0] = &cmd, + .len[0] = sizeof(cmd), + .data[1] = notif_payload, + .len[1] = notif_payload_size, + }; + int ret; + + /* size must be a multiple of DWORD */ + if (WARN_ON(cmd.sync_cmd.count & cpu_to_le32(3))) + return; + + mld->rxq_sync.state = (1 << num_rx_queues) - 1; + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) { + IWL_ERR(mld, "Failed to trigger RX queues sync (%d)\n", ret); + goto out; + } + + ret = wait_event_timeout(mld->rxq_sync.waitq, + READ_ONCE(mld->rxq_sync.state) == 0, + SYNC_RX_QUEUE_TIMEOUT); + WARN_ONCE(!ret, "RXQ sync failed: state=0x%lx, cookie=%d\n", + mld->rxq_sync.state, mld->rxq_sync.cookie); + +out: + mld->rxq_sync.state = 0; + mld->rxq_sync.cookie++; +} + +void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue) +{ + struct iwl_rxq_sync_notification *notif; + struct iwl_mld_internal_rxq_notif *internal_notif; + u32 len = iwl_rx_packet_payload_len(pkt); + size_t combined_notif_len = sizeof(*notif) + sizeof(*internal_notif); + + notif = (void *)pkt->data; + internal_notif = (void *)notif->payload; + + if (IWL_FW_CHECK(mld, len < combined_notif_len, + "invalid notification size %u (%zu)\n", + len, combined_notif_len)) + return; + + len -= combined_notif_len; + + if (IWL_FW_CHECK(mld, mld->rxq_sync.cookie != internal_notif->cookie, + "received expired RX queue sync message (cookie=%d expected=%d q[%d])\n", + internal_notif->cookie, mld->rxq_sync.cookie, queue)) + return; + + switch (internal_notif->type) { + case IWL_MLD_RXQ_EMPTY: + IWL_FW_CHECK(mld, len, + "invalid empty notification size %d\n", len); + break; + case IWL_MLD_RXQ_NOTIF_DEL_BA: + if (IWL_FW_CHECK(mld, len != sizeof(struct iwl_mld_delba_data), + "invalid delba notification size %u (%zu)\n", + len, sizeof(struct iwl_mld_delba_data))) + break; + iwl_mld_del_ba(mld, queue, (void *)internal_notif->payload); + break; + default: + WARN_ON_ONCE(1); + } + + IWL_FW_CHECK(mld, !test_and_clear_bit(queue, &mld->rxq_sync.state), + "RXQ sync: queue %d responded a second time!\n", queue); + + if (READ_ONCE(mld->rxq_sync.state) == 0) + wake_up(&mld->rxq_sync.waitq); +} + +void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue) +{ + struct iwl_rx_no_data_ver_3 *desc; + struct iwl_mld_rx_phy_data phy_data; + struct ieee80211_rx_status *rx_status; + struct sk_buff *skb; + u32 format, rssi; + u8 channel; + + if (unlikely(mld->fw_status.in_hw_restart)) + return; + + if (IWL_FW_CHECK(mld, iwl_rx_packet_payload_len(pkt) < sizeof(*desc), + "Bad RX_NO_DATA_NOTIF size (%d)\n", + iwl_rx_packet_payload_len(pkt))) + return; + + desc = (void *)pkt->data; + + rssi = le32_to_cpu(desc->rssi); + channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); + + phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK); + phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK); + phy_data.data0 = desc->phy_info[0]; + phy_data.data1 = desc->phy_info[1]; + phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD; + phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time); + phy_data.rate_n_flags = iwl_v3_rate_from_v2_v3(desc->rate, + mld->fw_rates_ver_3); + phy_data.with_data = false; + + BUILD_BUG_ON(sizeof(phy_data.rx_vec) != sizeof(desc->rx_vec)); + memcpy(phy_data.rx_vec, desc->rx_vec, sizeof(phy_data.rx_vec)); + + format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + + /* Don't use dev_alloc_skb(), we'll have enough headroom once + * ieee80211_hdr pulled. + */ + skb = alloc_skb(128, GFP_ATOMIC); + if (!skb) { + IWL_ERR(mld, "alloc_skb failed\n"); + return; + } + + rx_status = IEEE80211_SKB_RXCB(skb); + + /* 0-length PSDU */ + rx_status->flag |= RX_FLAG_NO_PSDU; + + /* mark as failed PLCP on any errors to skip checks in mac80211 */ + if (le32_get_bits(desc->info, RX_NO_DATA_INFO_ERR_MSK) != + RX_NO_DATA_INFO_ERR_NONE) + rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC; + + switch (le32_get_bits(desc->info, RX_NO_DATA_INFO_TYPE_MSK)) { + case RX_NO_DATA_INFO_TYPE_NDP: + rx_status->zero_length_psdu_type = + IEEE80211_RADIOTAP_ZERO_LEN_PSDU_SOUNDING; + break; + case RX_NO_DATA_INFO_TYPE_MU_UNMATCHED: + case RX_NO_DATA_INFO_TYPE_TB_UNMATCHED: + rx_status->zero_length_psdu_type = + IEEE80211_RADIOTAP_ZERO_LEN_PSDU_NOT_CAPTURED; + break; + default: + rx_status->zero_length_psdu_type = + IEEE80211_RADIOTAP_ZERO_LEN_PSDU_VENDOR; + break; + } + + rx_status->band = channel > 14 ? NL80211_BAND_5GHZ : + NL80211_BAND_2GHZ; + + rx_status->freq = ieee80211_channel_to_frequency(channel, + rx_status->band); + + iwl_mld_rx_fill_status(mld, skb, &phy_data, queue); + + /* No more radiotap info should be added after this point. + * Mark it as mac header for upper layers to know where + * the radiotap header ends. + */ + skb_set_mac_header(skb, skb->len); + + /* Override the nss from the rx_vec since the rate_n_flags has + * only 1 bit for the nss which gives a max of 2 ss but there + * may be up to 8 spatial streams. + */ + switch (format) { + case RATE_MCS_MOD_TYPE_VHT: + rx_status->nss = + le32_get_bits(desc->rx_vec[0], + RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1; + break; + case RATE_MCS_MOD_TYPE_HE: + rx_status->nss = + le32_get_bits(desc->rx_vec[0], + RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1; + break; + case RATE_MCS_MOD_TYPE_EHT: + rx_status->nss = + le32_get_bits(desc->rx_vec[2], + RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK) + 1; + } + + /* pass the packet to mac80211 */ + rcu_read_lock(); + ieee80211_rx_napi(mld->hw, NULL, skb, napi); + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/rx.h b/drivers/net/wireless/intel/iwlwifi/mld/rx.h new file mode 100644 index 000000000000..2beabd7e70b1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/rx.h @@ -0,0 +1,72 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_rx_h__ +#define __iwl_mld_rx_h__ + +#include "mld.h" + +/** + * enum iwl_mld_internal_rxq_notif_type - RX queue sync notif types + * + * @IWL_MLD_RXQ_EMPTY: empty sync notification + * @IWL_MLD_RXQ_NOTIF_DEL_BA: notify RSS queues of delBA + */ +enum iwl_mld_internal_rxq_notif_type { + IWL_MLD_RXQ_EMPTY, + IWL_MLD_RXQ_NOTIF_DEL_BA, +}; + +/** + * struct iwl_mld_internal_rxq_notif - @iwl_rxq_sync_cmd internal data. + * This data is echoed by the firmware to all RSS queues and should be DWORD + * aligned. FW is agnostic to the data, so there are no endianness requirements + * + * @type: one of &iwl_mld_internal_rxq_notif_type + * @cookie: unique internal cookie to identify old notifications + * @reserved: reserved for alignment + * @payload: data to send to RX queues based on the type (may be empty) + */ +struct iwl_mld_internal_rxq_notif { + u8 type; + u8 reserved[3]; + u32 cookie; + u8 payload[]; +} __packed; + +/** + * struct iwl_mld_rx_queues_sync - RX queues sync data + * + * @waitq: wait queue for RX queues sync completion + * @cookie: unique id to correlate sync requests with responses + * @state: bitmask representing the sync state of RX queues + * all RX queues bits are set before sending the command, and the + * corresponding queue bit cleared upon handling the notification + */ +struct iwl_mld_rx_queues_sync { + wait_queue_head_t waitq; + u32 cookie; + unsigned long state; +}; + +void iwl_mld_rx_mpdu(struct iwl_mld *mld, struct napi_struct *napi, + struct iwl_rx_cmd_buffer *rxb, int queue); + +void iwl_mld_sync_rx_queues(struct iwl_mld *mld, + enum iwl_mld_internal_rxq_notif_type type, + const void *notif_payload, u32 notif_payload_size); + +void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld, + struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue); + +void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, + struct napi_struct *napi, + struct sk_buff *skb, int queue, + struct ieee80211_sta *sta); + +void iwl_mld_rx_monitor_no_data(struct iwl_mld *mld, struct napi_struct *napi, + struct iwl_rx_packet *pkt, int queue); + +#endif /* __iwl_mld_agg_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.c b/drivers/net/wireless/intel/iwlwifi/mld/scan.c new file mode 100644 index 000000000000..3fce7cd2d512 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.c @@ -0,0 +1,2011 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <linux/crc32.h> + +#include "mld.h" +#include "scan.h" +#include "hcmd.h" +#include "iface.h" +#include "phy.h" +#include "mlo.h" + +#include "fw/api/scan.h" +#include "fw/dbg.h" + +#define IWL_SCAN_DWELL_ACTIVE 10 +#define IWL_SCAN_DWELL_PASSIVE 110 +#define IWL_SCAN_NUM_OF_FRAGS 3 + +/* adaptive dwell max budget time [TU] for full scan */ +#define IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN 300 + +/* adaptive dwell max budget time [TU] for directed scan */ +#define IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN 100 + +/* adaptive dwell default high band APs number */ +#define IWL_SCAN_ADWELL_DEFAULT_HB_N_APS 8 + +/* adaptive dwell default low band APs number */ +#define IWL_SCAN_ADWELL_DEFAULT_LB_N_APS 2 + +/* adaptive dwell default APs number for P2P social channels (1, 6, 11) */ +#define IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL 10 + +/* adaptive dwell number of APs override for P2P friendly GO channels */ +#define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY 10 + +/* adaptive dwell number of APs override for P2P social channels */ +#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS 2 + +/* adaptive dwell number of APs override mask for p2p friendly GO */ +#define IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT BIT(20) + +/* adaptive dwell number of APs override mask for social channels */ +#define IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT BIT(21) + +#define SCAN_TIMEOUT_MSEC (30000 * HZ) + +/* minimal number of 2GHz and 5GHz channels in the regular scan request */ +#define IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS 4 + +enum iwl_mld_scan_type { + IWL_SCAN_TYPE_NOT_SET, + IWL_SCAN_TYPE_UNASSOC, + IWL_SCAN_TYPE_WILD, + IWL_SCAN_TYPE_MILD, + IWL_SCAN_TYPE_FRAGMENTED, + IWL_SCAN_TYPE_FAST_BALANCE, +}; + +struct iwl_mld_scan_timing_params { + u32 suspend_time; + u32 max_out_time; +}; + +static const struct iwl_mld_scan_timing_params scan_timing[] = { + [IWL_SCAN_TYPE_UNASSOC] = { + .suspend_time = 0, + .max_out_time = 0, + }, + [IWL_SCAN_TYPE_WILD] = { + .suspend_time = 30, + .max_out_time = 120, + }, + [IWL_SCAN_TYPE_MILD] = { + .suspend_time = 120, + .max_out_time = 120, + }, + [IWL_SCAN_TYPE_FRAGMENTED] = { + .suspend_time = 95, + .max_out_time = 44, + }, + [IWL_SCAN_TYPE_FAST_BALANCE] = { + .suspend_time = 30, + .max_out_time = 37, + }, +}; + +struct iwl_mld_scan_params { + enum iwl_mld_scan_type type; + u32 n_channels; + u16 delay; + int n_ssids; + struct cfg80211_ssid *ssids; + struct ieee80211_channel **channels; + u32 flags; + u8 *mac_addr; + u8 *mac_addr_mask; + bool no_cck; + bool pass_all; + int n_match_sets; + struct iwl_scan_probe_req preq; + struct cfg80211_match_set *match_sets; + int n_scan_plans; + struct cfg80211_sched_scan_plan *scan_plans; + bool iter_notif; + bool respect_p2p_go; + u8 fw_link_id; + struct cfg80211_scan_6ghz_params *scan_6ghz_params; + u32 n_6ghz_params; + bool scan_6ghz; + bool enable_6ghz_passive; + u8 bssid[ETH_ALEN] __aligned(2); +}; + +struct iwl_mld_scan_respect_p2p_go_iter_data { + struct ieee80211_vif *current_vif; + bool p2p_go; +}; + +static void iwl_mld_scan_respect_p2p_go_iter(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_scan_respect_p2p_go_iter_data *data = _data; + + /* exclude the given vif */ + if (vif == data->current_vif) + return; + + /* TODO: CDB check the band of the GO */ + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_P2P_GO && + iwl_mld_vif_from_mac80211(vif)->ap_ibss_active) + data->p2p_go = true; +} + +static bool iwl_mld_get_respect_p2p_go(struct iwl_mld *mld, + struct ieee80211_vif *vif, + bool low_latency) +{ + struct iwl_mld_scan_respect_p2p_go_iter_data data = { + .current_vif = vif, + .p2p_go = false, + }; + + if (!low_latency) + return false; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_scan_respect_p2p_go_iter, + &data); + + return data.p2p_go; +} + +struct iwl_mld_scan_iter_data { + struct ieee80211_vif *current_vif; + bool active_vif; + bool is_dcm_with_p2p_go; + bool global_low_latency; +}; + +static void iwl_mld_scan_iterator(void *_data, u8 *mac, + struct ieee80211_vif *vif) +{ + struct iwl_mld_scan_iter_data *data = _data; + struct ieee80211_vif *curr_vif = data->current_vif; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_vif *curr_mld_vif; + unsigned long curr_vif_active_links; + u16 link_id; + + data->global_low_latency |= iwl_mld_vif_low_latency(mld_vif); + + if ((ieee80211_vif_is_mld(vif) && vif->active_links) || + (vif->type != NL80211_IFTYPE_P2P_DEVICE && + mld_vif->deflink.active)) + data->active_vif = true; + + if (vif == curr_vif) + return; + + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_P2P_GO) + return; + + /* Currently P2P GO can't be AP MLD so the logic below assumes that */ + WARN_ON_ONCE(ieee80211_vif_is_mld(vif)); + + curr_vif_active_links = + ieee80211_vif_is_mld(curr_vif) ? curr_vif->active_links : 1; + + curr_mld_vif = iwl_mld_vif_from_mac80211(curr_vif); + + for_each_set_bit(link_id, &curr_vif_active_links, + IEEE80211_MLD_MAX_NUM_LINKS) { + struct iwl_mld_link *curr_mld_link = + iwl_mld_link_dereference_check(curr_mld_vif, link_id); + + if (WARN_ON(!curr_mld_link)) + return; + + if (rcu_access_pointer(curr_mld_link->chan_ctx) && + rcu_access_pointer(mld_vif->deflink.chan_ctx) != + rcu_access_pointer(curr_mld_link->chan_ctx)) { + data->is_dcm_with_p2p_go = true; + return; + } + } +} + +static enum +iwl_mld_scan_type iwl_mld_get_scan_type(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct iwl_mld_scan_iter_data *data) +{ + enum iwl_mld_traffic_load load = mld->scan.traffic_load.status; + + /* A scanning AP interface probably wants to generate a survey to do + * ACS (automatic channel selection). + * Force a non-fragmented scan in that case. + */ + if (ieee80211_vif_type_p2p(vif) == NL80211_IFTYPE_AP) + return IWL_SCAN_TYPE_WILD; + + if (!data->active_vif) + return IWL_SCAN_TYPE_UNASSOC; + + if ((load == IWL_MLD_TRAFFIC_HIGH || data->global_low_latency) && + vif->type != NL80211_IFTYPE_P2P_DEVICE) + return IWL_SCAN_TYPE_FRAGMENTED; + + /* In case of DCM with P2P GO set all scan requests as + * fast-balance scan + */ + if (vif->type == NL80211_IFTYPE_STATION && + data->is_dcm_with_p2p_go) + return IWL_SCAN_TYPE_FAST_BALANCE; + + if (load >= IWL_MLD_TRAFFIC_MEDIUM || data->global_low_latency) + return IWL_SCAN_TYPE_MILD; + + return IWL_SCAN_TYPE_WILD; +} + +static u8 * +iwl_mld_scan_add_2ghz_elems(struct iwl_mld *mld, const u8 *ies, + size_t len, u8 *const pos) +{ + static const u8 before_ds_params[] = { + WLAN_EID_SSID, + WLAN_EID_SUPP_RATES, + WLAN_EID_REQUEST, + WLAN_EID_EXT_SUPP_RATES, + }; + size_t offs; + u8 *newpos = pos; + + offs = ieee80211_ie_split(ies, len, + before_ds_params, + ARRAY_SIZE(before_ds_params), + 0); + + memcpy(newpos, ies, offs); + newpos += offs; + + /* Add a placeholder for DS Parameter Set element */ + *newpos++ = WLAN_EID_DS_PARAMS; + *newpos++ = 1; + *newpos++ = 0; + + memcpy(newpos, ies + offs, len - offs); + newpos += len - offs; + + return newpos; +} + +static void +iwl_mld_scan_add_tpc_report_elem(u8 *pos) +{ + pos[0] = WLAN_EID_VENDOR_SPECIFIC; + pos[1] = WFA_TPC_IE_LEN - 2; + pos[2] = (WLAN_OUI_MICROSOFT >> 16) & 0xff; + pos[3] = (WLAN_OUI_MICROSOFT >> 8) & 0xff; + pos[4] = WLAN_OUI_MICROSOFT & 0xff; + pos[5] = WLAN_OUI_TYPE_MICROSOFT_TPC; + pos[6] = 0; + /* pos[7] - tx power will be inserted by the FW */ + pos[7] = 0; + pos[8] = 0; +} + +static u32 +iwl_mld_scan_ooc_priority(enum iwl_mld_scan_status scan_status) +{ + if (scan_status == IWL_MLD_SCAN_REGULAR) + return IWL_SCAN_PRIORITY_EXT_6; + if (scan_status == IWL_MLD_SCAN_INT_MLO) + return IWL_SCAN_PRIORITY_EXT_4; + + return IWL_SCAN_PRIORITY_EXT_2; +} + +static bool +iwl_mld_scan_is_regular(struct iwl_mld_scan_params *params) +{ + return params->n_scan_plans == 1 && + params->scan_plans[0].iterations == 1; +} + +static bool +iwl_mld_scan_is_fragmented(enum iwl_mld_scan_type type) +{ + return (type == IWL_SCAN_TYPE_FRAGMENTED || + type == IWL_SCAN_TYPE_FAST_BALANCE); +} + +static int +iwl_mld_scan_uid_by_status(struct iwl_mld *mld, int status) +{ + for (int i = 0; i < ARRAY_SIZE(mld->scan.uid_status); i++) + if (mld->scan.uid_status[i] == status) + return i; + + return -ENOENT; +} + +static const char * +iwl_mld_scan_ebs_status_str(enum iwl_scan_ebs_status status) +{ + switch (status) { + case IWL_SCAN_EBS_SUCCESS: + return "successful"; + case IWL_SCAN_EBS_INACTIVE: + return "inactive"; + case IWL_SCAN_EBS_FAILED: + case IWL_SCAN_EBS_CHAN_NOT_FOUND: + default: + return "failed"; + } +} + +static int +iwl_mld_scan_ssid_exist(u8 *ssid, u8 ssid_len, struct iwl_ssid_ie *ssid_list) +{ + for (int i = 0; i < PROBE_OPTION_MAX; i++) { + if (!ssid_list[i].len) + return -1; + if (ssid_list[i].len == ssid_len && + !memcmp(ssid_list[i].ssid, ssid, ssid_len)) + return i; + } + + return -1; +} + +static bool +iwl_mld_scan_fits(struct iwl_mld *mld, int n_ssids, + struct ieee80211_scan_ies *ies, int n_channels) +{ + return ((n_ssids <= PROBE_OPTION_MAX) && + (n_channels <= mld->fw->ucode_capa.n_scan_channels) & + (ies->common_ie_len + ies->len[NL80211_BAND_2GHZ] + + ies->len[NL80211_BAND_5GHZ] + ies->len[NL80211_BAND_6GHZ] <= + iwl_mld_scan_max_template_size())); +} + +static void +iwl_mld_scan_build_probe_req(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_scan_ies *ies, + struct iwl_mld_scan_params *params) +{ + struct ieee80211_mgmt *frame = (void *)params->preq.buf; + u8 *pos, *newpos; + const u8 *mac_addr = params->flags & NL80211_SCAN_FLAG_RANDOM_ADDR ? + params->mac_addr : NULL; + + if (mac_addr) + get_random_mask_addr(frame->sa, mac_addr, + params->mac_addr_mask); + else + memcpy(frame->sa, vif->addr, ETH_ALEN); + + frame->frame_control = cpu_to_le16(IEEE80211_STYPE_PROBE_REQ); + eth_broadcast_addr(frame->da); + ether_addr_copy(frame->bssid, params->bssid); + frame->seq_ctrl = 0; + + pos = frame->u.probe_req.variable; + *pos++ = WLAN_EID_SSID; + *pos++ = 0; + + params->preq.mac_header.offset = 0; + params->preq.mac_header.len = cpu_to_le16(24 + 2); + + /* Insert DS parameter set element on 2.4 GHz band */ + newpos = iwl_mld_scan_add_2ghz_elems(mld, + ies->ies[NL80211_BAND_2GHZ], + ies->len[NL80211_BAND_2GHZ], + pos); + params->preq.band_data[0].offset = cpu_to_le16(pos - params->preq.buf); + params->preq.band_data[0].len = cpu_to_le16(newpos - pos); + pos = newpos; + + memcpy(pos, ies->ies[NL80211_BAND_5GHZ], + ies->len[NL80211_BAND_5GHZ]); + params->preq.band_data[1].offset = cpu_to_le16(pos - params->preq.buf); + params->preq.band_data[1].len = + cpu_to_le16(ies->len[NL80211_BAND_5GHZ]); + pos += ies->len[NL80211_BAND_5GHZ]; + + memcpy(pos, ies->ies[NL80211_BAND_6GHZ], + ies->len[NL80211_BAND_6GHZ]); + params->preq.band_data[2].offset = cpu_to_le16(pos - params->preq.buf); + params->preq.band_data[2].len = + cpu_to_le16(ies->len[NL80211_BAND_6GHZ]); + pos += ies->len[NL80211_BAND_6GHZ]; + + memcpy(pos, ies->common_ies, ies->common_ie_len); + params->preq.common_data.offset = cpu_to_le16(pos - params->preq.buf); + + iwl_mld_scan_add_tpc_report_elem(pos + ies->common_ie_len); + params->preq.common_data.len = cpu_to_le16(ies->common_ie_len + + WFA_TPC_IE_LEN); +} + +static u16 +iwl_mld_scan_get_cmd_gen_flags(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, + enum iwl_mld_scan_status scan_status) +{ + u16 flags = 0; + + /* If no direct SSIDs are provided perform a passive scan. Otherwise, + * if there is a single SSID which is not the broadcast SSID, assume + * that the scan is intended for roaming purposes and thus enable Rx on + * all chains to improve chances of hearing the beacons/probe responses. + */ + if (params->n_ssids == 0) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FORCE_PASSIVE; + else if (params->n_ssids == 1 && params->ssids[0].ssid_len) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_USE_ALL_RX_CHAINS; + + if (params->pass_all) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PASS_ALL; + else + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_MATCH; + + if (iwl_mld_scan_is_fragmented(params->type)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1; + + if (!iwl_mld_scan_is_regular(params)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PERIODIC; + + if (params->iter_notif || + mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_ENABLED) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_NTFY_ITER_COMPLETE; + + if (scan_status == IWL_MLD_SCAN_SCHED || + scan_status == IWL_MLD_SCAN_NETDETECT) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_PREEMPTIVE; + + if (params->flags & (NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP | + NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE | + NL80211_SCAN_FLAG_FILS_MAX_CHANNEL_TIME)) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_OCE; + + if ((scan_status == IWL_MLD_SCAN_SCHED || + scan_status == IWL_MLD_SCAN_NETDETECT) && + params->flags & NL80211_SCAN_FLAG_COLOCATED_6GHZ) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_TRIGGER_UHB_SCAN; + + if (params->enable_6ghz_passive) + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_6GHZ_PASSIVE_SCAN; + + flags |= IWL_UMAC_SCAN_GEN_FLAGS_V2_ADAPTIVE_DWELL; + + return flags; +} + +static u8 +iwl_mld_scan_get_cmd_gen_flags2(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, u16 gen_flags) +{ + u8 flags = 0; + + /* TODO: CDB */ + if (params->respect_p2p_go) + flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_LB | + IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_RESPECT_P2P_GO_HB; + + if (params->scan_6ghz) + flags |= IWL_UMAC_SCAN_GEN_PARAMS_FLAGS2_DONT_TOGGLE_ANT; + + return flags; +} + +static void +iwl_mld_scan_cmd_set_dwell(struct iwl_mld *mld, + struct iwl_scan_general_params_v11 *gp, + struct iwl_mld_scan_params *params) +{ + const struct iwl_mld_scan_timing_params *timing = + &scan_timing[params->type]; + + gp->adwell_default_social_chn = + IWL_SCAN_ADWELL_DEFAULT_N_APS_SOCIAL; + gp->adwell_default_2g = IWL_SCAN_ADWELL_DEFAULT_LB_N_APS; + gp->adwell_default_5g = IWL_SCAN_ADWELL_DEFAULT_HB_N_APS; + + if (params->n_ssids && params->ssids[0].ssid_len) + gp->adwell_max_budget = + cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_DIRECTED_SCAN); + else + gp->adwell_max_budget = + cpu_to_le16(IWL_SCAN_ADWELL_MAX_BUDGET_FULL_SCAN); + + gp->scan_priority = cpu_to_le32(IWL_SCAN_PRIORITY_EXT_6); + + gp->max_out_of_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->max_out_time); + gp->suspend_time[SCAN_LB_LMAC_IDX] = cpu_to_le32(timing->suspend_time); + + gp->active_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE; + gp->passive_dwell[SCAN_LB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE; + gp->active_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_ACTIVE; + gp->passive_dwell[SCAN_HB_LMAC_IDX] = IWL_SCAN_DWELL_PASSIVE; + + IWL_DEBUG_SCAN(mld, + "Scan: adwell_max_budget=%d max_out_of_time=%d suspend_time=%d\n", + gp->adwell_max_budget, + gp->max_out_of_time[SCAN_LB_LMAC_IDX], + gp->suspend_time[SCAN_LB_LMAC_IDX]); +} + +static void +iwl_mld_scan_cmd_set_gen_params(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, + struct iwl_scan_general_params_v11 *gp, + enum iwl_mld_scan_status scan_status) +{ + u16 gen_flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif, + scan_status); + u8 gen_flags2 = iwl_mld_scan_get_cmd_gen_flags2(mld, params, vif, + gen_flags); + + IWL_DEBUG_SCAN(mld, "General: flags=0x%x, flags2=0x%x\n", + gen_flags, gen_flags2); + + gp->flags = cpu_to_le16(gen_flags); + gp->flags2 = gen_flags2; + + iwl_mld_scan_cmd_set_dwell(mld, gp, params); + + if (gen_flags & IWL_UMAC_SCAN_GEN_FLAGS_V2_FRAGMENTED_LMAC1) + gp->num_of_fragments[SCAN_LB_LMAC_IDX] = IWL_SCAN_NUM_OF_FRAGS; + + if (params->fw_link_id != IWL_MLD_INVALID_FW_ID) + gp->scan_start_mac_or_link_id = params->fw_link_id; +} + +static int +iwl_mld_scan_cmd_set_sched_params(struct iwl_mld_scan_params *params, + struct iwl_scan_umac_schedule *schedule, + __le16 *delay) +{ + if (WARN_ON(!params->n_scan_plans || + params->n_scan_plans > IWL_MAX_SCHED_SCAN_PLANS)) + return -EINVAL; + + for (int i = 0; i < params->n_scan_plans; i++) { + struct cfg80211_sched_scan_plan *scan_plan = + ¶ms->scan_plans[i]; + + schedule[i].iter_count = scan_plan->iterations; + schedule[i].interval = + cpu_to_le16(scan_plan->interval); + } + + /* If the number of iterations of the last scan plan is set to zero, + * it should run infinitely. However, this is not always the case. + * For example, when regular scan is requested the driver sets one scan + * plan with one iteration. + */ + if (!schedule[params->n_scan_plans - 1].iter_count) + schedule[params->n_scan_plans - 1].iter_count = 0xff; + + *delay = cpu_to_le16(params->delay); + + return 0; +} + +/* We insert the SSIDs in an inverted order, because the FW will + * invert it back. + */ +static void +iwl_mld_scan_cmd_build_ssids(struct iwl_mld_scan_params *params, + struct iwl_ssid_ie *ssids, u32 *ssid_bitmap) +{ + int i, j; + int index; + u32 tmp_bitmap = 0; + + /* copy SSIDs from match list. iwl_config_sched_scan_profiles() + * uses the order of these ssids to config match list. + */ + for (i = 0, j = params->n_match_sets - 1; + j >= 0 && i < PROBE_OPTION_MAX; + i++, j--) { + /* skip empty SSID match_sets */ + if (!params->match_sets[j].ssid.ssid_len) + continue; + + ssids[i].id = WLAN_EID_SSID; + ssids[i].len = params->match_sets[j].ssid.ssid_len; + memcpy(ssids[i].ssid, params->match_sets[j].ssid.ssid, + ssids[i].len); + } + + /* add SSIDs from scan SSID list */ + for (j = params->n_ssids - 1; + j >= 0 && i < PROBE_OPTION_MAX; + i++, j--) { + index = iwl_mld_scan_ssid_exist(params->ssids[j].ssid, + params->ssids[j].ssid_len, + ssids); + if (index < 0) { + ssids[i].id = WLAN_EID_SSID; + ssids[i].len = params->ssids[j].ssid_len; + memcpy(ssids[i].ssid, params->ssids[j].ssid, + ssids[i].len); + tmp_bitmap |= BIT(i); + } else { + tmp_bitmap |= BIT(index); + } + } + + if (ssid_bitmap) + *ssid_bitmap = tmp_bitmap; +} + +static void +iwl_mld_scan_fill_6g_chan_list(struct iwl_mld_scan_params *params, + struct iwl_scan_probe_params_v4 *pp) +{ + int j, idex_s = 0, idex_b = 0; + struct cfg80211_scan_6ghz_params *scan_6ghz_params = + params->scan_6ghz_params; + + for (j = 0; + j < params->n_ssids && idex_s < SCAN_SHORT_SSID_MAX_SIZE; + j++) { + if (!params->ssids[j].ssid_len) + continue; + + pp->short_ssid[idex_s] = + cpu_to_le32(~crc32_le(~0, params->ssids[j].ssid, + params->ssids[j].ssid_len)); + + /* hidden 6ghz scan */ + pp->direct_scan[idex_s].id = WLAN_EID_SSID; + pp->direct_scan[idex_s].len = params->ssids[j].ssid_len; + memcpy(pp->direct_scan[idex_s].ssid, params->ssids[j].ssid, + params->ssids[j].ssid_len); + idex_s++; + } + + /* Populate the arrays of the short SSIDs and the BSSIDs using the 6GHz + * collocated parameters. This might not be optimal, as this processing + * does not (yet) correspond to the actual channels, so it is possible + * that some entries would be left out. + */ + for (j = 0; j < params->n_6ghz_params; j++) { + int k; + + /* First, try to place the short SSID */ + if (scan_6ghz_params[j].short_ssid_valid) { + for (k = 0; k < idex_s; k++) { + if (pp->short_ssid[k] == + cpu_to_le32(scan_6ghz_params[j].short_ssid)) + break; + } + + if (k == idex_s && idex_s < SCAN_SHORT_SSID_MAX_SIZE) { + pp->short_ssid[idex_s++] = + cpu_to_le32(scan_6ghz_params[j].short_ssid); + } + } + + /* try to place BSSID for the same entry */ + for (k = 0; k < idex_b; k++) { + if (!memcmp(&pp->bssid_array[k], + scan_6ghz_params[j].bssid, ETH_ALEN)) + break; + } + + if (k == idex_b && idex_b < SCAN_BSSID_MAX_SIZE && + !WARN_ONCE(!is_valid_ether_addr(scan_6ghz_params[j].bssid), + "scan: invalid BSSID at index %u, index_b=%u\n", + j, idex_b)) { + memcpy(&pp->bssid_array[idex_b++], + scan_6ghz_params[j].bssid, ETH_ALEN); + } + } + + pp->short_ssid_num = idex_s; + pp->bssid_num = idex_b; +} + +static void +iwl_mld_scan_cmd_set_probe_params(struct iwl_mld_scan_params *params, + struct iwl_scan_probe_params_v4 *pp, + u32 *bitmap_ssid) +{ + pp->preq = params->preq; + + if (params->scan_6ghz) { + iwl_mld_scan_fill_6g_chan_list(params, pp); + return; + } + + /* relevant only for 2.4 GHz /5 GHz scan */ + iwl_mld_scan_cmd_build_ssids(params, pp->direct_scan, bitmap_ssid); +} + +static bool +iwl_mld_scan_use_ebs(struct iwl_mld *mld, struct ieee80211_vif *vif, + bool low_latency) +{ + const struct iwl_ucode_capabilities *capa = &mld->fw->ucode_capa; + + /* We can only use EBS if: + * 1. the feature is supported. + * 2. the last EBS was successful. + * 3. it's not a p2p find operation. + * 4. we are not in low latency mode, + * or if fragmented ebs is supported by the FW + * 5. the VIF is not an AP interface (scan wants survey results) + */ + return ((capa->flags & IWL_UCODE_TLV_FLAGS_EBS_SUPPORT) && + !mld->scan.last_ebs_failed && + vif->type != NL80211_IFTYPE_P2P_DEVICE && + (!low_latency || fw_has_api(capa, IWL_UCODE_TLV_API_FRAG_EBS)) && + ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_AP); +} + +static u8 +iwl_mld_scan_cmd_set_chan_flags(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, + bool low_latency) +{ + u8 flags = 0; + + flags |= IWL_SCAN_CHANNEL_FLAG_ENABLE_CHAN_ORDER; + + if (iwl_mld_scan_use_ebs(mld, vif, low_latency)) + flags |= IWL_SCAN_CHANNEL_FLAG_EBS | + IWL_SCAN_CHANNEL_FLAG_EBS_ACCURATE | + IWL_SCAN_CHANNEL_FLAG_CACHE_ADD; + + /* set fragmented ebs for fragmented scan */ + if (iwl_mld_scan_is_fragmented(params->type)) + flags |= IWL_SCAN_CHANNEL_FLAG_EBS_FRAG; + + /* Force EBS in case the scan is a fragmented and there is a need + * to take P2P GO operation into consideration during scan operation. + */ + /* TODO: CDB */ + if (iwl_mld_scan_is_fragmented(params->type) && + params->respect_p2p_go) { + IWL_DEBUG_SCAN(mld, "Respect P2P GO. Force EBS\n"); + flags |= IWL_SCAN_CHANNEL_FLAG_FORCE_EBS; + } + + return flags; +} + +static const u8 p2p_go_friendly_chs[] = { + 36, 40, 44, 48, 149, 153, 157, 161, 165, +}; + +static const u8 social_chs[] = { + 1, 6, 11 +}; + +static u32 iwl_mld_scan_ch_n_aps_flag(enum nl80211_iftype vif_type, u8 ch_id) +{ + if (vif_type != NL80211_IFTYPE_P2P_DEVICE) + return 0; + + for (int i = 0; i < ARRAY_SIZE(p2p_go_friendly_chs); i++) { + if (ch_id == p2p_go_friendly_chs[i]) + return IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT; + } + + for (int i = 0; i < ARRAY_SIZE(social_chs); i++) { + if (ch_id == social_chs[i]) + return IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS_BIT; + } + + return 0; +} + +static void +iwl_mld_scan_cmd_set_channels(struct iwl_mld *mld, + struct ieee80211_channel **channels, + struct iwl_scan_channel_params_v7 *cp, + int n_channels, u32 flags, + enum nl80211_iftype vif_type) +{ + for (int i = 0; i < n_channels; i++) { + enum nl80211_band band = channels[i]->band; + struct iwl_scan_channel_cfg_umac *cfg = &cp->channel_config[i]; + u8 iwl_band = iwl_mld_nl80211_band_to_fw(band); + u32 n_aps_flag = + iwl_mld_scan_ch_n_aps_flag(vif_type, + channels[i]->hw_value); + + if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE) + n_aps_flag = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY_BIT; + + cfg->flags = cpu_to_le32(flags | n_aps_flag); + cfg->channel_num = channels[i]->hw_value; + if (cfg80211_channel_is_psc(channels[i])) + cfg->flags = 0; + + if (band == NL80211_BAND_6GHZ) { + /* 6 GHz channels should only appear in a scan request + * that has scan_6ghz set. The only exception is MLO + * scan, which has to be passive. + */ + WARN_ON_ONCE(cfg->flags != 0); + cfg->flags = + cpu_to_le32(IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE); + } + + cfg->v2.iter_count = 1; + cfg->v2.iter_interval = 0; + cfg->flags |= cpu_to_le32(iwl_band << + IWL_CHAN_CFG_FLAGS_BAND_POS); + } +} + +static u8 +iwl_mld_scan_cfg_channels_6g(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + u32 n_channels, + struct iwl_scan_probe_params_v4 *pp, + struct iwl_scan_channel_params_v7 *cp, + enum nl80211_iftype vif_type) +{ + struct cfg80211_scan_6ghz_params *scan_6ghz_params = + params->scan_6ghz_params; + u32 i; + u8 ch_cnt; + + for (i = 0, ch_cnt = 0; i < params->n_channels; i++) { + struct iwl_scan_channel_cfg_umac *cfg = + &cp->channel_config[ch_cnt]; + + u32 s_ssid_bitmap = 0, bssid_bitmap = 0, flags = 0; + u8 k, n_s_ssids = 0, n_bssids = 0; + u8 max_s_ssids, max_bssids; + bool force_passive = false, found = false, allow_passive = true, + unsolicited_probe_on_chan = false, psc_no_listen = false; + s8 psd_20 = IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED; + + /* Avoid performing passive scan on non PSC channels unless the + * scan is specifically a passive scan, i.e., no SSIDs + * configured in the scan command. + */ + if (!cfg80211_channel_is_psc(params->channels[i]) && + !params->n_6ghz_params && params->n_ssids) + continue; + + cfg->channel_num = params->channels[i]->hw_value; + cfg->flags |= + cpu_to_le32(PHY_BAND_6 << IWL_CHAN_CFG_FLAGS_BAND_POS); + + cfg->v5.iter_count = 1; + cfg->v5.iter_interval = 0; + + for (u32 j = 0; j < params->n_6ghz_params; j++) { + s8 tmp_psd_20; + + if (!(scan_6ghz_params[j].channel_idx == i)) + continue; + + unsolicited_probe_on_chan |= + scan_6ghz_params[j].unsolicited_probe; + + /* Use the highest PSD value allowed as advertised by + * APs for this channel + */ + tmp_psd_20 = scan_6ghz_params[j].psd_20; + if (tmp_psd_20 != + IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED && + (psd_20 == + IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED || + psd_20 < tmp_psd_20)) + psd_20 = tmp_psd_20; + + psc_no_listen |= scan_6ghz_params[j].psc_no_listen; + } + + /* In the following cases apply passive scan: + * 1. Non fragmented scan: + * - PSC channel with NO_LISTEN_FLAG on should be treated + * like non PSC channel + * - Non PSC channel with more than 3 short SSIDs or more + * than 9 BSSIDs. + * - Non PSC Channel with unsolicited probe response and + * more than 2 short SSIDs or more than 6 BSSIDs. + * - PSC channel with more than 2 short SSIDs or more than + * 6 BSSIDs. + * 2. Fragmented scan: + * - PSC channel with more than 1 SSID or 3 BSSIDs. + * - Non PSC channel with more than 2 SSIDs or 6 BSSIDs. + * - Non PSC channel with unsolicited probe response and + * more than 1 SSID or more than 3 BSSIDs. + */ + if (!iwl_mld_scan_is_fragmented(params->type)) { + if (!cfg80211_channel_is_psc(params->channels[i]) || + psc_no_listen) { + if (unsolicited_probe_on_chan) { + max_s_ssids = 2; + max_bssids = 6; + } else { + max_s_ssids = 3; + max_bssids = 9; + } + } else { + max_s_ssids = 2; + max_bssids = 6; + } + } else if (cfg80211_channel_is_psc(params->channels[i])) { + max_s_ssids = 1; + max_bssids = 3; + } else { + if (unsolicited_probe_on_chan) { + max_s_ssids = 1; + max_bssids = 3; + } else { + max_s_ssids = 2; + max_bssids = 6; + } + } + + /* To optimize the scan time, i.e., reduce the scan dwell time + * on each channel, the below logic tries to set 3 direct BSSID + * probe requests for each broadcast probe request with a short + * SSID. + */ + for (u32 j = 0; j < params->n_6ghz_params; j++) { + if (!(scan_6ghz_params[j].channel_idx == i)) + continue; + + found = false; + + for (k = 0; + k < pp->short_ssid_num && n_s_ssids < max_s_ssids; + k++) { + if (!scan_6ghz_params[j].unsolicited_probe && + le32_to_cpu(pp->short_ssid[k]) == + scan_6ghz_params[j].short_ssid) { + /* Relevant short SSID bit set */ + if (s_ssid_bitmap & BIT(k)) { + found = true; + break; + } + + /* Prefer creating BSSID entries unless + * the short SSID probe can be done in + * the same channel dwell iteration. + * + * We also need to create a short SSID + * entry for any hidden AP. + */ + if (3 * n_s_ssids > n_bssids && + !pp->direct_scan[k].len) + break; + + /* Hidden AP, cannot do passive scan */ + if (pp->direct_scan[k].len) + allow_passive = false; + + s_ssid_bitmap |= BIT(k); + n_s_ssids++; + found = true; + break; + } + } + + if (found) + continue; + + for (k = 0; k < pp->bssid_num; k++) { + if (memcmp(&pp->bssid_array[k], + scan_6ghz_params[j].bssid, + ETH_ALEN)) + continue; + + if (bssid_bitmap & BIT(k)) + break; + + if (n_bssids < max_bssids) { + bssid_bitmap |= BIT(k); + n_bssids++; + } else { + force_passive = TRUE; + } + + break; + } + } + + if (cfg80211_channel_is_psc(params->channels[i]) && + psc_no_listen) + flags |= IWL_UHB_CHAN_CFG_FLAG_PSC_CHAN_NO_LISTEN; + + if (unsolicited_probe_on_chan) + flags |= IWL_UHB_CHAN_CFG_FLAG_UNSOLICITED_PROBE_RES; + + if ((allow_passive && force_passive) || + (!(bssid_bitmap | s_ssid_bitmap) && + !cfg80211_channel_is_psc(params->channels[i]))) + flags |= IWL_UHB_CHAN_CFG_FLAG_FORCE_PASSIVE; + else + flags |= bssid_bitmap | (s_ssid_bitmap << 16); + + cfg->flags |= cpu_to_le32(flags); + cfg->v5.psd_20 = psd_20; + + ch_cnt++; + } + + if (params->n_channels > ch_cnt) + IWL_DEBUG_SCAN(mld, + "6GHz: reducing number channels: (%u->%u)\n", + params->n_channels, ch_cnt); + + return ch_cnt; +} + +static int +iwl_mld_scan_cmd_set_6ghz_chan_params(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, + struct iwl_scan_req_params_v17 *scan_p, + enum iwl_mld_scan_status scan_status) +{ + struct iwl_scan_channel_params_v7 *chan_p = &scan_p->channel_params; + struct iwl_scan_probe_params_v4 *probe_p = &scan_p->probe_params; + + chan_p->flags = iwl_mld_scan_get_cmd_gen_flags(mld, params, vif, + scan_status); + chan_p->count = iwl_mld_scan_cfg_channels_6g(mld, params, + params->n_channels, + probe_p, chan_p, + vif->type); + if (!chan_p->count) + return -EINVAL; + + if (!params->n_ssids || + (params->n_ssids == 1 && !params->ssids[0].ssid_len)) + chan_p->flags |= IWL_SCAN_CHANNEL_FLAG_6G_PSC_NO_FILTER; + + return 0; +} + +static int +iwl_mld_scan_cmd_set_chan_params(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif, + struct iwl_scan_req_params_v17 *scan_p, + bool low_latency, + enum iwl_mld_scan_status scan_status, + u32 channel_cfg_flags) +{ + struct iwl_scan_channel_params_v7 *cp = &scan_p->channel_params; + struct ieee80211_supported_band *sband = + &mld->nvm_data->bands[NL80211_BAND_6GHZ]; + + cp->n_aps_override[0] = IWL_SCAN_ADWELL_N_APS_GO_FRIENDLY; + cp->n_aps_override[1] = IWL_SCAN_ADWELL_N_APS_SOCIAL_CHS; + + if (IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE) + cp->n_aps_override[0] = IWL_MLD_ADAPTIVE_DWELL_NUM_APS_OVERRIDE; + + if (params->scan_6ghz) + return iwl_mld_scan_cmd_set_6ghz_chan_params(mld, params, + vif, scan_p, + scan_status); + + /* relevant only for 2.4 GHz/5 GHz scan */ + cp->flags = iwl_mld_scan_cmd_set_chan_flags(mld, params, vif, + low_latency); + cp->count = params->n_channels; + + iwl_mld_scan_cmd_set_channels(mld, params->channels, cp, + params->n_channels, channel_cfg_flags, + vif->type); + + if (!params->enable_6ghz_passive) + return 0; + + /* fill 6 GHz passive scan cfg */ + for (int i = 0; i < sband->n_channels; i++) { + struct ieee80211_channel *channel = + &sband->channels[i]; + struct iwl_scan_channel_cfg_umac *cfg = + &cp->channel_config[cp->count]; + + if (!cfg80211_channel_is_psc(channel)) + continue; + + cfg->channel_num = channel->hw_value; + cfg->v5.iter_count = 1; + cfg->v5.iter_interval = 0; + cfg->v5.psd_20 = + IEEE80211_RNR_TBTT_PARAMS_PSD_RESERVED; + cfg->flags = cpu_to_le32(PHY_BAND_6 << + IWL_CHAN_CFG_FLAGS_BAND_POS); + cp->count++; + } + + return 0; +} + +static int +iwl_mld_scan_build_cmd(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct iwl_mld_scan_params *params, + enum iwl_mld_scan_status scan_status, + bool low_latency) +{ + struct iwl_scan_req_umac_v17 *cmd = mld->scan.cmd; + struct iwl_scan_req_params_v17 *scan_p = &cmd->scan_params; + u32 bitmap_ssid = 0; + int uid, ret; + + memset(mld->scan.cmd, 0, mld->scan.cmd_size); + + /* find a free UID entry */ + uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_NONE); + if (uid < 0) + return uid; + + cmd->uid = cpu_to_le32(uid); + cmd->ooc_priority = + cpu_to_le32(iwl_mld_scan_ooc_priority(scan_status)); + + iwl_mld_scan_cmd_set_gen_params(mld, params, vif, + &scan_p->general_params, scan_status); + + ret = iwl_mld_scan_cmd_set_sched_params(params, + scan_p->periodic_params.schedule, + &scan_p->periodic_params.delay); + if (ret) + return ret; + + iwl_mld_scan_cmd_set_probe_params(params, &scan_p->probe_params, + &bitmap_ssid); + + ret = iwl_mld_scan_cmd_set_chan_params(mld, params, vif, scan_p, + low_latency, scan_status, + bitmap_ssid); + if (ret) + return ret; + + return uid; +} + +static bool +iwl_mld_scan_pass_all(struct iwl_mld *mld, + struct cfg80211_sched_scan_request *req) +{ + if (req->n_match_sets && req->match_sets[0].ssid.ssid_len) { + IWL_DEBUG_SCAN(mld, + "Sending scheduled scan with filtering, n_match_sets %d\n", + req->n_match_sets); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + return false; + } + + IWL_DEBUG_SCAN(mld, "Sending Scheduled scan without filtering\n"); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED; + + return true; +} + +static int +iwl_mld_config_sched_scan_profiles(struct iwl_mld *mld, + struct cfg80211_sched_scan_request *req) +{ + struct iwl_host_cmd hcmd = { + .id = SCAN_OFFLOAD_UPDATE_PROFILES_CMD, + .dataflags[0] = IWL_HCMD_DFL_NOCOPY, + }; + struct iwl_scan_offload_profile *profile; + struct iwl_scan_offload_profile_cfg_data *cfg_data; + struct iwl_scan_offload_profile_cfg *profile_cfg; + struct iwl_scan_offload_blocklist *blocklist; + u32 blocklist_size = IWL_SCAN_MAX_BLACKLIST_LEN * sizeof(*blocklist); + u32 cmd_size = blocklist_size + sizeof(*profile_cfg); + u8 *cmd; + int ret; + + if (WARN_ON(req->n_match_sets > IWL_SCAN_MAX_PROFILES_V2)) + return -EIO; + + cmd = kzalloc(cmd_size, GFP_KERNEL); + if (!cmd) + return -ENOMEM; + + hcmd.data[0] = cmd; + hcmd.len[0] = cmd_size; + + blocklist = (struct iwl_scan_offload_blocklist *)cmd; + profile_cfg = (struct iwl_scan_offload_profile_cfg *)(cmd + blocklist_size); + + /* No blocklist configuration */ + cfg_data = &profile_cfg->data; + cfg_data->num_profiles = req->n_match_sets; + cfg_data->active_clients = SCAN_CLIENT_SCHED_SCAN; + cfg_data->pass_match = SCAN_CLIENT_SCHED_SCAN; + cfg_data->match_notify = SCAN_CLIENT_SCHED_SCAN; + + if (!req->n_match_sets || !req->match_sets[0].ssid.ssid_len) + cfg_data->any_beacon_notify = SCAN_CLIENT_SCHED_SCAN; + + for (int i = 0; i < req->n_match_sets; i++) { + profile = &profile_cfg->profiles[i]; + + /* Support any cipher and auth algorithm */ + profile->unicast_cipher = 0xff; + profile->auth_alg = IWL_AUTH_ALGO_UNSUPPORTED | + IWL_AUTH_ALGO_NONE | IWL_AUTH_ALGO_PSK | + IWL_AUTH_ALGO_8021X | IWL_AUTH_ALGO_SAE | + IWL_AUTH_ALGO_8021X_SHA384 | IWL_AUTH_ALGO_OWE; + profile->network_type = IWL_NETWORK_TYPE_ANY; + profile->band_selection = IWL_SCAN_OFFLOAD_SELECT_ANY; + profile->client_bitmap = SCAN_CLIENT_SCHED_SCAN; + profile->ssid_index = i; + } + + IWL_DEBUG_SCAN(mld, + "Sending scheduled scan profile config (n_match_sets=%u)\n", + req->n_match_sets); + + ret = iwl_mld_send_cmd(mld, &hcmd); + + kfree(cmd); + + return ret; +} + +static int +iwl_mld_sched_scan_handle_non_psc_channels(struct iwl_mld_scan_params *params, + bool *non_psc_included) +{ + int i, j; + + *non_psc_included = false; + /* for 6 GHZ band only PSC channels need to be added */ + for (i = 0; i < params->n_channels; i++) { + struct ieee80211_channel *channel = params->channels[i]; + + if (channel->band == NL80211_BAND_6GHZ && + !cfg80211_channel_is_psc(channel)) { + *non_psc_included = true; + break; + } + } + + if (!*non_psc_included) + return 0; + + params->channels = + kmemdup(params->channels, + sizeof(params->channels[0]) * params->n_channels, + GFP_KERNEL); + if (!params->channels) + return -ENOMEM; + + for (i = j = 0; i < params->n_channels; i++) { + if (params->channels[i]->band == NL80211_BAND_6GHZ && + !cfg80211_channel_is_psc(params->channels[i])) + continue; + params->channels[j++] = params->channels[i]; + } + + params->n_channels = j; + + return 0; +} + +static void +iwl_mld_scan_6ghz_passive_scan(struct iwl_mld *mld, + struct iwl_mld_scan_params *params, + struct ieee80211_vif *vif) +{ + struct ieee80211_supported_band *sband = + &mld->nvm_data->bands[NL80211_BAND_6GHZ]; + u32 n_disabled, i; + + params->enable_6ghz_passive = false; + + /* 6 GHz passive scan may be enabled in the first 2.4 GHz/5 GHz scan + * phase to discover geo location if no AP's are found. Skip it when + * we're in the 6 GHz scan phase. + */ + if (params->scan_6ghz) + return; + + /* 6 GHz passive scan allowed only on station interface */ + if (vif->type != NL80211_IFTYPE_STATION) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: not station interface\n"); + return; + } + + /* 6 GHz passive scan is allowed in a defined time interval following + * HW reset or resume flow, or while not associated and a large + * interval has passed since the last 6 GHz passive scan. + */ + if ((vif->cfg.assoc || + time_after(mld->scan.last_6ghz_passive_jiffies + + (IWL_MLD_6GHZ_PASSIVE_SCAN_TIMEOUT * HZ), jiffies)) && + (time_before(mld->scan.last_start_time_jiffies + + (IWL_MLD_6GHZ_PASSIVE_SCAN_ASSOC_TIMEOUT * HZ), + jiffies))) { + IWL_DEBUG_SCAN(mld, "6GHz passive scan: %s\n", + vif->cfg.assoc ? "associated" : + "timeout did not expire"); + return; + } + + /* not enough channels in the regular scan request */ + if (params->n_channels < IWL_MLD_6GHZ_PASSIVE_SCAN_MIN_CHANS) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: not enough channels %d\n", + params->n_channels); + return; + } + + for (i = 0; i < params->n_ssids; i++) { + if (!params->ssids[i].ssid_len) + break; + } + + /* not a wildcard scan, so cannot enable passive 6 GHz scan */ + if (i == params->n_ssids) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: no wildcard SSID\n"); + return; + } + + if (!sband || !sband->n_channels) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: no 6GHz channels\n"); + return; + } + + for (i = 0, n_disabled = 0; i < sband->n_channels; i++) { + if (sband->channels[i].flags & (IEEE80211_CHAN_DISABLED)) + n_disabled++; + } + + /* Not all the 6 GHz channels are disabled, so no need for 6 GHz + * passive scan + */ + if (n_disabled != sband->n_channels) { + IWL_DEBUG_SCAN(mld, + "6GHz passive scan: 6GHz channels enabled\n"); + return; + } + + /* all conditions to enable 6 GHz passive scan are satisfied */ + IWL_DEBUG_SCAN(mld, "6GHz passive scan: can be enabled\n"); + params->enable_6ghz_passive = true; +} + +static void +iwl_mld_scan_set_link_id(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct iwl_mld_scan_params *params, + s8 tsf_report_link_id, + enum iwl_mld_scan_status scan_status) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link; + + if (tsf_report_link_id < 0) { + if (vif->active_links) + tsf_report_link_id = __ffs(vif->active_links); + else + tsf_report_link_id = 0; + } + + link = iwl_mld_link_dereference_check(mld_vif, tsf_report_link_id); + if (!WARN_ON(!link)) { + params->fw_link_id = link->fw_id; + /* we to store fw_link_id only for regular scan, + * and use it in scan complete notif + */ + if (scan_status == IWL_MLD_SCAN_REGULAR) + mld->scan.fw_link_id = link->fw_id; + } else { + mld->scan.fw_link_id = IWL_MLD_INVALID_FW_ID; + params->fw_link_id = IWL_MLD_INVALID_FW_ID; + } +} + +static int +_iwl_mld_single_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req, + struct ieee80211_scan_ies *ies, + enum iwl_mld_scan_status scan_status) +{ + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC), + .len = { mld->scan.cmd_size, }, + .data = { mld->scan.cmd, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + struct iwl_mld_scan_iter_data scan_iter_data = { + .current_vif = vif, + }; + struct cfg80211_sched_scan_plan scan_plan = {.iterations = 1}; + struct iwl_mld_scan_params params = {}; + int ret, uid; + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(!mld->scan.cmd)) + return -ENOMEM; + + if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, req->n_channels)) + return -ENOBUFS; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_scan_iterator, + &scan_iter_data); + + params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data); + params.n_ssids = req->n_ssids; + params.flags = req->flags; + params.n_channels = req->n_channels; + params.delay = 0; + params.ssids = req->ssids; + params.channels = req->channels; + params.mac_addr = req->mac_addr; + params.mac_addr_mask = req->mac_addr_mask; + params.no_cck = req->no_cck; + params.pass_all = true; + params.n_match_sets = 0; + params.match_sets = NULL; + params.scan_plans = &scan_plan; + params.n_scan_plans = 1; + + params.n_6ghz_params = req->n_6ghz_params; + params.scan_6ghz_params = req->scan_6ghz_params; + params.scan_6ghz = req->scan_6ghz; + + ether_addr_copy(params.bssid, req->bssid); + /* TODO: CDB - per-band flag */ + params.respect_p2p_go = + iwl_mld_get_respect_p2p_go(mld, vif, + scan_iter_data.global_low_latency); + + if (req->duration) + params.iter_notif = true; + + iwl_mld_scan_set_link_id(mld, vif, ¶ms, req->tsf_report_link_id, + scan_status); + + iwl_mld_scan_build_probe_req(mld, vif, ies, ¶ms); + + iwl_mld_scan_6ghz_passive_scan(mld, ¶ms, vif); + + uid = iwl_mld_scan_build_cmd(mld, vif, ¶ms, scan_status, + scan_iter_data.global_low_latency); + if (uid < 0) + return uid; + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (ret) { + IWL_ERR(mld, "Scan failed! ret %d\n", ret); + return ret; + } + + IWL_DEBUG_SCAN(mld, "Scan request send success: status=%u, uid=%u\n", + scan_status, uid); + + mld->scan.uid_status[uid] = scan_status; + mld->scan.status |= scan_status; + + if (params.enable_6ghz_passive) + mld->scan.last_6ghz_passive_jiffies = jiffies; + + return 0; +} + +static int +iwl_mld_scan_send_abort_cmd_status(struct iwl_mld *mld, int uid, u32 *status) +{ + struct iwl_umac_scan_abort abort_cmd = { + .uid = cpu_to_le32(uid), + }; + struct iwl_host_cmd cmd = { + .id = WIDE_ID(LONG_GROUP, SCAN_ABORT_UMAC), + .flags = CMD_WANT_SKB, + .data = { &abort_cmd }, + .len[0] = sizeof(abort_cmd), + }; + struct iwl_rx_packet *pkt; + struct iwl_cmd_response *resp; + u32 resp_len; + int ret; + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) + return ret; + + pkt = cmd.resp_pkt; + + resp_len = iwl_rx_packet_payload_len(pkt); + if (IWL_FW_CHECK(mld, resp_len != sizeof(*resp), + "Scan Abort: unexpected response length %d\n", + resp_len)) { + ret = -EIO; + goto out; + } + + resp = (void *)pkt->data; + *status = le32_to_cpu(resp->status); + +out: + iwl_free_resp(&cmd); + return ret; +} + +static int +iwl_mld_scan_abort(struct iwl_mld *mld, int type, int uid, bool *wait) +{ + enum iwl_umac_scan_abort_status status; + int ret; + + *wait = true; + + IWL_DEBUG_SCAN(mld, "Sending scan abort, uid %u\n", uid); + + ret = iwl_mld_scan_send_abort_cmd_status(mld, uid, &status); + + IWL_DEBUG_SCAN(mld, "Scan abort: ret=%d status=%u\n", ret, status); + + /* We don't need to wait to scan complete in the following cases: + * 1. Driver failed to send the scan abort cmd. + * 2. The FW is no longer familiar with the scan that needs to be + * stopped. It is expected that the scan complete notification was + * already received but not yet processed. + * + * In both cases the flow should continue similar to the case that the + * scan was really aborted. + */ + if (ret || status == IWL_UMAC_SCAN_ABORT_STATUS_NOT_FOUND) + *wait = false; + + return ret; +} + +static int +iwl_mld_scan_stop_wait(struct iwl_mld *mld, int type, int uid) +{ + struct iwl_notification_wait wait_scan_done; + static const u16 scan_comp_notif[] = { SCAN_COMPLETE_UMAC }; + bool wait = true; + int ret; + + iwl_init_notification_wait(&mld->notif_wait, &wait_scan_done, + scan_comp_notif, + ARRAY_SIZE(scan_comp_notif), + NULL, NULL); + + IWL_DEBUG_SCAN(mld, "Preparing to stop scan, type=%x\n", type); + + ret = iwl_mld_scan_abort(mld, type, uid, &wait); + if (ret) { + IWL_DEBUG_SCAN(mld, "couldn't stop scan type=%d\n", type); + goto return_no_wait; + } + + if (!wait) { + IWL_DEBUG_SCAN(mld, "no need to wait for scan type=%d\n", type); + goto return_no_wait; + } + + return iwl_wait_notification(&mld->notif_wait, &wait_scan_done, HZ); + +return_no_wait: + iwl_remove_notification(&mld->notif_wait, &wait_scan_done); + return ret; +} + +int iwl_mld_sched_scan_start(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies, + int type) +{ + struct iwl_host_cmd hcmd = { + .id = WIDE_ID(LONG_GROUP, SCAN_REQ_UMAC), + .len = { mld->scan.cmd_size, }, + .data = { mld->scan.cmd, }, + .dataflags = { IWL_HCMD_DFL_NOCOPY, }, + }; + struct iwl_mld_scan_params params = {}; + struct iwl_mld_scan_iter_data scan_iter_data = { + .current_vif = vif, + }; + bool non_psc_included = false; + int ret, uid; + + /* we should have failed registration if scan_cmd was NULL */ + if (WARN_ON(!mld->scan.cmd)) + return -ENOMEM; + + /* FW supports only a single periodic scan */ + if (mld->scan.status & (IWL_MLD_SCAN_SCHED | IWL_MLD_SCAN_NETDETECT)) + return -EBUSY; + + ieee80211_iterate_active_interfaces_mtx(mld->hw, + IEEE80211_IFACE_ITER_NORMAL, + iwl_mld_scan_iterator, + &scan_iter_data); + + params.type = iwl_mld_get_scan_type(mld, vif, &scan_iter_data); + params.flags = req->flags; + params.n_ssids = req->n_ssids; + params.ssids = req->ssids; + params.n_channels = req->n_channels; + params.channels = req->channels; + params.mac_addr = req->mac_addr; + params.mac_addr_mask = req->mac_addr_mask; + params.no_cck = false; + params.pass_all = iwl_mld_scan_pass_all(mld, req); + params.n_match_sets = req->n_match_sets; + params.match_sets = req->match_sets; + params.n_scan_plans = req->n_scan_plans; + params.scan_plans = req->scan_plans; + /* TODO: CDB - per-band flag */ + params.respect_p2p_go = + iwl_mld_get_respect_p2p_go(mld, vif, + scan_iter_data.global_low_latency); + + /* UMAC scan supports up to 16-bit delays, trim it down to 16-bits */ + params.delay = req->delay > U16_MAX ? U16_MAX : req->delay; + + eth_broadcast_addr(params.bssid); + + ret = iwl_mld_config_sched_scan_profiles(mld, req); + if (ret) + return ret; + + iwl_mld_scan_build_probe_req(mld, vif, ies, ¶ms); + + ret = iwl_mld_sched_scan_handle_non_psc_channels(¶ms, + &non_psc_included); + if (ret) + goto out; + + if (!iwl_mld_scan_fits(mld, req->n_ssids, ies, params.n_channels)) { + ret = -ENOBUFS; + goto out; + } + + uid = iwl_mld_scan_build_cmd(mld, vif, ¶ms, type, + scan_iter_data.global_low_latency); + if (uid < 0) { + ret = uid; + goto out; + } + + ret = iwl_mld_send_cmd(mld, &hcmd); + if (!ret) { + IWL_DEBUG_SCAN(mld, + "Sched scan request send success: type=%u, uid=%u\n", + type, uid); + mld->scan.uid_status[uid] = type; + mld->scan.status |= type; + } else { + IWL_ERR(mld, "Sched scan failed! ret %d\n", ret); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + } + +out: + if (non_psc_included) + kfree(params.channels); + return ret; +} + +int iwl_mld_scan_stop(struct iwl_mld *mld, int type, bool notify) +{ + int uid, ret; + + IWL_DEBUG_SCAN(mld, + "Request to stop scan: type=0x%x, status=0x%x\n", + type, mld->scan.status); + + if (!(mld->scan.status & type)) + return 0; + + uid = iwl_mld_scan_uid_by_status(mld, type); + /* must be valid, we just checked it's running */ + if (WARN_ON_ONCE(uid < 0)) + return uid; + + ret = iwl_mld_scan_stop_wait(mld, type, uid); + if (ret) + IWL_DEBUG_SCAN(mld, "Failed to stop scan\n"); + + /* Clear the scan status so the next scan requests will + * succeed and mark the scan as stopping, so that the Rx + * handler doesn't do anything, as the scan was stopped from + * above. Also remove the handler to not notify mac80211 + * erroneously after a new scan starts, for example. + */ + mld->scan.status &= ~type; + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_SCAN, + uid); + + if (type == IWL_MLD_SCAN_REGULAR) { + if (notify) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(mld->hw, &info); + } + } else if (notify) { + ieee80211_sched_scan_stopped(mld->hw); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + } + + return ret; +} + +int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req, + struct ieee80211_scan_ies *ies) +{ + return _iwl_mld_single_scan_start(mld, vif, req, ies, + IWL_MLD_SCAN_REGULAR); +} + +static void iwl_mld_int_mlo_scan_start(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_channel **channels, + size_t n_channels) +{ + struct cfg80211_scan_request *req __free(kfree) = NULL; + struct ieee80211_scan_ies ies = {}; + size_t size; + int ret; + + IWL_DEBUG_SCAN(mld, "Starting Internal MLO scan: n_channels=%zu\n", + n_channels); + + size = struct_size(req, channels, n_channels); + req = kzalloc(size, GFP_KERNEL); + if (!req) + return; + + /* set the requested channels */ + for (int i = 0; i < n_channels; i++) + req->channels[i] = channels[i]; + + req->n_channels = n_channels; + + /* set the rates */ + for (int i = 0; i < NUM_NL80211_BANDS; i++) + if (mld->wiphy->bands[i]) + req->rates[i] = + (1 << mld->wiphy->bands[i]->n_bitrates) - 1; + + req->wdev = ieee80211_vif_to_wdev(vif); + req->wiphy = mld->wiphy; + req->scan_start = jiffies; + req->tsf_report_link_id = -1; + + ret = _iwl_mld_single_scan_start(mld, vif, req, &ies, + IWL_MLD_SCAN_INT_MLO); + + if (!ret) + mld->scan.last_mlo_scan_time = ktime_get_boottime_ns(); + + IWL_DEBUG_SCAN(mld, "Internal MLO scan: ret=%d\n", ret); +} + +void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif) +{ + struct ieee80211_channel *channels[IEEE80211_MLD_MAX_NUM_LINKS]; + unsigned long usable_links = ieee80211_vif_usable_links(vif); + size_t n_channels = 0; + u8 link_id; + + lockdep_assert_wiphy(mld->wiphy); + + if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif) || + hweight16(vif->valid_links) == 1) + return; + + if (mld->scan.status & IWL_MLD_SCAN_INT_MLO) { + IWL_DEBUG_SCAN(mld, "Internal MLO scan is already running\n"); + return; + } + + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf = + link_conf_dereference_check(vif, link_id); + + if (WARN_ON_ONCE(!link_conf)) + continue; + + channels[n_channels++] = link_conf->chanreq.oper.chan; + } + + if (!n_channels) + return; + + iwl_mld_int_mlo_scan_start(mld, vif, channels, n_channels); +} + +void iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_umac_scan_iter_complete_notif *notif = (void *)pkt->data; + u32 uid = __le32_to_cpu(notif->uid); + + if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status), + "FW reports out-of-range scan UID %d\n", uid)) + return; + + if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR) + mld->scan.start_tsf = le64_to_cpu(notif->start_tsf); + + IWL_DEBUG_SCAN(mld, + "UMAC Scan iteration complete: status=0x%x scanned_channels=%d\n", + notif->status, notif->scanned_channels); + + if (mld->scan.pass_all_sched_res == SCHED_SCAN_PASS_ALL_STATE_FOUND) { + IWL_DEBUG_SCAN(mld, "Pass all scheduled scan results found\n"); + ieee80211_sched_scan_results(mld->hw); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_ENABLED; + } + + IWL_DEBUG_SCAN(mld, + "UMAC Scan iteration complete: scan started at %llu (TSF)\n", + le64_to_cpu(notif->start_tsf)); +} + +void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + IWL_DEBUG_SCAN(mld, "Scheduled scan results\n"); + ieee80211_sched_scan_results(mld->hw); +} + +void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_umac_scan_complete *notif = (void *)pkt->data; + bool aborted = (notif->status == IWL_SCAN_OFFLOAD_ABORTED); + u32 uid = __le32_to_cpu(notif->uid); + + if (IWL_FW_CHECK(mld, uid >= ARRAY_SIZE(mld->scan.uid_status), + "FW reports out-of-range scan UID %d\n", uid)) + return; + + IWL_DEBUG_SCAN(mld, + "Scan completed: uid=%u type=%u, status=%s, EBS=%s\n", + uid, mld->scan.uid_status[uid], + notif->status == IWL_SCAN_OFFLOAD_COMPLETED ? + "completed" : "aborted", + iwl_mld_scan_ebs_status_str(notif->ebs_status)); + IWL_DEBUG_SCAN(mld, "Scan completed: scan_status=0x%x\n", + mld->scan.status); + IWL_DEBUG_SCAN(mld, + "Scan completed: line=%u, iter=%u, elapsed time=%u\n", + notif->last_schedule, notif->last_iter, + __le32_to_cpu(notif->time_from_last_iter)); + + if (IWL_FW_CHECK(mld, !(mld->scan.uid_status[uid] & mld->scan.status), + "FW reports scan UID %d we didn't trigger\n", uid)) + return; + + /* if the scan is already stopping, we don't need to notify mac80211 */ + if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_REGULAR) { + struct cfg80211_scan_info info = { + .aborted = aborted, + .scan_start_tsf = mld->scan.start_tsf, + }; + int fw_link_id = mld->scan.fw_link_id; + struct ieee80211_bss_conf *link_conf = NULL; + + if (fw_link_id != IWL_MLD_INVALID_FW_ID) + link_conf = + wiphy_dereference(mld->wiphy, + mld->fw_id_to_bss_conf[fw_link_id]); + + /* It is possible that by the time the scan is complete the + * link was already removed and is not valid. + */ + if (link_conf) + ether_addr_copy(info.tsf_bssid, link_conf->bssid); + else + IWL_DEBUG_SCAN(mld, "Scan link is no longer valid\n"); + + ieee80211_scan_completed(mld->hw, &info); + + /* Scan is over, we can check again the tpt counters */ + iwl_mld_stop_ignoring_tpt_updates(mld); + } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_SCHED) { + ieee80211_sched_scan_stopped(mld->hw); + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + } else if (mld->scan.uid_status[uid] == IWL_MLD_SCAN_INT_MLO) { + IWL_DEBUG_SCAN(mld, "Internal MLO scan completed\n"); + + /* + * We limit link selection to internal MLO scans as otherwise + * we do not know whether all channels were covered. + */ + iwl_mld_select_links(mld); + } + + mld->scan.status &= ~mld->scan.uid_status[uid]; + + IWL_DEBUG_SCAN(mld, "Scan completed: after update: scan_status=0x%x\n", + mld->scan.status); + + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + + if (notif->ebs_status != IWL_SCAN_EBS_SUCCESS && + notif->ebs_status != IWL_SCAN_EBS_INACTIVE) + mld->scan.last_ebs_failed = true; +} + +/* This function is used in nic restart flow, to inform mac80211 about scans + * that were aborted by restart flow or by an assert. + */ +void iwl_mld_report_scan_aborted(struct iwl_mld *mld) +{ + int uid; + + uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_REGULAR); + if (uid >= 0) { + struct cfg80211_scan_info info = { + .aborted = true, + }; + + ieee80211_scan_completed(mld->hw, &info); + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + } + + uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_SCHED); + if (uid >= 0) { + mld->scan.pass_all_sched_res = SCHED_SCAN_PASS_ALL_STATE_DISABLED; + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + + /* sched scan will be restarted by mac80211 in reconfig. + * report to mac80211 that sched scan stopped only if we won't + * restart the firmware. + */ + if (!iwlwifi_mod_params.fw_restart) + ieee80211_sched_scan_stopped(mld->hw); + } + + uid = iwl_mld_scan_uid_by_status(mld, IWL_MLD_SCAN_INT_MLO); + if (uid >= 0) { + IWL_DEBUG_SCAN(mld, "Internal MLO scan aborted\n"); + mld->scan.uid_status[uid] = IWL_MLD_SCAN_NONE; + } + + BUILD_BUG_ON(IWL_MLD_SCAN_NONE != 0); + memset(mld->scan.uid_status, 0, sizeof(mld->scan.uid_status)); +} + +int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld) +{ + u8 scan_cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, SCAN_REQ_UMAC, + IWL_FW_CMD_VER_UNKNOWN); + size_t scan_cmd_size; + + if (scan_cmd_ver == 17) { + scan_cmd_size = sizeof(struct iwl_scan_req_umac_v17); + } else { + IWL_ERR(mld, "Unexpected scan cmd version %d\n", scan_cmd_ver); + return -EINVAL; + } + + mld->scan.cmd = kmalloc(scan_cmd_size, GFP_KERNEL); + if (!mld->scan.cmd) + return -ENOMEM; + + mld->scan.cmd_size = scan_cmd_size; + + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/scan.h b/drivers/net/wireless/intel/iwlwifi/mld/scan.h new file mode 100644 index 000000000000..3ae940d55065 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/scan.h @@ -0,0 +1,136 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifndef __iwl_mld_scan_h__ +#define __iwl_mld_scan_h__ + +int iwl_mld_alloc_scan_cmd(struct iwl_mld *mld); + +int iwl_mld_regular_scan_start(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct cfg80211_scan_request *req, + struct ieee80211_scan_ies *ies); + +void iwl_mld_int_mlo_scan(struct iwl_mld *mld, struct ieee80211_vif *vif); + +void iwl_mld_handle_scan_iter_complete_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +int iwl_mld_scan_stop(struct iwl_mld *mld, int type, bool notify); + +int iwl_mld_sched_scan_start(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct cfg80211_sched_scan_request *req, + struct ieee80211_scan_ies *ies, + int type); + +void iwl_mld_handle_match_found_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +void iwl_mld_handle_scan_complete_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +#define WFA_TPC_IE_LEN 9 + +static inline int iwl_mld_scan_max_template_size(void) +{ +#define MAC_HDR_LEN 24 +#define DS_IE_LEN 3 +#define SSID_IE_LEN 2 + +/* driver create the 802.11 header, WFA TPC IE, DS parameter and SSID IE */ +#define DRIVER_TOTAL_IES_LEN \ + (MAC_HDR_LEN + WFA_TPC_IE_LEN + DS_IE_LEN + SSID_IE_LEN) + + BUILD_BUG_ON(SCAN_OFFLOAD_PROBE_REQ_SIZE < DRIVER_TOTAL_IES_LEN); + + return SCAN_OFFLOAD_PROBE_REQ_SIZE - DRIVER_TOTAL_IES_LEN; +} + +void iwl_mld_report_scan_aborted(struct iwl_mld *mld); + +enum iwl_mld_scan_status { + IWL_MLD_SCAN_NONE = 0, + IWL_MLD_SCAN_REGULAR = BIT(0), + IWL_MLD_SCAN_SCHED = BIT(1), + IWL_MLD_SCAN_NETDETECT = BIT(2), + IWL_MLD_SCAN_INT_MLO = BIT(3), +}; + +/* enum iwl_mld_pass_all_sched_results_states - Defines the states for + * handling/passing scheduled scan results to mac80211 + * @SCHED_SCAN_PASS_ALL_STATE_DISABLED: Don't pass all scan results, only when + * a match found. + * @SCHED_SCAN_PASS_ALL_STATE_ENABLED: Pass all scan results is enabled + * (no filtering). + * @SCHED_SCAN_PASS_ALL_STATE_FOUND: A scan result is found, pass it on the + * next scan iteration complete notification. + */ +enum iwl_mld_pass_all_sched_results_states { + SCHED_SCAN_PASS_ALL_STATE_DISABLED, + SCHED_SCAN_PASS_ALL_STATE_ENABLED, + SCHED_SCAN_PASS_ALL_STATE_FOUND, +}; + +/** + * enum iwl_mld_traffic_load - Levels of traffic load + * + * @IWL_MLD_TRAFFIC_LOW: low traffic load + * @IWL_MLD_TRAFFIC_MEDIUM: medium traffic load + * @IWL_MLD_TRAFFIC_HIGH: high traffic load + */ +enum iwl_mld_traffic_load { + IWL_MLD_TRAFFIC_LOW, + IWL_MLD_TRAFFIC_MEDIUM, + IWL_MLD_TRAFFIC_HIGH, +}; + +/** + * struct iwl_mld_scan - Scan data + * @status: scan status, a combination of %enum iwl_mld_scan_status, + * reflects the %scan.uid_status array. + * @uid_status: array to track the scan status per uid. + * @start_tsf: start time of last scan in TSF of the link that requested + * the scan. + * @last_ebs_failed: true if the last EBS (Energy Based Scan) failed. + * @pass_all_sched_res: see %enum iwl_mld_pass_all_sched_results_states. + * @fw_link_id: the current (regular) scan fw link id, used by scan + * complete notif. + * @traffic_load: traffic load related data + * @traffic_load.last_stats_ts_usec: The timestamp of the last statistics + * notification, used to calculate the elapsed time between two + * notifications and determine the traffic load + * @traffic_load.status: The current traffic load status, see + * &enum iwl_mld_traffic_load + * @cmd_size: size of %cmd. + * @cmd: pointer to scan cmd buffer (allocated once in op mode start). + * @last_6ghz_passive_jiffies: stores the last 6GHz passive scan time + * in jiffies. + * @last_start_time_jiffies: stores the last start time in jiffies + * (interface up/reset/resume). + * @last_mlo_scan_time: start time of the last MLO scan in nanoseconds since + * boot. + */ +struct iwl_mld_scan { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + unsigned int status; + u32 uid_status[IWL_MAX_UMAC_SCANS]; + u64 start_tsf; + bool last_ebs_failed; + enum iwl_mld_pass_all_sched_results_states pass_all_sched_res; + u8 fw_link_id; + struct { + u32 last_stats_ts_usec; + enum iwl_mld_traffic_load status; + } traffic_load; + ); + /* And here fields that survive a fw restart */ + size_t cmd_size; + void *cmd; + unsigned long last_6ghz_passive_jiffies; + unsigned long last_start_time_jiffies; + unsigned long last_mlo_scan_time; +}; + +#endif /* __iwl_mld_scan_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c new file mode 100644 index 000000000000..dbb5615dc3f6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.c @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 Intel Corporation + */ +#include "session-protect.h" +#include "fw/api/time-event.h" +#include "fw/api/context.h" +#include "iface.h" +#include <net/mac80211.h> + +void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_session_prot_notif *notif = (void *)pkt->data; + int fw_link_id = le32_to_cpu(notif->mac_link_id); + struct ieee80211_bss_conf *link_conf = + iwl_mld_fw_id_to_link_conf(mld, fw_link_id); + struct ieee80211_vif *vif; + struct iwl_mld_vif *mld_vif; + struct iwl_mld_session_protect *session_protect; + + if (WARN_ON(!link_conf)) + return; + + vif = link_conf->vif; + mld_vif = iwl_mld_vif_from_mac80211(vif); + session_protect = &mld_vif->session_protect; + + if (!le32_to_cpu(notif->status)) { + memset(session_protect, 0, sizeof(*session_protect)); + } else if (le32_to_cpu(notif->start)) { + /* End_jiffies indicates an active session */ + session_protect->session_requested = false; + session_protect->end_jiffies = + TU_TO_EXP_TIME(session_protect->duration); + /* !session_protect->end_jiffies means inactive session */ + if (!session_protect->end_jiffies) + session_protect->end_jiffies = 1; + } else { + memset(session_protect, 0, sizeof(*session_protect)); + } +} + +static int _iwl_mld_schedule_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link = + iwl_mld_link_dereference_check(mld_vif, link_id); + struct iwl_mld_session_protect *session_protect = + &mld_vif->session_protect; + struct iwl_session_prot_cmd cmd = { + .id_and_color = cpu_to_le32(link->fw_id), + .action = cpu_to_le32(FW_CTXT_ACTION_ADD), + .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), + .duration_tu = cpu_to_le32(MSEC_TO_TU(duration)), + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + WARN(hweight16(vif->active_links) > 1, + "Session protection isn't allowed with more than one active link"); + + if (session_protect->end_jiffies && + time_after(session_protect->end_jiffies, + TU_TO_EXP_TIME(min_duration))) { + IWL_DEBUG_TE(mld, "We have ample in the current session: %u\n", + jiffies_to_msecs(session_protect->end_jiffies - + jiffies)); + return -EALREADY; + } + + IWL_DEBUG_TE(mld, "Add a new session protection, duration %d TU\n", + le32_to_cpu(cmd.duration_tu)); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, + SESSION_PROTECTION_CMD), &cmd); + + if (ret) + return ret; + + /* end_jiffies will be updated when handling session_prot_notif */ + session_protect->end_jiffies = 0; + session_protect->duration = duration; + session_protect->session_requested = true; + + return 0; +} + +void iwl_mld_schedule_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id) +{ + int ret; + + ret = _iwl_mld_schedule_session_protection(mld, vif, duration, + min_duration, link_id); + if (ret && ret != -EALREADY) + IWL_ERR(mld, + "Couldn't send the SESSION_PROTECTION_CMD (%d)\n", + ret); +} + +struct iwl_mld_session_start_data { + struct iwl_mld *mld; + struct ieee80211_bss_conf *link_conf; + bool success; +}; + +static bool iwl_mld_session_start_fn(struct iwl_notif_wait_data *notif_wait, + struct iwl_rx_packet *pkt, void *_data) +{ + struct iwl_session_prot_notif *notif = (void *)pkt->data; + unsigned int pkt_len = iwl_rx_packet_payload_len(pkt); + struct iwl_mld_session_start_data *data = _data; + struct ieee80211_bss_conf *link_conf; + struct iwl_mld *mld = data->mld; + int fw_link_id; + + if (IWL_FW_CHECK(mld, pkt_len < sizeof(*notif), + "short session prot notif (%d)\n", + pkt_len)) + return false; + + fw_link_id = le32_to_cpu(notif->mac_link_id); + link_conf = iwl_mld_fw_id_to_link_conf(mld, fw_link_id); + + if (link_conf != data->link_conf) + return false; + + if (!le32_to_cpu(notif->status)) + return true; + + if (notif->start) { + data->success = true; + return true; + } + + return false; +} + +int iwl_mld_start_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id, unsigned long timeout) +{ + static const u16 start_notif[] = { SESSION_PROTECTION_NOTIF }; + struct iwl_notification_wait start_wait; + struct iwl_mld_session_start_data data = { + .mld = mld, + .link_conf = wiphy_dereference(mld->wiphy, + vif->link_conf[link_id]), + }; + int ret; + + if (WARN_ON(!data.link_conf)) + return -EINVAL; + + iwl_init_notification_wait(&mld->notif_wait, &start_wait, + start_notif, ARRAY_SIZE(start_notif), + iwl_mld_session_start_fn, &data); + + ret = _iwl_mld_schedule_session_protection(mld, vif, duration, + min_duration, link_id); + + if (ret) { + iwl_remove_notification(&mld->notif_wait, &start_wait); + return ret == -EALREADY ? 0 : ret; + } + + ret = iwl_wait_notification(&mld->notif_wait, &start_wait, timeout); + if (ret) + return ret; + return data.success ? 0 : -EIO; +} + +int iwl_mld_cancel_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + int link_id) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + struct iwl_mld_link *link = + iwl_mld_link_dereference_check(mld_vif, link_id); + struct iwl_mld_session_protect *session_protect = + &mld_vif->session_protect; + struct iwl_session_prot_cmd cmd = { + .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), + .conf_id = cpu_to_le32(SESSION_PROTECT_CONF_ASSOC), + }; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* If there isn't an active session or a requested one for this + * link do nothing + */ + if (!session_protect->session_requested && + !session_protect->end_jiffies) + return 0; + + if (WARN_ON(!link)) + return -EINVAL; + + cmd.id_and_color = cpu_to_le32(link->fw_id); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, + SESSION_PROTECTION_CMD), &cmd); + if (ret) { + IWL_ERR(mld, + "Couldn't send the SESSION_PROTECTION_CMD\n"); + return ret; + } + + memset(session_protect, 0, sizeof(*session_protect)); + + return 0; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h new file mode 100644 index 000000000000..642bec8451a1 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/session-protect.h @@ -0,0 +1,102 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#ifndef __session_protect_h__ +#define __session_protect_h__ + +#include "mld.h" +#include "hcmd.h" +#include <net/mac80211.h> +#include "fw/api/mac-cfg.h" + +/** + * DOC: session protection + * + * Session protection is an API from the firmware that allows the driver to + * request time on medium. This is needed before the association when we need + * to be on medium for the association frame exchange. Once we configure the + * firmware as 'associated', the firmware will allocate time on medium without + * needed a session protection. + * + * TDLS discover uses this API as well even after association to ensure that + * other activities internal to the firmware will not interrupt our presence + * on medium. + */ + +/** + * struct iwl_mld_session_protect - session protection parameters + * @end_jiffies: expected end_jiffies of current session protection. + * 0 if not active + * @duration: the duration in tu of current session + * @session_requested: A session protection command was sent and wasn't yet + * answered + */ +struct iwl_mld_session_protect { + unsigned long end_jiffies; + u32 duration; + bool session_requested; +}; + +#define IWL_MLD_SESSION_PROTECTION_ASSOC_TIME_MS 900 +#define IWL_MLD_SESSION_PROTECTION_MIN_TIME_MS 400 + +/** + * iwl_mld_handle_session_prot_notif - handles %SESSION_PROTECTION_NOTIF + * @mld: the mld component + * @pkt: the RX packet containing the notification + */ +void iwl_mld_handle_session_prot_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +/** + * iwl_mld_schedule_session_protection - schedule a session protection + * @mld: the mld component + * @vif: the virtual interface for which the protection issued + * @duration: the requested duration of the protection + * @min_duration: the minimum duration of the protection + * @link_id: The link to schedule a session protection for + */ +void iwl_mld_schedule_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id); + +/** + * iwl_mld_start_session_protection - start a session protection + * @mld: the mld component + * @vif: the virtual interface for which the protection issued + * @duration: the requested duration of the protection + * @min_duration: the minimum duration of the protection + * @link_id: The link to schedule a session protection for + * @timeout: timeout for waiting + * + * This schedules the session protection, and waits for it to start + * (with timeout) + * + * Returns: 0 if successful, error code otherwise + */ +int iwl_mld_start_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + u32 duration, u32 min_duration, + int link_id, unsigned long timeout); + +/** + * iwl_mld_cancel_session_protection - cancel the session protection. + * @mld: the mld component + * @vif: the virtual interface for which the session is issued + * @link_id: cancel the session protection for given link + * + * This functions cancels the session protection which is an act of good + * citizenship. If it is not needed any more it should be canceled because + * the other mac contexts wait for the medium during that time. + * + * Returns: 0 if successful, error code otherwise + * + */ +int iwl_mld_cancel_session_protection(struct iwl_mld *mld, + struct ieee80211_vif *vif, + int link_id); + +#endif /* __session_protect_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.c b/drivers/net/wireless/intel/iwlwifi/mld/sta.c new file mode 100644 index 000000000000..8fb51209b4a6 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.c @@ -0,0 +1,1321 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include <linux/ieee80211.h> +#include <kunit/static_stub.h> + +#include "sta.h" +#include "hcmd.h" +#include "iface.h" +#include "mlo.h" +#include "key.h" +#include "agg.h" +#include "tlc.h" +#include "fw/api/sta.h" +#include "fw/api/mac.h" +#include "fw/api/rx.h" + +int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_link_sta *mld_link_sta; + + /* This function should only be used with the wiphy lock held, + * In other cases, it is not guaranteed that the link_sta will exist + * in the driver too, and it is checked here. + */ + lockdep_assert_wiphy(mld->wiphy); + + /* This is not meant to be called with a NULL pointer */ + if (WARN_ON(!link_sta)) + return -ENOENT; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (!mld_link_sta) { + WARN_ON(!iwl_mld_error_before_recovery(mld)); + return -ENOENT; + } + + return mld_link_sta->fw_id; +} + +static void +iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta, + struct ieee80211_bss_conf *link, + __le32 *tx_ampdu_max_size, + __le32 *tx_ampdu_spacing) +{ + u32 agg_size = 0, mpdu_dens = 0; + + if (WARN_ON(!link_sta || !link)) + return; + + /* Note that we always use only legacy & highest supported PPDUs, so + * of Draft P802.11be D.30 Table 10-12a--Fields used for calculating + * the maximum A-MPDU size of various PPDU types in different bands, + * we only need to worry about the highest supported PPDU type here. + */ + + if (link_sta->ht_cap.ht_supported) { + agg_size = link_sta->ht_cap.ampdu_factor; + mpdu_dens = link_sta->ht_cap.ampdu_density; + } + + if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) { + /* overwrite HT values on 6 GHz */ + mpdu_dens = + le16_get_bits(link_sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MIN_MPDU_START); + agg_size = + le16_get_bits(link_sta->he_6ghz_capa.capa, + IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); + } else if (link_sta->vht_cap.vht_supported) { + /* if VHT supported overwrite HT value */ + agg_size = + u32_get_bits(link_sta->vht_cap.cap, + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); + } + + /* D6.0 10.12.2 A-MPDU length limit rules + * A STA indicates the maximum length of the A-MPDU preEOF padding + * that it can receive in an HE PPDU in the Maximum A-MPDU Length + * Exponent field in its HT Capabilities, VHT Capabilities, + * and HE 6 GHz Band Capabilities elements (if present) and the + * Maximum AMPDU Length Exponent Extension field in its HE + * Capabilities element + */ + if (link_sta->he_cap.has_he) + agg_size += + u8_get_bits(link_sta->he_cap.he_cap_elem.mac_cap_info[3], + IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_MASK); + + if (link_sta->eht_cap.has_eht) + agg_size += + u8_get_bits(link_sta->eht_cap.eht_cap_elem.mac_cap_info[1], + IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK); + + /* Limit to max A-MPDU supported by FW */ + agg_size = min_t(u32, agg_size, + STA_FLG_MAX_AGG_SIZE_4M >> STA_FLG_MAX_AGG_SIZE_SHIFT); + + *tx_ampdu_max_size = cpu_to_le32(agg_size); + *tx_ampdu_spacing = cpu_to_le32(mpdu_dens); +} + +static u8 iwl_mld_get_uapsd_acs(struct ieee80211_sta *sta) +{ + u8 uapsd_acs = 0; + + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BK) + uapsd_acs |= BIT(AC_BK); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_BE) + uapsd_acs |= BIT(AC_BE); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VI) + uapsd_acs |= BIT(AC_VI); + if (sta->uapsd_queues & IEEE80211_WMM_IE_STA_QOSINFO_AC_VO) + uapsd_acs |= BIT(AC_VO); + + return uapsd_acs | uapsd_acs << 4; +} + +static u8 iwl_mld_he_get_ppe_val(u8 *ppe, u8 ppe_pos_bit) +{ + u8 byte_num = ppe_pos_bit / 8; + u8 bit_num = ppe_pos_bit % 8; + u8 residue_bits; + u8 res; + + if (bit_num <= 5) + return (ppe[byte_num] >> bit_num) & + (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE) - 1); + + /* If bit_num > 5, we have to combine bits with next byte. + * Calculate how many bits we need to take from current byte (called + * here "residue_bits"), and add them to bits from next byte. + */ + + residue_bits = 8 - bit_num; + + res = (ppe[byte_num + 1] & + (BIT(IEEE80211_PPE_THRES_INFO_PPET_SIZE - residue_bits) - 1)) << + residue_bits; + res += (ppe[byte_num] >> bit_num) & (BIT(residue_bits) - 1); + + return res; +} + +static void iwl_mld_parse_ppe(struct iwl_mld *mld, + struct iwl_he_pkt_ext_v2 *pkt_ext, u8 nss, + u8 ru_index_bitmap, u8 *ppe, u8 ppe_pos_bit, + bool inheritance) +{ + /* FW currently supports only nss == MAX_HE_SUPP_NSS + * + * If nss > MAX: we can ignore values we don't support + * If nss < MAX: we can set zeros in other streams + */ + if (nss > MAX_HE_SUPP_NSS) { + IWL_DEBUG_INFO(mld, "Got NSS = %d - trimming to %d\n", nss, + MAX_HE_SUPP_NSS); + nss = MAX_HE_SUPP_NSS; + } + + for (int i = 0; i < nss; i++) { + u8 ru_index_tmp = ru_index_bitmap << 1; + u8 low_th = IWL_HE_PKT_EXT_NONE, high_th = IWL_HE_PKT_EXT_NONE; + + for (u8 bw = 0; + bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + ru_index_tmp >>= 1; + + /* According to the 11be spec, if for a specific BW the PPE Thresholds + * isn't present - it should inherit the thresholds from the last + * BW for which we had PPE Thresholds. In 11ax though, we don't have + * this inheritance - continue in this case + */ + if (!(ru_index_tmp & 1)) { + if (inheritance) + goto set_thresholds; + else + continue; + } + + high_th = iwl_mld_he_get_ppe_val(ppe, ppe_pos_bit); + ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; + low_th = iwl_mld_he_get_ppe_val(ppe, ppe_pos_bit); + ppe_pos_bit += IEEE80211_PPE_THRES_INFO_PPET_SIZE; + +set_thresholds: + pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; + pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; + } + } +} + +static void iwl_mld_set_pkt_ext_from_he_ppe(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta, + struct iwl_he_pkt_ext_v2 *pkt_ext, + bool inheritance) +{ + u8 nss = (link_sta->he_cap.ppe_thres[0] & + IEEE80211_PPE_THRES_NSS_MASK) + 1; + u8 *ppe = &link_sta->he_cap.ppe_thres[0]; + u8 ru_index_bitmap = + u8_get_bits(*ppe, + IEEE80211_PPE_THRES_RU_INDEX_BITMASK_MASK); + /* Starting after PPE header */ + u8 ppe_pos_bit = IEEE80211_HE_PPE_THRES_INFO_HEADER_SIZE; + + iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap, ppe, ppe_pos_bit, + inheritance); +} + +static int +iwl_mld_set_pkt_ext_from_nominal_padding(struct iwl_he_pkt_ext_v2 *pkt_ext, + u8 nominal_padding) +{ + int low_th = -1; + int high_th = -1; + + /* all the macros are the same for EHT and HE */ + switch (nominal_padding) { + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_0US: + low_th = IWL_HE_PKT_EXT_NONE; + high_th = IWL_HE_PKT_EXT_NONE; + break; + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US: + low_th = IWL_HE_PKT_EXT_BPSK; + high_th = IWL_HE_PKT_EXT_NONE; + break; + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_16US: + case IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_20US: + low_th = IWL_HE_PKT_EXT_NONE; + high_th = IWL_HE_PKT_EXT_BPSK; + break; + } + + if (low_th < 0 || high_th < 0) + return -EINVAL; + + /* Set the PPE thresholds accordingly */ + for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { + for (u8 bw = 0; + bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + pkt_ext->pkt_ext_qam_th[i][bw][0] = low_th; + pkt_ext->pkt_ext_qam_th[i][bw][1] = high_th; + } + } + + return 0; +} + +static void iwl_mld_get_optimal_ppe_info(struct iwl_he_pkt_ext_v2 *pkt_ext, + u8 nominal_padding) +{ + for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { + for (u8 bw = 0; bw < ARRAY_SIZE(pkt_ext->pkt_ext_qam_th[i]); + bw++) { + u8 *qam_th = &pkt_ext->pkt_ext_qam_th[i][bw][0]; + + if (nominal_padding > + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US && + qam_th[1] == IWL_HE_PKT_EXT_NONE) + qam_th[1] = IWL_HE_PKT_EXT_4096QAM; + else if (nominal_padding == + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_8US && + qam_th[0] == IWL_HE_PKT_EXT_NONE && + qam_th[1] == IWL_HE_PKT_EXT_NONE) + qam_th[0] = IWL_HE_PKT_EXT_4096QAM; + } + } +} + +static void iwl_mld_fill_pkt_ext(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta, + struct iwl_he_pkt_ext_v2 *pkt_ext) +{ + if (WARN_ON(!link_sta)) + return; + + /* Initialize the PPE thresholds to "None" (7), as described in Table + * 9-262ac of 80211.ax/D3.0. + */ + memset(pkt_ext, IWL_HE_PKT_EXT_NONE, sizeof(*pkt_ext)); + + if (link_sta->eht_cap.has_eht) { + u8 nominal_padding = + u8_get_bits(link_sta->eht_cap.eht_cap_elem.phy_cap_info[5], + IEEE80211_EHT_PHY_CAP5_COMMON_NOMINAL_PKT_PAD_MASK); + + /* If PPE Thresholds exists, parse them into a FW-familiar + * format. + */ + if (link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_PPE_THRESHOLD_PRESENT) { + u8 nss = (link_sta->eht_cap.eht_ppe_thres[0] & + IEEE80211_EHT_PPE_THRES_NSS_MASK) + 1; + u8 *ppe = &link_sta->eht_cap.eht_ppe_thres[0]; + u8 ru_index_bitmap = + u16_get_bits(*ppe, + IEEE80211_EHT_PPE_THRES_RU_INDEX_BITMASK_MASK); + /* Starting after PPE header */ + u8 ppe_pos_bit = IEEE80211_EHT_PPE_THRES_INFO_HEADER_SIZE; + + iwl_mld_parse_ppe(mld, pkt_ext, nss, ru_index_bitmap, + ppe, ppe_pos_bit, true); + /* EHT PPE Thresholds doesn't exist - set the API according to + * HE PPE Tresholds + */ + } else if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + /* Even though HE Capabilities IE doesn't contain PPE + * Thresholds for BW 320Mhz, thresholds for this BW will + * be filled in with the same values as 160Mhz, due to + * the inheritance, as required. + */ + iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext, + true); + + /* According to the requirements, for MCSs 12-13 the + * maximum value between HE PPE Threshold and Common + * Nominal Packet Padding needs to be taken + */ + iwl_mld_get_optimal_ppe_info(pkt_ext, nominal_padding); + + /* if PPE Thresholds doesn't present in both EHT IE and HE IE - + * take the Thresholds from Common Nominal Packet Padding field + */ + } else { + iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext, + nominal_padding); + } + } else if (link_sta->he_cap.has_he) { + /* If PPE Thresholds exist, parse them into a FW-familiar format. */ + if (link_sta->he_cap.he_cap_elem.phy_cap_info[6] & + IEEE80211_HE_PHY_CAP6_PPE_THRESHOLD_PRESENT) { + iwl_mld_set_pkt_ext_from_he_ppe(mld, link_sta, pkt_ext, + false); + /* PPE Thresholds doesn't exist - set the API PPE values + * according to Common Nominal Packet Padding field. + */ + } else { + u8 nominal_padding = + u8_get_bits(link_sta->he_cap.he_cap_elem.phy_cap_info[9], + IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_MASK); + if (nominal_padding != IEEE80211_HE_PHY_CAP9_NOMINAL_PKT_PADDING_RESERVED) + iwl_mld_set_pkt_ext_from_nominal_padding(pkt_ext, + nominal_padding); + } + } + + for (int i = 0; i < MAX_HE_SUPP_NSS; i++) { + for (int bw = 0; + bw < ARRAY_SIZE(*pkt_ext->pkt_ext_qam_th[i]); + bw++) { + u8 *qam_th = + &pkt_ext->pkt_ext_qam_th[i][bw][0]; + + IWL_DEBUG_HT(mld, + "PPE table: nss[%d] bw[%d] PPET8 = %d, PPET16 = %d\n", + i, bw, qam_th[0], qam_th[1]); + } + } +} + +static u32 iwl_mld_get_htc_flags(struct ieee80211_link_sta *link_sta) +{ + u8 *mac_cap_info = + &link_sta->he_cap.he_cap_elem.mac_cap_info[0]; + u32 htc_flags = 0; + + if (mac_cap_info[0] & IEEE80211_HE_MAC_CAP0_HTC_HE) + htc_flags |= IWL_HE_HTC_SUPPORT; + if ((mac_cap_info[1] & IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION) || + (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION)) { + u8 link_adap = + ((mac_cap_info[2] & + IEEE80211_HE_MAC_CAP2_LINK_ADAPTATION) << 1) + + (mac_cap_info[1] & + IEEE80211_HE_MAC_CAP1_LINK_ADAPTATION); + + if (link_adap == 2) + htc_flags |= + IWL_HE_HTC_LINK_ADAP_UNSOLICITED; + else if (link_adap == 3) + htc_flags |= IWL_HE_HTC_LINK_ADAP_BOTH; + } + if (mac_cap_info[2] & IEEE80211_HE_MAC_CAP2_BSR) + htc_flags |= IWL_HE_HTC_BSR_SUPP; + if (mac_cap_info[3] & IEEE80211_HE_MAC_CAP3_OMI_CONTROL) + htc_flags |= IWL_HE_HTC_OMI_SUPP; + if (mac_cap_info[4] & IEEE80211_HE_MAC_CAP4_BQR) + htc_flags |= IWL_HE_HTC_BQR_SUPP; + + return htc_flags; +} + +static int iwl_mld_send_sta_cmd(struct iwl_mld *mld, + const struct iwl_sta_cfg_cmd *cmd) +{ + u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD); + int cmd_len = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0) > 1 ? + sizeof(*cmd) : + sizeof(struct iwl_sta_cfg_cmd_v1); + int ret = iwl_mld_send_cmd_pdu(mld, cmd_id, cmd, cmd_len); + if (ret) + IWL_ERR(mld, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret); + return ret; +} + +static int +iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_sta *sta = link_sta->sta; + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_bss_conf *link; + struct iwl_mld_link *mld_link; + struct iwl_sta_cfg_cmd cmd = {}; + int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + + lockdep_assert_wiphy(mld->wiphy); + + link = link_conf_dereference_protected(mld_sta->vif, + link_sta->link_id); + + mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!link || !mld_link) || fw_id < 0) + return -EINVAL; + + cmd.sta_id = cpu_to_le32(fw_id); + cmd.station_type = cpu_to_le32(mld_sta->sta_type); + cmd.link_id = cpu_to_le32(mld_link->fw_id); + + memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN); + memcpy(&cmd.peer_link_address, link_sta->addr, ETH_ALEN); + + if (mld_sta->sta_state >= IEEE80211_STA_ASSOC) + cmd.assoc_id = cpu_to_le32(sta->aid); + + if (sta->mfp || mld_sta->sta_state < IEEE80211_STA_AUTHORIZED) + cmd.mfp = cpu_to_le32(1); + + switch (link_sta->rx_nss) { + case 1: + cmd.mimo = cpu_to_le32(0); + break; + case 2 ... 8: + cmd.mimo = cpu_to_le32(1); + break; + } + + switch (link_sta->smps_mode) { + case IEEE80211_SMPS_AUTOMATIC: + case IEEE80211_SMPS_NUM_MODES: + WARN_ON(1); + break; + case IEEE80211_SMPS_STATIC: + /* override NSS */ + cmd.mimo = cpu_to_le32(0); + break; + case IEEE80211_SMPS_DYNAMIC: + cmd.mimo_protection = cpu_to_le32(1); + break; + case IEEE80211_SMPS_OFF: + /* nothing */ + break; + } + + iwl_mld_fill_ampdu_size_and_dens(link_sta, link, + &cmd.tx_ampdu_max_size, + &cmd.tx_ampdu_spacing); + + if (sta->wme) { + cmd.sp_length = + cpu_to_le32(sta->max_sp ? sta->max_sp * 2 : 128); + cmd.uapsd_acs = cpu_to_le32(iwl_mld_get_uapsd_acs(sta)); + } + + if (link_sta->he_cap.has_he) { + cmd.trig_rnd_alloc = + cpu_to_le32(link->uora_exists ? 1 : 0); + + /* PPE Thresholds */ + iwl_mld_fill_pkt_ext(mld, link_sta, &cmd.pkt_ext); + + /* HTC flags */ + cmd.htc_flags = + cpu_to_le32(iwl_mld_get_htc_flags(link_sta)); + + if (link_sta->he_cap.he_cap_elem.mac_cap_info[2] & + IEEE80211_HE_MAC_CAP2_ACK_EN) + cmd.ack_enabled = cpu_to_le32(1); + } + + return iwl_mld_send_sta_cmd(mld, &cmd); +} + +IWL_MLD_ALLOC_FN(link_sta, link_sta) + +static int +iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + struct iwl_mld_link_sta *mld_link_sta; + int ret; + u8 fw_id; + + lockdep_assert_wiphy(mld->wiphy); + + /* We will fail to add it to the FW anyway */ + if (iwl_mld_error_before_recovery(mld)) + return -ENODEV; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + + /* We need to preserve the fw sta ids during a restart, since the fw + * will recover SN/PN for them, this is why the mld_link_sta exists. + */ + if (mld_link_sta) { + /* But if we are not restarting, this is not OK */ + WARN_ON(!mld->fw_status.in_hw_restart); + + /* Avoid adding a STA that is already in FW to avoid an assert */ + if (WARN_ON(mld_link_sta->in_fw)) + return -EINVAL; + + fw_id = mld_link_sta->fw_id; + goto add_to_fw; + } + + /* Allocate a fw id and map it to the link_sta */ + ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta); + if (ret) + return ret; + + if (link_sta == &link_sta->sta->deflink) { + mld_link_sta = &mld_sta->deflink; + } else { + mld_link_sta = kzalloc(sizeof(*mld_link_sta), GFP_KERNEL); + if (!mld_link_sta) + return -ENOMEM; + } + + mld_link_sta->fw_id = fw_id; + rcu_assign_pointer(mld_sta->link[link_sta->link_id], mld_link_sta); + +add_to_fw: + ret = iwl_mld_add_modify_sta_cmd(mld, link_sta); + if (ret) { + RCU_INIT_POINTER(mld->fw_id_to_link_sta[fw_id], NULL); + RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL); + if (link_sta != &link_sta->sta->deflink) + kfree(mld_link_sta); + return ret; + } + mld_link_sta->in_fw = true; + + return 0; +} + +static int iwl_mld_rm_sta_from_fw(struct iwl_mld *mld, u8 fw_sta_id) +{ + struct iwl_remove_sta_cmd cmd = { + .sta_id = cpu_to_le32(fw_sta_id), + }; + int ret; + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(MAC_CONF_GROUP, STA_REMOVE_CMD), + &cmd); + if (ret) + IWL_ERR(mld, "Failed to remove station. Id=%d\n", fw_sta_id); + + return ret; +} + +static void +iwl_mld_remove_link_sta(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + struct iwl_mld_link_sta *mld_link_sta = + iwl_mld_link_sta_from_mac80211(link_sta); + + if (WARN_ON(!mld_link_sta)) + return; + + iwl_mld_rm_sta_from_fw(mld, mld_link_sta->fw_id); + mld_link_sta->in_fw = false; + + /* Now that the STA doesn't exist in FW, we don't expect any new + * notifications for it. Cancel the ones that are already pending + */ + iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_STA, + mld_link_sta->fw_id); + + /* This will not be done upon reconfig, so do it also when + * failed to remove from fw + */ + RCU_INIT_POINTER(mld->fw_id_to_link_sta[mld_link_sta->fw_id], NULL); + RCU_INIT_POINTER(mld_sta->link[link_sta->link_id], NULL); + if (mld_link_sta != &mld_sta->deflink) + kfree_rcu(mld_link_sta, rcu_head); +} + +static void iwl_mld_set_max_amsdu_len(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta) +{ + const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + + /* For EHT, HE and VHT we can use the value as it was calculated by + * mac80211. For HT, mac80211 doesn't enforce to 4095, so force it + * here + */ + if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he || + link_sta->vht_cap.vht_supported || + !ht_cap->ht_supported || + !(ht_cap->cap & IEEE80211_HT_CAP_MAX_AMSDU)) + return; + + link_sta->agg.max_amsdu_len = IEEE80211_MAX_MPDU_LEN_HT_BA; + ieee80211_sta_recalc_aggregates(link_sta->sta); +} + +int iwl_mld_update_all_link_stations(struct iwl_mld *mld, + struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_link_sta *link_sta; + int link_id; + + for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) { + int ret = iwl_mld_add_modify_sta_cmd(mld, link_sta); + + if (ret) + return ret; + + if (mld_sta->sta_state == IEEE80211_STA_ASSOC) + iwl_mld_set_max_amsdu_len(mld, link_sta); + } + return 0; +} + +static void iwl_mld_destroy_sta(struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + kfree(mld_sta->dup_data); + kfree(mld_sta->mpdu_counters); +} + +static int +iwl_mld_alloc_dup_data(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta) +{ + struct iwl_mld_rxq_dup_data *dup_data; + + if (mld->fw_status.in_hw_restart) + return 0; + + dup_data = kcalloc(mld->trans->info.num_rxqs, sizeof(*dup_data), + GFP_KERNEL); + if (!dup_data) + return -ENOMEM; + + /* Initialize all the last_seq values to 0xffff which can never + * compare equal to the frame's seq_ctrl in the check in + * iwl_mld_is_dup() since the lower 4 bits are the fragment + * number and fragmented packets don't reach that function. + * + * This thus allows receiving a packet with seqno 0 and the + * retry bit set as the very first packet on a new TID. + */ + for (int q = 0; q < mld->trans->info.num_rxqs; q++) + memset(dup_data[q].last_seq, 0xff, + sizeof(dup_data[q].last_seq)); + mld_sta->dup_data = dup_data; + + return 0; +} + +static void iwl_mld_alloc_mpdu_counters(struct iwl_mld *mld, + struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_vif *vif = mld_sta->vif; + + if (mld->fw_status.in_hw_restart) + return; + + /* MPDUs are counted only when EMLSR is possible */ + if (ieee80211_vif_type_p2p(vif) != NL80211_IFTYPE_STATION || + sta->tdls || !ieee80211_vif_is_mld(vif)) + return; + + mld_sta->mpdu_counters = kcalloc(mld->trans->info.num_rxqs, + sizeof(*mld_sta->mpdu_counters), + GFP_KERNEL); + if (!mld_sta->mpdu_counters) + return; + + for (int q = 0; q < mld->trans->info.num_rxqs; q++) + spin_lock_init(&mld_sta->mpdu_counters[q].lock); +} + +static int +iwl_mld_init_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_vif *vif, enum iwl_fw_sta_type type) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + mld_sta->vif = vif; + mld_sta->sta_type = type; + mld_sta->mld = mld; + + if (!mld->fw_status.in_hw_restart) + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) + iwl_mld_init_txq(iwl_mld_txq_from_mac80211(sta->txq[i])); + + iwl_mld_alloc_mpdu_counters(mld, sta); + + iwl_mld_toggle_tx_ant(mld, &mld_sta->data_tx_ant); + + return iwl_mld_alloc_dup_data(mld, mld_sta); +} + +int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_vif *vif, enum iwl_fw_sta_type type) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_link_sta *link_sta; + int link_id; + int ret; + + ret = iwl_mld_init_sta(mld, sta, vif, type); + if (ret) + return ret; + + /* We could have add only the deflink link_sta, but it will not work + * in the restart case if the single link that is active during + * reconfig is not the deflink one. + */ + for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) { + ret = iwl_mld_add_link_sta(mld, link_sta); + if (ret) + goto destroy_sta; + } + + return 0; + +destroy_sta: + iwl_mld_destroy_sta(sta); + + return ret; +} + +void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_link_sta *link_sta; + int link_id; + + for_each_sta_active_link(mld_sta->vif, sta, link_sta, link_id) { + int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + + if (fw_sta_id < 0) + continue; + + iwl_mld_flush_link_sta_txqs(mld, fw_sta_id); + } +} + +void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + /* Avoid a warning in iwl_trans_wait_txq_empty if are anyway on the way + * to a restart. + */ + if (iwl_mld_error_before_recovery(mld)) + return; + + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct iwl_mld_txq *mld_txq = + iwl_mld_txq_from_mac80211(sta->txq[i]); + + if (!mld_txq->status.allocated) + continue; + + iwl_trans_wait_txq_empty(mld->trans, mld_txq->fw_id); + } +} + +void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct ieee80211_vif *vif = mld_sta->vif; + struct ieee80211_link_sta *link_sta; + u8 link_id; + + lockdep_assert_wiphy(mld->wiphy); + + /* Tell the HW to flush the queues */ + iwl_mld_flush_sta_txqs(mld, sta); + + /* Wait for trans to empty its queues */ + iwl_mld_wait_sta_txqs_empty(mld, sta); + + /* Now we can remove the queues */ + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) + iwl_mld_remove_txq(mld, sta->txq[i]); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + /* Mac8011 will remove the groupwise keys after the sta is + * removed, but FW expects all the keys to be removed before + * the STA is, so remove them all here. + */ + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) + iwl_mld_remove_ap_keys(mld, vif, sta, link_id); + + /* Remove the link_sta */ + iwl_mld_remove_link_sta(mld, link_sta); + } + + iwl_mld_destroy_sta(sta); +} + +u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + u32 result = 0; + + KUNIT_STATIC_STUB_REDIRECT(iwl_mld_fw_sta_id_mask, mld, sta); + + /* This function should only be used with the wiphy lock held, + * In other cases, it is not guaranteed that the link_sta will exist + * in the driver too, and it is checked in + * iwl_mld_fw_sta_id_from_link_sta. + */ + lockdep_assert_wiphy(mld->wiphy); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + int fw_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + + if (!(fw_id < 0)) + result |= BIT(fw_id); + } + + return result; +} +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_fw_sta_id_mask); + +static void iwl_mld_count_mpdu(struct ieee80211_link_sta *link_sta, int queue, + u32 count, bool tx) +{ + struct iwl_mld_per_q_mpdu_counter *queue_counter; + struct iwl_mld_per_link_mpdu_counter *link_counter; + struct iwl_mld_vif *mld_vif; + struct iwl_mld_sta *mld_sta; + struct iwl_mld_link *mld_link; + struct iwl_mld *mld; + int total_mpdus = 0; + + if (WARN_ON(!link_sta)) + return; + + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + if (!mld_sta->mpdu_counters) + return; + + mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + mld_link = iwl_mld_link_dereference_check(mld_vif, link_sta->link_id); + + if (WARN_ON_ONCE(!mld_link)) + return; + + queue_counter = &mld_sta->mpdu_counters[queue]; + + mld = mld_vif->mld; + + /* If it the window is over, first clear the counters. + * When we are not blocked by TPT, the window is managed by check_tpt_wk + */ + if ((mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT) && + time_is_before_jiffies(queue_counter->window_start_time + + IWL_MLD_TPT_COUNT_WINDOW)) { + memset(queue_counter->per_link, 0, + sizeof(queue_counter->per_link)); + queue_counter->window_start_time = jiffies; + + IWL_DEBUG_INFO(mld, "MPDU counters are cleared\n"); + } + + link_counter = &queue_counter->per_link[mld_link->fw_id]; + + spin_lock_bh(&queue_counter->lock); + + /* Update the statistics for this TPT measurement window */ + if (tx) + link_counter->tx += count; + else + link_counter->rx += count; + + /* + * Next, evaluate whether we should queue an unblock, + * skip this if we are not blocked due to low throughput. + */ + if (!(mld_vif->emlsr.blocked_reasons & IWL_MLD_EMLSR_BLOCKED_TPT)) + goto unlock; + + for (int i = 0; i <= IWL_FW_MAX_LINK_ID; i++) + total_mpdus += tx ? queue_counter->per_link[i].tx : + queue_counter->per_link[i].rx; + + /* Unblock is already queued if the threshold was reached before */ + if (total_mpdus - count >= IWL_MLD_ENTER_EMLSR_TPT_THRESH) + goto unlock; + + if (total_mpdus >= IWL_MLD_ENTER_EMLSR_TPT_THRESH) + wiphy_work_queue(mld->wiphy, &mld_vif->emlsr.unblock_tpt_wk); + +unlock: + spin_unlock_bh(&queue_counter->lock); +} + +/* must be called under rcu_read_lock() */ +void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue, + u32 count) +{ + iwl_mld_count_mpdu(link_sta, queue, count, false); +} + +/* must be called under rcu_read_lock() */ +void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count) +{ + /* use queue 0 for all TX */ + iwl_mld_count_mpdu(link_sta, 0, count, true); +} + +static int iwl_mld_allocate_internal_txq(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta, + u8 tid) +{ + u32 sta_mask = BIT(internal_sta->sta_id); + int queue, size; + + size = max_t(u32, IWL_MGMT_QUEUE_SIZE, + mld->trans->mac_cfg->base->min_txq_size); + + queue = iwl_trans_txq_alloc(mld->trans, 0, sta_mask, tid, size, + IWL_WATCHDOG_DISABLED); + + if (queue >= 0) + IWL_DEBUG_TX_QUEUES(mld, + "Enabling TXQ #%d for sta mask 0x%x tid %d\n", + queue, sta_mask, tid); + return queue; +} + +static int iwl_mld_send_aux_sta_cmd(struct iwl_mld *mld, + const struct iwl_mld_int_sta *internal_sta) +{ + struct iwl_aux_sta_cmd cmd = { + .sta_id = cpu_to_le32(internal_sta->sta_id), + /* TODO: CDB - properly set the lmac_id */ + .lmac_id = cpu_to_le32(IWL_LMAC_24G_INDEX), + }; + + return iwl_mld_send_cmd_pdu(mld, WIDE_ID(MAC_CONF_GROUP, AUX_STA_CMD), + &cmd); +} + +static int +iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld, + const struct iwl_mld_int_sta *internal_sta, + u8 fw_link_id, + const u8 *addr) +{ + struct iwl_sta_cfg_cmd cmd = {}; + + if (internal_sta->sta_type == STATION_TYPE_AUX) + return iwl_mld_send_aux_sta_cmd(mld, internal_sta); + + cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id); + cmd.link_id = cpu_to_le32(fw_link_id); + cmd.station_type = cpu_to_le32(internal_sta->sta_type); + + /* FW doesn't allow to add a IGTK/BIGTK if the sta isn't marked as MFP. + * On the other hand, FW will never check this flag during RX since + * an AP/GO doesn't receive protected broadcast management frames. + * So, we can set it unconditionally. + */ + if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT) + cmd.mfp = cpu_to_le32(1); + + if (addr) { + memcpy(cmd.peer_mld_address, addr, ETH_ALEN); + memcpy(cmd.peer_link_address, addr, ETH_ALEN); + } + + return iwl_mld_send_sta_cmd(mld, &cmd); +} + +static int iwl_mld_add_internal_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta, + enum iwl_fw_sta_type sta_type, + u8 fw_link_id, const u8 *addr, u8 tid) +{ + int ret, queue_id; + + ret = iwl_mld_allocate_link_sta_fw_id(mld, + &internal_sta->sta_id, + ERR_PTR(-EINVAL)); + if (ret) + return ret; + + internal_sta->sta_type = sta_type; + + ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id, + addr); + if (ret) + goto err; + + queue_id = iwl_mld_allocate_internal_txq(mld, internal_sta, tid); + if (queue_id < 0) { + iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id); + ret = queue_id; + goto err; + } + + internal_sta->queue_id = queue_id; + + return 0; +err: + iwl_mld_free_internal_sta(mld, internal_sta); + return ret; +} + +int iwl_mld_add_bcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + const u8 *addr; + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + addr = vif->type == NL80211_IFTYPE_ADHOC ? link->bssid : bcast_addr; + + return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta, + STATION_TYPE_BCAST_MGMT, + mld_link->fw_id, addr, + IWL_MGMT_TID); +} + +int iwl_mld_add_mcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + const u8 mcast_addr[] = {0x03, 0x00, 0x00, 0x00, 0x00, 0x00}; + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC)) + return -EINVAL; + + return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta, + STATION_TYPE_MCAST, + mld_link->fw_id, mcast_addr, 0); +} + +int iwl_mld_add_aux_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta) +{ + return iwl_mld_add_internal_sta(mld, internal_sta, STATION_TYPE_AUX, + 0, NULL, IWL_MAX_TID_COUNT); +} + +int iwl_mld_add_mon_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return -EINVAL; + + if (WARN_ON(vif->type != NL80211_IFTYPE_MONITOR)) + return -EINVAL; + + return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta, + STATION_TYPE_BCAST_MGMT, + mld_link->fw_id, NULL, + IWL_MAX_TID_COUNT); +} + +static void iwl_mld_remove_internal_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta, + bool flush, u8 tid) +{ + if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA || + internal_sta->queue_id == IWL_MLD_INVALID_QUEUE)) + return; + + if (flush) + iwl_mld_flush_link_sta_txqs(mld, internal_sta->sta_id); + + iwl_mld_free_txq(mld, BIT(internal_sta->sta_id), + tid, internal_sta->queue_id); + + iwl_mld_rm_sta_from_fw(mld, internal_sta->sta_id); + + iwl_mld_free_internal_sta(mld, internal_sta); +} + +void iwl_mld_remove_bcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_link->bcast_sta, true, + IWL_MGMT_TID); +} + +void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return; + + if (WARN_ON(vif->type != NL80211_IFTYPE_AP && + vif->type != NL80211_IFTYPE_ADHOC)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_link->mcast_sta, true, 0); +} + +void iwl_mld_remove_aux_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif) +{ + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + + if (WARN_ON(vif->type != NL80211_IFTYPE_P2P_DEVICE && + vif->type != NL80211_IFTYPE_STATION)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_vif->aux_sta, false, + IWL_MAX_TID_COUNT); +} + +void iwl_mld_remove_mon_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link) +{ + struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link); + + if (WARN_ON(!mld_link)) + return; + + if (WARN_ON(vif->type != NL80211_IFTYPE_MONITOR)) + return; + + iwl_mld_remove_internal_sta(mld, &mld_link->mon_sta, false, + IWL_MAX_TID_COUNT); +} + +static int iwl_mld_update_sta_resources(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u32 old_sta_mask, + u32 new_sta_mask) +{ + int ret; + + ret = iwl_mld_update_sta_txqs(mld, sta, old_sta_mask, new_sta_mask); + if (ret) + return ret; + + ret = iwl_mld_update_sta_keys(mld, vif, sta, old_sta_mask, new_sta_mask); + if (ret) + return ret; + + return iwl_mld_update_sta_baids(mld, old_sta_mask, new_sta_mask); +} + +int iwl_mld_update_link_stas(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + unsigned long links_to_add = ~old_links & new_links; + unsigned long links_to_rem = old_links & ~new_links; + unsigned long old_links_long = old_links; + unsigned long sta_mask_added = 0; + u32 current_sta_mask = 0, sta_mask_to_rem = 0; + unsigned int link_id, sta_id; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + for_each_set_bit(link_id, &old_links_long, + IEEE80211_MLD_MAX_NUM_LINKS) { + mld_link_sta = + iwl_mld_link_sta_dereference_check(mld_sta, link_id); + + if (WARN_ON(!mld_link_sta)) + return -EINVAL; + + current_sta_mask |= BIT(mld_link_sta->fw_id); + if (links_to_rem & BIT(link_id)) + sta_mask_to_rem |= BIT(mld_link_sta->fw_id); + } + + if (sta_mask_to_rem) { + ret = iwl_mld_update_sta_resources(mld, vif, sta, + current_sta_mask, + current_sta_mask & + ~sta_mask_to_rem); + if (ret) + return ret; + + current_sta_mask &= ~sta_mask_to_rem; + } + + for_each_set_bit(link_id, &links_to_rem, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_protected(sta, link_id); + + if (WARN_ON(!link_sta)) + return -EINVAL; + + iwl_mld_remove_link_sta(mld, link_sta); + } + + for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_protected(sta, link_id); + struct ieee80211_bss_conf *link; + + if (WARN_ON(!link_sta)) + return -EINVAL; + + ret = iwl_mld_add_link_sta(mld, link_sta); + if (ret) + goto remove_added_link_stas; + + mld_link_sta = + iwl_mld_link_sta_dereference_check(mld_sta, + link_id); + + link = link_conf_dereference_protected(mld_sta->vif, + link_sta->link_id); + + iwl_mld_set_max_amsdu_len(mld, link_sta); + iwl_mld_config_tlc_link(mld, vif, link, link_sta); + + sta_mask_added |= BIT(mld_link_sta->fw_id); + } + + if (sta_mask_added) { + ret = iwl_mld_update_sta_resources(mld, vif, sta, + current_sta_mask, + current_sta_mask | + sta_mask_added); + if (ret) + goto remove_added_link_stas; + } + + /* We couldn't activate the links before it has a STA. Now we can */ + for_each_set_bit(link_id, &links_to_add, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link = + link_conf_dereference_protected(mld_sta->vif, link_id); + + if (WARN_ON(!link)) + continue; + + iwl_mld_activate_link(mld, link); + } + + return 0; + +remove_added_link_stas: + for_each_set_bit(sta_id, &sta_mask_added, mld->fw->ucode_capa.num_stations) { + struct ieee80211_link_sta *link_sta = + wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[sta_id]); + + if (WARN_ON(!link_sta)) + continue; + + iwl_mld_remove_link_sta(mld, link_sta); + } + + return ret; +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/sta.h b/drivers/net/wireless/intel/iwlwifi/mld/sta.h new file mode 100644 index 000000000000..1897b121aae2 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/sta.h @@ -0,0 +1,273 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#ifndef __iwl_mld_sta_h__ +#define __iwl_mld_sta_h__ + +#include <net/mac80211.h> + +#include "mld.h" +#include "tx.h" + +/** + * struct iwl_mld_rxq_dup_data - Duplication detection data, per STA & Rx queue + * @last_seq: last sequence per tid. + * @last_sub_frame_idx: the index of the last subframe in an A-MSDU. This value + * will be zero if the packet is not part of an A-MSDU. + */ +struct iwl_mld_rxq_dup_data { + __le16 last_seq[IWL_MAX_TID_COUNT + 1]; + u8 last_sub_frame_idx[IWL_MAX_TID_COUNT + 1]; +} ____cacheline_aligned_in_smp; + +/** + * struct iwl_mld_link_sta - link-level station + * + * This represents the link-level sta - the driver level equivalent to the + * ieee80211_link_sta + * + * @last_rate_n_flags: rate_n_flags from the last &iwl_tlc_update_notif + * @signal_avg: the signal average coming from the firmware + * @in_fw: whether the link STA is uploaded to the FW (false during restart) + * @rcu_head: RCU head for freeing this object + * @fw_id: the FW id of this link sta. + */ +struct iwl_mld_link_sta { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + u32 last_rate_n_flags; + bool in_fw; + s8 signal_avg; + ); + /* And here fields that survive a fw restart */ + struct rcu_head rcu_head; + u32 fw_id; +}; + +#define iwl_mld_link_sta_dereference_check(mld_sta, link_id) \ + rcu_dereference_check((mld_sta)->link[link_id], \ + lockdep_is_held(&mld_sta->mld->wiphy->mtx)) + +#define for_each_mld_link_sta(mld_sta, link_sta, link_id) \ + for (link_id = 0; link_id < ARRAY_SIZE((mld_sta)->link); \ + link_id++) \ + if ((link_sta = \ + iwl_mld_link_sta_dereference_check(mld_sta, link_id))) + +#define IWL_NUM_DEFAULT_KEYS 4 + +/* struct iwl_mld_ptk_pn - Holds Packet Number (PN) per TID. + * @rcu_head: RCU head for freeing this data. + * @pn: Array storing PN for each TID. + */ +struct iwl_mld_ptk_pn { + struct rcu_head rcu_head; + struct { + u8 pn[IWL_MAX_TID_COUNT][IEEE80211_CCMP_PN_LEN]; + } ____cacheline_aligned_in_smp q[]; +}; + +/** + * struct iwl_mld_per_link_mpdu_counter - per-link TX/RX MPDU counters + * + * @tx: Number of TX MPDUs. + * @rx: Number of RX MPDUs. + */ +struct iwl_mld_per_link_mpdu_counter { + u32 tx; + u32 rx; +}; + +/** + * struct iwl_mld_per_q_mpdu_counter - per-queue MPDU counter + * + * @lock: Needed to protect the counters when modified from statistics. + * @per_link: per-link counters. + * @window_start_time: timestamp of the counting-window start + */ +struct iwl_mld_per_q_mpdu_counter { + spinlock_t lock; + struct iwl_mld_per_link_mpdu_counter per_link[IWL_FW_MAX_LINK_ID + 1]; + unsigned long window_start_time; +} ____cacheline_aligned_in_smp; + +/** + * struct iwl_mld_sta - representation of a station in the driver. + * + * This represent the MLD-level sta, and will not be added to the FW. + * Embedded in ieee80211_sta. + * + * @vif: pointer the vif object. + * @sta_state: station state according to enum %ieee80211_sta_state + * @sta_type: type of this station. See &enum iwl_fw_sta_type + * @mld: a pointer to the iwl_mld object + * @dup_data: per queue duplicate packet detection data + * @data_tx_ant: stores the last TX antenna index; used for setting + * TX rate_n_flags for injected data frames (toggles on every TX failure). + * @tid_to_baid: a simple map of TID to Block-Ack fw id + * @deflink: This holds the default link STA information, for non MLO STA all + * link specific STA information is accessed through @deflink or through + * link[0] which points to address of @deflink. For MLO Link STA + * the first added link STA will point to deflink. + * @link: reference to Link Sta entries. For Non MLO STA, except 1st link, + * i.e link[0] all links would be assigned to NULL by default and + * would access link information via @deflink or link[0]. For MLO + * STA, first link STA being added will point its link pointer to + * @deflink address and remaining would be allocated and the address + * would be assigned to link[link_id] where link_id is the id assigned + * by the AP. + * @ptk_pn: Array of pointers to PTK PN data, used to track the Packet Number + * per key index and per queue (TID). + * @mpdu_counters: RX/TX MPDUs counters for each queue. + */ +struct iwl_mld_sta { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + enum ieee80211_sta_state sta_state; + enum iwl_fw_sta_type sta_type; + ); + /* And here fields that survive a fw restart */ + struct iwl_mld *mld; + struct ieee80211_vif *vif; + struct iwl_mld_rxq_dup_data *dup_data; + u8 tid_to_baid[IWL_MAX_TID_COUNT]; + u8 data_tx_ant; + + struct iwl_mld_link_sta deflink; + struct iwl_mld_link_sta __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + struct iwl_mld_ptk_pn __rcu *ptk_pn[IWL_NUM_DEFAULT_KEYS]; + struct iwl_mld_per_q_mpdu_counter *mpdu_counters; +}; + +static inline struct iwl_mld_sta * +iwl_mld_sta_from_mac80211(struct ieee80211_sta *sta) +{ + return (void *)sta->drv_priv; +} + +static inline void +iwl_mld_cleanup_sta(void *data, struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + u8 link_id; + + for (int i = 0; i < ARRAY_SIZE(sta->txq); i++) + CLEANUP_STRUCT(iwl_mld_txq_from_mac80211(sta->txq[i])); + + for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) { + CLEANUP_STRUCT(mld_link_sta); + + if (!ieee80211_vif_is_mld(mld_sta->vif)) { + /* not an MLD STA; only has the deflink with ID zero */ + WARN_ON(link_id); + continue; + } + + if (mld_sta->vif->active_links & BIT(link_id)) + continue; + + /* Should not happen as link removal should always succeed */ + WARN_ON(1); + RCU_INIT_POINTER(mld_sta->link[link_id], NULL); + RCU_INIT_POINTER(mld_sta->mld->fw_id_to_link_sta[mld_link_sta->fw_id], + NULL); + if (mld_link_sta != &mld_sta->deflink) + kfree_rcu(mld_link_sta, rcu_head); + } + + CLEANUP_STRUCT(mld_sta); +} + +static inline struct iwl_mld_link_sta * +iwl_mld_link_sta_from_mac80211(struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + + return iwl_mld_link_sta_dereference_check(mld_sta, link_sta->link_id); +} + +int iwl_mld_add_sta(struct iwl_mld *mld, struct ieee80211_sta *sta, + struct ieee80211_vif *vif, enum iwl_fw_sta_type type); +void iwl_mld_remove_sta(struct iwl_mld *mld, struct ieee80211_sta *sta); +int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta); +u32 iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta); +int iwl_mld_update_all_link_stations(struct iwl_mld *mld, + struct ieee80211_sta *sta); +void iwl_mld_flush_sta_txqs(struct iwl_mld *mld, struct ieee80211_sta *sta); +void iwl_mld_wait_sta_txqs_empty(struct iwl_mld *mld, + struct ieee80211_sta *sta); +void iwl_mld_count_mpdu_rx(struct ieee80211_link_sta *link_sta, int queue, + u32 count); +void iwl_mld_count_mpdu_tx(struct ieee80211_link_sta *link_sta, u32 count); + +/** + * struct iwl_mld_int_sta - representation of an internal station + * (a station that exist in FW and in driver, but not in mac80211) + * + * @sta_id: the index of the station in the fw + * @queue_id: the if of the queue used by the station + * @sta_type: station type. One of &iwl_fw_sta_type + */ +struct iwl_mld_int_sta { + u8 sta_id; + u32 queue_id; + enum iwl_fw_sta_type sta_type; +}; + +static inline void +iwl_mld_init_internal_sta(struct iwl_mld_int_sta *internal_sta) +{ + internal_sta->sta_id = IWL_INVALID_STA; + internal_sta->queue_id = IWL_MLD_INVALID_QUEUE; +} + +static inline void +iwl_mld_free_internal_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta) +{ + if (WARN_ON(internal_sta->sta_id == IWL_INVALID_STA)) + return; + + RCU_INIT_POINTER(mld->fw_id_to_link_sta[internal_sta->sta_id], NULL); + iwl_mld_init_internal_sta(internal_sta); +} + +int iwl_mld_add_bcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_add_mcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_add_aux_sta(struct iwl_mld *mld, + struct iwl_mld_int_sta *internal_sta); + +int iwl_mld_add_mon_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +void iwl_mld_remove_bcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +void iwl_mld_remove_mcast_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +void iwl_mld_remove_aux_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif); + +void iwl_mld_remove_mon_sta(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link); + +int iwl_mld_update_link_stas(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + u16 old_links, u16 new_links); +#endif /* __iwl_mld_sta_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.c b/drivers/net/wireless/intel/iwlwifi/mld/stats.c new file mode 100644 index 000000000000..f633cb1cf510 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.c @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include "mld.h" +#include "stats.h" +#include "sta.h" +#include "mlo.h" +#include "hcmd.h" +#include "iface.h" +#include "scan.h" +#include "phy.h" +#include "fw/api/stats.h" + +static int iwl_mld_send_fw_stats_cmd(struct iwl_mld *mld, u32 cfg_mask, + u32 cfg_time, u32 type_mask) +{ + u32 cmd_id = WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_CMD); + struct iwl_system_statistics_cmd stats_cmd = { + .cfg_mask = cpu_to_le32(cfg_mask), + .config_time_sec = cpu_to_le32(cfg_time), + .type_id_mask = cpu_to_le32(type_mask), + }; + + return iwl_mld_send_cmd_pdu(mld, cmd_id, &stats_cmd); +} + +int iwl_mld_clear_stats_in_fw(struct iwl_mld *mld) +{ + u32 cfg_mask = IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK; + u32 type_mask = IWL_STATS_NTFY_TYPE_ID_OPER | + IWL_STATS_NTFY_TYPE_ID_OPER_PART1; + + return iwl_mld_send_fw_stats_cmd(mld, cfg_mask, 0, type_mask); +} + +static void +iwl_mld_fill_stats_from_oper_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt, + u8 fw_sta_id, struct station_info *sinfo) +{ + const struct iwl_system_statistics_notif_oper *notif = + (void *)&pkt->data; + const struct iwl_stats_ntfy_per_sta *per_sta = + ¬if->per_sta[fw_sta_id]; + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link_sta *mld_link_sta; + + /* 0 isn't a valid value, but FW might send 0. + * In that case, set the latest non-zero value we stored + */ + rcu_read_lock(); + + link_sta = rcu_dereference(mld->fw_id_to_link_sta[fw_sta_id]); + if (IS_ERR_OR_NULL(link_sta)) + goto unlock; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (WARN_ON(!mld_link_sta)) + goto unlock; + + if (per_sta->average_energy) + mld_link_sta->signal_avg = + -(s8)le32_to_cpu(per_sta->average_energy); + + sinfo->signal_avg = mld_link_sta->signal_avg; + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); + +unlock: + rcu_read_unlock(); +} + +struct iwl_mld_stats_data { + u8 fw_sta_id; + struct station_info *sinfo; + struct iwl_mld *mld; +}; + +static bool iwl_mld_wait_stats_handler(struct iwl_notif_wait_data *notif_data, + struct iwl_rx_packet *pkt, void *data) +{ + struct iwl_mld_stats_data *stats_data = data; + u16 cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); + + switch (cmd) { + case WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_NOTIF): + iwl_mld_fill_stats_from_oper_notif(stats_data->mld, pkt, + stats_data->fw_sta_id, + stats_data->sinfo); + break; + case WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF): + break; + case WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF): + return true; + } + + return false; +} + +static int +iwl_mld_fw_stats_to_mac80211(struct iwl_mld *mld, struct iwl_mld_sta *mld_sta, + struct station_info *sinfo) +{ + u32 cfg_mask = IWL_STATS_CFG_FLG_ON_DEMAND_NTFY_MSK | + IWL_STATS_CFG_FLG_RESET_MSK; + u32 type_mask = IWL_STATS_NTFY_TYPE_ID_OPER | + IWL_STATS_NTFY_TYPE_ID_OPER_PART1; + static const u16 notifications[] = { + WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_NOTIF), + WIDE_ID(STATISTICS_GROUP, STATISTICS_OPER_PART1_NOTIF), + WIDE_ID(SYSTEM_GROUP, SYSTEM_STATISTICS_END_NOTIF), + }; + struct iwl_mld_stats_data wait_stats_data = { + /* We don't support drv_sta_statistics in EMLSR */ + .fw_sta_id = mld_sta->deflink.fw_id, + .sinfo = sinfo, + .mld = mld, + }; + struct iwl_notification_wait stats_wait; + int ret; + + iwl_init_notification_wait(&mld->notif_wait, &stats_wait, + notifications, ARRAY_SIZE(notifications), + iwl_mld_wait_stats_handler, + &wait_stats_data); + + ret = iwl_mld_send_fw_stats_cmd(mld, cfg_mask, 0, type_mask); + if (ret) { + iwl_remove_notification(&mld->notif_wait, &stats_wait); + return ret; + } + + /* Wait 500ms for OPERATIONAL, PART1, and END notifications, + * which should be sufficient for the firmware to gather data + * from all LMACs and send notifications to the host. + */ + ret = iwl_wait_notification(&mld->notif_wait, &stats_wait, HZ / 2); + if (ret) + return ret; + + /* When periodic statistics are sent, FW will clear its statistics DB. + * If the statistics request here happens shortly afterwards, + * the response will contain data collected over a short time + * interval. The response we got here shouldn't be processed by + * the general statistics processing because it's incomplete. + * So, we delete it from the list so it won't be processed. + */ + iwl_mld_delete_handlers(mld, notifications, ARRAY_SIZE(notifications)); + + return 0; +} + +#define PERIODIC_STATS_SECONDS 5 + +int iwl_mld_request_periodic_fw_stats(struct iwl_mld *mld, bool enable) +{ + u32 cfg_mask = enable ? 0 : IWL_STATS_CFG_FLG_DISABLE_NTFY_MSK; + u32 type_mask = enable ? (IWL_STATS_NTFY_TYPE_ID_OPER | + IWL_STATS_NTFY_TYPE_ID_OPER_PART1) : 0; + u32 cfg_time = enable ? PERIODIC_STATS_SECONDS : 0; + + return iwl_mld_send_fw_stats_cmd(mld, cfg_mask, cfg_time, type_mask); +} + +static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta, + struct station_info *sinfo) +{ + struct rate_info *rinfo = &sinfo->txrate; + u32 rate_n_flags = mld_sta->deflink.last_rate_n_flags; + u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + u32 gi_ltf; + + sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); + + switch (rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK) { + case RATE_MCS_CHAN_WIDTH_20: + rinfo->bw = RATE_INFO_BW_20; + break; + case RATE_MCS_CHAN_WIDTH_40: + rinfo->bw = RATE_INFO_BW_40; + break; + case RATE_MCS_CHAN_WIDTH_80: + rinfo->bw = RATE_INFO_BW_80; + break; + case RATE_MCS_CHAN_WIDTH_160: + rinfo->bw = RATE_INFO_BW_160; + break; + case RATE_MCS_CHAN_WIDTH_320: + rinfo->bw = RATE_INFO_BW_320; + break; + } + + if (format == RATE_MCS_MOD_TYPE_CCK || + format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) { + int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK); + + /* add the offset needed to get to the legacy ofdm indices */ + if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) + rate += IWL_FIRST_OFDM_RATE; + + switch (rate) { + case IWL_RATE_1M_INDEX: + rinfo->legacy = 10; + break; + case IWL_RATE_2M_INDEX: + rinfo->legacy = 20; + break; + case IWL_RATE_5M_INDEX: + rinfo->legacy = 55; + break; + case IWL_RATE_11M_INDEX: + rinfo->legacy = 110; + break; + case IWL_RATE_6M_INDEX: + rinfo->legacy = 60; + break; + case IWL_RATE_9M_INDEX: + rinfo->legacy = 90; + break; + case IWL_RATE_12M_INDEX: + rinfo->legacy = 120; + break; + case IWL_RATE_18M_INDEX: + rinfo->legacy = 180; + break; + case IWL_RATE_24M_INDEX: + rinfo->legacy = 240; + break; + case IWL_RATE_36M_INDEX: + rinfo->legacy = 360; + break; + case IWL_RATE_48M_INDEX: + rinfo->legacy = 480; + break; + case IWL_RATE_54M_INDEX: + rinfo->legacy = 540; + } + return; + } + + rinfo->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; + + if (format == RATE_MCS_MOD_TYPE_HT) + rinfo->mcs = RATE_HT_MCS_INDEX(rate_n_flags); + else + rinfo->mcs = u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK); + + if (rate_n_flags & RATE_MCS_SGI_MSK) + rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; + + switch (format) { + case RATE_MCS_MOD_TYPE_EHT: + rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS; + break; + case RATE_MCS_MOD_TYPE_HE: + gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK); + + rinfo->flags |= RATE_INFO_FLAGS_HE_MCS; + + if (rate_n_flags & RATE_MCS_HE_106T_MSK) { + rinfo->bw = RATE_INFO_BW_HE_RU; + rinfo->he_ru_alloc = NL80211_RATE_INFO_HE_RU_ALLOC_106; + } + + switch (rate_n_flags & RATE_MCS_HE_TYPE_MSK) { + case RATE_MCS_HE_TYPE_SU: + case RATE_MCS_HE_TYPE_EXT_SU: + if (gi_ltf == 0 || gi_ltf == 1) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + else if (gi_ltf == 2) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else if (gi_ltf == 3) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + else + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + break; + case RATE_MCS_HE_TYPE_MU: + if (gi_ltf == 0 || gi_ltf == 1) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_0_8; + else if (gi_ltf == 2) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + break; + case RATE_MCS_HE_TYPE_TRIG: + if (gi_ltf == 0 || gi_ltf == 1) + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_1_6; + else + rinfo->he_gi = NL80211_RATE_INFO_HE_GI_3_2; + break; + } + + if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK) + rinfo->he_dcm = 1; + break; + case RATE_MCS_MOD_TYPE_HT: + rinfo->flags |= RATE_INFO_FLAGS_MCS; + break; + case RATE_MCS_MOD_TYPE_VHT: + rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; + break; + } +} + +void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + /* This API is not EMLSR ready, so we cannot provide complete + * information if EMLSR is active + */ + if (hweight16(vif->active_links) > 1) + return; + + if (iwl_mld_fw_stats_to_mac80211(mld_sta->mld, mld_sta, sinfo)) + return; + + iwl_mld_sta_stats_fill_txrate(mld_sta, sinfo); + + /* TODO: NL80211_STA_INFO_BEACON_RX */ + + /* TODO: NL80211_STA_INFO_BEACON_SIGNAL_AVG */ +} + +#define IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH 10 /* percentage */ +#define IWL_MLD_TRAFFIC_LOAD_HIGH_THRESH 50 /* percentage */ +#define IWL_MLD_TRAFFIC_LOAD_MIN_WINDOW_USEC (500 * 1000) + +static u8 iwl_mld_stats_load_percentage(u32 last_ts_usec, u32 curr_ts_usec, + u32 total_airtime_usec) +{ + u32 elapsed_usec = curr_ts_usec - last_ts_usec; + + if (elapsed_usec < IWL_MLD_TRAFFIC_LOAD_MIN_WINDOW_USEC) + return 0; + + return (100 * total_airtime_usec / elapsed_usec); +} + +static void iwl_mld_stats_recalc_traffic_load(struct iwl_mld *mld, + u32 total_airtime_usec, + u32 curr_ts_usec) +{ + u32 last_ts_usec = mld->scan.traffic_load.last_stats_ts_usec; + u8 load_prec; + + /* Skip the calculation as this is the first notification received */ + if (!last_ts_usec) + goto out; + + load_prec = iwl_mld_stats_load_percentage(last_ts_usec, curr_ts_usec, + total_airtime_usec); + + if (load_prec > IWL_MLD_TRAFFIC_LOAD_HIGH_THRESH) + mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_HIGH; + else if (load_prec > IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH) + mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_MEDIUM; + else + mld->scan.traffic_load.status = IWL_MLD_TRAFFIC_LOW; + +out: + mld->scan.traffic_load.last_stats_ts_usec = curr_ts_usec; +} + +static void iwl_mld_update_link_sig(struct ieee80211_vif *vif, int sig, + struct ieee80211_bss_conf *bss_conf) +{ + struct iwl_mld *mld = iwl_mld_vif_from_mac80211(vif)->mld; + int exit_emlsr_thresh; + + if (sig == 0) { + IWL_DEBUG_RX(mld, "RSSI is 0 - skip signal based decision\n"); + return; + } + + /* TODO: task=statistics handle CQM notifications */ + + if (sig < IWL_MLD_LOW_RSSI_MLO_SCAN_THRESH) + iwl_mld_int_mlo_scan(mld, vif); + + if (!iwl_mld_emlsr_active(vif)) + return; + + /* We are in EMLSR, check if we need to exit */ + exit_emlsr_thresh = + iwl_mld_get_emlsr_rssi_thresh(mld, &bss_conf->chanreq.oper, + true); + + if (sig < exit_emlsr_thresh) + iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_LOW_RSSI, + iwl_mld_get_other_link(vif, + bss_conf->link_id)); +} + +static void +iwl_mld_process_per_link_stats(struct iwl_mld *mld, + const struct iwl_stats_ntfy_per_link *per_link, + u32 curr_ts_usec) +{ + u32 total_airtime_usec = 0; + + for (u32 fw_id = 0; + fw_id < ARRAY_SIZE(mld->fw_id_to_bss_conf); + fw_id++) { + const struct iwl_stats_ntfy_per_link *link_stats; + struct ieee80211_bss_conf *bss_conf; + int sig; + + bss_conf = wiphy_dereference(mld->wiphy, + mld->fw_id_to_bss_conf[fw_id]); + if (!bss_conf || bss_conf->vif->type != NL80211_IFTYPE_STATION) + continue; + + link_stats = &per_link[fw_id]; + + total_airtime_usec += le32_to_cpu(link_stats->air_time); + + sig = -le32_to_cpu(link_stats->beacon_filter_average_energy); + iwl_mld_update_link_sig(bss_conf->vif, sig, bss_conf); + + /* TODO: parse more fields here (task=statistics)*/ + } + + iwl_mld_stats_recalc_traffic_load(mld, total_airtime_usec, + curr_ts_usec); +} + +static void +iwl_mld_process_per_sta_stats(struct iwl_mld *mld, + const struct iwl_stats_ntfy_per_sta *per_sta) +{ + for (int i = 0; i < mld->fw->ucode_capa.num_stations; i++) { + struct ieee80211_link_sta *link_sta = + wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[i]); + struct iwl_mld_link_sta *mld_link_sta; + s8 avg_energy = + -(s8)le32_to_cpu(per_sta[i].average_energy); + + if (IS_ERR_OR_NULL(link_sta) || !avg_energy) + continue; + + mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta); + if (WARN_ON(!mld_link_sta)) + continue; + + mld_link_sta->signal_avg = avg_energy; + } +} + +static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw, + struct ieee80211_chanctx_conf *ctx, + void *data) +{ + struct iwl_mld_phy *phy = iwl_mld_phy_from_mac80211(ctx); + const struct iwl_stats_ntfy_per_phy *per_phy = data; + u32 new_load, old_load; + + if (WARN_ON(phy->fw_id >= IWL_STATS_MAX_PHY_OPERATIONAL)) + return; + + phy->channel_load_by_us = + le32_to_cpu(per_phy[phy->fw_id].channel_load_by_us); + + old_load = phy->avg_channel_load_not_by_us; + new_load = le32_to_cpu(per_phy[phy->fw_id].channel_load_not_by_us); + + if (IWL_FW_CHECK(phy->mld, + new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD && + new_load > 100, + "Invalid channel load %u\n", new_load)) + return; + + if (new_load != IWL_STATS_UNKNOWN_CHANNEL_LOAD) { + /* update giving a weight of 0.5 for the old value */ + phy->avg_channel_load_not_by_us = (new_load >> 1) + + (old_load >> 1); + } + + iwl_mld_emlsr_check_chan_load(hw, phy, old_load); +} + +static void +iwl_mld_process_per_phy_stats(struct iwl_mld *mld, + const struct iwl_stats_ntfy_per_phy *per_phy) +{ + ieee80211_iter_chan_contexts_mtx(mld->hw, + iwl_mld_fill_chanctx_stats, + (void *)(uintptr_t)per_phy); + +} + +void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct iwl_system_statistics_notif_oper *stats = + (void *)&pkt->data; + u32 curr_ts_usec = le32_to_cpu(stats->time_stamp); + + BUILD_BUG_ON(ARRAY_SIZE(stats->per_sta) != IWL_STATION_COUNT_MAX); + BUILD_BUG_ON(ARRAY_SIZE(stats->per_link) < + ARRAY_SIZE(mld->fw_id_to_bss_conf)); + + iwl_mld_process_per_link_stats(mld, stats->per_link, curr_ts_usec); + iwl_mld_process_per_sta_stats(mld, stats->per_sta); + iwl_mld_process_per_phy_stats(mld, stats->per_phy); + + iwl_mld_check_omi_bw_reduction(mld); +} + +void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + /* TODO */ +} + diff --git a/drivers/net/wireless/intel/iwlwifi/mld/stats.h b/drivers/net/wireless/intel/iwlwifi/mld/stats.h new file mode 100644 index 000000000000..de434e66d555 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/stats.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_stats_h__ +#define __iwl_mld_stats_h__ + +int iwl_mld_request_periodic_fw_stats(struct iwl_mld *mld, bool enable); + +void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + struct station_info *sinfo); + +void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +int iwl_mld_clear_stats_in_fw(struct iwl_mld *mld); + +#endif /* __iwl_mld_stats_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile new file mode 100644 index 000000000000..3e2ae6020613 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +iwlmld-tests-y += module.o hcmd.o utils.o link.o rx.o agg.o link-selection.o emlsr_with_bt.o + +ccflags-y += -I$(src)/../ +obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmld-tests.o diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c new file mode 100644 index 000000000000..29b0248cec3d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/agg.c @@ -0,0 +1,663 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <kunit/test.h> +#include <kunit/static_stub.h> +#include <kunit/skbuff.h> + +#include "utils.h" +#include "mld.h" +#include "sta.h" +#include "agg.h" +#include "rx.h" + +#define FC_QOS_DATA (IEEE80211_FTYPE_DATA | IEEE80211_STYPE_QOS_DATA) +#define BA_WINDOW_SIZE 64 +#define QUEUE 0 + +static const struct reorder_buffer_case { + const char *desc; + struct { + /* ieee80211_hdr fields */ + u16 fc; + u8 tid; + bool multicast; + /* iwl_rx_mpdu_desc fields */ + u16 nssn; + /* used also for setting hdr::seq_ctrl */ + u16 sn; + u8 baid; + bool amsdu; + bool last_subframe; + bool old_sn; + bool dup; + } rx_pkt; + struct { + bool valid; + u16 head_sn; + u8 baid; + u16 num_entries; + /* The test prepares the reorder buffer with fake skbs based + * on the sequence numbers provided in @entries array. + */ + struct { + u16 sn; + /* Set add_subframes > 0 to simulate an A-MSDU by + * queueing additional @add_subframes skbs in the + * appropriate reorder buffer entry (based on the @sn) + */ + u8 add_subframes; + } entries[BA_WINDOW_SIZE]; + } reorder_buf_state; + struct { + enum iwl_mld_reorder_result reorder_res; + u16 head_sn; + u16 num_stored; + u16 skb_release_order[BA_WINDOW_SIZE]; + u16 skb_release_order_count; + } expected; +} reorder_buffer_cases[] = { + { + .desc = "RX packet with invalid BAID", + .rx_pkt = { + .fc = FC_QOS_DATA, + .baid = IWL_RX_REORDER_DATA_INVALID_BAID, + }, + .reorder_buf_state = { + .valid = true, + }, + .expected = { + /* Invalid BAID should not be buffered. The frame is + * passed to the network stack immediately. + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + }, + }, + { + .desc = "RX Multicast packet", + .rx_pkt = { + .fc = FC_QOS_DATA, + .multicast = true, + }, + .reorder_buf_state = { + .valid = true, + }, + .expected = { + /* Multicast packets are not buffered. The packet is + * passed to the network stack immediately. + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + }, + }, + { + .desc = "RX non-QoS data", + .rx_pkt = { + .fc = IEEE80211_FTYPE_DATA, + }, + .reorder_buf_state = { + .valid = true, + }, + .expected = { + /* non-QoS data frames do not require reordering. + * The packet is passed to the network stack + * immediately. + */ + .reorder_res = IWL_MLD_PASS_SKB, + }, + }, + { + .desc = "RX old SN packet, reorder buffer is not yet valid", + .rx_pkt = { + .fc = FC_QOS_DATA, + .old_sn = true, + }, + .reorder_buf_state = { + .valid = false, + }, + .expected = { + /* The buffer is invalid and the RX packet has an old + * SN. The packet is passed to the network stack + * immediately. + */ + .reorder_res = IWL_MLD_PASS_SKB, + }, + }, + { + .desc = "RX old SN packet, reorder buffer valid", + .rx_pkt = { + .fc = FC_QOS_DATA, + .old_sn = true, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + }, + .expected = { + /* The buffer is valid and the RX packet has an old SN. + * The packet should be dropped. + */ + .reorder_res = IWL_MLD_DROP_SKB, + .num_stored = 0, + .head_sn = 100, + }, + }, + { + .desc = "RX duplicate packet", + .rx_pkt = { + .fc = FC_QOS_DATA, + .dup = true, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + }, + .expected = { + /* Duplicate packets should be dropped */ + .reorder_res = IWL_MLD_DROP_SKB, + .num_stored = 0, + .head_sn = 100, + }, + }, + { + .desc = "RX In-order packet, sn < nssn", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 101, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + }, + .expected = { + /* 1. Reorder buffer is empty. + * 2. RX packet SN is in order and less than NSSN. + * Packet is released to the network stack immediately + * and buffer->head_sn is updated to NSSN. + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + .head_sn = 101, + }, + }, + { + .desc = "RX In-order packet, sn == head_sn", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 101, + .nssn = 100, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 101, + }, + .expected = { + /* 1. Reorder buffer is empty. + * 2. RX packet SN is equal to buffer->head_sn. + * Packet is released to the network stack immediately + * and buffer->head_sn is incremented. + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + .head_sn = 102, + }, + }, + { + .desc = "RX In-order packet, IEEE80211_MAX_SN wrap around", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = IEEE80211_MAX_SN, + .nssn = IEEE80211_MAX_SN - 1, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = IEEE80211_MAX_SN, + }, + .expected = { + /* 1. Reorder buffer is empty. + * 2. RX SN == buffer->head_sn == IEEE80211_MAX_SN + * Packet is released to the network stack immediately + * and buffer->head_sn is incremented correctly (wraps + * around to 0). + */ + .reorder_res = IWL_MLD_PASS_SKB, + .num_stored = 0, + .head_sn = 0, + }, + }, + { + .desc = "RX Out-of-order packet, pending packet in buffer", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 102, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 1, + .entries[0].sn = 101, + }, + .expected = { + /* 1. Reorder buffer contains one packet with SN=101. + * 2. RX packet SN = buffer->head_sn. + * Both packets are released (in order) to the network + * stack as there are no gaps. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 0, + .head_sn = 102, + .skb_release_order = {100, 101}, + .skb_release_order_count = 2, + }, + }, + { + .desc = "RX Out-of-order packet, pending packet in buffer (wrap around)", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 0, + .nssn = 1, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = IEEE80211_MAX_SN - 1, + .num_entries = 1, + .entries[0].sn = IEEE80211_MAX_SN, + }, + .expected = { + /* 1. Reorder buffer contains one packet with + * SN=IEEE80211_MAX_SN. + * 2. RX Packet SN = 0 (after wrap around) + * Both packets are released (in order) to the network + * stack as there are no gaps. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 0, + .head_sn = 1, + .skb_release_order = { 4095, 0 }, + .skb_release_order_count = 2, + }, + }, + { + .desc = "RX Out-of-order packet, filling 1/2 holes in buffer, release RX packet", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 101, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 1, + .entries[0].sn = 102, + }, + .expected = { + /* 1. Reorder buffer contains one packet with SN=102. + * 2. There are 2 holes at SN={100, 101}. + * Only the RX packet (SN=100) is released, there is + * still a hole at 101. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 1, + .head_sn = 101, + .skb_release_order = {100}, + .skb_release_order_count = 1, + }, + }, + { + .desc = "RX Out-of-order packet, filling 1/2 holes, release 2 packets", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 102, + .nssn = 103, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 3, + .entries[0].sn = 101, + .entries[1].sn = 104, + .entries[2].sn = 105, + }, + .expected = { + /* 1. Reorder buffer contains three packets. + * 2. RX packet fills one of two holes (at SN=102). + * Two packets are released (until the next hole at + * SN=103). + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 2, + .head_sn = 103, + .skb_release_order = {101, 102}, + .skb_release_order_count = 2, + }, + }, + { + .desc = "RX Out-of-order packet, filling 1/1 holes, no packets released", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 102, + .nssn = 100, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 3, + .entries[0].sn = 101, + .entries[1].sn = 103, + .entries[2].sn = 104, + }, + .expected = { + /* 1. Reorder buffer contains three packets: + * SN={101, 103, 104}. + * 2. RX packet fills a hole (SN=102), but NSSN is + * smaller than buffered frames. + * No packets can be released yet and buffer->head_sn + * is not updated. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 4, + .head_sn = 100, + }, + }, + { + .desc = "RX In-order A-MSDU, last subframe", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 101, + .amsdu = true, + .last_subframe = true, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 1, + .entries[0] = { + .sn = 100, + .add_subframes = 1, + }, + }, + .expected = { + /* 1. Reorder buffer contains a 2-sub frames A-MSDU + * at SN=100. + * 2. RX packet is the last SN=100 A-MSDU subframe + * All packets are released in order (3 x SN=100). + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 0, + .head_sn = 101, + .skb_release_order = {100, 100, 100}, + .skb_release_order_count = 3, + }, + }, + { + .desc = "RX In-order A-MSDU, not the last subframe", + .rx_pkt = { + .fc = FC_QOS_DATA, + .sn = 100, + .nssn = 101, + .amsdu = true, + .last_subframe = false, + }, + .reorder_buf_state = { + .valid = true, + .head_sn = 100, + .num_entries = 1, + .entries[0] = { + .sn = 100, + .add_subframes = 1, + }, + }, + .expected = { + /* 1. Reorder buffer contains a 2-sub frames A-MSDU + * at SN=100. + * 2. RX packet additional SN=100 A-MSDU subframe, + * but not the last one + * No packets are released and head_sn is not updated. + */ + .reorder_res = IWL_MLD_BUFFERED_SKB, + .num_stored = 3, + .head_sn = 100, + }, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_reorder_buffer, reorder_buffer_cases, desc); + +static struct sk_buff_head g_released_skbs; +static u16 g_num_released_skbs; + +/* Add released skbs from reorder buffer to a global list; This allows us + * to verify the correct release order of packets after they pass through the + * simulated reorder logic. + */ +static void +fake_iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld, + struct napi_struct *napi, + struct sk_buff *skb, int queue, + struct ieee80211_sta *sta) +{ + __skb_queue_tail(&g_released_skbs, skb); + g_num_released_skbs++; +} + +static u32 +fake_iwl_mld_fw_sta_id_mask(struct iwl_mld *mld, struct ieee80211_sta *sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld_link_sta *mld_link_sta; + u8 link_id; + u32 sta_mask = 0; + + /* This is the expectation in the real function */ + lockdep_assert_wiphy(mld->wiphy); + + /* We can't use for_each_sta_active_link */ + for_each_mld_link_sta(mld_sta, mld_link_sta, link_id) + sta_mask |= BIT(mld_link_sta->fw_id); + return sta_mask; +} + +static struct iwl_rx_mpdu_desc *setup_mpdu_desc(void) +{ + struct kunit *test = kunit_get_current_test(); + const struct reorder_buffer_case *param = + (const void *)(test->param_value); + struct iwl_rx_mpdu_desc *mpdu_desc; + + KUNIT_ALLOC_AND_ASSERT(test, mpdu_desc); + + mpdu_desc->reorder_data |= + le32_encode_bits(param->rx_pkt.baid, + IWL_RX_MPDU_REORDER_BAID_MASK); + mpdu_desc->reorder_data |= + le32_encode_bits(param->rx_pkt.sn, + IWL_RX_MPDU_REORDER_SN_MASK); + mpdu_desc->reorder_data |= + le32_encode_bits(param->rx_pkt.nssn, + IWL_RX_MPDU_REORDER_NSSN_MASK); + if (param->rx_pkt.old_sn) + mpdu_desc->reorder_data |= + cpu_to_le32(IWL_RX_MPDU_REORDER_BA_OLD_SN); + + if (param->rx_pkt.dup) + mpdu_desc->status |= cpu_to_le32(IWL_RX_MPDU_STATUS_DUPLICATE); + + if (param->rx_pkt.amsdu) { + mpdu_desc->mac_flags2 |= IWL_RX_MPDU_MFLG2_AMSDU; + if (param->rx_pkt.last_subframe) + mpdu_desc->amsdu_info |= + IWL_RX_MPDU_AMSDU_LAST_SUBFRAME; + } + + return mpdu_desc; +} + +static struct sk_buff *alloc_and_setup_skb(u16 fc, u16 seq_ctrl, u8 tid, + bool mcast) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_hdr hdr = { + .frame_control = cpu_to_le16(fc), + .seq_ctrl = cpu_to_le16(seq_ctrl), + }; + struct sk_buff *skb; + + skb = kunit_zalloc_skb(test, 128, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, skb); + + if (ieee80211_is_data_qos(hdr.frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(&hdr); + + qc[0] = tid & IEEE80211_QOS_CTL_TID_MASK; + } + + if (mcast) + hdr.addr1[0] = 0x1; + + skb_set_mac_header(skb, skb->len); + skb_put_data(skb, &hdr, ieee80211_hdrlen(hdr.frame_control)); + + return skb; +} + +static struct iwl_mld_reorder_buffer * +setup_reorder_buffer(struct iwl_mld_baid_data *baid_data) +{ + struct kunit *test = kunit_get_current_test(); + const struct reorder_buffer_case *param = + (const void *)(test->param_value); + struct iwl_mld_reorder_buffer *buffer = baid_data->reorder_buf; + struct iwl_mld_reorder_buf_entry *entries = baid_data->entries; + struct sk_buff *fake_skb; + + buffer->valid = param->reorder_buf_state.valid; + buffer->head_sn = param->reorder_buf_state.head_sn; + buffer->queue = QUEUE; + + for (int i = 0; i < baid_data->buf_size; i++) + __skb_queue_head_init(&entries[i].frames); + + for (int i = 0; i < param->reorder_buf_state.num_entries; i++) { + u16 sn = param->reorder_buf_state.entries[i].sn; + int index = sn % baid_data->buf_size; + u8 add_subframes = + param->reorder_buf_state.entries[i].add_subframes; + /* create 1 skb per entry + additional skbs per num of + * requested subframes + */ + u8 num_skbs = 1 + add_subframes; + + for (int j = 0; j < num_skbs; j++) { + fake_skb = alloc_and_setup_skb(FC_QOS_DATA, sn, 0, + false); + __skb_queue_tail(&entries[index].frames, fake_skb); + buffer->num_stored++; + } + } + + return buffer; +} + +static struct iwl_mld_reorder_buffer *setup_ba_data(struct ieee80211_sta *sta) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + const struct reorder_buffer_case *param = + (const void *)(test->param_value); + struct iwl_mld_baid_data *baid_data = NULL; + struct iwl_mld_reorder_buffer *buffer; + u32 reorder_buf_size = BA_WINDOW_SIZE * sizeof(baid_data->entries[0]); + u8 baid = param->reorder_buf_state.baid; + + /* Assuming only 1 RXQ */ + KUNIT_ALLOC_AND_ASSERT_SIZE(test, baid_data, + sizeof(*baid_data) + reorder_buf_size); + + baid_data->baid = baid; + baid_data->tid = param->rx_pkt.tid; + baid_data->buf_size = BA_WINDOW_SIZE; + + wiphy_lock(mld->wiphy); + baid_data->sta_mask = iwl_mld_fw_sta_id_mask(mld, sta); + wiphy_unlock(mld->wiphy); + + baid_data->entries_per_queue = BA_WINDOW_SIZE; + + buffer = setup_reorder_buffer(baid_data); + + KUNIT_EXPECT_NULL(test, rcu_access_pointer(mld->fw_id_to_ba[baid])); + rcu_assign_pointer(mld->fw_id_to_ba[baid], baid_data); + + return buffer; +} + +static void test_reorder_buffer(struct kunit *test) +{ + struct iwl_mld *mld = test->priv; + const struct reorder_buffer_case *param = + (const void *)(test->param_value); + struct iwl_rx_mpdu_desc *mpdu_desc; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + struct sk_buff *skb; + struct iwl_mld_reorder_buffer *buffer; + enum iwl_mld_reorder_result reorder_res; + u16 skb_release_order_count = param->expected.skb_release_order_count; + u16 skb_idx = 0; + + /* Init globals and activate stubs */ + __skb_queue_head_init(&g_released_skbs); + g_num_released_skbs = 0; + kunit_activate_static_stub(test, iwl_mld_fw_sta_id_mask, + fake_iwl_mld_fw_sta_id_mask); + kunit_activate_static_stub(test, iwl_mld_pass_packet_to_mac80211, + fake_iwl_mld_pass_packet_to_mac80211); + + vif = iwlmld_kunit_add_vif(false, NL80211_IFTYPE_STATION); + sta = iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, -1); + + /* Prepare skb, mpdu_desc, BA data and the reorder buffer */ + skb = alloc_and_setup_skb(param->rx_pkt.fc, param->rx_pkt.sn, + param->rx_pkt.tid, param->rx_pkt.multicast); + buffer = setup_ba_data(sta); + mpdu_desc = setup_mpdu_desc(); + + rcu_read_lock(); + reorder_res = iwl_mld_reorder(mld, NULL, QUEUE, sta, skb, mpdu_desc); + rcu_read_unlock(); + + KUNIT_ASSERT_EQ(test, reorder_res, param->expected.reorder_res); + KUNIT_ASSERT_EQ(test, buffer->num_stored, param->expected.num_stored); + KUNIT_ASSERT_EQ(test, buffer->head_sn, param->expected.head_sn); + + /* Verify skbs release order */ + KUNIT_ASSERT_EQ(test, skb_release_order_count, g_num_released_skbs); + while ((skb = __skb_dequeue(&g_released_skbs))) { + struct ieee80211_hdr *hdr = (void *)skb_mac_header(skb); + + KUNIT_ASSERT_EQ(test, le16_to_cpu(hdr->seq_ctrl), + param->expected.skb_release_order[skb_idx]); + skb_idx++; + } + KUNIT_ASSERT_EQ(test, skb_idx, skb_release_order_count); +} + +static struct kunit_case reorder_buffer_test_cases[] = { + KUNIT_CASE_PARAM(test_reorder_buffer, test_reorder_buffer_gen_params), + {}, +}; + +static struct kunit_suite reorder_buffer = { + .name = "iwlmld-reorder-buffer", + .test_cases = reorder_buffer_test_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(reorder_buffer); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c new file mode 100644 index 000000000000..91556ee5c142 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/emlsr_with_bt.c @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for link selection functions + * + * Copyright (C) 2025 Intel Corporation + */ +#include <kunit/static_stub.h> + +#include "utils.h" +#include "mld.h" +#include "mlo.h" + +static const struct emlsr_with_bt_test_case { + const char *desc; + struct { + struct iwl_bt_coex_profile_notif notif; + s32 signal; + bool check_entry; + } input; + bool emlsr_allowed; +} emlsr_with_bt_cases[] = { + { + .desc = "BT penalty(exit) with low rssi 4.5: emlsr allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {4, 5}, + .notif.wifi_loss_mid_high_rssi[1] = {7, 9}, + .signal = -69, + .check_entry = false, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(exit) from high rssi 5: emlsr allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {7, 9}, + .notif.wifi_loss_mid_high_rssi[1] = {5, 5}, + .signal = -68, + .check_entry = false, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(exit) with low rssi 8: emlsr not allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {7, 9}, + .notif.wifi_loss_mid_high_rssi[1] = {4, 5}, + .signal = -69, + .check_entry = false, + }, + .emlsr_allowed = false, + }, + { + .desc = "BT penalty(exit) from high rssi 9: emlsr not allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {4, 5}, + .notif.wifi_loss_mid_high_rssi[1] = {9, 9}, + .signal = -68, + .check_entry = false, + }, + .emlsr_allowed = false, + }, + { + .desc = "BT penalty(entry) with low rssi 4.5: emlsr allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {4, 5}, + .notif.wifi_loss_mid_high_rssi[1] = {7, 9}, + .signal = -63, + .check_entry = true, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(entry) from high rssi 5: emlsr allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {7, 9}, + .notif.wifi_loss_mid_high_rssi[1] = {5, 5}, + .signal = -62, + .check_entry = false, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(entry) with low rssi 8: emlsr not allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {7, 9}, + .notif.wifi_loss_mid_high_rssi[1] = {4, 5}, + .signal = -63, + .check_entry = false, + }, + .emlsr_allowed = true, + }, + { + .desc = "BT penalty(entry) from high rssi 9: emlsr not allowed", + .input = { + .notif.wifi_loss_low_rssi[1] = {4, 5}, + .notif.wifi_loss_mid_high_rssi[1] = {9, 9}, + .signal = -62, + .check_entry = true, + }, + .emlsr_allowed = false, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(emlsr_with_bt, emlsr_with_bt_cases, desc); + +static void test_emlsr_with_bt(struct kunit *test) +{ + struct iwl_mld *mld = test->priv; + const struct emlsr_with_bt_test_case *test_param = + (const void *)(test->param_value); + struct ieee80211_vif *vif = + iwlmld_kunit_add_vif(true, NL80211_IFTYPE_STATION); + struct ieee80211_bss_conf *link = iwlmld_kunit_add_link(vif, 1); + bool actual_value = false; + + KUNIT_ALLOC_AND_ASSERT(test, link->bss); + + /* Extract test case parameters */ + link->bss->signal = DBM_TO_MBM(test_param->input.signal); + memcpy(&mld->last_bt_notif, &test_param->input.notif, + sizeof(struct iwl_bt_coex_profile_notif)); + + actual_value = iwl_mld_bt_allows_emlsr(mld, link, + test_param->input.check_entry); + /* Assert that the returned value matches the expected emlsr_allowed */ + KUNIT_EXPECT_EQ(test, actual_value, test_param->emlsr_allowed); +} + +static struct kunit_case emlsr_with_bt_test_cases[] = { + KUNIT_CASE_PARAM(test_emlsr_with_bt, emlsr_with_bt_gen_params), + {}, +}; + +static struct kunit_suite emlsr_with_bt = { + .name = "iwlmld-emlsr-with-bt-tests", + .test_cases = emlsr_with_bt_test_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(emlsr_with_bt); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c new file mode 100644 index 000000000000..0e3b9417dd63 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/hcmd.c @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <kunit/test.h> + +#include <iwl-trans.h> +#include "mld.h" + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +static void test_hcmd_names_sorted(struct kunit *test) +{ + int i; + + for (i = 0; i < global_iwl_mld_goups_size; i++) { + const struct iwl_hcmd_arr *arr = &iwl_mld_groups[i]; + int j; + + if (!arr->arr) + continue; + for (j = 0; j < arr->size - 1; j++) + KUNIT_EXPECT_LE(test, arr->arr[j].cmd_id, + arr->arr[j + 1].cmd_id); + } +} + +static void test_hcmd_names_for_rx(struct kunit *test) +{ + static struct iwl_trans t = { + .conf.command_groups = iwl_mld_groups, + }; + + t.conf.command_groups_size = global_iwl_mld_goups_size; + + for (unsigned int i = 0; i < iwl_mld_rx_handlers_num; i++) { + const struct iwl_rx_handler *rxh; + const char *name; + + rxh = &iwl_mld_rx_handlers[i]; + + name = iwl_get_cmd_string(&t, rxh->cmd_id); + KUNIT_EXPECT_NOT_NULL(test, name); + KUNIT_EXPECT_NE_MSG(test, strcmp(name, "UNKNOWN"), 0, + "ID 0x%04x is UNKNOWN", rxh->cmd_id); + } +} + +static struct kunit_case hcmd_names_cases[] = { + KUNIT_CASE(test_hcmd_names_sorted), + KUNIT_CASE(test_hcmd_names_for_rx), + {}, +}; + +static struct kunit_suite hcmd_names = { + .name = "iwlmld-hcmd-names", + .test_cases = hcmd_names_cases, +}; + +kunit_test_suite(hcmd_names); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c new file mode 100644 index 000000000000..94a037bec1fa --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link-selection.c @@ -0,0 +1,345 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for link selection functions + * + * Copyright (C) 2025 Intel Corporation + */ +#include <kunit/static_stub.h> + +#include "utils.h" +#include "mld.h" +#include "link.h" +#include "iface.h" +#include "phy.h" +#include "mlo.h" + +static const struct link_grading_test_case { + const char *desc; + struct { + struct { + u8 link_id; + const struct cfg80211_chan_def *chandef; + bool active; + s32 signal; + bool has_chan_util_elem; + u8 chan_util; /* 0-255 , used only if has_chan_util_elem is true */ + u8 chan_load_by_us; /* 0-100, used only if active is true */; + } link; + } input; + unsigned int expected_grade; +} link_grading_cases[] = { + { + .desc = "channel util of 128 (50%)", + .input.link = { + .link_id = 0, + .chandef = &chandef_2ghz_20mhz, + .active = false, + .has_chan_util_elem = true, + .chan_util = 128, + }, + .expected_grade = 86, + }, + { + .desc = "channel util of 180 (70%)", + .input.link = { + .link_id = 0, + .chandef = &chandef_2ghz_20mhz, + .active = false, + .has_chan_util_elem = true, + .chan_util = 180, + }, + .expected_grade = 51, + }, + { + .desc = "channel util of 180 (70%), channel load by us of 10%", + .input.link = { + .link_id = 0, + .chandef = &chandef_2ghz_20mhz, + .has_chan_util_elem = true, + .chan_util = 180, + .active = true, + .chan_load_by_us = 10, + }, + .expected_grade = 67, + }, + { + .desc = "no channel util element", + .input.link = { + .link_id = 0, + .chandef = &chandef_2ghz_20mhz, + .active = true, + }, + .expected_grade = 120, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(link_grading, link_grading_cases, desc); + +static void setup_link(struct ieee80211_bss_conf *link) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + const struct link_grading_test_case *test_param = + (const void *)(test->param_value); + + KUNIT_ALLOC_AND_ASSERT(test, link->bss); + + link->bss->signal = DBM_TO_MBM(test_param->input.link.signal); + + link->chanreq.oper = *test_param->input.link.chandef; + + if (test_param->input.link.has_chan_util_elem) { + struct cfg80211_bss_ies *ies; + struct ieee80211_bss_load_elem bss_load = { + .channel_util = test_param->input.link.chan_util, + }; + struct element *elem = + iwlmld_kunit_gen_element(WLAN_EID_QBSS_LOAD, + &bss_load, + sizeof(bss_load)); + unsigned int elem_len = sizeof(*elem) + sizeof(bss_load); + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, ies, sizeof(*ies) + elem_len); + memcpy(ies->data, elem, elem_len); + ies->len = elem_len; + rcu_assign_pointer(link->bss->beacon_ies, ies); + rcu_assign_pointer(link->bss->ies, ies); + } + + if (test_param->input.link.active) { + struct ieee80211_chanctx_conf *chan_ctx = + wiphy_dereference(mld->wiphy, link->chanctx_conf); + struct iwl_mld_phy *phy; + + KUNIT_ASSERT_NOT_NULL(test, chan_ctx); + + phy = iwl_mld_phy_from_mac80211(chan_ctx); + + phy->channel_load_by_us = test_param->input.link.chan_load_by_us; + } +} + +static void test_link_grading(struct kunit *test) +{ + struct iwl_mld *mld = test->priv; + const struct link_grading_test_case *test_param = + (const void *)(test->param_value); + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link; + unsigned int actual_grade; + /* Extract test case parameters */ + u8 link_id = test_param->input.link.link_id; + bool active = test_param->input.link.active; + u16 valid_links; + struct iwl_mld_kunit_link assoc_link = { + .chandef = test_param->input.link.chandef, + }; + + /* If the link is not active, use a different link as the assoc link */ + if (active) { + assoc_link.id = link_id; + valid_links = BIT(link_id); + } else { + assoc_link.id = BIT(ffz(BIT(link_id))); + valid_links = BIT(assoc_link.id) | BIT(link_id); + } + + vif = iwlmld_kunit_setup_mlo_assoc(valid_links, &assoc_link); + + wiphy_lock(mld->wiphy); + link = wiphy_dereference(mld->wiphy, vif->link_conf[link_id]); + KUNIT_ASSERT_NOT_NULL(test, link); + + setup_link(link); + + actual_grade = iwl_mld_get_link_grade(mld, link); + wiphy_unlock(mld->wiphy); + + /* Assert that the returned grade matches the expected grade */ + KUNIT_EXPECT_EQ(test, actual_grade, test_param->expected_grade); +} + +static struct kunit_case link_selection_cases[] = { + KUNIT_CASE_PARAM(test_link_grading, link_grading_gen_params), + {}, +}; + +static struct kunit_suite link_selection = { + .name = "iwlmld-link-selection-tests", + .test_cases = link_selection_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(link_selection); + +static const struct link_pair_case { + const char *desc; + const struct cfg80211_chan_def *chandef_a, *chandef_b; + bool low_latency_vif; + u32 chan_load_not_by_us; + bool primary_link_active; + u32 expected_result; +} link_pair_cases[] = { + { + .desc = "Unequal bandwidth, primary link inactive, EMLSR not allowed", + .low_latency_vif = false, + .primary_link_active = false, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, + }, + { + .desc = "Equal bandwidths, sufficient channel load, EMLSR allowed", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 11, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_40mhz, + .expected_result = 0, + }, + { + .desc = "Equal bandwidths, insufficient channel load, EMLSR not allowed", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 6, + .chandef_a = &chandef_5ghz_80mhz, + .chandef_b = &chandef_6ghz_80mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, + }, + { + .desc = "Low latency VIF, sufficient channel load, EMLSR allowed", + .low_latency_vif = true, + .primary_link_active = true, + .chan_load_not_by_us = 6, + .chandef_a = &chandef_5ghz_160mhz, + .chandef_b = &chandef_6ghz_160mhz, + .expected_result = 0, + }, + { + .desc = "Different bandwidths (2x ratio), primary link load permits EMLSR", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = 0, + }, + { + .desc = "Different bandwidths (4x ratio), primary link load permits EMLSR", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 45, + .chandef_a = &chandef_5ghz_80mhz, + .chandef_b = &chandef_6ghz_20mhz, + .expected_result = 0, + }, + { + .desc = "Different bandwidths (16x ratio), primary link load insufficient", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 45, + .chandef_a = &chandef_6ghz_320mhz, + .chandef_b = &chandef_5ghz_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_CHAN_LOAD, + }, + { + .desc = "Same band not allowed (2.4 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_2ghz_20mhz, + .chandef_b = &chandef_2ghz_11_20mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, + }, + { + .desc = "Same band not allowed (5 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_5ghz_40mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, + }, + { + .desc = "Same band allowed (5 GHz separated)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_5ghz_40mhz, + .chandef_b = &chandef_5ghz_120_40mhz, + .expected_result = 0, + }, + { + .desc = "Same band not allowed (6 GHz)", + .low_latency_vif = false, + .primary_link_active = true, + .chan_load_not_by_us = 30, + .chandef_a = &chandef_6ghz_160mhz, + .chandef_b = &chandef_6ghz_221_160mhz, + .expected_result = IWL_MLD_EMLSR_EXIT_EQUAL_BAND, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(link_pair, link_pair_cases, desc); + +static void test_iwl_mld_link_pair_allows_emlsr(struct kunit *test) +{ + const struct link_pair_case *params = test->param_value; + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link; + /* link A is the primary and link B is the secondary */ + struct iwl_mld_link_sel_data a = { + .chandef = params->chandef_a, + .link_id = 4, + }; + struct iwl_mld_link_sel_data b = { + .chandef = params->chandef_b, + .link_id = 5, + }; + struct iwl_mld_kunit_link assoc_link = { + .chandef = params->primary_link_active ? a.chandef : b.chandef, + .id = params->primary_link_active ? a.link_id : b.link_id, + }; + u32 result; + + vif = iwlmld_kunit_setup_mlo_assoc(BIT(a.link_id) | BIT(b.link_id), + &assoc_link); + + if (params->low_latency_vif) + iwl_mld_vif_from_mac80211(vif)->low_latency_causes = 1; + + wiphy_lock(mld->wiphy); + + link = wiphy_dereference(mld->wiphy, vif->link_conf[a.link_id]); + KUNIT_ALLOC_AND_ASSERT(test, link->bss); + link = wiphy_dereference(mld->wiphy, vif->link_conf[b.link_id]); + KUNIT_ALLOC_AND_ASSERT(test, link->bss); + + /* Simulate channel load */ + if (params->primary_link_active) { + struct iwl_mld_phy *phy = + iwlmld_kunit_get_phy_of_link(vif, a.link_id); + + phy->avg_channel_load_not_by_us = params->chan_load_not_by_us; + } + + result = iwl_mld_emlsr_pair_state(vif, &a, &b); + + wiphy_unlock(mld->wiphy); + + KUNIT_EXPECT_EQ(test, result, params->expected_result); +} + +static struct kunit_case link_pair_criteria_test_cases[] = { + KUNIT_CASE_PARAM(test_iwl_mld_link_pair_allows_emlsr, link_pair_gen_params), + {} +}; + +static struct kunit_suite link_pair_criteria_tests = { + .name = "iwlmld_link_pair_allows_emlsr", + .test_cases = link_pair_criteria_test_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(link_pair_criteria_tests); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c new file mode 100644 index 000000000000..69a0d67858bf --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/link.c @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <kunit/static_stub.h> + +#include "utils.h" +#include "mld.h" +#include "link.h" +#include "iface.h" +#include "fw/api/mac-cfg.h" + +static const struct missed_beacon_test_case { + const char *desc; + struct { + struct iwl_missed_beacons_notif notif; + bool emlsr; + } input; + struct { + bool disconnected; + bool emlsr; + } output; +} missed_beacon_cases[] = { + { + .desc = "no EMLSR, no disconnect", + .input.notif = { + .consec_missed_beacons = cpu_to_le32(4), + }, + }, + { + .desc = "no EMLSR, no beacon loss since Rx, no disconnect", + .input.notif = { + .consec_missed_beacons = cpu_to_le32(20), + }, + }, + { + .desc = "no EMLSR, beacon loss since Rx, disconnect", + .input.notif = { + .consec_missed_beacons = cpu_to_le32(20), + .consec_missed_beacons_since_last_rx = + cpu_to_le32(10), + }, + .output.disconnected = true, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_missed_beacon, missed_beacon_cases, desc); + +static void fake_ieee80211_connection_loss(struct ieee80211_vif *vif) +{ + vif->cfg.assoc = false; +} + +static void test_missed_beacon(struct kunit *test) +{ + struct iwl_mld *mld = test->priv; + struct iwl_missed_beacons_notif *notif; + const struct missed_beacon_test_case *test_param = + (const void *)(test->param_value); + struct ieee80211_vif *vif; + struct iwl_rx_packet *pkt; + struct iwl_mld_kunit_link link1 = { + .id = 0, + .chandef = &chandef_6ghz_160mhz, + }; + struct iwl_mld_kunit_link link2 = { + .id = 1, + .chandef = &chandef_5ghz_80mhz, + }; + + kunit_activate_static_stub(test, ieee80211_connection_loss, + fake_ieee80211_connection_loss); + pkt = iwl_mld_kunit_create_pkt(test_param->input.notif); + notif = (void *)pkt->data; + + if (test_param->input.emlsr) { + vif = iwlmld_kunit_assoc_emlsr(&link1, &link2); + } else { + struct iwl_mld_vif *mld_vif; + + vif = iwlmld_kunit_setup_non_mlo_assoc(&link1); + mld_vif = iwl_mld_vif_from_mac80211(vif); + notif->link_id = cpu_to_le32(mld_vif->deflink.fw_id); + } + + wiphy_lock(mld->wiphy); + + iwl_mld_handle_missed_beacon_notif(mld, pkt); + + wiphy_unlock(mld->wiphy); + + KUNIT_ASSERT_NE(test, vif->cfg.assoc, test_param->output.disconnected); + + /* TODO: add test cases for esr and check */ +} + +static struct kunit_case link_cases[] = { + KUNIT_CASE_PARAM(test_missed_beacon, test_missed_beacon_gen_params), + {}, +}; + +static struct kunit_suite link = { + .name = "iwlmld-link", + .test_cases = link_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(link); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c new file mode 100644 index 000000000000..5d9818587b23 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/module.c @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * This is just module boilerplate for the iwlmld kunit module. + * + * Copyright (C) 2024 Intel Corporation + */ +#include <linux/module.h> + +MODULE_IMPORT_NS("IWLWIFI"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("kunit tests for iwlmld"); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c new file mode 100644 index 000000000000..20cb4e03ab41 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/rx.c @@ -0,0 +1,353 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024 Intel Corporation + */ +#include <kunit/test.h> +#include "utils.h" +#include "iwl-trans.h" +#include "mld.h" +#include "sta.h" + +static const struct is_dup_case { + const char *desc; + struct { + /* ieee80211_hdr fields */ + __le16 fc; + __le16 seq; + u8 tid; + bool multicast; + /* iwl_rx_mpdu_desc fields */ + bool is_amsdu; + u8 sub_frame_idx; + } rx_pkt; + struct { + __le16 last_seq; + u8 last_sub_frame_idx; + u8 tid; + } dup_data_state; + struct { + bool is_dup; + u32 rx_status_flag; + } result; +} is_dup_cases[] = { + { + .desc = "Control frame", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_CTL), + }, + .result = { + .is_dup = false, + .rx_status_flag = 0, + } + }, + { + .desc = "Null func frame", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_NULLFUNC), + }, + .result = { + .is_dup = false, + .rx_status_flag = 0, + } + }, + { + .desc = "Multicast data", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA), + .multicast = true, + }, + .result = { + .is_dup = false, + .rx_status_flag = 0, + } + }, + { + .desc = "QoS null func frame", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_NULLFUNC), + }, + .result = { + .is_dup = false, + .rx_status_flag = 0, + } + }, + { + .desc = "QoS data new sequence", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x101), + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "QoS data same sequence, no retry", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "QoS data same sequence, has retry", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA | + IEEE80211_FCTL_RETRY), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = true, + .rx_status_flag = 0, + }, + }, + { + .desc = "QoS data invalid tid", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + .tid = IWL_MAX_TID_COUNT + 1, + }, + .result = { + .is_dup = true, + .rx_status_flag = 0, + }, + }, + { + .desc = "non-QoS data, same sequence, same tid, no retry", + .rx_pkt = { + /* Driver will use tid = IWL_MAX_TID_COUNT */ + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .tid = IWL_MAX_TID_COUNT, + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "non-QoS data, same sequence, same tid, has retry", + .rx_pkt = { + /* Driver will use tid = IWL_MAX_TID_COUNT */ + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_FCTL_RETRY), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .tid = IWL_MAX_TID_COUNT, + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = true, + .rx_status_flag = 0, + }, + }, + { + .desc = "non-QoS data, same sequence on different tid's", + .rx_pkt = { + /* Driver will use tid = IWL_MAX_TID_COUNT */ + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA), + .seq = __cpu_to_le16(0x100), + }, + .dup_data_state = { + .tid = 7, + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "A-MSDU new subframe, allow same PN", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + .is_amsdu = true, + .sub_frame_idx = 1, + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_ALLOW_SAME_PN | + RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "A-MSDU subframe with smaller idx, disallow same PN", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + .is_amsdu = true, + .sub_frame_idx = 1, + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 2, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "A-MSDU same subframe, no retry, disallow same PN", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA), + .seq = __cpu_to_le16(0x100), + .is_amsdu = true, + .sub_frame_idx = 0, + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = false, + .rx_status_flag = RX_FLAG_DUP_VALIDATED, + }, + }, + { + .desc = "A-MSDU same subframe, has retry", + .rx_pkt = { + .fc = __cpu_to_le16(IEEE80211_FTYPE_DATA | + IEEE80211_STYPE_QOS_DATA | + IEEE80211_FCTL_RETRY), + .seq = __cpu_to_le16(0x100), + .is_amsdu = true, + .sub_frame_idx = 0, + }, + .dup_data_state = { + .last_seq = __cpu_to_le16(0x100), + .last_sub_frame_idx = 0, + }, + .result = { + .is_dup = true, + .rx_status_flag = 0, + }, + }, +}; + +KUNIT_ARRAY_PARAM_DESC(test_is_dup, is_dup_cases, desc); + +static void +setup_dup_data_state(struct ieee80211_sta *sta) +{ + struct kunit *test = kunit_get_current_test(); + const struct is_dup_case *param = (const void *)(test->param_value); + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + u8 tid = param->dup_data_state.tid; + struct iwl_mld_rxq_dup_data *dup_data; + + /* Allocate dup_data only for 1 queue */ + KUNIT_ALLOC_AND_ASSERT(test, dup_data); + + /* Initialize dup data, see iwl_mld_alloc_dup_data */ + memset(dup_data->last_seq, 0xff, sizeof(dup_data->last_seq)); + + dup_data->last_seq[tid] = param->dup_data_state.last_seq; + dup_data->last_sub_frame_idx[tid] = + param->dup_data_state.last_sub_frame_idx; + + mld_sta->dup_data = dup_data; +} + +static void setup_rx_pkt(const struct is_dup_case *param, + struct ieee80211_hdr *hdr, + struct iwl_rx_mpdu_desc *mpdu_desc) +{ + u8 tid = param->rx_pkt.tid; + + /* Set "new rx packet" header */ + hdr->frame_control = param->rx_pkt.fc; + hdr->seq_ctrl = param->rx_pkt.seq; + + if (ieee80211_is_data_qos(hdr->frame_control)) { + u8 *qc = ieee80211_get_qos_ctl(hdr); + + qc[0] = tid & IEEE80211_QOS_CTL_TID_MASK; + } + + if (param->rx_pkt.multicast) + hdr->addr1[0] = 0x1; + + /* Set mpdu_desc */ + mpdu_desc->amsdu_info = param->rx_pkt.sub_frame_idx & + IWL_RX_MPDU_AMSDU_SUBFRAME_IDX_MASK; + if (param->rx_pkt.is_amsdu) + mpdu_desc->mac_flags2 |= IWL_RX_MPDU_MFLG2_AMSDU; +} + +static void test_is_dup(struct kunit *test) +{ + const struct is_dup_case *param = (const void *)(test->param_value); + struct iwl_mld *mld = test->priv; + struct iwl_rx_mpdu_desc mpdu_desc = { }; + struct ieee80211_rx_status rx_status = { }; + struct ieee80211_vif *vif; + struct ieee80211_sta *sta; + struct ieee80211_hdr hdr; + + vif = iwlmld_kunit_add_vif(false, NL80211_IFTYPE_STATION); + sta = iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, -1); + + /* Prepare test case state */ + setup_dup_data_state(sta); + setup_rx_pkt(param, &hdr, &mpdu_desc); + + KUNIT_EXPECT_EQ(test, + iwl_mld_is_dup(mld, sta, &hdr, &mpdu_desc, &rx_status, + 0), /* assuming only 1 queue */ + param->result.is_dup); + KUNIT_EXPECT_EQ(test, rx_status.flag, param->result.rx_status_flag); +} + +static struct kunit_case is_dup_test_cases[] = { + KUNIT_CASE_PARAM(test_is_dup, test_is_dup_gen_params), + {}, +}; + +static struct kunit_suite is_dup = { + .name = "iwlmld-rx-is-dup", + .test_cases = is_dup_test_cases, + .init = iwlmld_kunit_test_init, +}; + +kunit_test_suite(is_dup); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c new file mode 100644 index 000000000000..26cf27be762d --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.c @@ -0,0 +1,503 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2024-2025 Intel Corporation + */ +#include <kunit/test.h> +#include <kunit/test-bug.h> + +#include "utils.h" + +#include <linux/device.h> + +#include "fw/api/scan.h" +#include "fw/api/mac-cfg.h" +#include "iwl-trans.h" +#include "mld.h" +#include "iface.h" +#include "link.h" +#include "phy.h" +#include "sta.h" + +int iwlmld_kunit_test_init(struct kunit *test) +{ + struct iwl_mld *mld; + struct iwl_trans *trans; + const struct iwl_rf_cfg *cfg; + struct iwl_fw *fw; + struct ieee80211_hw *hw; + + KUNIT_ALLOC_AND_ASSERT(test, trans); + KUNIT_ALLOC_AND_ASSERT(test, trans->dev); + KUNIT_ALLOC_AND_ASSERT(test, cfg); + KUNIT_ALLOC_AND_ASSERT(test, fw); + KUNIT_ALLOC_AND_ASSERT(test, hw); + KUNIT_ALLOC_AND_ASSERT(test, hw->wiphy); + + mutex_init(&hw->wiphy->mtx); + + /* Allocate and initialize the mld structure */ + KUNIT_ALLOC_AND_ASSERT(test, mld); + iwl_construct_mld(mld, trans, cfg, fw, hw, NULL); + + fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX; + fw->ucode_capa.num_links = IWL_FW_MAX_LINK_ID + 1; + + mld->fwrt.trans = trans; + mld->fwrt.fw = fw; + mld->fwrt.dev = trans->dev; + + /* TODO: add priv_size to hw allocation and setup hw->priv to enable + * testing mac80211 callbacks + */ + + KUNIT_ALLOC_AND_ASSERT(test, mld->nvm_data); + KUNIT_ALLOC_AND_ASSERT_SIZE(test, mld->scan.cmd, + sizeof(struct iwl_scan_req_umac_v17)); + mld->scan.cmd_size = sizeof(struct iwl_scan_req_umac_v17); + + /* This is not the state at the end of the regular opmode_start, + * but it is more common to need it. Explicitly undo this if needed. + */ + mld->trans->state = IWL_TRANS_FW_ALIVE; + mld->fw_status.running = true; + + /* Avoid passing mld struct around */ + test->priv = mld; + return 0; +} + +IWL_MLD_ALLOC_FN(link, bss_conf) + +static void iwlmld_kunit_init_link(struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct iwl_mld_link *mld_link, int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif); + int ret; + + /* setup mac80211 link */ + rcu_assign_pointer(vif->link_conf[link_id], link); + link->link_id = link_id; + link->vif = vif; + link->beacon_int = 100; + link->dtim_period = 3; + link->qos = true; + + /* and mld_link */ + ret = iwl_mld_allocate_link_fw_id(mld, &mld_link->fw_id, link); + KUNIT_ASSERT_EQ(test, ret, 0); + rcu_assign_pointer(mld_vif->link[link_id], mld_link); + rcu_assign_pointer(vif->link_conf[link_id], link); +} + +IWL_MLD_ALLOC_FN(vif, vif) + +/* Helper function to add and initialize a VIF for KUnit tests */ +struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct iwl_mld_vif *mld_vif; + int ret; + + /* TODO: support more types */ + KUNIT_ASSERT_EQ(test, type, NL80211_IFTYPE_STATION); + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, vif, + sizeof(*vif) + sizeof(*mld_vif)); + + vif->type = type; + mld_vif = iwl_mld_vif_from_mac80211(vif); + mld_vif->mld = mld; + + ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif); + KUNIT_ASSERT_EQ(test, ret, 0); + + /* TODO: revisit (task=EHT) */ + if (mlo) + return vif; + + /* Initialize the default link */ + iwlmld_kunit_init_link(vif, &vif->bss_conf, &mld_vif->deflink, 0); + + return vif; +} + +/* Use only for MLO vif */ +struct ieee80211_bss_conf * +iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_bss_conf *link; + struct iwl_mld_link *mld_link; + + KUNIT_ALLOC_AND_ASSERT(test, link); + KUNIT_ALLOC_AND_ASSERT(test, mld_link); + + iwlmld_kunit_init_link(vif, link, mld_link, link_id); + vif->valid_links |= BIT(link_id); + + return link; +} + +struct ieee80211_chanctx_conf * +iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_chanctx_conf *ctx; + struct iwl_mld_phy *phy; + int fw_id; + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, ctx, sizeof(*ctx) + sizeof(*phy)); + + /* Setup the chanctx conf */ + ctx->def = *def; + ctx->min_def = *def; + ctx->ap = *def; + + /* and the iwl_mld_phy */ + phy = iwl_mld_phy_from_mac80211(ctx); + + fw_id = iwl_mld_allocate_fw_phy_id(mld); + KUNIT_ASSERT_GE(test, fw_id, 0); + + phy->fw_id = fw_id; + phy->mld = mld; + phy->chandef = *def; + + return ctx; +} + +void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *ctx) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct iwl_mld_link *mld_link; + + KUNIT_EXPECT_NULL(test, rcu_access_pointer(link->chanctx_conf)); + rcu_assign_pointer(link->chanctx_conf, ctx); + + lockdep_assert_wiphy(mld->wiphy); + + mld_link = iwl_mld_link_from_mac80211(link); + + KUNIT_EXPECT_NULL(test, rcu_access_pointer(mld_link->chan_ctx)); + KUNIT_EXPECT_FALSE(test, mld_link->active); + + rcu_assign_pointer(mld_link->chan_ctx, ctx); + mld_link->active = true; + + if (ieee80211_vif_is_mld(vif)) + vif->active_links |= BIT(link->link_id); +} + +IWL_MLD_ALLOC_FN(link_sta, link_sta) + +static void iwlmld_kunit_add_link_sta(struct ieee80211_sta *sta, + struct ieee80211_link_sta *link_sta, + struct iwl_mld_link_sta *mld_link_sta, + u8 link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + struct iwl_mld *mld = test->priv; + u8 fw_id; + int ret; + + /* initialize mac80211's link_sta */ + link_sta->link_id = link_id; + rcu_assign_pointer(sta->link[link_id], link_sta); + + link_sta->sta = sta; + + /* and the iwl_mld_link_sta */ + ret = iwl_mld_allocate_link_sta_fw_id(mld, &fw_id, link_sta); + KUNIT_ASSERT_EQ(test, ret, 0); + mld_link_sta->fw_id = fw_id; + + rcu_assign_pointer(mld_sta->link[link_id], mld_link_sta); +} + +static struct ieee80211_link_sta * +iwlmld_kunit_alloc_link_sta(struct ieee80211_sta *sta, int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_link_sta *link_sta; + struct iwl_mld_link_sta *mld_link_sta; + + /* Only valid for MLO */ + KUNIT_ASSERT_TRUE(test, sta->valid_links); + + KUNIT_ALLOC_AND_ASSERT(test, link_sta); + KUNIT_ALLOC_AND_ASSERT(test, mld_link_sta); + + iwlmld_kunit_add_link_sta(sta, link_sta, mld_link_sta, link_id); + + sta->valid_links |= BIT(link_id); + + return link_sta; +} + +/* Allocate and initialize a STA with the first link_sta */ +static struct ieee80211_sta * +iwlmld_kunit_add_sta(struct ieee80211_vif *vif, int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_sta *sta; + struct iwl_mld_sta *mld_sta; + + /* Allocate memory for ieee80211_sta with embedded iwl_mld_sta */ + KUNIT_ALLOC_AND_ASSERT_SIZE(test, sta, sizeof(*sta) + sizeof(*mld_sta)); + + /* TODO: allocate and initialize the TXQs ? */ + + mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_sta->vif = vif; + mld_sta->mld = test->priv; + + /* TODO: adjust for internal stations */ + mld_sta->sta_type = STATION_TYPE_PEER; + + if (link_id >= 0) { + iwlmld_kunit_add_link_sta(sta, &sta->deflink, + &mld_sta->deflink, link_id); + sta->valid_links = BIT(link_id); + } else { + iwlmld_kunit_add_link_sta(sta, &sta->deflink, + &mld_sta->deflink, 0); + } + return sta; +} + +/* Move s STA to a state */ +static void iwlmld_kunit_move_sta_state(struct ieee80211_vif *vif, + struct ieee80211_sta *sta, + enum ieee80211_sta_state state) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld_sta *mld_sta; + struct iwl_mld_vif *mld_vif; + + /* The sta will be removed automatically at the end of the test */ + KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST); + + mld_sta = iwl_mld_sta_from_mac80211(sta); + mld_sta->sta_state = state; + + mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif); + mld_vif->authorized = state == IEEE80211_STA_AUTHORIZED; + + if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) + mld_vif->ap_sta = sta; +} + +struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif, + enum ieee80211_sta_state state, + int link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_sta *sta; + + /* The sta will be removed automatically at the end of the test */ + KUNIT_ASSERT_NE(test, state, IEEE80211_STA_NOTEXIST); + + /* First - allocate and init the STA */ + sta = iwlmld_kunit_add_sta(vif, link_id); + + /* Now move it all the way to the wanted state */ + for (enum ieee80211_sta_state _state = IEEE80211_STA_NONE; + _state <= state; _state++) + iwlmld_kunit_move_sta_state(vif, sta, state); + + return sta; +} + +static void iwlmld_kunit_set_vif_associated(struct ieee80211_vif *vif) +{ + /* TODO: setup chanreq */ + /* TODO setup capabilities */ + + vif->cfg.assoc = 1; +} + +static struct ieee80211_vif * +iwlmld_kunit_setup_assoc(bool mlo, struct iwl_mld_kunit_link *assoc_link) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link; + struct ieee80211_chanctx_conf *chan_ctx; + + KUNIT_ASSERT_TRUE(test, mlo || assoc_link->id == 0); + + vif = iwlmld_kunit_add_vif(mlo, NL80211_IFTYPE_STATION); + + if (mlo) + link = iwlmld_kunit_add_link(vif, assoc_link->id); + else + link = &vif->bss_conf; + + chan_ctx = iwlmld_kunit_add_chanctx(assoc_link->chandef); + + wiphy_lock(mld->wiphy); + iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); + wiphy_unlock(mld->wiphy); + + /* The AP sta will now be pointer to by mld_vif->ap_sta */ + iwlmld_kunit_setup_sta(vif, IEEE80211_STA_AUTHORIZED, assoc_link->id); + + iwlmld_kunit_set_vif_associated(vif); + + return vif; +} + +struct ieee80211_vif * +iwlmld_kunit_setup_mlo_assoc(u16 valid_links, + struct iwl_mld_kunit_link *assoc_link) +{ + struct kunit *test = kunit_get_current_test(); + struct ieee80211_vif *vif; + + KUNIT_ASSERT_TRUE(test, + hweight16(valid_links) == 1 || + hweight16(valid_links) == 2); + KUNIT_ASSERT_TRUE(test, valid_links & BIT(assoc_link->id)); + + vif = iwlmld_kunit_setup_assoc(true, assoc_link); + + /* Add the other link, if applicable */ + if (hweight16(valid_links) > 1) { + u8 other_link_id = ffs(valid_links & ~BIT(assoc_link->id)) - 1; + + iwlmld_kunit_add_link(vif, other_link_id); + } + + return vif; +} + +struct ieee80211_vif * +iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link *assoc_link) +{ + return iwlmld_kunit_setup_assoc(false, assoc_link); +} + +struct iwl_rx_packet * +_iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_rx_packet *pkt; + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, pkt, sizeof(pkt) + notif_sz); + + memcpy(pkt->data, notif, notif_sz); + pkt->len_n_flags = cpu_to_le32(sizeof(pkt->hdr) + notif_sz); + + return pkt; +} + +struct ieee80211_vif *iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, + struct iwl_mld_kunit_link *link2) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_vif *vif; + struct ieee80211_bss_conf *link; + struct ieee80211_chanctx_conf *chan_ctx; + struct ieee80211_sta *sta; + struct iwl_mld_vif *mld_vif; + u16 valid_links = BIT(link1->id) | BIT(link2->id); + + KUNIT_ASSERT_TRUE(test, hweight16(valid_links) == 2); + + vif = iwlmld_kunit_setup_mlo_assoc(valid_links, link1); + mld_vif = iwl_mld_vif_from_mac80211(vif); + + /* Activate second link */ + wiphy_lock(mld->wiphy); + + link = wiphy_dereference(mld->wiphy, vif->link_conf[link2->id]); + KUNIT_EXPECT_NOT_NULL(test, link); + + chan_ctx = iwlmld_kunit_add_chanctx(link2->chandef); + iwlmld_kunit_assign_chanctx_to_link(vif, link, chan_ctx); + + wiphy_unlock(mld->wiphy); + + /* And other link sta */ + sta = mld_vif->ap_sta; + KUNIT_EXPECT_NOT_NULL(test, sta); + + iwlmld_kunit_alloc_link_sta(sta, link2->id); + + return vif; +} + +struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len) +{ + struct kunit *test = kunit_get_current_test(); + struct element *elem; + + KUNIT_ALLOC_AND_ASSERT_SIZE(test, elem, sizeof(*elem) + len); + + elem->id = id; + elem->datalen = len; + memcpy(elem->data, data, len); + + return elem; +} + +struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif, + u8 link_id) +{ + struct kunit *test = kunit_get_current_test(); + struct iwl_mld *mld = test->priv; + struct ieee80211_chanctx_conf *chanctx; + struct ieee80211_bss_conf *link = + wiphy_dereference(mld->wiphy, vif->link_conf[link_id]); + + KUNIT_EXPECT_NOT_NULL(test, link); + + chanctx = wiphy_dereference(mld->wiphy, link->chanctx_conf); + KUNIT_EXPECT_NOT_NULL(test, chanctx); + + return iwl_mld_phy_from_mac80211(chanctx); +} + +static const struct chandef_case { + const char *desc; + const struct cfg80211_chan_def *chandef; +} chandef_cases[] = { +#define CHANDEF(c, ...) { .desc = "chandef " #c " valid", .chandef = &c, }, + CHANDEF_LIST +#undef CHANDEF +}; + +KUNIT_ARRAY_PARAM_DESC(chandef, chandef_cases, desc); + +static void test_iwl_mld_chandef_valid(struct kunit *test) +{ + const struct chandef_case *params = test->param_value; + + KUNIT_EXPECT_EQ(test, true, cfg80211_chandef_valid(params->chandef)); +} + +static struct kunit_case chandef_test_cases[] = { + KUNIT_CASE_PARAM(test_iwl_mld_chandef_valid, chandef_gen_params), + {} +}; + +static struct kunit_suite chandef_tests = { + .name = "iwlmld_valid_test_chandefs", + .test_cases = chandef_test_cases, +}; + +kunit_test_suite(chandef_tests); diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h new file mode 100644 index 000000000000..edf8eef4e81a --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tests/utils.h @@ -0,0 +1,140 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#ifndef __iwl_mld_kunit_utils_h__ +#define __iwl_mld_kunit_utils_h__ + +#include <net/mac80211.h> +#include <kunit/test-bug.h> + +struct iwl_mld; + +int iwlmld_kunit_test_init(struct kunit *test); + +struct iwl_mld_kunit_link { + const struct cfg80211_chan_def *chandef; + u8 id; +}; + +enum nl80211_iftype; + +struct ieee80211_vif *iwlmld_kunit_add_vif(bool mlo, enum nl80211_iftype type); + +struct ieee80211_bss_conf * +iwlmld_kunit_add_link(struct ieee80211_vif *vif, int link_id); + +#define KUNIT_ALLOC_AND_ASSERT_SIZE(test, ptr, size) \ +do { \ + (ptr) = kunit_kzalloc((test), (size), GFP_KERNEL); \ + KUNIT_ASSERT_NOT_NULL((test), (ptr)); \ +} while (0) + +#define KUNIT_ALLOC_AND_ASSERT(test, ptr) \ + KUNIT_ALLOC_AND_ASSERT_SIZE(test, ptr, sizeof(*(ptr))) + +#define CHANNEL(_name, _band, _freq) \ +static struct ieee80211_channel _name = { \ + .band = (_band), \ + .center_freq = (_freq), \ + .hw_value = (_freq), \ +} + +CHANNEL(chan_2ghz, NL80211_BAND_2GHZ, 2412); +CHANNEL(chan_2ghz_11, NL80211_BAND_2GHZ, 2462); +CHANNEL(chan_5ghz, NL80211_BAND_5GHZ, 5200); +CHANNEL(chan_5ghz_120, NL80211_BAND_5GHZ, 5600); +CHANNEL(chan_6ghz, NL80211_BAND_6GHZ, 6115); +CHANNEL(chan_6ghz_221, NL80211_BAND_6GHZ, 7055); +/* Feel free to add more */ +#undef CHANNEL + +#define CHANDEF_LIST \ + CHANDEF(chandef_2ghz_20mhz, chan_2ghz, 2412, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_2ghz_40mhz, chan_2ghz, 2422, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_2ghz_11_20mhz, chan_2ghz_11, 2462, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_5ghz_20mhz, chan_5ghz, 5200, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_5ghz_40mhz, chan_5ghz, 5210, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_5ghz_80mhz, chan_5ghz, 5210, \ + NL80211_CHAN_WIDTH_80) \ + CHANDEF(chandef_5ghz_160mhz, chan_5ghz, 5250, \ + NL80211_CHAN_WIDTH_160) \ + CHANDEF(chandef_5ghz_120_40mhz, chan_5ghz_120, 5610, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_6ghz_20mhz, chan_6ghz, 6115, \ + NL80211_CHAN_WIDTH_20) \ + CHANDEF(chandef_6ghz_40mhz, chan_6ghz, 6125, \ + NL80211_CHAN_WIDTH_40) \ + CHANDEF(chandef_6ghz_80mhz, chan_6ghz, 6145, \ + NL80211_CHAN_WIDTH_80) \ + CHANDEF(chandef_6ghz_160mhz, chan_6ghz, 6185, \ + NL80211_CHAN_WIDTH_160) \ + CHANDEF(chandef_6ghz_320mhz, chan_6ghz, 6105, \ + NL80211_CHAN_WIDTH_320) \ + CHANDEF(chandef_6ghz_221_160mhz, chan_6ghz_221, 6985, \ + NL80211_CHAN_WIDTH_160) \ + /* Feel free to add more */ + +#define CHANDEF(_name, _channel, _freq1, _width) \ +__maybe_unused static const struct cfg80211_chan_def _name = { \ + .chan = &(_channel), \ + .center_freq1 = (_freq1), \ + .width = (_width), \ +}; +CHANDEF_LIST +#undef CHANDEF + +struct ieee80211_chanctx_conf * +iwlmld_kunit_add_chanctx(const struct cfg80211_chan_def *def); + +void iwlmld_kunit_assign_chanctx_to_link(struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link, + struct ieee80211_chanctx_conf *ctx); + +/* Allocate a sta, initialize it and move it to the wanted state */ +struct ieee80211_sta *iwlmld_kunit_setup_sta(struct ieee80211_vif *vif, + enum ieee80211_sta_state state, + int link_id); + +struct ieee80211_vif * +iwlmld_kunit_setup_mlo_assoc(u16 valid_links, + struct iwl_mld_kunit_link *assoc_link); + +struct ieee80211_vif * +iwlmld_kunit_setup_non_mlo_assoc(struct iwl_mld_kunit_link *assoc_link); + +struct iwl_rx_packet * +_iwl_mld_kunit_create_pkt(const void *notif, size_t notif_sz); + +#define iwl_mld_kunit_create_pkt(_notif) \ + _iwl_mld_kunit_create_pkt(&(_notif), sizeof(_notif)) + +struct ieee80211_vif * +iwlmld_kunit_assoc_emlsr(struct iwl_mld_kunit_link *link1, + struct iwl_mld_kunit_link *link2); + +struct element *iwlmld_kunit_gen_element(u8 id, const void *data, size_t len); + +/** + * iwlmld_kunit_get_phy_of_link - Get the phy of a link + * + * @vif: The vif to get the phy from. + * @link_id: The id of the link to get the phy for. + * + * given a vif and link id, return the phy pointer of that link. + * This assumes that the link exists, and that it had a chanctx + * assigned. + * If this is not the case, the test will fail. + * + * Return: phy pointer. + */ +struct iwl_mld_phy *iwlmld_kunit_get_phy_of_link(struct ieee80211_vif *vif, + u8 link_id); + +#endif /* __iwl_mld_kunit_utils_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.c b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c new file mode 100644 index 000000000000..f8a8c35066be --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.c @@ -0,0 +1,467 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ +#ifdef CONFIG_THERMAL +#include <linux/sort.h> +#include <linux/thermal.h> +#endif + +#include "fw/api/phy.h" + +#include "thermal.h" +#include "mld.h" +#include "hcmd.h" + +#define IWL_MLD_NUM_CTDP_STEPS 20 +#define IWL_MLD_MIN_CTDP_BUDGET_MW 150 + +#define IWL_MLD_CT_KILL_DURATION (5 * HZ) + +void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + const struct ct_kill_notif *notif = (const void *)pkt->data; + + IWL_ERR(mld, + "CT Kill notification: temp = %d, DTS = 0x%x, Scheme 0x%x - Enter CT Kill\n", + le16_to_cpu(notif->temperature), notif->dts, + notif->scheme); + + iwl_mld_set_ctkill(mld, true); + + wiphy_delayed_work_queue(mld->wiphy, &mld->ct_kill_exit_wk, + round_jiffies_relative(IWL_MLD_CT_KILL_DURATION)); +} + +static void iwl_mld_exit_ctkill(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld *mld; + + mld = container_of(wk, struct iwl_mld, ct_kill_exit_wk.work); + + IWL_ERR(mld, "Exit CT Kill\n"); + iwl_mld_set_ctkill(mld, false); +} + +void iwl_mld_handle_temp_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt) +{ + const struct iwl_dts_measurement_notif *notif = + (const void *)pkt->data; + int temp; + u32 ths_crossed; + + temp = le32_to_cpu(notif->temp); + + /* shouldn't be negative, but since it's s32, make sure it isn't */ + if (IWL_FW_CHECK(mld, temp < 0, "negative temperature %d\n", temp)) + return; + + ths_crossed = le32_to_cpu(notif->threshold_idx); + + /* 0xFF in ths_crossed means the notification is not related + * to a trip, so we can ignore it here. + */ + if (ths_crossed == 0xFF) + return; + + IWL_DEBUG_TEMP(mld, "Temp = %d Threshold crossed = %d\n", + temp, ths_crossed); + + if (IWL_FW_CHECK(mld, ths_crossed >= IWL_MAX_DTS_TRIPS, + "bad threshold: %d\n", ths_crossed)) + return; + +#ifdef CONFIG_THERMAL + if (mld->tzone) + thermal_zone_device_update(mld->tzone, THERMAL_TRIP_VIOLATED); +#endif /* CONFIG_THERMAL */ +} + +#ifdef CONFIG_THERMAL +static int iwl_mld_get_temp(struct iwl_mld *mld, s32 *temp) +{ + struct iwl_host_cmd cmd = { + .id = WIDE_ID(PHY_OPS_GROUP, CMD_DTS_MEASUREMENT_TRIGGER_WIDE), + .flags = CMD_WANT_SKB, + }; + const struct iwl_dts_measurement_resp *resp; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) { + IWL_ERR(mld, + "Failed to send the temperature measurement command (err=%d)\n", + ret); + return ret; + } + + if (iwl_rx_packet_payload_len(cmd.resp_pkt) < sizeof(*resp)) { + IWL_ERR(mld, + "Failed to get a valid response to DTS measurement\n"); + ret = -EIO; + goto free_resp; + } + + resp = (const void *)cmd.resp_pkt->data; + *temp = le32_to_cpu(resp->temp); + + IWL_DEBUG_TEMP(mld, + "Got temperature measurement response: temp=%d\n", + *temp); + +free_resp: + iwl_free_resp(&cmd); + return ret; +} + +static int compare_temps(const void *a, const void *b) +{ + return ((s16)le16_to_cpu(*(const __le16 *)a) - + (s16)le16_to_cpu(*(const __le16 *)b)); +} + +struct iwl_trip_walk_data { + __le16 *thresholds; + int count; +}; + +static int iwl_trip_temp_iter(struct thermal_trip *trip, void *arg) +{ + struct iwl_trip_walk_data *twd = arg; + + if (trip->temperature == THERMAL_TEMP_INVALID) + return 0; + + twd->thresholds[twd->count++] = + cpu_to_le16((s16)(trip->temperature / 1000)); + return 0; +} +#endif + +int iwl_mld_config_temp_report_ths(struct iwl_mld *mld) +{ + struct temp_report_ths_cmd cmd = {0}; + int ret; +#ifdef CONFIG_THERMAL + struct iwl_trip_walk_data twd = { + .thresholds = cmd.thresholds, + .count = 0 + }; + + if (!mld->tzone) + goto send; + + /* The thermal core holds an array of temperature trips that are + * unsorted and uncompressed, the FW should get it compressed and + * sorted. + */ + + /* compress trips to cmd array, remove uninitialized values*/ + for_each_thermal_trip(mld->tzone, iwl_trip_temp_iter, &twd); + + cmd.num_temps = cpu_to_le32(twd.count); + if (twd.count) + sort(cmd.thresholds, twd.count, sizeof(s16), + compare_temps, NULL); + +send: +#endif + lockdep_assert_wiphy(mld->wiphy); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, + TEMP_REPORTING_THRESHOLDS_CMD), + &cmd); + if (ret) + IWL_ERR(mld, "TEMP_REPORT_THS_CMD command failed (err=%d)\n", + ret); + + return ret; +} + +#ifdef CONFIG_THERMAL +static int iwl_mld_tzone_get_temp(struct thermal_zone_device *device, + int *temperature) +{ + struct iwl_mld *mld = thermal_zone_device_priv(device); + int temp; + int ret = 0; + + wiphy_lock(mld->wiphy); + + if (!mld->fw_status.running) { + /* Tell the core that there is no valid temperature value to + * return, but it need not worry about this. + */ + *temperature = THERMAL_TEMP_INVALID; + goto unlock; + } + + ret = iwl_mld_get_temp(mld, &temp); + if (ret) + goto unlock; + + *temperature = temp * 1000; +unlock: + wiphy_unlock(mld->wiphy); + return ret; +} + +static int iwl_mld_tzone_set_trip_temp(struct thermal_zone_device *device, + const struct thermal_trip *trip, + int temp) +{ + struct iwl_mld *mld = thermal_zone_device_priv(device); + int ret; + + wiphy_lock(mld->wiphy); + + if (!mld->fw_status.running) { + ret = -EIO; + goto unlock; + } + + if ((temp / 1000) > S16_MAX) { + ret = -EINVAL; + goto unlock; + } + + ret = iwl_mld_config_temp_report_ths(mld); +unlock: + wiphy_unlock(mld->wiphy); + return ret; +} + +static struct thermal_zone_device_ops tzone_ops = { + .get_temp = iwl_mld_tzone_get_temp, + .set_trip_temp = iwl_mld_tzone_set_trip_temp, +}; + +static void iwl_mld_thermal_zone_register(struct iwl_mld *mld) +{ + int ret; + char name[16]; + static atomic_t counter = ATOMIC_INIT(0); + struct thermal_trip trips[IWL_MAX_DTS_TRIPS] = { + [0 ... IWL_MAX_DTS_TRIPS - 1] = { + .temperature = THERMAL_TEMP_INVALID, + .type = THERMAL_TRIP_PASSIVE, + .flags = THERMAL_TRIP_FLAG_RW_TEMP, + }, + }; + + BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); + + sprintf(name, "iwlwifi_%u", atomic_inc_return(&counter) & 0xFF); + mld->tzone = + thermal_zone_device_register_with_trips(name, trips, + IWL_MAX_DTS_TRIPS, + mld, &tzone_ops, + NULL, 0, 0); + if (IS_ERR(mld->tzone)) { + IWL_DEBUG_TEMP(mld, + "Failed to register to thermal zone (err = %ld)\n", + PTR_ERR(mld->tzone)); + mld->tzone = NULL; + return; + } + + ret = thermal_zone_device_enable(mld->tzone); + if (ret) { + IWL_DEBUG_TEMP(mld, "Failed to enable thermal zone\n"); + thermal_zone_device_unregister(mld->tzone); + } +} + +int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, + enum iwl_ctdp_cmd_operation op) +{ + struct iwl_ctdp_cmd cmd = { + .operation = cpu_to_le32(op), + .window_size = 0, + }; + u32 budget; + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + /* Do a linear scale from IWL_MLD_MIN_CTDP_BUDGET_MW to the configured + * maximum in the predefined number of steps. + */ + budget = ((mld->power_budget_mw - IWL_MLD_MIN_CTDP_BUDGET_MW) * + (IWL_MLD_NUM_CTDP_STEPS - 1 - state)) / + (IWL_MLD_NUM_CTDP_STEPS - 1) + + IWL_MLD_MIN_CTDP_BUDGET_MW; + cmd.budget = cpu_to_le32(budget); + + ret = iwl_mld_send_cmd_pdu(mld, WIDE_ID(PHY_OPS_GROUP, CTDP_CONFIG_CMD), + &cmd); + + if (ret) { + IWL_ERR(mld, "cTDP command failed (err=%d)\n", ret); + return ret; + } + + if (op == CTDP_CMD_OPERATION_START) + mld->cooling_dev.cur_state = state; + + return 0; +} + +static int iwl_mld_tcool_get_max_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + *state = IWL_MLD_NUM_CTDP_STEPS - 1; + + return 0; +} + +static int iwl_mld_tcool_get_cur_state(struct thermal_cooling_device *cdev, + unsigned long *state) +{ + struct iwl_mld *mld = (struct iwl_mld *)(cdev->devdata); + + *state = mld->cooling_dev.cur_state; + + return 0; +} + +static int iwl_mld_tcool_set_cur_state(struct thermal_cooling_device *cdev, + unsigned long new_state) +{ + struct iwl_mld *mld = (struct iwl_mld *)(cdev->devdata); + int ret; + + wiphy_lock(mld->wiphy); + + if (!mld->fw_status.running) { + ret = -EIO; + goto unlock; + } + + if (new_state >= IWL_MLD_NUM_CTDP_STEPS) { + ret = -EINVAL; + goto unlock; + } + + ret = iwl_mld_config_ctdp(mld, new_state, CTDP_CMD_OPERATION_START); + +unlock: + wiphy_unlock(mld->wiphy); + return ret; +} + +static const struct thermal_cooling_device_ops tcooling_ops = { + .get_max_state = iwl_mld_tcool_get_max_state, + .get_cur_state = iwl_mld_tcool_get_cur_state, + .set_cur_state = iwl_mld_tcool_set_cur_state, +}; + +static void iwl_mld_cooling_device_register(struct iwl_mld *mld) +{ + char name[] = "iwlwifi"; + + BUILD_BUG_ON(ARRAY_SIZE(name) >= THERMAL_NAME_LENGTH); + + mld->cooling_dev.cdev = + thermal_cooling_device_register(name, + mld, + &tcooling_ops); + + if (IS_ERR(mld->cooling_dev.cdev)) { + IWL_DEBUG_TEMP(mld, + "Failed to register to cooling device (err = %ld)\n", + PTR_ERR(mld->cooling_dev.cdev)); + mld->cooling_dev.cdev = NULL; + return; + } +} + +static void iwl_mld_thermal_zone_unregister(struct iwl_mld *mld) +{ + if (!mld->tzone) + return; + + IWL_DEBUG_TEMP(mld, "Thermal zone device unregister\n"); + if (mld->tzone) { + thermal_zone_device_unregister(mld->tzone); + mld->tzone = NULL; + } +} + +static void iwl_mld_cooling_device_unregister(struct iwl_mld *mld) +{ + if (!mld->cooling_dev.cdev) + return; + + IWL_DEBUG_TEMP(mld, "Cooling device unregister\n"); + if (mld->cooling_dev.cdev) { + thermal_cooling_device_unregister(mld->cooling_dev.cdev); + mld->cooling_dev.cdev = NULL; + } +} +#endif /* CONFIG_THERMAL */ + +static u32 iwl_mld_ctdp_get_max_budget(struct iwl_mld *mld) +{ + u64 bios_power_budget = 0; + u32 default_power_budget; + + switch (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id)) { + case IWL_CFG_RF_TYPE_GF: + /* dual-radio devices have a higher budget */ + if (CSR_HW_RFID_IS_CDB(mld->trans->info.hw_rf_id)) + default_power_budget = 5200; + else + default_power_budget = 2880; + break; + case IWL_CFG_RF_TYPE_FM: + default_power_budget = 3450; + break; + case IWL_CFG_RF_TYPE_WH: + case IWL_CFG_RF_TYPE_PE: + default: + default_power_budget = 5550; + break; + } + + iwl_bios_get_pwr_limit(&mld->fwrt, &bios_power_budget); + + /* 32bit in UEFI, 16bit in ACPI; use BIOS value if it is in range */ + if (bios_power_budget && + bios_power_budget != 0xffff && bios_power_budget != 0xffffffff && + bios_power_budget >= IWL_MLD_MIN_CTDP_BUDGET_MW && + bios_power_budget <= default_power_budget) + return (u32)bios_power_budget; + + return default_power_budget; +} + +void iwl_mld_thermal_initialize(struct iwl_mld *mld) +{ + lockdep_assert_not_held(&mld->wiphy->mtx); + + wiphy_delayed_work_init(&mld->ct_kill_exit_wk, iwl_mld_exit_ctkill); + + mld->power_budget_mw = iwl_mld_ctdp_get_max_budget(mld); + IWL_DEBUG_TEMP(mld, "cTDP power budget: %d mW\n", mld->power_budget_mw); + +#ifdef CONFIG_THERMAL + iwl_mld_cooling_device_register(mld); + iwl_mld_thermal_zone_register(mld); +#endif +} + +void iwl_mld_thermal_exit(struct iwl_mld *mld) +{ + wiphy_lock(mld->wiphy); + wiphy_delayed_work_cancel(mld->wiphy, &mld->ct_kill_exit_wk); + wiphy_unlock(mld->wiphy); + +#ifdef CONFIG_THERMAL + iwl_mld_cooling_device_unregister(mld); + iwl_mld_thermal_zone_unregister(mld); +#endif +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/thermal.h b/drivers/net/wireless/intel/iwlwifi/mld/thermal.h new file mode 100644 index 000000000000..8c8893331b05 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/thermal.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_thermal_h__ +#define __iwl_mld_thermal_h__ + +#include "iwl-trans.h" + +struct iwl_mld; + +#ifdef CONFIG_THERMAL +#include <linux/thermal.h> + +/* + * struct iwl_mld_cooling_device + * @cur_state: current state + * @cdev: struct thermal cooling device + */ +struct iwl_mld_cooling_device { + u32 cur_state; + struct thermal_cooling_device *cdev; +}; + +int iwl_mld_config_ctdp(struct iwl_mld *mld, u32 state, + enum iwl_ctdp_cmd_operation op); +#endif + +void iwl_mld_handle_temp_notif(struct iwl_mld *mld, struct iwl_rx_packet *pkt); +void iwl_mld_handle_ct_kill_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +int iwl_mld_config_temp_report_ths(struct iwl_mld *mld); +void iwl_mld_thermal_initialize(struct iwl_mld *mld); +void iwl_mld_thermal_exit(struct iwl_mld *mld); + +#endif /* __iwl_mld_thermal_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c new file mode 100644 index 000000000000..50799f9bfccb --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.c @@ -0,0 +1,240 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2025 Intel Corporation + */ + +#include "mld.h" +#include "hcmd.h" +#include "ptp.h" +#include "time_sync.h" +#include <linux/ieee80211.h> + +static int iwl_mld_init_time_sync(struct iwl_mld *mld, u32 protocols, + const u8 *addr) +{ + struct iwl_mld_time_sync_data *time_sync = kzalloc(sizeof(*time_sync), + GFP_KERNEL); + + if (!time_sync) + return -ENOMEM; + + time_sync->active_protocols = protocols; + ether_addr_copy(time_sync->peer_addr, addr); + skb_queue_head_init(&time_sync->frame_list); + rcu_assign_pointer(mld->time_sync, time_sync); + + return 0; +} + +int iwl_mld_time_sync_fw_config(struct iwl_mld *mld) +{ + struct iwl_time_sync_cfg_cmd cmd = {}; + struct iwl_mld_time_sync_data *time_sync; + int err; + + time_sync = wiphy_dereference(mld->wiphy, mld->time_sync); + if (!time_sync) + return -EINVAL; + + cmd.protocols = cpu_to_le32(time_sync->active_protocols); + ether_addr_copy(cmd.peer_addr, time_sync->peer_addr); + + err = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + WNM_80211V_TIMING_MEASUREMENT_CONFIG_CMD), + &cmd); + if (err) + IWL_ERR(mld, "Failed to send time sync cfg cmd: %d\n", err); + + return err; +} + +int iwl_mld_time_sync_config(struct iwl_mld *mld, const u8 *addr, u32 protocols) +{ + struct iwl_mld_time_sync_data *time_sync; + int err; + + time_sync = wiphy_dereference(mld->wiphy, mld->time_sync); + + /* The fw only supports one peer. We do allow reconfiguration of the + * same peer for cases of fw reset etc. + */ + if (time_sync && time_sync->active_protocols && + !ether_addr_equal(addr, time_sync->peer_addr)) { + IWL_DEBUG_INFO(mld, "Time sync: reject config for peer: %pM\n", + addr); + return -ENOBUFS; + } + + if (protocols & ~(IWL_TIME_SYNC_PROTOCOL_TM | + IWL_TIME_SYNC_PROTOCOL_FTM)) + return -EINVAL; + + IWL_DEBUG_INFO(mld, "Time sync: set peer addr=%pM\n", addr); + + iwl_mld_deinit_time_sync(mld); + err = iwl_mld_init_time_sync(mld, protocols, addr); + if (err) + return err; + + err = iwl_mld_time_sync_fw_config(mld); + return err; +} + +void iwl_mld_deinit_time_sync(struct iwl_mld *mld) +{ + struct iwl_mld_time_sync_data *time_sync = + wiphy_dereference(mld->wiphy, mld->time_sync); + + if (!time_sync) + return; + + RCU_INIT_POINTER(mld->time_sync, NULL); + skb_queue_purge(&time_sync->frame_list); + kfree_rcu(time_sync, rcu_head); +} + +bool iwl_mld_time_sync_frame(struct iwl_mld *mld, struct sk_buff *skb, u8 *addr) +{ + struct iwl_mld_time_sync_data *time_sync; + + rcu_read_lock(); + time_sync = rcu_dereference(mld->time_sync); + if (time_sync && ether_addr_equal(time_sync->peer_addr, addr) && + (ieee80211_is_timing_measurement(skb) || ieee80211_is_ftm(skb))) { + skb_queue_tail(&time_sync->frame_list, skb); + rcu_read_unlock(); + return true; + } + rcu_read_unlock(); + + return false; +} + +static bool iwl_mld_is_skb_match(struct sk_buff *skb, u8 *addr, u8 dialog_token) +{ + struct ieee80211_mgmt *mgmt = (void *)skb->data; + u8 skb_dialog_token; + + if (ieee80211_is_timing_measurement(skb)) + skb_dialog_token = mgmt->u.action.u.wnm_timing_msr.dialog_token; + else + skb_dialog_token = mgmt->u.action.u.ftm.dialog_token; + + if ((ether_addr_equal(mgmt->sa, addr) || + ether_addr_equal(mgmt->da, addr)) && + skb_dialog_token == dialog_token) + return true; + + return false; +} + +static struct sk_buff *iwl_mld_time_sync_find_skb(struct iwl_mld *mld, u8 *addr, + u8 dialog_token) +{ + struct iwl_mld_time_sync_data *time_sync; + struct sk_buff *skb; + + rcu_read_lock(); + + time_sync = rcu_dereference(mld->time_sync); + if (IWL_FW_CHECK(mld, !time_sync, + "Time sync notification but time sync is not initialized\n")) { + rcu_read_unlock(); + return NULL; + } + + /* The notifications are expected to arrive in the same order of the + * frames. If the incoming notification doesn't match the first SKB + * in the queue, it means there was no time sync notification for this + * SKB and it can be dropped. + */ + while ((skb = skb_dequeue(&time_sync->frame_list))) { + if (iwl_mld_is_skb_match(skb, addr, dialog_token)) + break; + + kfree_skb(skb); + skb = NULL; + IWL_DEBUG_DROP(mld, + "Time sync: drop SKB without matching notification\n"); + } + rcu_read_unlock(); + + return skb; +} + +static u64 iwl_mld_get_64_bit(__le32 high, __le32 low) +{ + return ((u64)le32_to_cpu(high) << 32) | le32_to_cpu(low); +} + +void iwl_mld_handle_time_msmt_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct ptp_data *data = &mld->ptp_data; + struct iwl_time_msmt_notify *notif = (void *)pkt->data; + struct ieee80211_rx_status *rx_status; + struct skb_shared_hwtstamps *shwt; + u64 ts_10ns; + struct sk_buff *skb = + iwl_mld_time_sync_find_skb(mld, notif->peer_addr, + le32_to_cpu(notif->dialog_token)); + u64 adj_time; + + if (IWL_FW_CHECK(mld, !skb, "Time sync event but no pending skb\n")) + return; + + spin_lock_bh(&data->lock); + ts_10ns = iwl_mld_get_64_bit(notif->t2_hi, notif->t2_lo); + adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10); + shwt = skb_hwtstamps(skb); + shwt->hwtstamp = ktime_set(0, adj_time); + + ts_10ns = iwl_mld_get_64_bit(notif->t3_hi, notif->t3_lo); + adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10); + rx_status = IEEE80211_SKB_RXCB(skb); + rx_status->ack_tx_hwtstamp = ktime_set(0, adj_time); + spin_unlock_bh(&data->lock); + + IWL_DEBUG_INFO(mld, + "Time sync: RX event - report frame t2=%llu t3=%llu\n", + ktime_to_ns(shwt->hwtstamp), + ktime_to_ns(rx_status->ack_tx_hwtstamp)); + ieee80211_rx_napi(mld->hw, NULL, skb, NULL); +} + +void iwl_mld_handle_time_sync_confirm_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct ptp_data *data = &mld->ptp_data; + struct iwl_time_msmt_cfm_notify *notif = (void *)pkt->data; + struct ieee80211_tx_status status = {}; + struct skb_shared_hwtstamps *shwt; + u64 ts_10ns, adj_time; + + status.skb = + iwl_mld_time_sync_find_skb(mld, notif->peer_addr, + le32_to_cpu(notif->dialog_token)); + + if (IWL_FW_CHECK(mld, !status.skb, + "Time sync confirm but no pending skb\n")) + return; + + spin_lock_bh(&data->lock); + ts_10ns = iwl_mld_get_64_bit(notif->t1_hi, notif->t1_lo); + adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10); + shwt = skb_hwtstamps(status.skb); + shwt->hwtstamp = ktime_set(0, adj_time); + + ts_10ns = iwl_mld_get_64_bit(notif->t4_hi, notif->t4_lo); + adj_time = iwl_mld_ptp_get_adj_time(mld, ts_10ns * 10); + status.info = IEEE80211_SKB_CB(status.skb); + status.ack_hwtstamp = ktime_set(0, adj_time); + spin_unlock_bh(&data->lock); + + IWL_DEBUG_INFO(mld, + "Time sync: TX event - report frame t1=%llu t4=%llu\n", + ktime_to_ns(shwt->hwtstamp), + ktime_to_ns(status.ack_hwtstamp)); + ieee80211_tx_status_ext(mld->hw, &status); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h new file mode 100644 index 000000000000..2d4c5512e961 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/time_sync.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2025 Intel Corporation + */ +#ifndef __iwl_mld_time_sync_h__ +#define __iwl_mld_time_sync_h__ + +struct iwl_mld_time_sync_data { + struct rcu_head rcu_head; + u8 peer_addr[ETH_ALEN]; + u32 active_protocols; + struct sk_buff_head frame_list; +}; + +int iwl_mld_time_sync_config(struct iwl_mld *mld, const u8 *addr, + u32 protocols); +int iwl_mld_time_sync_fw_config(struct iwl_mld *mld); +void iwl_mld_deinit_time_sync(struct iwl_mld *mld); +void iwl_mld_handle_time_msmt_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +bool iwl_mld_time_sync_frame(struct iwl_mld *mld, struct sk_buff *skb, + u8 *addr); +void iwl_mld_handle_time_sync_confirm_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +#endif /* __iwl_mld_time_sync_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.c b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c new file mode 100644 index 000000000000..a9ca92c0455e --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.c @@ -0,0 +1,702 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024-2025 Intel Corporation + */ + +#include <net/mac80211.h> + +#include "tlc.h" +#include "hcmd.h" +#include "sta.h" + +#include "fw/api/rs.h" +#include "fw/api/context.h" +#include "fw/api/dhc.h" + +static u8 iwl_mld_fw_bw_from_sta_bw(const struct ieee80211_link_sta *link_sta) +{ + switch (link_sta->bandwidth) { + case IEEE80211_STA_RX_BW_320: + return IWL_TLC_MNG_CH_WIDTH_320MHZ; + case IEEE80211_STA_RX_BW_160: + return IWL_TLC_MNG_CH_WIDTH_160MHZ; + case IEEE80211_STA_RX_BW_80: + return IWL_TLC_MNG_CH_WIDTH_80MHZ; + case IEEE80211_STA_RX_BW_40: + return IWL_TLC_MNG_CH_WIDTH_40MHZ; + case IEEE80211_STA_RX_BW_20: + default: + return IWL_TLC_MNG_CH_WIDTH_20MHZ; + } +} + +static __le16 +iwl_mld_get_tlc_cmd_flags(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + const struct ieee80211_sta_he_cap *own_he_cap, + const struct ieee80211_sta_eht_cap *own_eht_cap) +{ + struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; + struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + bool has_vht = vht_cap->vht_supported; + u16 flags = 0; + + /* STBC flags */ + if (mld->cfg->ht_params.stbc && + (hweight8(iwl_mld_get_valid_tx_ant(mld)) > 1)) { + if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] & + IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) + flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; + else if (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK) + flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; + else if (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC) + flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; + } + + /* LDPC */ + if (mld->cfg->ht_params.ldpc && + ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || + (has_vht && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) + flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; + + if (he_cap->has_he && (he_cap->he_cap_elem.phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) + flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; + + if (own_he_cap && + !(own_he_cap->he_cap_elem.phy_cap_info[1] & + IEEE80211_HE_PHY_CAP1_LDPC_CODING_IN_PAYLOAD)) + flags &= ~IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; + + /* DCM */ + if (he_cap->has_he && + (he_cap->he_cap_elem.phy_cap_info[3] & + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_RX_MASK && + own_he_cap && + own_he_cap->he_cap_elem.phy_cap_info[3] & + IEEE80211_HE_PHY_CAP3_DCM_MAX_CONST_TX_MASK)) + flags |= IWL_TLC_MNG_CFG_FLAGS_HE_DCM_NSS_1_MSK; + + /* Extra EHT LTF */ + if (own_eht_cap && + own_eht_cap->eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF && + link_sta->eht_cap.has_eht && + link_sta->eht_cap.eht_cap_elem.phy_cap_info[5] & + IEEE80211_EHT_PHY_CAP5_SUPP_EXTRA_EHT_LTF) { + flags |= IWL_TLC_MNG_CFG_FLAGS_EHT_EXTRA_LTF_MSK; + } + + return cpu_to_le16(flags); +} + +static u8 iwl_mld_get_fw_chains(struct iwl_mld *mld) +{ + u8 chains = iwl_mld_get_valid_tx_ant(mld); + u8 fw_chains = 0; + + if (chains & ANT_A) + fw_chains |= IWL_TLC_MNG_CHAIN_A_MSK; + if (chains & ANT_B) + fw_chains |= IWL_TLC_MNG_CHAIN_B_MSK; + + return fw_chains; +} + +static u8 iwl_mld_get_fw_sgi(struct ieee80211_link_sta *link_sta) +{ + struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; + struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + u8 sgi_chwidths = 0; + + /* If the association supports HE, HT/VHT rates will never be used for + * Tx and therefor there's no need to set the + * sgi-per-channel-width-support bits + */ + if (he_cap->has_he) + return 0; + + if (ht_cap->cap & IEEE80211_HT_CAP_SGI_20) + sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_20MHZ); + if (ht_cap->cap & IEEE80211_HT_CAP_SGI_40) + sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_40MHZ); + if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_80) + sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_80MHZ); + if (vht_cap->cap & IEEE80211_VHT_CAP_SHORT_GI_160) + sgi_chwidths |= BIT(IWL_TLC_MNG_CH_WIDTH_160MHZ); + + return sgi_chwidths; +} + +static int +iwl_mld_get_highest_fw_mcs(const struct ieee80211_sta_vht_cap *vht_cap, + int nss) +{ + u16 rx_mcs = le16_to_cpu(vht_cap->vht_mcs.rx_mcs_map) & + (0x3 << (2 * (nss - 1))); + rx_mcs >>= (2 * (nss - 1)); + + switch (rx_mcs) { + case IEEE80211_VHT_MCS_SUPPORT_0_7: + return IWL_TLC_MNG_HT_RATE_MCS7; + case IEEE80211_VHT_MCS_SUPPORT_0_8: + return IWL_TLC_MNG_HT_RATE_MCS8; + case IEEE80211_VHT_MCS_SUPPORT_0_9: + return IWL_TLC_MNG_HT_RATE_MCS9; + default: + WARN_ON_ONCE(1); + break; + } + + return 0; +} + +static void +iwl_mld_fill_vht_rates(const struct ieee80211_link_sta *link_sta, + const struct ieee80211_sta_vht_cap *vht_cap, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + u16 supp; + int i, highest_mcs; + u8 max_nss = link_sta->rx_nss; + struct ieee80211_vht_cap ieee_vht_cap = { + .vht_cap_info = cpu_to_le32(vht_cap->cap), + .supp_mcs = vht_cap->vht_mcs, + }; + + /* the station support only a single receive chain */ + if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + max_nss = 1; + + for (i = 0; i < max_nss && i < IWL_TLC_NSS_MAX; i++) { + int nss = i + 1; + + highest_mcs = iwl_mld_get_highest_fw_mcs(vht_cap, nss); + if (!highest_mcs) + continue; + + supp = BIT(highest_mcs + 1) - 1; + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_20) + supp &= ~BIT(IWL_TLC_MNG_HT_RATE_MCS9); + + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = cpu_to_le16(supp); + /* Check if VHT extended NSS indicates that the bandwidth/NSS + * configuration is supported - only for MCS 0 since we already + * decoded the MCS bits anyway ourselves. + */ + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160 && + ieee80211_get_vht_max_nss(&ieee_vht_cap, + IEEE80211_VHT_CHANWIDTH_160MHZ, + 0, true, nss) >= nss) + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] = + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80]; + } +} + +static u16 iwl_mld_he_mac80211_mcs_to_fw_mcs(u16 mcs) +{ + switch (mcs) { + case IEEE80211_HE_MCS_SUPPORT_0_7: + return BIT(IWL_TLC_MNG_HT_RATE_MCS7 + 1) - 1; + case IEEE80211_HE_MCS_SUPPORT_0_9: + return BIT(IWL_TLC_MNG_HT_RATE_MCS9 + 1) - 1; + case IEEE80211_HE_MCS_SUPPORT_0_11: + return BIT(IWL_TLC_MNG_HT_RATE_MCS11 + 1) - 1; + case IEEE80211_HE_MCS_NOT_SUPPORTED: + return 0; + } + + WARN(1, "invalid HE MCS %d\n", mcs); + return 0; +} + +static void +iwl_mld_fill_he_rates(const struct ieee80211_link_sta *link_sta, + const struct ieee80211_sta_he_cap *own_he_cap, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + u16 mcs_160 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_160); + u16 mcs_80 = le16_to_cpu(he_cap->he_mcs_nss_supp.rx_mcs_80); + u16 tx_mcs_80 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_80); + u16 tx_mcs_160 = le16_to_cpu(own_he_cap->he_mcs_nss_supp.tx_mcs_160); + int i; + u8 nss = link_sta->rx_nss; + + /* the station support only a single receive chain */ + if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + nss = 1; + + for (i = 0; i < nss && i < IWL_TLC_NSS_MAX; i++) { + u16 _mcs_160 = (mcs_160 >> (2 * i)) & 0x3; + u16 _mcs_80 = (mcs_80 >> (2 * i)) & 0x3; + u16 _tx_mcs_160 = (tx_mcs_160 >> (2 * i)) & 0x3; + u16 _tx_mcs_80 = (tx_mcs_80 >> (2 * i)) & 0x3; + + /* If one side doesn't support - mark both as not supporting */ + if (_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED || + _tx_mcs_80 == IEEE80211_HE_MCS_NOT_SUPPORTED) { + _mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; + _tx_mcs_80 = IEEE80211_HE_MCS_NOT_SUPPORTED; + } + if (_mcs_80 > _tx_mcs_80) + _mcs_80 = _tx_mcs_80; + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_80] = + cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_80)); + + /* If one side doesn't support - mark both as not supporting */ + if (_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED || + _tx_mcs_160 == IEEE80211_HE_MCS_NOT_SUPPORTED) { + _mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; + _tx_mcs_160 = IEEE80211_HE_MCS_NOT_SUPPORTED; + } + if (_mcs_160 > _tx_mcs_160) + _mcs_160 = _tx_mcs_160; + cmd->ht_rates[i][IWL_TLC_MCS_PER_BW_160] = + cpu_to_le16(iwl_mld_he_mac80211_mcs_to_fw_mcs(_mcs_160)); + } +} + +static void iwl_mld_set_eht_mcs(__le16 ht_rates[][3], + enum IWL_TLC_MCS_PER_BW bw, + u8 max_nss, u16 mcs_msk) +{ + if (max_nss >= 2) + ht_rates[IWL_TLC_NSS_2][bw] |= cpu_to_le16(mcs_msk); + + if (max_nss >= 1) + ht_rates[IWL_TLC_NSS_1][bw] |= cpu_to_le16(mcs_msk); +} + +static const +struct ieee80211_eht_mcs_nss_supp_bw * +iwl_mld_get_eht_mcs_of_bw(enum IWL_TLC_MCS_PER_BW bw, + const struct ieee80211_eht_mcs_nss_supp *eht_mcs) +{ + switch (bw) { + case IWL_TLC_MCS_PER_BW_80: + return &eht_mcs->bw._80; + case IWL_TLC_MCS_PER_BW_160: + return &eht_mcs->bw._160; + case IWL_TLC_MCS_PER_BW_320: + return &eht_mcs->bw._320; + default: + return NULL; + } +} + +static u8 iwl_mld_get_eht_max_nss(u8 rx_nss, u8 tx_nss) +{ + u8 tx = u8_get_bits(tx_nss, IEEE80211_EHT_MCS_NSS_TX); + u8 rx = u8_get_bits(rx_nss, IEEE80211_EHT_MCS_NSS_RX); + /* the max nss that can be used, + * is the min with our tx capa and the peer rx capa. + */ + return min(tx, rx); +} + +#define MAX_NSS_MCS(mcs_num, rx, tx) \ + iwl_mld_get_eht_max_nss((rx)->rx_tx_mcs ##mcs_num## _max_nss, \ + (tx)->rx_tx_mcs ##mcs_num## _max_nss) + +static void +iwl_mld_fill_eht_rates(struct ieee80211_vif *vif, + const struct ieee80211_link_sta *link_sta, + const struct ieee80211_sta_he_cap *own_he_cap, + const struct ieee80211_sta_eht_cap *own_eht_cap, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + /* peer RX mcs capa */ + const struct ieee80211_eht_mcs_nss_supp *eht_rx_mcs = + &link_sta->eht_cap.eht_mcs_nss_supp; + /* our TX mcs capa */ + const struct ieee80211_eht_mcs_nss_supp *eht_tx_mcs = + &own_eht_cap->eht_mcs_nss_supp; + + enum IWL_TLC_MCS_PER_BW bw; + struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_rx_20; + struct ieee80211_eht_mcs_nss_supp_20mhz_only mcs_tx_20; + + /* peer is 20 MHz only */ + if (vif->type == NL80211_IFTYPE_AP && + !(link_sta->he_cap.he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + mcs_rx_20 = eht_rx_mcs->only_20mhz; + } else { + mcs_rx_20.rx_tx_mcs7_max_nss = + eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_rx_20.rx_tx_mcs9_max_nss = + eht_rx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_rx_20.rx_tx_mcs11_max_nss = + eht_rx_mcs->bw._80.rx_tx_mcs11_max_nss; + mcs_rx_20.rx_tx_mcs13_max_nss = + eht_rx_mcs->bw._80.rx_tx_mcs13_max_nss; + } + + /* NIC is capable of 20 MHz only */ + if (!(own_he_cap->he_cap_elem.phy_cap_info[0] & + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_MASK_ALL)) { + mcs_tx_20 = eht_tx_mcs->only_20mhz; + } else { + mcs_tx_20.rx_tx_mcs7_max_nss = + eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_tx_20.rx_tx_mcs9_max_nss = + eht_tx_mcs->bw._80.rx_tx_mcs9_max_nss; + mcs_tx_20.rx_tx_mcs11_max_nss = + eht_tx_mcs->bw._80.rx_tx_mcs11_max_nss; + mcs_tx_20.rx_tx_mcs13_max_nss = + eht_tx_mcs->bw._80.rx_tx_mcs13_max_nss; + } + + /* rates for 20/40/80 MHz */ + bw = IWL_TLC_MCS_PER_BW_80; + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(7, &mcs_rx_20, &mcs_tx_20), + GENMASK(7, 0)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(9, &mcs_rx_20, &mcs_tx_20), + GENMASK(9, 8)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(11, &mcs_rx_20, &mcs_tx_20), + GENMASK(11, 10)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(13, &mcs_rx_20, &mcs_tx_20), + GENMASK(13, 12)); + + /* rates for 160/320 MHz */ + for (bw = IWL_TLC_MCS_PER_BW_160; bw <= IWL_TLC_MCS_PER_BW_320; bw++) { + const struct ieee80211_eht_mcs_nss_supp_bw *mcs_rx = + iwl_mld_get_eht_mcs_of_bw(bw, eht_rx_mcs); + const struct ieee80211_eht_mcs_nss_supp_bw *mcs_tx = + iwl_mld_get_eht_mcs_of_bw(bw, eht_tx_mcs); + + /* got unsupported index for bw */ + if (!mcs_rx || !mcs_tx) + continue; + + /* break out if we don't support the bandwidth */ + if (cmd->max_ch_width < (bw + IWL_TLC_MNG_CH_WIDTH_80MHZ)) + break; + + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(9, mcs_rx, mcs_tx), + GENMASK(9, 0)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(11, mcs_rx, mcs_tx), + GENMASK(11, 10)); + iwl_mld_set_eht_mcs(cmd->ht_rates, bw, + MAX_NSS_MCS(13, mcs_rx, mcs_tx), + GENMASK(13, 12)); + } + + /* the station support only a single receive chain */ + if (link_sta->smps_mode == IEEE80211_SMPS_STATIC || + link_sta->rx_nss < 2) + memset(cmd->ht_rates[IWL_TLC_NSS_2], 0, + sizeof(cmd->ht_rates[IWL_TLC_NSS_2])); +} + +static void +iwl_mld_fill_supp_rates(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct ieee80211_supported_band *sband, + const struct ieee80211_sta_he_cap *own_he_cap, + const struct ieee80211_sta_eht_cap *own_eht_cap, + struct iwl_tlc_config_cmd_v4 *cmd) +{ + int i; + u16 non_ht_rates = 0; + unsigned long rates_bitmap; + const struct ieee80211_sta_ht_cap *ht_cap = &link_sta->ht_cap; + const struct ieee80211_sta_vht_cap *vht_cap = &link_sta->vht_cap; + const struct ieee80211_sta_he_cap *he_cap = &link_sta->he_cap; + + /* non HT rates */ + rates_bitmap = link_sta->supp_rates[sband->band]; + for_each_set_bit(i, &rates_bitmap, BITS_PER_LONG) + non_ht_rates |= BIT(sband->bitrates[i].hw_value); + + cmd->non_ht_rates = cpu_to_le16(non_ht_rates); + cmd->mode = IWL_TLC_MNG_MODE_NON_HT; + + if (link_sta->eht_cap.has_eht && own_he_cap && own_eht_cap) { + cmd->mode = IWL_TLC_MNG_MODE_EHT; + iwl_mld_fill_eht_rates(vif, link_sta, own_he_cap, + own_eht_cap, cmd); + } else if (he_cap->has_he && own_he_cap) { + cmd->mode = IWL_TLC_MNG_MODE_HE; + iwl_mld_fill_he_rates(link_sta, own_he_cap, cmd); + } else if (vht_cap->vht_supported) { + cmd->mode = IWL_TLC_MNG_MODE_VHT; + iwl_mld_fill_vht_rates(link_sta, vht_cap, cmd); + } else if (ht_cap->ht_supported) { + cmd->mode = IWL_TLC_MNG_MODE_HT; + cmd->ht_rates[IWL_TLC_NSS_1][IWL_TLC_MCS_PER_BW_80] = + cpu_to_le16(ht_cap->mcs.rx_mask[0]); + + /* the station support only a single receive chain */ + if (link_sta->smps_mode == IEEE80211_SMPS_STATIC) + cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = + 0; + else + cmd->ht_rates[IWL_TLC_NSS_2][IWL_TLC_MCS_PER_BW_80] = + cpu_to_le16(ht_cap->mcs.rx_mask[1]); + } +} + +static void iwl_mld_send_tlc_cmd(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + enum nl80211_band band) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + struct ieee80211_supported_band *sband = mld->hw->wiphy->bands[band]; + const struct ieee80211_sta_he_cap *own_he_cap = + ieee80211_get_he_iftype_cap_vif(sband, vif); + const struct ieee80211_sta_eht_cap *own_eht_cap = + ieee80211_get_eht_iftype_cap_vif(sband, vif); + struct iwl_tlc_config_cmd_v4 cmd = { + /* For AP mode, use 20 MHz until the STA is authorized */ + .max_ch_width = mld_sta->sta_state > IEEE80211_STA_ASSOC ? + iwl_mld_fw_bw_from_sta_bw(link_sta) : + IWL_TLC_MNG_CH_WIDTH_20MHZ, + .flags = iwl_mld_get_tlc_cmd_flags(mld, vif, link_sta, + own_he_cap, own_eht_cap), + .chains = iwl_mld_get_fw_chains(mld), + .sgi_ch_width_supp = iwl_mld_get_fw_sgi(link_sta), + .max_mpdu_len = cpu_to_le16(link_sta->agg.max_amsdu_len), + }; + int fw_sta_id = iwl_mld_fw_sta_id_from_link_sta(mld, link_sta); + int ret; + + if (fw_sta_id < 0) + return; + + cmd.sta_id = fw_sta_id; + + iwl_mld_fill_supp_rates(mld, vif, link_sta, sband, + own_he_cap, own_eht_cap, + &cmd); + + IWL_DEBUG_RATE(mld, + "TLC CONFIG CMD, sta_id=%d, max_ch_width=%d, mode=%d\n", + cmd.sta_id, cmd.max_ch_width, cmd.mode); + + /* Send async since this can be called within a RCU-read section */ + ret = iwl_mld_send_cmd_with_flags_pdu(mld, WIDE_ID(DATA_PATH_GROUP, + TLC_MNG_CONFIG_CMD), + CMD_ASYNC, &cmd); + if (ret) + IWL_ERR(mld, "Failed to send TLC cmd (%d)\n", ret); +} + +int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data) +{ + struct { + struct iwl_dhc_cmd dhc; + struct iwl_dhc_tlc_cmd tlc; + } __packed cmd = { + .tlc.sta_id = sta_id, + .tlc.type = cpu_to_le32(type), + .tlc.data[0] = cpu_to_le32(data), + .dhc.length = cpu_to_le32(sizeof(cmd.tlc) >> 2), + .dhc.index_and_mask = + cpu_to_le32(DHC_TABLE_INTEGRATION | DHC_TARGET_UMAC | + DHC_INTEGRATION_TLC_DEBUG_CONFIG), + }; + int ret; + + ret = iwl_mld_send_cmd_with_flags_pdu(mld, + WIDE_ID(IWL_ALWAYS_LONG_GROUP, + DEBUG_HOST_COMMAND), + CMD_ASYNC, &cmd); + IWL_DEBUG_RATE(mld, "sta_id %d, type: 0x%X, value: 0x%X, ret%d\n", + sta_id, type, data, ret); + return ret; +} + +void iwl_mld_config_tlc_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta) +{ + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + enum nl80211_band band; + + if (WARN_ON_ONCE(!link_conf->chanreq.oper.chan)) + return; + + /* Before we have information about a station, configure the A-MSDU RC + * limit such that iwlmd and mac80211 would not be allowed to build + * A-MSDUs. + */ + if (mld_sta->sta_state < IEEE80211_STA_ASSOC) { + link_sta->agg.max_rc_amsdu_len = 1; + ieee80211_sta_recalc_aggregates(link_sta->sta); + } + + band = link_conf->chanreq.oper.chan->band; + iwl_mld_send_tlc_cmd(mld, vif, link_sta, band); +} + +void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct ieee80211_bss_conf *link; + int link_id; + + lockdep_assert_wiphy(mld->wiphy); + + for_each_vif_active_link(vif, link, link_id) { + struct ieee80211_link_sta *link_sta = + link_sta_dereference_check(sta, link_id); + + if (!link || !link_sta) + continue; + + iwl_mld_config_tlc_link(mld, vif, link, link_sta); + } +} + +static u16 +iwl_mld_get_amsdu_size_of_tid(struct iwl_mld *mld, + struct ieee80211_link_sta *link_sta, + unsigned int tid) +{ + struct ieee80211_sta *sta = link_sta->sta; + struct ieee80211_vif *vif = iwl_mld_sta_from_mac80211(sta)->vif; + const u8 tid_to_mac80211_ac[] = { + IEEE80211_AC_BE, + IEEE80211_AC_BK, + IEEE80211_AC_BK, + IEEE80211_AC_BE, + IEEE80211_AC_VI, + IEEE80211_AC_VI, + IEEE80211_AC_VO, + IEEE80211_AC_VO, + }; + unsigned int result = link_sta->agg.max_rc_amsdu_len; + u8 ac, txf, lmac; + + lockdep_assert_wiphy(mld->wiphy); + + /* Don't send an AMSDU that will be longer than the TXF. + * Add a security margin of 256 for the TX command + headers. + * We also want to have the start of the next packet inside the + * fifo to be able to send bursts. + */ + + if (WARN_ON(tid >= ARRAY_SIZE(tid_to_mac80211_ac))) + return 0; + + ac = tid_to_mac80211_ac[tid]; + + /* For HE redirect to trigger based fifos */ + if (link_sta->he_cap.has_he) + ac += 4; + + txf = iwl_mld_mac80211_ac_to_fw_tx_fifo(ac); + + /* Only one link: take the lmac according to the band */ + if (hweight16(sta->valid_links) <= 1) { + enum nl80211_band band; + struct ieee80211_bss_conf *link = + wiphy_dereference(mld->wiphy, + vif->link_conf[link_sta->link_id]); + + if (WARN_ON(!link || !link->chanreq.oper.chan)) + band = NL80211_BAND_2GHZ; + else + band = link->chanreq.oper.chan->band; + lmac = iwl_mld_get_lmac_id(mld, band); + + /* More than one link but with 2 lmacs: take the minimum */ + } else if (fw_has_capa(&mld->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_CDB_SUPPORT)) { + lmac = IWL_LMAC_5G_INDEX; + result = min_t(unsigned int, result, + mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); + lmac = IWL_LMAC_24G_INDEX; + /* More than one link but only one lmac */ + } else { + lmac = IWL_LMAC_24G_INDEX; + } + + return min_t(unsigned int, result, + mld->fwrt.smem_cfg.lmac[lmac].txfifo_size[txf] - 256); +} + +void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_tlc_update_notif *notif = (void *)pkt->data; + struct ieee80211_link_sta *link_sta; + u32 flags = le32_to_cpu(notif->flags); + u32 enabled; + u16 size; + + if (IWL_FW_CHECK(mld, notif->sta_id >= mld->fw->ucode_capa.num_stations, + "Invalid sta id (%d) in TLC notification\n", + notif->sta_id)) + return; + + link_sta = wiphy_dereference(mld->wiphy, + mld->fw_id_to_link_sta[notif->sta_id]); + + if (WARN(IS_ERR_OR_NULL(link_sta), + "link_sta of sta id (%d) doesn't exist\n", notif->sta_id)) + return; + + if (flags & IWL_TLC_NOTIF_FLAG_RATE) { + struct iwl_mld_link_sta *mld_link_sta = + iwl_mld_link_sta_from_mac80211(link_sta); + char pretty_rate[100]; + + if (WARN_ON(!mld_link_sta)) + return; + + mld_link_sta->last_rate_n_flags = + iwl_v3_rate_from_v2_v3(notif->rate, + mld->fw_rates_ver_3); + + rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), + mld_link_sta->last_rate_n_flags); + IWL_DEBUG_RATE(mld, "TLC notif: new rate = %s\n", pretty_rate); + } + + /* We are done processing the notif */ + if (!(flags & IWL_TLC_NOTIF_FLAG_AMSDU)) + return; + + enabled = le32_to_cpu(notif->amsdu_enabled); + size = le32_to_cpu(notif->amsdu_size); + + if (size < 2000) { + size = 0; + enabled = 0; + } + + if (IWL_FW_CHECK(mld, size > link_sta->agg.max_amsdu_len, + "Invalid AMSDU len in TLC notif: %d (Max AMSDU len: %d)\n", + size, link_sta->agg.max_amsdu_len)) + return; + + link_sta->agg.max_rc_amsdu_len = size; + + for (int i = 0; i < IWL_MAX_TID_COUNT; i++) { + if (enabled & BIT(i)) + link_sta->agg.max_tid_amsdu_len[i] = + iwl_mld_get_amsdu_size_of_tid(mld, link_sta, i); + else + link_sta->agg.max_tid_amsdu_len[i] = 1; + } + + ieee80211_sta_recalc_aggregates(link_sta->sta); + + IWL_DEBUG_RATE(mld, + "AMSDU update. AMSDU size: %d, AMSDU selected size: %d, AMSDU TID bitmap 0x%X\n", + le32_to_cpu(notif->amsdu_size), size, enabled); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tlc.h b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h new file mode 100644 index 000000000000..c32f42e8840b --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tlc.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_tlc_h__ +#define __iwl_mld_tlc_h__ + +#include "mld.h" + +void iwl_mld_config_tlc_link(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta); + +void iwl_mld_config_tlc(struct iwl_mld *mld, struct ieee80211_vif *vif, + struct ieee80211_sta *sta); + +void iwl_mld_handle_tlc_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); + +int iwl_mld_send_tlc_dhc(struct iwl_mld *mld, u8 sta_id, u32 type, u32 data); + +#endif /* __iwl_mld_tlc_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.c b/drivers/net/wireless/intel/iwlwifi/mld/tx.c new file mode 100644 index 000000000000..3b4b575aadaa --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.c @@ -0,0 +1,1394 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * Copyright (C) 2024 - 2025 Intel Corporation + */ +#include <net/ip.h> + +#include "tx.h" +#include "sta.h" +#include "hcmd.h" +#include "iwl-utils.h" +#include "iface.h" + +#include "fw/dbg.h" + +#include "fw/api/tx.h" +#include "fw/api/rs.h" +#include "fw/api/txq.h" +#include "fw/api/datapath.h" +#include "fw/api/time-event.h" + +#define MAX_ANT_NUM 2 + +/* Toggles between TX antennas. Receives the bitmask of valid TX antennas and + * the *index* used for the last TX, and returns the next valid *index* to use. + * In order to set it in the tx_cmd, must do BIT(idx). + */ +static u8 iwl_mld_next_ant(u8 valid, u8 last_idx) +{ + u8 index = last_idx; + + for (int i = 0; i < MAX_ANT_NUM; i++) { + index = (index + 1) % MAX_ANT_NUM; + if (valid & BIT(index)) + return index; + } + + WARN_ONCE(1, "Failed to toggle between antennas 0x%x", valid); + + return last_idx; +} + +void iwl_mld_toggle_tx_ant(struct iwl_mld *mld, u8 *ant) +{ + *ant = iwl_mld_next_ant(iwl_mld_get_valid_tx_ant(mld), *ant); +} + +static int +iwl_mld_get_queue_size(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct ieee80211_sta *sta = txq->sta; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + int max_size = IWL_DEFAULT_QUEUE_SIZE; + + lockdep_assert_wiphy(mld->wiphy); + + for_each_sta_active_link(txq->vif, sta, link_sta, link_id) { + if (link_sta->eht_cap.has_eht) { + max_size = IWL_DEFAULT_QUEUE_SIZE_EHT; + break; + } + + if (link_sta->he_cap.has_he) + max_size = IWL_DEFAULT_QUEUE_SIZE_HE; + } + + return max_size; +} + +static int iwl_mld_allocate_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + u8 tid = txq->tid == IEEE80211_NUM_TIDS ? IWL_MGMT_TID : txq->tid; + u32 fw_sta_mask = iwl_mld_fw_sta_id_mask(mld, txq->sta); + /* We can't know when the station is asleep or awake, so we + * must disable the queue hang detection. + */ + unsigned int watchdog_timeout = txq->vif->type == NL80211_IFTYPE_AP ? + IWL_WATCHDOG_DISABLED : + mld->trans->mac_cfg->base->wd_timeout; + int queue, size; + + lockdep_assert_wiphy(mld->wiphy); + + if (tid == IWL_MGMT_TID) + size = max_t(u32, IWL_MGMT_QUEUE_SIZE, + mld->trans->mac_cfg->base->min_txq_size); + else + size = iwl_mld_get_queue_size(mld, txq); + + queue = iwl_trans_txq_alloc(mld->trans, 0, fw_sta_mask, tid, size, + watchdog_timeout); + + if (queue >= 0) + IWL_DEBUG_TX_QUEUES(mld, + "Enabling TXQ #%d for sta mask 0x%x tid %d\n", + queue, fw_sta_mask, tid); + return queue; +} + +static int iwl_mld_add_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + int id; + + lockdep_assert_wiphy(mld->wiphy); + + /* This will alse send the SCD_QUEUE_CONFIG_CMD */ + id = iwl_mld_allocate_txq(mld, txq); + if (id < 0) + return id; + + mld_txq->fw_id = id; + mld_txq->status.allocated = true; + + rcu_assign_pointer(mld->fw_id_to_txq[id], txq); + + return 0; +} + +void iwl_mld_add_txq_list(struct iwl_mld *mld) +{ + lockdep_assert_wiphy(mld->wiphy); + + while (!list_empty(&mld->txqs_to_add)) { + struct ieee80211_txq *txq; + struct iwl_mld_txq *mld_txq = + list_first_entry(&mld->txqs_to_add, struct iwl_mld_txq, + list); + int failed; + + txq = container_of((void *)mld_txq, struct ieee80211_txq, + drv_priv); + + failed = iwl_mld_add_txq(mld, txq); + + local_bh_disable(); + spin_lock(&mld->add_txqs_lock); + list_del_init(&mld_txq->list); + spin_unlock(&mld->add_txqs_lock); + /* If the queue allocation failed, we can't transmit. Leave the + * frames on the txq, maybe the attempt to allocate the queue + * will succeed. + */ + if (!failed) + iwl_mld_tx_from_txq(mld, txq); + local_bh_enable(); + } +} + +void iwl_mld_add_txqs_wk(struct wiphy *wiphy, struct wiphy_work *wk) +{ + struct iwl_mld *mld = container_of(wk, struct iwl_mld, + add_txqs_wk); + + /* will reschedule to run after restart */ + if (mld->fw_status.in_hw_restart) + return; + + iwl_mld_add_txq_list(mld); +} + +void +iwl_mld_free_txq(struct iwl_mld *mld, u32 fw_sta_mask, u32 tid, u32 queue_id) +{ + struct iwl_scd_queue_cfg_cmd remove_cmd = { + .operation = cpu_to_le32(IWL_SCD_QUEUE_REMOVE), + .u.remove.tid = cpu_to_le32(tid), + .u.remove.sta_mask = cpu_to_le32(fw_sta_mask), + }; + + iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD), + &remove_cmd); + + iwl_trans_txq_free(mld->trans, queue_id); +} + +void iwl_mld_remove_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + u32 sta_msk, tid; + + lockdep_assert_wiphy(mld->wiphy); + + spin_lock_bh(&mld->add_txqs_lock); + if (!list_empty(&mld_txq->list)) + list_del_init(&mld_txq->list); + spin_unlock_bh(&mld->add_txqs_lock); + + if (!mld_txq->status.allocated || + WARN_ON(mld_txq->fw_id >= ARRAY_SIZE(mld->fw_id_to_txq))) + return; + + sta_msk = iwl_mld_fw_sta_id_mask(mld, txq->sta); + + tid = txq->tid == IEEE80211_NUM_TIDS ? IWL_MGMT_TID : + txq->tid; + + iwl_mld_free_txq(mld, sta_msk, tid, mld_txq->fw_id); + + RCU_INIT_POINTER(mld->fw_id_to_txq[mld_txq->fw_id], NULL); + mld_txq->status.allocated = false; +} + +#define OPT_HDR(type, skb, off) \ + (type *)(skb_network_header(skb) + (off)) + +static __le32 +iwl_mld_get_offload_assist(struct sk_buff *skb, bool amsdu) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + u16 mh_len = ieee80211_hdrlen(hdr->frame_control); + u16 offload_assist = 0; +#if IS_ENABLED(CONFIG_INET) + u8 protocol = 0; + + /* Do not compute checksum if already computed */ + if (skb->ip_summed != CHECKSUM_PARTIAL) + goto out; + + /* We do not expect to be requested to csum stuff we do not support */ + + /* TBD: do we also need to check + * !(mvm->hw->netdev_features & IWL_TX_CSUM_NETIF_FLAGS) now that all + * the devices we support has this flags? + */ + if (WARN_ONCE(skb->protocol != htons(ETH_P_IP) && + skb->protocol != htons(ETH_P_IPV6), + "No support for requested checksum\n")) { + skb_checksum_help(skb); + goto out; + } + + if (skb->protocol == htons(ETH_P_IP)) { + protocol = ip_hdr(skb)->protocol; + } else { +#if IS_ENABLED(CONFIG_IPV6) + struct ipv6hdr *ipv6h = + (struct ipv6hdr *)skb_network_header(skb); + unsigned int off = sizeof(*ipv6h); + + protocol = ipv6h->nexthdr; + while (protocol != NEXTHDR_NONE && ipv6_ext_hdr(protocol)) { + struct ipv6_opt_hdr *hp; + + /* only supported extension headers */ + if (protocol != NEXTHDR_ROUTING && + protocol != NEXTHDR_HOP && + protocol != NEXTHDR_DEST) { + skb_checksum_help(skb); + goto out; + } + + hp = OPT_HDR(struct ipv6_opt_hdr, skb, off); + protocol = hp->nexthdr; + off += ipv6_optlen(hp); + } + /* if we get here - protocol now should be TCP/UDP */ +#endif + } + + if (protocol != IPPROTO_TCP && protocol != IPPROTO_UDP) { + WARN_ON_ONCE(1); + skb_checksum_help(skb); + goto out; + } + + /* enable L4 csum */ + offload_assist |= BIT(TX_CMD_OFFLD_L4_EN); + + /* Set offset to IP header (snap). + * We don't support tunneling so no need to take care of inner header. + * Size is in words. + */ + offload_assist |= (4 << TX_CMD_OFFLD_IP_HDR); + + /* Do IPv4 csum for AMSDU only (no IP csum for Ipv6) */ + if (skb->protocol == htons(ETH_P_IP) && amsdu) { + ip_hdr(skb)->check = 0; + offload_assist |= BIT(TX_CMD_OFFLD_L3_EN); + } + + /* reset UDP/TCP header csum */ + if (protocol == IPPROTO_TCP) + tcp_hdr(skb)->check = 0; + else + udp_hdr(skb)->check = 0; + +out: +#endif + mh_len /= 2; + offload_assist |= mh_len << TX_CMD_OFFLD_MH_SIZE; + + if (amsdu) + offload_assist |= BIT(TX_CMD_OFFLD_AMSDU); + else if (ieee80211_hdrlen(hdr->frame_control) % 4) + /* padding is inserted later in transport */ + offload_assist |= BIT(TX_CMD_OFFLD_PAD); + + return cpu_to_le32(offload_assist); +} + +static void iwl_mld_get_basic_rates_and_band(struct iwl_mld *mld, + struct ieee80211_vif *vif, + struct ieee80211_tx_info *info, + unsigned long *basic_rates, + u8 *band) +{ + u32 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + *basic_rates = vif->bss_conf.basic_rates; + *band = info->band; + + if (link_id == IEEE80211_LINK_UNSPECIFIED && + ieee80211_vif_is_mld(vif)) { + /* shouldn't do this when >1 link is active */ + WARN_ON(hweight16(vif->active_links) != 1); + link_id = __ffs(vif->active_links); + } + + if (link_id < IEEE80211_LINK_UNSPECIFIED) { + struct ieee80211_bss_conf *link_conf; + + rcu_read_lock(); + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (link_conf) { + *basic_rates = link_conf->basic_rates; + if (link_conf->chanreq.oper.chan) + *band = link_conf->chanreq.oper.chan->band; + } + rcu_read_unlock(); + } +} + +u8 iwl_mld_get_lowest_rate(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif) +{ + struct ieee80211_supported_band *sband; + u16 lowest_cck = IWL_RATE_COUNT, lowest_ofdm = IWL_RATE_COUNT; + unsigned long basic_rates; + u8 band, rate; + u32 i; + + iwl_mld_get_basic_rates_and_band(mld, vif, info, &basic_rates, &band); + + sband = mld->hw->wiphy->bands[band]; + for_each_set_bit(i, &basic_rates, BITS_PER_LONG) { + u16 hw = sband->bitrates[i].hw_value; + + if (hw >= IWL_FIRST_OFDM_RATE) { + if (lowest_ofdm > hw) + lowest_ofdm = hw; + } else if (lowest_cck > hw) { + lowest_cck = hw; + } + } + + if (band == NL80211_BAND_2GHZ && !vif->p2p && + vif->type != NL80211_IFTYPE_P2P_DEVICE && + !(info->flags & IEEE80211_TX_CTL_NO_CCK_RATE)) { + if (lowest_cck != IWL_RATE_COUNT) + rate = lowest_cck; + else if (lowest_ofdm != IWL_RATE_COUNT) + rate = lowest_ofdm; + else + rate = IWL_FIRST_CCK_RATE; + } else if (lowest_ofdm != IWL_RATE_COUNT) { + rate = lowest_ofdm; + } else { + rate = IWL_FIRST_OFDM_RATE; + } + + return rate; +} + +static u32 iwl_mld_mac80211_rate_idx_to_fw(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + int rate_idx) +{ + u32 rate_flags = 0; + u8 rate_plcp; + + /* if the rate isn't a well known legacy rate, take the lowest one */ + if (rate_idx < 0 || rate_idx >= IWL_RATE_COUNT_LEGACY) + rate_idx = iwl_mld_get_lowest_rate(mld, info, + info->control.vif); + + WARN_ON_ONCE(rate_idx < 0); + + /* Set CCK or OFDM flag */ + if (rate_idx <= IWL_LAST_CCK_RATE) + rate_flags |= RATE_MCS_MOD_TYPE_CCK; + else + rate_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM; + + /* Legacy rates are indexed: + * 0 - 3 for CCK and 0 - 7 for OFDM + */ + rate_plcp = (rate_idx >= IWL_FIRST_OFDM_RATE ? + rate_idx - IWL_FIRST_OFDM_RATE : rate_idx); + + return (u32)rate_plcp | rate_flags; +} + +static u32 iwl_mld_get_tx_ant(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) +{ + if (sta && ieee80211_is_data(fc)) { + struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta); + + return BIT(mld_sta->data_tx_ant) << RATE_MCS_ANT_POS; + } + + return BIT(mld->mgmt_tx_ant) << RATE_MCS_ANT_POS; +} + +static u32 iwl_mld_get_inject_tx_rate(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, + __le16 fc) +{ + struct ieee80211_tx_rate *rate = &info->control.rates[0]; + u32 result; + + if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { + u8 mcs = ieee80211_rate_get_vht_mcs(rate); + u8 nss = ieee80211_rate_get_vht_nss(rate); + + result = RATE_MCS_MOD_TYPE_VHT; + result |= u32_encode_bits(mcs, RATE_MCS_CODE_MSK); + result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); + + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + result |= RATE_MCS_SGI_MSK; + + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + result |= RATE_MCS_CHAN_WIDTH_40; + else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) + result |= RATE_MCS_CHAN_WIDTH_80; + else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) + result |= RATE_MCS_CHAN_WIDTH_160; + } else if (rate->flags & IEEE80211_TX_RC_MCS) { + /* only MCS 0-15 are supported */ + u8 mcs = rate->idx & 7; + u8 nss = rate->idx > 7; + + result = RATE_MCS_MOD_TYPE_HT; + result |= u32_encode_bits(mcs, RATE_MCS_CODE_MSK); + result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); + + if (rate->flags & IEEE80211_TX_RC_SHORT_GI) + result |= RATE_MCS_SGI_MSK; + if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) + result |= RATE_MCS_CHAN_WIDTH_40; + if (info->flags & IEEE80211_TX_CTL_LDPC) + result |= RATE_MCS_LDPC_MSK; + if (u32_get_bits(info->flags, IEEE80211_TX_CTL_STBC)) + result |= RATE_MCS_STBC_MSK; + } else { + result = iwl_mld_mac80211_rate_idx_to_fw(mld, info, rate->idx); + } + + if (info->control.antennas) + result |= u32_encode_bits(info->control.antennas, + RATE_MCS_ANT_AB_MSK); + else + result |= iwl_mld_get_tx_ant(mld, info, sta, fc); + + return result; +} + +static __le32 iwl_mld_get_tx_rate_n_flags(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) +{ + u32 rate; + + if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) + rate = iwl_mld_get_inject_tx_rate(mld, info, sta, fc); + else + rate = iwl_mld_mac80211_rate_idx_to_fw(mld, info, -1) | + iwl_mld_get_tx_ant(mld, info, sta, fc); + + return iwl_v3_rate_to_v2_v3(rate, mld->fw_rates_ver_3); +} + +static void +iwl_mld_fill_tx_cmd_hdr(struct iwl_tx_cmd *tx_cmd, + struct sk_buff *skb, bool amsdu) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_vif *vif; + + /* Copy MAC header from skb into command buffer */ + memcpy(tx_cmd->hdr, hdr, ieee80211_hdrlen(hdr->frame_control)); + + if (!amsdu || !skb_is_gso(skb)) + return; + + /* As described in IEEE sta 802.11-2020, table 9-30 (Address + * field contents), A-MSDU address 3 should contain the BSSID + * address. + * + * In TSO, the skb header address 3 contains the original address 3 to + * correctly create all the A-MSDU subframes headers from it. + * Override now the address 3 in the command header with the BSSID. + * + * Note: we fill in the MLD address, but the firmware will do the + * necessary translation to link address after encryption. + */ + vif = info->control.vif; + switch (vif->type) { + case NL80211_IFTYPE_STATION: + ether_addr_copy(tx_cmd->hdr->addr3, vif->cfg.ap_addr); + break; + case NL80211_IFTYPE_AP: + ether_addr_copy(tx_cmd->hdr->addr3, vif->addr); + break; + default: + break; + } +} + +static void +iwl_mld_fill_tx_cmd(struct iwl_mld *mld, struct sk_buff *skb, + struct iwl_device_tx_cmd *dev_tx_cmd, + struct ieee80211_sta *sta) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + struct iwl_mld_sta *mld_sta = sta ? iwl_mld_sta_from_mac80211(sta) : + NULL; + struct iwl_tx_cmd *tx_cmd; + bool amsdu = ieee80211_is_data_qos(hdr->frame_control) && + (*ieee80211_get_qos_ctl(hdr) & + IEEE80211_QOS_CTL_A_MSDU_PRESENT); + __le32 rate_n_flags = 0; + u16 flags = 0; + + dev_tx_cmd->hdr.cmd = TX_CMD; + + if (!info->control.hw_key) + flags |= IWL_TX_FLAGS_ENCRYPT_DIS; + + /* For data and mgmt packets rate info comes from the fw. + * Only set rate/antenna for injected frames with fixed rate, or + * when no sta is given. + */ + if (unlikely(!sta || + info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) { + flags |= IWL_TX_FLAGS_CMD_RATE; + rate_n_flags = iwl_mld_get_tx_rate_n_flags(mld, info, sta, + hdr->frame_control); + } else if (!ieee80211_is_data(hdr->frame_control) || + (mld_sta && + mld_sta->sta_state < IEEE80211_STA_AUTHORIZED)) { + /* These are important frames */ + flags |= IWL_TX_FLAGS_HIGH_PRI; + } + + tx_cmd = (void *)dev_tx_cmd->payload; + + iwl_mld_fill_tx_cmd_hdr(tx_cmd, skb, amsdu); + + tx_cmd->offload_assist = iwl_mld_get_offload_assist(skb, amsdu); + + /* Total # bytes to be transmitted */ + tx_cmd->len = cpu_to_le16((u16)skb->len); + + tx_cmd->flags = cpu_to_le16(flags); + + tx_cmd->rate_n_flags = rate_n_flags; +} + +/* Caller of this need to check that info->control.vif is not NULL */ +static struct iwl_mld_link * +iwl_mld_get_link_from_tx_info(struct ieee80211_tx_info *info) +{ + struct iwl_mld_vif *mld_vif = + iwl_mld_vif_from_mac80211(info->control.vif); + u32 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + if (link_id == IEEE80211_LINK_UNSPECIFIED) { + if (info->control.vif->active_links) + link_id = ffs(info->control.vif->active_links) - 1; + else + link_id = 0; + } + + return rcu_dereference(mld_vif->link[link_id]); +} + +static int +iwl_mld_get_tx_queue_id(struct iwl_mld *mld, struct ieee80211_txq *txq, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + __le16 fc = hdr->frame_control; + struct iwl_mld_vif *mld_vif; + struct iwl_mld_link *link; + + if (txq && txq->sta) + return iwl_mld_txq_from_mac80211(txq)->fw_id; + + if (!info->control.vif) + return IWL_MLD_INVALID_QUEUE; + + switch (info->control.vif->type) { + case NL80211_IFTYPE_AP: + case NL80211_IFTYPE_ADHOC: + link = iwl_mld_get_link_from_tx_info(info); + + if (WARN_ON(!link)) + break; + + /* ucast disassociate/deauth frames without a station might + * happen, especially with reason 7 ("Class 3 frame received + * from nonassociated STA"). + */ + if (ieee80211_is_mgmt(fc) && + (!ieee80211_is_bufferable_mmpdu(skb) || + ieee80211_is_deauth(fc) || ieee80211_is_disassoc(fc))) + return link->bcast_sta.queue_id; + + if (is_multicast_ether_addr(hdr->addr1) && + !ieee80211_has_order(fc)) + return link->mcast_sta.queue_id; + + WARN_ONCE(info->control.vif->type != NL80211_IFTYPE_ADHOC, + "Couldn't find a TXQ. fc=0x%02x", le16_to_cpu(fc)); + return link->bcast_sta.queue_id; + case NL80211_IFTYPE_P2P_DEVICE: + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + + if (mld_vif->roc_activity != ROC_ACTIVITY_P2P_DISC && + mld_vif->roc_activity != ROC_ACTIVITY_P2P_NEG) { + IWL_DEBUG_DROP(mld, + "Drop tx outside ROC with activity %d\n", + mld_vif->roc_activity); + return IWL_MLD_INVALID_DROP_TX; + } + + WARN_ON(!ieee80211_is_mgmt(fc)); + + return mld_vif->aux_sta.queue_id; + case NL80211_IFTYPE_MONITOR: + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + return mld_vif->deflink.mon_sta.queue_id; + case NL80211_IFTYPE_STATION: + mld_vif = iwl_mld_vif_from_mac80211(info->control.vif); + + if (!(info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) { + IWL_DEBUG_DROP(mld, "Drop tx not off-channel\n"); + return IWL_MLD_INVALID_DROP_TX; + } + + if (mld_vif->roc_activity != ROC_ACTIVITY_HOTSPOT) { + IWL_DEBUG_DROP(mld, "Drop tx outside ROC\n"); + return IWL_MLD_INVALID_DROP_TX; + } + + WARN_ON(!ieee80211_is_mgmt(fc)); + return mld_vif->aux_sta.queue_id; + default: + WARN_ONCE(1, "Unsupported vif type\n"); + break; + } + + return IWL_MLD_INVALID_QUEUE; +} + +static void iwl_mld_probe_resp_set_noa(struct iwl_mld *mld, + struct sk_buff *skb) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct iwl_mld_link *mld_link = + &iwl_mld_vif_from_mac80211(info->control.vif)->deflink; + struct iwl_probe_resp_data *resp_data; + u8 *pos; + + if (!info->control.vif->p2p) + return; + + rcu_read_lock(); + + resp_data = rcu_dereference(mld_link->probe_resp_data); + if (!resp_data) + goto out; + + if (!resp_data->notif.noa_active) + goto out; + + if (skb_tailroom(skb) < resp_data->noa_len) { + if (pskb_expand_head(skb, 0, resp_data->noa_len, GFP_ATOMIC)) { + IWL_ERR(mld, + "Failed to reallocate probe resp\n"); + goto out; + } + } + + pos = skb_put(skb, resp_data->noa_len); + + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + /* Set length of IE body (not including ID and length itself) */ + *pos++ = resp_data->noa_len - 2; + *pos++ = (WLAN_OUI_WFA >> 16) & 0xff; + *pos++ = (WLAN_OUI_WFA >> 8) & 0xff; + *pos++ = WLAN_OUI_WFA & 0xff; + *pos++ = WLAN_OUI_TYPE_WFA_P2P; + + memcpy(pos, &resp_data->notif.noa_attr, + resp_data->noa_len - sizeof(struct ieee80211_vendor_ie)); + +out: + rcu_read_unlock(); +} + +/* This function must be called with BHs disabled */ +static int iwl_mld_tx_mpdu(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_sta *sta = txq ? txq->sta : NULL; + struct iwl_device_tx_cmd *dev_tx_cmd; + int queue = iwl_mld_get_tx_queue_id(mld, txq, skb); + u8 tid = IWL_MAX_TID_COUNT; + + if (WARN_ONCE(queue == IWL_MLD_INVALID_QUEUE, "Invalid TX Queue id") || + queue == IWL_MLD_INVALID_DROP_TX) + return -1; + + if (unlikely(ieee80211_is_any_nullfunc(hdr->frame_control))) + return -1; + + dev_tx_cmd = iwl_trans_alloc_tx_cmd(mld->trans); + if (unlikely(!dev_tx_cmd)) + return -1; + + if (unlikely(ieee80211_is_probe_resp(hdr->frame_control))) { + if (IWL_MLD_NON_TRANSMITTING_AP) + return -1; + + iwl_mld_probe_resp_set_noa(mld, skb); + } + + iwl_mld_fill_tx_cmd(mld, skb, dev_tx_cmd, sta); + + if (ieee80211_is_data(hdr->frame_control)) { + if (ieee80211_is_data_qos(hdr->frame_control)) + tid = ieee80211_get_tid(hdr); + else + tid = IWL_TID_NON_QOS; + } + + IWL_DEBUG_TX(mld, "TX TID:%d from Q:%d len %d\n", + tid, queue, skb->len); + + /* From now on, we cannot access info->control */ + memset(&info->status, 0, sizeof(info->status)); + memset(info->driver_data, 0, sizeof(info->driver_data)); + + info->driver_data[1] = dev_tx_cmd; + + if (iwl_trans_tx(mld->trans, skb, dev_tx_cmd, queue)) + goto err; + + /* Update low-latency counter when a packet is queued instead + * of after TX, it makes sense for early low-latency detection + */ + if (sta) + iwl_mld_low_latency_update_counters(mld, hdr, sta, 0); + + return 0; + +err: + iwl_trans_free_tx_cmd(mld->trans, dev_tx_cmd); + IWL_DEBUG_TX(mld, "TX from Q:%d dropped\n", queue); + return -1; +} + +#ifdef CONFIG_INET + +/* This function handles the segmentation of a large TSO packet into multiple + * MPDUs, ensuring that the resulting segments conform to AMSDU limits and + * constraints. + */ +static int iwl_mld_tx_tso_segment(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_sta *sta, + struct sk_buff_head *mpdus_skbs) +{ + struct ieee80211_hdr *hdr = (void *)skb->data; + netdev_features_t netdev_flags = NETIF_F_CSUM_MASK | NETIF_F_SG; + unsigned int mss = skb_shinfo(skb)->gso_size; + unsigned int num_subframes, tcp_payload_len, subf_len; + u16 snap_ip_tcp, pad, max_tid_amsdu_len; + u8 tid; + + snap_ip_tcp = 8 + skb_network_header_len(skb) + tcp_hdrlen(skb); + + if (!ieee80211_is_data_qos(hdr->frame_control) || + !sta->cur->max_rc_amsdu_len) + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + + /* Do not build AMSDU for IPv6 with extension headers. + * Ask stack to segment and checksum the generated MPDUs for us. + */ + if (skb->protocol == htons(ETH_P_IPV6) && + ((struct ipv6hdr *)skb_network_header(skb))->nexthdr != + IPPROTO_TCP) { + netdev_flags &= ~NETIF_F_CSUM_MASK; + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + } + + tid = ieee80211_get_tid(hdr); + if (WARN_ON_ONCE(tid >= IWL_MAX_TID_COUNT)) + return -EINVAL; + + max_tid_amsdu_len = sta->cur->max_tid_amsdu_len[tid]; + if (!max_tid_amsdu_len) + return iwl_tx_tso_segment(skb, 1, netdev_flags, mpdus_skbs); + + /* Sub frame header + SNAP + IP header + TCP header + MSS */ + subf_len = sizeof(struct ethhdr) + snap_ip_tcp + mss; + pad = (4 - subf_len) & 0x3; + + /* If we have N subframes in the A-MSDU, then the A-MSDU's size is + * N * subf_len + (N - 1) * pad. + */ + num_subframes = (max_tid_amsdu_len + pad) / (subf_len + pad); + + if (sta->max_amsdu_subframes && + num_subframes > sta->max_amsdu_subframes) + num_subframes = sta->max_amsdu_subframes; + + tcp_payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - + tcp_hdrlen(skb) + skb->data_len; + + /* Make sure we have enough TBs for the A-MSDU: + * 2 for each subframe + * 1 more for each fragment + * 1 more for the potential data in the header + */ + if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) > + mld->trans->info.max_skb_frags) + num_subframes = 1; + + if (num_subframes > 1) + *ieee80211_get_qos_ctl(hdr) |= IEEE80211_QOS_CTL_A_MSDU_PRESENT; + + /* This skb fits in one single A-MSDU */ + if (tcp_payload_len <= num_subframes * mss) { + __skb_queue_tail(mpdus_skbs, skb); + return 0; + } + + /* Trick the segmentation function to make it create SKBs that can fit + * into one A-MSDU. + */ + return iwl_tx_tso_segment(skb, num_subframes, netdev_flags, mpdus_skbs); +} + +/* Manages TSO (TCP Segmentation Offload) packet transmission by segmenting + * large packets when necessary and transmitting each segment as MPDU. + */ +static int iwl_mld_tx_tso(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq) +{ + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct sk_buff *orig_skb = skb; + struct sk_buff_head mpdus_skbs; + unsigned int payload_len; + int ret; + + if (WARN_ON(!txq || !txq->sta)) + return -1; + + payload_len = skb_tail_pointer(skb) - skb_transport_header(skb) - + tcp_hdrlen(skb) + skb->data_len; + + if (payload_len <= skb_shinfo(skb)->gso_size) + return iwl_mld_tx_mpdu(mld, skb, txq); + + if (!info->control.vif) + return -1; + + __skb_queue_head_init(&mpdus_skbs); + + ret = iwl_mld_tx_tso_segment(mld, skb, txq->sta, &mpdus_skbs); + if (ret) + return ret; + + WARN_ON(skb_queue_empty(&mpdus_skbs)); + + while (!skb_queue_empty(&mpdus_skbs)) { + skb = __skb_dequeue(&mpdus_skbs); + + ret = iwl_mld_tx_mpdu(mld, skb, txq); + if (!ret) + continue; + + /* Free skbs created as part of TSO logic that have not yet + * been dequeued + */ + __skb_queue_purge(&mpdus_skbs); + + /* skb here is not necessarily same as skb that entered + * this method, so free it explicitly. + */ + if (skb == orig_skb) + ieee80211_free_txskb(mld->hw, skb); + else + kfree_skb(skb); + + /* there was error, but we consumed skb one way or + * another, so return 0 + */ + return 0; + } + + return 0; +} +#else +static int iwl_mld_tx_tso(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq) +{ + /* Impossible to get TSO without CONFIG_INET */ + WARN_ON(1); + + return -1; +} +#endif /* CONFIG_INET */ + +void iwl_mld_tx_skb(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq) +{ + if (skb_is_gso(skb)) { + if (!iwl_mld_tx_tso(mld, skb, txq)) + return; + goto err; + } + + if (likely(!iwl_mld_tx_mpdu(mld, skb, txq))) + return; + +err: + ieee80211_free_txskb(mld->hw, skb); +} + +void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + struct sk_buff *skb = NULL; + u8 zero_addr[ETH_ALEN] = {}; + + /* + * No need for threads to be pending here, they can leave the first + * taker all the work. + * + * mld_txq->tx_request logic: + * + * If 0, no one is currently TXing, set to 1 to indicate current thread + * will now start TX and other threads should quit. + * + * If 1, another thread is currently TXing, set to 2 to indicate to + * that thread that there was another request. Since that request may + * have raced with the check whether the queue is empty, the TXing + * thread should check the queue's status one more time before leaving. + * This check is done in order to not leave any TX hanging in the queue + * until the next TX invocation (which may not even happen). + * + * If 2, another thread is currently TXing, and it will already double + * check the queue, so do nothing. + */ + if (atomic_fetch_add_unless(&mld_txq->tx_request, 1, 2)) + return; + + rcu_read_lock(); + do { + while (likely(!mld_txq->status.stop_full) && + (skb = ieee80211_tx_dequeue(mld->hw, txq))) + iwl_mld_tx_skb(mld, skb, txq); + } while (atomic_dec_return(&mld_txq->tx_request)); + + IWL_DEBUG_TX(mld, "TXQ of sta %pM tid %d is now empty\n", + txq->sta ? txq->sta->addr : zero_addr, txq->tid); + + rcu_read_unlock(); +} + +static void iwl_mld_hwrate_to_tx_rate(struct iwl_mld *mld, + __le32 rate_n_flags_fw, + struct ieee80211_tx_info *info) +{ + enum nl80211_band band = info->band; + struct ieee80211_tx_rate *tx_rate = &info->status.rates[0]; + u32 rate_n_flags = iwl_v3_rate_from_v2_v3(rate_n_flags_fw, + mld->fw_rates_ver_3); + u32 sgi = rate_n_flags & RATE_MCS_SGI_MSK; + u32 chan_width = rate_n_flags & RATE_MCS_CHAN_WIDTH_MSK; + u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; + + if (sgi) + tx_rate->flags |= IEEE80211_TX_RC_SHORT_GI; + + switch (chan_width) { + case RATE_MCS_CHAN_WIDTH_20: + break; + case RATE_MCS_CHAN_WIDTH_40: + tx_rate->flags |= IEEE80211_TX_RC_40_MHZ_WIDTH; + break; + case RATE_MCS_CHAN_WIDTH_80: + tx_rate->flags |= IEEE80211_TX_RC_80_MHZ_WIDTH; + break; + case RATE_MCS_CHAN_WIDTH_160: + tx_rate->flags |= IEEE80211_TX_RC_160_MHZ_WIDTH; + break; + default: + break; + } + + switch (format) { + case RATE_MCS_MOD_TYPE_HT: + tx_rate->flags |= IEEE80211_TX_RC_MCS; + tx_rate->idx = RATE_HT_MCS_INDEX(rate_n_flags); + break; + case RATE_MCS_MOD_TYPE_VHT: + ieee80211_rate_set_vht(tx_rate, + rate_n_flags & RATE_MCS_CODE_MSK, + u32_get_bits(rate_n_flags, + RATE_MCS_NSS_MSK) + 1); + tx_rate->flags |= IEEE80211_TX_RC_VHT_MCS; + break; + case RATE_MCS_MOD_TYPE_HE: + case RATE_MCS_MOD_TYPE_EHT: + /* mac80211 cannot do this without ieee80211_tx_status_ext() + * but it only matters for radiotap + */ + tx_rate->idx = 0; + break; + default: + tx_rate->idx = + iwl_mld_legacy_hw_idx_to_mac80211_idx(rate_n_flags, + band); + break; + } +} + +void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_tx_resp *tx_resp = (void *)pkt->data; + int txq_id = le16_to_cpu(tx_resp->tx_queue); + struct agg_tx_status *agg_status = &tx_resp->status; + u32 status = le16_to_cpu(agg_status->status); + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + size_t notif_size = sizeof(*tx_resp) + sizeof(u32); + int sta_id = IWL_TX_RES_GET_RA(tx_resp->ra_tid); + int tid = IWL_TX_RES_GET_TID(tx_resp->ra_tid); + struct ieee80211_link_sta *link_sta; + struct iwl_mld_sta *mld_sta; + u16 ssn; + struct sk_buff_head skbs; + u8 skb_freed = 0; + bool mgmt = false; + bool tx_failure = (status & TX_STATUS_MSK) != TX_STATUS_SUCCESS; + + if (IWL_FW_CHECK(mld, tx_resp->frame_count != 1, + "Invalid tx_resp notif frame_count (%d)\n", + tx_resp->frame_count)) + return; + + /* validate the size of the variable part of the notif */ + if (IWL_FW_CHECK(mld, notif_size != pkt_len, + "Invalid tx_resp notif size (expected=%zu got=%u)\n", + notif_size, pkt_len)) + return; + + ssn = le32_to_cpup((__le32 *)agg_status + + tx_resp->frame_count) & 0xFFFF; + + __skb_queue_head_init(&skbs); + + /* we can free until ssn % q.n_bd not inclusive */ + iwl_trans_reclaim(mld->trans, txq_id, ssn, &skbs, false); + + while (!skb_queue_empty(&skbs)) { + struct sk_buff *skb = __skb_dequeue(&skbs); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + struct ieee80211_hdr *hdr = (void *)skb->data; + + skb_freed++; + + iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]); + + memset(&info->status, 0, sizeof(info->status)); + + info->flags &= ~(IEEE80211_TX_STAT_ACK | IEEE80211_TX_STAT_TX_FILTERED); + + /* inform mac80211 about what happened with the frame */ + switch (status & TX_STATUS_MSK) { + case TX_STATUS_SUCCESS: + case TX_STATUS_DIRECT_DONE: + info->flags |= IEEE80211_TX_STAT_ACK; + break; + default: + break; + } + + /* If we are freeing multiple frames, mark all the frames + * but the first one as acked, since they were acknowledged + * before + */ + if (skb_freed > 1) + info->flags |= IEEE80211_TX_STAT_ACK; + + if (tx_failure) { + enum iwl_fw_ini_time_point tp = + IWL_FW_INI_TIME_POINT_TX_FAILED; + + if (ieee80211_is_action(hdr->frame_control)) + tp = IWL_FW_INI_TIME_POINT_TX_WFD_ACTION_FRAME_FAILED; + else if (ieee80211_is_mgmt(hdr->frame_control)) + mgmt = true; + + iwl_dbg_tlv_time_point(&mld->fwrt, tp, NULL); + } + + iwl_mld_hwrate_to_tx_rate(mld, tx_resp->initial_rate, info); + + if (likely(!iwl_mld_time_sync_frame(mld, skb, hdr->addr1))) + ieee80211_tx_status_skb(mld->hw, skb); + } + + IWL_DEBUG_TX_REPLY(mld, + "TXQ %d status 0x%08x ssn=%d initial_rate 0x%x retries %d\n", + txq_id, status, ssn, le32_to_cpu(tx_resp->initial_rate), + tx_resp->failure_frame); + + if (tx_failure && mgmt) + iwl_mld_toggle_tx_ant(mld, &mld->mgmt_tx_ant); + + if (IWL_FW_CHECK(mld, sta_id >= mld->fw->ucode_capa.num_stations, + "Got invalid sta_id (%d)\n", sta_id)) + return; + + rcu_read_lock(); + + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (!link_sta) { + /* This can happen if the TX cmd was sent before pre_rcu_remove + * but the TX response was received after + */ + IWL_DEBUG_TX_REPLY(mld, + "Got valid sta_id (%d) but sta is NULL\n", + sta_id); + goto out; + } + + if (IS_ERR(link_sta)) + goto out; + + mld_sta = iwl_mld_sta_from_mac80211(link_sta->sta); + + if (tx_failure && mld_sta->sta_state < IEEE80211_STA_AUTHORIZED) + iwl_mld_toggle_tx_ant(mld, &mld_sta->data_tx_ant); + + if (tid < IWL_MAX_TID_COUNT) + iwl_mld_count_mpdu_tx(link_sta, 1); + +out: + rcu_read_unlock(); +} + +static void iwl_mld_tx_reclaim_txq(struct iwl_mld *mld, int txq, int index, + bool in_flush) +{ + struct sk_buff_head reclaimed_skbs; + + __skb_queue_head_init(&reclaimed_skbs); + + iwl_trans_reclaim(mld->trans, txq, index, &reclaimed_skbs, in_flush); + + while (!skb_queue_empty(&reclaimed_skbs)) { + struct sk_buff *skb = __skb_dequeue(&reclaimed_skbs); + struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); + + iwl_trans_free_tx_cmd(mld->trans, info->driver_data[1]); + + memset(&info->status, 0, sizeof(info->status)); + + /* Packet was transmitted successfully, failures come as single + * frames because before failing a frame the firmware transmits + * it without aggregation at least once. + */ + if (!in_flush) + info->flags |= IEEE80211_TX_STAT_ACK; + else + info->flags &= ~IEEE80211_TX_STAT_ACK; + + ieee80211_tx_status_skb(mld->hw, skb); + } +} + +int iwl_mld_flush_link_sta_txqs(struct iwl_mld *mld, u32 fw_sta_id) +{ + struct iwl_tx_path_flush_cmd_rsp *rsp; + struct iwl_tx_path_flush_cmd flush_cmd = { + .sta_id = cpu_to_le32(fw_sta_id), + .tid_mask = cpu_to_le16(0xffff), + }; + struct iwl_host_cmd cmd = { + .id = TXPATH_FLUSH, + .len = { sizeof(flush_cmd), }, + .data = { &flush_cmd, }, + .flags = CMD_WANT_SKB, + }; + int ret, num_flushed_queues; + u32 resp_len; + + IWL_DEBUG_TX_QUEUES(mld, "flush for sta id %d tid mask 0x%x\n", + fw_sta_id, 0xffff); + + ret = iwl_mld_send_cmd(mld, &cmd); + if (ret) { + IWL_ERR(mld, "Failed to send flush command (%d)\n", ret); + return ret; + } + + resp_len = iwl_rx_packet_payload_len(cmd.resp_pkt); + if (IWL_FW_CHECK(mld, resp_len != sizeof(*rsp), + "Invalid TXPATH_FLUSH response len: %d\n", + resp_len)) { + ret = -EIO; + goto free_rsp; + } + + rsp = (void *)cmd.resp_pkt->data; + + if (IWL_FW_CHECK(mld, le16_to_cpu(rsp->sta_id) != fw_sta_id, + "sta_id %d != rsp_sta_id %d\n", fw_sta_id, + le16_to_cpu(rsp->sta_id))) { + ret = -EIO; + goto free_rsp; + } + + num_flushed_queues = le16_to_cpu(rsp->num_flushed_queues); + if (IWL_FW_CHECK(mld, num_flushed_queues > IWL_TX_FLUSH_QUEUE_RSP, + "num_flushed_queues %d\n", num_flushed_queues)) { + ret = -EIO; + goto free_rsp; + } + + for (int i = 0; i < num_flushed_queues; i++) { + struct iwl_flush_queue_info *queue_info = &rsp->queues[i]; + int read_after = le16_to_cpu(queue_info->read_after_flush); + int txq_id = le16_to_cpu(queue_info->queue_num); + + if (IWL_FW_CHECK(mld, + txq_id >= ARRAY_SIZE(mld->fw_id_to_txq), + "Invalid txq id %d\n", txq_id)) + continue; + + IWL_DEBUG_TX_QUEUES(mld, + "tid %d txq_id %d read-before %d read-after %d\n", + le16_to_cpu(queue_info->tid), txq_id, + le16_to_cpu(queue_info->read_before_flush), + read_after); + + iwl_mld_tx_reclaim_txq(mld, txq_id, read_after, true); + } + +free_rsp: + iwl_free_resp(&cmd); + return ret; +} + +int iwl_mld_ensure_queue(struct iwl_mld *mld, struct ieee80211_txq *txq) +{ + struct iwl_mld_txq *mld_txq = iwl_mld_txq_from_mac80211(txq); + int ret; + + lockdep_assert_wiphy(mld->wiphy); + + if (likely(mld_txq->status.allocated)) + return 0; + + ret = iwl_mld_add_txq(mld, txq); + + spin_lock_bh(&mld->add_txqs_lock); + if (!list_empty(&mld_txq->list)) + list_del_init(&mld_txq->list); + spin_unlock_bh(&mld->add_txqs_lock); + + return ret; +} + +int iwl_mld_update_sta_txqs(struct iwl_mld *mld, + struct ieee80211_sta *sta, + u32 old_sta_mask, u32 new_sta_mask) +{ + struct iwl_scd_queue_cfg_cmd cmd = { + .operation = cpu_to_le32(IWL_SCD_QUEUE_MODIFY), + .u.modify.old_sta_mask = cpu_to_le32(old_sta_mask), + .u.modify.new_sta_mask = cpu_to_le32(new_sta_mask), + }; + + lockdep_assert_wiphy(mld->wiphy); + + for (int tid = 0; tid <= IWL_MAX_TID_COUNT; tid++) { + struct ieee80211_txq *txq = + sta->txq[tid != IWL_MAX_TID_COUNT ? + tid : IEEE80211_NUM_TIDS]; + struct iwl_mld_txq *mld_txq = + iwl_mld_txq_from_mac80211(txq); + int ret; + + if (!mld_txq->status.allocated) + continue; + + if (tid == IWL_MAX_TID_COUNT) + cmd.u.modify.tid = cpu_to_le32(IWL_MGMT_TID); + else + cmd.u.modify.tid = cpu_to_le32(tid); + + ret = iwl_mld_send_cmd_pdu(mld, + WIDE_ID(DATA_PATH_GROUP, + SCD_QUEUE_CONFIG_CMD), + &cmd); + if (ret) + return ret; + } + + return 0; +} + +void iwl_mld_handle_compressed_ba_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt) +{ + struct iwl_compressed_ba_notif *ba_res = (void *)pkt->data; + u32 pkt_len = iwl_rx_packet_payload_len(pkt); + u16 tfd_cnt = le16_to_cpu(ba_res->tfd_cnt); + u8 sta_id = ba_res->sta_id; + struct ieee80211_link_sta *link_sta; + + if (!tfd_cnt) + return; + + if (IWL_FW_CHECK(mld, struct_size(ba_res, tfd, tfd_cnt) > pkt_len, + "Short BA notif (tfd_cnt=%d, size:0x%x)\n", + tfd_cnt, pkt_len)) + return; + + IWL_DEBUG_TX_REPLY(mld, + "BA notif received from sta_id=%d, flags=0x%x, sent:%d, acked:%d\n", + sta_id, le32_to_cpu(ba_res->flags), + le16_to_cpu(ba_res->txed), + le16_to_cpu(ba_res->done)); + + for (int i = 0; i < tfd_cnt; i++) { + struct iwl_compressed_ba_tfd *ba_tfd = &ba_res->tfd[i]; + int txq_id = le16_to_cpu(ba_tfd->q_num); + int index = le16_to_cpu(ba_tfd->tfd_index); + + if (IWL_FW_CHECK(mld, + txq_id >= ARRAY_SIZE(mld->fw_id_to_txq), + "Invalid txq id %d\n", txq_id)) + continue; + + iwl_mld_tx_reclaim_txq(mld, txq_id, index, false); + } + + if (IWL_FW_CHECK(mld, sta_id >= mld->fw->ucode_capa.num_stations, + "Got invalid sta_id (%d)\n", sta_id)) + return; + + rcu_read_lock(); + + link_sta = rcu_dereference(mld->fw_id_to_link_sta[sta_id]); + if (IWL_FW_CHECK(mld, IS_ERR_OR_NULL(link_sta), + "Got valid sta_id (%d) but link_sta is NULL\n", + sta_id)) + goto out; + + iwl_mld_count_mpdu_tx(link_sta, le16_to_cpu(ba_res->txed)); +out: + rcu_read_unlock(); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mld/tx.h b/drivers/net/wireless/intel/iwlwifi/mld/tx.h new file mode 100644 index 000000000000..520f15f9d33c --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mld/tx.h @@ -0,0 +1,77 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* + * Copyright (C) 2024 Intel Corporation + */ +#ifndef __iwl_mld_tx_h__ +#define __iwl_mld_tx_h__ + +#include "mld.h" + +#define IWL_MLD_INVALID_QUEUE 0xFFFF +#define IWL_MLD_INVALID_DROP_TX 0xFFFE + +/** + * struct iwl_mld_txq - TX Queue data + * + * @fw_id: the fw id of this txq. Only valid when &status.allocated is true. + * @status: bitmap of the txq status + * @status.allocated: Indicates that the queue was allocated. + * @status.stop_full: Indicates that the queue is full and should stop TXing. + * @list: list pointer, for &mld::txqs_to_add + * @tx_request: makes sure that if there are multiple threads that want to tx + * from this txq, only one of them will do all the TXing. + * This is needed to avoid spinning the trans txq lock, which is expensive + */ +struct iwl_mld_txq { + /* Add here fields that need clean up on restart */ + struct_group(zeroed_on_hw_restart, + u16 fw_id; + struct { + u8 allocated:1; + u8 stop_full:1; + } status; + ); + struct list_head list; + atomic_t tx_request; + /* And here fields that survive a fw restart */ +}; + +static inline void iwl_mld_init_txq(struct iwl_mld_txq *mld_txq) +{ + INIT_LIST_HEAD(&mld_txq->list); + atomic_set(&mld_txq->tx_request, 0); +} + +static inline struct iwl_mld_txq * +iwl_mld_txq_from_mac80211(struct ieee80211_txq *txq) +{ + return (void *)txq->drv_priv; +} + +void iwl_mld_add_txqs_wk(struct wiphy *wiphy, struct wiphy_work *wk); +void iwl_mld_remove_txq(struct iwl_mld *mld, struct ieee80211_txq *txq); +void iwl_mld_add_txq_list(struct iwl_mld *mld); +void +iwl_mld_free_txq(struct iwl_mld *mld, u32 fw_sta_mask, u32 tid, u32 queue_id); +void iwl_mld_tx_from_txq(struct iwl_mld *mld, struct ieee80211_txq *txq); +void iwl_mld_handle_tx_resp_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +int iwl_mld_flush_link_sta_txqs(struct iwl_mld *mld, u32 fw_sta_id); +int iwl_mld_ensure_queue(struct iwl_mld *mld, struct ieee80211_txq *txq); + +int iwl_mld_update_sta_txqs(struct iwl_mld *mld, + struct ieee80211_sta *sta, + u32 old_sta_mask, u32 new_sta_mask); + +void iwl_mld_handle_compressed_ba_notif(struct iwl_mld *mld, + struct iwl_rx_packet *pkt); +void iwl_mld_toggle_tx_ant(struct iwl_mld *mld, u8 *ant); + +u8 iwl_mld_get_lowest_rate(struct iwl_mld *mld, + struct ieee80211_tx_info *info, + struct ieee80211_vif *vif); + +void iwl_mld_tx_skb(struct iwl_mld *mld, struct sk_buff *skb, + struct ieee80211_txq *txq); + +#endif /* __iwl_mld_tx_h__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c index 21641d41a958..13cdc077d8d3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/coex.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/coex.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2013-2014, 2018-2020, 2022-2024 Intel Corporation + * Copyright (C) 2013-2014, 2018-2020, 2022-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH */ #include <linux/ieee80211.h> @@ -181,7 +181,7 @@ static int iwl_mvm_bt_coex_reduced_txp(struct iwl_mvm *mvm, u8 sta_id, struct iwl_mvm_sta *mvmsta; u32 value; - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) return 0; mvmsta = iwl_mvm_sta_from_staid_protected(mvm, sta_id); @@ -569,7 +569,7 @@ static void iwl_mvm_bt_coex_notif_handle(struct iwl_mvm *mvm) mvm->hw, IEEE80211_IFACE_ITER_NORMAL, iwl_mvm_bt_notif_iterator, &data); - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { rcu_read_unlock(); return; } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c index 129b6bdf9ef9..507c03198c92 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/d3.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -300,7 +300,7 @@ static void iwl_mvm_wowlan_get_rsc_tsc_data(struct ieee80211_hw *hw, for (i = 0; i < IWL_MAX_TID_COUNT; i++) { pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i, - mvm->trans->num_rx_queues); + mvm->trans->info.num_rxqs); aes_sc[i].pn = cpu_to_le64((u64)pn[5] | ((u64)pn[4] << 8) | ((u64)pn[3] << 16) | @@ -421,7 +421,7 @@ static void iwl_mvm_wowlan_get_rsc_v5_data(struct ieee80211_hw *hw, for (i = 0; i < IWL_MAX_TID_COUNT; i++) { pn = iwl_mvm_find_max_pn(key, ptk_pn, &seq, i, - mvm->trans->num_rx_queues); + mvm->trans->info.num_rxqs); rsc[i] = cpu_to_le64((u64)pn[5] | ((u64)pn[4] << 8) | ((u64)pn[3] << 16) | @@ -1266,7 +1266,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, }; struct iwl_host_cmd d3_cfg_cmd = { .id = D3_CONFIG_CMD, - .flags = CMD_WANT_SKB | CMD_SEND_IN_D3, + .flags = CMD_WANT_SKB, .data[0] = &d3_cfg_cmd_data, .len[0] = sizeof(d3_cfg_cmd_data), }; @@ -1370,11 +1370,9 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, * recording before entering D3. In later devices the FW stops the * recording automatically. */ - if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000) + if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000) iwl_fw_dbg_stop_restart_recording(&mvm->fwrt, NULL, true); - mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; - /* must be last -- this switches firmware state */ ret = iwl_mvm_send_cmd(mvm, &d3_cfg_cmd); if (ret) @@ -1686,7 +1684,7 @@ static void iwl_mvm_set_aes_ptk_rx_seq(struct iwl_mvm *mvm, for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { int i; - for (i = 1; i < mvm->trans->num_rx_queues; i++) + for (i = 1; i < mvm->trans->info.num_rxqs; i++) memcpy(ptk_pn->q[i].pn[tid], status->ptk.aes.seq[tid].ccmp.pn, IEEE80211_CCMP_PN_LEN); @@ -2825,7 +2823,7 @@ static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, status->qos_seq_ctr[i] + 0x10; } - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { i = mvm->offload_tid; iwl_trans_set_q_ptrs(mvm->trans, mvm_ap_sta->tid_data[i].txq_id, @@ -3092,8 +3090,14 @@ static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, ieee80211_resume_disconnect(vif); } -static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm, - struct ieee80211_vif *vif) +enum rt_status { + FW_ALIVE, + FW_NEEDS_RESET, + FW_ERROR, +}; + +static enum rt_status iwl_mvm_check_rt_status(struct iwl_mvm *mvm, + struct ieee80211_vif *vif) { u32 err_id; @@ -3101,29 +3105,35 @@ static bool iwl_mvm_check_rt_status(struct iwl_mvm *mvm, if (iwl_fwrt_read_err_table(mvm->trans, mvm->trans->dbg.lmac_error_event_table[0], &err_id)) { - if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN && vif) { - struct cfg80211_wowlan_wakeup wakeup = { - .rfkill_release = true, - }; - ieee80211_report_wowlan_wakeup(vif, &wakeup, - GFP_KERNEL); + if (err_id == RF_KILL_INDICATOR_FOR_WOWLAN) { + IWL_WARN(mvm, "Rfkill was toggled during suspend\n"); + if (vif) { + struct cfg80211_wowlan_wakeup wakeup = { + .rfkill_release = true, + }; + + ieee80211_report_wowlan_wakeup(vif, &wakeup, + GFP_KERNEL); + } + + return FW_NEEDS_RESET; } - return true; + return FW_ERROR; } /* check if we have lmac2 set and check for error */ if (iwl_fwrt_read_err_table(mvm->trans, mvm->trans->dbg.lmac_error_event_table[1], NULL)) - return true; + return FW_ERROR; /* check for umac error */ if (iwl_fwrt_read_err_table(mvm->trans, mvm->trans->dbg.umac_error_event_table, NULL)) - return true; + return FW_ERROR; - return false; + return FW_ALIVE; } /* @@ -3376,7 +3386,7 @@ static bool iwl_mvm_wait_d3_notif(struct iwl_notif_wait_data *notif_wait, break; } case WIDE_ID(PROT_OFFLOAD_GROUP, D3_END_NOTIFICATION): { - struct iwl_mvm_d3_end_notif *notif = (void *)pkt->data; + struct iwl_d3_end_notif *notif = (void *)pkt->data; d3_data->d3_end_flags = __le32_to_cpu(notif->flags); d3_data->notif_received |= IWL_D3_NOTIF_D3_END_NOTIF; @@ -3395,9 +3405,9 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm, bool test) int ret; enum iwl_d3_status d3_status; struct iwl_host_cmd cmd = { - .id = D0I3_END_CMD, - .flags = CMD_WANT_SKB | CMD_SEND_IN_D3, - }; + .id = D0I3_END_CMD, + .flags = CMD_WANT_SKB, + }; bool reset = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_CNSLDTD_D3_D0_IMG); @@ -3415,7 +3425,7 @@ static int iwl_mvm_resume_firmware(struct iwl_mvm *mvm, bool test) * AX210 and above don't need the command since they have * the doorbell interrupt. */ - if (mvm->trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_22000 && + if (mvm->trans->mac_cfg->device_family <= IWL_DEVICE_FAMILY_22000 && fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_D0I3_END_FIRST)) { ret = iwl_mvm_send_cmd(mvm, &cmd); if (ret < 0) @@ -3492,6 +3502,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) bool d0i3_first = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_D0I3_END_FIRST); bool resume_notif_based = iwl_mvm_d3_resume_notif_based(mvm); + enum rt_status rt_status; bool keep = false; mutex_lock(&mvm->mutex); @@ -3515,14 +3526,19 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); - if (iwl_mvm_check_rt_status(mvm, vif)) { - IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); + rt_status = iwl_mvm_check_rt_status(mvm, vif); + if (rt_status != FW_ALIVE) { set_bit(STATUS_FW_ERROR, &mvm->trans->status); - iwl_mvm_dump_nic_error_log(mvm); - iwl_dbg_tlv_time_point(&mvm->fwrt, - IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); - iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert, - false, 0); + if (rt_status == FW_ERROR) { + IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); + iwl_mvm_dump_nic_error_log(mvm); + iwl_dbg_tlv_time_point(&mvm->fwrt, + IWL_FW_INI_TIME_POINT_FW_ASSERT, + NULL); + iwl_fw_dbg_collect_desc(&mvm->fwrt, + &iwl_dump_desc_assert, + false, 0); + } ret = 1; goto err; } @@ -3546,9 +3562,6 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) iwl_mvm_unblock_esr(mvm, vif, IWL_MVM_ESR_BLOCKED_WOWLAN); - /* after the successful handshake, we're out of D3 */ - mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; - /* when reset is required we can't send these following commands */ if (d3_data.d3_end_flags & IWL_D0I3_RESET_REQUIRE) goto query_wakeup_reasons; @@ -3621,9 +3634,6 @@ out: */ set_bit(IWL_MVM_STATUS_HW_RESTART_REQUESTED, &mvm->status); - /* regardless of what happened, we're now out of D3 */ - mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; - return 1; } @@ -3661,8 +3671,7 @@ void iwl_mvm_fast_suspend(struct iwl_mvm *mvm) set_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); WARN_ON(iwl_mvm_power_update_device(mvm)); - mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_D3; - ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, CMD_SEND_IN_D3, + ret = iwl_mvm_send_cmd_pdu(mvm, D3_CONFIG_CMD, 0, sizeof(d3_cfg_cmd_data), &d3_cfg_cmd_data); if (ret) IWL_ERR(mvm, @@ -3679,6 +3688,7 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm) .notif_expected = IWL_D3_NOTIF_D3_END_NOTIF, }; + enum rt_status rt_status; int ret; lockdep_assert_held(&mvm->mutex); @@ -3688,14 +3698,20 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm) mvm->last_reset_or_resume_time_jiffies = jiffies; iwl_fw_dbg_read_d3_debug_data(&mvm->fwrt); - if (iwl_mvm_check_rt_status(mvm, NULL)) { - IWL_ERR(mvm, "FW Error occurred during suspend. Restarting.\n"); + rt_status = iwl_mvm_check_rt_status(mvm, NULL); + if (rt_status != FW_ALIVE) { set_bit(STATUS_FW_ERROR, &mvm->trans->status); - iwl_mvm_dump_nic_error_log(mvm); - iwl_dbg_tlv_time_point(&mvm->fwrt, - IWL_FW_INI_TIME_POINT_FW_ASSERT, NULL); - iwl_fw_dbg_collect_desc(&mvm->fwrt, &iwl_dump_desc_assert, - false, 0); + if (rt_status == FW_ERROR) { + IWL_ERR(mvm, + "iwl_mvm_check_rt_status failed, device is gone during suspend\n"); + iwl_mvm_dump_nic_error_log(mvm); + iwl_dbg_tlv_time_point(&mvm->fwrt, + IWL_FW_INI_TIME_POINT_FW_ASSERT, + NULL); + iwl_fw_dbg_collect_desc(&mvm->fwrt, + &iwl_dump_desc_assert, + false, 0); + } mvm->trans->state = IWL_TRANS_NO_FW; ret = -ENODEV; @@ -3710,7 +3726,6 @@ int iwl_mvm_fast_resume(struct iwl_mvm *mvm) out: clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); - mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; mvm->fast_resume = false; return ret; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c index 83e3c1160362..86a87ea89916 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/debugfs.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2023 Intel Corporation + * Copyright (C) 2012-2014, 2018-2023, 2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -542,7 +542,7 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, size_t count, loff_t *ppos) { struct iwl_mvm *mvm = file->private_data; - struct iwl_mvm_tas_status_resp *rsp = NULL; + struct iwl_tas_status_resp *rsp = NULL; static const size_t bufsz = 1024; char *buff, *pos, *endpos; const char * const tas_dis_reason[TAS_DISABLED_REASON_MAX] = { @@ -552,6 +552,8 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, "Due To SAR Limit Less Than 6 dBm", [TAS_DISABLED_REASON_INVALID] = "N/A", + [TAS_DISABLED_DUE_TO_TABLE_SOURCE_INVALID] = + "Due to table source invalid", }; const char * const tas_current_status[TAS_DYNA_STATUS_MAX] = { [TAS_DYNA_INACTIVE] = "INACTIVE", @@ -598,23 +600,19 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, pos += scnprintf(pos, endpos - pos, "TAS Conclusion:\n"); for (i = 0; i < rsp->in_dual_radio + 1; i++) { - if (rsp->tas_status_mac[i].band != TAS_LMAC_BAND_INVALID && - rsp->tas_status_mac[i].dynamic_status & BIT(TAS_DYNA_ACTIVE)) { + if (rsp->tas_status_mac[i].dynamic_status & + BIT(TAS_DYNA_ACTIVE)) { pos += scnprintf(pos, endpos - pos, "\tON for "); switch (rsp->tas_status_mac[i].band) { - case TAS_LMAC_BAND_HB: + case PHY_BAND_5: pos += scnprintf(pos, endpos - pos, "HB\n"); break; - case TAS_LMAC_BAND_LB: + case PHY_BAND_24: pos += scnprintf(pos, endpos - pos, "LB\n"); break; - case TAS_LMAC_BAND_UHB: + case PHY_BAND_6: pos += scnprintf(pos, endpos - pos, "UHB\n"); break; - case TAS_LMAC_BAND_INVALID: - pos += scnprintf(pos, endpos - pos, - "INVALID BAND\n"); - break; default: pos += scnprintf(pos, endpos - pos, "Unsupported band (%d)\n", @@ -668,20 +666,16 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, pos += scnprintf(pos, endpos - pos, "TAS status for "); switch (rsp->tas_status_mac[i].band) { - case TAS_LMAC_BAND_HB: + case PHY_BAND_5: pos += scnprintf(pos, endpos - pos, "High band\n"); break; - case TAS_LMAC_BAND_LB: + case PHY_BAND_24: pos += scnprintf(pos, endpos - pos, "Low band\n"); break; - case TAS_LMAC_BAND_UHB: + case PHY_BAND_6: pos += scnprintf(pos, endpos - pos, "Ultra high band\n"); break; - case TAS_LMAC_BAND_INVALID: - pos += scnprintf(pos, endpos - pos, - "INVALID band\n"); - break; default: pos += scnprintf(pos, endpos - pos, "Unsupported band (%d)\n", @@ -704,11 +698,9 @@ static ssize_t iwl_dbgfs_tas_get_status_read(struct file *file, pos += scnprintf(pos, endpos - pos, "Dynamic status:\n"); dyn_status = (rsp->tas_status_mac[i].dynamic_status); - for_each_set_bit(tmp, &dyn_status, sizeof(dyn_status)) { - if (tmp >= 0 && tmp < TAS_DYNA_STATUS_MAX) - pos += scnprintf(pos, endpos - pos, - "\t%s (%d)\n", - tas_current_status[tmp], tmp); + for_each_set_bit(tmp, &dyn_status, TAS_DYNA_STATUS_MAX) { + pos += scnprintf(pos, endpos - pos, "\t%s (%d)\n", + tas_current_status[tmp], tmp); } pos += scnprintf(pos, endpos - pos, @@ -1285,7 +1277,7 @@ static ssize_t iwl_dbgfs_inject_packet_write(struct iwl_mvm *mvm, return -EIO; /* supporting only MQ RX */ - if (!mvm->trans->trans_cfg->mq_rx_supported) + if (!mvm->trans->mac_cfg->mq_rx_supported) return -EOPNOTSUPP; rxb._page = alloc_pages(GFP_ATOMIC, 0); @@ -1476,9 +1468,16 @@ static ssize_t iwl_dbgfs_fw_dbg_clear_write(struct iwl_mvm *mvm, char *buf, size_t count, loff_t *ppos) { - if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_9000) + if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_9000) return -EOPNOTSUPP; + /* + * If the firmware is not running, silently succeed since there is + * no data to clear. + */ + if (!iwl_mvm_firmware_running(mvm)) + return count; + mutex_lock(&mvm->mutex); iwl_fw_dbg_clear_monitor_buf(&mvm->fwrt); mutex_unlock(&mvm->mutex); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c index b26141c30c61..a493ef6bedc3 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-initiator.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2015-2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include <linux/etherdevice.h> #include <linux/math64.h> @@ -46,107 +46,6 @@ struct iwl_mvm_ftm_iter_data { u8 *tk; }; -int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u8 *addr, u32 cipher, u8 *tk, u32 tk_len, - u8 *hltk, u32 hltk_len) -{ - struct iwl_mvm_ftm_pasn_entry *pasn = kzalloc(sizeof(*pasn), - GFP_KERNEL); - u32 expected_tk_len; - - lockdep_assert_held(&mvm->mutex); - - if (!pasn) - return -ENOBUFS; - - iwl_mvm_ftm_remove_pasn_sta(mvm, addr); - - pasn->cipher = iwl_mvm_cipher_to_location_cipher(cipher); - - switch (pasn->cipher) { - case IWL_LOCATION_CIPHER_CCMP_128: - case IWL_LOCATION_CIPHER_GCMP_128: - expected_tk_len = WLAN_KEY_LEN_CCMP; - break; - case IWL_LOCATION_CIPHER_GCMP_256: - expected_tk_len = WLAN_KEY_LEN_GCMP_256; - break; - default: - goto out; - } - - /* - * If associated to this AP and already have security context, - * the TK is already configured for this station, so it - * shouldn't be set again here. - */ - if (vif->cfg.assoc) { - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - struct ieee80211_bss_conf *link_conf; - unsigned int link_id; - struct ieee80211_sta *sta; - u8 sta_id; - - rcu_read_lock(); - for_each_vif_active_link(vif, link_conf, link_id) { - if (memcmp(addr, link_conf->bssid, ETH_ALEN)) - continue; - - sta_id = mvmvif->link[link_id]->ap_sta_id; - sta = rcu_dereference(mvm->fw_id_to_mac_id[sta_id]); - if (!IS_ERR_OR_NULL(sta) && sta->mfp) - expected_tk_len = 0; - break; - } - rcu_read_unlock(); - } - - if (tk_len != expected_tk_len || - (hltk_len && hltk_len != sizeof(pasn->hltk))) { - IWL_ERR(mvm, "Invalid key length: tk_len=%u hltk_len=%u\n", - tk_len, hltk_len); - goto out; - } - - if (!expected_tk_len && !hltk_len) { - IWL_ERR(mvm, "TK and HLTK not set\n"); - goto out; - } - - memcpy(pasn->addr, addr, sizeof(pasn->addr)); - - if (hltk_len) { - memcpy(pasn->hltk, hltk, sizeof(pasn->hltk)); - pasn->flags |= IWL_MVM_PASN_FLAG_HAS_HLTK; - } - - if (tk && tk_len) - memcpy(pasn->tk, tk, sizeof(pasn->tk)); - - list_add_tail(&pasn->list, &mvm->ftm_initiator.pasn_list); - return 0; -out: - kfree(pasn); - return -EINVAL; -} - -void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr) -{ - struct iwl_mvm_ftm_pasn_entry *entry, *prev; - - lockdep_assert_held(&mvm->mutex); - - list_for_each_entry_safe(entry, prev, &mvm->ftm_initiator.pasn_list, - list) { - if (memcmp(entry->addr, addr, sizeof(entry->addr))) - continue; - - list_del(&entry->list); - kfree(entry); - return; - } -} - static void iwl_mvm_ftm_reset(struct iwl_mvm *mvm) { struct iwl_mvm_loc_entry *e, *t; @@ -773,7 +672,11 @@ iwl_mvm_ftm_set_secured_ranging(struct iwl_mvm *mvm, struct ieee80211_vif *vif, target.bssid = bssid; target.cipher = cipher; + target.tk = NULL; ieee80211_iter_keys(mvm->hw, vif, iter, &target); + + if (!WARN_ON(!target.tk)) + memcpy(tk, target.tk, TK_11AZ_LEN); } else { memcpy(tk, entry->tk, sizeof(entry->tk)); } @@ -949,7 +852,7 @@ static int iwl_mvm_ftm_start_v13(struct iwl_mvm *mvm, static int iwl_mvm_ftm_put_target_v10(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_pmsr_request_peer *peer, - struct iwl_tof_range_req_ap_entry_v10 *target) + struct iwl_tof_range_req_ap_entry *target) { u32 i2r_max_sts, flags; int ret; @@ -1021,7 +924,7 @@ static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct cfg80211_pmsr_request *req) { - struct iwl_tof_range_req_cmd_v14 cmd; + struct iwl_tof_range_req_cmd cmd; struct iwl_host_cmd hcmd = { .id = WIDE_ID(LOCATION_GROUP, TOF_RANGE_REQ_CMD), .dataflags[0] = IWL_HCMD_DFL_DUP, @@ -1035,7 +938,7 @@ static int iwl_mvm_ftm_start_v14(struct iwl_mvm *mvm, for (i = 0; i < cmd.num_of_ap; i++) { struct cfg80211_pmsr_request_peer *peer = &req->peers[i]; - struct iwl_tof_range_req_ap_entry_v10 *target = &cmd.ap[i]; + struct iwl_tof_range_req_ap_entry *target = &cmd.ap[i]; err = iwl_mvm_ftm_put_target_v10(mvm, vif, peer, target); if (err) @@ -1301,7 +1204,7 @@ static void iwl_mvm_debug_range_resp(struct iwl_mvm *mvm, u8 index, static void iwl_mvm_ftm_pasn_update_pn(struct iwl_mvm *mvm, - struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap) + struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap) { struct iwl_mvm_ftm_pasn_entry *entry; @@ -1339,7 +1242,7 @@ static bool iwl_mvm_ftm_resp_size_validation(u8 ver, unsigned int pkt_len) switch (ver) { case 9: case 8: - return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v8); + return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy); case 7: return pkt_len == sizeof(struct iwl_tof_range_rsp_ntfy_v7); case 6: @@ -1359,7 +1262,7 @@ void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) struct iwl_tof_range_rsp_ntfy_v5 *fw_resp_v5 = (void *)pkt->data; struct iwl_tof_range_rsp_ntfy_v6 *fw_resp_v6 = (void *)pkt->data; struct iwl_tof_range_rsp_ntfy_v7 *fw_resp_v7 = (void *)pkt->data; - struct iwl_tof_range_rsp_ntfy_v8 *fw_resp_v8 = (void *)pkt->data; + struct iwl_tof_range_rsp_ntfy *fw_resp_v8 = (void *)pkt->data; int i; bool new_api = fw_has_api(&mvm->fw->ucode_capa, IWL_UCODE_TLV_API_FTM_NEW_RANGE_REQ); @@ -1395,9 +1298,9 @@ void iwl_mvm_ftm_range_resp(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) IWL_DEBUG_INFO(mvm, "request id: %lld, num of entries: %u\n", mvm->ftm_initiator.req->cookie, num_of_aps); - for (i = 0; i < num_of_aps && i < IWL_MVM_TOF_MAX_APS; i++) { + for (i = 0; i < num_of_aps && i < IWL_TOF_MAX_APS; i++) { struct cfg80211_pmsr_result result = {}; - struct iwl_tof_range_rsp_ap_entry_ntfy_v6 *fw_ap; + struct iwl_tof_range_rsp_ap_entry_ntfy *fw_ap; int peer_idx; if (new_api) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c index e6e468e81ab3..83f6e508a094 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ftm-responder.c @@ -324,92 +324,6 @@ static void iwl_mvm_resp_del_pasn_sta(struct iwl_mvm *mvm, kfree(sta); } -int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u8 *addr, u32 cipher, u8 *tk, u32 tk_len, - u8 *hltk, u32 hltk_len) -{ - int ret; - struct iwl_mvm_pasn_sta *sta = NULL; - struct iwl_mvm_pasn_hltk_data hltk_data = { - .addr = addr, - .hltk = hltk, - }; - struct iwl_mvm_pasn_hltk_data *hltk_data_ptr = NULL; - - u8 cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, - WIDE_ID(LOCATION_GROUP, TOF_RESPONDER_DYN_CONFIG_CMD), - 2); - - lockdep_assert_held(&mvm->mutex); - - if (cmd_ver < 3) { - IWL_ERR(mvm, "Adding PASN station not supported by FW\n"); - return -EOPNOTSUPP; - } - - if ((!hltk || !hltk_len) && (!tk || !tk_len)) { - IWL_ERR(mvm, "TK and HLTK not set\n"); - return -EINVAL; - } - - if (hltk && hltk_len) { - if (!fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_SECURE_LTF_SUPPORT)) { - IWL_ERR(mvm, "No support for secure LTF measurement\n"); - return -EINVAL; - } - - hltk_data.cipher = iwl_mvm_cipher_to_location_cipher(cipher); - if (hltk_data.cipher == IWL_LOCATION_CIPHER_INVALID) { - IWL_ERR(mvm, "invalid cipher: %u\n", cipher); - return -EINVAL; - } - - hltk_data_ptr = &hltk_data; - } - - if (tk && tk_len) { - sta = kzalloc(sizeof(*sta) + tk_len, GFP_KERNEL); - if (!sta) - return -ENOBUFS; - - ret = iwl_mvm_add_pasn_sta(mvm, vif, &sta->int_sta, addr, - cipher, tk, tk_len, &sta->keyconf); - if (ret) { - kfree(sta); - return ret; - } - - memcpy(sta->addr, addr, ETH_ALEN); - list_add_tail(&sta->list, &mvm->resp_pasn_list); - } - - ret = iwl_mvm_ftm_responder_dyn_cfg_v3(mvm, vif, NULL, hltk_data_ptr); - if (ret && sta) - iwl_mvm_resp_del_pasn_sta(mvm, vif, sta); - - return ret; -} - -int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, u8 *addr) -{ - struct iwl_mvm_pasn_sta *sta, *prev; - - lockdep_assert_held(&mvm->mutex); - - list_for_each_entry_safe(sta, prev, &mvm->resp_pasn_list, list) { - if (!memcmp(sta->addr, addr, ETH_ALEN)) { - iwl_mvm_resp_del_pasn_sta(mvm, vif, sta); - return 0; - } - } - - IWL_ERR(mvm, "FTM: PASN station %pM not found\n", addr); - return -EINVAL; -} - int iwl_mvm_ftm_start_responder(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c index df49dd2e2026..819e3228462a 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/fw.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -29,8 +29,8 @@ #define MVM_UCODE_CALIB_TIMEOUT (2 * HZ) struct iwl_mvm_alive_data { + __le32 sku_id[3]; bool valid; - u32 scd_base_addr; }; static int iwl_send_tx_ant_cfg(struct iwl_mvm *mvm, u8 valid_tx_ant) @@ -57,13 +57,13 @@ static int iwl_send_rss_cfg_cmd(struct iwl_mvm *mvm) BIT(IWL_RSS_HASH_TYPE_IPV6_PAYLOAD), }; - if (mvm->trans->num_rx_queues == 1) + if (mvm->trans->info.num_rxqs == 1) return 0; /* Do not direct RSS traffic to Q 0 which is our fallback queue */ for (i = 0; i < ARRAY_SIZE(cmd.indirection_table); i++) cmd.indirection_table[i] = - 1 + (i % (mvm->trans->num_rx_queues - 1)); + 1 + (i % (mvm->trans->info.num_rxqs - 1)); netdev_rss_key_fill(cmd.secret_key, sizeof(cmd.secret_key)); return iwl_mvm_send_cmd_pdu(mvm, RSS_CONFIG_CMD, 0, sizeof(cmd), &cmd); @@ -114,7 +114,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, u32 i; - if (version == 6) { + if (version >= 6) { struct iwl_alive_ntf_v6 *palive; if (pkt_len < sizeof(*palive)) @@ -157,6 +157,17 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, } } } + + if (version >= 8) { + const struct iwl_alive_ntf *palive_v8 = + (void *)pkt->data; + + if (pkt_len < sizeof(*palive_v8)) + return false; + + IWL_DEBUG_FW(mvm, "platform id: 0x%llx\n", + palive_v8->platform_id); + } } if (version >= 5) { @@ -171,14 +182,15 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, lmac2 = &palive->lmac_data[1]; status = le16_to_cpu(palive->status); - mvm->trans->sku_id[0] = le32_to_cpu(palive->sku_id.data[0]); - mvm->trans->sku_id[1] = le32_to_cpu(palive->sku_id.data[1]); - mvm->trans->sku_id[2] = le32_to_cpu(palive->sku_id.data[2]); + BUILD_BUG_ON(sizeof(palive->sku_id.data) != + sizeof(alive_data->sku_id)); + memcpy(alive_data->sku_id, palive->sku_id.data, + sizeof(palive->sku_id.data)); IWL_DEBUG_FW(mvm, "Got sku_id: 0x0%x 0x0%x 0x0%x\n", - mvm->trans->sku_id[0], - mvm->trans->sku_id[1], - mvm->trans->sku_id[2]); + le32_to_cpu(alive_data->sku_id[0]), + le32_to_cpu(alive_data->sku_id[1]), + le32_to_cpu(alive_data->sku_id[2])); } else if (iwl_rx_packet_payload_len(pkt) == sizeof(struct iwl_alive_ntf_v4)) { struct iwl_alive_ntf_v4 *palive; @@ -221,7 +233,7 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, if (umac_error_table) { if (umac_error_table >= - mvm->trans->cfg->min_umac_error_event_table) { + mvm->trans->mac_cfg->base->min_umac_error_event_table) { iwl_fw_umac_set_alive_err_table(mvm->trans, umac_error_table); } else { @@ -233,7 +245,6 @@ static bool iwl_alive_fn(struct iwl_notif_wait_data *notif_wait, } } - alive_data->scd_base_addr = le32_to_cpu(lmac1->dbg_ptrs.scd_base_ptr); alive_data->valid = status == IWL_ALIVE_STATUS_OK; IWL_DEBUG_FW(mvm, @@ -282,7 +293,7 @@ static void iwl_mvm_print_pd_notification(struct iwl_mvm *mvm) IWL_ERR(mvm, #reg_name ": 0x%x\n", iwl_read_umac_prph(trans, reg_name)) struct iwl_trans *trans = mvm->trans; - enum iwl_device_family device_family = trans->trans_cfg->device_family; + enum iwl_device_family device_family = trans->mac_cfg->device_family; if (device_family < IWL_DEVICE_FAMILY_8000) return; @@ -304,7 +315,6 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, { struct iwl_notification_wait alive_wait; struct iwl_mvm_alive_data alive_data = {}; - const struct fw_img *fw; int ret; enum iwl_ucode_type old_type = mvm->fwrt.cur_fw_img; static const u16 alive_cmd[] = { UCODE_ALIVE_NTFY }; @@ -317,11 +327,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, iwl_fw_dbg_conf_usniffer(mvm->fw, FW_DBG_START_FROM_ALIVE) && !(fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_USNIFFER_UNIFIED))) - fw = iwl_get_ucode_image(mvm->fw, IWL_UCODE_REGULAR_USNIFFER); - else - fw = iwl_get_ucode_image(mvm->fw, ucode_type); - if (WARN_ON(!fw)) - return -EINVAL; + ucode_type = IWL_UCODE_REGULAR_USNIFFER; iwl_fw_set_current_image(&mvm->fwrt, ucode_type); clear_bit(IWL_MVM_STATUS_FIRMWARE_RUNNING, &mvm->status); @@ -334,7 +340,8 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, * For the unified firmware case, the ucode_type is not * INIT, but we still need to run it. */ - ret = iwl_trans_start_fw(mvm->trans, fw, run_in_rfkill); + ret = iwl_trans_start_fw(mvm->trans, mvm->fw, ucode_type, + run_in_rfkill); if (ret) { iwl_fw_set_current_image(&mvm->fwrt, old_type); iwl_remove_notification(&mvm->notif_wait, &alive_wait); @@ -348,7 +355,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, ret = iwl_wait_notification(&mvm->notif_wait, &alive_wait, MVM_UCODE_ALIVE_TIMEOUT); - if (mvm->trans->trans_cfg->device_family == + if (mvm->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { /* print these registers regardless of alive fail/success */ IWL_INFO(mvm, "WFPM_UMAC_PD_NOTIFICATION: 0x%x\n", @@ -365,14 +372,14 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, struct iwl_trans *trans = mvm->trans; /* SecBoot info */ - if (trans->trans_cfg->device_family >= + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { IWL_ERR(mvm, "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", iwl_read_umac_prph(trans, UMAG_SB_CPU_1_STATUS), iwl_read_umac_prph(trans, UMAG_SB_CPU_2_STATUS)); - } else if (trans->trans_cfg->device_family >= + } else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000) { IWL_ERR(mvm, "SecBoot CPU1 Status: 0x%x, CPU2 Status: 0x%x\n", @@ -383,7 +390,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, iwl_mvm_print_pd_notification(mvm); /* LMAC/UMAC PC info */ - if (trans->trans_cfg->device_family >= + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { pc_data = trans->dbg.pc_data; for (count = 0; count < trans->dbg.num_pc; @@ -391,7 +398,7 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, IWL_ERR(mvm, "%s: 0x%x\n", pc_data->pc_name, pc_data->pc_address); - } else if (trans->trans_cfg->device_family >= + } else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_9000) { IWL_ERR(mvm, "UMAC PC: 0x%x\n", iwl_read_umac_prph(trans, @@ -422,16 +429,16 @@ static int iwl_mvm_load_ucode_wait_alive(struct iwl_mvm *mvm, /* if reached this point, Alive notification was received */ iwl_mei_alive_notif(true); + iwl_trans_fw_alive(mvm->trans); + ret = iwl_pnvm_load(mvm->trans, &mvm->notif_wait, - &mvm->fw->ucode_capa); + &mvm->fw->ucode_capa, alive_data.sku_id); if (ret) { IWL_ERR(mvm, "Timeout waiting for PNVM load!\n"); iwl_fw_set_current_image(&mvm->fwrt, old_type); return ret; } - iwl_trans_fw_alive(mvm->trans, alive_data.scd_base_addr); - /* * Note: all the queues are enabled as part of the interface * initialization, but in firmware restart scenarios they @@ -473,7 +480,7 @@ static void iwl_mvm_phy_filter_init(struct iwl_mvm *mvm, struct iwl_phy_specific_cfg *phy_filters) { #ifdef CONFIG_ACPI - *phy_filters = mvm->phy_filters; + *phy_filters = mvm->fwrt.phy_filters; #endif /* CONFIG_ACPI */ } @@ -490,7 +497,7 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) .dataflags[0] = IWL_HCMD_DFL_NOCOPY, }; - if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { + if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) { IWL_DEBUG_RADIO(mvm, "UATS feature is not supported\n"); return; } @@ -504,11 +511,10 @@ static void iwl_mvm_uats_init(struct iwl_mvm *mvm) return; } - ret = iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt); - if (ret < 0) { - IWL_DEBUG_FW(mvm, "failed to read UATS table (%d)\n", ret); + iwl_uefi_get_uats_table(mvm->trans, &mvm->fwrt); + + if (!mvm->fwrt.uats_valid) return; - } ret = iwl_mvm_send_cmd(mvm, &cmd); if (ret < 0) @@ -578,7 +584,7 @@ static int iwl_send_phy_cfg_cmd(struct iwl_mvm *mvm) /* set flags extra PHY configuration flags from the device's cfg */ phy_cfg_cmd.phy_cfg |= - cpu_to_le32(mvm->trans->trans_cfg->extra_phy_cfg_flags); + cpu_to_le32(mvm->trans->mac_cfg->extra_phy_cfg_flags); phy_cfg_cmd.calib_control.event_trigger = mvm->fw->default_calib[ucode_type].event_trigger; @@ -617,7 +623,7 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm) mvm->rfkill_safe_init_done = false; - if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { + if (mvm->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { sb_cfg = iwl_read_umac_prph(mvm->trans, SB_MODIFY_CFG_FLAG); /* if needed, we'll reset this on our way out later */ mvm->fw_product_reset = sb_cfg == SB_CFG_RESIDES_IN_ROM; @@ -651,11 +657,6 @@ static int iwl_run_unified_mvm_ucode(struct iwl_mvm *mvm) iwl_dbg_tlv_time_point(&mvm->fwrt, IWL_FW_INI_TIME_POINT_AFTER_ALIVE, NULL); - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) - mvm->trans->step_urm = !!(iwl_read_umac_prph(mvm->trans, - CNVI_PMU_STEP_FLOW) & - CNVI_PMU_STEP_FLOW_FORCE_URM); - /* Send init config command to mark that we are sending NVM access * commands */ @@ -756,7 +757,7 @@ int iwl_run_init_mvm_ucode(struct iwl_mvm *mvm) goto remove_notif; } - if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) { + if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000) { ret = iwl_mvm_send_bt_init_conf(mvm); if (ret) goto remove_notif; @@ -1094,22 +1095,6 @@ static int iwl_mvm_ppag_init(struct iwl_mvm *mvm) return iwl_mvm_ppag_send_cmd(mvm); } -static bool -iwl_mvm_add_to_tas_block_list(u16 *list, u8 *size, u16 mcc) -{ - /* Verify that there is room for another country */ - if (*size >= IWL_WTAS_BLACK_LIST_MAX) - return false; - - for (u8 i = 0; i < *size; i++) { - if (list[i] == mcc) - return true; - } - - list[*size++] = mcc; - return true; -} - static void iwl_mvm_tas_init(struct iwl_mvm *mvm) { u32 cmd_id = WIDE_ID(REGULATORY_AND_NVM_GROUP, TAS_CONFIG); @@ -1150,10 +1135,10 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "System vendor '%s' is not in the approved list, disabling TAS in US and Canada.\n", dmi_get_system_info(DMI_SYS_VENDOR) ?: "<unknown>"); - if ((!iwl_mvm_add_to_tas_block_list(data.block_list_array, + if ((!iwl_add_mcc_to_tas_block_list(data.block_list_array, &data.block_list_size, IWL_MCC_US)) || - (!iwl_mvm_add_to_tas_block_list(data.block_list_array, + (!iwl_add_mcc_to_tas_block_list(data.block_list_array, &data.block_list_size, IWL_MCC_CANADA))) { IWL_DEBUG_RADIO(mvm, @@ -1213,38 +1198,6 @@ static void iwl_mvm_tas_init(struct iwl_mvm *mvm) IWL_DEBUG_RADIO(mvm, "failed to send TAS_CONFIG (%d)\n", ret); } -static bool iwl_mvm_eval_dsm_rfi(struct iwl_mvm *mvm) -{ - u32 value = 0; - /* default behaviour is disabled */ - bool bios_enable_rfi = false; - int ret = iwl_bios_get_dsm(&mvm->fwrt, DSM_FUNC_RFI_CONFIG, &value); - - - if (ret < 0) { - IWL_DEBUG_RADIO(mvm, "Failed to get DSM RFI, ret=%d\n", ret); - return bios_enable_rfi; - } - - value &= DSM_VALUE_RFI_DISABLE; - /* RFI BIOS CONFIG value can be 0 or 3 only. - * i.e 0 means DDR and DLVR enabled. 3 means DDR and DLVR disabled. - * 1 and 2 are invalid BIOS configurations, So, it's not possible to - * disable ddr/dlvr separately. - */ - if (!value) { - IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to enable\n"); - bios_enable_rfi = true; - } else if (value == DSM_VALUE_RFI_DISABLE) { - IWL_DEBUG_RADIO(mvm, "DSM RFI is evaluated to disable\n"); - } else { - IWL_DEBUG_RADIO(mvm, - "DSM RFI got invalid value, value=%d\n", value); - } - - return bios_enable_rfi; -} - static void iwl_mvm_lari_cfg(struct iwl_mvm *mvm) { struct iwl_lari_config_change_cmd cmd; @@ -1318,7 +1271,7 @@ void iwl_mvm_get_bios_tables(struct iwl_mvm *mvm) } } - iwl_acpi_get_phy_filters(&mvm->fwrt, &mvm->phy_filters); + iwl_acpi_get_phy_filters(&mvm->fwrt); if (iwl_bios_get_eckv(&mvm->fwrt, &mvm->ext_clock_valid)) IWL_DEBUG_RADIO(mvm, "ECKV table doesn't exist in BIOS\n"); @@ -1631,7 +1584,7 @@ int iwl_mvm_up(struct iwl_mvm *mvm) iwl_mvm_uats_init(mvm); if (iwl_rfi_supported(mvm)) { - if (iwl_mvm_eval_dsm_rfi(mvm)) + if (iwl_rfi_is_enabled_in_bios(&mvm->fwrt)) iwl_rfi_send_config_cmd(mvm, NULL); } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/led.c b/drivers/net/wireless/intel/iwlwifi/mvm/led.c index 1ea7c44250d4..c3cc1ea3ccc9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/led.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/led.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2019 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2025 Intel Corporation * Copyright (C) 2017 Intel Deutschland GmbH */ #include <linux/leds.h> @@ -102,7 +102,7 @@ void iwl_mvm_leds_sync(struct iwl_mvm *mvm) * if we control through the register, we're doing it * even when the firmware isn't up, so no need to sync */ - if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) + if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000) return; iwl_mvm_led_set(mvm, mvm->led.brightness > 0); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c index 6b06732441c3..9098a36530cc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -938,12 +938,19 @@ u8 iwl_mvm_mac_ctxt_get_lowest_rate(struct iwl_mvm *mvm, u16 iwl_mvm_mac_ctxt_get_beacon_flags(const struct iwl_fw *fw, u8 rate_idx) { - u16 flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx); bool is_new_rate = iwl_fw_lookup_cmd_ver(fw, BEACON_TEMPLATE_CMD, 0) > 10; + u16 flags, cck_flag; - if (rate_idx <= IWL_FIRST_CCK_RATE) - flags |= is_new_rate ? IWL_MAC_BEACON_CCK - : IWL_MAC_BEACON_CCK_V1; + if (is_new_rate) { + flags = iwl_mvm_mac80211_idx_to_hwrate(fw, rate_idx); + cck_flag = IWL_MAC_BEACON_CCK; + } else { + cck_flag = IWL_MAC_BEACON_CCK_V1; + flags = iwl_fw_rate_idx_to_plcp(rate_idx); + } + + if (rate_idx <= IWL_LAST_CCK_RATE) + flags |= cck_flag; return flags; } @@ -969,7 +976,7 @@ u8 iwl_mvm_mac_ctxt_get_beacon_rate(struct iwl_mvm *mvm, static void iwl_mvm_mac_ctxt_set_tx(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct sk_buff *beacon, - struct iwl_tx_cmd *tx) + struct iwl_tx_cmd_v6 *tx) { struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_tx_info *info; @@ -1960,26 +1967,3 @@ void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm, ieee80211_channel_switch_disconnect(vif); rcu_read_unlock(); } - -void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb) -{ - struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_missed_vap_notif *mb = (void *)pkt->data; - struct ieee80211_vif *vif; - u32 id = le32_to_cpu(mb->mac_id); - - IWL_DEBUG_INFO(mvm, - "missed_vap notify mac_id=%u, num_beacon_intervals_elapsed=%u, profile_periodicity=%u\n", - le32_to_cpu(mb->mac_id), - mb->num_beacon_intervals_elapsed, - mb->profile_periodicity); - - rcu_read_lock(); - - vif = iwl_mvm_rcu_dereference_vif_id(mvm, id, true); - if (vif) - iwl_mvm_connection_loss(mvm, vif, "missed vap beacon"); - - rcu_read_unlock(); -} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c index af6644b7e95f..0f056a6641bd 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -70,7 +70,7 @@ static const struct ieee80211_iface_combination iwl_mvm_iface_combinations[] = { }; static const struct cfg80211_pmsr_capabilities iwl_mvm_pmsr_capa = { - .max_peers = IWL_MVM_TOF_MAX_APS, + .max_peers = IWL_TOF_MAX_APS, .report_ap_tsf = 1, .randomize_mac_addr = 1, @@ -146,7 +146,7 @@ struct ieee80211_regdomain *iwl_mvm_get_regdomain(struct wiphy *wiphy, MCC_UPDATE_CMD, 0); IWL_DEBUG_LAR(mvm, "MCC update response version: %d\n", resp_ver); - regd = iwl_parse_nvm_mcc_info(mvm->trans->dev, mvm->cfg, + regd = iwl_parse_nvm_mcc_info(mvm->trans, __le32_to_cpu(resp->n_channels), resp->channels, __le16_to_cpu(resp->mcc), @@ -272,9 +272,10 @@ static const u8 tm_if_types_ext_capa_sta[] = { __bf_shf(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY) | \ IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_64US << \ __bf_shf(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY)) -#define IWL_MVM_MLD_CAPA_OPS FIELD_PREP_CONST( \ +#define IWL_MVM_MLD_CAPA_OPS (FIELD_PREP_CONST( \ IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP, \ - IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) + IEEE80211_MLD_CAP_OP_TID_TO_LINK_MAP_NEG_SUPP_SAME) | \ + IEEE80211_MLD_CAP_OP_LINK_RECONF_SUPPORT) static const struct wiphy_iftype_ext_capab add_iftypes_ext_capa[] = { { @@ -310,8 +311,8 @@ int iwl_mvm_op_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); /* This has been tested on those devices only */ - if (mvm->trans->trans_cfg->device_family != IWL_DEVICE_FAMILY_9000 && - mvm->trans->trans_cfg->device_family != IWL_DEVICE_FAMILY_22000) + if (mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_9000 && + mvm->trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_22000) return -EOPNOTSUPP; if (!mvm->nvm_data) @@ -390,7 +391,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) * for older devices. We also don't see this issue on any newer * devices. */ - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000) + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_9000) ieee80211_hw_set(hw, TX_AMSDU); ieee80211_hw_set(hw, TX_FRAG_LIST); @@ -401,7 +402,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) /* We want to use the mac80211's reorder buffer for 9000 */ if (iwl_mvm_has_new_rx_api(mvm) && - mvm->trans->trans_cfg->device_family > IWL_DEVICE_FAMILY_9000) + mvm->trans->mac_cfg->device_family > IWL_DEVICE_FAMILY_9000) ieee80211_hw_set(hw, SUPPORTS_REORDERING_BUFFER); if (fw_has_capa(&mvm->fw->ucode_capa, @@ -416,10 +417,10 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) return -EINVAL; } - if (mvm->trans->num_rx_queues > 1) + if (mvm->trans->info.num_rxqs > 1) ieee80211_hw_set(hw, USES_RSS); - if (mvm->trans->max_skb_frags) + if (mvm->trans->info.max_skb_frags) hw->netdev_features = NETIF_F_HIGHDMA | NETIF_F_SG; hw->queues = IEEE80211_NUM_ACS; @@ -440,7 +441,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->uapsd_queues = IWL_MVM_UAPSD_QUEUES; hw->uapsd_max_sp_len = IWL_UAPSD_MAX_SP; - hw->max_tx_fragments = mvm->trans->max_skb_frags; + hw->max_tx_fragments = mvm->trans->info.max_skb_frags; BUILD_BUG_ON(ARRAY_SIZE(mvm->ciphers) < ARRAY_SIZE(mvm_ciphers) + 6); memcpy(mvm->ciphers, mvm_ciphers, sizeof(mvm_ciphers)); @@ -543,7 +544,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG | REGULATORY_DISABLE_BEACON_HINTS; - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_DFS_CONCURRENT); @@ -609,7 +610,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->bands[NL80211_BAND_6GHZ] = &mvm->nvm_data->bands[NL80211_BAND_6GHZ]; - hw->wiphy->hw_version = mvm->trans->hw_id; + hw->wiphy->hw_version = mvm->trans->info.hw_id; if (iwlmvm_mod_params.power_scheme != IWL_POWER_SCHEME_CAM) hw->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT; @@ -730,8 +731,6 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) mvm->rts_threshold = IEEE80211_MAX_RTS_THRESHOLD; - ieee80211_hw_set(hw, DISALLOW_PUNCTURING_5GHZ); - #ifdef CONFIG_PM_SLEEP if ((unified || mvm->fw->img[IWL_UCODE_WOWLAN].num_sec) && device_can_wakeup(mvm->trans->dev)) { @@ -770,7 +769,7 @@ int iwl_mvm_mac_setup_register(struct iwl_mvm *mvm) hw->wiphy->features |= NL80211_FEATURE_TDLS_CHANNEL_SWITCH; } - hw->netdev_features |= mvm->cfg->features; + hw->netdev_features |= mvm->trans->mac_cfg->base->features; if (!iwl_mvm_is_csum_supported(mvm)) hw->netdev_features &= ~IWL_CSUM_NETIF_FLAGS_MASK; @@ -1378,7 +1377,7 @@ void __iwl_mvm_mac_stop(struct iwl_mvm *mvm, bool suspend) iwl_mvm_rm_aux_sta(mvm); if (suspend && - mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { + mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000) { iwl_mvm_fast_suspend(mvm); /* From this point on, we won't touch the device */ iwl_mvm_mei_device_state(mvm, false); @@ -1987,15 +1986,8 @@ static void iwl_mvm_mac_remove_interface(struct ieee80211_hw *hw, * interface is be handled as part of the stop_ap flow. */ if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) { -#ifdef CONFIG_NL80211_TESTMODE - if (vif == mvm->noa_vif) { - mvm->noa_vif = NULL; - mvm->noa_duration = 0; - } -#endif + vif->type == NL80211_IFTYPE_ADHOC) goto out; - } iwl_mvm_power_update_mac(mvm); @@ -3058,7 +3050,7 @@ static int iwl_mvm_start_ap_ibss(struct ieee80211_hw *hw, * context. For the newer, the beacon is a resource that belongs to a * MAC, so need to send beacon template after adding the mac. */ - if (mvm->trans->trans_cfg->device_family > IWL_DEVICE_FAMILY_22000) { + if (mvm->trans->mac_cfg->device_family > IWL_DEVICE_FAMILY_22000) { /* Add the mac context */ ret = iwl_mvm_mac_ctxt_add(mvm, vif); if (ret) @@ -4099,6 +4091,21 @@ iwl_mvm_sta_state_authorized_to_assoc(struct iwl_mvm *mvm, return 0; } +void iwl_mvm_smps_workaround(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool update) +{ + struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); + + if (!iwl_mvm_has_rlc_offload(mvm) || + iwl_fw_lookup_cmd_ver(mvm->fw, MAC_PM_POWER_TABLE, 0) >= 2) + return; + + mvmvif->ps_disabled = !vif->cfg.ps; + + if (update) + iwl_mvm_power_update_mac(mvm); +} + /* Common part for MLD and non-MLD modes */ int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw, struct ieee80211_vif *vif, @@ -4191,6 +4198,7 @@ int iwl_mvm_mac_sta_state_common(struct ieee80211_hw *hw, new_state == IEEE80211_STA_AUTHORIZED) { ret = iwl_mvm_sta_state_assoc_to_authorized(mvm, vif, sta, callbacks); + iwl_mvm_smps_workaround(mvm, vif, true); } else if (old_state == IEEE80211_STA_AUTHORIZED && new_state == IEEE80211_STA_ASSOC) { ret = iwl_mvm_sta_state_authorized_to_assoc(mvm, vif, sta, @@ -4379,7 +4387,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, switch (key->cipher) { case WLAN_CIPHER_SUITE_TKIP: - if (!mvm->trans->trans_cfg->gen2) { + if (!mvm->trans->mac_cfg->gen2) { key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; key->flags |= IEEE80211_KEY_FLAG_PUT_IV_SPACE; } else if (vif->type == NL80211_IFTYPE_STATION) { @@ -4496,7 +4504,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, WARN_ON(rcu_access_pointer(mvmsta->ptk_pn[keyidx])); ptk_pn = kzalloc(struct_size(ptk_pn, q, - mvm->trans->num_rx_queues), + mvm->trans->info.num_rxqs), GFP_KERNEL); if (!ptk_pn) { ret = -ENOMEM; @@ -4505,7 +4513,7 @@ static int __iwl_mvm_mac_set_key(struct ieee80211_hw *hw, for (tid = 0; tid < IWL_MAX_TID_COUNT; tid++) { ieee80211_get_key_rx_seq(key, tid, &seq); - for (q = 0; q < mvm->trans->num_rx_queues; q++) + for (q = 0; q < mvm->trans->info.num_rxqs; q++) memcpy(ptk_pn->q[q].pn[tid], seq.ccmp.pn, IEEE80211_CCMP_PN_LEN); @@ -5509,70 +5517,6 @@ static int iwl_mvm_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, &mvm_sta->vif->bss_conf); } -#ifdef CONFIG_NL80211_TESTMODE -static const struct nla_policy iwl_mvm_tm_policy[IWL_MVM_TM_ATTR_MAX + 1] = { - [IWL_MVM_TM_ATTR_CMD] = { .type = NLA_U32 }, - [IWL_MVM_TM_ATTR_NOA_DURATION] = { .type = NLA_U32 }, - [IWL_MVM_TM_ATTR_BEACON_FILTER_STATE] = { .type = NLA_U32 }, -}; - -static int __iwl_mvm_mac_testmode_cmd(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - void *data, int len) -{ - struct nlattr *tb[IWL_MVM_TM_ATTR_MAX + 1]; - int err; - u32 noa_duration; - - err = nla_parse_deprecated(tb, IWL_MVM_TM_ATTR_MAX, data, len, - iwl_mvm_tm_policy, NULL); - if (err) - return err; - - if (!tb[IWL_MVM_TM_ATTR_CMD]) - return -EINVAL; - - switch (nla_get_u32(tb[IWL_MVM_TM_ATTR_CMD])) { - case IWL_MVM_TM_CMD_SET_NOA: - if (!vif || vif->type != NL80211_IFTYPE_AP || !vif->p2p || - !vif->bss_conf.enable_beacon || - !tb[IWL_MVM_TM_ATTR_NOA_DURATION]) - return -EINVAL; - - noa_duration = nla_get_u32(tb[IWL_MVM_TM_ATTR_NOA_DURATION]); - if (noa_duration >= vif->bss_conf.beacon_int) - return -EINVAL; - - mvm->noa_duration = noa_duration; - mvm->noa_vif = vif; - - return iwl_mvm_update_quotas(mvm, true, NULL); - case IWL_MVM_TM_CMD_SET_BEACON_FILTER: - /* must be associated client vif - ignore authorized */ - if (!vif || vif->type != NL80211_IFTYPE_STATION || - !vif->cfg.assoc || !vif->bss_conf.dtim_period || - !tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE]) - return -EINVAL; - - if (nla_get_u32(tb[IWL_MVM_TM_ATTR_BEACON_FILTER_STATE])) - return iwl_mvm_enable_beacon_filter(mvm, vif); - return iwl_mvm_disable_beacon_filter(mvm, vif); - } - - return -EOPNOTSUPP; -} - -int iwl_mvm_mac_testmode_cmd(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - void *data, int len) -{ - struct iwl_mvm *mvm = IWL_MAC80211_GET_MVM(hw); - - guard(mvm)(mvm); - return __iwl_mvm_mac_testmode_cmd(mvm, vif, data, len); -} -#endif - void iwl_mvm_channel_switch(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_channel_switch *chsw) { @@ -6124,12 +6068,12 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo) break; } - if (format == RATE_MCS_CCK_MSK || - format == RATE_MCS_LEGACY_OFDM_MSK) { + if (format == RATE_MCS_MOD_TYPE_CCK || + format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) { int rate = u32_get_bits(rate_n_flags, RATE_LEGACY_RATE_MSK); /* add the offset needed to get to the legacy ofdm indices */ - if (format == RATE_MCS_LEGACY_OFDM_MSK) + if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) rate += IWL_FIRST_OFDM_RATE; switch (rate) { @@ -6174,7 +6118,7 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo) rinfo->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; - rinfo->mcs = format == RATE_MCS_HT_MSK ? + rinfo->mcs = format == RATE_MCS_MOD_TYPE_HT ? RATE_HT_MCS_INDEX(rate_n_flags) : u32_get_bits(rate_n_flags, RATE_MCS_CODE_MSK); @@ -6182,11 +6126,11 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo) rinfo->flags |= RATE_INFO_FLAGS_SHORT_GI; switch (format) { - case RATE_MCS_EHT_MSK: + case RATE_MCS_MOD_TYPE_EHT: /* TODO: GI/LTF/RU. How does the firmware encode them? */ rinfo->flags |= RATE_INFO_FLAGS_EHT_MCS; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: gi_ltf = u32_get_bits(rate_n_flags, RATE_MCS_HE_GI_LTF_MSK); rinfo->flags |= RATE_INFO_FLAGS_HE_MCS; @@ -6227,10 +6171,10 @@ static void iwl_mvm_set_sta_rate(u32 rate_n_flags, struct rate_info *rinfo) if (rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK) rinfo->he_dcm = 1; break; - case RATE_MCS_HT_MSK: + case RATE_MCS_MOD_TYPE_HT: rinfo->flags |= RATE_INFO_FLAGS_MCS; break; - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: rinfo->flags |= RATE_INFO_FLAGS_VHT_MCS; break; } @@ -6410,17 +6354,10 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, bool sync, const void *data, u32 size) { - struct { - struct iwl_rxq_sync_cmd cmd; - struct iwl_mvm_internal_rxq_notif notif; - } __packed cmd = { - .cmd.rxq_mask = cpu_to_le32(BIT(mvm->trans->num_rx_queues) - 1), - .cmd.count = - cpu_to_le32(sizeof(struct iwl_mvm_internal_rxq_notif) + - size), - .notif.type = type, - .notif.sync = sync, - }; + DEFINE_RAW_FLEX(struct iwl_rxq_sync_cmd, cmd, payload, + sizeof(struct iwl_mvm_internal_rxq_notif)); + struct iwl_mvm_internal_rxq_notif *notif = + (struct iwl_mvm_internal_rxq_notif *)cmd->payload; struct iwl_host_cmd hcmd = { .id = WIDE_ID(DATA_PATH_GROUP, TRIGGER_RX_QUEUES_NOTIF_CMD), .data[0] = &cmd, @@ -6431,16 +6368,22 @@ void iwl_mvm_sync_rx_queues_internal(struct iwl_mvm *mvm, }; int ret; + cmd->rxq_mask = cpu_to_le32(BIT(mvm->trans->info.num_rxqs) - 1); + cmd->count = cpu_to_le32(sizeof(struct iwl_mvm_internal_rxq_notif) + + size); + notif->type = type; + notif->sync = sync; + /* size must be a multiple of DWORD */ - if (WARN_ON(cmd.cmd.count & cpu_to_le32(3))) + if (WARN_ON(cmd->count & cpu_to_le32(3))) return; if (!iwl_mvm_has_new_rx_api(mvm)) return; if (sync) { - cmd.notif.cookie = mvm->queue_sync_cookie; - mvm->queue_sync_state = (1 << mvm->trans->num_rx_queues) - 1; + notif->cookie = mvm->queue_sync_cookie; + mvm->queue_sync_state = (1 << mvm->trans->info.num_rxqs) - 1; } ret = iwl_mvm_send_cmd(mvm, &hcmd); @@ -6633,8 +6576,6 @@ const struct ieee80211_ops iwl_mvm_hw_ops = { .sync_rx_queues = iwl_mvm_sync_rx_queues, - CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) - #ifdef CONFIG_PM_SLEEP /* look at d3.c */ .suspend = iwl_mvm_suspend, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c index bb7851042177..81ca9ff67be9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac.c @@ -1,17 +1,25 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022 - 2024 Intel Corporation + * Copyright (C) 2022 - 2025 Intel Corporation */ #include "mvm.h" static void iwl_mvm_mld_set_he_support(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mac_config_cmd *cmd) + struct iwl_mac_config_cmd *cmd, + int cmd_ver) { - if (vif->type == NL80211_IFTYPE_AP) - cmd->he_ap_support = cpu_to_le16(1); - else - cmd->he_support = cpu_to_le16(1); + if (vif->type == NL80211_IFTYPE_AP) { + if (cmd_ver == 2) + cmd->wifi_gen_v2.he_ap_support = cpu_to_le16(1); + else + cmd->wifi_gen.he_ap_support = 1; + } else { + if (cmd_ver == 2) + cmd->wifi_gen_v2.he_support = cpu_to_le16(1); + else + cmd->wifi_gen.he_support = 1; + } } static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, @@ -22,6 +30,12 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); struct ieee80211_bss_conf *link_conf; unsigned int link_id; + int cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, + WIDE_ID(MAC_CONF_GROUP, + MAC_CONFIG_CMD), 0); + + if (WARN_ON(cmd_ver < 1 && cmd_ver > 3)) + return; cmd->id_and_color = cpu_to_le32(mvmvif->id); cmd->action = cpu_to_le32(action); @@ -30,8 +44,8 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, memcpy(cmd->local_mld_addr, vif->addr, ETH_ALEN); - cmd->he_support = 0; - cmd->eht_support = 0; + cmd->wifi_gen_v2.he_support = 0; + cmd->wifi_gen_v2.eht_support = 0; /* should be set by specific context type handler */ cmd->filter_flags = 0; @@ -51,8 +65,11 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, * and enable both when we have MLO. */ if (ieee80211_vif_is_mld(vif)) { - iwl_mvm_mld_set_he_support(mvm, vif, cmd); - cmd->eht_support = cpu_to_le32(1); + iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver); + if (cmd_ver == 2) + cmd->wifi_gen_v2.eht_support = cpu_to_le32(1); + else + cmd->wifi_gen.eht_support = 1; return; } @@ -63,16 +80,19 @@ static void iwl_mvm_mld_mac_ctxt_cmd_common(struct iwl_mvm *mvm, continue; if (link_conf->he_support) - iwl_mvm_mld_set_he_support(mvm, vif, cmd); + iwl_mvm_mld_set_he_support(mvm, vif, cmd, cmd_ver); - /* it's not reasonable to have EHT without HE and FW API doesn't + /* It's not reasonable to have EHT without HE and FW API doesn't * support it. Ignore EHT in this case. */ if (!link_conf->he_support && link_conf->eht_support) continue; if (link_conf->eht_support) { - cmd->eht_support = cpu_to_le32(1); + if (cmd_ver == 2) + cmd->wifi_gen_v2.eht_support = cpu_to_le32(1); + else + cmd->wifi_gen.eht_support = 1; break; } } diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c index 341a2a7a49ec..bf24f8cb673e 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-mac80211.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022-2024 Intel Corporation + * Copyright (C) 2022-2025 Intel Corporation */ #include "mvm.h" @@ -150,19 +150,6 @@ static void iwl_mvm_mld_mac_remove_interface(struct ieee80211_hw *hw, iwl_mvm_vif_dbgfs_rm_link(mvm, vif); - /* For AP/GO interface, the tear down of the resources allocated to the - * interface is be handled as part of the stop_ap flow. - */ - if (vif->type == NL80211_IFTYPE_AP || - vif->type == NL80211_IFTYPE_ADHOC) { -#ifdef CONFIG_NL80211_TESTMODE - if (vif == mvm->noa_vif) { - mvm->noa_vif = NULL; - mvm->noa_duration = 0; - } -#endif - } - iwl_mvm_power_update_mac(mvm); /* Before the interface removal, mac80211 would cancel the ROC, and the @@ -887,6 +874,7 @@ static void iwl_mvm_mld_vif_cfg_changed_station(struct iwl_mvm *mvm, } if (changes & BSS_CHANGED_PS) { + iwl_mvm_smps_workaround(mvm, vif, false); ret = iwl_mvm_power_update_mac(mvm); if (ret) IWL_ERR(mvm, "failed to update power mode\n"); @@ -1402,8 +1390,6 @@ const struct ieee80211_ops iwl_mvm_mld_hw_ops = { .sync_rx_queues = iwl_mvm_sync_rx_queues, - CFG80211_TESTMODE_CMD(iwl_mvm_mac_testmode_cmd) - #ifdef CONFIG_PM_SLEEP /* look at d3.c */ .suspend = iwl_mvm_suspend, diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c index 2f159024eeb8..e1010521c3ea 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mld-sta.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2022-2024 Intel Corporation + * Copyright (C) 2022-2025 Intel Corporation */ #include "mvm.h" #include "time-sync.h" @@ -48,9 +48,13 @@ u32 iwl_mvm_sta_fw_id_mask(struct iwl_mvm *mvm, struct ieee80211_sta *sta, static int iwl_mvm_mld_send_sta_cmd(struct iwl_mvm *mvm, struct iwl_sta_cfg_cmd *cmd) { + u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD); + int cmd_len = iwl_fw_lookup_cmd_ver(mvm->fw, cmd_id, 0) > 1 ? + sizeof(*cmd) : + sizeof(struct iwl_sta_cfg_cmd_v1); int ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), - 0, sizeof(*cmd), cmd); + 0, cmd_len, cmd); if (ret) IWL_ERR(mvm, "STA_CONFIG_CMD send failed, ret=0x%x\n", ret); return ret; @@ -121,7 +125,7 @@ static int iwl_mvm_add_aux_sta_to_fw(struct iwl_mvm *mvm, { int ret; - struct iwl_mvm_aux_sta_cmd cmd = { + struct iwl_aux_sta_cmd cmd = { .sta_id = cpu_to_le32(sta->sta_id), .lmac_id = cpu_to_le32(lmac_id), }; @@ -144,7 +148,7 @@ int iwl_mvm_mld_add_int_sta_with_queue(struct iwl_mvm *mvm, { int ret, txq; unsigned int wdg_timeout = _wdg_timeout ? *_wdg_timeout : - mvm->trans->trans_cfg->base_params->wd_timeout; + mvm->trans->mac_cfg->base->wd_timeout; if (WARN_ON_ONCE(sta->sta_id == IWL_INVALID_STA)) return -ENOSPC; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index ee769da72e68..a4f412e750d0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -126,7 +126,7 @@ struct iwl_mvm_time_event_data { /* Power management */ /** - * enum iwl_power_scheme + * enum iwl_power_scheme - iwl power schemes * @IWL_POWER_SCHEME_CAM: Continuously Active Mode * @IWL_POWER_SCHEME_BPS: Balanced Power Save (default) * @IWL_POWER_SCHEME_LP: Low Power @@ -664,6 +664,8 @@ enum iwl_mvm_sched_scan_pass_all_states { * @min_backoff: The minimal tx backoff due to power restrictions * @params: Parameters to configure the thermal throttling algorithm. * @throttle: Is thermal throttling is active? + * @power_budget_mw: maximum cTDP power budget as defined for this system and + * device */ struct iwl_mvm_tt_mgmt { struct delayed_work ct_kill_exit; @@ -672,6 +674,8 @@ struct iwl_mvm_tt_mgmt { u32 min_backoff; struct iwl_tt_params params; bool throttle; + + u32 power_budget_mw; }; #ifdef CONFIG_THERMAL @@ -999,7 +1003,7 @@ struct iwl_mvm { struct iwl_trans *trans; const struct iwl_fw *fw; - const struct iwl_cfg *cfg; + const struct iwl_rf_cfg *cfg; struct iwl_phy_db *phy_db; struct ieee80211_hw *hw; @@ -1033,6 +1037,8 @@ struct iwl_mvm { u8 cca_40mhz_workaround; + u8 fw_rates_ver; + u32 ampdu_ref; bool ampdu_toggle; @@ -1246,11 +1252,6 @@ struct iwl_mvm { struct iwl_time_quota_cmd last_quota_cmd; -#ifdef CONFIG_NL80211_TESTMODE - u32 noa_duration; - struct ieee80211_vif *noa_vif; -#endif - /* Tx queues */ u16 aux_queue; u16 snif_queue; @@ -1310,7 +1311,7 @@ struct iwl_mvm { struct cfg80211_pmsr_request *req; struct wireless_dev *req_wdev; struct list_head loc_list; - int responses[IWL_MVM_TOF_MAX_APS]; + int responses[IWL_TOF_MAX_APS]; struct { struct list_head resp; } smooth; @@ -1348,10 +1349,6 @@ struct iwl_mvm { __le16 cur_aid; u8 cur_bssid[ETH_ALEN]; -#ifdef CONFIG_ACPI - struct iwl_phy_specific_cfg phy_filters; -#endif - /* report rx timestamp in ptp clock time */ bool rx_ts_ptp; @@ -1562,7 +1559,7 @@ static inline bool iwl_mvm_is_lar_supported(struct iwl_mvm *mvm) * Enable LAR only if it is supported by the FW (TLV) && * enabled in the NVM */ - if (mvm->cfg->nvm_type == IWL_NVM_EXT) + if (mvm->trans->cfg->nvm_type == IWL_NVM_EXT) return nvm_lar && tlv_lar; else return tlv_lar; @@ -1626,13 +1623,13 @@ static inline bool iwl_mvm_has_new_station_api(const struct iwl_fw *fw) static inline bool iwl_mvm_has_new_tx_api(struct iwl_mvm *mvm) { /* TODO - replace with TLV once defined */ - return mvm->trans->trans_cfg->gen2; + return mvm->trans->mac_cfg->gen2; } static inline bool iwl_mvm_has_unified_ucode(struct iwl_mvm *mvm) { /* TODO - better define this */ - return mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000; + return mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000; } static inline bool iwl_mvm_is_cdb_supported(struct iwl_mvm *mvm) @@ -1657,7 +1654,7 @@ static inline bool iwl_mvm_cdb_scan_api(struct iwl_mvm *mvm) * but then there's a little bit of code in scan that won't make * any sense... */ - return mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000; + return mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000; } static inline bool iwl_mvm_is_scan_ext_chan_supported(struct iwl_mvm *mvm) @@ -1732,13 +1729,13 @@ static inline bool iwl_mvm_is_ctdp_supported(struct iwl_mvm *mvm) static inline bool iwl_mvm_is_esr_supported(struct iwl_trans *trans) { - if (CSR_HW_RFID_IS_CDB(trans->hw_rf_id)) + if (CSR_HW_RFID_IS_CDB(trans->info.hw_rf_id)) return false; - switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { + switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) { case IWL_CFG_RF_TYPE_FM: /* Step A doesn't support eSR */ - return CSR_HW_RFID_STEP(trans->hw_rf_id); + return CSR_HW_RFID_STEP(trans->info.hw_rf_id); case IWL_CFG_RF_TYPE_WH: case IWL_CFG_RF_TYPE_PE: return true; @@ -1757,8 +1754,8 @@ static inline int iwl_mvm_max_active_links(struct iwl_mvm *mvm, /* Check if HW supports eSR or STR */ if (iwl_mvm_is_esr_supported(trans) || - (CSR_HW_RFID_TYPE(trans->hw_rf_id) == IWL_CFG_RF_TYPE_FM && - CSR_HW_RFID_IS_CDB(trans->hw_rf_id))) + (CSR_HW_RFID_TYPE(trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM && + CSR_HW_RFID_IS_CDB(trans->info.hw_rf_id))) return IWL_FW_MAX_ACTIVE_LINKS_NUM; return 1; @@ -1771,7 +1768,7 @@ extern const u8 iwl_mvm_ac_to_bz_tx_fifo[]; static inline u8 iwl_mvm_mac_ac_to_tx_fifo(struct iwl_mvm *mvm, enum ieee80211_ac_numbers ac) { - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) return iwl_mvm_ac_to_bz_tx_fifo[ac]; if (iwl_mvm_has_new_tx_api(mvm)) return iwl_mvm_ac_to_gen2_tx_fifo[ac]; @@ -1810,9 +1807,6 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, enum nl80211_band band, struct ieee80211_tx_rate *r); -void iwl_mvm_hwrate_to_tx_rate_v1(u32 rate_n_flags, - enum nl80211_band band, - struct ieee80211_tx_rate *r); u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx); u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac); bool iwl_mvm_is_nic_ack_enabled(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -1843,9 +1837,9 @@ int iwl_mvm_tx_skb_sta(struct iwl_mvm *mvm, struct sk_buff *skb, struct ieee80211_sta *sta); int iwl_mvm_tx_skb_non_sta(struct iwl_mvm *mvm, struct sk_buff *skb); void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, - struct iwl_tx_cmd *tx_cmd, + struct iwl_tx_cmd_v6 *tx_cmd, struct ieee80211_tx_info *info, u8 sta_id); -void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, +void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd_v6 *tx_cmd, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, __le16 fc); void iwl_mvm_mac_itxq_xmit(struct ieee80211_hw *hw, struct ieee80211_txq *txq); @@ -1876,7 +1870,7 @@ int iwl_mvm_set_sta_pkt_ext(struct iwl_mvm *mvm, void iwl_mvm_async_handlers_purge(struct iwl_mvm *mvm); static inline void iwl_mvm_set_tx_cmd_ccmp(struct ieee80211_tx_info *info, - struct iwl_tx_cmd *tx_cmd) + struct iwl_tx_cmd_v6 *tx_cmd) { struct ieee80211_key_conf *keyconf = info->control.hw_key; @@ -2095,8 +2089,6 @@ void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm, struct ieee80211_vif *vif); void iwl_mvm_probe_resp_data_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -void iwl_mvm_rx_missed_vap_notif(struct iwl_mvm *mvm, - struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_channel_switch_start_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); void iwl_mvm_channel_switch_error_notif(struct iwl_mvm *mvm, @@ -2140,6 +2132,10 @@ bool iwl_mvm_mld_valid_link_pair(struct ieee80211_vif *vif, const struct iwl_mvm_link_sel_data *b); s8 iwl_mvm_average_dbm_values(const struct iwl_umac_scan_channel_survey_notif *notif); + + +extern const struct iwl_hcmd_arr iwl_mvm_groups[]; +extern const unsigned int iwl_mvm_groups_size; #endif /* AP and IBSS */ @@ -2461,7 +2457,7 @@ void iwl_mvm_vif_set_low_latency(struct iwl_mvm_vif *mvmvif, bool set, */ static inline u32 iwl_mvm_flushable_queues(struct iwl_mvm *mvm) { - return ((BIT(mvm->trans->trans_cfg->base_params->num_of_queues) - 1) & + return ((BIT(mvm->trans->mac_cfg->base->num_of_queues) - 1) & ~BIT(IWL_MVM_DQA_CMD_QUEUE)); } @@ -2520,12 +2516,6 @@ void iwl_mvm_ftm_restart_responder(struct iwl_mvm *mvm, struct ieee80211_bss_conf *bss_conf); void iwl_mvm_ftm_responder_stats(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb); -int iwl_mvm_ftm_resp_remove_pasn_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, u8 *addr); -int iwl_mvm_ftm_respoder_add_pasn_sta(struct iwl_mvm *mvm, - struct ieee80211_vif *vif, - u8 *addr, u32 cipher, u8 *tk, u32 tk_len, - u8 *hltk, u32 hltk_len); void iwl_mvm_ftm_responder_clear(struct iwl_mvm *mvm, struct ieee80211_vif *vif); @@ -2540,10 +2530,6 @@ int iwl_mvm_ftm_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, void iwl_mvm_ftm_abort(struct iwl_mvm *mvm, struct cfg80211_pmsr_request *req); void iwl_mvm_ftm_initiator_smooth_config(struct iwl_mvm *mvm); void iwl_mvm_ftm_initiator_smooth_stop(struct iwl_mvm *mvm); -int iwl_mvm_ftm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - u8 *addr, u32 cipher, u8 *tk, u32 tk_len, - u8 *hltk, u32 hltk_len); -void iwl_mvm_ftm_remove_pasn_sta(struct iwl_mvm *mvm, u8 *addr); /* TDLS */ @@ -3043,4 +3029,7 @@ iwl_mvm_send_ap_tx_power_constraint_cmd(struct iwl_mvm *mvm, struct ieee80211_vif *vif, struct ieee80211_bss_conf *bss_conf, bool is_ap); + +void iwl_mvm_smps_workaround(struct iwl_mvm *mvm, struct ieee80211_vif *vif, + bool update); #endif /* __IWL_MVM_H__ */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c index 80ec59c58ae4..953218f1e025 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/nvm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -120,7 +120,7 @@ static int iwl_nvm_read_chunk(struct iwl_mvm *mvm, u16 section, } else { IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM access command failed with status %d (device: %s)\n", - ret, mvm->trans->name); + ret, mvm->trans->info.name); ret = -ENODATA; } goto exit; @@ -191,7 +191,7 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, while (ret == length) { /* Check no memory assumptions fail and cause an overflow */ if ((size_read + offset + length) > - mvm->trans->trans_cfg->base_params->eeprom_size) { + mvm->trans->mac_cfg->base->eeprom_size) { IWL_ERR(mvm, "EEPROM size is too small for NVM\n"); return -ENOBUFS; } @@ -206,7 +206,7 @@ static int iwl_nvm_read_section(struct iwl_mvm *mvm, u16 section, offset += ret; } - iwl_nvm_fixups(mvm->trans->hw_id, section, data, offset); + iwl_nvm_fixups(mvm->trans->info.hw_id, section, data, offset); IWL_DEBUG_EEPROM(mvm->trans->dev, "NVM section %d read completed\n", section); @@ -226,7 +226,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) /* Checking for required sections */ if (mvm->trans->cfg->nvm_type == IWL_NVM) { if (!mvm->nvm_sections[NVM_SECTION_TYPE_SW].data || - !mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data) { + !mvm->nvm_sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data) { IWL_ERR(mvm, "Can't parse empty OTP/NVM sections\n"); return NULL; } @@ -244,7 +244,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) return NULL; } /* MAC_OVERRIDE or at least HW section must exist */ - if (!mvm->nvm_sections[mvm->cfg->nvm_hw_section_num].data && + if (!mvm->nvm_sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data && !mvm->nvm_sections[NVM_SECTION_TYPE_MAC_OVERRIDE].data) { IWL_ERR(mvm, "Can't parse mac_address, empty sections\n"); @@ -260,7 +260,7 @@ iwl_parse_nvm_sections(struct iwl_mvm *mvm) } } - hw = (const __be16 *)sections[mvm->cfg->nvm_hw_section_num].data; + hw = (const __be16 *)sections[mvm->trans->mac_cfg->base->nvm_hw_section_num].data; sw = (const __le16 *)sections[NVM_SECTION_TYPE_SW].data; calib = (const __le16 *)sections[NVM_SECTION_TYPE_CALIBRATION].data; mac_override = @@ -308,16 +308,15 @@ int iwl_nvm_init(struct iwl_mvm *mvm) int ret, section; u32 size_read = 0; u8 *nvm_buffer, *temp; - const char *nvm_file_C = mvm->cfg->default_nvm_file_C_step; - if (WARN_ON_ONCE(mvm->cfg->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) + if (WARN_ON_ONCE(mvm->trans->mac_cfg->base->nvm_hw_section_num >= NVM_MAX_NUM_SECTIONS)) return -EINVAL; /* load NVM values from nic */ /* Read From FW NVM */ IWL_DEBUG_EEPROM(mvm->trans->dev, "Read from NVM\n"); - nvm_buffer = kmalloc(mvm->trans->trans_cfg->base_params->eeprom_size, + nvm_buffer = kmalloc(mvm->trans->mac_cfg->base->eeprom_size, GFP_KERNEL); if (!nvm_buffer) return -ENOMEM; @@ -338,7 +337,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm) break; } - iwl_nvm_fixups(mvm->trans->hw_id, section, temp, ret); + iwl_nvm_fixups(mvm->trans->info.hw_id, section, temp, ret); mvm->nvm_sections[section].data = temp; mvm->nvm_sections[section].length = ret; @@ -367,7 +366,7 @@ int iwl_nvm_init(struct iwl_mvm *mvm) mvm->nvm_reg_blob.size = ret; break; default: - if (section == mvm->cfg->nvm_hw_section_num) { + if (section == mvm->trans->mac_cfg->base->nvm_hw_section_num) { mvm->nvm_hw_blob.data = temp; mvm->nvm_hw_blob.size = ret; break; @@ -384,21 +383,8 @@ int iwl_nvm_init(struct iwl_mvm *mvm) /* read External NVM file from the mod param */ ret = iwl_read_external_nvm(mvm->trans, mvm->nvm_file_name, mvm->nvm_sections); - if (ret) { - mvm->nvm_file_name = nvm_file_C; - - if ((ret == -EFAULT || ret == -ENOENT) && - mvm->nvm_file_name) { - /* in case nvm file was failed try again */ - ret = iwl_read_external_nvm(mvm->trans, - mvm->nvm_file_name, - mvm->nvm_sections); - if (ret) - return ret; - } else { - return ret; - } - } + if (ret) + return ret; } /* parse the relevant nvm sections */ @@ -554,7 +540,7 @@ int iwl_mvm_init_mcc(struct iwl_mvm *mvm) struct ieee80211_regdomain *regd; char mcc[3]; - if (mvm->cfg->nvm_type == IWL_NVM_EXT) { + if (mvm->trans->cfg->nvm_type == IWL_NVM_EXT) { tlv_lar = fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_LAR_SUPPORT); nvm_lar = mvm->nvm_data->lar_enabled; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c index 984f407f7027..a2dc5c3b0596 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/ops.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -92,11 +92,11 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) IWL_DEBUG_INFO(mvm, "Radio type=0x%x-0x%x-0x%x\n", radio_cfg_type, radio_cfg_step, radio_cfg_dash); - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) return; /* SKU control */ - reg_val = CSR_HW_REV_STEP_DASH(mvm->trans->hw_rev); + reg_val = CSR_HW_REV_STEP_DASH(mvm->trans->info.hw_rev); /* radio configuration */ reg_val |= radio_cfg_type << CSR_HW_IF_CONFIG_REG_POS_PHY_TYPE; @@ -114,7 +114,7 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) * unrelated errors. Need to further investigate this, but for now * we'll separate cases. */ - if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) + if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000) reg_val |= CSR_HW_IF_CONFIG_REG_BIT_RADIO_SI; if (iwl_fw_dbg_is_d3_debug_enabled(&mvm->fwrt)) @@ -135,7 +135,7 @@ static void iwl_mvm_nic_config(struct iwl_op_mode *op_mode) * (PCIe power is lost before PERST# is asserted), causing ME FW * to lose ownership and not being able to obtain it back. */ - if (!mvm->trans->cfg->apmg_not_supported) + if (!mvm->trans->mac_cfg->base->apmg_not_supported) iwl_set_bits_mask_prph(mvm->trans, APMG_PS_CTRL_REG, APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS, ~APMG_PS_CTRL_EARLY_PWR_OFF_RESET_DIS); @@ -145,7 +145,7 @@ static void iwl_mvm_rx_esr_mode_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_mvm_esr_mode_notif *notif = (void *)pkt->data; + struct iwl_esr_mode_notif *notif = (void *)pkt->data; struct ieee80211_vif *vif = iwl_mvm_get_bss_vif(mvm); /* FW recommendations is only for entering EMLSR */ @@ -495,7 +495,7 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = { RX_HANDLER_GRP(DATA_PATH_GROUP, ESR_MODE_NOTIF, iwl_mvm_rx_esr_mode_notif, RX_HANDLER_ASYNC_LOCKED_WIPHY, - struct iwl_mvm_esr_mode_notif), + struct iwl_esr_mode_notif), RX_HANDLER_GRP(DATA_PATH_GROUP, MONITOR_NOTIF, iwl_mvm_rx_monitor_notif, RX_HANDLER_ASYNC_LOCKED, @@ -777,7 +777,8 @@ static const struct iwl_hcmd_names iwl_mvm_bt_coex_names[] = { HCMD_NAME(PROFILE_NOTIF), }; -static const struct iwl_hcmd_arr iwl_mvm_groups[] = { +VISIBLE_IF_IWLWIFI_KUNIT +const struct iwl_hcmd_arr iwl_mvm_groups[] = { [LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names), [LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names), [SYSTEM_GROUP] = HCMD_ARR(iwl_mvm_system_names), @@ -793,6 +794,11 @@ static const struct iwl_hcmd_arr iwl_mvm_groups[] = { [DEBUG_GROUP] = HCMD_ARR(iwl_mvm_debug_names), [STATISTICS_GROUP] = HCMD_ARR(iwl_mvm_statistics_names), }; +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_groups); +#if IS_ENABLED(CONFIG_IWLWIFI_KUNIT_TESTS) +const unsigned int iwl_mvm_groups_size = ARRAY_SIZE(iwl_mvm_groups); +EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mvm_groups_size); +#endif /* this forward declaration can avoid to export the function */ static void iwl_mvm_async_handlers_wk(struct work_struct *wk); @@ -1272,13 +1278,12 @@ static void iwl_mvm_trig_link_selection(struct wiphy *wiphy, } static struct iwl_op_mode * -iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, +iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg, const struct iwl_fw *fw, struct dentry *dbgfs_dir) { struct ieee80211_hw *hw; struct iwl_op_mode *op_mode; struct iwl_mvm *mvm; - struct iwl_trans_config trans_cfg = {}; static const u8 no_reclaim_cmds[] = { TX_CMD, }; @@ -1286,6 +1291,7 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, size_t scan_size; u32 min_backoff; struct iwl_mvm_csme_conn_info *csme_conn_info __maybe_unused; + int ratecheck; int err; /* @@ -1306,18 +1312,13 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, if (!hw) return ERR_PTR(-ENOMEM); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) max_agg = 512; else max_agg = IEEE80211_MAX_AMPDU_BUF_HE; hw->max_rx_aggregation_subframes = max_agg; - if (cfg->max_tx_agg_size) - hw->max_tx_aggregation_subframes = cfg->max_tx_agg_size; - else - hw->max_tx_aggregation_subframes = max_agg; - op_mode = hw->priv; mvm = IWL_OP_MODE_GET_MVM(op_mode); @@ -1337,19 +1338,58 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, mvm->init_status = 0; + /* start with v1 rates */ + mvm->fw_rates_ver = 1; + + /* check for rates version 2 */ + ratecheck = + (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) >= 8) + + (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP, + TLC_MNG_UPDATE_NOTIF, 0) >= 3) + + (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, + REPLY_RX_MPDU_CMD, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) >= 6); + if (ratecheck != 0 && ratecheck != 4) { + IWL_ERR(mvm, "Firmware has inconsistent rates\n"); + err = -EINVAL; + goto out_free; + } + if (ratecheck == 4) + mvm->fw_rates_ver = 2; + + /* check for rates version 3 */ + ratecheck = + (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) >= 11) + + (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP, + TLC_MNG_UPDATE_NOTIF, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, + REPLY_RX_MPDU_CMD, 0) >= 6) + + (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP, + RX_NO_DATA_NOTIF, 0) >= 4) + + (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) >= 9); + if (ratecheck != 0 && ratecheck != 5) { + IWL_ERR(mvm, "Firmware has inconsistent rates\n"); + err = -EINVAL; + goto out_free; + } + if (ratecheck == 5) + mvm->fw_rates_ver = 3; + + trans->conf.rx_mpdu_cmd = REPLY_RX_MPDU_CMD; + if (iwl_mvm_has_new_rx_api(mvm)) { op_mode->ops = &iwl_mvm_ops_mq; - trans->rx_mpdu_cmd_hdr_size = - (trans->trans_cfg->device_family >= + trans->conf.rx_mpdu_cmd_hdr_size = + (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) ? sizeof(struct iwl_rx_mpdu_desc) : IWL_RX_DESC_SIZE_V1; } else { op_mode->ops = &iwl_mvm_ops; - trans->rx_mpdu_cmd_hdr_size = + trans->conf.rx_mpdu_cmd_hdr_size = sizeof(struct iwl_rx_mpdu_res_start); - if (WARN_ON(trans->num_rx_queues > 1)) { + if (WARN_ON(trans->info.num_rxqs > 1)) { err = -EINVAL; goto out_free; } @@ -1437,53 +1477,50 @@ iwl_op_mode_mvm_start(struct iwl_trans *trans, const struct iwl_cfg *cfg, * Populate the state variables that the transport layer needs * to know about. */ - trans_cfg.op_mode = op_mode; - trans_cfg.no_reclaim_cmds = no_reclaim_cmds; - trans_cfg.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); + BUILD_BUG_ON(sizeof(no_reclaim_cmds) > + sizeof(trans->conf.no_reclaim_cmds)); + memcpy(trans->conf.no_reclaim_cmds, no_reclaim_cmds, + sizeof(no_reclaim_cmds)); + trans->conf.n_no_reclaim_cmds = ARRAY_SIZE(no_reclaim_cmds); - trans_cfg.rx_buf_size = iwl_amsdu_size_to_rxb_size(); + trans->conf.rx_buf_size = iwl_amsdu_size_to_rxb_size(); - trans->wide_cmd_header = true; - trans_cfg.bc_table_dword = - mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210; + trans->conf.wide_cmd_header = true; - trans_cfg.command_groups = iwl_mvm_groups; - trans_cfg.command_groups_size = ARRAY_SIZE(iwl_mvm_groups); + trans->conf.command_groups = iwl_mvm_groups; + trans->conf.command_groups_size = ARRAY_SIZE(iwl_mvm_groups); - trans_cfg.cmd_queue = IWL_MVM_DQA_CMD_QUEUE; - trans_cfg.cmd_fifo = IWL_MVM_TX_FIFO_CMD; - trans_cfg.scd_set_active = true; + trans->conf.cmd_queue = IWL_MVM_DQA_CMD_QUEUE; + trans->conf.cmd_fifo = IWL_MVM_TX_FIFO_CMD; + trans->conf.scd_set_active = true; - trans_cfg.cb_data_offs = offsetof(struct ieee80211_tx_info, - driver_data[2]); + trans->conf.cb_data_offs = offsetof(struct ieee80211_tx_info, + driver_data[2]); snprintf(mvm->hw->wiphy->fw_version, sizeof(mvm->hw->wiphy->fw_version), "%.31s", fw->fw_version); - trans_cfg.fw_reset_handshake = fw_has_capa(&mvm->fw->ucode_capa, - IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE); + trans->conf.fw_reset_handshake = + fw_has_capa(&mvm->fw->ucode_capa, + IWL_UCODE_TLV_CAPA_FW_RESET_HANDSHAKE); - trans_cfg.queue_alloc_cmd_ver = + trans->conf.queue_alloc_cmd_ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(DATA_PATH_GROUP, SCD_QUEUE_CONFIG_CMD), 0); mvm->sta_remove_requires_queue_remove = - trans_cfg.queue_alloc_cmd_ver > 0; + trans->conf.queue_alloc_cmd_ver > 0; mvm->mld_api_is_used = iwl_mvm_has_mld_api(mvm->fw); /* Configure transport layer */ - iwl_trans_configure(mvm->trans, &trans_cfg); + iwl_trans_op_mode_enter(mvm->trans, op_mode); - trans->rx_mpdu_cmd = REPLY_RX_MPDU_CMD; trans->dbg.dest_tlv = mvm->fw->dbg.dest_tlv; trans->dbg.n_dest_reg = mvm->fw->dbg.n_dest_reg; - trans->iml = mvm->fw->iml; - trans->iml_len = mvm->fw->iml_len; - /* set up notification wait support */ iwl_notification_wait_init(&mvm->notif_wait); @@ -2100,7 +2137,7 @@ static bool iwl_mvm_sw_reset(struct iwl_op_mode *op_mode, mvm->fwrt.trans->dbg.restart_required = false; ieee80211_restart_hw(mvm->hw); return true; - } else if (mvm->trans->trans_cfg->device_family <= IWL_DEVICE_FAMILY_8000) { + } else if (mvm->trans->mac_cfg->device_family <= IWL_DEVICE_FAMILY_8000) { ieee80211_restart_hw(mvm->hw); return true; } @@ -2125,7 +2162,6 @@ static void iwl_op_mode_mvm_device_powered_off(struct iwl_op_mode *op_mode) mutex_lock(&mvm->mutex); clear_bit(IWL_MVM_STATUS_IN_D3, &mvm->status); - mvm->trans->system_pm_mode = IWL_PLAT_PM_MODE_DISABLED; iwl_mvm_stop_device(mvm); mvm->fast_resume = false; mutex_unlock(&mvm->mutex); @@ -2165,7 +2201,7 @@ static void iwl_mvm_rx_mq_rss(struct iwl_op_mode *op_mode, struct iwl_rx_packet *pkt = rxb_addr(rxb); u16 cmd = WIDE_ID(pkt->hdr.group_id, pkt->hdr.cmd); - if (unlikely(queue >= mvm->trans->num_rx_queues)) + if (unlikely(queue >= mvm->trans->info.num_rxqs)) return; if (unlikely(cmd == WIDE_ID(LEGACY_GROUP, FRAME_RELEASE))) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/power.c b/drivers/net/wireless/intel/iwlwifi/mvm/power.c index a386b315e52f..0057fddf88f0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/power.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/power.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2019, 2021-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2019, 2021-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -376,6 +376,9 @@ static void iwl_mvm_power_build_cmd(struct iwl_mvm *mvm, if (!vif->cfg.ps || !mvmvif->pm_enabled) return; + if (iwl_fw_lookup_cmd_ver(mvm->fw, MAC_PM_POWER_TABLE, 0) >= 2) + cmd->flags |= cpu_to_le16(POWER_FLAGS_ENABLE_SMPS_MSK); + if (iwl_mvm_vif_low_latency(mvmvif) && vif->p2p && (!fw_has_capa(&mvm->fw->ucode_capa, IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS) || diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c index aad2614af9ad..798a7e4bea83 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/quota.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/quota.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018, 2021-2022 Intel Corporation + * Copyright (C) 2012-2014, 2018, 2021-2022, 2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -86,45 +86,6 @@ static void iwl_mvm_quota_iterator(void *_data, u8 *mac, } } -static void iwl_mvm_adjust_quota_for_noa(struct iwl_mvm *mvm, - struct iwl_time_quota_cmd *cmd) -{ -#ifdef CONFIG_NL80211_TESTMODE - struct iwl_mvm_vif *mvmvif; - int i, phy_id = -1, beacon_int = 0; - - if (!mvm->noa_duration || !mvm->noa_vif) - return; - - mvmvif = iwl_mvm_vif_from_mac80211(mvm->noa_vif); - if (!mvmvif->ap_ibss_active) - return; - - phy_id = mvmvif->deflink.phy_ctxt->id; - beacon_int = mvm->noa_vif->bss_conf.beacon_int; - - for (i = 0; i < MAX_BINDINGS; i++) { - struct iwl_time_quota_data *data = - iwl_mvm_quota_cmd_get_quota(mvm, cmd, - i); - u32 id_n_c = le32_to_cpu(data->id_and_color); - u32 id = (id_n_c & FW_CTXT_ID_MSK) >> FW_CTXT_ID_POS; - u32 quota = le32_to_cpu(data->quota); - - if (id != phy_id) - continue; - - quota *= (beacon_int - mvm->noa_duration); - quota /= beacon_int; - - IWL_DEBUG_QUOTA(mvm, "quota: adjust for NoA from %d to %d\n", - le32_to_cpu(data->quota), quota); - - data->quota = cpu_to_le32(quota); - } -#endif -} - int iwl_mvm_update_quotas(struct iwl_mvm *mvm, bool force_update, struct ieee80211_vif *disabled_vif) @@ -260,8 +221,6 @@ int iwl_mvm_update_quotas(struct iwl_mvm *mvm, } } - iwl_mvm_adjust_quota_for_noa(mvm, &cmd); - /* check that we have non-zero quota for all valid bindings */ for (i = 0; i < MAX_BINDINGS; i++) { qdata = iwl_mvm_quota_cmd_get_quota(mvm, &cmd, i); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c index de5ac000272e..89ac4c6b3e54 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs-fw.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include "rs.h" #include "fw-api.h" @@ -72,7 +72,7 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, u16 flags = 0; /* get STBC flags */ - if (mvm->cfg->ht_params->stbc && + if (mvm->cfg->ht_params.stbc && (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1)) { if (he_cap->has_he && he_cap->he_cap_elem.phy_cap_info[2] & IEEE80211_HE_PHY_CAP2_STBC_RX_UNDER_80MHZ) @@ -83,7 +83,7 @@ static u16 rs_fw_get_config_flags(struct iwl_mvm *mvm, flags |= IWL_TLC_MNG_CFG_FLAGS_STBC_MSK; } - if (mvm->cfg->ht_params->ldpc && + if (mvm->cfg->ht_params.ldpc && ((ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING) || (vht_ena && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)))) flags |= IWL_TLC_MNG_CFG_FLAGS_LDPC_MSK; @@ -454,22 +454,11 @@ void iwl_mvm_tlc_update_notif(struct iwl_mvm *mvm, if (flags & IWL_TLC_NOTIF_FLAG_RATE) { char pretty_rate[100]; - if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP, - TLC_MNG_UPDATE_NOTIF, 0) < 3) { - rs_pretty_print_rate_v1(pretty_rate, - sizeof(pretty_rate), - le32_to_cpu(notif->rate)); - IWL_DEBUG_RATE(mvm, - "Got rate in old format. Rate: %s. Converting.\n", - pretty_rate); - lq_sta->last_rate_n_flags = - iwl_new_rate_from_v1(le32_to_cpu(notif->rate)); - } else { - lq_sta->last_rate_n_flags = le32_to_cpu(notif->rate); - } + lq_sta->last_rate_n_flags = + iwl_mvm_v3_rate_from_fw(notif->rate, mvm->fw_rates_ver); rs_pretty_print_rate(pretty_rate, sizeof(pretty_rate), lq_sta->last_rate_n_flags); - IWL_DEBUG_RATE(mvm, "new rate: %s\n", pretty_rate); + IWL_DEBUG_RATE(mvm, "rate: %s\n", pretty_rate); } if (flags & IWL_TLC_NOTIF_FLAG_AMSDU && !mvm_link_sta->orig_amsdu_len) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c index a8c4e354e2ce..5802ed80a9ca 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.c @@ -2,6 +2,7 @@ /****************************************************************************** * * Copyright(c) 2005 - 2014, 2018 - 2023 Intel Corporation. All rights reserved. + * Copyright(c) 2025 Intel Corporation * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH * Copyright(c) 2016 - 2017 Intel Deutschland GmbH *****************************************************************************/ @@ -895,7 +896,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, WARN_ON_ONCE(1); } } else if (ucode_rate & RATE_MCS_VHT_MSK_V1) { - nss = FIELD_GET(RATE_MCS_NSS_MSK, ucode_rate) + 1; + nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, ucode_rate) + 1; if (nss == 1) { rate->type = LQ_VHT_SISO; @@ -909,7 +910,7 @@ static int rs_rate_from_ucode_rate(const u32 ucode_rate, WARN_ON_ONCE(1); } } else if (ucode_rate & RATE_MCS_HE_MSK_V1) { - nss = FIELD_GET(RATE_MCS_NSS_MSK, ucode_rate) + 1; + nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, ucode_rate) + 1; if (nss == 1) { rate->type = LQ_HE_SISO; @@ -1783,7 +1784,7 @@ static enum rs_action rs_get_rate_action(struct iwl_mvm *mvm, if ((high_tpt != IWL_INVALID_VALUE) && (high_tpt > current_tpt)) { IWL_DEBUG_RATE(mvm, - "Higher rate is better. Increate rate\n"); + "Higher rate is better. Increase rate\n"); return RS_ACTION_UPSCALE; } @@ -2696,8 +2697,10 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, lq_sta = mvm_sta; spin_lock_bh(&lq_sta->pers.lock); - iwl_mvm_hwrate_to_tx_rate_v1(lq_sta->last_rate_n_flags, - info->band, &info->control.rates[0]); + iwl_mvm_hwrate_to_tx_rate(iwl_mvm_v3_rate_from_fw( + cpu_to_le32(lq_sta->last_rate_n_flags), + 1), + info->band, &info->control.rates[0]); info->control.rates[0].count = 1; /* Report the optimal rate based on rssi and STA caps if we haven't @@ -2707,8 +2710,12 @@ static void rs_drv_get_rate(void *mvm_r, struct ieee80211_sta *sta, optimal_rate = rs_get_optimal_rate(mvm, lq_sta); last_ucode_rate = ucode_rate_from_rs_rate(mvm, optimal_rate); - iwl_mvm_hwrate_to_tx_rate_v1(last_ucode_rate, info->band, - &txrc->reported_rate); + last_ucode_rate = + iwl_mvm_v3_rate_from_fw(cpu_to_le32(last_ucode_rate), + 1); + iwl_mvm_hwrate_to_tx_rate(last_ucode_rate, info->band, + &txrc->reported_rate); + txrc->reported_rate.count = 1; } spin_unlock_bh(&lq_sta->pers.lock); } @@ -2813,11 +2820,11 @@ static void rs_ht_init(struct iwl_mvm *mvm, lq_sta->active_mimo2_rate &= ~((u16)0x2); lq_sta->active_mimo2_rate <<= IWL_FIRST_OFDM_RATE; - if (mvm->cfg->ht_params->ldpc && + if (mvm->cfg->ht_params.ldpc && (ht_cap->cap & IEEE80211_HT_CAP_LDPC_CODING)) lq_sta->ldpc = true; - if (mvm->cfg->ht_params->stbc && + if (mvm->cfg->ht_params.stbc && (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && (ht_cap->cap & IEEE80211_HT_CAP_RX_STBC)) lq_sta->stbc_capable = true; @@ -2832,11 +2839,11 @@ static void rs_vht_init(struct iwl_mvm *mvm, { rs_vht_set_enabled_rates(sta, vht_cap, lq_sta); - if (mvm->cfg->ht_params->ldpc && + if (mvm->cfg->ht_params.ldpc && (vht_cap->cap & IEEE80211_VHT_CAP_RXLDPC)) lq_sta->ldpc = true; - if (mvm->cfg->ht_params->stbc && + if (mvm->cfg->ht_params.stbc && (num_of_ant(iwl_mvm_get_valid_tx_ant(mvm)) > 1) && (vht_cap->cap & IEEE80211_VHT_CAP_RXSTBC_MASK)) lq_sta->stbc_capable = true; @@ -2887,10 +2894,10 @@ void iwl_mvm_update_frame_stats(struct iwl_mvm *mvm, u32 rate, bool agg) if (rate & RATE_MCS_HT_MSK_V1) { mvm->drv_rx_stats.ht_frames++; - nss = ((rate & RATE_HT_MCS_NSS_MSK_V1) >> RATE_HT_MCS_NSS_POS_V1) + 1; + nss = FIELD_GET(RATE_HT_MCS_MIMO2_MSK, rate) + 1; } else if (rate & RATE_MCS_VHT_MSK_V1) { mvm->drv_rx_stats.vht_frames++; - nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1; + nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, rate) + 1; } else { mvm->drv_rx_stats.legacy_frames++; } @@ -3304,7 +3311,7 @@ static void rs_build_rates_table_from_fixed(struct iwl_mvm *mvm, if (num_of_ant(ant) == 1) lq_cmd->single_stream_ant_msk = ant; - if (!mvm->trans->trans_cfg->gen2) + if (!mvm->trans->mac_cfg->gen2) lq_cmd->agg_frame_cnt_limit = LINK_QUAL_AGG_FRAME_LIMIT_DEF; else lq_cmd->agg_frame_cnt_limit = @@ -3673,16 +3680,15 @@ int rs_pretty_print_rate_v1(char *buf, int bufsz, const u32 rate) if (rate & RATE_MCS_VHT_MSK_V1) { type = "VHT"; mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; - nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1; + nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, rate) + 1; } else if (rate & RATE_MCS_HT_MSK_V1) { type = "HT"; mcs = rate & RATE_HT_MCS_INDEX_MSK_V1; - nss = ((rate & RATE_HT_MCS_NSS_MSK_V1) - >> RATE_HT_MCS_NSS_POS_V1) + 1; + nss = FIELD_GET(RATE_HT_MCS_MIMO2_MSK, rate) + 1; } else if (rate & RATE_MCS_HE_MSK_V1) { type = "HE"; mcs = rate & RATE_VHT_MCS_RATE_CODE_MSK; - nss = FIELD_GET(RATE_MCS_NSS_MSK, rate) + 1; + nss = FIELD_GET(RATE_VHT_MCS_NSS_MSK, rate) + 1; } else { type = "Unknown"; /* shouldn't happen */ } @@ -4172,3 +4178,167 @@ int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, else return rs_drv_tx_protection(mvm, mvmsta, enable); } + +static u32 iwl_legacy_rate_to_fw_idx(u32 rate_n_flags) +{ + int rate = rate_n_flags & RATE_LEGACY_RATE_MSK_V1; + int idx; + bool ofdm = !(rate_n_flags & RATE_MCS_CCK_MSK_V1); + int offset = ofdm ? IWL_FIRST_OFDM_RATE : 0; + int last = ofdm ? IWL_RATE_COUNT_LEGACY : IWL_FIRST_OFDM_RATE; + + for (idx = offset; idx < last; idx++) + if (iwl_fw_rate_idx_to_plcp(idx) == rate) + return idx - offset; + return IWL_RATE_INVALID; +} + +u32 iwl_mvm_v3_rate_from_fw(__le32 rate, u8 rate_ver) +{ + u32 rate_v3 = 0, rate_v1; + u32 dup = 0; + + if (rate_ver > 1) + return iwl_v3_rate_from_v2_v3(rate, rate_ver >= 3); + + rate_v1 = le32_to_cpu(rate); + if (rate_v1 == 0) + return rate_v1; + /* convert rate */ + if (rate_v1 & RATE_MCS_HT_MSK_V1) { + u32 nss; + + rate_v3 |= RATE_MCS_MOD_TYPE_HT; + rate_v3 |= + rate_v1 & RATE_HT_MCS_RATE_CODE_MSK_V1; + nss = u32_get_bits(rate_v1, RATE_HT_MCS_MIMO2_MSK); + rate_v3 |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); + } else if (rate_v1 & RATE_MCS_VHT_MSK_V1 || + rate_v1 & RATE_MCS_HE_MSK_V1) { + u32 nss = u32_get_bits(rate_v1, RATE_VHT_MCS_NSS_MSK); + + rate_v3 |= rate_v1 & RATE_VHT_MCS_RATE_CODE_MSK; + + rate_v3 |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); + + if (rate_v1 & RATE_MCS_HE_MSK_V1) { + u32 he_type_bits = rate_v1 & RATE_MCS_HE_TYPE_MSK_V1; + u32 he_type = he_type_bits >> RATE_MCS_HE_TYPE_POS_V1; + u32 he_106t = (rate_v1 & RATE_MCS_HE_106T_MSK_V1) >> + RATE_MCS_HE_106T_POS_V1; + u32 he_gi_ltf = (rate_v1 & RATE_MCS_HE_GI_LTF_MSK_V1) >> + RATE_MCS_HE_GI_LTF_POS; + + if ((he_type_bits == RATE_MCS_HE_TYPE_SU || + he_type_bits == RATE_MCS_HE_TYPE_EXT_SU) && + he_gi_ltf == RATE_MCS_HE_SU_4_LTF) + /* the new rate have an additional bit to + * represent the value 4 rather then using SGI + * bit for this purpose - as it was done in the + * old rate + */ + he_gi_ltf += (rate_v1 & RATE_MCS_SGI_MSK_V1) >> + RATE_MCS_SGI_POS_V1; + + rate_v3 |= he_gi_ltf << RATE_MCS_HE_GI_LTF_POS; + rate_v3 |= he_type << RATE_MCS_HE_TYPE_POS; + rate_v3 |= he_106t << RATE_MCS_HE_106T_POS; + rate_v3 |= rate_v1 & RATE_HE_DUAL_CARRIER_MODE_MSK; + rate_v3 |= RATE_MCS_MOD_TYPE_HE; + } else { + rate_v3 |= RATE_MCS_MOD_TYPE_VHT; + } + /* if legacy format */ + } else { + u32 legacy_rate = iwl_legacy_rate_to_fw_idx(rate_v1); + + if (WARN_ON_ONCE(legacy_rate == IWL_RATE_INVALID)) + legacy_rate = (rate_v1 & RATE_MCS_CCK_MSK_V1) ? + IWL_FIRST_CCK_RATE : IWL_FIRST_OFDM_RATE; + + rate_v3 |= legacy_rate; + if (!(rate_v1 & RATE_MCS_CCK_MSK_V1)) + rate_v3 |= RATE_MCS_MOD_TYPE_LEGACY_OFDM; + } + + /* convert flags */ + if (rate_v1 & RATE_MCS_LDPC_MSK_V1) + rate_v3 |= RATE_MCS_LDPC_MSK; + rate_v3 |= (rate_v1 & RATE_MCS_CHAN_WIDTH_MSK_V1) | + (rate_v1 & RATE_MCS_ANT_AB_MSK) | + (rate_v1 & RATE_MCS_STBC_MSK) | + (rate_v1 & RATE_MCS_BF_MSK); + + dup = (rate_v1 & RATE_MCS_DUP_MSK_V1) >> RATE_MCS_DUP_POS_V1; + if (dup) { + rate_v3 |= RATE_MCS_DUP_MSK; + rate_v3 |= dup << RATE_MCS_CHAN_WIDTH_POS; + } + + if ((!(rate_v1 & RATE_MCS_HE_MSK_V1)) && + (rate_v1 & RATE_MCS_SGI_MSK_V1)) + rate_v3 |= RATE_MCS_SGI_MSK; + + return rate_v3; +} + +__le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver) +{ + u32 result = 0; + int rate_idx; + + if (rate_ver > 1) + return iwl_v3_rate_to_v2_v3(rate, rate_ver > 2); + + switch (rate & RATE_MCS_MOD_TYPE_MSK) { + case RATE_MCS_MOD_TYPE_CCK: + result = RATE_MCS_CCK_MSK_V1; + fallthrough; + case RATE_MCS_MOD_TYPE_LEGACY_OFDM: + rate_idx = u32_get_bits(rate, RATE_LEGACY_RATE_MSK); + if (!(result & RATE_MCS_CCK_MSK_V1)) + rate_idx += IWL_FIRST_OFDM_RATE; + result |= u32_encode_bits(iwl_fw_rate_idx_to_plcp(rate_idx), + RATE_LEGACY_RATE_MSK_V1); + break; + case RATE_MCS_MOD_TYPE_HT: + result = RATE_MCS_HT_MSK_V1; + result |= u32_encode_bits(u32_get_bits(rate, + RATE_HT_MCS_CODE_MSK), + RATE_HT_MCS_RATE_CODE_MSK_V1); + result |= u32_encode_bits(u32_get_bits(rate, + RATE_MCS_NSS_MSK), + RATE_HT_MCS_MIMO2_MSK); + break; + case RATE_MCS_MOD_TYPE_VHT: + result = RATE_MCS_VHT_MSK_V1; + result |= u32_encode_bits(u32_get_bits(rate, + RATE_VHT_MCS_NSS_MSK), + RATE_MCS_CODE_MSK); + result |= u32_encode_bits(u32_get_bits(rate, RATE_MCS_NSS_MSK), + RATE_VHT_MCS_NSS_MSK); + break; + case RATE_MCS_MOD_TYPE_HE: /* not generated */ + default: + WARN_ONCE(1, "bad modulation type %d\n", + u32_get_bits(rate, RATE_MCS_MOD_TYPE_MSK)); + return 0; + } + + if (rate & RATE_MCS_LDPC_MSK) + result |= RATE_MCS_LDPC_MSK_V1; + WARN_ON_ONCE(u32_get_bits(rate, RATE_MCS_CHAN_WIDTH_MSK) > + RATE_MCS_CHAN_WIDTH_160_VAL); + result |= (rate & RATE_MCS_CHAN_WIDTH_MSK_V1) | + (rate & RATE_MCS_ANT_AB_MSK) | + (rate & RATE_MCS_STBC_MSK) | + (rate & RATE_MCS_BF_MSK); + + /* not handling DUP since we don't use it */ + WARN_ON_ONCE(rate & RATE_MCS_DUP_MSK); + + if (rate & RATE_MCS_SGI_MSK) + result |= RATE_MCS_SGI_MSK_V1; + + return cpu_to_le32(result); +} diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h index ea81cb236d5c..69259ebb966b 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rs.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rs.h @@ -3,7 +3,7 @@ * * Copyright(c) 2015 Intel Mobile Communications GmbH * Copyright(c) 2017 Intel Deutschland GmbH - * Copyright (C) 2003 - 2014, 2018 - 2024 Intel Corporation + * Copyright (C) 2003 - 2014, 2018 - 2025 Intel Corporation *****************************************************************************/ #ifndef __rs_h__ @@ -424,6 +424,9 @@ void iwl_mvm_rate_control_unregister(void); struct iwl_mvm_sta; +u32 iwl_mvm_v3_rate_from_fw(__le32 rate, u8 rate_ver); +__le32 iwl_mvm_v3_rate_to_fw(u32 rate, u8 rate_ver); + int iwl_mvm_tx_protection(struct iwl_mvm *mvm, struct iwl_mvm_sta *mvmsta, bool enable); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c index 2dbef7b46355..8eb0aa448c85 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -490,8 +490,6 @@ void iwl_mvm_rx_rx_mpdu(struct iwl_mvm *mvm, struct napi_struct *napi, if (!(rate_n_flags & RATE_MCS_CCK_MSK_V1) && rate_n_flags & RATE_MCS_SGI_MSK_V1) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; - if (rate_n_flags & RATE_HT_MCS_GF_MSK) - rx_status->enc_flags |= RX_ENC_FLAG_HT_GF; if (rate_n_flags & RATE_MCS_LDPC_MSK_V1) rx_status->enc_flags |= RX_ENC_FLAG_LDPC; if (rate_n_flags & RATE_MCS_HT_MSK_V1) { @@ -1001,7 +999,7 @@ static void iwl_mvm_update_esr_mode_tpt(struct iwl_mvm *mvm) sec_link = mvmvif->link[sec_link]->fw_link_id; /* Sum up RX and TX MPDUs from the different queues/links */ - for (int q = 0; q < mvm->trans->num_rx_queues; q++) { + for (int q = 0; q < mvm->trans->info.num_rxqs; q++) { spin_lock_bh(&mvmsta->mpdu_counters[q].lock); /* The link IDs that doesn't exist will contain 0 */ diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c index 09fd8752046e..077aadbf95db 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/rxmq.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -171,7 +171,7 @@ static int iwl_mvm_create_skb(struct iwl_mvm *mvm, struct sk_buff *skb, shdr->type != htons(ETH_P_PAE) && shdr->type != htons(ETH_P_TDLS)))) skb->ip_summed = CHECKSUM_NONE; - else if (mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + else if (mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ) /* mac80211 assumes full CSUM including SNAP header */ skb_postpush_rcsum(skb, shdr, sizeof(*shdr)); } @@ -409,7 +409,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta, !(status & IWL_RX_MPDU_RES_STATUS_TTAK_OK)) return 0; - if (mvm->trans->trans_cfg->gen2 && + if (mvm->trans->mac_cfg->gen2 && !(status & RX_MPDU_RES_STATUS_MIC_OK)) stats->flag |= RX_FLAG_MMIC_ERROR; @@ -426,7 +426,7 @@ static int iwl_mvm_rx_crypto(struct iwl_mvm *mvm, struct ieee80211_sta *sta, if (pkt_flags & FH_RSCSR_RADA_EN) { stats->flag |= RX_FLAG_ICV_STRIPPED; - if (mvm->trans->trans_cfg->gen2) + if (mvm->trans->mac_cfg->gen2) stats->flag |= RX_FLAG_MMIC_STRIPPED; } @@ -462,7 +462,7 @@ static void iwl_mvm_rx_csum(struct iwl_mvm *mvm, { struct iwl_rx_mpdu_desc *desc = (void *)pkt->data; - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { if (pkt->len_n_flags & cpu_to_le32(FH_RSCSR_RPA_EN)) { u16 hwsum = be16_to_cpu(desc->v3.raw_xsum); @@ -744,7 +744,7 @@ static bool iwl_mvm_reorder(struct iwl_mvm *mvm, baid = (reorder & IWL_RX_MPDU_REORDER_BAID_MASK) >> IWL_RX_MPDU_REORDER_BAID_SHIFT; - if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000) + if (mvm->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000) return false; /* @@ -995,7 +995,7 @@ iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data, */ u8 ru = le32_get_bits(phy_data->d1, IWL_RX_PHY_DATA1_HE_RU_ALLOC_MASK); u32 rate_n_flags = phy_data->rate_n_flags; - u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK_V1; + u32 he_type = rate_n_flags & RATE_MCS_HE_TYPE_MSK; u8 offs = 0; rx_status->bw = RATE_INFO_BW_HE_RU; @@ -1050,13 +1050,13 @@ iwl_mvm_decode_he_phy_ru_alloc(struct iwl_mvm_rx_phy_data *phy_data, if (he_mu) he_mu->flags2 |= - le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK_V1, + le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, rate_n_flags), IEEE80211_RADIOTAP_HE_MU_FLAGS2_BW_FROM_SIG_A_BW); - else if (he_type == RATE_MCS_HE_TYPE_TRIG_V1) + else if (he_type == RATE_MCS_HE_TYPE_TRIG) he->data6 |= cpu_to_le16(IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW_KNOWN) | - le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK_V1, + le16_encode_bits(FIELD_GET(RATE_MCS_CHAN_WIDTH_MSK, rate_n_flags), IEEE80211_RADIOTAP_HE_DATA6_TB_PPDU_BW); } @@ -1944,7 +1944,7 @@ static void iwl_mvm_rx_fill_status(struct iwl_mvm *mvm, } /* must be before L-SIG data */ - if (format == RATE_MCS_HE_MSK) + if (format == RATE_MCS_MOD_TYPE_HE) iwl_mvm_rx_he(mvm, skb, phy_data, queue); iwl_mvm_decode_lsig(skb, phy_data); @@ -1966,45 +1966,45 @@ static void iwl_mvm_rx_fill_status(struct iwl_mvm *mvm, phy_data->energy_a, phy_data->energy_b); /* using TLV format and must be after all fixed len fields */ - if (format == RATE_MCS_EHT_MSK) + if (format == RATE_MCS_MOD_TYPE_EHT) iwl_mvm_rx_eht(mvm, skb, phy_data, queue); if (unlikely(mvm->monitor_on)) iwl_mvm_add_rtap_sniffer_config(mvm, skb); - is_sgi = format == RATE_MCS_HE_MSK ? + is_sgi = format == RATE_MCS_MOD_TYPE_HE ? iwl_he_is_sgi(rate_n_flags) : rate_n_flags & RATE_MCS_SGI_MSK; - if (!(format == RATE_MCS_CCK_MSK) && is_sgi) + if (!(format == RATE_MCS_MOD_TYPE_CCK) && is_sgi) rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; if (rate_n_flags & RATE_MCS_LDPC_MSK) rx_status->enc_flags |= RX_ENC_FLAG_LDPC; switch (format) { - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: rx_status->encoding = RX_ENC_VHT; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: rx_status->encoding = RX_ENC_HE; rx_status->he_dcm = !!(rate_n_flags & RATE_HE_DUAL_CARRIER_MODE_MSK); break; - case RATE_MCS_EHT_MSK: + case RATE_MCS_MOD_TYPE_EHT: rx_status->encoding = RX_ENC_EHT; break; } switch (format) { - case RATE_MCS_HT_MSK: + case RATE_MCS_MOD_TYPE_HT: rx_status->encoding = RX_ENC_HT; rx_status->rate_idx = RATE_HT_MCS_INDEX(rate_n_flags); rx_status->enc_flags |= stbc << RX_ENC_FLAG_STBC_SHIFT; break; - case RATE_MCS_VHT_MSK: - case RATE_MCS_HE_MSK: - case RATE_MCS_EHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: + case RATE_MCS_MOD_TYPE_HE: + case RATE_MCS_MOD_TYPE_EHT: rx_status->nss = u32_get_bits(rate_n_flags, RATE_MCS_NSS_MSK) + 1; rx_status->rate_idx = rate_n_flags & RATE_MCS_CODE_MSK; @@ -2048,7 +2048,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (unlikely(test_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status))) return; - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) desc_size = sizeof(*desc); else desc_size = IWL_RX_DESC_SIZE_V1; @@ -2058,8 +2058,10 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, return; } - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { - phy_data.rate_n_flags = le32_to_cpu(desc->v3.rate_n_flags); + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + phy_data.rate_n_flags = + iwl_mvm_v3_rate_from_fw(desc->v3.rate_n_flags, + mvm->fw_rates_ver); phy_data.channel = desc->v3.channel; phy_data.gp2_on_air_rise = le32_to_cpu(desc->v3.gp2_on_air_rise); phy_data.energy_a = desc->v3.energy_a; @@ -2072,7 +2074,9 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, phy_data.eht_d4 = desc->phy_eht_data4; phy_data.d5 = desc->v3.phy_data5; } else { - phy_data.rate_n_flags = le32_to_cpu(desc->v1.rate_n_flags); + phy_data.rate_n_flags = + iwl_mvm_v3_rate_from_fw(desc->v1.rate_n_flags, + mvm->fw_rates_ver); phy_data.channel = desc->v1.channel; phy_data.gp2_on_air_rise = le32_to_cpu(desc->v1.gp2_on_air_rise); phy_data.energy_a = desc->v1.energy_a; @@ -2084,13 +2088,6 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, phy_data.d3 = desc->v1.phy_data3; } - if (iwl_fw_lookup_notif_ver(mvm->fw, LEGACY_GROUP, - REPLY_RX_MPDU_CMD, 0) < 4) { - phy_data.rate_n_flags = iwl_new_rate_from_v1(phy_data.rate_n_flags); - IWL_DEBUG_DROP(mvm, "Got old format rate, converting. New rate: 0x%x\n", - phy_data.rate_n_flags); - } - format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK; len = le16_to_cpu(desc->mpdu_len); @@ -2138,14 +2135,14 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, } /* set the preamble flag if appropriate */ - if (format == RATE_MCS_CCK_MSK && + if (format == RATE_MCS_MOD_TYPE_CCK && phy_data.phy_info & IWL_RX_MPDU_PHY_SHORT_PREAMBLE) rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; if (likely(!(phy_data.phy_info & IWL_RX_MPDU_PHY_TSF_OVERLOAD))) { u64 tsf_on_air_rise; - if (mvm->trans->trans_cfg->device_family >= + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) tsf_on_air_rise = le64_to_cpu(desc->v3.tsf_on_air_rise); else @@ -2157,7 +2154,8 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, } if (iwl_mvm_is_band_in_rx_supported(mvm)) { - u8 band = BAND_IN_RX_STATUS(desc->mac_phy_idx); + u8 band = u8_get_bits(desc->mac_phy_band, + IWL_RX_MPDU_MAC_PHY_BAND_BAND_MASK); rx_status->band = iwl_mvm_nl80211_band_from_phy(band); } else { @@ -2303,7 +2301,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, *qc &= ~IEEE80211_QOS_CTL_A_MSDU_PRESENT; - if (mvm->trans->trans_cfg->device_family == + if (mvm->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000) { iwl_mvm_flip_address(hdr->addr3); @@ -2349,7 +2347,7 @@ void iwl_mvm_rx_mpdu_mq(struct iwl_mvm *mvm, struct napi_struct *napi, if (!iwl_mvm_reorder(mvm, napi, queue, sta, skb, desc) && likely(!iwl_mvm_time_sync_frame(mvm, skb, hdr->addr2)) && likely(!iwl_mvm_mei_filter_scan(mvm, skb))) { - if (mvm->trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_9000 && + if (mvm->trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_9000 && (desc->mac_flags2 & IWL_RX_MPDU_MFLG2_AMSDU) && !(desc->amsdu_info & IWL_RX_MPDU_AMSDU_LAST_SUBFRAME)) rx_status->flag |= RX_FLAG_AMSDU_MORE; @@ -2383,7 +2381,6 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, phy_data.d1 = desc->phy_info[1]; phy_data.phy_info = IWL_RX_MPDU_PHY_TSF_OVERLOAD; phy_data.gp2_on_air_rise = le32_to_cpu(desc->on_air_rise_time); - phy_data.rate_n_flags = le32_to_cpu(desc->rate); phy_data.energy_a = u32_get_bits(rssi, RX_NO_DATA_CHAIN_A_MSK); phy_data.energy_b = u32_get_bits(rssi, RX_NO_DATA_CHAIN_B_MSK); phy_data.channel = u32_get_bits(rssi, RX_NO_DATA_CHANNEL_MSK); @@ -2391,14 +2388,8 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, phy_data.rx_vec[0] = desc->rx_vec[0]; phy_data.rx_vec[1] = desc->rx_vec[1]; - if (iwl_fw_lookup_notif_ver(mvm->fw, DATA_PATH_GROUP, - RX_NO_DATA_NOTIF, 0) < 2) { - IWL_DEBUG_DROP(mvm, "Got an old rate format. Old rate: 0x%x\n", - phy_data.rate_n_flags); - phy_data.rate_n_flags = iwl_new_rate_from_v1(phy_data.rate_n_flags); - IWL_DEBUG_DROP(mvm, " Rate after conversion to the new format: 0x%x\n", - phy_data.rate_n_flags); - } + phy_data.rate_n_flags = iwl_mvm_v3_rate_from_fw(desc->rate, + mvm->fw_rates_ver); format = phy_data.rate_n_flags & RATE_MCS_MOD_TYPE_MSK; @@ -2411,7 +2402,7 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, phy_data.rx_vec[2] = desc->rx_vec[2]; phy_data.rx_vec[3] = desc->rx_vec[3]; } else { - if (format == RATE_MCS_EHT_MSK) + if (format == RATE_MCS_MOD_TYPE_EHT) /* no support for EHT before version 3 API */ return; } @@ -2472,17 +2463,17 @@ void iwl_mvm_rx_monitor_no_data(struct iwl_mvm *mvm, struct napi_struct *napi, * may be up to 8 spatial streams. */ switch (format) { - case RATE_MCS_VHT_MSK: + case RATE_MCS_MOD_TYPE_VHT: rx_status->nss = le32_get_bits(desc->rx_vec[0], RX_NO_DATA_RX_VEC0_VHT_NSTS_MSK) + 1; break; - case RATE_MCS_HE_MSK: + case RATE_MCS_MOD_TYPE_HE: rx_status->nss = le32_get_bits(desc->rx_vec[0], RX_NO_DATA_RX_VEC0_HE_NSTS_MSK) + 1; break; - case RATE_MCS_EHT_MSK: + case RATE_MCS_MOD_TYPE_EHT: rx_status->nss = le32_get_bits(desc->rx_vec[2], RX_NO_DATA_RX_VEC2_EHT_NSTS_MSK) + 1; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c index 7a4844ec3c10..8ec4a007b4b0 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2015, 2018-2024 Intel Corporation + * Copyright (C) 2012-2015, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -791,10 +791,10 @@ static int iwl_mvm_find_free_queue(struct iwl_mvm *mvm, u8 sta_id, lockdep_assert_held(&mvm->mutex); - if (WARN(maxq >= mvm->trans->trans_cfg->base_params->num_of_queues, + if (WARN(maxq >= mvm->trans->mac_cfg->base->num_of_queues, "max queue %d >= num_of_queues (%d)", maxq, - mvm->trans->trans_cfg->base_params->num_of_queues)) - maxq = mvm->trans->trans_cfg->base_params->num_of_queues - 1; + mvm->trans->mac_cfg->base->num_of_queues)) + maxq = mvm->trans->mac_cfg->base->num_of_queues - 1; /* This should not be hit with new TX path */ if (WARN_ON(iwl_mvm_has_new_tx_api(mvm))) @@ -852,7 +852,7 @@ int iwl_mvm_tvqm_enable_txq(struct iwl_mvm *mvm, if (tid == IWL_MAX_TID_COUNT) { tid = IWL_MGMT_TID; size = max_t(u32, IWL_MGMT_QUEUE_SIZE, - mvm->trans->cfg->min_txq_size); + mvm->trans->mac_cfg->base->min_txq_size); } else { size = iwl_mvm_get_queue_size(sta); } @@ -1765,7 +1765,7 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif, mvm_sta->deflink.sta_id = sta_id; rcu_assign_pointer(mvm_sta->link[0], &mvm_sta->deflink); - if (!mvm->trans->trans_cfg->gen2) + if (!mvm->trans->mac_cfg->gen2) mvm_sta->deflink.lq_sta.rs_drv.pers.max_agg_bufsize = LINK_QUAL_AGG_FRAME_LIMIT_DEF; else @@ -1798,7 +1798,7 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (iwl_mvm_has_new_rx_api(mvm)) { int q; - dup_data = kcalloc(mvm->trans->num_rx_queues, + dup_data = kcalloc(mvm->trans->info.num_rxqs, sizeof(*dup_data), GFP_KERNEL); if (!dup_data) return -ENOMEM; @@ -1811,7 +1811,7 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * This thus allows receiving a packet with seqno 0 and the * retry bit set as the very first packet on a new TID. */ - for (q = 0; q < mvm->trans->num_rx_queues; q++) + for (q = 0; q < mvm->trans->info.num_rxqs; q++) memset(dup_data[q].last_seq, 0xff, sizeof(dup_data[q].last_seq)); mvm_sta->dup_data = dup_data; @@ -1839,11 +1839,11 @@ int iwl_mvm_sta_init(struct iwl_mvm *mvm, struct ieee80211_vif *vif, if (vif->type == NL80211_IFTYPE_STATION && !vif->p2p && !sta->tdls && ieee80211_vif_is_mld(vif)) { mvm_sta->mpdu_counters = - kcalloc(mvm->trans->num_rx_queues, + kcalloc(mvm->trans->info.num_rxqs, sizeof(*mvm_sta->mpdu_counters), GFP_KERNEL); if (mvm_sta->mpdu_counters) - for (int q = 0; q < mvm->trans->num_rx_queues; q++) + for (int q = 0; q < mvm->trans->info.num_rxqs; q++) spin_lock_init(&mvm_sta->mpdu_counters[q].lock); } @@ -2189,7 +2189,7 @@ static void iwl_mvm_enable_aux_snif_queue(struct iwl_mvm *mvm, u16 queue, u8 sta_id, u8 fifo) { unsigned int wdg_timeout = - mvm->trans->trans_cfg->base_params->wd_timeout; + mvm->trans->mac_cfg->base->wd_timeout; struct iwl_trans_txq_scd_cfg cfg = { .fifo = fifo, .sta_id = sta_id, @@ -2206,7 +2206,7 @@ static void iwl_mvm_enable_aux_snif_queue(struct iwl_mvm *mvm, u16 queue, static int iwl_mvm_enable_aux_snif_queue_tvqm(struct iwl_mvm *mvm, u8 sta_id) { unsigned int wdg_timeout = - mvm->trans->trans_cfg->base_params->wd_timeout; + mvm->trans->mac_cfg->base->wd_timeout; WARN_ON(!iwl_mvm_has_new_tx_api(mvm)); @@ -2717,7 +2717,7 @@ static void iwl_mvm_free_reorder(struct iwl_mvm *mvm, iwl_mvm_sync_rxq_del_ba(mvm, data->baid); - for (i = 0; i < mvm->trans->num_rx_queues; i++) { + for (i = 0; i < mvm->trans->info.num_rxqs; i++) { int j; struct iwl_mvm_reorder_buffer *reorder_buf = &data->reorder_buf[i]; @@ -2750,7 +2750,7 @@ static void iwl_mvm_init_reorder_buffer(struct iwl_mvm *mvm, { int i; - for (i = 0; i < mvm->trans->num_rx_queues; i++) { + for (i = 0; i < mvm->trans->info.num_rxqs; i++) { struct iwl_mvm_reorder_buffer *reorder_buf = &data->reorder_buf[i]; struct iwl_mvm_reorder_buf_entry *entries = @@ -2925,7 +2925,7 @@ int iwl_mvm_sta_rx_agg(struct iwl_mvm *mvm, struct ieee80211_sta *sta, * before starting the BA session in the firmware */ baid_data = kzalloc(sizeof(*baid_data) + - mvm->trans->num_rx_queues * + mvm->trans->info.num_rxqs * reorder_buf_size, GFP_KERNEL); if (!baid_data) @@ -3177,7 +3177,7 @@ int iwl_mvm_sta_tx_agg_start(struct iwl_mvm *mvm, struct ieee80211_vif *vif, * to align the wrap around of ssn so we compare relevant values. */ normalized_ssn = tid_data->ssn; - if (mvm->trans->trans_cfg->gen2) + if (mvm->trans->mac_cfg->gen2) normalized_ssn &= 0xff; if (normalized_ssn == tid_data->next_reclaimed) { @@ -4305,73 +4305,12 @@ u16 iwl_mvm_tid_queued(struct iwl_mvm *mvm, struct iwl_mvm_tid_data *tid_data) * In 22000 HW, the next_reclaimed index is only 8 bit, so we'll need * to align the wrap around of ssn so we compare relevant values. */ - if (mvm->trans->trans_cfg->gen2) + if (mvm->trans->mac_cfg->gen2) sn &= 0xff; return ieee80211_sn_sub(sn, tid_data->next_reclaimed); } -int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher, - u8 *key, u32 key_len, - struct ieee80211_key_conf *keyconf) -{ - int ret; - u16 queue; - struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); - unsigned int wdg_timeout = - iwl_mvm_get_wd_timeout(mvm, vif); - bool mld = iwl_mvm_has_mld_api(mvm->fw); - u32 type = IWL_STA_LINK; - - if (mld) - type = STATION_TYPE_PEER; - - ret = iwl_mvm_allocate_int_sta(mvm, sta, 0, - NL80211_IFTYPE_UNSPECIFIED, type); - if (ret) - return ret; - - if (mld) - ret = iwl_mvm_mld_add_int_sta_with_queue(mvm, sta, addr, - mvmvif->deflink.fw_link_id, - &queue, - IWL_MAX_TID_COUNT, - &wdg_timeout); - else - ret = iwl_mvm_add_int_sta_with_queue(mvm, mvmvif->id, - mvmvif->color, addr, sta, - &queue, - IWL_MVM_TX_FIFO_BE); - if (ret) - goto out; - - keyconf->cipher = cipher; - memcpy(keyconf->key, key, key_len); - keyconf->keylen = key_len; - keyconf->flags = IEEE80211_KEY_FLAG_PAIRWISE; - - if (mld) { - /* The MFP flag is set according to the station mfp field. Since - * we don't have a station, set it manually. - */ - u32 key_flags = - iwl_mvm_get_sec_flags(mvm, vif, NULL, keyconf) | - IWL_SEC_KEY_FLAG_MFP; - u32 sta_mask = BIT(sta->sta_id); - - ret = iwl_mvm_mld_send_key(mvm, sta_mask, key_flags, keyconf); - } else { - ret = iwl_mvm_send_sta_key(mvm, sta->sta_id, keyconf, false, - 0, NULL, 0, 0, true); - } - -out: - if (ret) - iwl_mvm_dealloc_int_sta(mvm, sta); - return ret; -} - void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 id) diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h index 6856f7440ef3..6b183f5e9bbc 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/sta.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/sta.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2016 Intel Deutschland GmbH */ @@ -225,8 +225,6 @@ struct iwl_mvm_vif; * @IWL_AGG_ON: aggregation session is up * @IWL_EMPTYING_HW_QUEUE_ADDBA: establishing a BA session - waiting for the * HW queue to be empty from packets for this RA /TID. - * @IWL_EMPTYING_HW_QUEUE_DELBA: tearing down a BA session - waiting for the - * HW queue to be empty from packets for this RA /TID. */ enum iwl_mvm_agg_state { IWL_AGG_OFF = 0, @@ -234,7 +232,6 @@ enum iwl_mvm_agg_state { IWL_AGG_STARTING, IWL_AGG_ON, IWL_EMPTYING_HW_QUEUE_ADDBA, - IWL_EMPTYING_HW_QUEUE_DELBA, }; /** @@ -262,7 +259,7 @@ struct iwl_mvm_tid_data { u16 seq_number; u16 next_reclaimed; /* The rest is Tx AGG related */ - u32 rate_n_flags; + __le32 rate_n_flags; u8 lq_color; bool amsdu_in_ampdu_allowed; enum iwl_mvm_agg_state state; @@ -597,10 +594,6 @@ void iwl_mvm_modify_all_sta_disable_tx(struct iwl_mvm *mvm, void iwl_mvm_csa_client_absent(struct iwl_mvm *mvm, struct ieee80211_vif *vif); int iwl_mvm_sta_ensure_queue(struct iwl_mvm *mvm, struct ieee80211_txq *txq); void iwl_mvm_add_new_dqa_stream_wk(struct work_struct *wk); -int iwl_mvm_add_pasn_sta(struct iwl_mvm *mvm, struct ieee80211_vif *vif, - struct iwl_mvm_int_sta *sta, u8 *addr, u32 cipher, - u8 *key, u32 key_len, - struct ieee80211_key_conf *key_conf_out); void iwl_mvm_cancel_channel_switch(struct iwl_mvm *mvm, struct ieee80211_vif *vif, u32 id); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile b/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile index 6bd56a28cffd..895d53f223e9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tests/Makefile @@ -1,3 +1,3 @@ -iwlmvm-tests-y += module.o links.o scan.o +iwlmvm-tests-y += module.o links.o scan.o hcmd.o obj-$(CONFIG_IWLWIFI_KUNIT_TESTS) += iwlmvm-tests.o diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tests/hcmd.c b/drivers/net/wireless/intel/iwlwifi/mvm/tests/hcmd.c new file mode 100644 index 000000000000..1fee0320c756 --- /dev/null +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tests/hcmd.c @@ -0,0 +1,38 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* + * KUnit tests for channel helper functions + * + * Copyright (C) 2025 Intel Corporation + */ +#include <kunit/test.h> + +#include <iwl-trans.h> +#include "../mvm.h" + +MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); + +static void test_hcmd_names_sorted(struct kunit *test) +{ + for (int i = 0; i < iwl_mvm_groups_size; i++) { + const struct iwl_hcmd_arr *arr = &iwl_mvm_groups[i]; + + if (!arr->arr) + continue; + + for (int j = 0; j < arr->size - 1; j++) + KUNIT_EXPECT_LE(test, arr->arr[j].cmd_id, + arr->arr[j + 1].cmd_id); + } +} + +static struct kunit_case hcmd_names_cases[] = { + KUNIT_CASE(test_hcmd_names_sorted), + {}, +}; + +static struct kunit_suite hcmd_names = { + .name = "iwlmvm-hcmd-names", + .test_cases = hcmd_names_cases, +}; + +kunit_test_suite(hcmd_names); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c index 9216c43a35c4..478408f802d9 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/time-event.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2017 Intel Deutschland GmbH */ @@ -775,11 +775,13 @@ static void iwl_mvm_roc_rm_cmd(struct iwl_mvm *mvm, u32 activity) .action = cpu_to_le32(FW_CTXT_ACTION_REMOVE), .activity = cpu_to_le32(activity), }; + u8 ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); + u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(roc_cmd); int ret; lockdep_assert_held(&mvm->mutex); ret = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0, - sizeof(roc_cmd), &roc_cmd); + cmd_len, &roc_cmd); if (ret) IWL_ERR(mvm, "Couldn't send the ROC_CMD: %d\n", ret); } @@ -1030,6 +1032,8 @@ void iwl_mvm_rx_session_protect_notif(struct iwl_mvm *mvm, /* End TE, notify mac80211 */ mvmvif->time_event_data.id = SESSION_PROTECT_CONF_MAX_ID; mvmvif->time_event_data.link_id = -1; + /* set the bit so the ROC cleanup will actually clean up */ + set_bit(IWL_MVM_STATUS_ROC_P2P_RUNNING, &mvm->status); iwl_mvm_roc_finished(mvm); ieee80211_remain_on_channel_expired(mvm->hw); } else if (le32_to_cpu(notif->start)) { @@ -1105,6 +1109,8 @@ int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm, .activity = cpu_to_le32(activity), .sta_id = cpu_to_le32(mvm->aux_sta.sta_id), }; + u8 ver = iwl_fw_lookup_cmd_ver(mvm->fw, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), 0); + u16 cmd_len = ver < 6 ? sizeof(struct iwl_roc_req_v5) : sizeof(roc_req); struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); lockdep_assert_held(&mvm->mutex); @@ -1134,7 +1140,7 @@ int iwl_mvm_roc_add_cmd(struct iwl_mvm *mvm, memcpy(roc_req.node_addr, vif->addr, ETH_ALEN); res = iwl_mvm_send_cmd_pdu(mvm, WIDE_ID(MAC_CONF_GROUP, ROC_CMD), - 0, sizeof(roc_req), &roc_req); + 0, cmd_len, &roc_req); if (!res) mvmvif->roc_activity = activity; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c index d92470960b38..53bab21ebae2 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tt.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tt.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2019-2022, 2024 Intel Corporation + * Copyright (C) 2012-2014, 2019-2022, 2024-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2016 Intel Deutschland GmbH */ @@ -8,6 +8,9 @@ #include "mvm.h" +#define IWL_MVM_NUM_CTDP_STEPS 20 +#define IWL_MVM_MIN_CTDP_BUDGET_MW 150 + #define IWL_MVM_TEMP_NOTIF_WAIT_TIMEOUT HZ void iwl_mvm_enter_ctkill(struct iwl_mvm *mvm) @@ -105,7 +108,7 @@ static bool iwl_mvm_temp_notif_wait(struct iwl_notif_wait_data *notif_wait, void iwl_mvm_temp_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) { struct iwl_rx_packet *pkt = rxb_addr(rxb); - struct iwl_dts_measurement_notif_v2 *notif_v2; + struct iwl_dts_measurement_notif *notif_v2; int len = iwl_rx_packet_payload_len(pkt); int temp; u32 ths_crossed; @@ -479,43 +482,28 @@ static const struct iwl_tt_params iwl_mvm_default_tt_params = { .support_tx_backoff = true, }; -/* budget in mWatt */ -static const u32 iwl_mvm_cdev_budgets[] = { - 2400, /* cooling state 0 */ - 2000, /* cooling state 1 */ - 1800, /* cooling state 2 */ - 1600, /* cooling state 3 */ - 1400, /* cooling state 4 */ - 1200, /* cooling state 5 */ - 1000, /* cooling state 6 */ - 900, /* cooling state 7 */ - 800, /* cooling state 8 */ - 700, /* cooling state 9 */ - 650, /* cooling state 10 */ - 600, /* cooling state 11 */ - 550, /* cooling state 12 */ - 500, /* cooling state 13 */ - 450, /* cooling state 14 */ - 400, /* cooling state 15 */ - 350, /* cooling state 16 */ - 300, /* cooling state 17 */ - 250, /* cooling state 18 */ - 200, /* cooling state 19 */ - 150, /* cooling state 20 */ -}; - int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state) { - struct iwl_mvm_ctdp_cmd cmd = { + struct iwl_ctdp_cmd cmd = { .operation = cpu_to_le32(op), - .budget = cpu_to_le32(iwl_mvm_cdev_budgets[state]), .window_size = 0, }; + u32 budget; int ret; u32 status; lockdep_assert_held(&mvm->mutex); + /* Do a linear scale from IWL_MVM_MIN_CTDP_BUDGET_MW to the configured + * maximum in the predefined number of steps. + */ + budget = ((mvm->thermal_throttle.power_budget_mw - + IWL_MVM_MIN_CTDP_BUDGET_MW) * + (IWL_MVM_NUM_CTDP_STEPS - 1 - state)) / + (IWL_MVM_NUM_CTDP_STEPS - 1) + + IWL_MVM_MIN_CTDP_BUDGET_MW; + cmd.budget = cpu_to_le32(budget); + status = 0; ret = iwl_mvm_send_cmd_pdu_status(mvm, WIDE_ID(PHY_OPS_GROUP, CTDP_CONFIG_CMD), @@ -552,8 +540,8 @@ int iwl_mvm_ctdp_command(struct iwl_mvm *mvm, u32 op, u32 state) #ifdef CONFIG_THERMAL static int compare_temps(const void *a, const void *b) { - return ((s16)le16_to_cpu(*(__le16 *)a) - - (s16)le16_to_cpu(*(__le16 *)b)); + return ((s16)le16_to_cpu(*(const __le16 *)a) - + (s16)le16_to_cpu(*(const __le16 *)b)); } struct iwl_trip_walk_data { @@ -707,7 +695,7 @@ static void iwl_mvm_thermal_zone_register(struct iwl_mvm *mvm) static int iwl_mvm_tcool_get_max_state(struct thermal_cooling_device *cdev, unsigned long *state) { - *state = ARRAY_SIZE(iwl_mvm_cdev_budgets) - 1; + *state = IWL_MVM_NUM_CTDP_STEPS - 1; return 0; } @@ -733,7 +721,7 @@ static int iwl_mvm_tcool_set_cur_state(struct thermal_cooling_device *cdev, mvm->fwrt.cur_fw_img != IWL_UCODE_REGULAR) return -EIO; - if (new_state >= ARRAY_SIZE(iwl_mvm_cdev_budgets)) + if (new_state >= IWL_MVM_NUM_CTDP_STEPS) return -EINVAL; return iwl_mvm_ctdp_command(mvm, CTDP_CMD_OPERATION_START, @@ -794,6 +782,47 @@ static void iwl_mvm_cooling_device_unregister(struct iwl_mvm *mvm) } #endif /* CONFIG_THERMAL */ +static u32 iwl_mvm_ctdp_get_max_budget(struct iwl_mvm *mvm) +{ + u64 bios_power_budget = 0; + u32 default_power_budget; + + switch (CSR_HW_RFID_TYPE(mvm->trans->info.hw_rf_id)) { + case IWL_CFG_RF_TYPE_JF2: + case IWL_CFG_RF_TYPE_JF1: + default_power_budget = 2000; + break; + case IWL_CFG_RF_TYPE_HR2: + case IWL_CFG_RF_TYPE_HR1: + default_power_budget = 2400; + break; + case IWL_CFG_RF_TYPE_GF: + /* dual-radio devices have a higher budget */ + if (CSR_HW_RFID_IS_CDB(mvm->trans->info.hw_rf_id)) + default_power_budget = 5200; + else + default_power_budget = 2880; + break; + case IWL_CFG_RF_TYPE_FM: + default_power_budget = 3450; + break; + default: + default_power_budget = 5550; + break; + } + + iwl_bios_get_pwr_limit(&mvm->fwrt, &bios_power_budget); + + /* 32bit in UEFI, 16bit in ACPI; use BIOS value if it is in range */ + if (bios_power_budget && + bios_power_budget != 0xffff && bios_power_budget != 0xffffffff && + bios_power_budget >= IWL_MVM_MIN_CTDP_BUDGET_MW && + bios_power_budget <= default_power_budget) + return (u32)bios_power_budget; + + return default_power_budget; +} + void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff) { struct iwl_mvm_tt_mgmt *tt = &mvm->thermal_throttle; @@ -805,6 +834,8 @@ void iwl_mvm_thermal_initialize(struct iwl_mvm *mvm, u32 min_backoff) else tt->params = iwl_mvm_default_tt_params; + tt->power_budget_mw = iwl_mvm_ctdp_get_max_budget(mvm); + IWL_DEBUG_TEMP(mvm, "cTDP power budget: %d mW\n", tt->power_budget_mw); tt->throttle = false; tt->dynamic_smps = false; tt->min_backoff = min_backoff; diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c index f67afb66ef2b..ac2cf1b8ce23 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -148,7 +148,7 @@ out: * Sets most of the Tx cmd's fields */ void iwl_mvm_set_tx_cmd(struct iwl_mvm *mvm, struct sk_buff *skb, - struct iwl_tx_cmd *tx_cmd, + struct iwl_tx_cmd_v6 *tx_cmd, struct ieee80211_tx_info *info, u8 sta_id) { struct ieee80211_hdr *hdr = (void *)skb->data; @@ -283,14 +283,10 @@ static u32 iwl_mvm_convert_rate_idx(struct iwl_mvm *mvm, (rate_idx <= IWL_LAST_CCK_RATE); /* Set CCK or OFDM flag */ - if (iwl_fw_lookup_cmd_ver(mvm->fw, TX_CMD, 0) > 8) { - if (!is_cck) - rate_flags |= RATE_MCS_LEGACY_OFDM_MSK; - else - rate_flags |= RATE_MCS_CCK_MSK; - } else if (is_cck) { - rate_flags |= RATE_MCS_CCK_MSK_V1; - } + if (!is_cck) + rate_flags |= RATE_MCS_MOD_TYPE_LEGACY_OFDM; + else + rate_flags |= RATE_MCS_MOD_TYPE_CCK; return (u32)rate_plcp | rate_flags; } @@ -303,45 +299,35 @@ static u32 iwl_mvm_get_inject_tx_rate(struct iwl_mvm *mvm, struct ieee80211_tx_rate *rate = &info->control.rates[0]; u32 result; - /* - * we only care about legacy/HT/VHT so far, so we can - * build in v1 and use iwl_new_rate_from_v1() - */ - if (rate->flags & IEEE80211_TX_RC_VHT_MCS) { u8 mcs = ieee80211_rate_get_vht_mcs(rate); u8 nss = ieee80211_rate_get_vht_nss(rate); - result = RATE_MCS_VHT_MSK_V1; + result = RATE_MCS_MOD_TYPE_VHT; result |= u32_encode_bits(mcs, RATE_VHT_MCS_RATE_CODE_MSK); result |= u32_encode_bits(nss, RATE_MCS_NSS_MSK); if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - result |= RATE_MCS_SGI_MSK_V1; + result |= RATE_MCS_SGI_MSK; if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_40; else if (rate->flags & IEEE80211_TX_RC_80_MHZ_WIDTH) - result |= u32_encode_bits(2, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_80; else if (rate->flags & IEEE80211_TX_RC_160_MHZ_WIDTH) - result |= u32_encode_bits(3, RATE_MCS_CHAN_WIDTH_MSK_V1); - - if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) > 6) - result = iwl_new_rate_from_v1(result); + result |= RATE_MCS_CHAN_WIDTH_160; } else if (rate->flags & IEEE80211_TX_RC_MCS) { - result = RATE_MCS_HT_MSK_V1; - result |= u32_encode_bits(rate->idx, - RATE_HT_MCS_RATE_CODE_MSK_V1 | - RATE_HT_MCS_NSS_MSK_V1); + result = RATE_MCS_MOD_TYPE_HT; + result |= u32_encode_bits(rate->idx & 0x7, + RATE_HT_MCS_CODE_MSK); + result |= u32_encode_bits(rate->idx >> 3, + RATE_MCS_NSS_MSK); if (rate->flags & IEEE80211_TX_RC_SHORT_GI) - result |= RATE_MCS_SGI_MSK_V1; + result |= RATE_MCS_SGI_MSK; if (rate->flags & IEEE80211_TX_RC_40_MHZ_WIDTH) - result |= u32_encode_bits(1, RATE_MCS_CHAN_WIDTH_MSK_V1); + result |= RATE_MCS_CHAN_WIDTH_40; if (info->flags & IEEE80211_TX_CTL_LDPC) - result |= RATE_MCS_LDPC_MSK_V1; + result |= RATE_MCS_LDPC_MSK; if (u32_get_bits(info->flags, IEEE80211_TX_CTL_STBC)) result |= RATE_MCS_STBC_MSK; - - if (iwl_fw_lookup_notif_ver(mvm->fw, LONG_GROUP, TX_CMD, 0) > 6) - result = iwl_new_rate_from_v1(result); } else { int rate_idx = info->control.rates[0].idx; @@ -391,21 +377,25 @@ static u32 iwl_mvm_get_tx_rate(struct iwl_mvm *mvm, return iwl_mvm_convert_rate_idx(mvm, info, rate_idx); } -static u32 iwl_mvm_get_tx_rate_n_flags(struct iwl_mvm *mvm, - struct ieee80211_tx_info *info, - struct ieee80211_sta *sta, __le16 fc) +static __le32 iwl_mvm_get_tx_rate_n_flags(struct iwl_mvm *mvm, + struct ieee80211_tx_info *info, + struct ieee80211_sta *sta, __le16 fc) { + u32 rate; + if (unlikely(info->control.flags & IEEE80211_TX_CTRL_RATE_INJECT)) - return iwl_mvm_get_inject_tx_rate(mvm, info, sta, fc); + rate = iwl_mvm_get_inject_tx_rate(mvm, info, sta, fc); + else + rate = iwl_mvm_get_tx_rate(mvm, info, sta, fc) | + iwl_mvm_get_tx_ant(mvm, info, sta, fc); - return iwl_mvm_get_tx_rate(mvm, info, sta, fc) | - iwl_mvm_get_tx_ant(mvm, info, sta, fc); + return iwl_mvm_v3_rate_to_fw(rate, mvm->fw_rates_ver); } /* * Sets the fields in the Tx cmd that are rate related */ -void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, +void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd_v6 *tx_cmd, struct ieee80211_tx_info *info, struct ieee80211_sta *sta, __le16 fc) { @@ -443,8 +433,7 @@ void iwl_mvm_set_tx_cmd_rate(struct iwl_mvm *mvm, struct iwl_tx_cmd *tx_cmd, } /* Set the rate in the TX cmd */ - tx_cmd->rate_n_flags = - cpu_to_le32(iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, fc)); + tx_cmd->rate_n_flags = iwl_mvm_get_tx_rate_n_flags(mvm, info, sta, fc); } static inline void iwl_mvm_set_tx_cmd_pn(struct ieee80211_tx_info *info, @@ -469,7 +458,7 @@ static inline void iwl_mvm_set_tx_cmd_pn(struct ieee80211_tx_info *info, */ static void iwl_mvm_set_tx_cmd_crypto(struct iwl_mvm *mvm, struct ieee80211_tx_info *info, - struct iwl_tx_cmd *tx_cmd, + struct iwl_tx_cmd_v6 *tx_cmd, struct sk_buff *skb_frag, int hdrlen) { @@ -543,7 +532,7 @@ static bool iwl_mvm_use_host_rate(struct iwl_mvm *mvm, * (since we don't necesarily know the link), but FW rate * selection was fixed. */ - return mvm->trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ; + return mvm->trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ; } static void iwl_mvm_copy_hdr(void *cmd, const void *hdr, int hdrlen, @@ -567,7 +556,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct iwl_device_tx_cmd *dev_cmd; - struct iwl_tx_cmd *tx_cmd; + struct iwl_tx_cmd_v6 *tx_cmd; dev_cmd = iwl_trans_alloc_tx_cmd(mvm->trans); @@ -577,7 +566,7 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, dev_cmd->hdr.cmd = TX_CMD; if (iwl_mvm_has_new_tx_api(mvm)) { - u32 rate_n_flags = 0; + __le32 rate_n_flags = 0; u16 flags = 0; struct iwl_mvm_sta *mvmsta = sta ? iwl_mvm_sta_from_mac80211(sta) : NULL; @@ -609,9 +598,9 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, flags |= IWL_TX_FLAGS_HIGH_PRI; } - if (mvm->trans->trans_cfg->device_family >= + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { - struct iwl_tx_cmd_gen3 *cmd = (void *)dev_cmd->payload; + struct iwl_tx_cmd *cmd = (void *)dev_cmd->payload; u32 offload_assist = iwl_mvm_tx_csum(mvm, skb, info, amsdu); @@ -624,9 +613,9 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, iwl_mvm_copy_hdr(cmd->hdr, hdr, hdrlen, addr3_override); cmd->flags = cpu_to_le16(flags); - cmd->rate_n_flags = cpu_to_le32(rate_n_flags); + cmd->rate_n_flags = rate_n_flags; } else { - struct iwl_tx_cmd_gen2 *cmd = (void *)dev_cmd->payload; + struct iwl_tx_cmd_v9 *cmd = (void *)dev_cmd->payload; u16 offload_assist = iwl_mvm_tx_csum(mvm, skb, info, amsdu); @@ -639,12 +628,12 @@ iwl_mvm_set_tx_params(struct iwl_mvm *mvm, struct sk_buff *skb, iwl_mvm_copy_hdr(cmd->hdr, hdr, hdrlen, addr3_override); cmd->flags = cpu_to_le32(flags); - cmd->rate_n_flags = cpu_to_le32(rate_n_flags); + cmd->rate_n_flags = rate_n_flags; } goto out; } - tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + tx_cmd = (struct iwl_tx_cmd_v6 *)dev_cmd->payload; if (info->control.hw_key) iwl_mvm_set_tx_cmd_crypto(mvm, info, tx_cmd, skb, hdrlen); @@ -1023,7 +1012,7 @@ static int iwl_mvm_tx_tso(struct iwl_mvm *mvm, struct sk_buff *skb, * 1 more for the potential data in the header */ if ((num_subframes * 2 + skb_shinfo(skb)->nr_frags + 1) > - mvm->trans->max_skb_frags) + mvm->trans->info.max_skb_frags) num_subframes = 1; if (num_subframes > 1) @@ -1185,7 +1174,7 @@ static int iwl_mvm_tx_mpdu(struct iwl_mvm *mvm, struct sk_buff *skb, seq_number &= IEEE80211_SCTL_SEQ; if (!iwl_mvm_has_new_tx_api(mvm)) { - struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; + struct iwl_tx_cmd_v6 *tx_cmd = (void *)dev_cmd->payload; hdr->seq_ctrl &= cpu_to_le16(IEEE80211_SCTL_FRAG); hdr->seq_ctrl |= cpu_to_le16(seq_number); @@ -1372,8 +1361,7 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, lockdep_assert_held(&mvmsta->lock); - if ((tid_data->state == IWL_AGG_ON || - tid_data->state == IWL_EMPTYING_HW_QUEUE_DELBA) && + if (tid_data->state == IWL_AGG_ON && iwl_mvm_tid_queued(mvm, tid_data) == 0) { /* * Now that this aggregation or DQA queue is empty tell @@ -1388,7 +1376,7 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, * to align the wrap around of ssn so we compare relevant values. */ normalized_ssn = tid_data->ssn; - if (mvm->trans->trans_cfg->gen2) + if (mvm->trans->mac_cfg->gen2) normalized_ssn &= 0xff; if (normalized_ssn != tid_data->next_reclaimed) @@ -1402,15 +1390,6 @@ static void iwl_mvm_check_ratid_empty(struct iwl_mvm *mvm, tid_data->state = IWL_AGG_STARTING; ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; - - case IWL_EMPTYING_HW_QUEUE_DELBA: - IWL_DEBUG_TX_QUEUES(mvm, - "Can continue DELBA flow ssn = next_recl = %d\n", - tid_data->next_reclaimed); - tid_data->state = IWL_AGG_OFF; - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; - default: break; } @@ -1477,7 +1456,7 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, struct ieee80211_tx_rate *r) { u32 format = rate_n_flags & RATE_MCS_MOD_TYPE_MSK; - u32 rate = format == RATE_MCS_HT_MSK ? + u32 rate = format == RATE_MCS_MOD_TYPE_HT ? RATE_HT_MCS_INDEX(rate_n_flags) : rate_n_flags & RATE_MCS_CODE_MSK; @@ -1487,68 +1466,51 @@ void iwl_mvm_hwrate_to_tx_rate(u32 rate_n_flags, if (rate_n_flags & RATE_MCS_SGI_MSK) r->flags |= IEEE80211_TX_RC_SHORT_GI; - if (format == RATE_MCS_HT_MSK) { + switch (format) { + case RATE_MCS_MOD_TYPE_HT: r->flags |= IEEE80211_TX_RC_MCS; r->idx = rate; - } else if (format == RATE_MCS_VHT_MSK) { + break; + case RATE_MCS_MOD_TYPE_VHT: ieee80211_rate_set_vht(r, rate, FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags) + 1); r->flags |= IEEE80211_TX_RC_VHT_MCS; - } else if (format == RATE_MCS_HE_MSK) { + break; + case RATE_MCS_MOD_TYPE_HE: + case RATE_MCS_MOD_TYPE_EHT: /* mac80211 cannot do this without ieee80211_tx_status_ext() * but it only matters for radiotap */ r->idx = 0; - } else { + break; + default: r->idx = iwl_mvm_legacy_hw_idx_to_mac80211_idx(rate_n_flags, band); } } -void iwl_mvm_hwrate_to_tx_rate_v1(u32 rate_n_flags, - enum nl80211_band band, - struct ieee80211_tx_rate *r) -{ - if (rate_n_flags & RATE_HT_MCS_GF_MSK) - r->flags |= IEEE80211_TX_RC_GREEN_FIELD; - - r->flags |= - iwl_mvm_get_hwrate_chan_width(rate_n_flags & - RATE_MCS_CHAN_WIDTH_MSK_V1); - - if (rate_n_flags & RATE_MCS_SGI_MSK_V1) - r->flags |= IEEE80211_TX_RC_SHORT_GI; - if (rate_n_flags & RATE_MCS_HT_MSK_V1) { - r->flags |= IEEE80211_TX_RC_MCS; - r->idx = rate_n_flags & RATE_HT_MCS_INDEX_MSK_V1; - } else if (rate_n_flags & RATE_MCS_VHT_MSK_V1) { - ieee80211_rate_set_vht( - r, rate_n_flags & RATE_VHT_MCS_RATE_CODE_MSK, - FIELD_GET(RATE_MCS_NSS_MSK, rate_n_flags) + 1); - r->flags |= IEEE80211_TX_RC_VHT_MCS; - } else { - r->idx = iwl_mvm_legacy_rate_to_mac80211_idx(rate_n_flags, - band); - } -} - /* * translate ucode response to mac80211 tx status control values */ -static void iwl_mvm_hwrate_to_tx_status(const struct iwl_fw *fw, - u32 rate_n_flags, +static void iwl_mvm_hwrate_to_tx_status(struct iwl_mvm *mvm, + __le32 rate_n_flags, struct ieee80211_tx_info *info) { struct ieee80211_tx_rate *r = &info->status.rates[0]; + u32 rate; - if (iwl_fw_lookup_notif_ver(fw, LONG_GROUP, - TX_CMD, 0) <= 6) - rate_n_flags = iwl_new_rate_from_v1(rate_n_flags); + /* + * Technically this conversion is incorrect for BA status, however: + * - we only use the BA notif data for older firmware that have + * host rate scaling and don't use newer rate formats + * - the firmware API changed together for BA notif and TX CMD + * as well + */ + rate = iwl_mvm_v3_rate_from_fw(rate_n_flags, mvm->fw_rates_ver); info->status.antenna = - ((rate_n_flags & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS); - iwl_mvm_hwrate_to_tx_rate(rate_n_flags, - info->band, r); + ((rate & RATE_MCS_ANT_AB_MSK) >> RATE_MCS_ANT_POS); + iwl_mvm_hwrate_to_tx_rate(rate, info->band, r); } static void iwl_mvm_tx_status_check_trigger(struct iwl_mvm *mvm, @@ -1613,7 +1575,7 @@ static inline u32 iwl_mvm_get_scd_ssn(struct iwl_mvm *mvm, u32 val = le32_to_cpup((__le32 *)iwl_mvm_get_agg_status(mvm, tx_resp) + tx_resp->frame_count); - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) return val & 0xFFFF; return val & 0xFFF; } @@ -1700,9 +1662,7 @@ static void iwl_mvm_rx_tx_cmd_single(struct iwl_mvm *mvm, info->status.rates[0].count = tx_resp->failure_frame + 1; - iwl_mvm_hwrate_to_tx_status(mvm->fw, - le32_to_cpu(tx_resp->initial_rate), - info); + iwl_mvm_hwrate_to_tx_status(mvm, tx_resp->initial_rate, info); /* Don't assign the converted initial_rate, because driver * TLC uses this and doesn't support the new FW rate @@ -1944,7 +1904,7 @@ static void iwl_mvm_rx_tx_cmd_agg(struct iwl_mvm *mvm, if (!WARN_ON_ONCE(!mvmsta)) { mvmsta->tid_data[tid].rate_n_flags = - le32_to_cpu(tx_resp->initial_rate); + tx_resp->initial_rate; mvmsta->tid_data[tid].tx_time = le16_to_cpu(tx_resp->wireless_media_time); mvmsta->tid_data[tid].lq_color = @@ -1969,7 +1929,7 @@ void iwl_mvm_rx_tx_cmd(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, int txq, int index, - struct ieee80211_tx_info *tx_info, u32 rate, + struct ieee80211_tx_info *tx_info, __le32 rate, bool is_flush) { struct sk_buff_head reclaimed_skbs; @@ -2053,7 +2013,9 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, tx_info->status.status_driver_data[0] = RS_DRV_DATA_PACK(tid_data->lq_color, tx_info->status.status_driver_data[0]); - tx_info->status.status_driver_data[1] = (void *)(uintptr_t)rate; + /* the value is only consumed for old FW that has v1 rates anyway */ + tx_info->status.status_driver_data[1] = + (void *)(uintptr_t)le32_to_cpu(rate); skb_queue_walk(&reclaimed_skbs, skb) { struct ieee80211_hdr *hdr = (void *)skb->data; @@ -2072,7 +2034,7 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, info->flags |= IEEE80211_TX_STAT_AMPDU; memcpy(&info->status, &tx_info->status, sizeof(tx_info->status)); - iwl_mvm_hwrate_to_tx_status(mvm->fw, rate, info); + iwl_mvm_hwrate_to_tx_status(mvm, rate, info); } } @@ -2095,7 +2057,7 @@ static void iwl_mvm_tx_reclaim(struct iwl_mvm *mvm, int sta_id, int tid, goto out; tx_info->band = chanctx_conf->def.chan->band; - iwl_mvm_hwrate_to_tx_status(mvm->fw, rate, tx_info); + iwl_mvm_hwrate_to_tx_status(mvm, rate, tx_info); IWL_DEBUG_TX_REPLY(mvm, "No reclaim. Update rs directly\n"); iwl_mvm_rs_tx_status(mvm, sta, tid, tx_info, false); @@ -2184,7 +2146,7 @@ void iwl_mvm_rx_ba_notif(struct iwl_mvm *mvm, struct iwl_rx_cmd_buffer *rxb) (int)(le16_to_cpu(ba_tfd->q_num)), le16_to_cpu(ba_tfd->tfd_index), &ba_info, - le32_to_cpu(ba_res->tx_rate), false); + ba_res->tx_rate, false); } if (mvmsta) { diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c index dd890dcd1505..62da0132f383 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/utils.c +++ b/drivers/net/wireless/intel/iwlwifi/mvm/utils.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2012-2014, 2018-2024 Intel Corporation + * Copyright (C) 2012-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2014 Intel Mobile Communications GmbH * Copyright (C) 2015-2017 Intel Deutschland GmbH */ @@ -142,7 +142,7 @@ int iwl_mvm_legacy_hw_idx_to_mac80211_idx(u32 rate_n_flags, int rate = rate_n_flags & RATE_LEGACY_RATE_MSK; bool is_LB = band == NL80211_BAND_2GHZ; - if (format == RATE_MCS_LEGACY_OFDM_MSK) + if (format == RATE_MCS_MOD_TYPE_LEGACY_OFDM) return is_LB ? rate + IWL_FIRST_OFDM_RATE : rate; @@ -169,15 +169,9 @@ int iwl_mvm_legacy_rate_to_mac80211_idx(u32 rate_n_flags, u8 iwl_mvm_mac80211_idx_to_hwrate(const struct iwl_fw *fw, int rate_idx) { - if (iwl_fw_lookup_cmd_ver(fw, TX_CMD, 0) > 8) - /* In the new rate legacy rates are indexed: - * 0 - 3 for CCK and 0 - 7 for OFDM. - */ - return (rate_idx >= IWL_FIRST_OFDM_RATE ? - rate_idx - IWL_FIRST_OFDM_RATE : - rate_idx); - - return iwl_fw_rate_idx_to_plcp(rate_idx); + return (rate_idx >= IWL_FIRST_OFDM_RATE ? + rate_idx - IWL_FIRST_OFDM_RATE : + rate_idx); } u8 iwl_mvm_mac80211_ac_to_ucode_ac(enum ieee80211_ac_numbers ac) @@ -748,7 +742,7 @@ unsigned int iwl_mvm_get_wd_timeout(struct iwl_mvm *mvm, struct ieee80211_vif *vif) { unsigned int default_timeout = - mvm->trans->trans_cfg->base_params->wd_timeout; + mvm->trans->mac_cfg->base->wd_timeout; /* * We can't know when the station is asleep or awake, so we @@ -1187,9 +1181,9 @@ u32 iwl_mvm_get_systime(struct iwl_mvm *mvm) { u32 reg_addr = DEVICE_SYSTEM_TIME_REG; - if (mvm->trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_22000 && - mvm->trans->cfg->gp2_reg_addr) - reg_addr = mvm->trans->cfg->gp2_reg_addr; + if (mvm->trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_22000 && + mvm->trans->mac_cfg->base->gp2_reg_addr) + reg_addr = mvm->trans->mac_cfg->base->gp2_reg_addr; return iwl_read_prph(mvm->trans, reg_addr); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-v2.c index 838c426db7f0..976fd1f58da4 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-gen3.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info-v2.c @@ -1,11 +1,11 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include <linux/dmi.h> #include "iwl-trans.h" #include "iwl-fh.h" -#include "iwl-context-info-gen3.h" +#include "iwl-context-info-v2.h" #include "internal.h" #include "iwl-prph.h" @@ -97,11 +97,12 @@ out: *control_flags |= IWL_PRPH_SCRATCH_EARLY_DEBUG_EN | dbg_flags; } -int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, - const struct fw_img *fw) +int iwl_pcie_ctxt_info_v2_alloc(struct iwl_trans *trans, + const struct iwl_fw *fw, + const struct fw_img *img) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_context_info_gen3 *ctxt_info_gen3; + struct iwl_context_info_v2 *ctxt_info_v2; struct iwl_prph_scratch *prph_scratch; struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl; struct iwl_prph_info *prph_info; @@ -109,9 +110,9 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, u32 control_flags_ext = 0; int ret; int cmdq_size = max_t(u32, IWL_CMD_QUEUE_SIZE, - trans->cfg->min_txq_size); + trans->mac_cfg->base->min_txq_size); - switch (trans_pcie->rx_buf_size) { + switch (trans->conf.rx_buf_size) { case IWL_AMSDU_DEF: return -EINVAL; case IWL_AMSDU_2K: @@ -131,12 +132,15 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, break; } - if (trans->dsbr_urm_fw_dependent) + if (trans->conf.dsbr_urm_fw_dependent) control_flags_ext |= IWL_PRPH_SCRATCH_EXT_URM_FW; - if (trans->dsbr_urm_permanent) + if (trans->conf.dsbr_urm_permanent) control_flags_ext |= IWL_PRPH_SCRATCH_EXT_URM_PERM; + if (trans->conf.ext_32khz_clock_valid) + control_flags_ext |= IWL_PRPH_SCRATCH_EXT_32KHZ_CLK_VALID; + /* Allocate prph scratch */ prph_scratch = dma_alloc_coherent(trans->dev, sizeof(*prph_scratch), &trans_pcie->prph_scratch_dma_addr, @@ -148,16 +152,16 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, prph_sc_ctrl->version.version = 0; prph_sc_ctrl->version.mac_id = - cpu_to_le16((u16)trans->hw_rev); + cpu_to_le16((u16)trans->info.hw_rev); prph_sc_ctrl->version.size = cpu_to_le16(sizeof(*prph_scratch) / 4); control_flags |= IWL_PRPH_SCRATCH_MTR_MODE; control_flags |= IWL_PRPH_MTR_FORMAT_256B & IWL_PRPH_SCRATCH_MTR_FORMAT; - if (trans->trans_cfg->imr_enabled) + if (trans->mac_cfg->imr_enabled) control_flags |= IWL_PRPH_SCRATCH_IMR_DEBUG_EN; - if (CSR_HW_REV_TYPE(trans->hw_rev) == IWL_CFG_MAC_TYPE_GL && + if (CSR_HW_REV_TYPE(trans->info.hw_rev) == IWL_CFG_MAC_TYPE_GL && iwl_is_force_scu_active_approved()) { control_flags |= IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE; IWL_DEBUG_FW(trans, @@ -165,6 +169,11 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, IWL_PRPH_SCRATCH_SCU_FORCE_ACTIVE); } + if (trans->do_top_reset) { + WARN_ON(trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC); + control_flags |= IWL_PRPH_SCRATCH_TOP_RESET; + } + /* initialize RX default queue */ prph_sc_ctrl->rbd_cfg.free_rbd_addr = cpu_to_le64(trans_pcie->rxq->bd_dma); @@ -175,15 +184,16 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, prph_sc_ctrl->control.control_flags_ext = cpu_to_le32(control_flags_ext); /* initialize the Step equalizer data */ - prph_sc_ctrl->step_cfg.mbx_addr_0 = cpu_to_le32(trans->mbx_addr_0_step); - prph_sc_ctrl->step_cfg.mbx_addr_1 = cpu_to_le32(trans->mbx_addr_1_step); + prph_sc_ctrl->step_cfg.mbx_addr_0 = + cpu_to_le32(trans->conf.mbx_addr_0_step); + prph_sc_ctrl->step_cfg.mbx_addr_1 = + cpu_to_le32(trans->conf.mbx_addr_1_step); /* allocate ucode sections in dram and set addresses */ - ret = iwl_pcie_init_fw_sec(trans, fw, &prph_scratch->dram); + ret = iwl_pcie_init_fw_sec(trans, img, &prph_scratch->dram.common); if (ret) goto err_free_prph_scratch; - /* Allocate prph information * currently we don't assign to the prph info anything, but it would get * assigned later @@ -203,42 +213,58 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, } /* Allocate context info */ - ctxt_info_gen3 = dma_alloc_coherent(trans->dev, - sizeof(*ctxt_info_gen3), - &trans_pcie->ctxt_info_dma_addr, - GFP_KERNEL); - if (!ctxt_info_gen3) { + ctxt_info_v2 = dma_alloc_coherent(trans->dev, + sizeof(*ctxt_info_v2), + &trans_pcie->ctxt_info_dma_addr, + GFP_KERNEL); + if (!ctxt_info_v2) { ret = -ENOMEM; goto err_free_prph_info; } - ctxt_info_gen3->prph_info_base_addr = + ctxt_info_v2->prph_info_base_addr = cpu_to_le64(trans_pcie->prph_info_dma_addr); - ctxt_info_gen3->prph_scratch_base_addr = + ctxt_info_v2->prph_scratch_base_addr = cpu_to_le64(trans_pcie->prph_scratch_dma_addr); - ctxt_info_gen3->prph_scratch_size = - cpu_to_le32(sizeof(*prph_scratch)); - ctxt_info_gen3->cr_head_idx_arr_base_addr = + + /* + * This code assumes the FSEQ is last and we can make that + * optional; old devices _should_ be fine with a bigger size, + * but in simulation we check the size more precisely. + */ + BUILD_BUG_ON(offsetofend(typeof(*prph_scratch), dram.common) + + sizeof(prph_scratch->dram.fseq_img) != + sizeof(*prph_scratch)); + if (control_flags_ext & IWL_PRPH_SCRATCH_EXT_EXT_FSEQ) + ctxt_info_v2->prph_scratch_size = + cpu_to_le32(sizeof(*prph_scratch)); + else + ctxt_info_v2->prph_scratch_size = + cpu_to_le32(offsetofend(typeof(*prph_scratch), + dram.common)); + + ctxt_info_v2->cr_head_idx_arr_base_addr = cpu_to_le64(trans_pcie->rxq->rb_stts_dma); - ctxt_info_gen3->tr_tail_idx_arr_base_addr = + ctxt_info_v2->tr_tail_idx_arr_base_addr = cpu_to_le64(trans_pcie->prph_info_dma_addr + PAGE_SIZE / 2); - ctxt_info_gen3->cr_tail_idx_arr_base_addr = + ctxt_info_v2->cr_tail_idx_arr_base_addr = cpu_to_le64(trans_pcie->prph_info_dma_addr + 3 * PAGE_SIZE / 4); - ctxt_info_gen3->mtr_base_addr = - cpu_to_le64(trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]->dma_addr); - ctxt_info_gen3->mcr_base_addr = + ctxt_info_v2->mtr_base_addr = + cpu_to_le64(trans_pcie->txqs.txq[trans->conf.cmd_queue]->dma_addr); + ctxt_info_v2->mcr_base_addr = cpu_to_le64(trans_pcie->rxq->used_bd_dma); - ctxt_info_gen3->mtr_size = + ctxt_info_v2->mtr_size = cpu_to_le16(TFD_QUEUE_CB_SIZE(cmdq_size)); - ctxt_info_gen3->mcr_size = - cpu_to_le16(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds)); + ctxt_info_v2->mcr_size = + cpu_to_le16(RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans))); - trans_pcie->ctxt_info_gen3 = ctxt_info_gen3; + trans_pcie->ctxt_info_v2 = ctxt_info_v2; trans_pcie->prph_info = prph_info; trans_pcie->prph_scratch = prph_scratch; /* Allocate IML */ - trans_pcie->iml = dma_alloc_coherent(trans->dev, trans->iml_len, + trans_pcie->iml_len = fw->iml_len; + trans_pcie->iml = dma_alloc_coherent(trans->dev, fw->iml_len, &trans_pcie->iml_dma_addr, GFP_KERNEL); if (!trans_pcie->iml) { @@ -246,27 +272,15 @@ int iwl_pcie_ctxt_info_gen3_init(struct iwl_trans *trans, goto err_free_ctxt_info; } - memcpy(trans_pcie->iml, trans->iml, trans->iml_len); - - iwl_enable_fw_load_int_ctx_info(trans); - - /* kick FW self load */ - iwl_write64(trans, CSR_CTXT_INFO_ADDR, - trans_pcie->ctxt_info_dma_addr); - iwl_write64(trans, CSR_IML_DATA_ADDR, - trans_pcie->iml_dma_addr); - iwl_write32(trans, CSR_IML_SIZE_ADDR, trans->iml_len); - - iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL, - CSR_AUTO_FUNC_BOOT_ENA); + memcpy(trans_pcie->iml, fw->iml, fw->iml_len); return 0; err_free_ctxt_info: - dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_gen3), - trans_pcie->ctxt_info_gen3, + dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_v2), + trans_pcie->ctxt_info_v2, trans_pcie->ctxt_info_dma_addr); - trans_pcie->ctxt_info_gen3 = NULL; + trans_pcie->ctxt_info_v2 = NULL; err_free_prph_info: dma_free_coherent(trans->dev, PAGE_SIZE, prph_info, trans_pcie->prph_info_dma_addr); @@ -280,14 +294,31 @@ err_free_prph_scratch: } -void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive) +void iwl_pcie_ctxt_info_v2_kick(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_enable_fw_load_int_ctx_info(trans, trans->do_top_reset); + + /* kick FW self load */ + iwl_write64(trans, CSR_CTXT_INFO_ADDR, trans_pcie->ctxt_info_dma_addr); + iwl_write64(trans, CSR_IML_DATA_ADDR, trans_pcie->iml_dma_addr); + iwl_write32(trans, CSR_IML_SIZE_ADDR, trans_pcie->iml_len); + + iwl_set_bit(trans, CSR_CTXT_INFO_BOOT_CTRL, + CSR_AUTO_FUNC_BOOT_ENA); +} + +void iwl_pcie_ctxt_info_v2_free(struct iwl_trans *trans, bool alive) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (trans_pcie->iml) { - dma_free_coherent(trans->dev, trans->iml_len, trans_pcie->iml, + dma_free_coherent(trans->dev, trans_pcie->iml_len, + trans_pcie->iml, trans_pcie->iml_dma_addr); trans_pcie->iml_dma_addr = 0; + trans_pcie->iml_len = 0; trans_pcie->iml = NULL; } @@ -296,15 +327,15 @@ void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive) if (alive) return; - if (!trans_pcie->ctxt_info_gen3) + if (!trans_pcie->ctxt_info_v2) return; - /* ctxt_info_gen3 and prph_scratch are still needed for PNVM load */ - dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_gen3), - trans_pcie->ctxt_info_gen3, + /* ctxt_info_v2 and prph_scratch are still needed for PNVM load */ + dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info_v2), + trans_pcie->ctxt_info_v2, trans_pcie->ctxt_info_dma_addr); trans_pcie->ctxt_info_dma_addr = 0; - trans_pcie->ctxt_info_gen3 = NULL; + trans_pcie->ctxt_info_v2 = NULL; dma_free_coherent(trans->dev, sizeof(*trans_pcie->prph_scratch), trans_pcie->prph_scratch, @@ -319,9 +350,9 @@ void iwl_pcie_ctxt_info_gen3_free(struct iwl_trans *trans, bool alive) trans_pcie->prph_info = NULL; } -static int iwl_pcie_load_payloads_continuously(struct iwl_trans *trans, - const struct iwl_pnvm_image *pnvm_data, - struct iwl_dram_data *dram) +static int iwl_pcie_load_payloads_contig(struct iwl_trans *trans, + const struct iwl_pnvm_image *pnvm_data, + struct iwl_dram_data *dram) { u32 len, len0, len1; @@ -408,9 +439,9 @@ static int iwl_pcie_load_payloads_segments } -int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans, - const struct iwl_pnvm_image *pnvm_payloads, - const struct iwl_ucode_capabilities *capa) +int iwl_trans_pcie_ctx_info_v2_load_pnvm(struct iwl_trans *trans, + const struct iwl_pnvm_image *pnvm_payloads, + const struct iwl_ucode_capabilities *capa) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl = @@ -425,7 +456,7 @@ int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans, if (WARN_ON(prph_sc_ctrl->pnvm_cfg.pnvm_size)) return -EBUSY; - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return 0; if (!pnvm_payloads->n_chunks) { @@ -442,10 +473,8 @@ int iwl_trans_pcie_ctx_info_gen3_load_pnvm(struct iwl_trans *trans, trans->pnvm_loaded = true; } else { /* save only in one DRAM section */ - ret = iwl_pcie_load_payloads_continuously - (trans, - pnvm_payloads, - &dram_regions->drams[0]); + ret = iwl_pcie_load_payloads_contig(trans, pnvm_payloads, + &dram_regions->drams[0]); if (!ret) { dram_regions->n_regions = 1; trans->pnvm_loaded = true; @@ -480,7 +509,7 @@ static void iwl_pcie_set_pnvm_segments(struct iwl_trans *trans) cpu_to_le32(iwl_dram_regions_size(dram_regions)); } -static void iwl_pcie_set_continuous_pnvm(struct iwl_trans *trans) +static void iwl_pcie_set_contig_pnvm(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl = @@ -492,21 +521,21 @@ static void iwl_pcie_set_continuous_pnvm(struct iwl_trans *trans) cpu_to_le32(trans_pcie->pnvm_data.drams[0].size); } -void iwl_trans_pcie_ctx_info_gen3_set_pnvm(struct iwl_trans *trans, - const struct iwl_ucode_capabilities *capa) +void iwl_trans_pcie_ctx_info_v2_set_pnvm(struct iwl_trans *trans, + const struct iwl_ucode_capabilities *capa) { - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return; if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_FRAGMENTED_PNVM_IMG)) iwl_pcie_set_pnvm_segments(trans); else - iwl_pcie_set_continuous_pnvm(trans); + iwl_pcie_set_contig_pnvm(trans); } -int iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans, - const struct iwl_pnvm_image *payloads, - const struct iwl_ucode_capabilities *capa) +int iwl_trans_pcie_ctx_info_v2_load_reduce_power(struct iwl_trans *trans, + const struct iwl_pnvm_image *payloads, + const struct iwl_ucode_capabilities *capa) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl = @@ -518,7 +547,7 @@ int iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans, if (trans->reduce_power_loaded) return 0; - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return 0; if (WARN_ON(prph_sc_ctrl->reduce_power_cfg.size)) @@ -538,10 +567,8 @@ int iwl_trans_pcie_ctx_info_gen3_load_reduce_power(struct iwl_trans *trans, trans->reduce_power_loaded = true; } else { /* save only in one DRAM section */ - ret = iwl_pcie_load_payloads_continuously - (trans, - payloads, - &dram_regions->drams[0]); + ret = iwl_pcie_load_payloads_contig(trans, payloads, + &dram_regions->drams[0]); if (!ret) { dram_regions->n_regions = 1; trans->reduce_power_loaded = true; @@ -564,7 +591,7 @@ static void iwl_pcie_set_reduce_power_segments(struct iwl_trans *trans) cpu_to_le32(iwl_dram_regions_size(dram_regions)); } -static void iwl_pcie_set_continuous_reduce_power(struct iwl_trans *trans) +static void iwl_pcie_set_contig_reduce_power(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_prph_scratch_ctrl_cfg *prph_sc_ctrl = @@ -577,15 +604,15 @@ static void iwl_pcie_set_continuous_reduce_power(struct iwl_trans *trans) } void -iwl_trans_pcie_ctx_info_gen3_set_reduce_power(struct iwl_trans *trans, - const struct iwl_ucode_capabilities *capa) +iwl_trans_pcie_ctx_info_v2_set_reduce_power(struct iwl_trans *trans, + const struct iwl_ucode_capabilities *capa) { - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) return; if (fw_has_capa(capa, IWL_UCODE_TLV_CAPA_FRAGMENTED_PNVM_IMG)) iwl_pcie_set_reduce_power_segments(trans); else - iwl_pcie_set_continuous_reduce_power(trans); + iwl_pcie_set_contig_reduce_power(trans); } diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c index 344e4d5a1c6e..cb36baac14da 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/ctxt-info.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include "iwl-trans.h" #include "iwl-fh.h" @@ -83,7 +83,7 @@ void iwl_pcie_ctxt_info_free_paging(struct iwl_trans *trans) int iwl_pcie_init_fw_sec(struct iwl_trans *trans, const struct fw_img *fw, - struct iwl_context_info_dram *ctxt_dram) + struct iwl_context_info_dram_nonfseq *ctxt_dram) { struct iwl_self_init_dram *dram = &trans->init_dram; int i, ret, lmac_cnt, umac_cnt, paging_cnt; @@ -161,7 +161,7 @@ int iwl_pcie_init_fw_sec(struct iwl_trans *trans, } int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, - const struct fw_img *fw) + const struct fw_img *img) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_context_info *ctxt_info; @@ -180,11 +180,11 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, ctxt_info->version.version = 0; ctxt_info->version.mac_id = - cpu_to_le16((u16)trans->hw_rev); + cpu_to_le16((u16)trans->info.hw_rev); /* size is in DWs */ ctxt_info->version.size = cpu_to_le16(sizeof(*ctxt_info) / 4); - switch (trans_pcie->rx_buf_size) { + switch (trans->conf.rx_buf_size) { case IWL_AMSDU_2K: rb_size = IWL_CTXT_INFO_RB_SIZE_2K; break; @@ -202,10 +202,10 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, rb_size = IWL_CTXT_INFO_RB_SIZE_4K; } - WARN_ON(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds) > 12); + WARN_ON(RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans)) > 12); control_flags = IWL_CTXT_INFO_TFD_FORMAT_LONG; control_flags |= - u32_encode_bits(RX_QUEUE_CB_SIZE(trans->cfg->num_rbds), + u32_encode_bits(RX_QUEUE_CB_SIZE(iwl_trans_get_num_rbds(trans)), IWL_CTXT_INFO_RB_CB_SIZE); control_flags |= u32_encode_bits(rb_size, IWL_CTXT_INFO_RB_SIZE); ctxt_info->control.control_flags = cpu_to_le32(control_flags); @@ -218,12 +218,12 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, /* initialize TX command queue */ ctxt_info->hcmd_cfg.cmd_queue_addr = - cpu_to_le64(trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]->dma_addr); + cpu_to_le64(trans_pcie->txqs.txq[trans->conf.cmd_queue]->dma_addr); ctxt_info->hcmd_cfg.cmd_queue_size = TFD_QUEUE_CB_SIZE(IWL_CMD_QUEUE_SIZE); /* allocate ucode sections in dram and set addresses */ - ret = iwl_pcie_init_fw_sec(trans, fw, &ctxt_info->dram); + ret = iwl_pcie_init_fw_sec(trans, img, &ctxt_info->dram); if (ret) { dma_free_coherent(trans->dev, sizeof(*trans_pcie->ctxt_info), ctxt_info, trans_pcie->ctxt_info_dma_addr); @@ -232,7 +232,7 @@ int iwl_pcie_ctxt_info_init(struct iwl_trans *trans, trans_pcie->ctxt_info = ctxt_info; - iwl_enable_fw_load_int_ctx_info(trans); + iwl_enable_fw_load_int_ctx_info(trans, false); /* Configure debug, if exists */ if (iwl_pcie_dbg_on(trans)) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c index e0b657b2f74b..656f8b06c27b 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/drv.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/drv.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2005-2014, 2018-2024 Intel Corporation + * Copyright (C) 2005-2014, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -17,15 +17,13 @@ #include "iwl-prph.h" #include "internal.h" -#define TRANS_CFG_MARKER BIT(0) #define _IS_A(cfg, _struct) __builtin_types_compatible_p(typeof(cfg), \ struct _struct) extern int _invalid_type; -#define _TRANS_CFG_MARKER(cfg) \ - (__builtin_choose_expr(_IS_A(cfg, iwl_cfg_trans_params), \ - TRANS_CFG_MARKER, \ - __builtin_choose_expr(_IS_A(cfg, iwl_cfg), 0, _invalid_type))) -#define _ASSIGN_CFG(cfg) (_TRANS_CFG_MARKER(cfg) + (kernel_ulong_t)&(cfg)) +#define _TRANS_CFG_CHECK(cfg) \ + (__builtin_choose_expr(_IS_A(cfg, iwl_mac_cfg), \ + 0, _invalid_type)) +#define _ASSIGN_CFG(cfg) (_TRANS_CFG_CHECK(cfg) + (kernel_ulong_t)&(cfg)) #define IWL_PCI_DEVICE(dev, subdev, cfg) \ .vendor = PCI_VENDOR_ID_INTEL, .device = (dev), \ @@ -35,1170 +33,1038 @@ extern int _invalid_type; /* Hardware specific file defines the PCI IDs table for that hardware module */ VISIBLE_IF_IWLWIFI_KUNIT const struct pci_device_id iwl_hw_card_ids[] = { #if IS_ENABLED(CONFIG_IWLDVM) - {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5100_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5100_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5100_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5100_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5100_bgn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5100_bgn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5100_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5100_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1201, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1301, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1204, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1304, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1205, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1305, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1206, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1306, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1221, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1321, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1224, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1324, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1225, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1325, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1226, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4232, 0x1326, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1211, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1311, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1214, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1314, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1215, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1315, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1216, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4237, 0x1316, iwl5000_mac_cfg)}, /* Half Mini Card */ /* 5300 Series WiFi */ - {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5300_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5300_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5300_agn_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1021, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1121, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1024, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1124, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1001, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1101, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1004, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4235, 0x1104, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1011, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1111, iwl5000_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1014, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x4236, 0x1114, iwl5000_mac_cfg)}, /* Half Mini Card */ /* 5350 Series WiFi/WiMax */ - {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5350_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5350_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5350_agn_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423A, 0x1001, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423A, 0x1021, iwl5000_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423B, 0x1011, iwl5000_mac_cfg)}, /* Mini Card */ /* 5150 Series Wifi/WiMax */ - {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_abg_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_abg_cfg)}, /* Half Mini Card */ - - {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_agn_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_agn_cfg)}, /* Half Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_abg_cfg)}, /* Mini Card */ - {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_abg_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1201, iwl5150_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1301, iwl5150_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1206, iwl5150_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1306, iwl5150_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1221, iwl5150_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1321, iwl5150_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423C, 0x1326, iwl5150_mac_cfg)}, /* Half Mini Card */ + + {IWL_PCI_DEVICE(0x423D, 0x1211, iwl5150_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1311, iwl5150_mac_cfg)}, /* Half Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1216, iwl5150_mac_cfg)}, /* Mini Card */ + {IWL_PCI_DEVICE(0x423D, 0x1316, iwl5150_mac_cfg)}, /* Half Mini Card */ /* 6x00 Series */ - {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_2abg_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_2bg_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_2abg_cfg)}, - {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_3agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_2agn_cfg)}, - {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_2abg_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1101, iwl6000_mac_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1108, iwl6000_mac_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1121, iwl6000_mac_cfg)}, + {IWL_PCI_DEVICE(0x422B, 0x1128, iwl6000_mac_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1301, iwl6000i_mac_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1306, iwl6000i_mac_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1307, iwl6000i_mac_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1321, iwl6000i_mac_cfg)}, + {IWL_PCI_DEVICE(0x422C, 0x1326, iwl6000i_mac_cfg)}, + {IWL_PCI_DEVICE(0x4238, 0x1111, iwl6000_mac_cfg)}, + {IWL_PCI_DEVICE(0x4238, 0x1118, iwl6000_mac_cfg)}, + {IWL_PCI_DEVICE(0x4239, 0x1311, iwl6000i_mac_cfg)}, + {IWL_PCI_DEVICE(0x4239, 0x1316, iwl6000i_mac_cfg)}, /* 6x05 Series */ - {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_2agn_d_cfg)}, - {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_2agn_mow1_cfg)},/* low 5GHz active */ - {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_2agn_mow2_cfg)},/* high 5GHz active */ + {IWL_PCI_DEVICE(0x0082, 0x1301, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1306, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1307, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1308, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1321, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1326, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1328, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0x1311, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0x1318, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0x1316, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0xC020, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0xC220, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0085, 0xC228, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x4820, iwl6005_mac_cfg)}, + {IWL_PCI_DEVICE(0x0082, 0x1304, iwl6005_mac_cfg)},/* low 5GHz active */ + {IWL_PCI_DEVICE(0x0082, 0x1305, iwl6005_mac_cfg)},/* high 5GHz active */ /* 6x30 Series */ - {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1030_bgn_cfg)}, - {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1030_bg_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_2bg_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_2abg_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5305, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5307, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5325, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x008A, 0x5327, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x008B, 0x5315, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x008B, 0x5317, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5211, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5215, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0090, 0x5216, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5201, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5205, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5206, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5207, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5221, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5225, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0091, 0x5226, iwl6030_mac_cfg)}, /* 6x50 WiFi/WiMax Series */ - {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_2abg_cfg)}, - {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_2agn_cfg)}, - {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_2abg_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1301, iwl6050_mac_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1306, iwl6050_mac_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1321, iwl6050_mac_cfg)}, + {IWL_PCI_DEVICE(0x0087, 0x1326, iwl6050_mac_cfg)}, + {IWL_PCI_DEVICE(0x0089, 0x1311, iwl6050_mac_cfg)}, + {IWL_PCI_DEVICE(0x0089, 0x1316, iwl6050_mac_cfg)}, /* 6150 WiFi/WiMax Series */ - {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_bg_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_bg_cfg)}, - {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_bg_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1305, iwl6150_mac_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1307, iwl6150_mac_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1325, iwl6150_mac_cfg)}, + {IWL_PCI_DEVICE(0x0885, 0x1327, iwl6150_mac_cfg)}, + {IWL_PCI_DEVICE(0x0886, 0x1315, iwl6150_mac_cfg)}, + {IWL_PCI_DEVICE(0x0886, 0x1317, iwl6150_mac_cfg)}, /* 1000 Series WiFi */ - {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_bg_cfg)}, - {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_bg_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1205, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1305, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1225, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1325, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1215, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1315, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1206, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1306, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1226, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0083, 0x1326, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1216, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0084, 0x1316, iwl1000_mac_cfg)}, /* 100 Series WiFi */ - {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl100_bg_cfg)}, - {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl100_bg_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl100_bgn_cfg)}, - {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl100_bg_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1005, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1007, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08AF, 0x1015, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08AF, 0x1017, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1025, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08AE, 0x1027, iwl1000_mac_cfg)}, /* 130 Series WiFi */ - {IWL_PCI_DEVICE(0x0896, 0x5005, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5007, iwl130_bg_cfg)}, - {IWL_PCI_DEVICE(0x0897, 0x5015, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0897, 0x5017, iwl130_bg_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5025, iwl130_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0896, 0x5027, iwl130_bg_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5005, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5007, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0897, 0x5015, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0897, 0x5017, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5025, iwl1000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0896, 0x5027, iwl1000_mac_cfg)}, /* 2x00 Series */ - {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_2bgn_d_cfg)}, + {IWL_PCI_DEVICE(0x0890, 0x4022, iwl2000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0891, 0x4222, iwl2000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0890, 0x4422, iwl2000_mac_cfg)}, + {IWL_PCI_DEVICE(0x0890, 0x4822, iwl2000_mac_cfg)}, /* 2x30 Series */ - {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_2bgn_cfg)}, - {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_2bgn_cfg)}, + {IWL_PCI_DEVICE(0x0887, 0x4062, iwl2030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0888, 0x4262, iwl2030_mac_cfg)}, + {IWL_PCI_DEVICE(0x0887, 0x4462, iwl2030_mac_cfg)}, /* 6x35 Series */ - {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6035_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6035_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6035_2agn_sff_cfg)}, - {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6035_2agn_cfg)}, - {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6035_2agn_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x4060, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x406A, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x088F, 0x4260, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x088F, 0x426A, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x4460, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x446A, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x088E, 0x4860, iwl6030_mac_cfg)}, + {IWL_PCI_DEVICE(0x088F, 0x5260, iwl6030_mac_cfg)}, /* 105 Series */ - {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_bgn_d_cfg)}, + {IWL_PCI_DEVICE(0x0894, 0x0022, iwl105_mac_cfg)}, + {IWL_PCI_DEVICE(0x0895, 0x0222, iwl105_mac_cfg)}, + {IWL_PCI_DEVICE(0x0894, 0x0422, iwl105_mac_cfg)}, + {IWL_PCI_DEVICE(0x0894, 0x0822, iwl105_mac_cfg)}, /* 135 Series */ - {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_bgn_cfg)}, - {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_bgn_cfg)}, + {IWL_PCI_DEVICE(0x0892, 0x0062, iwl135_mac_cfg)}, + {IWL_PCI_DEVICE(0x0893, 0x0262, iwl135_mac_cfg)}, + {IWL_PCI_DEVICE(0x0892, 0x0462, iwl135_mac_cfg)}, #endif /* CONFIG_IWLDVM */ #if IS_ENABLED(CONFIG_IWLMVM) /* 7260 Series */ - {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4C60, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4C70, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7260_2ac_cfg_high_temp)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7260_2ac_cfg_high_temp)}, - {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7260_2ac_cfg_high_temp)}, - {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xCC70, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xCC60, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7260_n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7260_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7260_2n_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4070, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4072, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4170, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4C60, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4C70, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4060, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x406A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4160, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4062, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4162, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4270, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4272, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4260, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x426A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4262, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4470, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4472, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4460, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x446A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4462, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4870, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x486E, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A70, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6E, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4A6C, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4570, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4560, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4370, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4360, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5070, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5072, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5170, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x5770, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4020, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x402A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0x4220, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0x4420, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC070, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC072, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC170, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC060, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC06A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC160, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC062, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC162, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC770, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC760, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC270, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xCC70, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xCC60, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC272, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC260, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC26A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC262, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC470, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC472, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC460, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC462, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC570, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC560, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC370, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC360, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC020, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC02A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B2, 0xC220, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B1, 0xC420, iwl7000_mac_cfg)}, /* 3160 Series */ - {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl3160_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl3160_n_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl3160_2n_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl3160_n_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x8370, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B4, 0x8272, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl3160_2ac_cfg)}, - {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl3160_2ac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0070, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0072, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0170, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0172, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0060, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0062, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0270, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0272, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0470, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x0472, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x0370, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8070, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8072, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8170, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8172, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8060, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8062, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8270, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8370, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B4, 0x8272, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8470, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x8570, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x1070, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x08B3, 0x1170, iwl7000_mac_cfg)}, /* 3165 Series */ - {IWL_PCI_DEVICE(0x3165, 0x4010, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4012, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3166, 0x4212, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4410, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4510, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x4110, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3166, 0x4310, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3166, 0x4210, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x8010, iwl3165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x3165, 0x8110, iwl3165_2ac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4010, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4012, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4212, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4410, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4510, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x4110, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4310, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3166, 0x4210, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x8010, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x3165, 0x8110, iwl7000_mac_cfg)}, /* 3168 Series */ - {IWL_PCI_DEVICE(0x24FB, 0x2010, iwl3168_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FB, 0x2110, iwl3168_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FB, 0x2050, iwl3168_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FB, 0x2150, iwl3168_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FB, 0x0000, iwl3168_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24FB, 0x2010, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FB, 0x2110, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FB, 0x2050, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FB, 0x2150, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FB, 0x0000, iwl7000_mac_cfg)}, /* 7265 Series */ - {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7265_n_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5C10, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7265_n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7265_n_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7265_n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x9210, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x9310, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7265_2n_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x095A, 0x9E10, iwl7265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5010, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5110, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5100, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5310, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5302, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5210, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5C10, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5012, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5412, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5410, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5510, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5400, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x1010, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5000, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x500A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5200, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5002, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5102, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5202, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9010, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9012, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x900A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9110, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9112, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9210, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9200, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9510, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x9310, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9410, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5020, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x502A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5420, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5090, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5190, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5590, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5290, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5490, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x5F10, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x5212, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095B, 0x520A, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9000, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9400, iwl7000_mac_cfg)}, + {IWL_PCI_DEVICE(0x095A, 0x9E10, iwl7000_mac_cfg)}, /* 8000 Series */ - {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x10B0, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0130, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1130, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0132, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1132, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x01F0, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0012, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1012, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x1150, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xD0B0, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0xB0B0, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x8110, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x9110, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0xC030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F4, 0xD030, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x8130, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x9130, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x8132, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x9132, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x8150, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x9150, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8260_2n_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0044, iwl8260_2n_cfg)}, - {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl4165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl4165_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24F3, 0x4010, iwl8260_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0110, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x1110, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x1130, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0130, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x1010, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x10D0, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0050, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0150, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x9010, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x8110, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x8050, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0810, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x9110, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x8130, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0910, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0930, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0950, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0850, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x1014, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x3E02, iwl8275_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x3E01, iwl8275_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x1012, iwl8275_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0012, iwl8275_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x0014, iwl8265_2ac_cfg)}, - {IWL_PCI_DEVICE(0x24FD, 0x9074, iwl8265_2ac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x10B0, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0130, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1130, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0132, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1132, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x01F0, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0012, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1012, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0050, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0250, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1050, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0150, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x1150, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x0030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x1030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC050, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD050, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD0B0, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xB0B0, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x8030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0x9030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0xC030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F4, 0xD030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8130, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9130, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8132, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9132, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8050, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x8150, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9050, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x9150, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0004, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0044, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F5, 0x0010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F6, 0x0030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0810, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0910, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0850, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0950, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0930, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x0000, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0x4010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xC030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24F3, 0xD030, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1130, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0130, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x10D0, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0050, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0150, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x9010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8050, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8010, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0810, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x9110, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x8130, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0910, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0930, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0950, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0850, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1014, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x3E02, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x3E01, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1012, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0012, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x0014, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x9074, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1431, iwl8000_mac_cfg)}, + {IWL_PCI_DEVICE(0x24FD, 0x1432, iwl8000_mac_cfg)}, /* 9000 Series */ - {IWL_PCI_DEVICE(0x2526, PCI_ANY_ID, iwl9000_trans_cfg)}, - {IWL_PCI_DEVICE(0x271B, PCI_ANY_ID, iwl9000_trans_cfg)}, - {IWL_PCI_DEVICE(0x271C, PCI_ANY_ID, iwl9000_trans_cfg)}, - {IWL_PCI_DEVICE(0x30DC, PCI_ANY_ID, iwl9560_long_latency_trans_cfg)}, - {IWL_PCI_DEVICE(0x31DC, PCI_ANY_ID, iwl9560_shared_clk_trans_cfg)}, - {IWL_PCI_DEVICE(0x9DF0, PCI_ANY_ID, iwl9560_trans_cfg)}, - {IWL_PCI_DEVICE(0xA370, PCI_ANY_ID, iwl9560_trans_cfg)}, + {IWL_PCI_DEVICE(0x2526, PCI_ANY_ID, iwl9000_mac_cfg)}, + {IWL_PCI_DEVICE(0x271B, PCI_ANY_ID, iwl9000_mac_cfg)}, + {IWL_PCI_DEVICE(0x271C, PCI_ANY_ID, iwl9000_mac_cfg)}, + {IWL_PCI_DEVICE(0x30DC, PCI_ANY_ID, iwl9560_long_latency_mac_cfg)}, + {IWL_PCI_DEVICE(0x31DC, PCI_ANY_ID, iwl9560_shared_clk_mac_cfg)}, + {IWL_PCI_DEVICE(0x9DF0, PCI_ANY_ID, iwl9560_mac_cfg)}, + {IWL_PCI_DEVICE(0xA370, PCI_ANY_ID, iwl9560_mac_cfg)}, /* Qu devices */ - {IWL_PCI_DEVICE(0x02F0, PCI_ANY_ID, iwl_qu_trans_cfg)}, - {IWL_PCI_DEVICE(0x06F0, PCI_ANY_ID, iwl_qu_trans_cfg)}, + {IWL_PCI_DEVICE(0x02F0, PCI_ANY_ID, iwl_qu_mac_cfg)}, + {IWL_PCI_DEVICE(0x06F0, PCI_ANY_ID, iwl_qu_mac_cfg)}, - {IWL_PCI_DEVICE(0x34F0, PCI_ANY_ID, iwl_qu_medium_latency_trans_cfg)}, - {IWL_PCI_DEVICE(0x3DF0, PCI_ANY_ID, iwl_qu_medium_latency_trans_cfg)}, - {IWL_PCI_DEVICE(0x4DF0, PCI_ANY_ID, iwl_qu_medium_latency_trans_cfg)}, + {IWL_PCI_DEVICE(0x34F0, PCI_ANY_ID, iwl_qu_medium_latency_mac_cfg)}, + {IWL_PCI_DEVICE(0x3DF0, PCI_ANY_ID, iwl_qu_medium_latency_mac_cfg)}, + {IWL_PCI_DEVICE(0x4DF0, PCI_ANY_ID, iwl_qu_medium_latency_mac_cfg)}, - {IWL_PCI_DEVICE(0x43F0, PCI_ANY_ID, iwl_qu_long_latency_trans_cfg)}, - {IWL_PCI_DEVICE(0xA0F0, PCI_ANY_ID, iwl_qu_long_latency_trans_cfg)}, + {IWL_PCI_DEVICE(0x43F0, PCI_ANY_ID, iwl_qu_long_latency_mac_cfg)}, + {IWL_PCI_DEVICE(0xA0F0, PCI_ANY_ID, iwl_qu_long_latency_mac_cfg)}, - {IWL_PCI_DEVICE(0x2723, PCI_ANY_ID, iwl_ax200_trans_cfg)}, + {IWL_PCI_DEVICE(0x2723, PCI_ANY_ID, iwl_ax200_mac_cfg)}, -/* So devices */ - {IWL_PCI_DEVICE(0x2725, PCI_ANY_ID, iwl_so_trans_cfg)}, - {IWL_PCI_DEVICE(0x7A70, PCI_ANY_ID, iwl_so_long_latency_imr_trans_cfg)}, - {IWL_PCI_DEVICE(0x7AF0, PCI_ANY_ID, iwl_so_trans_cfg)}, - {IWL_PCI_DEVICE(0x51F0, PCI_ANY_ID, iwl_so_long_latency_trans_cfg)}, - {IWL_PCI_DEVICE(0x51F1, PCI_ANY_ID, iwl_so_long_latency_imr_trans_cfg)}, - {IWL_PCI_DEVICE(0x54F0, PCI_ANY_ID, iwl_so_long_latency_trans_cfg)}, - {IWL_PCI_DEVICE(0x7F70, PCI_ANY_ID, iwl_so_trans_cfg)}, +/* Ty/So devices */ + {IWL_PCI_DEVICE(0x2725, PCI_ANY_ID, iwl_ty_mac_cfg)}, + {IWL_PCI_DEVICE(0x7A70, PCI_ANY_ID, iwl_so_long_latency_imr_mac_cfg)}, + {IWL_PCI_DEVICE(0x7AF0, PCI_ANY_ID, iwl_so_mac_cfg)}, + {IWL_PCI_DEVICE(0x51F0, PCI_ANY_ID, iwl_so_long_latency_mac_cfg)}, + {IWL_PCI_DEVICE(0x51F1, PCI_ANY_ID, iwl_so_long_latency_imr_mac_cfg)}, + {IWL_PCI_DEVICE(0x54F0, PCI_ANY_ID, iwl_so_long_latency_mac_cfg)}, + {IWL_PCI_DEVICE(0x7F70, PCI_ANY_ID, iwl_so_mac_cfg)}, /* Ma devices */ - {IWL_PCI_DEVICE(0x2729, PCI_ANY_ID, iwl_ma_trans_cfg)}, - {IWL_PCI_DEVICE(0x7E40, PCI_ANY_ID, iwl_ma_trans_cfg)}, - + {IWL_PCI_DEVICE(0x2729, PCI_ANY_ID, iwl_ma_mac_cfg)}, + {IWL_PCI_DEVICE(0x7E40, PCI_ANY_ID, iwl_ma_mac_cfg)}, +#endif /* CONFIG_IWLMVM */ +#if IS_ENABLED(CONFIG_IWLMLD) /* Bz devices */ - {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_gl_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0000, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0090, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0094, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0098, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x009C, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x00C0, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x00C4, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x00E0, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x00E4, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x00E8, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x00EC, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0100, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0110, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0114, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0118, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x011C, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0310, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0314, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0510, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x0A10, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x1671, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x1672, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x1771, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x1772, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x1791, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x1792, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x4090, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x40C4, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x40E0, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x4110, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0xA840, 0x4314, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_trans_cfg)}, - {IWL_PCI_DEVICE(0x4D40, PCI_ANY_ID, iwl_bz_trans_cfg)}, + {IWL_PCI_DEVICE(0x272b, PCI_ANY_ID, iwl_gl_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0000, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0090, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0094, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0098, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x009C, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x00C0, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x00C4, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x00E0, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x00E4, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x00E8, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x00EC, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0100, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0110, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0114, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0118, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x011C, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0310, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0314, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0510, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x0A10, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1671, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1672, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1771, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1772, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1791, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1792, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x4090, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x40C4, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x40E0, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x4110, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x4314, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1775, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0xA840, 0x1776, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0x7740, PCI_ANY_ID, iwl_bz_mac_cfg)}, + {IWL_PCI_DEVICE(0x4D40, PCI_ANY_ID, iwl_bz_mac_cfg)}, /* Sc devices */ - {IWL_PCI_DEVICE(0xE440, PCI_ANY_ID, iwl_sc_trans_cfg)}, - {IWL_PCI_DEVICE(0xE340, PCI_ANY_ID, iwl_sc_trans_cfg)}, - {IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_trans_cfg)}, - {IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_trans_cfg)}, - -/* Dr devices */ - {IWL_PCI_DEVICE(0x272F, PCI_ANY_ID, iwl_dr_trans_cfg)}, -#endif /* CONFIG_IWLMVM */ + {IWL_PCI_DEVICE(0xE440, PCI_ANY_ID, iwl_sc_mac_cfg)}, + {IWL_PCI_DEVICE(0xE340, PCI_ANY_ID, iwl_sc_mac_cfg)}, + {IWL_PCI_DEVICE(0xD340, PCI_ANY_ID, iwl_sc_mac_cfg)}, + {IWL_PCI_DEVICE(0x6E70, PCI_ANY_ID, iwl_sc_mac_cfg)}, +#endif /* CONFIG_IWLMLD */ {0} }; MODULE_DEVICE_TABLE(pci, iwl_hw_card_ids); EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_hw_card_ids); -#define _IWL_DEV_INFO(_device, _subdevice, _mac_type, _mac_step, _rf_type, \ - _rf_id, _rf_step, _no_160, _cores, _cdb, _cfg, _name) \ - { .device = (_device), .subdevice = (_subdevice), .cfg = &(_cfg), \ - .name = _name, .mac_type = _mac_type, .rf_type = _rf_type, .rf_step = _rf_step, \ - .no_160 = _no_160, .cores = _cores, .rf_id = _rf_id, \ - .mac_step = _mac_step, .cdb = _cdb, .jacket = IWL_CFG_ANY } - -#define IWL_DEV_INFO(_device, _subdevice, _cfg, _name) \ - _IWL_DEV_INFO(_device, _subdevice, IWL_CFG_ANY, IWL_CFG_ANY, \ - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, \ - IWL_CFG_ANY, _cfg, _name) +#define _IWL_DEV_INFO(_cfg, _name, ...) { \ + .cfg = &_cfg, \ + .name = _name, \ + .device = IWL_CFG_ANY, \ + .subdevice = IWL_CFG_ANY, \ + .subdevice_m_h = 15, \ + __VA_ARGS__ \ +} +#define IWL_DEV_INFO(_cfg, _name, ...) \ + _IWL_DEV_INFO(_cfg, _name, __VA_ARGS__) + +#define DEVICE(n) .device = (n) +#define SUBDEV(n) .subdevice = (n) +#define _LOWEST_BIT(n) (__builtin_ffs(n) - 1) +#define _BIT_ABOVE_MASK(n) ((n) + (1 << _LOWEST_BIT(n))) +#define _HIGHEST_BIT(n) (__builtin_ffs(_BIT_ABOVE_MASK(n)) - 2) +#define _IS_POW2(n) (((n) & ((n) - 1)) == 0) +#define _IS_CONTIG(n) _IS_POW2(_BIT_ABOVE_MASK(n)) +#define _CHECK_MASK(m) BUILD_BUG_ON_ZERO(!_IS_CONTIG(m)) +#define SUBDEV_MASKED(v, m) .subdevice = (v) + _CHECK_MASK(m), \ + .subdevice_m_l = _LOWEST_BIT(m), \ + .subdevice_m_h = _HIGHEST_BIT(m) +#define RF_TYPE(n) .match_rf_type = 1, \ + .rf_type = IWL_CFG_RF_TYPE_##n +#define RF_STEP(n) .match_rf_step = 1, \ + .rf_step = SILICON_##n##_STEP +#define RF_ID(n) .match_rf_id = 1, \ + .rf_id = IWL_CFG_RF_ID_##n +#define NO_CDB .match_cdb = 1, .cdb = 0 +#define CDB .match_cdb = 1, .cdb = 1 +#define BW_NOT_LIMITED .match_bw_limit = 1, .bw_limit = 0 +#define BW_LIMITED .match_bw_limit = 1, .bw_limit = 1 VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info iwl_dev_info_table[] = { +#if IS_ENABLED(CONFIG_IWLDVM) + IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_agn_name, + DEVICE(0x4232), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_agn_name, + DEVICE(0x4232), SUBDEV_MASKED(0x4, 0xF)), + IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_bgn_name, + DEVICE(0x4232), SUBDEV_MASKED(0x5, 0xF)), + IWL_DEV_INFO(iwl5100_abg_cfg, iwl5100_abg_name, + DEVICE(0x4232), SUBDEV_MASKED(0x6, 0xF)), + IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_agn_name, + DEVICE(0x4237), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_agn_name, + DEVICE(0x4237), SUBDEV_MASKED(0x4, 0xF)), + IWL_DEV_INFO(iwl5100_n_cfg, iwl5100_bgn_name, + DEVICE(0x4237), SUBDEV_MASKED(0x5, 0xF)), + IWL_DEV_INFO(iwl5100_abg_cfg, iwl5100_abg_name, + DEVICE(0x4237), SUBDEV_MASKED(0x6, 0xF)), + +/* 5300 Series WiFi */ + IWL_DEV_INFO(iwl5300_agn_cfg, iwl5300_agn_name, + DEVICE(0x4235), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl5300_agn_cfg, iwl5300_agn_name, + DEVICE(0x4235), SUBDEV_MASKED(0x4, 0xF)), + IWL_DEV_INFO(iwl5300_agn_cfg, iwl5300_agn_name, + DEVICE(0x4236), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl5300_agn_cfg, iwl5300_agn_name, + DEVICE(0x4236), SUBDEV_MASKED(0x4, 0xF)), + +/* 5350 Series WiFi/WiMax */ + IWL_DEV_INFO(iwl5350_agn_cfg, iwl5350_agn_name, + DEVICE(0x423A)), + IWL_DEV_INFO(iwl5350_agn_cfg, iwl5350_agn_name, + DEVICE(0x423B)), + +/* 5150 Series Wifi/WiMax */ + IWL_DEV_INFO(iwl5150_agn_cfg, iwl5150_agn_name, + DEVICE(0x423C), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl5150_abg_cfg, iwl5150_abg_name, + DEVICE(0x423C), SUBDEV_MASKED(0x6, 0xF)), + + IWL_DEV_INFO(iwl5150_agn_cfg, iwl5150_agn_name, + DEVICE(0x423D), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl5150_abg_cfg, iwl5150_abg_name, + DEVICE(0x423D), SUBDEV_MASKED(0x6, 0xF)), + +/* 6x00 Series */ + IWL_DEV_INFO(iwl6000_3agn_cfg, iwl6000_3agn_name, + DEVICE(0x422B), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl6000_3agn_cfg, iwl6000_3agn_name, + DEVICE(0x422B), SUBDEV_MASKED(0x8, 0xF)), + IWL_DEV_INFO(iwl6000i_2agn_cfg, iwl6000i_2agn_name, + DEVICE(0x422C), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl6000i_non_n_cfg, iwl6000i_2abg_name, + DEVICE(0x422C), SUBDEV_MASKED(0x6, 0xF)), + IWL_DEV_INFO(iwl6000i_non_n_cfg, iwl6000i_2bg_name, + DEVICE(0x422C), SUBDEV_MASKED(0x7, 0xF)), + IWL_DEV_INFO(iwl6000_3agn_cfg, iwl6000_3agn_name, + DEVICE(0x4238), SUBDEV(0x1111)), + IWL_DEV_INFO(iwl6000_3agn_cfg, iwl6000_3agn_name, + DEVICE(0x4238), SUBDEV(0x1118)), + IWL_DEV_INFO(iwl6000i_2agn_cfg, iwl6000i_2agn_name, + DEVICE(0x4239), SUBDEV(0x1311)), + IWL_DEV_INFO(iwl6000i_non_n_cfg, iwl6000i_2abg_name, + DEVICE(0x4239), SUBDEV(0x1316)), + +/* 6x05 Series */ + IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_name, + DEVICE(0x0082), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl6005_non_n_cfg, iwl6005_2abg_name, + DEVICE(0x0082), SUBDEV_MASKED(0x6, 0xF)), + IWL_DEV_INFO(iwl6005_non_n_cfg, iwl6005_2bg_name, + DEVICE(0x0082), SUBDEV_MASKED(0x7, 0xF)), + IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_name, + DEVICE(0x0082), SUBDEV_MASKED(0x8, 0xF)), + + IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_name, + DEVICE(0x0085), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_name, + DEVICE(0x0085), SUBDEV_MASKED(0x8, 0xF)), + IWL_DEV_INFO(iwl6005_non_n_cfg, iwl6005_2abg_name, + DEVICE(0x0085), SUBDEV_MASKED(0x6, 0xF)), + + IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_sff_name, + DEVICE(0x0082), SUBDEV_MASKED(0xC000, 0xF000)), + IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_d_name, + DEVICE(0x0082), SUBDEV(0x4820)), + IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_mow1_name, + DEVICE(0x0082), SUBDEV(0x1304)),/* low 5GHz active */ + IWL_DEV_INFO(iwl6005_n_cfg, iwl6005_2agn_mow2_name, + DEVICE(0x0082), SUBDEV(0x1305)),/* high 5GHz active */ + +/* 6x30 Series */ + IWL_DEV_INFO(iwl6030_n_cfg, iwl1030_bgn_name, + DEVICE(0x008A), SUBDEV_MASKED(0x5, 0xF)), + IWL_DEV_INFO(iwl6030_non_n_cfg, iwl1030_bg_name, + DEVICE(0x008A), SUBDEV_MASKED(0x7, 0xF)), + IWL_DEV_INFO(iwl6030_n_cfg, iwl1030_bgn_name, + DEVICE(0x008B), SUBDEV(0x5315)), + IWL_DEV_INFO(iwl6030_non_n_cfg, iwl1030_bg_name, + DEVICE(0x008B), SUBDEV(0x5317)), + IWL_DEV_INFO(iwl6030_n_cfg, iwl6030_2agn_name, + DEVICE(0x0090), SUBDEV(0x5211)), + IWL_DEV_INFO(iwl6030_n_cfg, iwl6030_2bgn_name, + DEVICE(0x0090), SUBDEV(0x5215)), + IWL_DEV_INFO(iwl6030_non_n_cfg, iwl6030_2abg_name, + DEVICE(0x0090), SUBDEV(0x5216)), + IWL_DEV_INFO(iwl6030_n_cfg, iwl6030_2agn_name, + DEVICE(0x0091), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl6030_n_cfg, iwl6030_2bgn_name, + DEVICE(0x0091), SUBDEV_MASKED(0x5, 0xF)), + IWL_DEV_INFO(iwl6030_non_n_cfg, iwl6030_2abg_name, + DEVICE(0x0091), SUBDEV_MASKED(0x6, 0xF)), + IWL_DEV_INFO(iwl6030_non_n_cfg, iwl6030_2bg_name, + DEVICE(0x0091), SUBDEV(0x5207)), + +/* 6x50 WiFi/WiMax Series */ + IWL_DEV_INFO(iwl6050_2agn_cfg, iwl6050_2agn_name, + DEVICE(0x0087), SUBDEV_MASKED(0x1, 0xF)), + IWL_DEV_INFO(iwl6050_2abg_cfg, iwl6050_2abg_name, + DEVICE(0x0087), SUBDEV_MASKED(0x6, 0xF)), + IWL_DEV_INFO(iwl6050_2agn_cfg, iwl6050_2agn_name, + DEVICE(0x0089), SUBDEV(0x1311)), + IWL_DEV_INFO(iwl6050_2abg_cfg, iwl6050_2abg_name, + DEVICE(0x0089), SUBDEV(0x1316)), + +/* 6150 WiFi/WiMax Series */ + IWL_DEV_INFO(iwl6150_bgn_cfg, iwl6150_bgn_name, + DEVICE(0x0885), SUBDEV_MASKED(0x5, 0xF)), + IWL_DEV_INFO(iwl6150_bg_cfg, iwl6150_bg_name, + DEVICE(0x0885), SUBDEV_MASKED(0x7, 0xF)), + IWL_DEV_INFO(iwl6150_bgn_cfg, iwl6150_bgn_name, + DEVICE(0x0886), SUBDEV(0x1315)), + IWL_DEV_INFO(iwl6150_bg_cfg, iwl6150_bg_name, + DEVICE(0x0886), SUBDEV(0x1317)), + +/* 1000 Series WiFi */ + IWL_DEV_INFO(iwl1000_bgn_cfg, iwl1000_bgn_name, + DEVICE(0x0083), SUBDEV_MASKED(0x5, 0xF)), + IWL_DEV_INFO(iwl1000_bg_cfg, iwl1000_bg_name, + DEVICE(0x0083), SUBDEV_MASKED(0x6, 0xF)), + IWL_DEV_INFO(iwl1000_bg_cfg, iwl1000_bg_name, + DEVICE(0x0084), SUBDEV(0x1216)), + IWL_DEV_INFO(iwl1000_bg_cfg, iwl1000_bg_name, + DEVICE(0x0084), SUBDEV(0x1316)), + +/* 100 Series WiFi */ + IWL_DEV_INFO(iwl100_bgn_cfg, iwl100_bgn_name, + DEVICE(0x08AE), SUBDEV_MASKED(0x5, 0xF)), + IWL_DEV_INFO(iwl100_bg_cfg, iwl100_bg_name, + DEVICE(0x08AE), SUBDEV_MASKED(0x7, 0xF)), + IWL_DEV_INFO(iwl100_bgn_cfg, iwl100_bgn_name, + DEVICE(0x08AF), SUBDEV(0x1015)), + IWL_DEV_INFO(iwl100_bg_cfg, iwl100_bg_name, + DEVICE(0x08AF), SUBDEV(0x1017)), + +/* 130 Series WiFi */ + IWL_DEV_INFO(iwl130_bgn_cfg, iwl130_bgn_name, + DEVICE(0x0896), SUBDEV_MASKED(0x5, 0xF)), + IWL_DEV_INFO(iwl130_bg_cfg, iwl130_bg_name, + DEVICE(0x0896), SUBDEV_MASKED(0x7, 0xF)), + IWL_DEV_INFO(iwl130_bgn_cfg, iwl130_bgn_name, + DEVICE(0x0897), SUBDEV(0x5015)), + IWL_DEV_INFO(iwl130_bg_cfg, iwl130_bg_name, + DEVICE(0x0897), SUBDEV(0x5017)), + +/* 2x00 Series */ + IWL_DEV_INFO(iwl2000_2bgn_cfg, iwl2000_2bgn_name, + DEVICE(0x0890), SUBDEV(0x4022)), + IWL_DEV_INFO(iwl2000_2bgn_cfg, iwl2000_2bgn_name, + DEVICE(0x0891), SUBDEV(0x4222)), + IWL_DEV_INFO(iwl2000_2bgn_cfg, iwl2000_2bgn_name, + DEVICE(0x0890), SUBDEV(0x4422)), + IWL_DEV_INFO(iwl2000_2bgn_cfg, iwl2000_2bgn_d_name, + DEVICE(0x0890), SUBDEV(0x4822)), + +/* 2x30 Series */ + IWL_DEV_INFO(iwl2030_2bgn_cfg, iwl2030_2bgn_name, + DEVICE(0x0887)), + IWL_DEV_INFO(iwl2030_2bgn_cfg, iwl2030_2bgn_name, + DEVICE(0x0888), SUBDEV(0x4262)), + +/* 6x35 Series */ + IWL_DEV_INFO(iwl6035_2agn_cfg, iwl6035_2agn_name, + DEVICE(0x088E), SUBDEV_MASKED(0x0, 0xF)), + IWL_DEV_INFO(iwl6035_2agn_cfg, iwl6035_2agn_sff_name, + DEVICE(0x088E), SUBDEV_MASKED(0xA, 0xF)), + IWL_DEV_INFO(iwl6035_2agn_cfg, iwl6035_2agn_name, + DEVICE(0x088F), SUBDEV_MASKED(0x0, 0xF)), + IWL_DEV_INFO(iwl6035_2agn_cfg, iwl6035_2agn_sff_name, + DEVICE(0x088F), SUBDEV_MASKED(0xA, 0xF)), + +/* 105 Series */ + IWL_DEV_INFO(iwl105_bgn_cfg, iwl105_bgn_name, + DEVICE(0x0894)), + IWL_DEV_INFO(iwl105_bgn_cfg, iwl105_bgn_name, + DEVICE(0x0895), SUBDEV(0x0222)), + +/* 135 Series */ + IWL_DEV_INFO(iwl135_bgn_cfg, iwl135_bgn_name, + DEVICE(0x0892)), + IWL_DEV_INFO(iwl135_bgn_cfg, iwl135_bgn_name, + DEVICE(0x0893), SUBDEV(0x0262)), +#endif /* CONFIG_IWLDVM */ + #if IS_ENABLED(CONFIG_IWLMVM) -/* 9000 */ - IWL_DEV_INFO(0x2526, 0x1550, iwl9260_2ac_cfg, iwl9260_killer_1550_name), - IWL_DEV_INFO(0x2526, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_name), - IWL_DEV_INFO(0x2526, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name), - IWL_DEV_INFO(0x30DC, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_name), - IWL_DEV_INFO(0x30DC, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name), - IWL_DEV_INFO(0x31DC, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_name), - IWL_DEV_INFO(0x31DC, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name), - IWL_DEV_INFO(0xA370, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_name), - IWL_DEV_INFO(0xA370, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name), - IWL_DEV_INFO(0x54F0, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_160_name), - IWL_DEV_INFO(0x54F0, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_name), - IWL_DEV_INFO(0x51F0, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_160_name), - IWL_DEV_INFO(0x51F0, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_160_name), - IWL_DEV_INFO(0x51F0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), - IWL_DEV_INFO(0x51F0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), - IWL_DEV_INFO(0x51F1, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), - IWL_DEV_INFO(0x54F0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), - IWL_DEV_INFO(0x54F0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), - IWL_DEV_INFO(0x7A70, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), - IWL_DEV_INFO(0x7A70, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), - IWL_DEV_INFO(0x7AF0, 0x1691, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690s_name), - IWL_DEV_INFO(0x7AF0, 0x1692, iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_killer_1690i_name), - - IWL_DEV_INFO(0x271C, 0x0214, iwl9260_2ac_cfg, iwl9260_1_name), - IWL_DEV_INFO(0x7E40, 0x1691, iwl_cfg_ma, iwl_ax411_killer_1690s_name), - IWL_DEV_INFO(0x7E40, 0x1692, iwl_cfg_ma, iwl_ax411_killer_1690i_name), - -/* AX200 */ - IWL_DEV_INFO(0x2723, IWL_CFG_ANY, iwl_ax200_cfg_cc, iwl_ax200_name), - IWL_DEV_INFO(0x2723, 0x1653, iwl_ax200_cfg_cc, iwl_ax200_killer_1650w_name), - IWL_DEV_INFO(0x2723, 0x1654, iwl_ax200_cfg_cc, iwl_ax200_killer_1650x_name), - - /* Qu with Hr */ - IWL_DEV_INFO(0x43F0, 0x0070, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x43F0, 0x0074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x43F0, 0x0078, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x43F0, 0x007C, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x43F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, iwl_ax201_killer_1650s_name), - IWL_DEV_INFO(0x43F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, iwl_ax201_killer_1650i_name), - IWL_DEV_INFO(0x43F0, 0x2074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x43F0, 0x4070, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0xA0F0, 0x0070, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0xA0F0, 0x0074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0xA0F0, 0x0078, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0xA0F0, 0x007C, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0xA0F0, 0x0A10, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0xA0F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, NULL), - IWL_DEV_INFO(0xA0F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, NULL), - IWL_DEV_INFO(0xA0F0, 0x2074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0xA0F0, 0x4070, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0xA0F0, 0x6074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x0070, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x0074, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x6074, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x0078, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x007C, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x0310, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x1651, iwl_ax1650s_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x1652, iwl_ax1650i_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x2074, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x02F0, 0x4070, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x0070, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x0074, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x0078, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x007C, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x0310, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x1651, iwl_ax1650s_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x1652, iwl_ax1650i_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x2074, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x06F0, 0x4070, iwl_ax201_cfg_quz_hr, NULL), - IWL_DEV_INFO(0x34F0, 0x0070, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x34F0, 0x0074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x34F0, 0x0078, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x34F0, 0x007C, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x34F0, 0x0310, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x34F0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, NULL), - IWL_DEV_INFO(0x34F0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, NULL), - IWL_DEV_INFO(0x34F0, 0x2074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x34F0, 0x4070, iwl_ax201_cfg_qu_hr, NULL), - - IWL_DEV_INFO(0x3DF0, 0x0070, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x3DF0, 0x0074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x3DF0, 0x0078, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x3DF0, 0x007C, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x3DF0, 0x0310, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x3DF0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, NULL), - IWL_DEV_INFO(0x3DF0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, NULL), - IWL_DEV_INFO(0x3DF0, 0x2074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x3DF0, 0x4070, iwl_ax201_cfg_qu_hr, NULL), - - IWL_DEV_INFO(0x4DF0, 0x0070, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x4DF0, 0x0074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x4DF0, 0x0078, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x4DF0, 0x007C, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x4DF0, 0x0310, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x4DF0, 0x1651, killer1650s_2ax_cfg_qu_b0_hr_b0, NULL), - IWL_DEV_INFO(0x4DF0, 0x1652, killer1650i_2ax_cfg_qu_b0_hr_b0, NULL), - IWL_DEV_INFO(0x4DF0, 0x2074, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x4DF0, 0x4070, iwl_ax201_cfg_qu_hr, NULL), - IWL_DEV_INFO(0x4DF0, 0x6074, iwl_ax201_cfg_qu_hr, NULL), - - /* So with HR */ - IWL_DEV_INFO(0x2725, 0x0090, iwlax211_2ax_cfg_so_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x0020, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x2020, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x0024, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x0310, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x0510, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x0A10, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0xE020, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0xE024, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x4020, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x6020, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x6024, iwlax210_2ax_cfg_ty_gf_a0, NULL), - IWL_DEV_INFO(0x2725, 0x1673, iwlax210_2ax_cfg_ty_gf_a0, iwl_ax210_killer_1675w_name), - IWL_DEV_INFO(0x2725, 0x1674, iwlax210_2ax_cfg_ty_gf_a0, iwl_ax210_killer_1675x_name), - IWL_DEV_INFO(0x7A70, 0x0090, iwlax211_2ax_cfg_so_gf_a0_long, NULL), - IWL_DEV_INFO(0x7A70, 0x0098, iwlax211_2ax_cfg_so_gf_a0_long, NULL), - IWL_DEV_INFO(0x7A70, 0x00B0, iwlax411_2ax_cfg_so_gf4_a0_long, NULL), - IWL_DEV_INFO(0x7A70, 0x0310, iwlax211_2ax_cfg_so_gf_a0_long, NULL), - IWL_DEV_INFO(0x7A70, 0x0510, iwlax211_2ax_cfg_so_gf_a0_long, NULL), - IWL_DEV_INFO(0x7A70, 0x0A10, iwlax211_2ax_cfg_so_gf_a0_long, NULL), - IWL_DEV_INFO(0x7AF0, 0x0090, iwlax211_2ax_cfg_so_gf_a0, NULL), - IWL_DEV_INFO(0x7AF0, 0x0098, iwlax211_2ax_cfg_so_gf_a0, NULL), - IWL_DEV_INFO(0x7AF0, 0x00B0, iwlax411_2ax_cfg_so_gf4_a0, NULL), - IWL_DEV_INFO(0x7AF0, 0x0310, iwlax211_2ax_cfg_so_gf_a0, NULL), - IWL_DEV_INFO(0x7AF0, 0x0510, iwlax211_2ax_cfg_so_gf_a0, NULL), - IWL_DEV_INFO(0x7AF0, 0x0A10, iwlax211_2ax_cfg_so_gf_a0, NULL), - - /* So with JF */ - IWL_DEV_INFO(0x7A70, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_160_name), - IWL_DEV_INFO(0x7A70, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_160_name), - IWL_DEV_INFO(0x7AF0, 0x1551, iwl9560_2ac_cfg_soc, iwl9560_killer_1550s_160_name), - IWL_DEV_INFO(0x7AF0, 0x1552, iwl9560_2ac_cfg_soc, iwl9560_killer_1550i_160_name), - - /* SO with GF2 */ - IWL_DEV_INFO(0x2726, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), - IWL_DEV_INFO(0x2726, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), - IWL_DEV_INFO(0x51F0, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), - IWL_DEV_INFO(0x51F0, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), - IWL_DEV_INFO(0x51F1, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), - IWL_DEV_INFO(0x51F1, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), - IWL_DEV_INFO(0x54F0, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), - IWL_DEV_INFO(0x54F0, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), - IWL_DEV_INFO(0x7A70, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), - IWL_DEV_INFO(0x7A70, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), - IWL_DEV_INFO(0x7AF0, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), - IWL_DEV_INFO(0x7AF0, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), - IWL_DEV_INFO(0x7F70, 0x1671, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675s_name), - IWL_DEV_INFO(0x7F70, 0x1672, iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_killer_1675i_name), - - /* MA with GF2 */ - IWL_DEV_INFO(0x7E40, 0x1671, iwl_cfg_ma, iwl_ax211_killer_1675s_name), - IWL_DEV_INFO(0x7E40, 0x1672, iwl_cfg_ma, iwl_ax211_killer_1675i_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_2ac_cfg_soc, iwl9461_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_2ac_cfg_soc, iwl9461_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_2ac_cfg_soc, iwl9462_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_2ac_cfg_soc, iwl9462_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_2ac_cfg_soc, iwl9560_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_PU, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_2ac_cfg_soc, iwl9560_name), - - _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, - iwl9260_2ac_cfg, iwl9270_160_name), - _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT_GNSS, IWL_CFG_NO_CDB, - iwl9260_2ac_cfg, iwl9270_name), - - _IWL_DEV_INFO(0x271B, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9260_2ac_cfg, iwl9162_160_name), - _IWL_DEV_INFO(0x271B, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_TH1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9260_2ac_cfg, iwl9162_name), - - _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9260_2ac_cfg, iwl9260_160_name), - _IWL_DEV_INFO(0x2526, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_TH, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_TH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9260_2ac_cfg, iwl9260_name), - -/* Qu with Jf */ - /* Qu B step */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_b0_jf_b0_cfg, iwl9461_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_b0_jf_b0_cfg, iwl9461_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_b0_jf_b0_cfg, iwl9462_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_b0_jf_b0_cfg, iwl9462_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_b0_jf_b0_cfg, iwl9560_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_b0_jf_b0_cfg, iwl9560_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550s_name), - _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_b0_jf_b0_cfg, iwl9560_killer_1550i_name), - - /* Qu C step */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_c0_jf_b0_cfg, iwl9461_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_c0_jf_b0_cfg, iwl9461_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_c0_jf_b0_cfg, iwl9462_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_c0_jf_b0_cfg, iwl9462_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_c0_jf_b0_cfg, iwl9560_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_c0_jf_b0_cfg, iwl9560_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550s_name), - _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_qu_c0_jf_b0_cfg, iwl9560_killer_1550i_name), - - /* QuZ */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_quz_a0_jf_b0_cfg, iwl9461_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_quz_a0_jf_b0_cfg, iwl9461_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_quz_a0_jf_b0_cfg, iwl9462_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_quz_a0_jf_b0_cfg, iwl9462_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_quz_a0_jf_b0_cfg, iwl9560_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_quz_a0_jf_b0_cfg, iwl9560_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, 0x1551, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550s_name), - _IWL_DEV_INFO(IWL_CFG_ANY, 0x1552, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwl9560_quz_a0_jf_b0_cfg, iwl9560_killer_1550i_name), - -/* Qu with Hr */ - /* Qu B step */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_qu_b0_hr1_b0, iwl_ax101_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_B_STEP, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_qu_b0_hr_b0, iwl_ax203_name), - - /* Qu C step */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_qu_c0_hr1_b0, iwl_ax101_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_qu_c0_hr_b0, iwl_ax203_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QU, SILICON_C_STEP, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_qu_c0_hr_b0, iwl_ax201_name), - - /* QuZ */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_quz_a0_hr1_b0, iwl_ax101_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_quz_a0_hr_b0, iwl_ax203_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_QUZ, SILICON_B_STEP, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_quz_a0_hr_b0, iwl_ax201_name), - -/* Ma */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_ma, iwl_ax201_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_ma, iwl_ax211_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_MA, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_ma, iwl_ax231_name), - -/* So with Hr */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_so_a0_hr_a0, iwl_ax203_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_so_a0_hr_a0, iwl_ax101_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_so_a0_hr_a0, iwl_ax201_name), - -/* So-F with Hr */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_so_a0_hr_a0, iwl_ax203_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR1, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_so_a0_hr_a0, iwl_ax101_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_so_a0_hr_a0, iwl_ax201_name), - -/* So-F with Gf */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB, - iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name), - -/* SoF with JF2 */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9560_name), - -/* SoF with JF */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9461_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SOF, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), - -/* So with GF */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwlax211_2ax_cfg_so_gf_a0, iwl_ax211_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_ANY, IWL_CFG_CDB, - iwlax411_2ax_cfg_so_gf4_a0, iwl_ax411_name), - -/* So with JF2 */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9560_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF2, IWL_CFG_RF_ID_JF, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9560_name), - -/* So with JF */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9461_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9462_160_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9461_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SO, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_JF1, IWL_CFG_RF_ID_JF1_DIV, IWL_CFG_ANY, - IWL_CFG_NO_160, IWL_CFG_CORES_BT, IWL_CFG_NO_CDB, - iwlax210_2ax_cfg_so_jf_b0, iwl9462_name), - -/* Bz */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_bz, iwl_ax201_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_bz, iwl_ax211_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_bz, iwl_fm_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BZ, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_bz, iwl_wh_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_HR2, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_bz, iwl_ax201_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_GF, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_bz, iwl_ax211_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_bz, iwl_fm_name), - - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BZ_W, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_WH, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_bz, iwl_wh_name), - -/* Ga (Gl) */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_320, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_gl, iwl_gl_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_GL, IWL_CFG_ANY, - IWL_CFG_RF_TYPE_FM, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_NO_320, IWL_CFG_ANY, IWL_CFG_NO_CDB, - iwl_cfg_gl, iwl_mtp_name), - -/* Sc */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SC, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc, iwl_sc_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SC2, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc2, iwl_sc2_name), - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_SC2F, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_sc2f, iwl_sc2f_name), -/* Dr */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_DR, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_dr, iwl_dr_name), - -/* Br */ - _IWL_DEV_INFO(IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_MAC_TYPE_BR, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - IWL_CFG_ANY, IWL_CFG_ANY, IWL_CFG_ANY, - iwl_cfg_br, iwl_br_name), +/* 7260 Series */ + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name, + DEVICE(0x08B1)), // unlisted ones fall through to here + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x4060)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x406A)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x4160)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B1), SUBDEV(0x4062)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B1), SUBDEV(0x4162)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x4460)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x446A)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B1), SUBDEV(0x4462)), + IWL_DEV_INFO(iwl7260_high_temp_cfg, iwl7260_2ac_name, + DEVICE(0x08B1), SUBDEV(0x4A70)), + IWL_DEV_INFO(iwl7260_high_temp_cfg, iwl7260_2ac_name, + DEVICE(0x08B1), SUBDEV(0x4A6E)), + IWL_DEV_INFO(iwl7260_high_temp_cfg, iwl7260_2ac_name, + DEVICE(0x08B1), SUBDEV(0x4A6C)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x4560)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x4020)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x402A)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0x4420)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC060)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC06A)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC160)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B1), SUBDEV(0xC062)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B1), SUBDEV(0xC162)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC760)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC460)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B1), SUBDEV(0xC462)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC560)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC360)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC020)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC02A)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B1), SUBDEV(0xC420)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name, + DEVICE(0x08B2), SUBDEV(0x4270)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name, + DEVICE(0x08B2), SUBDEV(0x4272)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B2), SUBDEV(0x4260)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B2), SUBDEV(0x426A)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B2), SUBDEV(0x4262)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name, + DEVICE(0x08B2), SUBDEV(0x4370)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B2), SUBDEV(0x4360)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B2), SUBDEV(0x4220)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name, + DEVICE(0x08B2), SUBDEV(0xC270)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name, + DEVICE(0x08B2), SUBDEV(0xC272)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B2), SUBDEV(0xC260)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B2), SUBDEV(0xC26A)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_n_name, + DEVICE(0x08B2), SUBDEV(0xC262)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2ac_name, + DEVICE(0x08B2), SUBDEV(0xC370)), + IWL_DEV_INFO(iwl7260_cfg, iwl7260_2n_name, + DEVICE(0x08B2), SUBDEV(0xC220)), + +/* 3160 Series */ + IWL_DEV_INFO(iwl3160_cfg, iwl3160_2ac_name, + DEVICE(0x08B3)), + + IWL_DEV_INFO(iwl3160_cfg, iwl3160_n_name, + DEVICE(0x08B3), SUBDEV_MASKED(0x62, 0xFF)), + IWL_DEV_INFO(iwl3160_cfg, iwl3160_2n_name, + DEVICE(0x08B3), SUBDEV_MASKED(0x60, 0xFF)), + + IWL_DEV_INFO(iwl3160_cfg, iwl3160_2ac_name, + DEVICE(0x08B4)), + +/* 3165 Series */ + IWL_DEV_INFO(iwl3165_2ac_cfg, iwl3165_2ac_name, + DEVICE(0x3165)), + IWL_DEV_INFO(iwl3165_2ac_cfg, iwl3165_2ac_name, + DEVICE(0x3166)), + +/* 3168 Series */ + IWL_DEV_INFO(iwl3168_2ac_cfg, iwl3168_2ac_name, + DEVICE(0x24FB)), + +/* 7265 Series */ + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2ac_name, + DEVICE(0x095A)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5000)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x500A)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_n_name, + DEVICE(0x095A), SUBDEV(0x5002)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_n_name, + DEVICE(0x095A), SUBDEV(0x5102)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5020)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x502A)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5090)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5190)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5100)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5400)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5420)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5490)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5C10)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x5590)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x9000)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x900A)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095A), SUBDEV(0x9400)), + + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2ac_name, + DEVICE(0x095B)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095B), SUBDEV(0x520A)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_n_name, + DEVICE(0x095B), SUBDEV(0x5302)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095B), SUBDEV(0x5200)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_n_name, + DEVICE(0x095B), SUBDEV(0x5202)), + IWL_DEV_INFO(iwl7265_cfg, iwl7265_2n_name, + DEVICE(0x095B), SUBDEV(0x9200)), + +/* 8000 Series */ + IWL_DEV_INFO(iwl8260_cfg, iwl8260_2ac_name, + DEVICE(0x24F3)), + IWL_DEV_INFO(iwl8260_cfg, iwl8260_2n_name, + DEVICE(0x24F3), SUBDEV(0x0004)), + IWL_DEV_INFO(iwl8260_cfg, iwl8260_2n_name, + DEVICE(0x24F3), SUBDEV(0x0044)), + IWL_DEV_INFO(iwl8265_cfg, iwl8265_2ac_name, + DEVICE(0x24FD)), + IWL_DEV_INFO(iwl8265_cfg, iwl8275_2ac_name, + DEVICE(0x24FD), SUBDEV(0x3E02)), + IWL_DEV_INFO(iwl8265_cfg, iwl8275_2ac_name, + DEVICE(0x24FD), SUBDEV(0x3E01)), + IWL_DEV_INFO(iwl8265_cfg, iwl8275_2ac_name, + DEVICE(0x24FD), SUBDEV(0x1012)), + IWL_DEV_INFO(iwl8265_cfg, iwl8275_2ac_name, + DEVICE(0x24FD), SUBDEV(0x0012)), + IWL_DEV_INFO(iwl8265_cfg, iwl_killer_1435i_name, + DEVICE(0x24FD), SUBDEV(0x1431)), + IWL_DEV_INFO(iwl8265_cfg, iwl_killer_1434_kix_name, + DEVICE(0x24FD), SUBDEV(0x1432)), + +/* JF1 RF */ + IWL_DEV_INFO(iwl_rf_jf, iwl9461_160_name, + RF_TYPE(JF1)), + IWL_DEV_INFO(iwl_rf_jf_80mhz, iwl9461_name, + RF_TYPE(JF1), BW_LIMITED), + IWL_DEV_INFO(iwl_rf_jf, iwl9462_160_name, + RF_TYPE(JF1), RF_ID(JF1_DIV)), + IWL_DEV_INFO(iwl_rf_jf_80mhz, iwl9462_name, + RF_TYPE(JF1), RF_ID(JF1_DIV), BW_LIMITED), +/* JF2 RF */ + IWL_DEV_INFO(iwl_rf_jf, iwl9260_160_name, + RF_TYPE(JF2)), + IWL_DEV_INFO(iwl_rf_jf_80mhz, iwl9260_name, + RF_TYPE(JF2), BW_LIMITED), + IWL_DEV_INFO(iwl_rf_jf, iwl9560_160_name, + RF_TYPE(JF2), RF_ID(JF)), + IWL_DEV_INFO(iwl_rf_jf_80mhz, iwl9560_name, + RF_TYPE(JF2), RF_ID(JF), BW_LIMITED), + +/* HR RF */ + IWL_DEV_INFO(iwl_rf_hr, iwl_ax201_name, RF_TYPE(HR2)), + IWL_DEV_INFO(iwl_rf_hr_80mhz, iwl_ax101_name, RF_TYPE(HR1)), + IWL_DEV_INFO(iwl_rf_hr_80mhz, iwl_ax203_name, RF_TYPE(HR2), BW_LIMITED), + IWL_DEV_INFO(iwl_rf_hr, iwl_ax200_name, DEVICE(0x2723)), + +/* GF RF */ + IWL_DEV_INFO(iwl_rf_gf, iwl_ax211_name, RF_TYPE(GF)), + IWL_DEV_INFO(iwl_rf_gf, iwl_ax411_name, RF_TYPE(GF), CDB), + IWL_DEV_INFO(iwl_rf_gf, iwl_ax210_name, DEVICE(0x2725)), + +/* Killer CRFs */ + IWL_DEV_INFO(iwl_rf_jf, iwl9260_killer_1550_name, SUBDEV(0x1550)), + IWL_DEV_INFO(iwl_rf_jf, iwl9560_killer_1550s_name, SUBDEV(0x1551)), + IWL_DEV_INFO(iwl_rf_jf, iwl9560_killer_1550i_name, SUBDEV(0x1552)), + + IWL_DEV_INFO(iwl_rf_hr, iwl_ax201_killer_1650s_name, SUBDEV(0x1651)), + IWL_DEV_INFO(iwl_rf_hr, iwl_ax201_killer_1650i_name, SUBDEV(0x1652)), + + IWL_DEV_INFO(iwl_rf_gf, iwl_ax211_killer_1675s_name, SUBDEV(0x1671)), + IWL_DEV_INFO(iwl_rf_gf, iwl_ax211_killer_1675i_name, SUBDEV(0x1672)), + IWL_DEV_INFO(iwl_rf_gf, iwl_ax210_killer_1675w_name, SUBDEV(0x1673)), + IWL_DEV_INFO(iwl_rf_gf, iwl_ax210_killer_1675x_name, SUBDEV(0x1674)), + IWL_DEV_INFO(iwl_rf_gf, iwl_ax411_killer_1690s_name, SUBDEV(0x1691)), + IWL_DEV_INFO(iwl_rf_gf, iwl_ax411_killer_1690i_name, SUBDEV(0x1692)), + +/* Killer discrete */ + IWL_DEV_INFO(iwl_rf_hr, iwl_ax200_killer_1650w_name, + DEVICE(0x2723), SUBDEV(0x1653)), + IWL_DEV_INFO(iwl_rf_hr, iwl_ax200_killer_1650x_name, + DEVICE(0x2723), SUBDEV(0x1654)), #endif /* CONFIG_IWLMVM */ +#if IS_ENABLED(CONFIG_IWLMLD) +/* FM RF */ + IWL_DEV_INFO(iwl_rf_fm, iwl_be201_name, RF_TYPE(FM)), + IWL_DEV_INFO(iwl_rf_fm, iwl_be401_name, RF_TYPE(FM), CDB), + /* the discrete NICs got the RF B0, it's only for the name anyway */ + IWL_DEV_INFO(iwl_rf_fm, iwl_be200_name, RF_TYPE(FM), + DEVICE(0x272B), RF_STEP(B)), + IWL_DEV_INFO(iwl_rf_fm_160mhz, iwl_be202_name, + RF_TYPE(FM), BW_LIMITED), + +/* Killer CRFs */ + IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750s_name, SUBDEV(0x1771)), + IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750i_name, SUBDEV(0x1772)), + IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1790s_name, SUBDEV(0x1791)), + IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1790i_name, SUBDEV(0x1792)), + +/* Killer discrete */ + IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750w_name, + DEVICE(0x272B), SUBDEV(0x1773)), + IWL_DEV_INFO(iwl_rf_fm, iwl_killer_be1750x_name, + DEVICE(0x272B), SUBDEV(0x1774)), + +/* WH RF */ + IWL_DEV_INFO(iwl_rf_wh, iwl_be211_name, RF_TYPE(WH)), + IWL_DEV_INFO(iwl_rf_wh_160mhz, iwl_be213_name, RF_TYPE(WH), BW_LIMITED), + +/* PE RF */ + IWL_DEV_INFO(iwl_rf_pe, iwl_bn201_name, RF_TYPE(PE)), + IWL_DEV_INFO(iwl_rf_pe, iwl_be223_name, RF_TYPE(PE), SUBDEV(0x0524)), + IWL_DEV_INFO(iwl_rf_pe, iwl_be221_name, RF_TYPE(PE), SUBDEV(0x0324)), + +/* Killer */ + IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775s_name, SUBDEV(0x1776)), + IWL_DEV_INFO(iwl_rf_wh, iwl_killer_be1775i_name, SUBDEV(0x1775)), + + IWL_DEV_INFO(iwl_rf_pe, iwl_killer_bn1850w2_name, SUBDEV(0x1851)), + IWL_DEV_INFO(iwl_rf_pe, iwl_killer_bn1850i_name, SUBDEV(0x1852)), +#endif /* CONFIG_IWLMLD */ }; EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table); @@ -1210,13 +1076,15 @@ EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_dev_info_table_size); /* * Read rf id and cdb info from prph register and store it */ -static void get_crf_id(struct iwl_trans *iwl_trans) +static void get_crf_id(struct iwl_trans *iwl_trans, + struct iwl_trans_info *info) { u32 sd_reg_ver_addr; + u32 hw_wfpm_id; u32 val = 0; u8 step; - if (iwl_trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (iwl_trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) sd_reg_ver_addr = SD_REG_VER_GEN2; else sd_reg_ver_addr = SD_REG_VER; @@ -1227,83 +1095,83 @@ static void get_crf_id(struct iwl_trans *iwl_trans) iwl_write_umac_prph_no_grab(iwl_trans, WFPM_CTRL_REG, val); /* Read crf info */ - iwl_trans->hw_crf_id = iwl_read_prph_no_grab(iwl_trans, sd_reg_ver_addr); + info->hw_crf_id = iwl_read_prph_no_grab(iwl_trans, sd_reg_ver_addr); /* Read cnv info */ - iwl_trans->hw_cnv_id = - iwl_read_prph_no_grab(iwl_trans, CNVI_AUX_MISC_CHIP); + info->hw_cnv_id = iwl_read_prph_no_grab(iwl_trans, CNVI_AUX_MISC_CHIP); /* For BZ-W, take B step also when A step is indicated */ - if (CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ_W) + if (CSR_HW_REV_TYPE(info->hw_rev) == IWL_CFG_MAC_TYPE_BZ_W) step = SILICON_B_STEP; /* In BZ, the MAC step must be read from the CNVI aux register */ - if (CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ) { - step = CNVI_AUX_MISC_CHIP_MAC_STEP(iwl_trans->hw_cnv_id); + if (CSR_HW_REV_TYPE(info->hw_rev) == IWL_CFG_MAC_TYPE_BZ) { + step = CNVI_AUX_MISC_CHIP_MAC_STEP(info->hw_cnv_id); /* For BZ-U, take B step also when A step is indicated */ - if ((CNVI_AUX_MISC_CHIP_PROD_TYPE(iwl_trans->hw_cnv_id) == + if ((CNVI_AUX_MISC_CHIP_PROD_TYPE(info->hw_cnv_id) == CNVI_AUX_MISC_CHIP_PROD_TYPE_BZ_U) && step == SILICON_A_STEP) step = SILICON_B_STEP; } - if (CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ || - CSR_HW_REV_TYPE(iwl_trans->hw_rev) == IWL_CFG_MAC_TYPE_BZ_W) { - iwl_trans->hw_rev_step = step; - iwl_trans->hw_rev |= step; + if (CSR_HW_REV_TYPE(info->hw_rev) == IWL_CFG_MAC_TYPE_BZ || + CSR_HW_REV_TYPE(info->hw_rev) == IWL_CFG_MAC_TYPE_BZ_W) { + info->hw_rev_step = step; + info->hw_rev |= step; } /* Read cdb info (also contains the jacket info if needed in the future */ - iwl_trans->hw_wfpm_id = - iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR); + hw_wfpm_id = iwl_read_umac_prph_no_grab(iwl_trans, WFPM_OTP_CFG1_ADDR); IWL_INFO(iwl_trans, "Detected crf-id 0x%x, cnv-id 0x%x wfpm id 0x%x\n", - iwl_trans->hw_crf_id, iwl_trans->hw_cnv_id, - iwl_trans->hw_wfpm_id); + info->hw_crf_id, info->hw_cnv_id, hw_wfpm_id); } /* * In case that there is no OTP on the NIC, map the rf id and cdb info * from the prph registers. */ -static int map_crf_id(struct iwl_trans *iwl_trans) +static int map_crf_id(struct iwl_trans *iwl_trans, + struct iwl_trans_info *info) { int ret = 0; - u32 val = iwl_trans->hw_crf_id; + u32 val = info->hw_crf_id; u32 step_id = REG_CRF_ID_STEP(val); u32 slave_id = REG_CRF_ID_SLAVE(val); - u32 jacket_id_cnv = REG_CRF_ID_SLAVE(iwl_trans->hw_cnv_id); - u32 jacket_id_wfpm = WFPM_OTP_CFG1_IS_JACKET(iwl_trans->hw_wfpm_id); - u32 cdb_id_wfpm = WFPM_OTP_CFG1_IS_CDB(iwl_trans->hw_wfpm_id); + u32 jacket_id_cnv = REG_CRF_ID_SLAVE(info->hw_cnv_id); + u32 hw_wfpm_id = iwl_read_umac_prph_no_grab(iwl_trans, + WFPM_OTP_CFG1_ADDR); + u32 jacket_id_wfpm = WFPM_OTP_CFG1_IS_JACKET(hw_wfpm_id); + u32 cdb_id_wfpm = WFPM_OTP_CFG1_IS_CDB(hw_wfpm_id); /* Map between crf id to rf id */ switch (REG_CRF_ID_TYPE(val)) { case REG_CRF_ID_TYPE_JF_1: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_JF1 << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_JF1 << 12); break; case REG_CRF_ID_TYPE_JF_2: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_JF2 << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_JF2 << 12); break; case REG_CRF_ID_TYPE_HR_NONE_CDB_1X1: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR1 << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_HR1 << 12); break; case REG_CRF_ID_TYPE_HR_NONE_CDB: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12); break; case REG_CRF_ID_TYPE_HR_CDB: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_HR2 << 12); break; case REG_CRF_ID_TYPE_GF: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_GF << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_GF << 12); break; case REG_CRF_ID_TYPE_FM: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_FM << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_FM << 12); break; case REG_CRF_ID_TYPE_WHP: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_WH << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_WH << 12); break; case REG_CRF_ID_TYPE_PE: - iwl_trans->hw_rf_id = (IWL_CFG_RF_TYPE_PE << 12); + info->hw_rf_id = (IWL_CFG_RF_TYPE_PE << 12); break; default: ret = -EIO; @@ -1315,28 +1183,28 @@ static int map_crf_id(struct iwl_trans *iwl_trans) } /* Set Step-id */ - iwl_trans->hw_rf_id |= (step_id << 8); + info->hw_rf_id |= (step_id << 8); /* Set CDB capabilities */ if (cdb_id_wfpm || slave_id) { - iwl_trans->hw_rf_id += BIT(28); + info->hw_rf_id += BIT(28); IWL_INFO(iwl_trans, "Adding cdb to rf id\n"); } /* Set Jacket capabilities */ if (jacket_id_wfpm || jacket_id_cnv) { - iwl_trans->hw_rf_id += BIT(29); + info->hw_rf_id += BIT(29); IWL_INFO(iwl_trans, "Adding jacket to rf id\n"); } IWL_INFO(iwl_trans, "Detected rf-type 0x%x step-id 0x%x slave-id 0x%x from crf id 0x%x\n", - REG_CRF_ID_TYPE(val), step_id, slave_id, iwl_trans->hw_rf_id); + REG_CRF_ID_TYPE(val), step_id, slave_id, info->hw_rf_id); IWL_INFO(iwl_trans, "Detected cdb-id 0x%x jacket-id 0x%x from wfpm id 0x%x\n", - cdb_id_wfpm, jacket_id_wfpm, iwl_trans->hw_wfpm_id); + cdb_id_wfpm, jacket_id_wfpm, hw_wfpm_id); IWL_INFO(iwl_trans, "Detected jacket-id 0x%x from cnvi id 0x%x\n", - jacket_id_cnv, iwl_trans->hw_cnv_id); + jacket_id_cnv, info->hw_cnv_id); out: return ret; @@ -1346,9 +1214,8 @@ out: #define PCI_CFG_RETRY_TIMEOUT 0x041 VISIBLE_IF_IWLWIFI_KUNIT const struct iwl_dev_info * -iwl_pci_find_dev_info(u16 device, u16 subsystem_device, - u16 mac_type, u8 mac_step, u16 rf_type, u8 cdb, - u8 jacket, u8 rf_id, u8 no_160, u8 cores, u8 rf_step) +iwl_pci_find_dev_info(u16 device, u16 subsystem_device, u16 rf_type, u8 cdb, + u8 rf_id, u8 bw_limit, u8 rf_step) { int num_devices = ARRAY_SIZE(iwl_dev_info_table); int i; @@ -1358,49 +1225,32 @@ iwl_pci_find_dev_info(u16 device, u16 subsystem_device, for (i = num_devices - 1; i >= 0; i--) { const struct iwl_dev_info *dev_info = &iwl_dev_info_table[i]; + u16 subdevice_mask; if (dev_info->device != (u16)IWL_CFG_ANY && dev_info->device != device) continue; - if (dev_info->subdevice != (u16)IWL_CFG_ANY && - dev_info->subdevice != subsystem_device) - continue; - - if (dev_info->mac_type != (u16)IWL_CFG_ANY && - dev_info->mac_type != mac_type) - continue; - - if (dev_info->mac_step != (u8)IWL_CFG_ANY && - dev_info->mac_step != mac_step) - continue; + subdevice_mask = GENMASK(dev_info->subdevice_m_h, + dev_info->subdevice_m_l); - if (dev_info->rf_type != (u16)IWL_CFG_ANY && - dev_info->rf_type != rf_type) - continue; - - if (dev_info->cdb != (u8)IWL_CFG_ANY && - dev_info->cdb != cdb) + if (dev_info->subdevice != (u16)IWL_CFG_ANY && + dev_info->subdevice != (subsystem_device & subdevice_mask)) continue; - if (dev_info->jacket != (u8)IWL_CFG_ANY && - dev_info->jacket != jacket) + if (dev_info->match_rf_type && dev_info->rf_type != rf_type) continue; - if (dev_info->rf_id != (u8)IWL_CFG_ANY && - dev_info->rf_id != rf_id) + if (dev_info->match_cdb && dev_info->cdb != cdb) continue; - if (dev_info->no_160 != (u8)IWL_CFG_ANY && - dev_info->no_160 != no_160) + if (dev_info->match_rf_id && dev_info->rf_id != rf_id) continue; - if (dev_info->cores != (u8)IWL_CFG_ANY && - dev_info->cores != cores) + if (dev_info->match_bw_limit && dev_info->bw_limit != bw_limit) continue; - if (dev_info->rf_step != (u8)IWL_CFG_ANY && - dev_info->rf_step != rf_step) + if (dev_info->match_rf_step && dev_info->rf_step != rf_step) continue; return dev_info; @@ -1412,30 +1262,32 @@ EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_pci_find_dev_info); static void iwl_pcie_recheck_me_status(struct work_struct *wk) { - struct iwl_trans *trans = container_of(wk, typeof(*trans), - me_recheck_wk.work); + struct iwl_trans_pcie *trans_pcie = container_of(wk, + typeof(*trans_pcie), + me_recheck_wk.work); u32 val; - val = iwl_read32(trans, CSR_HW_IF_CONFIG_REG); - trans->me_present = !!(val & CSR_HW_IF_CONFIG_REG_IAMT_UP); + val = iwl_read32(trans_pcie->trans, CSR_HW_IF_CONFIG_REG); + trans_pcie->me_present = !!(val & CSR_HW_IF_CONFIG_REG_IAMT_UP); } static void iwl_pcie_check_me_status(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 val; - trans->me_present = -1; + trans_pcie->me_present = -1; - INIT_DELAYED_WORK(&trans->me_recheck_wk, + INIT_DELAYED_WORK(&trans_pcie->me_recheck_wk, iwl_pcie_recheck_me_status); /* we don't have a good way of determining this until BZ */ - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_BZ) return; val = iwl_read_prph(trans, CNVI_SCU_REG_FOR_ECO_1); if (val & CNVI_SCU_REG_FOR_ECO_1_WIAMT_KNOWN) { - trans->me_present = + trans_pcie->me_present = !!(val & CNVI_SCU_REG_FOR_ECO_1_WIAMT_PRESENT); return; } @@ -1443,38 +1295,28 @@ static void iwl_pcie_check_me_status(struct iwl_trans *trans) val = iwl_read32(trans, CSR_HW_IF_CONFIG_REG); if (val & (CSR_HW_IF_CONFIG_REG_ME_OWN | CSR_HW_IF_CONFIG_REG_IAMT_UP)) { - trans->me_present = 1; + trans_pcie->me_present = 1; return; } /* recheck again later, ME might still be initializing */ - schedule_delayed_work(&trans->me_recheck_wk, HZ); + schedule_delayed_work(&trans_pcie->me_recheck_wk, HZ); } static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - const struct iwl_cfg_trans_params *trans; - const struct iwl_cfg *cfg_7265d __maybe_unused = NULL; + const struct iwl_mac_cfg *trans; const struct iwl_dev_info *dev_info; + struct iwl_trans_info info = { + .hw_id = (pdev->device << 16) + pdev->subsystem_device, + }; struct iwl_trans *iwl_trans; struct iwl_trans_pcie *trans_pcie; int ret; - const struct iwl_cfg *cfg; - trans = (void *)(ent->driver_data & ~TRANS_CFG_MARKER); + trans = (void *)ent->driver_data; - /* - * This is needed for backwards compatibility with the old - * tables, so we don't need to change all the config structs - * at the same time. The cfg is used to compare with the old - * full cfg structs. - */ - cfg = (void *)(ent->driver_data & ~TRANS_CFG_MARKER); - - /* make sure trans is the first element in iwl_cfg */ - BUILD_BUG_ON(offsetof(struct iwl_cfg, trans)); - - iwl_trans = iwl_trans_pcie_alloc(pdev, ent, trans); + iwl_trans = iwl_trans_pcie_alloc(pdev, trans, &info); if (IS_ERR(iwl_trans)) return PTR_ERR(iwl_trans); @@ -1483,6 +1325,9 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) iwl_trans_pcie_check_product_reset_status(pdev); iwl_trans_pcie_check_product_reset_mode(pdev); + /* set the things we know so far for the grab NIC access */ + iwl_trans_set_info(iwl_trans, &info); + /* * Let's try to grab NIC access early here. Sometimes, NICs may * fail to initialize, and if that happens it's better if we see @@ -1496,7 +1341,7 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (ret) goto out_free_trans; if (iwl_trans_grab_nic_access(iwl_trans)) { - get_crf_id(iwl_trans); + get_crf_id(iwl_trans, &info); /* all good */ iwl_trans_release_nic_access(iwl_trans); } else { @@ -1505,38 +1350,32 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) } } - iwl_trans->hw_rf_id = iwl_read32(iwl_trans, CSR_HW_RF_ID); + info.hw_rf_id = iwl_read32(iwl_trans, CSR_HW_RF_ID); /* * The RF_ID is set to zero in blank OTP so read version to * extract the RF_ID. * This is relevant only for family 9000 and up. */ - if (iwl_trans->trans_cfg->rf_id && - iwl_trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000 && - !CSR_HW_RFID_TYPE(iwl_trans->hw_rf_id) && map_crf_id(iwl_trans)) { + if (iwl_trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_9000 && + !CSR_HW_RFID_TYPE(info.hw_rf_id) && map_crf_id(iwl_trans, &info)) { ret = -EINVAL; goto out_free_trans; } IWL_INFO(iwl_trans, "PCI dev %04x/%04x, rev=0x%x, rfid=0x%x\n", pdev->device, pdev->subsystem_device, - iwl_trans->hw_rev, iwl_trans->hw_rf_id); + info.hw_rev, info.hw_rf_id); dev_info = iwl_pci_find_dev_info(pdev->device, pdev->subsystem_device, - CSR_HW_REV_TYPE(iwl_trans->hw_rev), - iwl_trans->hw_rev_step, - CSR_HW_RFID_TYPE(iwl_trans->hw_rf_id), - CSR_HW_RFID_IS_CDB(iwl_trans->hw_rf_id), - CSR_HW_RFID_IS_JACKET(iwl_trans->hw_rf_id), + CSR_HW_RFID_TYPE(info.hw_rf_id), + CSR_HW_RFID_IS_CDB(info.hw_rf_id), IWL_SUBDEVICE_RF_ID(pdev->subsystem_device), - IWL_SUBDEVICE_NO_160(pdev->subsystem_device), - IWL_SUBDEVICE_CORES(pdev->subsystem_device), - CSR_HW_RFID_STEP(iwl_trans->hw_rf_id)); + IWL_SUBDEVICE_BW_LIM(pdev->subsystem_device), + CSR_HW_RFID_STEP(info.hw_rf_id)); if (dev_info) { iwl_trans->cfg = dev_info->cfg; - iwl_trans->name = dev_info->name; - iwl_trans->no_160 = dev_info->no_160 == IWL_CFG_NO_160; + info.name = dev_info->name; } #if IS_ENABLED(CONFIG_IWLMVM) @@ -1547,82 +1386,41 @@ static int iwl_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent) * all the parameters that the transport uses must, until that is * changed, be identical to the ones in the 7265D configuration. */ - if (cfg == &iwl7265_2ac_cfg) - cfg_7265d = &iwl7265d_2ac_cfg; - else if (cfg == &iwl7265_2n_cfg) - cfg_7265d = &iwl7265d_2n_cfg; - else if (cfg == &iwl7265_n_cfg) - cfg_7265d = &iwl7265d_n_cfg; - if (cfg_7265d && - (iwl_trans->hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) - iwl_trans->cfg = cfg_7265d; - - /* - * This is a hack to switch from Qu B0 to Qu C0. We need to - * do this for all cfgs that use Qu B0, except for those using - * Jf, which have already been moved to the new table. The - * rest must be removed once we convert Qu with Hr as well. - */ - if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QU_C0) { - if (iwl_trans->cfg == &iwl_ax201_cfg_qu_hr) - iwl_trans->cfg = &iwl_ax201_cfg_qu_c0_hr_b0; - else if (iwl_trans->cfg == &killer1650s_2ax_cfg_qu_b0_hr_b0) - iwl_trans->cfg = &killer1650s_2ax_cfg_qu_c0_hr_b0; - else if (iwl_trans->cfg == &killer1650i_2ax_cfg_qu_b0_hr_b0) - iwl_trans->cfg = &killer1650i_2ax_cfg_qu_c0_hr_b0; - } - - /* same thing for QuZ... */ - if (iwl_trans->hw_rev == CSR_HW_REV_TYPE_QUZ) { - if (iwl_trans->cfg == &iwl_ax201_cfg_qu_hr) - iwl_trans->cfg = &iwl_ax201_cfg_quz_hr; - else if (iwl_trans->cfg == &killer1650s_2ax_cfg_qu_b0_hr_b0) - iwl_trans->cfg = &iwl_ax1650s_cfg_quz_hr; - else if (iwl_trans->cfg == &killer1650i_2ax_cfg_qu_b0_hr_b0) - iwl_trans->cfg = &iwl_ax1650i_cfg_quz_hr; - } - + if (iwl_trans->cfg == &iwl7265_cfg && + (info.hw_rev & CSR_HW_REV_TYPE_MSK) == CSR_HW_REV_TYPE_7265D) + iwl_trans->cfg = &iwl7265d_cfg; #endif - /* - * If we didn't set the cfg yet, the PCI ID table entry should have - * been a full config - if yes, use it, otherwise fail. - */ if (!iwl_trans->cfg) { - if (ent->driver_data & TRANS_CFG_MARKER) { - pr_err("No config found for PCI dev %04x/%04x, rev=0x%x, rfid=0x%x\n", - pdev->device, pdev->subsystem_device, - iwl_trans->hw_rev, iwl_trans->hw_rf_id); - ret = -EINVAL; - goto out_free_trans; - } - iwl_trans->cfg = cfg; + pr_err("No config found for PCI dev %04x/%04x, rev=0x%x, rfid=0x%x\n", + pdev->device, pdev->subsystem_device, + info.hw_rev, info.hw_rf_id); + ret = -EINVAL; + goto out_free_trans; } - /* if we don't have a name yet, copy name from the old cfg */ - if (!iwl_trans->name) - iwl_trans->name = iwl_trans->cfg->name; - - IWL_INFO(iwl_trans, "Detected %s\n", iwl_trans->name); + IWL_INFO(iwl_trans, "Detected %s\n", info.name); - if (iwl_trans->trans_cfg->mq_rx_supported) { + if (iwl_trans->mac_cfg->mq_rx_supported) { if (WARN_ON(!iwl_trans->cfg->num_rbds)) { ret = -EINVAL; goto out_free_trans; } - trans_pcie->num_rx_bufs = iwl_trans->cfg->num_rbds; + trans_pcie->num_rx_bufs = iwl_trans_get_num_rbds(iwl_trans); } else { trans_pcie->num_rx_bufs = RX_QUEUE_SIZE; } - if (!iwl_trans->trans_cfg->integrated) { + if (!iwl_trans->mac_cfg->integrated) { u16 link_status; pcie_capability_read_word(pdev, PCI_EXP_LNKSTA, &link_status); - iwl_trans->pcie_link_speed = + info.pcie_link_speed = u16_get_bits(link_status, PCI_EXP_LNKSTA_CLS); } + iwl_trans_set_info(iwl_trans, &info); + ret = iwl_trans_init(iwl_trans); if (ret) goto out_free_trans; @@ -1654,11 +1452,12 @@ out_free_trans: static void iwl_pci_remove(struct pci_dev *pdev) { struct iwl_trans *trans = pci_get_drvdata(pdev); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); if (!trans) return; - cancel_delayed_work_sync(&trans->me_recheck_wk); + cancel_delayed_work_sync(&trans_pcie->me_recheck_wk); iwl_drv_stop(trans->drv); @@ -1704,7 +1503,7 @@ static int _iwl_pci_resume(struct device *device, bool restore) * Note: MAC (bits 0:7) will be cleared upon suspend even with wowlan, * so assume that any bits there mean that the device is usable. */ - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ && + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ && !iwl_read32(trans, CSR_FUNC_SCRATCH)) device_was_powered_off = true; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h index 856b7e9f717d..3b7c12fc4f9e 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/internal.h +++ b/drivers/net/wireless/intel/iwlwifi/pcie/internal.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2003-2015, 2018-2024 Intel Corporation + * Copyright (C) 2003-2015, 2018-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -194,7 +194,7 @@ struct iwl_rb_allocator { static inline u16 iwl_get_closed_rb_stts(struct iwl_trans *trans, struct iwl_rxq *rxq) { - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { __le16 *rb_stts = rxq->rb_stts; return le16_to_cpu(READ_ONCE(*rb_stts)); @@ -269,6 +269,7 @@ enum iwl_pcie_fw_reset_state { FW_RESET_REQUESTED, FW_RESET_OK, FW_RESET_ERROR, + FW_RESET_TOP_REQUESTED, }; /** @@ -288,22 +289,14 @@ enum iwl_pcie_imr_status { /** * struct iwl_pcie_txqs - TX queues data * - * @bc_table_dword: true if the BC table expects DWORD (as opposed to bytes) - * @page_offs: offset from skb->cb to mac header page pointer - * @dev_cmd_offs: offset from skb->cb to iwl_device_tx_cmd pointer * @queue_used: bit mask of used queues * @queue_stopped: bit mask of stopped queues * @txq: array of TXQ data structures representing the TXQs * @scd_bc_tbls: gen1 pointer to the byte count table of the scheduler - * @queue_alloc_cmd_ver: queue allocation command version * @bc_pool: bytecount DMA allocations pool * @bc_tbl_size: bytecount table size * @tso_hdr_page: page allocated (per CPU) for A-MSDU headers when doing TSO * (and similar usage) - * @cmd: command queue data - * @cmd.fifo: FIFO number - * @cmd.q_id: queue ID - * @cmd.wdg_timeout: watchdog timeout * @tfd: TFD data * @tfd.max_tbs: max number of buffers per TFD * @tfd.size: TFD size @@ -315,26 +308,15 @@ struct iwl_pcie_txqs { struct iwl_txq *txq[IWL_MAX_TVQM_QUEUES]; struct dma_pool *bc_pool; size_t bc_tbl_size; - bool bc_table_dword; - u8 page_offs; - u8 dev_cmd_offs; struct iwl_tso_hdr_page __percpu *tso_hdr_page; struct { - u8 fifo; - u8 q_id; - unsigned int wdg_timeout; - } cmd; - - struct { u8 max_tbs; u16 size; u8 addr_size; } tfd; struct iwl_dma_ptr scd_bc_tbls; - - u8 queue_alloc_cmd_ver; }; /** @@ -344,7 +326,7 @@ struct iwl_pcie_txqs { * @global_table: table mapping received VID from hw to rxb * @rba: allocator for RX replenishing * @ctxt_info: context information for FW self init - * @ctxt_info_gen3: context information for gen3 devices + * @ctxt_info_v2: context information for v1 devices * @prph_info: prph info for self init * @prph_scratch: prph scratch for self init * @ctxt_info_dma_addr: dma addr of context information @@ -352,6 +334,7 @@ struct iwl_pcie_txqs { * @prph_scratch_dma_addr: dma addr of prph scratch * @ctxt_info_dma_addr: dma addr of context information * @iml: image loader image virtual address + * @iml_len: image loader image size * @iml_dma_addr: image loader image DMA address * @trans: pointer to the generic transport area * @scd_base_addr: scheduler sram base address in SRAM @@ -363,9 +346,6 @@ struct iwl_pcie_txqs { * @hw_base: pci hardware address support * @ucode_write_complete: indicates that the ucode has been copied. * @ucode_write_waitq: wait queue for uCode load - * @cmd_queue - command queue number - * @rx_buf_size: Rx buffer size - * @scd_set_active: should the transport configure the SCD for HCMD queue * @rx_page_order: page order for receive buffer size * @rx_buf_bytes: RX buffer (RB) size in bytes * @reg_lock: protect hw register access @@ -406,19 +386,20 @@ struct iwl_pcie_txqs { * @pcie_dbg_dumped_once: indicates PCIe regs were dumped already * @opmode_down: indicates opmode went away * @num_rx_bufs: number of RX buffers to allocate/use - * @no_reclaim_cmds: special commands not using reclaim flow - * (firmware workaround) - * @n_no_reclaim_cmds: number of special commands not using reclaim flow * @affinity_mask: IRQ affinity mask for each RX queue * @debug_rfkill: RF-kill debugging state, -1 for unset, 0/1 for radio * enable/disable - * @fw_reset_handshake: indicates FW reset handshake is needed * @fw_reset_state: state of FW reset handshake * @fw_reset_waitq: waitqueue for FW reset handshake * @is_down: indicates the NIC is down * @isr_stats: interrupt statistics * @napi_dev: (fake) netdev for NAPI registration * @txqs: transport tx queues data. + * @me_present: WiAMT/CSME is detected as present (1), not present (0) + * or unknown (-1, so can still use it as a boolean safely) + * @me_recheck_wk: worker to recheck WiAMT/CSME presence + * @invalid_tx_cmd: invalid TX command buffer + * @wait_command_queue: wait queue for sync commands */ struct iwl_trans_pcie { struct iwl_rxq *rxq; @@ -427,11 +408,12 @@ struct iwl_trans_pcie { struct iwl_rb_allocator rba; union { struct iwl_context_info *ctxt_info; - struct iwl_context_info_gen3 *ctxt_info_gen3; + struct iwl_context_info_v2 *ctxt_info_v2; }; struct iwl_prph_info *prph_info; struct iwl_prph_scratch *prph_scratch; void *iml; + size_t iml_len; dma_addr_t ctxt_info_dma_addr; dma_addr_t prph_info_dma_addr; dma_addr_t prph_scratch_dma_addr; @@ -470,12 +452,8 @@ struct iwl_trans_pcie { wait_queue_head_t ucode_write_waitq; wait_queue_head_t sx_waitq; - u8 n_no_reclaim_cmds; - u8 no_reclaim_cmds[MAX_NO_RECLAIM_CMDS]; u16 num_rx_bufs; - enum iwl_amsdu_size rx_buf_size; - bool scd_set_active; bool pcie_dbg_dumped_once; u32 rx_page_order; u32 rx_buf_bytes; @@ -510,7 +488,6 @@ struct iwl_trans_pcie { void *base_rb_stts; dma_addr_t base_rb_stts_dma; - bool fw_reset_handshake; enum iwl_pcie_fw_reset_state fw_reset_state; wait_queue_head_t fw_reset_waitq; enum iwl_pcie_imr_status imr_status; @@ -518,6 +495,13 @@ struct iwl_trans_pcie { char rf_name[32]; struct iwl_pcie_txqs txqs; + + s8 me_present; + struct delayed_work me_recheck_wk; + + struct iwl_dma_ptr invalid_tx_cmd; + + wait_queue_head_t wait_command_queue; }; static inline struct iwl_trans_pcie * @@ -552,16 +536,16 @@ iwl_trans_pcie_get_trans(struct iwl_trans_pcie *trans_pcie) */ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, - const struct pci_device_id *ent, - const struct iwl_cfg_trans_params *cfg_trans); + const struct iwl_mac_cfg *mac_cfg, + struct iwl_trans_info *info); void iwl_trans_pcie_free(struct iwl_trans *trans); void iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions, struct device *dev); -bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans); -#define _iwl_trans_pcie_grab_nic_access(trans) \ +bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent); +#define _iwl_trans_pcie_grab_nic_access(trans, silent) \ __cond_lock(nic_access_nobh, \ - likely(__iwl_trans_pcie_grab_nic_access(trans))) + likely(__iwl_trans_pcie_grab_nic_access(trans, silent))) void iwl_trans_pcie_check_product_reset_status(struct pci_dev *pdev); void iwl_trans_pcie_check_product_reset_mode(struct pci_dev *pdev); @@ -623,7 +607,7 @@ struct iwl_tso_page_info { IWL_TSO_PAGE_DATA_SIZE)) int iwl_pcie_tx_init(struct iwl_trans *trans); -void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr); +void iwl_pcie_tx_start(struct iwl_trans *trans); int iwl_pcie_tx_stop(struct iwl_trans *trans); void iwl_pcie_tx_free(struct iwl_trans *trans); bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int queue, u16 ssn, @@ -646,7 +630,8 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset, unsigned int len); struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *cmd_meta, - u8 **hdr, unsigned int hdr_room); + u8 **hdr, unsigned int hdr_room, + unsigned int offset); void iwl_pcie_free_tso_pages(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *cmd_meta); @@ -678,7 +663,7 @@ static inline void *iwl_txq_get_tfd(struct iwl_trans *trans, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) idx = iwl_txq_get_cmd_index(txq, idx); return (u8 *)txq->tfds + trans_pcie->txqs.tfd.size * idx; @@ -717,7 +702,7 @@ static inline void iwl_txq_stop(struct iwl_trans *trans, struct iwl_txq *txq) static inline int iwl_txq_inc_wrap(struct iwl_trans *trans, int index) { return ++index & - (trans->trans_cfg->base_params->max_tfd_queue_size - 1); + (trans->mac_cfg->base->max_tfd_queue_size - 1); } /** @@ -728,7 +713,7 @@ static inline int iwl_txq_inc_wrap(struct iwl_trans *trans, int index) static inline int iwl_txq_dec_wrap(struct iwl_trans *trans, int index) { return --index & - (trans->trans_cfg->base_params->max_tfd_queue_size - 1); + (trans->mac_cfg->base->max_tfd_queue_size - 1); } void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq); @@ -751,10 +736,12 @@ int iwl_txq_gen2_set_tb(struct iwl_trans *trans, static inline void iwl_txq_set_tfd_invalid_gen2(struct iwl_trans *trans, struct iwl_tfh_tfd *tfd) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + tfd->num_tbs = 0; - iwl_txq_gen2_set_tb(trans, tfd, trans->invalid_tx_cmd.dma, - trans->invalid_tx_cmd.size); + iwl_txq_gen2_set_tb(trans, tfd, trans_pcie->invalid_tx_cmd.dma, + trans_pcie->invalid_tx_cmd.size); } void iwl_txq_gen2_tfd_unmap(struct iwl_trans *trans, @@ -781,7 +768,7 @@ static inline u16 iwl_txq_gen1_tfd_tb_get_len(struct iwl_trans *trans, struct iwl_tfd *tfd; struct iwl_tfd_tb *tb; - if (trans->trans_cfg->gen2) { + if (trans->mac_cfg->gen2) { struct iwl_tfh_tfd *tfh_tfd = _tfd; struct iwl_tfh_tb *tfh_tb = &tfh_tfd->tbs[idx]; @@ -939,11 +926,13 @@ static inline void iwl_enable_fw_load_int(struct iwl_trans *trans) } } -static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans) +static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans, + bool top_reset) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - IWL_DEBUG_ISR(trans, "Enabling ALIVE interrupt only\n"); + IWL_DEBUG_ISR(trans, "Enabling %s interrupt only\n", + top_reset ? "RESET" : "ALIVE"); if (!trans_pcie->msix_enabled) { /* @@ -953,11 +942,20 @@ static inline void iwl_enable_fw_load_int_ctx_info(struct iwl_trans *trans) * RX interrupt which will allow us to receive the ALIVE * notification (which is Rx) and continue the flow. */ - trans_pcie->inta_mask = CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX; + if (top_reset) + trans_pcie->inta_mask = CSR_INT_BIT_RESET_DONE; + else + trans_pcie->inta_mask = CSR_INT_BIT_ALIVE | + CSR_INT_BIT_FH_RX; iwl_write32(trans, CSR_INT_MASK, trans_pcie->inta_mask); } else { - iwl_enable_hw_int_msk_msix(trans, - MSIX_HW_INT_CAUSES_REG_ALIVE); + u32 val = top_reset ? MSIX_HW_INT_CAUSES_REG_RESET_DONE + : MSIX_HW_INT_CAUSES_REG_ALIVE; + + iwl_enable_hw_int_msk_msix(trans, val); + + if (top_reset) + return; /* * Leave all the FH causes enabled to get the ALIVE * notification. @@ -1004,7 +1002,7 @@ static inline void iwl_enable_rfkill_int(struct iwl_trans *trans) MSIX_HW_INT_CAUSES_REG_RF_KILL); } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_9000) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_9000) { /* * On 9000-series devices this bit isn't enabled by default, so * when we power down the device we need set the bit to allow it @@ -1075,8 +1073,7 @@ static inline void iwl_trans_pcie_dbgfs_register(struct iwl_trans *trans) { } void iwl_pcie_rx_allocator_work(struct work_struct *data); /* common trans ops for all generations transports */ -void iwl_trans_pcie_configure(struct iwl_trans *trans, - const struct iwl_trans_config *trans_cfg); +void iwl_trans_pcie_op_mode_enter(struct iwl_trans *trans); int iwl_trans_pcie_start_hw(struct iwl_trans *trans); void iwl_trans_pcie_op_mode_leave(struct iwl_trans *trans); void iwl_trans_pcie_write8(struct iwl_trans *trans, u32 ofs, u8 val); @@ -1104,12 +1101,16 @@ void iwl_trans_pcie_set_bits_mask(struct iwl_trans *trans, u32 reg, int iwl_trans_pcie_read_config32(struct iwl_trans *trans, u32 ofs, u32 *val); bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans); -void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans); +void __releases(nic_access_nobh) +iwl_trans_pcie_release_nic_access(struct iwl_trans *trans); +void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power); /* transport gen 1 exported functions */ -void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr); +void iwl_trans_pcie_fw_alive(struct iwl_trans *trans); int iwl_trans_pcie_start_fw(struct iwl_trans *trans, - const struct fw_img *fw, bool run_in_rfkill); + const struct iwl_fw *fw, + const struct fw_img *img, + bool run_in_rfkill); void iwl_trans_pcie_stop_device(struct iwl_trans *trans); /* common functions that are used by gen2 transport */ @@ -1127,15 +1128,12 @@ int iwl_pcie_alloc_dma_ptr(struct iwl_trans *trans, void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr); void iwl_pcie_apply_destination(struct iwl_trans *trans); -/* common functions that are used by gen3 transport */ -void iwl_pcie_alloc_fw_monitor(struct iwl_trans *trans, u8 max_power); - /* transport gen 2 exported functions */ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, - const struct fw_img *fw, bool run_in_rfkill); + const struct iwl_fw *fw, + const struct fw_img *img, + bool run_in_rfkill); void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans); -int iwl_trans_pcie_gen2_send_hcmd(struct iwl_trans *trans, - struct iwl_host_cmd *cmd); void iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans); int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c index 4a442d03d8d2..f0405eddc367 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/rx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/rx.c @@ -12,7 +12,8 @@ #include "iwl-io.h" #include "internal.h" #include "iwl-op-mode.h" -#include "iwl-context-info-gen3.h" +#include "iwl-context-info-v2.h" +#include "fw/dbg.h" /****************************************************************************** * @@ -143,12 +144,12 @@ static inline __le32 iwl_pcie_dma_addr2rbd_ptr(dma_addr_t dma_addr) */ int iwl_pcie_rx_stop(struct iwl_trans *trans) { - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { /* TODO: remove this once fw does it */ - iwl_write_umac_prph(trans, RFH_RXF_DMA_CFG_GEN3, 0); - return iwl_poll_umac_prph_bit(trans, RFH_GEN_STATUS_GEN3, + iwl_write_umac_prph(trans, RFH_RXF_DMA_CFG_AX210, 0); + return iwl_poll_umac_prph_bit(trans, RFH_GEN_STATUS_AX210, RXF_DMA_IDLE, RXF_DMA_IDLE, 1000); - } else if (trans->trans_cfg->mq_rx_supported) { + } else if (trans->mac_cfg->mq_rx_supported) { iwl_write_prph(trans, RFH_RXF_DMA_CFG, 0); return iwl_poll_prph_bit(trans, RFH_GEN_STATUS, RXF_DMA_IDLE, RXF_DMA_IDLE, 1000); @@ -175,7 +176,7 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, * 1. shadow registers aren't enabled * 2. there is a chance that the NIC is asleep */ - if (!trans->trans_cfg->base_params->shadow_reg_enable && + if (!trans->mac_cfg->base->shadow_reg_enable && test_bit(STATUS_TPOWER_PMI, &trans->status)) { reg = iwl_read32(trans, CSR_UCODE_DRV_GP1); @@ -190,9 +191,9 @@ static void iwl_pcie_rxq_inc_wr_ptr(struct iwl_trans *trans, } rxq->write_actual = round_down(rxq->write, 8); - if (!trans->trans_cfg->mq_rx_supported) + if (!trans->mac_cfg->mq_rx_supported) iwl_write32(trans, FH_RSCSR_CHNL0_WPTR, rxq->write_actual); - else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) iwl_write32(trans, HBUS_TARG_WRPTR, rxq->write_actual | HBUS_TARG_WRPTR_RX_Q(rxq->id)); else @@ -205,7 +206,7 @@ static void iwl_pcie_rxq_check_wrptr(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int i; - for (i = 0; i < trans->num_rx_queues; i++) { + for (i = 0; i < trans->info.num_rxqs; i++) { struct iwl_rxq *rxq = &trans_pcie->rxq[i]; if (!rxq->need_update) @@ -221,7 +222,7 @@ static void iwl_pcie_restock_bd(struct iwl_trans *trans, struct iwl_rxq *rxq, struct iwl_rx_mem_buffer *rxb) { - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { struct iwl_rx_transfer_desc *bd = rxq->bd; BUILD_BUG_ON(sizeof(*bd) != 2 * sizeof(u64)); @@ -348,7 +349,7 @@ static void iwl_pcie_rxsq_restock(struct iwl_trans *trans, static void iwl_pcie_rxq_restock(struct iwl_trans *trans, struct iwl_rxq *rxq) { - if (trans->trans_cfg->mq_rx_supported) + if (trans->mac_cfg->mq_rx_supported) iwl_pcie_rxmq_restock(trans, rxq); else iwl_pcie_rxsq_restock(trans, rxq); @@ -362,8 +363,8 @@ static struct page *iwl_pcie_rx_alloc_page(struct iwl_trans *trans, u32 *offset, gfp_t priority) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - unsigned int rbsize = iwl_trans_get_rb_size(trans_pcie->rx_buf_size); unsigned int allocsize = PAGE_SIZE << trans_pcie->rx_page_order; + unsigned int rbsize = trans_pcie->rx_buf_bytes; struct page *page; gfp_t gfp_mask = priority; @@ -657,19 +658,19 @@ void iwl_pcie_rx_allocator_work(struct work_struct *data) static int iwl_pcie_free_bd_size(struct iwl_trans *trans) { - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) return sizeof(struct iwl_rx_transfer_desc); - return trans->trans_cfg->mq_rx_supported ? + return trans->mac_cfg->mq_rx_supported ? sizeof(__le64) : sizeof(__le32); } static int iwl_pcie_used_bd_size(struct iwl_trans *trans) { - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) return sizeof(struct iwl_rx_completion_desc_bz); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) return sizeof(struct iwl_rx_completion_desc); return sizeof(__le32); @@ -701,7 +702,7 @@ static void iwl_pcie_free_rxq_dma(struct iwl_trans *trans, static size_t iwl_pcie_rb_stts_size(struct iwl_trans *trans) { - bool use_rx_td = (trans->trans_cfg->device_family >= + bool use_rx_td = (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210); if (use_rx_td) @@ -720,8 +721,8 @@ static int iwl_pcie_alloc_rxq_dma(struct iwl_trans *trans, int free_size; spin_lock_init(&rxq->lock); - if (trans->trans_cfg->mq_rx_supported) - rxq->queue_size = trans->cfg->num_rbds; + if (trans->mac_cfg->mq_rx_supported) + rxq->queue_size = iwl_trans_get_num_rbds(trans); else rxq->queue_size = RX_QUEUE_SIZE; @@ -736,7 +737,7 @@ static int iwl_pcie_alloc_rxq_dma(struct iwl_trans *trans, if (!rxq->bd) goto err; - if (trans->trans_cfg->mq_rx_supported) { + if (trans->mac_cfg->mq_rx_supported) { rxq->used_bd = dma_alloc_coherent(dev, iwl_pcie_used_bd_size(trans) * rxq->queue_size, @@ -753,7 +754,7 @@ static int iwl_pcie_alloc_rxq_dma(struct iwl_trans *trans, return 0; err: - for (i = 0; i < trans->num_rx_queues; i++) { + for (i = 0; i < trans->info.num_rxqs; i++) { struct iwl_rxq *rxq = &trans_pcie->rxq[i]; iwl_pcie_free_rxq_dma(trans, rxq); @@ -772,7 +773,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans) if (WARN_ON(trans_pcie->rxq)) return -EINVAL; - trans_pcie->rxq = kcalloc(trans->num_rx_queues, sizeof(struct iwl_rxq), + trans_pcie->rxq = kcalloc(trans->info.num_rxqs, sizeof(struct iwl_rxq), GFP_KERNEL); trans_pcie->rx_pool = kcalloc(RX_POOL_SIZE(trans_pcie->num_rx_bufs), sizeof(trans_pcie->rx_pool[0]), @@ -795,7 +796,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans) */ trans_pcie->base_rb_stts = dma_alloc_coherent(trans->dev, - rb_stts_size * trans->num_rx_queues, + rb_stts_size * trans->info.num_rxqs, &trans_pcie->base_rb_stts_dma, GFP_KERNEL); if (!trans_pcie->base_rb_stts) { @@ -803,7 +804,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans) goto err; } - for (i = 0; i < trans->num_rx_queues; i++) { + for (i = 0; i < trans->info.num_rxqs; i++) { struct iwl_rxq *rxq = &trans_pcie->rxq[i]; rxq->id = i; @@ -816,7 +817,7 @@ static int iwl_pcie_rx_alloc(struct iwl_trans *trans) err: if (trans_pcie->base_rb_stts) { dma_free_coherent(trans->dev, - rb_stts_size * trans->num_rx_queues, + rb_stts_size * trans->info.num_rxqs, trans_pcie->base_rb_stts, trans_pcie->base_rb_stts_dma); trans_pcie->base_rb_stts = NULL; @@ -834,11 +835,10 @@ err: static void iwl_pcie_rx_hw_init(struct iwl_trans *trans, struct iwl_rxq *rxq) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 rb_size; const u32 rfdnlog = RX_QUEUE_SIZE_LOG; /* 256 RBDs */ - switch (trans_pcie->rx_buf_size) { + switch (trans->conf.rx_buf_size) { case IWL_AMSDU_4K: rb_size = FH_RCSR_RX_CONFIG_REG_VAL_RB_SIZE_4K; break; @@ -906,7 +906,7 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) u32 rb_size, enabled = 0; int i; - switch (trans_pcie->rx_buf_size) { + switch (trans->conf.rx_buf_size) { case IWL_AMSDU_2K: rb_size = RFH_RXF_DMA_RB_SIZE_2K; break; @@ -932,7 +932,7 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) /* disable free amd used rx queue operation */ iwl_write_prph_no_grab(trans, RFH_RXF_RXQ_ACTIVE, 0); - for (i = 0; i < trans->num_rx_queues; i++) { + for (i = 0; i < trans->info.num_rxqs; i++) { /* Tell device where to find RBD free table in DRAM */ iwl_write_prph64_no_grab(trans, RFH_Q_FRBDCB_BA_LSB(i), @@ -976,7 +976,7 @@ static void iwl_pcie_rx_mq_hw_init(struct iwl_trans *trans) RFH_GEN_CFG_VAL(DEFAULT_RXQ_NUM, 0) | RFH_GEN_CFG_SERVICE_DMA_SNOOP | RFH_GEN_CFG_VAL(RB_CHUNK_SIZE, - trans->trans_cfg->integrated ? + trans->mac_cfg->integrated ? RFH_GEN_CFG_RB_CHUNK_SIZE_64 : RFH_GEN_CFG_RB_CHUNK_SIZE_128)); /* Enable the relevant rx queues */ @@ -1072,7 +1072,7 @@ void iwl_pcie_rx_napi_sync(struct iwl_trans *trans) if (unlikely(!trans_pcie->rxq)) return; - for (i = 0; i < trans->num_rx_queues; i++) { + for (i = 0; i < trans->info.num_rxqs; i++) { struct iwl_rxq *rxq = &trans_pcie->rxq[i]; if (rxq && rxq->napi.poll) @@ -1109,7 +1109,7 @@ static int _iwl_pcie_rx_init(struct iwl_trans *trans) for (i = 0; i < RX_QUEUE_SIZE; i++) def_rxq->queue[i] = NULL; - for (i = 0; i < trans->num_rx_queues; i++) { + for (i = 0; i < trans->info.num_rxqs; i++) { struct iwl_rxq *rxq = &trans_pcie->rxq[i]; spin_lock_bh(&rxq->lock); @@ -1122,7 +1122,7 @@ static int _iwl_pcie_rx_init(struct iwl_trans *trans) rxq->write = 0; rxq->write_actual = 0; memset(rxq->rb_stts, 0, - (trans->trans_cfg->device_family >= + (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) ? sizeof(__le16) : sizeof(struct iwl_rb_status)); @@ -1144,9 +1144,9 @@ static int _iwl_pcie_rx_init(struct iwl_trans *trans) } /* move the pool to the default queue and allocator ownerships */ - queue_size = trans->trans_cfg->mq_rx_supported ? + queue_size = trans->mac_cfg->mq_rx_supported ? trans_pcie->num_rx_bufs - 1 : RX_QUEUE_SIZE; - allocator_pool_size = trans->num_rx_queues * + allocator_pool_size = trans->info.num_rxqs * (RX_CLAIM_REQ_ALLOC - RX_POST_REQ_ALLOC); num_alloc = queue_size + allocator_pool_size; @@ -1175,7 +1175,7 @@ int iwl_pcie_rx_init(struct iwl_trans *trans) if (ret) return ret; - if (trans->trans_cfg->mq_rx_supported) + if (trans->mac_cfg->mq_rx_supported) iwl_pcie_rx_mq_hw_init(trans); else iwl_pcie_rx_hw_init(trans, trans_pcie->rxq); @@ -1223,14 +1223,14 @@ void iwl_pcie_rx_free(struct iwl_trans *trans) if (trans_pcie->base_rb_stts) { dma_free_coherent(trans->dev, - rb_stts_size * trans->num_rx_queues, + rb_stts_size * trans->info.num_rxqs, trans_pcie->base_rb_stts, trans_pcie->base_rb_stts_dma); trans_pcie->base_rb_stts = NULL; trans_pcie->base_rb_stts_dma = 0; } - for (i = 0; i < trans->num_rx_queues; i++) { + for (i = 0; i < trans->info.num_rxqs; i++) { struct iwl_rxq *rxq = &trans_pcie->rxq[i]; iwl_pcie_free_rxq_dma(trans, rxq); @@ -1301,7 +1301,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, int i) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]; + struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue]; bool page_stolen = false; int max_len = trans_pcie->rx_buf_bytes; u32 offset = 0; @@ -1368,8 +1368,8 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, if (reclaim && !pkt->hdr.group_id) { int i; - for (i = 0; i < trans_pcie->n_no_reclaim_cmds; i++) { - if (trans_pcie->no_reclaim_cmds[i] == + for (i = 0; i < trans->conf.n_no_reclaim_cmds; i++) { + if (trans->conf.no_reclaim_cmds[i] == pkt->hdr.cmd) { reclaim = false; break; @@ -1408,7 +1408,7 @@ static void iwl_pcie_rx_handle_rb(struct iwl_trans *trans, } page_stolen |= rxcb._page_stolen; - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) break; } @@ -1454,18 +1454,18 @@ static struct iwl_rx_mem_buffer *iwl_pcie_get_rxb(struct iwl_trans *trans, BUILD_BUG_ON(sizeof(struct iwl_rx_completion_desc) != 32); BUILD_BUG_ON(sizeof(struct iwl_rx_completion_desc_bz) != 4); - if (!trans->trans_cfg->mq_rx_supported) { + if (!trans->mac_cfg->mq_rx_supported) { rxb = rxq->queue[i]; rxq->queue[i] = NULL; return rxb; } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { struct iwl_rx_completion_desc_bz *cd = rxq->used_bd; vid = le16_to_cpu(cd[i].rbid); *join = cd[i].flags & IWL_RX_CD_FLAGS_FRAGMENTED; - } else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + } else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { struct iwl_rx_completion_desc *cd = rxq->used_bd; vid = le16_to_cpu(cd[i].rbid); @@ -1648,7 +1648,7 @@ irqreturn_t iwl_pcie_irq_rx_msix_handler(int irq, void *dev_id) trace_iwlwifi_dev_irq_msix(trans->dev, entry, false, 0, 0); - if (WARN_ON(entry->entry >= trans->num_rx_queues)) + if (WARN_ON(entry->entry >= trans->info.num_rxqs)) return IRQ_NONE; if (!trans_pcie->rxq) { @@ -1683,21 +1683,21 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) /* W/A for WiFi/WiMAX coex and WiMAX own the RF */ if (trans->cfg->internal_wimax_coex && - !trans->cfg->apmg_not_supported && + !trans->mac_cfg->base->apmg_not_supported && (!(iwl_read_prph(trans, APMG_CLK_CTRL_REG) & APMS_CLK_VAL_MRB_FUNC_MODE) || (iwl_read_prph(trans, APMG_PS_CTRL_REG) & APMG_PS_CTRL_VAL_RESET_REQ))) { clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); iwl_op_mode_wimax_active(trans->op_mode); - wake_up(&trans->wait_command_queue); + wake_up(&trans_pcie->wait_command_queue); return; } - for (i = 0; i < trans->trans_cfg->base_params->num_of_queues; i++) { + for (i = 0; i < trans->mac_cfg->base->num_of_queues; i++) { if (!trans_pcie->txqs.txq[i]) continue; - del_timer(&trans_pcie->txqs.txq[i]->stuck_timer); + timer_delete(&trans_pcie->txqs.txq[i]->stuck_timer); } /* The STATUS_FW_ERROR bit is set in this function. This must happen @@ -1705,7 +1705,7 @@ static void iwl_pcie_irq_handle_error(struct iwl_trans *trans) iwl_trans_fw_error(trans, IWL_ERR_TYPE_IRQ); clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); - wake_up(&trans->wait_command_queue); + wake_up(&trans_pcie->wait_command_queue); } static u32 iwl_pcie_int_cause_non_ict(struct iwl_trans *trans) @@ -1820,7 +1820,7 @@ void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq) &trans->status)) IWL_DEBUG_RF_KILL(trans, "Rfkill while SYNC HCMD in flight\n"); - wake_up(&trans->wait_command_queue); + wake_up(&trans_pcie->wait_command_queue); } else { clear_bit(STATUS_RFKILL_HW, &trans->status); if (trans_pcie->opmode_down) @@ -1828,6 +1828,54 @@ void iwl_pcie_handle_rfkill_irq(struct iwl_trans *trans, bool from_irq) } } +static void iwl_trans_pcie_handle_reset_interrupt(struct iwl_trans *trans) +{ + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + u32 state; + + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_SC) { + u32 val = iwl_read32(trans, CSR_IPC_STATE); + + state = u32_get_bits(val, CSR_IPC_STATE_RESET); + IWL_DEBUG_ISR(trans, "IPC state = 0x%x/%d\n", val, state); + } else { + state = CSR_IPC_STATE_RESET_SW_READY; + } + + switch (state) { + case CSR_IPC_STATE_RESET_SW_READY: + if (trans_pcie->fw_reset_state == FW_RESET_REQUESTED) { + IWL_DEBUG_ISR(trans, "Reset flow completed\n"); + trans_pcie->fw_reset_state = FW_RESET_OK; + wake_up(&trans_pcie->fw_reset_waitq); + break; + } + fallthrough; + case CSR_IPC_STATE_RESET_TOP_READY: + /* FIXME: handle this case when requesting TOP reset */ + fallthrough; + case CSR_IPC_STATE_RESET_NONE: + IWL_FW_CHECK_FAILED(trans, + "Invalid reset interrupt (state=%d)!\n", + state); + break; + case CSR_IPC_STATE_RESET_TOP_FOLLOWER: + if (trans_pcie->fw_reset_state == FW_RESET_REQUESTED) { + /* if we were in reset, wake that up */ + IWL_INFO(trans, + "TOP reset from BT while doing reset\n"); + trans_pcie->fw_reset_state = FW_RESET_OK; + wake_up(&trans_pcie->fw_reset_waitq); + } else { + IWL_INFO(trans, "TOP reset from BT\n"); + trans->state = IWL_TRANS_NO_FW; + iwl_trans_schedule_reset(trans, + IWL_ERR_TYPE_TOP_RESET_BY_BT); + } + break; + } +} + irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) { struct iwl_trans *trans = dev_id; @@ -1936,7 +1984,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) if (inta & CSR_INT_BIT_ALIVE) { IWL_DEBUG_ISR(trans, "Alive interrupt\n"); isr_stats->alive++; - if (trans->trans_cfg->gen2) { + if (trans->mac_cfg->gen2) { /* * We can restock, since firmware configured * the RFH @@ -1947,6 +1995,11 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) handled |= CSR_INT_BIT_ALIVE; } + if (inta & CSR_INT_BIT_RESET_DONE) { + iwl_trans_pcie_handle_reset_interrupt(trans); + handled |= CSR_INT_BIT_RESET_DONE; + } + /* Safely ignore these bits for debug checks below */ inta &= ~(CSR_INT_BIT_SCD | CSR_INT_BIT_ALIVE); @@ -1968,7 +2021,12 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) IWL_ERR(trans, "Microcode SW error detected. " " Restarting 0x%X.\n", inta); isr_stats->sw++; - iwl_pcie_irq_handle_error(trans); + if (trans_pcie->fw_reset_state == FW_RESET_REQUESTED) { + trans_pcie->fw_reset_state = FW_RESET_ERROR; + wake_up(&trans_pcie->fw_reset_waitq); + } else { + iwl_pcie_irq_handle_error(trans); + } handled |= CSR_INT_BIT_SW_ERR; } @@ -2074,7 +2132,7 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id) iwl_enable_rfkill_int(trans); /* Re-enable the ALIVE / Rx interrupt if it occurred */ else if (handled & (CSR_INT_BIT_ALIVE | CSR_INT_BIT_FH_RX)) - iwl_enable_fw_load_int_ctx_info(trans); + iwl_enable_fw_load_int_ctx_info(trans, false); spin_unlock_bh(&trans_pcie->irq_lock); } @@ -2289,7 +2347,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) } } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) sw_err = inta_hw & MSIX_HW_INT_CAUSES_REG_SW_ERR_BZ; else sw_err = inta_hw & MSIX_HW_INT_CAUSES_REG_SW_ERR; @@ -2297,9 +2355,13 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) if (inta_hw & MSIX_HW_INT_CAUSES_REG_TOP_FATAL_ERR) { IWL_ERR(trans, "TOP Fatal error detected, inta_hw=0x%x.\n", inta_hw); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) - iwl_trans_pcie_reset(trans, - IWL_RESET_MODE_PROD_RESET); + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + trans->request_top_reset = 1; + iwl_op_mode_nic_error(trans->op_mode, + IWL_ERR_TYPE_TOP_FATAL_ERROR); + iwl_trans_schedule_reset(trans, + IWL_ERR_TYPE_TOP_FATAL_ERROR); + } } /* Error detected by uCode */ @@ -2338,7 +2400,7 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) if (inta_hw & MSIX_HW_INT_CAUSES_REG_ALIVE) { IWL_DEBUG_ISR(trans, "Alive interrupt\n"); isr_stats->alive++; - if (trans->trans_cfg->gen2) { + if (trans->mac_cfg->gen2) { /* We can restock, since firmware configured the RFH */ iwl_pcie_rxmq_restock(trans, trans_pcie->rxq); } @@ -2388,11 +2450,8 @@ irqreturn_t iwl_pcie_irq_msix_handler(int irq, void *dev_id) iwl_pcie_irq_handle_error(trans); } - if (inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE) { - IWL_DEBUG_ISR(trans, "Reset flow completed\n"); - trans_pcie->fw_reset_state = FW_RESET_OK; - wake_up(&trans_pcie->fw_reset_waitq); - } + if (inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE) + iwl_trans_pcie_handle_reset_interrupt(trans); if (!polling) iwl_pcie_clear_irq(trans, entry->entry); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c index 793514a1852a..38ad719161e6 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans-gen2.c @@ -1,12 +1,12 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2024 Intel Corporation + * Copyright (C) 2018-2025 Intel Corporation */ #include "iwl-trans.h" #include "iwl-prph.h" #include "iwl-context-info.h" -#include "iwl-context-info-gen3.h" +#include "iwl-context-info-v2.h" #include "internal.h" #include "fw/dbg.h" @@ -81,13 +81,13 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) /* Stop device's DMA activity */ iwl_pcie_apm_stop_master(trans); - iwl_trans_sw_reset(trans, false); + iwl_trans_pcie_sw_reset(trans, false); /* * Clear "initialization complete" bit to move adapter from * D0A* (powered-up Active) --> D0U* (Uninitialized) state. */ - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_MAC_INIT); else @@ -95,17 +95,17 @@ static void iwl_pcie_gen2_apm_stop(struct iwl_trans *trans, bool op_mode_leave) CSR_GP_CNTRL_REG_FLAG_INIT_DONE); } -static void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) +void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; trans_pcie->fw_reset_state = FW_RESET_REQUESTED; - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) iwl_write_umac_prph(trans, UREG_NIC_SET_NMI_DRIVER, UREG_NIC_SET_NMI_DRIVER_RESET_HANDSHAKE); - else if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) + else if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210) iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, UREG_DOORBELL_TO_ISR6_RESET_HANDSHAKE); else @@ -117,13 +117,23 @@ static void iwl_trans_pcie_fw_reset_handshake(struct iwl_trans *trans) trans_pcie->fw_reset_state != FW_RESET_REQUESTED, FW_RESET_TIMEOUT); if (!ret || trans_pcie->fw_reset_state == FW_RESET_ERROR) { - u32 inta_hw = iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD); + bool reset_done; + u32 inta_hw; + + if (trans_pcie->msix_enabled) { + inta_hw = iwl_read32(trans, CSR_MSIX_HW_INT_CAUSES_AD); + reset_done = + inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE; + } else { + inta_hw = iwl_read32(trans, CSR_INT_MASK); + reset_done = inta_hw & CSR_INT_BIT_RESET_DONE; + } IWL_ERR(trans, - "timeout waiting for FW reset ACK (inta_hw=0x%x)\n", - inta_hw); + "timeout waiting for FW reset ACK (inta_hw=0x%x, reset_done %d)\n", + inta_hw, reset_done); - if (!(inta_hw & MSIX_HW_INT_CAUSES_REG_RESET_DONE)) { + if (!reset_done) { struct iwl_fw_error_dump_mode mode = { .type = IWL_ERR_TYPE_RESET_HS_TIMEOUT, .context = IWL_ERR_CONTEXT_FROM_OPMODE, @@ -147,8 +157,14 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) return; if (trans->state >= IWL_TRANS_FW_STARTED && - trans_pcie->fw_reset_handshake) + trans->conf.fw_reset_handshake) { + /* + * Reset handshake can dump firmware on timeout, but that + * should assume that the firmware is already dead. + */ + trans->state = IWL_TRANS_NO_FW; iwl_trans_pcie_fw_reset_handshake(trans); + } trans_pcie->is_down = true; @@ -175,8 +191,8 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) } iwl_pcie_ctxt_info_free_paging(trans); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) - iwl_pcie_ctxt_info_gen3_free(trans, false); + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + iwl_pcie_ctxt_info_v2_free(trans, false); else iwl_pcie_ctxt_info_free(trans); @@ -184,7 +200,7 @@ static void _iwl_trans_pcie_gen2_stop_device(struct iwl_trans *trans) iwl_pcie_gen2_apm_stop(trans, false); /* re-take ownership to prevent other users from stealing the device */ - iwl_trans_sw_reset(trans, true); + iwl_trans_pcie_sw_reset(trans, true); /* * Upon stop, the IVAR table gets erased, so msi-x won't @@ -237,7 +253,7 @@ static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int queue_size = max_t(u32, IWL_CMD_QUEUE_SIZE, - trans->cfg->min_txq_size); + trans->mac_cfg->base->min_txq_size); int ret; /* TODO: most of the logic can be removed in A0 - but not in Z0 */ @@ -254,7 +270,7 @@ static int iwl_pcie_gen2_nic_init(struct iwl_trans *trans) return -ENOMEM; /* Allocate or reset and init all Tx and Command queues */ - if (iwl_txq_gen2_init(trans, trans_pcie->txqs.cmd.q_id, queue_size)) + if (iwl_txq_gen2_init(trans, trans->conf.cmd_queue, queue_size)) return -ENOMEM; /* enable shadow regs in HW */ @@ -275,7 +291,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) if (buf[0]) return; - switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { + switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) { case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_JF): pos = scnprintf(buf, buflen, "JF"); break; @@ -299,7 +315,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) break; case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_WP): if (SILICON_Z_STEP == - CSR_HW_RFID_STEP(trans->hw_rf_id)) + CSR_HW_RFID_STEP(trans->info.hw_rf_id)) pos = scnprintf(buf, buflen, "WHTC"); else pos = scnprintf(buf, buflen, "WH"); @@ -308,7 +324,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) return; } - switch (CSR_HW_RFID_TYPE(trans->hw_rf_id)) { + switch (CSR_HW_RFID_TYPE(trans->info.hw_rf_id)) { case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR): case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HR1): case CSR_HW_RFID_TYPE(CSR_HW_RF_ID_TYPE_HRCDB): @@ -331,7 +347,7 @@ static void iwl_pcie_get_rf_name(struct iwl_trans *trans) } pos += scnprintf(buf + pos, buflen - pos, ", rfid=0x%x", - trans->hw_rf_id); + trans->info.hw_rf_id); IWL_INFO(trans, "Detected RF %s\n", buf); @@ -358,8 +374,8 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans) /* now that we got alive we can free the fw image & the context info. * paging memory cannot be freed included since FW will still use it */ - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) - iwl_pcie_ctxt_info_gen3_free(trans, true); + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + iwl_pcie_ctxt_info_v2_free(trans, true); else iwl_pcie_ctxt_info_free(trans); @@ -373,6 +389,11 @@ void iwl_trans_pcie_gen2_fw_alive(struct iwl_trans *trans) iwl_pcie_get_rf_name(trans); mutex_unlock(&trans_pcie->mutex); + + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + trans->step_urm = !!(iwl_read_umac_prph(trans, + CNVI_PMU_STEP_FLOW) & + CNVI_PMU_STEP_FLOW_FORCE_URM); } static bool iwl_pcie_set_ltr(struct iwl_trans *trans) @@ -392,21 +413,21 @@ static bool iwl_pcie_set_ltr(struct iwl_trans *trans) * initialize the LTR to ~250 usec (see ltr_val above). * The firmware initializes this again later (to a smaller value). */ - if ((trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210 || - trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) && - !trans->trans_cfg->integrated) { + if ((trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210 || + trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000) && + !trans->mac_cfg->integrated) { iwl_write32(trans, CSR_LTR_LONG_VAL_AD, ltr_val); return true; } - if (trans->trans_cfg->integrated && - trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000) { + if (trans->mac_cfg->integrated && + trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000) { iwl_write_prph(trans, HPM_MAC_LTR_CSR, HPM_MAC_LRT_ENABLE_ALL); iwl_write_prph(trans, HPM_UMAC_LTR, ltr_val); return true; } - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210) { /* First clear the interrupt, just in case */ iwl_write32(trans, CSR_MSIX_HW_INT_CAUSES_AD, MSIX_HW_INT_CAUSES_REG_IML); @@ -463,16 +484,22 @@ static void iwl_pcie_spin_for_iml(struct iwl_trans *trans) } int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, - const struct fw_img *fw, bool run_in_rfkill) + const struct iwl_fw *fw, + const struct fw_img *img, + bool run_in_rfkill) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); bool hw_rfkill, keep_ram_busy; + bool top_reset_done = false; int ret; + mutex_lock(&trans_pcie->mutex); +again: /* This may fail if AMT took ownership of the device */ if (iwl_pcie_prepare_card_hw(trans)) { IWL_WARN(trans, "Exit HW not ready\n"); - return -EIO; + ret = -EIO; + goto out; } iwl_enable_rfkill_int(trans); @@ -489,8 +516,6 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, /* Make sure it finished running */ iwl_pcie_synchronize_irqs(trans); - mutex_lock(&trans_pcie->mutex); - /* If platform's RF_KILL switch is NOT set to KILL */ hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); if (hw_rfkill && !run_in_rfkill) { @@ -520,22 +545,37 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, goto out; } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) - ret = iwl_pcie_ctxt_info_gen3_init(trans, fw); - else - ret = iwl_pcie_ctxt_info_init(trans, fw); - if (ret) - goto out; + if (WARN_ON(trans->do_top_reset && + trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC)) + return -EINVAL; + + /* we need to wait later - set state */ + if (trans->do_top_reset) + trans_pcie->fw_reset_state = FW_RESET_TOP_REQUESTED; + + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (!top_reset_done) { + ret = iwl_pcie_ctxt_info_v2_alloc(trans, fw, img); + if (ret) + goto out; + } + + iwl_pcie_ctxt_info_v2_kick(trans); + } else { + ret = iwl_pcie_ctxt_info_init(trans, img); + if (ret) + goto out; + } keep_ram_busy = !iwl_pcie_set_ltr(trans); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { IWL_DEBUG_POWER(trans, "function scratch register value is 0x%08x\n", iwl_read32(trans, CSR_FUNC_SCRATCH)); iwl_write32(trans, CSR_FUNC_SCRATCH, CSR_FUNC_SCRATCH_INIT_VALUE); iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_ROM_START); - } else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + } else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { iwl_write_umac_prph(trans, UREG_CPU_INIT_RUN, 1); } else { iwl_write_prph(trans, UREG_CPU_INIT_RUN, 1); @@ -544,6 +584,38 @@ int iwl_trans_pcie_gen2_start_fw(struct iwl_trans *trans, if (keep_ram_busy) iwl_pcie_spin_for_iml(trans); + if (trans->do_top_reset) { + trans->do_top_reset = 0; + +#define FW_TOP_RESET_TIMEOUT (HZ / 4) + ret = wait_event_timeout(trans_pcie->fw_reset_waitq, + trans_pcie->fw_reset_state != FW_RESET_TOP_REQUESTED, + FW_TOP_RESET_TIMEOUT); + + if (trans_pcie->fw_reset_state != FW_RESET_OK) { + if (trans_pcie->fw_reset_state != FW_RESET_TOP_REQUESTED) + IWL_ERR(trans, + "TOP reset interrupted by error (state %d)!\n", + trans_pcie->fw_reset_state); + else + IWL_ERR(trans, "TOP reset timed out!\n"); + iwl_op_mode_nic_error(trans->op_mode, + IWL_ERR_TYPE_TOP_RESET_FAILED); + iwl_trans_schedule_reset(trans, + IWL_ERR_TYPE_TOP_RESET_FAILED); + ret = -EIO; + goto out; + } + + msleep(10); + IWL_INFO(trans, "TOP reset successful, reinit now\n"); + /* now load the firmware again properly */ + trans_pcie->prph_scratch->ctrl_cfg.control.control_flags &= + ~cpu_to_le32(IWL_PRPH_SCRATCH_TOP_RESET); + top_reset_done = true; + goto again; + } + /* re-check RF-Kill state since we may have missed the interrupt */ hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); if (hw_rfkill && !run_in_rfkill) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index c917ed4c19bc..cc4d289b110d 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -28,7 +28,7 @@ #include "mei/iwl-mei.h" #include "internal.h" #include "iwl-fh.h" -#include "iwl-context-info-gen3.h" +#include "iwl-context-info-v2.h" /* extended range in FW SRAM */ #define IWL_FW_MEM_EXTENDED_START 0x40000 @@ -131,7 +131,7 @@ out: int iwl_trans_pcie_sw_reset(struct iwl_trans *trans, bool retake_ownership) { /* Reset entire device - do controller reset (results in SHRD_HW_RST) */ - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_SW_RESET); usleep_range(10000, 20000); @@ -237,7 +237,7 @@ static void iwl_trans_pcie_write_shr(struct iwl_trans *trans, u32 reg, u32 val) static void iwl_pcie_set_pwr(struct iwl_trans *trans, bool vaux) { - if (trans->cfg->apmg_not_supported) + if (trans->mac_cfg->base->apmg_not_supported) return; if (vaux && pci_pme_capable(to_pci_dev(trans->dev), PCI_D3cold)) @@ -293,7 +293,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) */ /* Disable L0S exit timer (platform NMI Work/Around) */ - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000) iwl_set_bit(trans, CSR_GIO_CHICKEN_BITS, CSR_GIO_CHICKEN_BITS_REG_BIT_DIS_L0S_EXIT_TIMER); @@ -317,7 +317,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) iwl_pcie_apm_config(trans); /* Configure analog phase-lock-loop before activating to D0A */ - if (trans->trans_cfg->base_params->pll_cfg) + if (trans->mac_cfg->base->pll_cfg) iwl_set_bit(trans, CSR_ANA_PLL_CFG, CSR50_ANA_PLL_CFG_VAL); ret = iwl_finish_nic_init(trans); @@ -353,7 +353,7 @@ static int iwl_pcie_apm_init(struct iwl_trans *trans) * bits do not disable clocks. This preserves any hardware * bits already set by default in "CLK_CTRL_REG" after reset. */ - if (!trans->cfg->apmg_not_supported) { + if (!trans->mac_cfg->base->apmg_not_supported) { iwl_write_prph(trans, APMG_CLK_EN_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(20); @@ -469,7 +469,7 @@ void iwl_pcie_apm_stop_master(struct iwl_trans *trans) /* stop device's busmaster DMA activity */ - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_BUS_MASTER_DISABLE_REQ); @@ -501,10 +501,10 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) iwl_pcie_apm_init(trans); /* inform ME that we are leaving */ - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000) + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000) iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_WAKE_ME); - else if (trans->trans_cfg->device_family >= + else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000) { iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, CSR_RESET_LINK_PWR_MGMT_DISABLED); @@ -565,7 +565,7 @@ static int iwl_pcie_nic_init(struct iwl_trans *trans) return -ENOMEM; } - if (trans->trans_cfg->base_params->shadow_reg_enable) { + if (trans->mac_cfg->base->shadow_reg_enable) { /* enable shadow regs in HW */ iwl_set_bit(trans, CSR_MAC_SHADOW_REG_CTRL, 0x800FFFFF); IWL_DEBUG_INFO(trans, "Enabling shadow registers in device\n"); @@ -634,7 +634,7 @@ int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) IWL_DEBUG_INFO(trans, "Couldn't prepare the card but SAP is connected\n"); trans->csme_own = true; - if (trans->trans_cfg->device_family != + if (trans->mac_cfg->device_family != IWL_DEVICE_FAMILY_9000) IWL_ERR(trans, "SAP not supported for this NIC family\n"); @@ -819,7 +819,7 @@ static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, iwl_enable_interrupts(trans); - if (trans->trans_cfg->gen2) { + if (trans->mac_cfg->gen2) { if (cpu == 1) iwl_write_prph(trans, UREG_UCODE_LOAD_STATUS, 0xFFFF); @@ -977,7 +977,7 @@ monitor: if (dest->monitor_mode == EXTERNAL_MODE && fw_mon->size) { iwl_write_prph(trans, le32_to_cpu(dest->base_reg), fw_mon->physical >> dest->base_shift); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000) iwl_write_prph(trans, le32_to_cpu(dest->end_reg), (fw_mon->physical + fw_mon->size - 256) >> dest->end_shift); @@ -1153,7 +1153,7 @@ static void iwl_pcie_map_non_rx_causes(struct iwl_trans *trans) */ iwl_pcie_map_list(trans, causes_list_common, ARRAY_SIZE(causes_list_common), val); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) iwl_pcie_map_list(trans, causes_list_bz, ARRAY_SIZE(causes_list_bz), val); else @@ -1175,7 +1175,7 @@ static void iwl_pcie_map_rx_causes(struct iwl_trans *trans) * the other (N - 2) interrupt vectors. */ val = BIT(MSIX_FH_INT_CAUSES_Q(0)); - for (idx = 1; idx < trans->num_rx_queues; idx++) { + for (idx = 1; idx < trans->info.num_rxqs; idx++) { iwl_write8(trans, CSR_MSIX_RX_IVAR(idx), MSIX_FH_INT_CAUSES_Q(idx - offset)); val |= BIT(MSIX_FH_INT_CAUSES_Q(idx)); @@ -1196,7 +1196,7 @@ void iwl_pcie_conf_msix_hw(struct iwl_trans_pcie *trans_pcie) struct iwl_trans *trans = trans_pcie->trans; if (!trans_pcie->msix_enabled) { - if (trans->trans_cfg->mq_rx_supported && + if (trans->mac_cfg->mq_rx_supported && test_bit(STATUS_DEVICE_ENABLED, &trans->status)) iwl_write_umac_prph(trans, UREG_CHICK, UREG_CHICK_MSI_ENABLE); @@ -1271,7 +1271,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq) iwl_pcie_rx_stop(trans); /* Power-down device's busmaster DMA clocks */ - if (!trans->cfg->apmg_not_supported) { + if (!trans->mac_cfg->base->apmg_not_supported) { iwl_write_prph(trans, APMG_CLK_DIS_REG, APMG_CLK_VAL_DMA_CLK_RQT); udelay(5); @@ -1279,7 +1279,7 @@ static void _iwl_trans_pcie_stop_device(struct iwl_trans *trans, bool from_irq) } /* Make sure (redundant) we've released our request to stay awake */ - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ); else @@ -1337,7 +1337,9 @@ void iwl_pcie_synchronize_irqs(struct iwl_trans *trans) } int iwl_trans_pcie_start_fw(struct iwl_trans *trans, - const struct fw_img *fw, bool run_in_rfkill) + const struct iwl_fw *fw, + const struct fw_img *img, + bool run_in_rfkill) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); bool hw_rfkill; @@ -1408,10 +1410,10 @@ int iwl_trans_pcie_start_fw(struct iwl_trans *trans, iwl_write32(trans, CSR_UCODE_DRV_GP1_CLR, CSR_UCODE_SW_BIT_RFKILL); /* Load the given image to the HW */ - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) - ret = iwl_pcie_load_given_ucode_8000(trans, fw); + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000) + ret = iwl_pcie_load_given_ucode_8000(trans, img); else - ret = iwl_pcie_load_given_ucode(trans, fw); + ret = iwl_pcie_load_given_ucode(trans, img); /* re-check RF-Kill state since we may have missed the interrupt */ hw_rfkill = iwl_pcie_check_hw_rf_kill(trans); @@ -1423,10 +1425,10 @@ out: return ret; } -void iwl_trans_pcie_fw_alive(struct iwl_trans *trans, u32 scd_addr) +void iwl_trans_pcie_fw_alive(struct iwl_trans *trans) { iwl_pcie_reset_ict(trans); - iwl_pcie_tx_start(trans, scd_addr); + iwl_pcie_tx_start(trans); } void iwl_trans_pcie_handle_stop_rfkill(struct iwl_trans *trans, @@ -1485,7 +1487,7 @@ void iwl_trans_pcie_rf_kill(struct iwl_trans *trans, bool state, bool from_irq) IWL_WARN(trans, "reporting RF_KILL (radio %s)\n", state ? "disabled" : "enabled"); if (iwl_op_mode_hw_rf_kill(trans->op_mode, state) && - !WARN_ON(trans->trans_cfg->gen2)) + !WARN_ON(trans->mac_cfg->gen2)) _iwl_trans_pcie_stop_device(trans, from_irq); } @@ -1505,7 +1507,7 @@ static void iwl_pcie_d3_complete_suspend(struct iwl_trans *trans, iwl_pcie_synchronize_irqs(trans); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { iwl_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ); iwl_clear_bit(trans, CSR_GP_CNTRL, @@ -1534,11 +1536,11 @@ static int iwl_pcie_d3_handshake(struct iwl_trans *trans, bool suspend) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int ret; - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_AX210) iwl_write_umac_prph(trans, UREG_DOORBELL_TO_ISR6, suspend ? UREG_DOORBELL_TO_ISR6_SUSPEND : UREG_DOORBELL_TO_ISR6_RESUME); - else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) iwl_write32(trans, CSR_IPC_SLEEP_CONTROL, suspend ? CSR_IPC_SLEEP_CONTROL_SUSPEND : CSR_IPC_SLEEP_CONTROL_RESUME); @@ -1593,7 +1595,7 @@ int iwl_trans_pcie_d3_resume(struct iwl_trans *trans, goto out; } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) iwl_set_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ); else @@ -1653,17 +1655,18 @@ out: static void iwl_pcie_set_interrupt_capa(struct pci_dev *pdev, struct iwl_trans *trans, - const struct iwl_cfg_trans_params *cfg_trans) + const struct iwl_mac_cfg *mac_cfg, + struct iwl_trans_info *info) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int max_irqs, num_irqs, i, ret; u16 pci_cmd; u32 max_rx_queues = IWL_MAX_RX_HW_QUEUES; - if (!cfg_trans->mq_rx_supported) + if (!mac_cfg->mq_rx_supported) goto enable_msi; - if (cfg_trans->device_family <= IWL_DEVICE_FAMILY_9000) + if (mac_cfg->device_family <= IWL_DEVICE_FAMILY_9000) max_rx_queues = IWL_9000_MAX_RX_HW_QUEUES; max_irqs = min_t(u32, num_online_cpus() + 2, max_rx_queues); @@ -1693,27 +1696,28 @@ iwl_pcie_set_interrupt_capa(struct pci_dev *pdev, * More than two interrupts: we will use fewer RSS queues. */ if (num_irqs <= max_irqs - 2) { - trans_pcie->trans->num_rx_queues = num_irqs + 1; + info->num_rxqs = num_irqs + 1; trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX | IWL_SHARED_IRQ_FIRST_RSS; } else if (num_irqs == max_irqs - 1) { - trans_pcie->trans->num_rx_queues = num_irqs; + info->num_rxqs = num_irqs; trans_pcie->shared_vec_mask = IWL_SHARED_IRQ_NON_RX; } else { - trans_pcie->trans->num_rx_queues = num_irqs - 1; + info->num_rxqs = num_irqs - 1; } IWL_DEBUG_INFO(trans, "MSI-X enabled with rx queues %d, vec mask 0x%x\n", - trans_pcie->trans->num_rx_queues, trans_pcie->shared_vec_mask); + info->num_rxqs, trans_pcie->shared_vec_mask); - WARN_ON(trans_pcie->trans->num_rx_queues > IWL_MAX_RX_HW_QUEUES); + WARN_ON(info->num_rxqs > IWL_MAX_RX_HW_QUEUES); trans_pcie->alloc_vecs = num_irqs; trans_pcie->msix_enabled = true; return; enable_msi: + info->num_rxqs = 1; ret = pci_enable_msi(pdev); if (ret) { dev_err(&pdev->dev, "pci_enable_msi failed - %d\n", ret); @@ -1726,14 +1730,15 @@ enable_msi: } } -static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans) +static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans, + struct iwl_trans_info *info) { #if defined(CONFIG_SMP) int iter_rx_q, i, ret, cpu, offset; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); i = trans_pcie->shared_vec_mask & IWL_SHARED_IRQ_FIRST_RSS ? 0 : 1; - iter_rx_q = trans_pcie->trans->num_rx_queues - 1 + i; + iter_rx_q = info->num_rxqs - 1 + i; offset = 1 + i; for (; i < iter_rx_q ; i++) { /* @@ -1753,7 +1758,8 @@ static void iwl_pcie_irq_set_affinity(struct iwl_trans *trans) } static int iwl_pcie_init_msix_handler(struct pci_dev *pdev, - struct iwl_trans_pcie *trans_pcie) + struct iwl_trans_pcie *trans_pcie, + struct iwl_trans_info *info) { int i; @@ -1782,7 +1788,7 @@ static int iwl_pcie_init_msix_handler(struct pci_dev *pdev, return ret; } } - iwl_pcie_irq_set_affinity(trans_pcie->trans); + iwl_pcie_irq_set_affinity(trans_pcie->trans, info); return 0; } @@ -1791,7 +1797,7 @@ static int iwl_trans_pcie_clear_persistence_bit(struct iwl_trans *trans) { u32 hpm, wprot; - switch (trans->trans_cfg->device_family) { + switch (trans->mac_cfg->device_family) { case IWL_DEVICE_FAMILY_9000: wprot = PREG_PRPH_WPROT_9000; break; @@ -1860,8 +1866,8 @@ static int _iwl_trans_pcie_start_hw(struct iwl_trans *trans) if (err) return err; - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_22000 && - trans->trans_cfg->integrated) { + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_22000 && + trans->mac_cfg->integrated) { err = iwl_pcie_gen2_force_power_gating(trans); if (err) return err; @@ -1936,7 +1942,7 @@ u32 iwl_trans_pcie_read32(struct iwl_trans *trans, u32 ofs) static u32 iwl_trans_pcie_prph_msk(struct iwl_trans *trans) { - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) return 0x00FFFFFF; else return 0x000FFFFF; @@ -1960,45 +1966,17 @@ void iwl_trans_pcie_write_prph(struct iwl_trans *trans, u32 addr, u32 val) iwl_trans_pcie_write32(trans, HBUS_TARG_PRPH_WDAT, val); } -void iwl_trans_pcie_configure(struct iwl_trans *trans, - const struct iwl_trans_config *trans_cfg) +void iwl_trans_pcie_op_mode_enter(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); /* free all first - we might be reconfigured for a different size */ iwl_pcie_free_rbs_pool(trans); - trans_pcie->txqs.cmd.q_id = trans_cfg->cmd_queue; - trans_pcie->txqs.cmd.fifo = trans_cfg->cmd_fifo; - trans_pcie->txqs.page_offs = trans_cfg->cb_data_offs; - trans_pcie->txqs.dev_cmd_offs = trans_cfg->cb_data_offs + sizeof(void *); - trans_pcie->txqs.queue_alloc_cmd_ver = trans_cfg->queue_alloc_cmd_ver; - - if (WARN_ON(trans_cfg->n_no_reclaim_cmds > MAX_NO_RECLAIM_CMDS)) - trans_pcie->n_no_reclaim_cmds = 0; - else - trans_pcie->n_no_reclaim_cmds = trans_cfg->n_no_reclaim_cmds; - if (trans_pcie->n_no_reclaim_cmds) - memcpy(trans_pcie->no_reclaim_cmds, trans_cfg->no_reclaim_cmds, - trans_pcie->n_no_reclaim_cmds * sizeof(u8)); - - trans_pcie->rx_buf_size = trans_cfg->rx_buf_size; trans_pcie->rx_page_order = - iwl_trans_get_rb_size_order(trans_pcie->rx_buf_size); + iwl_trans_get_rb_size_order(trans->conf.rx_buf_size); trans_pcie->rx_buf_bytes = - iwl_trans_get_rb_size(trans_pcie->rx_buf_size); - trans_pcie->supported_dma_mask = DMA_BIT_MASK(12); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) - trans_pcie->supported_dma_mask = DMA_BIT_MASK(11); - - trans_pcie->txqs.bc_table_dword = trans_cfg->bc_table_dword; - trans_pcie->scd_set_active = trans_cfg->scd_set_active; - - trans->command_groups = trans_cfg->command_groups; - trans->command_groups_size = trans_cfg->command_groups_size; - - - trans_pcie->fw_reset_handshake = trans_cfg->fw_reset_handshake; + iwl_trans_get_rb_size(trans->conf.rx_buf_size); } void iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions, @@ -2026,11 +2004,14 @@ void iwl_trans_pcie_free_pnvm_dram_regions(struct iwl_dram_regions *dram_regions static void iwl_pcie_free_invalid_tx_cmd(struct iwl_trans *trans) { - iwl_pcie_free_dma_ptr(trans, &trans->invalid_tx_cmd); + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + + iwl_pcie_free_dma_ptr(trans, &trans_pcie->invalid_tx_cmd); } static int iwl_pcie_alloc_invalid_tx_cmd(struct iwl_trans *trans) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_cmd_header_wide bad_cmd = { .cmd = INVALID_WR_PTR_CMD, .group_id = DEBUG_GROUP, @@ -2040,11 +2021,11 @@ static int iwl_pcie_alloc_invalid_tx_cmd(struct iwl_trans *trans) }; int ret; - ret = iwl_pcie_alloc_dma_ptr(trans, &trans->invalid_tx_cmd, + ret = iwl_pcie_alloc_dma_ptr(trans, &trans_pcie->invalid_tx_cmd, sizeof(bad_cmd)); if (ret) return ret; - memcpy(trans->invalid_tx_cmd.addr, &bad_cmd, sizeof(bad_cmd)); + memcpy(trans_pcie->invalid_tx_cmd.addr, &bad_cmd, sizeof(bad_cmd)); return 0; } @@ -2055,7 +2036,7 @@ void iwl_trans_pcie_free(struct iwl_trans *trans) iwl_pcie_synchronize_irqs(trans); - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) iwl_txq_gen2_tx_free(trans); else iwl_pcie_tx_free(trans); @@ -2348,18 +2329,20 @@ out: void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_trans_pcie_removal *removal; char _msg = 0, *msg = &_msg; - if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY)) + if (WARN_ON(mode < IWL_RESET_MODE_REMOVE_ONLY || + mode == IWL_RESET_MODE_BACKOFF)) return; if (test_bit(STATUS_TRANS_DEAD, &trans->status)) return; - if (trans->me_present && mode == IWL_RESET_MODE_PROD_RESET) { + if (trans_pcie->me_present && mode == IWL_RESET_MODE_PROD_RESET) { mode = IWL_RESET_MODE_FUNC_RESET; - if (trans->me_present < 0) + if (trans_pcie->me_present < 0) msg = " instead of product reset as ME may be present"; else msg = " instead of product reset as ME is present"; @@ -2394,7 +2377,7 @@ void iwl_trans_pcie_reset(struct iwl_trans *trans, enum iwl_reset_mode mode) removal->pdev = to_pci_dev(trans->dev); removal->mode = mode; - removal->integrated = trans->trans_cfg->integrated; + removal->integrated = trans->mac_cfg->integrated; INIT_WORK(&removal->work, iwl_trans_pcie_removal_wk); pci_dev_get(removal->pdev); schedule_work(&removal->work); @@ -2405,7 +2388,7 @@ EXPORT_SYMBOL(iwl_trans_pcie_reset); * This version doesn't disable BHs but rather assumes they're * already disabled. */ -bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) +bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans, bool silent) { int ret; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -2422,7 +2405,7 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) if (trans_pcie->cmd_hold_nic_awake) goto out; - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) { write = CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ; mask = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS; poll = CSR_GP_CNTRL_REG_FLAG_MAC_STATUS; @@ -2430,7 +2413,7 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) /* this bit wakes up the NIC */ __iwl_trans_pcie_set_bit(trans, CSR_GP_CNTRL, write); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_8000) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000) udelay(2); /* @@ -2457,6 +2440,11 @@ bool __iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) if (unlikely(ret < 0)) { u32 cntrl = iwl_read32(trans, CSR_GP_CNTRL); + if (silent) { + spin_unlock(&trans_pcie->reg_lock); + return false; + } + WARN_ONCE(1, "Timeout waiting for hardware access (CSR_GP_CNTRL 0x%08x)\n", cntrl); @@ -2488,7 +2476,7 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) bool ret; local_bh_disable(); - ret = __iwl_trans_pcie_grab_nic_access(trans); + ret = __iwl_trans_pcie_grab_nic_access(trans, false); if (ret) { /* keep BHs disabled until iwl_trans_pcie_release_nic_access */ return ret; @@ -2497,7 +2485,8 @@ bool iwl_trans_pcie_grab_nic_access(struct iwl_trans *trans) return false; } -void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) +void __releases(nic_access_nobh) +iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); @@ -2511,7 +2500,7 @@ void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) if (trans_pcie->cmd_hold_nic_awake) goto out; - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) __iwl_trans_pcie_clear_bit(trans, CSR_GP_CNTRL, CSR_GP_CNTRL_REG_FLAG_BZ_MAC_ACCESS_REQ); else @@ -2524,6 +2513,7 @@ void iwl_trans_pcie_release_nic_access(struct iwl_trans *trans) * scheduled on different CPUs (after we drop reg_lock). */ out: + __release(nic_access_nobh); spin_unlock_bh(&trans_pcie->reg_lock); } @@ -2609,7 +2599,7 @@ int iwl_trans_pcie_rxq_dma_data(struct iwl_trans *trans, int queue, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (queue >= trans->num_rx_queues || !trans_pcie->rxq) + if (queue >= trans->info.num_rxqs || !trans_pcie->rxq) return -EINVAL; data->fr_bd_cb = trans_pcie->rxq[queue].bd_dma; @@ -2690,10 +2680,10 @@ int iwl_trans_pcie_wait_txqs_empty(struct iwl_trans *trans, u32 txq_bm) /* waiting for all the tx frames complete might take a while */ for (cnt = 0; - cnt < trans->trans_cfg->base_params->num_of_queues; + cnt < trans->mac_cfg->base->num_of_queues; cnt++) { - if (cnt == trans_pcie->txqs.cmd.q_id) + if (cnt == trans->conf.cmd_queue) continue; if (!test_bit(cnt, trans_pcie->txqs.queue_used)) continue; @@ -2834,7 +2824,7 @@ static void *iwl_dbgfs_tx_queue_seq_start(struct seq_file *seq, loff_t *pos) struct iwl_dbgfs_tx_queue_priv *priv = seq->private; struct iwl_dbgfs_tx_queue_state *state; - if (*pos >= priv->trans->trans_cfg->base_params->num_of_queues) + if (*pos >= priv->trans->mac_cfg->base->num_of_queues) return NULL; state = kmalloc(sizeof(*state), GFP_KERNEL); @@ -2852,7 +2842,7 @@ static void *iwl_dbgfs_tx_queue_seq_next(struct seq_file *seq, *pos = ++state->pos; - if (*pos >= priv->trans->trans_cfg->base_params->num_of_queues) + if (*pos >= priv->trans->mac_cfg->base->num_of_queues) return NULL; return state; @@ -2884,7 +2874,7 @@ static int iwl_dbgfs_tx_queue_seq_show(struct seq_file *seq, void *v) else seq_puts(seq, "(unallocated)"); - if (state->pos == trans_pcie->txqs.cmd.q_id) + if (state->pos == trans->conf.cmd_queue) seq_puts(seq, " (HCMD)"); seq_puts(seq, "\n"); @@ -2922,7 +2912,7 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, int pos = 0, i, ret; size_t bufsz; - bufsz = sizeof(char) * 121 * trans->num_rx_queues; + bufsz = sizeof(char) * 121 * trans->info.num_rxqs; if (!trans_pcie->rxq) return -EAGAIN; @@ -2931,9 +2921,11 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, if (!buf) return -ENOMEM; - for (i = 0; i < trans->num_rx_queues && pos < bufsz; i++) { + for (i = 0; i < trans->info.num_rxqs && pos < bufsz; i++) { struct iwl_rxq *rxq = &trans_pcie->rxq[i]; + spin_lock_bh(&rxq->lock); + pos += scnprintf(buf + pos, bufsz - pos, "queue#: %2d\n", i); pos += scnprintf(buf + pos, bufsz - pos, "\tread: %u\n", @@ -2954,6 +2946,7 @@ static ssize_t iwl_dbgfs_rx_queue_read(struct file *file, pos += scnprintf(buf + pos, bufsz - pos, "\tclosed_rb_num: Not Allocated\n"); } + spin_unlock_bh(&rxq->lock); } ret = simple_read_from_buffer(user_buf, count, ppos, buf, pos); kfree(buf); @@ -3258,8 +3251,9 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file, { struct iwl_trans *trans = file->private_data; static const char * const modes[] = { - [IWL_RESET_MODE_SW_RESET] = "n/a", - [IWL_RESET_MODE_REPROBE] = "n/a", + [IWL_RESET_MODE_SW_RESET] = "sw", + [IWL_RESET_MODE_REPROBE] = "reprobe", + [IWL_RESET_MODE_TOP_RESET] = "top", [IWL_RESET_MODE_REMOVE_ONLY] = "remove", [IWL_RESET_MODE_RESCAN] = "rescan", [IWL_RESET_MODE_FUNC_RESET] = "function", @@ -3278,8 +3272,18 @@ static ssize_t iwl_dbgfs_reset_write(struct file *file, if (mode < 0) return mode; - if (mode < IWL_RESET_MODE_REMOVE_ONLY) - return -EINVAL; + if (mode < IWL_RESET_MODE_REMOVE_ONLY) { + if (!test_bit(STATUS_DEVICE_ENABLED, &trans->status)) + return -EINVAL; + if (mode == IWL_RESET_MODE_TOP_RESET) { + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_SC) + return -EINVAL; + trans->request_top_reset = 1; + } + iwl_op_mode_nic_error(trans->op_mode, IWL_ERR_TYPE_DEBUGFS); + iwl_trans_schedule_reset(trans, IWL_ERR_TYPE_DEBUGFS); + return count; + } iwl_trans_pcie_reset(trans, mode); @@ -3420,7 +3424,7 @@ static u32 iwl_trans_pcie_fh_regs_dump(struct iwl_trans *trans, (*data)->len = cpu_to_le32(fh_regs_len); val = (void *)(*data)->data; - if (!trans->trans_cfg->gen2) + if (!trans->mac_cfg->gen2) for (i = FH_MEM_LOWER_BOUND; i < FH_MEM_UPPER_BOUND; i += sizeof(u32)) *val++ = cpu_to_le32(iwl_trans_pcie_read32(trans, i)); @@ -3467,7 +3471,7 @@ iwl_trans_pcie_dump_pointers(struct iwl_trans *trans, { u32 base, base_high, write_ptr, write_ptr_val, wrap_cnt; - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { base = DBGC_CUR_DBGBUF_BASE_ADDR_LSB; base_high = DBGC_CUR_DBGBUF_BASE_ADDR_MSB; write_ptr = DBGC_CUR_DBGBUF_STATUS; @@ -3487,7 +3491,7 @@ iwl_trans_pcie_dump_pointers(struct iwl_trans *trans, cpu_to_le32(iwl_read_prph(trans, wrap_cnt)); fw_mon_data->fw_mon_base_ptr = cpu_to_le32(iwl_read_prph(trans, base)); - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { fw_mon_data->fw_mon_base_high_ptr = cpu_to_le32(iwl_read_prph(trans, base_high)); write_ptr_val &= DBGC_CUR_DBGBUF_STATUS_OFFSET_MSK; @@ -3507,8 +3511,8 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, if (trans->dbg.dest_tlv || (fw_mon->size && - (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_7000 || - trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210))) { + (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_7000 || + trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210))) { struct iwl_fw_error_dump_fw_mon *fw_mon_data; (*data)->type = cpu_to_le32(IWL_FW_ERROR_DUMP_FW_MONITOR); @@ -3531,14 +3535,14 @@ iwl_trans_pcie_dump_monitor(struct iwl_trans *trans, IWL_LDBG_M2S_BUF_BA_MSK) << trans->dbg.dest_tlv->base_shift; base *= IWL_M2S_UNIT_SIZE; - base += trans->cfg->smem_offset; + base += trans->mac_cfg->base->smem_offset; } else { base = iwl_read_prph(trans, base) << trans->dbg.dest_tlv->base_shift; } - iwl_trans_read_mem(trans, base, fw_mon_data->data, - monitor_len / sizeof(u32)); + iwl_trans_pcie_read_mem(trans, base, fw_mon_data->data, + monitor_len / sizeof(u32)); } else if (trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) { monitor_len = iwl_trans_pci_dump_marbh_monitor(trans, @@ -3572,7 +3576,7 @@ static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, u32 *len) base = (cfg_reg & IWL_LDBG_M2S_BUF_BA_MSK) << trans->dbg.dest_tlv->base_shift; base *= IWL_M2S_UNIT_SIZE; - base += trans->cfg->smem_offset; + base += trans->mac_cfg->base->smem_offset; monitor_len = (cfg_reg & IWL_LDBG_M2S_BUF_SIZE_MSK) >> @@ -3588,7 +3592,7 @@ static int iwl_trans_get_fw_monitor_len(struct iwl_trans *trans, u32 *len) trans->dbg.dest_tlv->end_shift; /* Make "end" point to the actual end */ - if (trans->trans_cfg->device_family >= + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000 || trans->dbg.dest_tlv->monitor_mode == MARBH_MODE) end += (1 << trans->dbg.dest_tlv->end_shift); @@ -3609,13 +3613,13 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_fw_error_dump_data *data; - struct iwl_txq *cmdq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]; + struct iwl_txq *cmdq = trans_pcie->txqs.txq[trans->conf.cmd_queue]; struct iwl_fw_error_dump_txcmd *txcmd; struct iwl_trans_dump_data *dump_data; u32 len, num_rbs = 0, monitor_len = 0; int i, ptr; bool dump_rbs = test_bit(STATUS_FW_ERROR, &trans->status) && - !trans->trans_cfg->mq_rx_supported && + !trans->mac_cfg->mq_rx_supported && dump_mask & BIT(IWL_FW_ERROR_DUMP_RB); if (!dump_mask) @@ -3640,7 +3644,7 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, /* FH registers */ if (dump_mask & BIT(IWL_FW_ERROR_DUMP_FH_REGS)) { - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) len += sizeof(*data) + (iwl_umac_prph(trans, FH_MEM_UPPER_BOUND_GEN2) - iwl_umac_prph(trans, FH_MEM_LOWER_BOUND_GEN2)); @@ -3654,15 +3658,18 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, /* Dump RBs is supported only for pre-9000 devices (1 queue) */ struct iwl_rxq *rxq = &trans_pcie->rxq[0]; /* RBs */ + spin_lock_bh(&rxq->lock); num_rbs = iwl_get_closed_rb_stts(trans, rxq); num_rbs = (num_rbs - rxq->read) & RX_QUEUE_MASK; + spin_unlock_bh(&rxq->lock); + len += num_rbs * (sizeof(*data) + sizeof(struct iwl_fw_error_dump_rb) + (PAGE_SIZE << trans_pcie->rx_page_order)); } /* Paged memory for gen2 HW */ - if (trans->trans_cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) + if (trans->mac_cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) for (i = 0; i < trans->init_dram.paging_cnt; i++) len += sizeof(*data) + sizeof(struct iwl_fw_error_dump_paging) + @@ -3687,7 +3694,7 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, u8 tfdidx; u32 caplen, cmdlen; - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) tfdidx = idx; else tfdidx = ptr; @@ -3727,7 +3734,7 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u32 dump_mask, len += iwl_trans_pcie_dump_rbs(trans, &data, num_rbs); /* Paged memory for gen2 HW */ - if (trans->trans_cfg->gen2 && + if (trans->mac_cfg->gen2 && dump_mask & BIT(IWL_FW_ERROR_DUMP_PAGING)) { for (i = 0; i < trans->init_dram.paging_cnt; i++) { struct iwl_fw_error_dump_paging *paging; @@ -3767,7 +3774,7 @@ void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans) if (trans_pcie->msix_enabled) { inta_addr = CSR_MSIX_HW_INT_CAUSES_AD; - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) sw_err_bit = MSIX_HW_INT_CAUSES_REG_SW_ERR_BZ; else sw_err_bit = MSIX_HW_INT_CAUSES_REG_SW_ERR; @@ -3779,12 +3786,14 @@ void iwl_trans_pcie_sync_nmi(struct iwl_trans *trans) iwl_trans_sync_nmi_with_addr(trans, inta_addr, sw_err_bit); } -struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, - const struct pci_device_id *ent, - const struct iwl_cfg_trans_params *cfg_trans) +struct iwl_trans * +iwl_trans_pcie_alloc(struct pci_dev *pdev, + const struct iwl_mac_cfg *mac_cfg, + struct iwl_trans_info *info) { struct iwl_trans_pcie *trans_pcie, **priv; struct iwl_trans *trans; + unsigned int bc_tbl_n_entries; int ret, addr_size; u32 bar0; @@ -3801,13 +3810,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, return ERR_PTR(ret); trans = iwl_trans_alloc(sizeof(struct iwl_trans_pcie), &pdev->dev, - cfg_trans); + mac_cfg); if (!trans) return ERR_PTR(-ENOMEM); trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (trans->trans_cfg->gen2) { + /* Initialize the wait queue for commands */ + init_waitqueue_head(&trans_pcie->wait_command_queue); + + if (trans->mac_cfg->gen2) { trans_pcie->txqs.tfd.addr_size = 64; trans_pcie->txqs.tfd.max_tbs = IWL_TFH_NUM_TBS; trans_pcie->txqs.tfd.size = sizeof(struct iwl_tfh_tfd); @@ -3816,10 +3828,12 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans_pcie->txqs.tfd.max_tbs = IWL_NUM_OF_TBS; trans_pcie->txqs.tfd.size = sizeof(struct iwl_tfd); } - trans->max_skb_frags = IWL_TRANS_PCIE_MAX_FRAGS(trans_pcie); - /* Set a short watchdog for the command queue */ - trans_pcie->txqs.cmd.wdg_timeout = IWL_DEF_WD_TIMEOUT; + trans_pcie->supported_dma_mask = (u32)DMA_BIT_MASK(12); + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + trans_pcie->supported_dma_mask = (u32)DMA_BIT_MASK(11); + + info->max_skb_frags = IWL_TRANS_PCIE_MAX_FRAGS(trans_pcie); trans_pcie->txqs.tso_hdr_page = alloc_percpu(struct iwl_tso_hdr_page); if (!trans_pcie->txqs.tso_hdr_page) { @@ -3827,20 +3841,21 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, goto out_free_trans; } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) - trans_pcie->txqs.bc_tbl_size = - sizeof(struct iwl_gen3_bc_tbl_entry) * TFD_QUEUE_BC_SIZE_GEN3_BZ; - else if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) - trans_pcie->txqs.bc_tbl_size = - sizeof(struct iwl_gen3_bc_tbl_entry) * TFD_QUEUE_BC_SIZE_GEN3_AX210; + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_BZ) + bc_tbl_n_entries = TFD_QUEUE_BC_SIZE_BZ; + else if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) + bc_tbl_n_entries = TFD_QUEUE_BC_SIZE_AX210; else - trans_pcie->txqs.bc_tbl_size = sizeof(struct iwlagn_scd_bc_tbl); + bc_tbl_n_entries = TFD_QUEUE_BC_SIZE; + + trans_pcie->txqs.bc_tbl_size = + sizeof(struct iwl_bc_tbl_entry) * bc_tbl_n_entries; /* * For gen2 devices, we use a single allocation for each byte-count * table, but they're pretty small (1k) so use a DMA pool that we * allocate here. */ - if (trans->trans_cfg->gen2) { + if (trans->mac_cfg->gen2) { trans_pcie->txqs.bc_pool = dmam_pool_create("iwlwifi:bc", trans->dev, trans_pcie->txqs.bc_tbl_size, @@ -3853,7 +3868,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, /* Some things must not change even if the config does */ WARN_ON(trans_pcie->txqs.tfd.addr_size != - (trans->trans_cfg->gen2 ? 64 : 36)); + (trans->mac_cfg->gen2 ? 64 : 36)); /* Initialize NAPI here - it should be before registering to mac80211 * in the opmode but after the HW struct is allocated. @@ -3887,7 +3902,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans_pcie->debug_rfkill = -1; - if (!cfg_trans->base_params->pcie_l1_allowed) { + if (!mac_cfg->base->pcie_l1_allowed) { /* * W/A - seems to solve weird behavior. We need to remove this * if we don't want to stay in L1 all the time. This wastes a @@ -3931,8 +3946,8 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, trans_pcie->pci_dev = pdev; iwl_disable_interrupts(trans); - trans->hw_rev = iwl_read32(trans, CSR_HW_REV); - if (trans->hw_rev == 0xffffffff) { + info->hw_rev = iwl_read32(trans, CSR_HW_REV); + if (info->hw_rev == 0xffffffff) { dev_err(&pdev->dev, "HW_REV=0xFFFFFFFF, PCI issues?\n"); ret = -EIO; goto out_no_pci; @@ -3944,17 +3959,14 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, * "dash" value). To keep hw_rev backwards compatible - we'll store it * in the old format. */ - if (cfg_trans->device_family >= IWL_DEVICE_FAMILY_8000) - trans->hw_rev_step = trans->hw_rev & 0xF; + if (mac_cfg->device_family >= IWL_DEVICE_FAMILY_8000) + info->hw_rev_step = info->hw_rev & 0xF; else - trans->hw_rev_step = (trans->hw_rev & 0xC) >> 2; + info->hw_rev_step = (info->hw_rev & 0xC) >> 2; - IWL_DEBUG_INFO(trans, "HW REV: 0x%0x\n", trans->hw_rev); + IWL_DEBUG_INFO(trans, "HW REV: 0x%0x\n", info->hw_rev); - iwl_pcie_set_interrupt_capa(pdev, trans, cfg_trans); - trans->hw_id = (pdev->device << 16) + pdev->subsystem_device; - snprintf(trans->hw_id_str, sizeof(trans->hw_id_str), - "PCI ID: 0x%04X:0x%04X", pdev->device, pdev->subsystem_device); + iwl_pcie_set_interrupt_capa(pdev, trans, mac_cfg, info); init_waitqueue_head(&trans_pcie->sx_waitq); @@ -3963,7 +3975,7 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev, goto out_no_pci; if (trans_pcie->msix_enabled) { - ret = iwl_pcie_init_msix_handler(pdev, trans_pcie); + ret = iwl_pcie_init_msix_handler(pdev, trans_pcie, info); if (ret) goto out_no_pci; } else { diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c index 1f483f15c238..df0545f09da9 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx-gen2.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* * Copyright (C) 2017 Intel Deutschland GmbH - * Copyright (C) 2018-2020, 2023-2024 Intel Corporation + * Copyright (C) 2018-2020, 2023-2025 Intel Corporation */ #include <net/tso.h> #include <linux/tcp.h> @@ -18,13 +18,12 @@ static struct page *get_workaround_page(struct iwl_trans *trans, struct sk_buff *skb) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct iwl_tso_page_info *info; struct page **page_ptr; struct page *ret; dma_addr_t phys; - page_ptr = (void *)((u8 *)skb->cb + trans_pcie->txqs.page_offs); + page_ptr = (void *)((u8 *)skb->cb + trans->conf.cb_data_offs); ret = alloc_page(GFP_ATOMIC); if (!ret) @@ -164,7 +163,7 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans, struct iwl_device_tx_cmd *dev_cmd) { #ifdef CONFIG_INET - struct iwl_tx_cmd_gen2 *tx_cmd = (void *)dev_cmd->payload; + struct iwl_tx_cmd_v9 *tx_cmd = (void *)dev_cmd->payload; struct ieee80211_hdr *hdr = (void *)skb->data; unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room; unsigned int mss = skb_shinfo(skb)->gso_size; @@ -188,7 +187,8 @@ static int iwl_txq_gen2_build_amsdu(struct iwl_trans *trans, (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)); /* Our device supports 9 segments at most, it will fit in 1 page */ - sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room); + sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room, + snap_ip_tcp_hdrlen + hdr_len); if (!sgt) return -ENOMEM; @@ -347,6 +347,7 @@ iwl_tfh_tfd *iwl_txq_gen2_build_tx_amsdu(struct iwl_trans *trans, return tfd; out_err: + iwl_pcie_free_tso_pages(trans, skb, out_meta); iwl_txq_gen2_tfd_unmap(trans, out_meta, tfd); return NULL; } @@ -489,21 +490,21 @@ struct iwl_tfh_tfd *iwl_txq_gen2_build_tfd(struct iwl_trans *trans, bool amsdu; /* There must be data left over for TB1 or this code must be changed */ - BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen2) < IWL_FIRST_TB_SIZE); + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_v9) < IWL_FIRST_TB_SIZE); BUILD_BUG_ON(sizeof(struct iwl_cmd_header) + - offsetofend(struct iwl_tx_cmd_gen2, dram_info) > + offsetofend(struct iwl_tx_cmd_v9, dram_info) > IWL_FIRST_TB_SIZE); - BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_gen3) < IWL_FIRST_TB_SIZE); + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_FIRST_TB_SIZE); BUILD_BUG_ON(sizeof(struct iwl_cmd_header) + - offsetofend(struct iwl_tx_cmd_gen3, dram_info) > + offsetofend(struct iwl_tx_cmd, dram_info) > IWL_FIRST_TB_SIZE); memset(tfd, 0, sizeof(*tfd)); - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_AX210) - len = sizeof(struct iwl_tx_cmd_gen2); + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) + len = sizeof(struct iwl_tx_cmd_v9); else - len = sizeof(struct iwl_tx_cmd_gen3); + len = sizeof(struct iwl_tx_cmd); amsdu = ieee80211_is_data_qos(hdr->frame_control) && (*ieee80211_get_qos_ctl(hdr) & @@ -534,17 +535,17 @@ int iwl_txq_space(struct iwl_trans *trans, const struct iwl_txq *q) * If q->n_window is smaller than max_tfd_queue_size, there is no need * to reserve any queue entries for this purpose. */ - if (q->n_window < trans->trans_cfg->base_params->max_tfd_queue_size) + if (q->n_window < trans->mac_cfg->base->max_tfd_queue_size) max = q->n_window; else - max = trans->trans_cfg->base_params->max_tfd_queue_size - 1; + max = trans->mac_cfg->base->max_tfd_queue_size - 1; /* * max_tfd_queue_size is a power of 2, so the following is equivalent to * modulo by max_tfd_queue_size and is well defined. */ used = (q->write_ptr - q->read_ptr) & - (trans->trans_cfg->base_params->max_tfd_queue_size - 1); + (trans->mac_cfg->base->max_tfd_queue_size - 1); if (WARN_ON(used > max)) return 0; @@ -559,8 +560,8 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans, struct iwl_txq *txq, u16 byte_cnt, int num_tbs) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int idx = iwl_txq_get_cmd_index(txq, txq->write_ptr); + struct iwl_bc_tbl_entry *scd_bc_tbl = txq->bc_tbl.addr; u8 filled_tfd_size, num_fetch_chunks; u16 len = byte_cnt; __le16 bc_ent; @@ -580,24 +581,16 @@ static void iwl_pcie_gen2_update_byte_tbl(struct iwl_trans *trans, */ num_fetch_chunks = DIV_ROUND_UP(filled_tfd_size, 64) - 1; - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { - struct iwl_gen3_bc_tbl_entry *scd_bc_tbl_gen3 = txq->bc_tbl.addr; - - /* Starting from AX210, the HW expects bytes */ - WARN_ON(trans_pcie->txqs.bc_table_dword); + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { WARN_ON(len > 0x3FFF); bc_ent = cpu_to_le16(len | (num_fetch_chunks << 14)); - scd_bc_tbl_gen3[idx].tfd_offset = bc_ent; } else { - struct iwlagn_scd_bc_tbl *scd_bc_tbl = txq->bc_tbl.addr; - - /* Before AX210, the HW expects DW */ - WARN_ON(!trans_pcie->txqs.bc_table_dword); len = DIV_ROUND_UP(len, 4); WARN_ON(len > 0xFFF); bc_ent = cpu_to_le16(len | (num_fetch_chunks << 12)); - scd_bc_tbl->tfd_offset[idx] = bc_ent; } + + scd_bc_tbl[idx].tfd_offset = bc_ent; } static u8 iwl_txq_gen2_get_num_tbs(struct iwl_tfh_tfd *tfd) @@ -754,7 +747,8 @@ int iwl_txq_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_tx_cmd **dev_cmd_ptr; dev_cmd_ptr = (void *)((u8 *)skb->cb + - trans_pcie->txqs.dev_cmd_offs); + trans->conf.cb_data_offs + + sizeof(void *)); *dev_cmd_ptr = dev_cmd; __skb_queue_tail(&txq->overflow_q, skb); @@ -783,16 +777,16 @@ int iwl_txq_gen2_tx(struct iwl_trans *trans, struct sk_buff *skb, return -1; } - if (trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { - struct iwl_tx_cmd_gen3 *tx_cmd_gen3 = + if (trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210) { + struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; - cmd_len = le16_to_cpu(tx_cmd_gen3->len); + cmd_len = le16_to_cpu(tx_cmd->len); } else { - struct iwl_tx_cmd_gen2 *tx_cmd_gen2 = + struct iwl_tx_cmd_v9 *tx_cmd_v9 = (void *)dev_cmd->payload; - cmd_len = le16_to_cpu(tx_cmd_gen2->len); + cmd_len = le16_to_cpu(tx_cmd_v9->len); } /* Set up entry for this TFD in Tx byte-count array */ @@ -830,7 +824,7 @@ static void iwl_txq_gen2_unmap(struct iwl_trans *trans, int txq_id) IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", txq_id, txq->read_ptr); - if (txq_id != trans_pcie->txqs.cmd.q_id) { + if (txq_id != trans->conf.cmd_queue) { int idx = iwl_txq_get_cmd_index(txq, txq->read_ptr); struct iwl_cmd_meta *cmd_meta = &txq->entries[idx].meta; struct sk_buff *skb = txq->entries[idx].skb; @@ -904,12 +898,12 @@ static void iwl_txq_gen2_free(struct iwl_trans *trans, int txq_id) iwl_txq_gen2_unmap(trans, txq_id); /* De-alloc array of command/tx buffers */ - if (txq_id == trans_pcie->txqs.cmd.q_id) + if (txq_id == trans->conf.cmd_queue) for (i = 0; i < txq->n_window; i++) { kfree_sensitive(txq->entries[i].cmd); kfree_sensitive(txq->entries[i].free_buf); } - del_timer_sync(&txq->stuck_timer); + timer_delete_sync(&txq->stuck_timer); iwl_txq_gen2_free_memory(trans, txq); @@ -1005,7 +999,7 @@ static int iwl_pcie_txq_alloc_response(struct iwl_trans *trans, txq->id = qid; trans_pcie->txqs.txq[qid] = txq; - wr_ptr &= (trans->trans_cfg->base_params->max_tfd_queue_size - 1); + wr_ptr &= (trans->mac_cfg->base->max_tfd_queue_size - 1); /* Place first TFD at index corresponding to start sequence number */ txq->read_ptr = wr_ptr; @@ -1041,8 +1035,8 @@ int iwl_txq_dyn_alloc(struct iwl_trans *trans, u32 flags, u32 sta_mask, /* but must be power of 2 values for calculating read/write pointers */ size = rounddown_pow_of_two(size); - if (trans->trans_cfg->device_family == IWL_DEVICE_FAMILY_BZ && - trans->hw_rev_step == SILICON_A_STEP) { + if (trans->mac_cfg->device_family == IWL_DEVICE_FAMILY_BZ && + trans->info.hw_rev_step == SILICON_A_STEP) { size = 4096; txq = iwl_txq_dyn_alloc_dma(trans, size, timeout); } else { @@ -1062,7 +1056,7 @@ int iwl_txq_dyn_alloc(struct iwl_trans *trans, u32 flags, u32 sta_mask, if (IS_ERR(txq)) return PTR_ERR(txq); - if (trans_pcie->txqs.queue_alloc_cmd_ver == 0) { + if (trans->conf.queue_alloc_cmd_ver == 0) { memset(&cmd.old, 0, sizeof(cmd.old)); cmd.old.tfdq_addr = cpu_to_le64(txq->dma_addr); cmd.old.byte_cnt_addr = cpu_to_le64(txq->bc_tbl.dma); @@ -1079,7 +1073,7 @@ int iwl_txq_dyn_alloc(struct iwl_trans *trans, u32 flags, u32 sta_mask, hcmd.id = SCD_QUEUE_CFG; hcmd.len[0] = sizeof(cmd.old); hcmd.data[0] = &cmd.old; - } else if (trans_pcie->txqs.queue_alloc_cmd_ver == 3) { + } else if (trans->conf.queue_alloc_cmd_ver == 3) { memset(&cmd.new, 0, sizeof(cmd.new)); cmd.new.operation = cpu_to_le32(IWL_SCD_QUEUE_ADD); cmd.new.u.add.tfdq_dram_addr = cpu_to_le64(txq->dma_addr); @@ -1174,7 +1168,7 @@ int iwl_txq_gen2_init(struct iwl_trans *trans, int txq_id, int queue_size) } ret = iwl_txq_init(trans, queue, queue_size, - (txq_id == trans_pcie->txqs.cmd.q_id)); + (txq_id == trans->conf.cmd_queue)); if (ret) { IWL_ERR(trans, "Tx %d queue alloc failed\n", txq_id); goto error; @@ -1204,7 +1198,7 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]; + struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue]; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; void *dup_buf = NULL; @@ -1321,7 +1315,7 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, cpu_to_le16(cmd_size - sizeof(struct iwl_cmd_header_wide)); out_cmd->hdr_wide.reserved = 0; out_cmd->hdr_wide.sequence = - cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->txqs.cmd.q_id) | + cpu_to_le16(QUEUE_TO_SEQ(trans->conf.cmd_queue) | INDEX_TO_SEQ(txq->write_ptr)); cmd_pos = sizeof(struct iwl_cmd_header_wide); @@ -1369,7 +1363,7 @@ int iwl_pcie_gen2_enqueue_hcmd(struct iwl_trans *trans, "Sending command %s (%.2x.%.2x), seq: 0x%04X, %d bytes at %d[%d]:%d\n", iwl_get_cmd_string(trans, cmd->id), group_id, out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), - cmd_size, txq->write_ptr, idx, trans_pcie->txqs.cmd.q_id); + cmd_size, txq->write_ptr, idx, trans->conf.cmd_queue); /* start the TFD with the minimum copy bytes */ tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE); diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 334ebd4c12fa..7abd7c7daa89 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause /* - * Copyright (C) 2003-2014, 2018-2021, 2023-2024 Intel Corporation + * Copyright (C) 2003-2014, 2018-2021, 2023-2025 Intel Corporation * Copyright (C) 2013-2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH */ @@ -78,7 +78,6 @@ void iwl_pcie_free_dma_ptr(struct iwl_trans *trans, struct iwl_dma_ptr *ptr) static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, struct iwl_txq *txq) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); u32 reg = 0; int txq_id = txq->id; @@ -90,8 +89,8 @@ static void iwl_pcie_txq_inc_wr_ptr(struct iwl_trans *trans, * 2. NIC is woken up for CMD regardless of shadow outside this function * 3. there is a chance that the NIC is asleep */ - if (!trans->trans_cfg->base_params->shadow_reg_enable && - txq_id != trans_pcie->txqs.cmd.q_id && + if (!trans->mac_cfg->base->shadow_reg_enable && + txq_id != trans->conf.cmd_queue && test_bit(STATUS_TPOWER_PMI, &trans->status)) { /* * wake up nic if it's powered down ... @@ -125,7 +124,7 @@ void iwl_pcie_txq_check_wrptrs(struct iwl_trans *trans) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int i; - for (i = 0; i < trans->trans_cfg->base_params->num_of_queues; i++) { + for (i = 0; i < trans->mac_cfg->base->num_of_queues; i++) { struct iwl_txq *txq = trans_pcie->txqs.txq[i]; if (!test_bit(i, trans_pcie->txqs.queue_used)) @@ -193,7 +192,7 @@ static void iwl_pcie_clear_cmd_in_flight(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - if (!trans->trans_cfg->base_params->apmg_wake_up_wa) + if (!trans->mac_cfg->base->apmg_wake_up_wa) return; spin_lock(&trans_pcie->reg_lock); @@ -226,11 +225,10 @@ static void iwl_pcie_free_and_unmap_tso_page(struct iwl_trans *trans, void iwl_pcie_free_tso_pages(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *cmd_meta) { - struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct page **page_ptr; struct page *next; - page_ptr = (void *)((u8 *)skb->cb + trans_pcie->txqs.page_offs); + page_ptr = (void *)((u8 *)skb->cb + trans->conf.cb_data_offs); next = *page_ptr; *page_ptr = NULL; @@ -280,10 +278,12 @@ iwl_txq_gen1_tfd_tb_get_addr(struct iwl_tfd *tfd, u8 idx) static void iwl_txq_set_tfd_invalid_gen1(struct iwl_trans *trans, struct iwl_tfd *tfd) { + struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); + tfd->num_tbs = 0; - iwl_pcie_gen1_tfd_set_tb(tfd, 0, trans->invalid_tx_cmd.dma, - trans->invalid_tx_cmd.size); + iwl_pcie_gen1_tfd_set_tb(tfd, 0, trans_pcie->invalid_tx_cmd.dma, + trans_pcie->invalid_tx_cmd.size); } static void iwl_txq_gen1_tfd_unmap(struct iwl_trans *trans, @@ -355,7 +355,7 @@ static void iwl_txq_free_tfd(struct iwl_trans *trans, struct iwl_txq *txq, /* We have only q->n_window txq->entries, but we use * TFD_QUEUE_SIZE_MAX tfds */ - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) iwl_txq_gen2_tfd_unmap(trans, &txq->entries[idx].meta, iwl_txq_get_tfd(trans, txq, read_ptr)); else @@ -394,7 +394,7 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) IWL_DEBUG_TX_REPLY(trans, "Q %d Free %d\n", txq_id, txq->read_ptr); - if (txq_id != trans_pcie->txqs.cmd.q_id) { + if (txq_id != trans->conf.cmd_queue) { struct sk_buff *skb = txq->entries[txq->read_ptr].skb; struct iwl_cmd_meta *cmd_meta = &txq->entries[txq->read_ptr].meta; @@ -408,7 +408,7 @@ static void iwl_pcie_txq_unmap(struct iwl_trans *trans, int txq_id) txq->read_ptr = iwl_txq_inc_wrap(trans, txq->read_ptr); if (txq->read_ptr == txq->write_ptr && - txq_id == trans_pcie->txqs.cmd.q_id) + txq_id == trans->conf.cmd_queue) iwl_pcie_clear_cmd_in_flight(trans); } @@ -446,7 +446,7 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) iwl_pcie_txq_unmap(trans, txq_id); /* De-alloc array of command/tx buffers */ - if (txq_id == trans_pcie->txqs.cmd.q_id) + if (txq_id == trans->conf.cmd_queue) for (i = 0; i < txq->n_window; i++) { kfree_sensitive(txq->entries[i].cmd); kfree_sensitive(txq->entries[i].free_buf); @@ -456,7 +456,7 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) if (txq->tfds) { dma_free_coherent(dev, trans_pcie->txqs.tfd.size * - trans->trans_cfg->base_params->max_tfd_queue_size, + trans->mac_cfg->base->max_tfd_queue_size, txq->tfds, txq->dma_addr); txq->dma_addr = 0; txq->tfds = NULL; @@ -469,16 +469,16 @@ static void iwl_pcie_txq_free(struct iwl_trans *trans, int txq_id) kfree(txq->entries); txq->entries = NULL; - del_timer_sync(&txq->stuck_timer); + timer_delete_sync(&txq->stuck_timer); /* 0-fill queue descriptor structure */ memset(txq, 0, sizeof(*txq)); } -void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) +void iwl_pcie_tx_start(struct iwl_trans *trans) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - int nq = trans->trans_cfg->base_params->num_of_queues; + int nq = trans->mac_cfg->base->num_of_queues; int chan; u32 reg_val; int clear_dwords = (SCD_TRANS_TBL_OFFSET_QUEUE(nq) - @@ -493,13 +493,10 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) trans_pcie->scd_base_addr = iwl_read_prph(trans, SCD_SRAM_BASE_ADDR); - WARN_ON(scd_base_addr != 0 && - scd_base_addr != trans_pcie->scd_base_addr); - /* reset context data, TX status and translation data */ - iwl_trans_write_mem(trans, trans_pcie->scd_base_addr + - SCD_CONTEXT_MEM_LOWER_BOUND, - NULL, clear_dwords); + iwl_trans_pcie_write_mem(trans, trans_pcie->scd_base_addr + + SCD_CONTEXT_MEM_LOWER_BOUND, + NULL, clear_dwords); iwl_write_prph(trans, SCD_DRAM_BASE_ADDR, trans_pcie->txqs.scd_bc_tbls.dma >> 10); @@ -507,12 +504,12 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) /* The chain extension of the SCD doesn't work well. This feature is * enabled by default by the HW, so we need to disable it manually. */ - if (trans->trans_cfg->base_params->scd_chain_ext_wa) + if (trans->mac_cfg->base->scd_chain_ext_wa) iwl_write_prph(trans, SCD_CHAINEXT_EN, 0); - iwl_trans_ac_txq_enable(trans, trans_pcie->txqs.cmd.q_id, - trans_pcie->txqs.cmd.fifo, - trans_pcie->txqs.cmd.wdg_timeout); + iwl_trans_ac_txq_enable(trans, trans->conf.cmd_queue, + trans->conf.cmd_fifo, + IWL_DEF_WD_TIMEOUT); /* Activate all Tx DMA/FIFO channels */ iwl_scd_activate_fifos(trans); @@ -529,7 +526,7 @@ void iwl_pcie_tx_start(struct iwl_trans *trans, u32 scd_base_addr) reg_val | FH_TX_CHICKEN_BITS_SCD_AUTO_RETRY_EN); /* Enable L1-Active */ - if (trans->trans_cfg->device_family < IWL_DEVICE_FAMILY_8000) + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_8000) iwl_clear_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_L1_ACT_DIS); } @@ -543,13 +540,13 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) * we should never get here in gen2 trans mode return early to avoid * having invalid accesses */ - if (WARN_ON_ONCE(trans->trans_cfg->gen2)) + if (WARN_ON_ONCE(trans->mac_cfg->gen2)) return; - for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues; + for (txq_id = 0; txq_id < trans->mac_cfg->base->num_of_queues; txq_id++) { struct iwl_txq *txq = trans_pcie->txqs.txq[txq_id]; - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) iwl_write_direct64(trans, FH_MEM_CBBC_QUEUE(trans, txq_id), txq->dma_addr); @@ -571,7 +568,7 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) * while we were in WoWLAN in which case SCD_SRAM_BASE_ADDR will * contain garbage. */ - iwl_pcie_tx_start(trans, 0); + iwl_pcie_tx_start(trans); } static void iwl_pcie_tx_stop_fh(struct iwl_trans *trans) @@ -633,7 +630,7 @@ int iwl_pcie_tx_stop(struct iwl_trans *trans) return 0; /* Unmap DMA from host system and free skb's */ - for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues; + for (txq_id = 0; txq_id < trans->mac_cfg->base->num_of_queues; txq_id++) iwl_pcie_txq_unmap(trans, txq_id); @@ -656,7 +653,7 @@ void iwl_pcie_tx_free(struct iwl_trans *trans) /* Tx queues */ if (trans_pcie->txq_memory) { for (txq_id = 0; - txq_id < trans->trans_cfg->base_params->num_of_queues; + txq_id < trans->mac_cfg->base->num_of_queues; txq_id++) { iwl_pcie_txq_free(trans, txq_id); trans_pcie->txqs.txq[txq_id] = NULL; @@ -678,7 +675,7 @@ void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq) bool active; u8 fifo; - if (trans->trans_cfg->gen2) { + if (trans->mac_cfg->gen2) { IWL_ERR(trans, "Queue %d is stuck %d %d\n", txq_id, txq->read_ptr, txq->write_ptr); /* TODO: access new SCD registers and dump them */ @@ -695,9 +692,9 @@ void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq) jiffies_to_msecs(txq->wd_timeout), txq->read_ptr, txq->write_ptr, iwl_read_prph(trans, SCD_QUEUE_RDPTR(txq_id)) & - (trans->trans_cfg->base_params->max_tfd_queue_size - 1), + (trans->mac_cfg->base->max_tfd_queue_size - 1), iwl_read_prph(trans, SCD_QUEUE_WRPTR(txq_id)) & - (trans->trans_cfg->base_params->max_tfd_queue_size - 1), + (trans->mac_cfg->base->max_tfd_queue_size - 1), iwl_read_direct32(trans, FH_TX_TRB_REG(fifo))); } @@ -723,8 +720,8 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num, bool cmd_queue) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - size_t num_entries = trans->trans_cfg->gen2 ? - slots_num : trans->trans_cfg->base_params->max_tfd_queue_size; + size_t num_entries = trans->mac_cfg->gen2 ? + slots_num : trans->mac_cfg->base->max_tfd_queue_size; size_t tfd_sz; size_t tb0_buf_sz; int i; @@ -779,7 +776,7 @@ int iwl_pcie_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, for (i = 0; i < num_entries; i++) { void *tfd = iwl_txq_get_tfd(trans, txq, i); - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) iwl_txq_set_tfd_invalid_gen2(trans, tfd); else iwl_txq_set_tfd_invalid_gen1(trans, tfd); @@ -799,6 +796,8 @@ error: return -ENOMEM; } +#define BC_TABLE_SIZE (sizeof(struct iwl_bc_tbl_entry) * TFD_QUEUE_BC_SIZE) + /* * iwl_pcie_tx_alloc - allocate TX context * Allocate all Tx DMA structures and initialize them @@ -808,12 +807,12 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) int ret; int txq_id, slots_num; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - u16 bc_tbls_size = trans->trans_cfg->base_params->num_of_queues; + u16 bc_tbls_size = trans->mac_cfg->base->num_of_queues; - if (WARN_ON(trans->trans_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)) + if (WARN_ON(trans->mac_cfg->device_family >= IWL_DEVICE_FAMILY_AX210)) return -EINVAL; - bc_tbls_size *= sizeof(struct iwlagn_scd_bc_tbl); + bc_tbls_size *= BC_TABLE_SIZE; /*It is not allowed to alloc twice, so warn when this happens. * We cannot rely on the previous allocation, so free and fail */ @@ -837,7 +836,7 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) } trans_pcie->txq_memory = - kcalloc(trans->trans_cfg->base_params->num_of_queues, + kcalloc(trans->mac_cfg->base->num_of_queues, sizeof(struct iwl_txq), GFP_KERNEL); if (!trans_pcie->txq_memory) { IWL_ERR(trans, "Not enough memory for txq\n"); @@ -846,16 +845,16 @@ static int iwl_pcie_tx_alloc(struct iwl_trans *trans) } /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues; + for (txq_id = 0; txq_id < trans->mac_cfg->base->num_of_queues; txq_id++) { - bool cmd_queue = (txq_id == trans_pcie->txqs.cmd.q_id); + bool cmd_queue = (txq_id == trans->conf.cmd_queue); if (cmd_queue) slots_num = max_t(u32, IWL_CMD_QUEUE_SIZE, - trans->cfg->min_txq_size); + trans->mac_cfg->base->min_txq_size); else slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE, - trans->cfg->min_ba_txq_size); + trans->mac_cfg->base->min_ba_txq_size); trans_pcie->txqs.txq[txq_id] = &trans_pcie->txq_memory[txq_id]; ret = iwl_pcie_txq_alloc(trans, trans_pcie->txqs.txq[txq_id], slots_num, cmd_queue); @@ -905,7 +904,7 @@ int iwl_txq_init(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num, bool cmd_queue) { u32 tfd_queue_max_size = - trans->trans_cfg->base_params->max_tfd_queue_size; + trans->mac_cfg->base->max_tfd_queue_size; int ret; txq->need_update = false; @@ -963,16 +962,16 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) spin_unlock_bh(&trans_pcie->irq_lock); /* Alloc and init all Tx queues, including the command queue (#4/#9) */ - for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues; + for (txq_id = 0; txq_id < trans->mac_cfg->base->num_of_queues; txq_id++) { - bool cmd_queue = (txq_id == trans_pcie->txqs.cmd.q_id); + bool cmd_queue = (txq_id == trans->conf.cmd_queue); if (cmd_queue) slots_num = max_t(u32, IWL_CMD_QUEUE_SIZE, - trans->cfg->min_txq_size); + trans->mac_cfg->base->min_txq_size); else slots_num = max_t(u32, IWL_DEFAULT_QUEUE_SIZE, - trans->cfg->min_ba_txq_size); + trans->mac_cfg->base->min_ba_txq_size); ret = iwl_txq_init(trans, trans_pcie->txqs.txq[txq_id], slots_num, cmd_queue); if (ret) { @@ -991,7 +990,7 @@ int iwl_pcie_tx_init(struct iwl_trans *trans) } iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_AUTO_ACTIVE_MODE); - if (trans->trans_cfg->base_params->num_of_queues > 20) + if (trans->mac_cfg->base->num_of_queues > 20) iwl_set_bits_prph(trans, SCD_GP_CTRL, SCD_GP_CTRL_ENABLE_31_QUEUES); @@ -1012,7 +1011,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, if (test_bit(STATUS_TRANS_DEAD, &trans->status)) return -ENODEV; - if (!trans->trans_cfg->base_params->apmg_wake_up_wa) + if (!trans->mac_cfg->base->apmg_wake_up_wa) return 0; /* @@ -1021,7 +1020,7 @@ static int iwl_pcie_set_cmd_in_flight(struct iwl_trans *trans, * returned. This needs to be done only on NICs that have * apmg_wake_up_wa set (see above.) */ - if (!_iwl_trans_pcie_grab_nic_access(trans)) + if (!_iwl_trans_pcie_grab_nic_access(trans, false)) return -EIO; /* @@ -1054,7 +1053,7 @@ static void iwl_txq_progress(struct iwl_txq *txq) * since we're making progress on this queue */ if (txq->read_ptr == txq->write_ptr) - del_timer(&txq->stuck_timer); + timer_delete(&txq->stuck_timer); else mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); } @@ -1090,12 +1089,12 @@ static void iwl_pcie_cmdq_reclaim(struct iwl_trans *trans, int txq_id, int idx) idx = iwl_txq_get_cmd_index(txq, idx); r = iwl_txq_get_cmd_index(txq, txq->read_ptr); - if (idx >= trans->trans_cfg->base_params->max_tfd_queue_size || + if (idx >= trans->mac_cfg->base->max_tfd_queue_size || (!iwl_txq_used(txq, idx, txq->read_ptr, txq->write_ptr))) { WARN_ONCE(test_bit(txq_id, trans_pcie->txqs.queue_used), "%s: Read index for DMA queue txq id (%d), index %d is out of range [0-%d] %d %d.\n", __func__, txq_id, idx, - trans->trans_cfg->base_params->max_tfd_queue_size, + trans->mac_cfg->base->max_tfd_queue_size, txq->write_ptr, txq->read_ptr); return; } @@ -1164,15 +1163,15 @@ bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, fifo = cfg->fifo; /* Disable the scheduler prior configuring the cmd queue */ - if (txq_id == trans_pcie->txqs.cmd.q_id && - trans_pcie->scd_set_active) + if (txq_id == trans->conf.cmd_queue && + trans->conf.scd_set_active) iwl_scd_enable_set_active(trans, 0); /* Stop this Tx queue before configuring it */ iwl_scd_txq_set_inactive(trans, txq_id); /* Set this queue as a chain-building queue unless it is CMD */ - if (txq_id != trans_pcie->txqs.cmd.q_id) + if (txq_id != trans->conf.cmd_queue) iwl_scd_txq_set_chain(trans, txq_id); if (cfg->aggregate) { @@ -1206,7 +1205,7 @@ bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, * this sad hardware issue. * This bug has been fixed on devices 9000 and up. */ - scd_bug = !trans->trans_cfg->mq_rx_supported && + scd_bug = !trans->mac_cfg->mq_rx_supported && !((ssn - txq->write_ptr) & 0x3f) && (ssn != txq->write_ptr); if (scd_bug) @@ -1242,8 +1241,8 @@ bool iwl_trans_pcie_txq_enable(struct iwl_trans *trans, int txq_id, u16 ssn, SCD_QUEUE_STTS_REG_MSK); /* enable the scheduler for this queue (only) */ - if (txq_id == trans_pcie->txqs.cmd.q_id && - trans_pcie->scd_set_active) + if (txq_id == trans->conf.cmd_queue && + trans->conf.scd_set_active) iwl_scd_enable_set_active(trans, BIT(txq_id)); IWL_DEBUG_TX_QUEUES(trans, @@ -1293,8 +1292,9 @@ void iwl_trans_pcie_txq_disable(struct iwl_trans *trans, int txq_id, if (configure_scd) { iwl_scd_txq_set_inactive(trans, txq_id); - iwl_trans_write_mem(trans, stts_addr, (const void *)zero_val, - ARRAY_SIZE(zero_val)); + iwl_trans_pcie_write_mem(trans, stts_addr, + (const void *)zero_val, + ARRAY_SIZE(zero_val)); } iwl_pcie_txq_unmap(trans, txq_id); @@ -1310,10 +1310,10 @@ static void iwl_trans_pcie_block_txq_ptrs(struct iwl_trans *trans, bool block) struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); int i; - for (i = 0; i < trans->trans_cfg->base_params->num_of_queues; i++) { + for (i = 0; i < trans->mac_cfg->base->num_of_queues; i++) { struct iwl_txq *txq = trans_pcie->txqs.txq[i]; - if (i == trans_pcie->txqs.cmd.q_id) + if (i == trans->conf.cmd_queue) continue; /* we skip the command queue (obviously) so it's OK to nest */ @@ -1346,7 +1346,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]; + struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue]; struct iwl_device_cmd *out_cmd; struct iwl_cmd_meta *out_meta; void *dup_buf = NULL; @@ -1361,7 +1361,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, u16 cmdlen[IWL_MAX_CMD_TBS_PER_TFD]; unsigned long flags; - if (WARN(!trans->wide_cmd_header && + if (WARN(!trans->conf.wide_cmd_header && group_id > IWL_ALWAYS_LONG_GROUP, "unsupported wide command %#x\n", cmd->id)) return -EINVAL; @@ -1475,7 +1475,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, sizeof(struct iwl_cmd_header_wide)); out_cmd->hdr_wide.reserved = 0; out_cmd->hdr_wide.sequence = - cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->txqs.cmd.q_id) | + cpu_to_le16(QUEUE_TO_SEQ(trans->conf.cmd_queue) | INDEX_TO_SEQ(txq->write_ptr)); cmd_pos = sizeof(struct iwl_cmd_header_wide); @@ -1483,7 +1483,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, } else { out_cmd->hdr.cmd = iwl_cmd_opcode(cmd->id); out_cmd->hdr.sequence = - cpu_to_le16(QUEUE_TO_SEQ(trans_pcie->txqs.cmd.q_id) | + cpu_to_le16(QUEUE_TO_SEQ(trans->conf.cmd_queue) | INDEX_TO_SEQ(txq->write_ptr)); out_cmd->hdr.group_id = 0; @@ -1534,7 +1534,7 @@ int iwl_pcie_enqueue_hcmd(struct iwl_trans *trans, iwl_get_cmd_string(trans, cmd->id), group_id, out_cmd->hdr.cmd, le16_to_cpu(out_cmd->hdr.sequence), - cmd_size, txq->write_ptr, idx, trans_pcie->txqs.cmd.q_id); + cmd_size, txq->write_ptr, idx, trans->conf.cmd_queue); /* start the TFD with the minimum copy bytes */ tb0_size = min_t(int, copy_size, IWL_FIRST_TB_SIZE); @@ -1633,14 +1633,14 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, struct iwl_device_cmd *cmd; struct iwl_cmd_meta *meta; struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]; + struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue]; /* If a Tx command is being handled and it isn't in the actual * command queue then there a command routing bug has been introduced * in the queue management code. */ - if (WARN(txq_id != trans_pcie->txqs.cmd.q_id, + if (WARN(txq_id != trans->conf.cmd_queue, "wrong command queue %d (should be %d), sequence 0x%X readp=%d writep=%d\n", - txq_id, trans_pcie->txqs.cmd.q_id, sequence, txq->read_ptr, + txq_id, trans->conf.cmd_queue, sequence, txq->read_ptr, txq->write_ptr)) { iwl_print_hex_error(trans, pkt, 32); return; @@ -1654,7 +1654,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, group_id = cmd->hdr.group_id; cmd_id = WIDE_ID(group_id, cmd->hdr.cmd); - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) iwl_txq_gen2_tfd_unmap(trans, meta, iwl_txq_get_tfd(trans, txq, index)); else @@ -1683,7 +1683,7 @@ void iwl_pcie_hcmd_complete(struct iwl_trans *trans, clear_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status); IWL_DEBUG_INFO(trans, "Clearing HCMD_ACTIVE for command %s\n", iwl_get_cmd_string(trans, cmd_id)); - wake_up(&trans->wait_command_queue); + wake_up(&trans_pcie->wait_command_queue); } meta->flags = 0; @@ -1753,7 +1753,7 @@ static void *iwl_pcie_get_page_hdr(struct iwl_trans *trans, dma_addr_t phys; void *ret; - page_ptr = (void *)((u8 *)skb->cb + trans_pcie->txqs.page_offs); + page_ptr = (void *)((u8 *)skb->cb + trans->conf.cb_data_offs); if (WARN_ON(*page_ptr)) return NULL; @@ -1855,6 +1855,7 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset, * @cmd_meta: command meta to store the scatter list information for unmapping * @hdr: output argument for TSO headers * @hdr_room: requested length for TSO headers + * @offset: offset into the data from which mapping should start * * Allocate space for a scatter gather list and TSO headers and map the SKB * using the scatter gather list. The SKB is unmapped again when the page is @@ -1864,9 +1865,12 @@ dma_addr_t iwl_pcie_get_sgt_tb_phys(struct sg_table *sgt, unsigned int offset, */ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_cmd_meta *cmd_meta, - u8 **hdr, unsigned int hdr_room) + u8 **hdr, unsigned int hdr_room, + unsigned int offset) { struct sg_table *sgt; + unsigned int n_segments = skb_shinfo(skb)->nr_frags + 1; + int orig_nents; if (WARN_ON_ONCE(skb_has_frag_list(skb))) return NULL; @@ -1874,8 +1878,7 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, *hdr = iwl_pcie_get_page_hdr(trans, hdr_room + __alignof__(struct sg_table) + sizeof(struct sg_table) + - (skb_shinfo(skb)->nr_frags + 1) * - sizeof(struct scatterlist), + n_segments * sizeof(struct scatterlist), skb); if (!*hdr) return NULL; @@ -1883,14 +1886,15 @@ struct sg_table *iwl_pcie_prep_tso(struct iwl_trans *trans, struct sk_buff *skb, sgt = (void *)PTR_ALIGN(*hdr + hdr_room, __alignof__(struct sg_table)); sgt->sgl = (void *)(sgt + 1); - sg_init_table(sgt->sgl, skb_shinfo(skb)->nr_frags + 1); + sg_init_table(sgt->sgl, n_segments); /* Only map the data, not the header (it is copied to the TSO page) */ - sgt->orig_nents = skb_to_sgvec(skb, sgt->sgl, skb_headlen(skb), - skb->data_len); - if (WARN_ON_ONCE(sgt->orig_nents <= 0)) + orig_nents = skb_to_sgvec(skb, sgt->sgl, offset, skb->len - offset); + if (WARN_ON_ONCE(orig_nents <= 0)) return NULL; + sgt->orig_nents = orig_nents; + /* And map the entire SKB */ if (dma_map_sgtable(trans->dev, sgt, DMA_TO_DEVICE, 0) < 0) return NULL; @@ -1908,7 +1912,7 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, u16 tb1_len) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; + struct iwl_tx_cmd_v6 *tx_cmd = (void *)dev_cmd->payload; struct ieee80211_hdr *hdr = (void *)skb->data; unsigned int snap_ip_tcp_hdrlen, ip_hdrlen, total_len, hdr_room; unsigned int mss = skb_shinfo(skb)->gso_size; @@ -1939,7 +1943,8 @@ static int iwl_fill_data_tbs_amsdu(struct iwl_trans *trans, struct sk_buff *skb, (3 + snap_ip_tcp_hdrlen + sizeof(struct ethhdr)) + iv_len; /* Our device supports 9 segments at most, it will fit in 1 page */ - sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room); + sgt = iwl_pcie_prep_tso(trans, skb, out_meta, &start_hdr, hdr_room, + snap_ip_tcp_hdrlen + hdr_len + iv_len); if (!sgt) return -ENOMEM; @@ -2062,14 +2067,14 @@ static void iwl_txq_gen1_update_byte_cnt_tbl(struct iwl_trans *trans, int num_tbs) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwlagn_scd_bc_tbl *scd_bc_tbl; + struct iwl_bc_tbl_entry *scd_bc_tbl; int write_ptr = txq->write_ptr; int txq_id = txq->id; u8 sec_ctl = 0; u16 len = byte_cnt + IWL_TX_CRC_SIZE + IWL_TX_DELIMITER_SIZE; __le16 bc_ent; struct iwl_device_tx_cmd *dev_cmd = txq->entries[txq->write_ptr].cmd; - struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; + struct iwl_tx_cmd_v6 *tx_cmd = (void *)dev_cmd->payload; u8 sta_id = tx_cmd->sta_id; scd_bc_tbl = trans_pcie->txqs.scd_bc_tbls.addr; @@ -2087,7 +2092,8 @@ static void iwl_txq_gen1_update_byte_cnt_tbl(struct iwl_trans *trans, len += IEEE80211_WEP_IV_LEN + IEEE80211_WEP_ICV_LEN; break; } - if (trans_pcie->txqs.bc_table_dword) + + if (trans->mac_cfg->device_family < IWL_DEVICE_FAMILY_AX210) len = DIV_ROUND_UP(len, 4); if (WARN_ON(len > 0xFFF || write_ptr >= TFD_QUEUE_SIZE_MAX)) @@ -2095,10 +2101,10 @@ static void iwl_txq_gen1_update_byte_cnt_tbl(struct iwl_trans *trans, bc_ent = cpu_to_le16(len | (sta_id << 12)); - scd_bc_tbl[txq_id].tfd_offset[write_ptr] = bc_ent; + scd_bc_tbl[txq_id * BC_TABLE_SIZE + write_ptr].tfd_offset = bc_ent; if (write_ptr < TFD_QUEUE_SIZE_BC_DUP) - scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + write_ptr] = + scd_bc_tbl[txq_id * BC_TABLE_SIZE + TFD_QUEUE_SIZE_MAX + write_ptr].tfd_offset = bc_ent; } @@ -2107,7 +2113,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); struct ieee80211_hdr *hdr; - struct iwl_tx_cmd *tx_cmd = (struct iwl_tx_cmd *)dev_cmd->payload; + struct iwl_tx_cmd_v6 *tx_cmd = (struct iwl_tx_cmd_v6 *)dev_cmd->payload; struct iwl_cmd_meta *out_meta; struct iwl_txq *txq; dma_addr_t tb0_phys, tb1_phys, scratch_phys; @@ -2148,7 +2154,8 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, struct iwl_device_tx_cmd **dev_cmd_ptr; dev_cmd_ptr = (void *)((u8 *)skb->cb + - trans_pcie->txqs.dev_cmd_offs); + trans->conf.cb_data_offs + + sizeof(void *)); *dev_cmd_ptr = dev_cmd; __skb_queue_tail(&txq->overflow_q, skb); @@ -2179,7 +2186,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, tb0_phys = iwl_txq_get_first_tb_dma(txq, txq->write_ptr); scratch_phys = tb0_phys + sizeof(struct iwl_cmd_header) + - offsetof(struct iwl_tx_cmd, scratch); + offsetof(struct iwl_tx_cmd_v6, scratch); tx_cmd->dram_lsb_ptr = cpu_to_le32(scratch_phys); tx_cmd->dram_msb_ptr = iwl_get_dma_hi_addr(scratch_phys); @@ -2194,7 +2201,7 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, * (This calculation modifies the TX command, so do it before the * setup of the first TB) */ - len = sizeof(struct iwl_tx_cmd) + sizeof(struct iwl_cmd_header) + + len = sizeof(struct iwl_tx_cmd_v6) + sizeof(struct iwl_cmd_header) + hdr_len - IWL_FIRST_TB_SIZE; /* do not align A-MSDU to dword as the subframe header aligns it */ amsdu = ieee80211_is_data_qos(fc) && @@ -2217,9 +2224,9 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, IWL_FIRST_TB_SIZE, true); /* there must be data left over for TB1 or this code must be changed */ - BUILD_BUG_ON(sizeof(struct iwl_tx_cmd) < IWL_FIRST_TB_SIZE); + BUILD_BUG_ON(sizeof(struct iwl_tx_cmd_v6) < IWL_FIRST_TB_SIZE); BUILD_BUG_ON(sizeof(struct iwl_cmd_header) + - offsetofend(struct iwl_tx_cmd, scratch) > + offsetofend(struct iwl_tx_cmd_v6, scratch) > IWL_FIRST_TB_SIZE); /* map the data for TB1 */ @@ -2307,24 +2314,24 @@ static void iwl_txq_gen1_inval_byte_cnt_tbl(struct iwl_trans *trans, int read_ptr) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - struct iwlagn_scd_bc_tbl *scd_bc_tbl = trans_pcie->txqs.scd_bc_tbls.addr; + struct iwl_bc_tbl_entry *scd_bc_tbl = trans_pcie->txqs.scd_bc_tbls.addr; int txq_id = txq->id; u8 sta_id = 0; __le16 bc_ent; struct iwl_device_tx_cmd *dev_cmd = txq->entries[read_ptr].cmd; - struct iwl_tx_cmd *tx_cmd = (void *)dev_cmd->payload; + struct iwl_tx_cmd_v6 *tx_cmd = (void *)dev_cmd->payload; WARN_ON(read_ptr >= TFD_QUEUE_SIZE_MAX); - if (txq_id != trans_pcie->txqs.cmd.q_id) + if (txq_id != trans->conf.cmd_queue) sta_id = tx_cmd->sta_id; bc_ent = cpu_to_le16(1 | (sta_id << 12)); - scd_bc_tbl[txq_id].tfd_offset[read_ptr] = bc_ent; + scd_bc_tbl[txq_id * BC_TABLE_SIZE + read_ptr].tfd_offset = bc_ent; if (read_ptr < TFD_QUEUE_SIZE_BC_DUP) - scd_bc_tbl[txq_id].tfd_offset[TFD_QUEUE_SIZE_MAX + read_ptr] = + scd_bc_tbl[txq_id * BC_TABLE_SIZE + TFD_QUEUE_SIZE_MAX + read_ptr].tfd_offset = bc_ent; } @@ -2338,7 +2345,7 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, int txq_read_ptr, txq_write_ptr; /* This function is not meant to release cmd queue*/ - if (WARN_ON(txq_id == trans_pcie->txqs.cmd.q_id)) + if (WARN_ON(txq_id == trans->conf.cmd_queue)) return; if (WARN_ON(!txq)) @@ -2380,7 +2387,7 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, IWL_ERR(trans, "%s: Read index for txq id (%d), last_to_free %d is out of range [0-%d] %d %d.\n", __func__, txq_id, last_to_free, - trans->trans_cfg->base_params->max_tfd_queue_size, + trans->mac_cfg->base->max_tfd_queue_size, txq_write_ptr, txq_read_ptr); iwl_op_mode_time_point(trans->op_mode, @@ -2409,7 +2416,7 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, txq->entries[read_ptr].skb = NULL; - if (!trans->trans_cfg->gen2) + if (!trans->mac_cfg->gen2) iwl_txq_gen1_inval_byte_cnt_tbl(trans, txq, txq_read_ptr); @@ -2451,7 +2458,8 @@ void iwl_pcie_reclaim(struct iwl_trans *trans, int txq_id, int ssn, struct iwl_device_tx_cmd *dev_cmd_ptr; dev_cmd_ptr = *(void **)((u8 *)skb->cb + - trans_pcie->txqs.dev_cmd_offs); + trans->conf.cb_data_offs + + sizeof(void *)); /* * Note that we can very well be overflowing again. @@ -2524,7 +2532,7 @@ void iwl_pcie_freeze_txq_timer(struct iwl_trans *trans, /* remember how long until the timer fires */ txq->frozen_expiry_remainder = txq->stuck_timer.expires - now; - del_timer(&txq->stuck_timer); + timer_delete(&txq->stuck_timer); goto next_queue; } @@ -2543,11 +2551,11 @@ next_queue: #define HOST_COMPLETE_TIMEOUT (2 * HZ) static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans, - struct iwl_host_cmd *cmd) + struct iwl_host_cmd *cmd, + const char *cmd_str) { struct iwl_trans_pcie *trans_pcie = IWL_TRANS_GET_PCIE_TRANS(trans); - const char *cmd_str = iwl_get_cmd_string(trans, cmd->id); - struct iwl_txq *txq = trans_pcie->txqs.txq[trans_pcie->txqs.cmd.q_id]; + struct iwl_txq *txq = trans_pcie->txqs.txq[trans->conf.cmd_queue]; int cmd_idx; int ret; @@ -2560,7 +2568,7 @@ static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans, IWL_DEBUG_INFO(trans, "Setting HCMD_ACTIVE for command %s\n", cmd_str); - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) cmd_idx = iwl_pcie_gen2_enqueue_hcmd(trans, cmd); else cmd_idx = iwl_pcie_enqueue_hcmd(trans, cmd); @@ -2573,7 +2581,7 @@ static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans, return ret; } - ret = wait_event_timeout(trans->wait_command_queue, + ret = wait_event_timeout(trans_pcie->wait_command_queue, !test_bit(STATUS_SYNC_HCMD_ACTIVE, &trans->status), HOST_COMPLETE_TIMEOUT); @@ -2589,7 +2597,7 @@ static int iwl_trans_pcie_send_hcmd_sync(struct iwl_trans *trans, cmd_str); ret = -ETIMEDOUT; - iwl_trans_sync_nmi(trans); + iwl_trans_pcie_sync_nmi(trans); goto cancel; } @@ -2640,6 +2648,8 @@ cancel: int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, struct iwl_host_cmd *cmd) { + const char *cmd_str = iwl_get_cmd_string(trans, cmd->id); + /* Make sure the NIC is still alive in the bus */ if (test_bit(STATUS_TRANS_DEAD, &trans->status)) return -ENODEV; @@ -2651,20 +2661,16 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, return -ERFKILL; } - if (unlikely(trans->system_pm_mode == IWL_PLAT_PM_MODE_D3 && - !(cmd->flags & CMD_SEND_IN_D3))) { - IWL_DEBUG_WOWLAN(trans, "Dropping CMD 0x%x: D3\n", cmd->id); - return -EHOSTDOWN; - } - if (cmd->flags & CMD_ASYNC) { int ret; + IWL_DEBUG_INFO(trans, "Sending async command %s\n", cmd_str); + /* An asynchronous command can not expect an SKB to be set. */ if (WARN_ON(cmd->flags & CMD_WANT_SKB)) return -EINVAL; - if (trans->trans_cfg->gen2) + if (trans->mac_cfg->gen2) ret = iwl_pcie_gen2_enqueue_hcmd(trans, cmd); else ret = iwl_pcie_enqueue_hcmd(trans, cmd); @@ -2678,6 +2684,5 @@ int iwl_trans_pcie_send_hcmd(struct iwl_trans *trans, return 0; } - return iwl_trans_pcie_send_hcmd_sync(trans, cmd); + return iwl_trans_pcie_send_hcmd_sync(trans, cmd, cmd_str); } -IWL_EXPORT_SYMBOL(iwl_trans_pcie_send_hcmd); diff --git a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c index d0bda23c628a..784433bb246a 100644 --- a/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c +++ b/drivers/net/wireless/intel/iwlwifi/tests/devinfo.c @@ -2,7 +2,7 @@ /* * KUnit tests for the iwlwifi device info table * - * Copyright (C) 2023-2024 Intel Corporation + * Copyright (C) 2023-2025 Intel Corporation */ #include <kunit/test.h> #include <linux/pci.h> @@ -13,10 +13,50 @@ MODULE_IMPORT_NS("EXPORTED_FOR_KUNIT_TESTING"); static void iwl_pci_print_dev_info(const char *pfx, const struct iwl_dev_info *di) { - printk(KERN_DEBUG "%sdev=%.4x,subdev=%.4x,mac_type=%.4x,mac_step=%.4x,rf_type=%.4x,cdb=%d,jacket=%d,rf_id=%.2x,no_160=%d,cores=%.2x\n", - pfx, di->device, di->subdevice, di->mac_type, di->mac_step, - di->rf_type, di->cdb, di->jacket, di->rf_id, di->no_160, - di->cores); + u16 subdevice_mask = GENMASK(di->subdevice_m_h, di->subdevice_m_l); + char buf[100] = {}; + int pos = 0; + + if (di->match_rf_type) + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " rf_type=%03x", di->rf_type); + else + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " rf_type=*"); + + if (di->match_bw_limit) + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " bw_limit=%d", di->bw_limit); + else + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " bw_limit=*"); + + if (di->match_rf_step) + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " rf_step=%c", + di->rf_step == SILICON_Z_STEP ? 'Z' : + 'A' + di->rf_step); + else + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " rf_step=*"); + + if (di->match_rf_id) + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " rf_id=0x%x", di->rf_id); + else + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " rf_id=*"); + + if (di->match_cdb) + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " cdb=%d", di->cdb); + else + pos += scnprintf(buf + pos, sizeof(buf) - pos, + " cdb=*"); + + + printk(KERN_DEBUG "%sdev=%04x subdev=%04x/%04x%s\n", + pfx, di->device, di->subdevice, subdevice_mask, buf); } static void devinfo_table_order(struct kunit *test) @@ -28,11 +68,14 @@ static void devinfo_table_order(struct kunit *test) const struct iwl_dev_info *ret; ret = iwl_pci_find_dev_info(di->device, di->subdevice, - di->mac_type, di->mac_step, di->rf_type, di->cdb, - di->jacket, di->rf_id, - di->no_160, di->cores, di->rf_step); - if (ret != di) { + di->rf_id, di->bw_limit, + di->rf_step); + if (!ret) { + iwl_pci_print_dev_info("No entry found for: ", di); + KUNIT_FAIL(test, + "No entry found for entry at index %d\n", idx); + } else if (ret != di) { iwl_pci_print_dev_info("searched: ", di); iwl_pci_print_dev_info("found: ", ret); KUNIT_FAIL(test, @@ -42,6 +85,92 @@ static void devinfo_table_order(struct kunit *test) } } +static void devinfo_names(struct kunit *test) +{ + int idx; + + for (idx = 0; idx < iwl_dev_info_table_size; idx++) { + const struct iwl_dev_info *di = &iwl_dev_info_table[idx]; + + KUNIT_ASSERT_TRUE(test, di->name); + } +} + +static void devinfo_no_cfg_dups(struct kunit *test) +{ + for (int i = 0; i < iwl_dev_info_table_size; i++) { + const struct iwl_rf_cfg *cfg_i = iwl_dev_info_table[i].cfg; + + for (int j = 0; j < i; j++) { + const struct iwl_rf_cfg *cfg_j = iwl_dev_info_table[j].cfg; + + if (cfg_i == cfg_j) + continue; + + KUNIT_EXPECT_NE_MSG(test, memcmp(cfg_i, cfg_j, + sizeof(*cfg_i)), 0, + "identical configs: %ps and %ps\n", + cfg_i, cfg_j); + } + } +} + +static void devinfo_no_name_dups(struct kunit *test) +{ + for (int i = 0; i < iwl_dev_info_table_size; i++) { + for (int j = 0; j < i; j++) { + if (iwl_dev_info_table[i].name == iwl_dev_info_table[j].name) + continue; + + KUNIT_EXPECT_NE_MSG(test, + strcmp(iwl_dev_info_table[i].name, + iwl_dev_info_table[j].name), + 0, + "name dup: %ps/%ps", + iwl_dev_info_table[i].name, + iwl_dev_info_table[j].name); + } + } +} + +static void devinfo_check_subdev_match(struct kunit *test) +{ + for (int i = 0; i < iwl_dev_info_table_size; i++) { + const struct iwl_dev_info *di = &iwl_dev_info_table[i]; + u16 subdevice_mask = GENMASK(di->subdevice_m_h, + di->subdevice_m_l); + + /* if BW limit bit is matched then must have a limit */ + if (di->match_bw_limit == 1 && di->bw_limit == 1) + KUNIT_EXPECT_NE(test, di->cfg->bw_limit, 0); + + /* if subdevice is ANY we can have RF ID/BW limit */ + if (di->subdevice == (u16)IWL_CFG_ANY) + continue; + + /* same if the subdevice mask doesn't overlap them */ + if (IWL_SUBDEVICE_RF_ID(subdevice_mask) == 0 && + IWL_SUBDEVICE_BW_LIM(subdevice_mask) == 0) + continue; + + /* but otherwise they shouldn't be used */ + KUNIT_EXPECT_EQ(test, (int)di->match_rf_id, 0); + KUNIT_EXPECT_EQ(test, (int)di->match_bw_limit, 0); + } +} + +static void devinfo_check_killer_subdev(struct kunit *test) +{ + for (int i = 0; i < iwl_dev_info_table_size; i++) { + const struct iwl_dev_info *di = &iwl_dev_info_table[i]; + + if (!strstr(di->name, "Killer")) + continue; + + KUNIT_EXPECT_NE(test, di->subdevice, (u16)IWL_CFG_ANY); + } +} + static void devinfo_pci_ids(struct kunit *test) { struct pci_dev *dev; @@ -64,9 +193,36 @@ static void devinfo_pci_ids(struct kunit *test) } } +static void devinfo_no_mac_cfg_dups(struct kunit *test) +{ + for (int i = 0; iwl_hw_card_ids[i].vendor; i++) { + const struct iwl_mac_cfg *cfg_i = + (void *)iwl_hw_card_ids[i].driver_data; + + for (int j = 0; j < i; j++) { + const struct iwl_mac_cfg *cfg_j = + (void *)iwl_hw_card_ids[j].driver_data; + + if (cfg_i == cfg_j) + continue; + + KUNIT_EXPECT_NE_MSG(test, memcmp(cfg_j, cfg_i, + sizeof(*cfg_i)), 0, + "identical configs: %ps and %ps\n", + cfg_i, cfg_j); + } + } +} + static struct kunit_case devinfo_test_cases[] = { KUNIT_CASE(devinfo_table_order), + KUNIT_CASE(devinfo_names), + KUNIT_CASE(devinfo_no_cfg_dups), + KUNIT_CASE(devinfo_no_name_dups), + KUNIT_CASE(devinfo_check_subdev_match), + KUNIT_CASE(devinfo_check_killer_subdev), KUNIT_CASE(devinfo_pci_ids), + KUNIT_CASE(devinfo_no_mac_cfg_dups), {} }; diff --git a/drivers/net/wireless/intersil/p54/fwio.c b/drivers/net/wireless/intersil/p54/fwio.c index 772084a9bd8d..3baf8ab01e22 100644 --- a/drivers/net/wireless/intersil/p54/fwio.c +++ b/drivers/net/wireless/intersil/p54/fwio.c @@ -231,6 +231,7 @@ int p54_download_eeprom(struct p54_common *priv, void *buf, mutex_lock(&priv->eeprom_mutex); priv->eeprom = buf; + priv->eeprom_slice_size = len; eeprom_hdr = skb_put(skb, eeprom_hdr_size + len); if (priv->fw_var < 0x509) { @@ -253,6 +254,7 @@ int p54_download_eeprom(struct p54_common *priv, void *buf, ret = -EBUSY; } priv->eeprom = NULL; + priv->eeprom_slice_size = 0; mutex_unlock(&priv->eeprom_mutex); return ret; } diff --git a/drivers/net/wireless/intersil/p54/p54.h b/drivers/net/wireless/intersil/p54/p54.h index 522656de4159..aeb5e40cc5ef 100644 --- a/drivers/net/wireless/intersil/p54/p54.h +++ b/drivers/net/wireless/intersil/p54/p54.h @@ -258,6 +258,7 @@ struct p54_common { /* eeprom handling */ void *eeprom; + size_t eeprom_slice_size; struct completion eeprom_comp; struct mutex eeprom_mutex; }; diff --git a/drivers/net/wireless/intersil/p54/txrx.c b/drivers/net/wireless/intersil/p54/txrx.c index 8414aa208655..2deb1bb54f24 100644 --- a/drivers/net/wireless/intersil/p54/txrx.c +++ b/drivers/net/wireless/intersil/p54/txrx.c @@ -496,14 +496,19 @@ static void p54_rx_eeprom_readback(struct p54_common *priv, return ; if (priv->fw_var >= 0x509) { - memcpy(priv->eeprom, eeprom->v2.data, - le16_to_cpu(eeprom->v2.len)); + if (le16_to_cpu(eeprom->v2.len) != priv->eeprom_slice_size) + return; + + memcpy(priv->eeprom, eeprom->v2.data, priv->eeprom_slice_size); } else { - memcpy(priv->eeprom, eeprom->v1.data, - le16_to_cpu(eeprom->v1.len)); + if (le16_to_cpu(eeprom->v1.len) != priv->eeprom_slice_size) + return; + + memcpy(priv->eeprom, eeprom->v1.data, priv->eeprom_slice_size); } priv->eeprom = NULL; + priv->eeprom_slice_size = 0; tmp = p54_find_and_unlink_skb(priv, hdr->req_id); dev_kfree_skb_any(tmp); complete(&priv->eeprom_comp); diff --git a/drivers/net/wireless/marvell/libertas/cmd.c b/drivers/net/wireless/marvell/libertas/cmd.c index 5a525da434c2..21fde876bb0d 100644 --- a/drivers/net/wireless/marvell/libertas/cmd.c +++ b/drivers/net/wireless/marvell/libertas/cmd.c @@ -453,46 +453,6 @@ out: } /** - * lbs_get_snmp_mib - Get an SNMP MIB value - * - * @priv: A pointer to &struct lbs_private structure - * @oid: The OID to retrieve from the firmware - * @out_val: Location for the returned value - * - * returns: 0 on success, error on failure - */ -int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val) -{ - struct cmd_ds_802_11_snmp_mib cmd; - int ret; - - memset(&cmd, 0, sizeof (cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_GET); - cmd.oid = cpu_to_le16(oid); - - ret = lbs_cmd_with_response(priv, CMD_802_11_SNMP_MIB, &cmd); - if (ret) - goto out; - - switch (le16_to_cpu(cmd.bufsize)) { - case sizeof(u8): - *out_val = cmd.value[0]; - break; - case sizeof(u16): - *out_val = le16_to_cpu(*((__le16 *)(&cmd.value))); - break; - default: - lbs_deb_cmd("SNMP_CMD: (get) unhandled OID 0x%x size %d\n", - oid, le16_to_cpu(cmd.bufsize)); - break; - } - -out: - return ret; -} - -/** * lbs_get_tx_power - Get the min, max, and current TX power * * @priv: A pointer to &struct lbs_private structure @@ -525,31 +485,6 @@ int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, } /** - * lbs_set_tx_power - Set the TX power - * - * @priv: A pointer to &struct lbs_private structure - * @dbm: The desired power level in dBm - * - * returns: 0 on success, error on failure - */ -int lbs_set_tx_power(struct lbs_private *priv, s16 dbm) -{ - struct cmd_ds_802_11_rf_tx_power cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.curlevel = cpu_to_le16(dbm); - - lbs_deb_cmd("SET_RF_TX_POWER: %d dBm\n", dbm); - - ret = lbs_cmd_with_response(priv, CMD_802_11_RF_TX_POWER, &cmd); - - return ret; -} - -/** * lbs_set_monitor_mode - Enable or disable monitor mode * (only implemented on OLPC usb8388 FW) * @@ -968,10 +903,6 @@ static void lbs_submit_command(struct lbs_private *priv, } if (command == CMD_802_11_DEEP_SLEEP) { - if (priv->is_auto_deep_sleep_enabled) { - priv->wakeup_dev_required = 1; - priv->dnld_sent = 0; - } priv->is_deep_sleep = 1; lbs_complete_command(priv, cmdnode, 0); } else { @@ -1440,70 +1371,6 @@ void lbs_ps_confirm_sleep(struct lbs_private *priv) } -/** - * lbs_set_tpc_cfg - Configures the transmission power control functionality - * - * @priv: A pointer to &struct lbs_private structure - * @enable: Transmission power control enable - * @p0: Power level when link quality is good (dBm). - * @p1: Power level when link quality is fair (dBm). - * @p2: Power level when link quality is poor (dBm). - * @usesnr: Use Signal to Noise Ratio in TPC - * - * returns: 0 on success - */ -int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, - int8_t p2, int usesnr) -{ - struct cmd_ds_802_11_tpc_cfg cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.enable = !!enable; - cmd.usesnr = !!usesnr; - cmd.P0 = p0; - cmd.P1 = p1; - cmd.P2 = p2; - - ret = lbs_cmd_with_response(priv, CMD_802_11_TPC_CFG, &cmd); - - return ret; -} - -/** - * lbs_set_power_adapt_cfg - Configures the power adaptation settings - * - * @priv: A pointer to &struct lbs_private structure - * @enable: Power adaptation enable - * @p0: Power level for 1, 2, 5.5 and 11 Mbps (dBm). - * @p1: Power level for 6, 9, 12, 18, 22, 24 and 36 Mbps (dBm). - * @p2: Power level for 48 and 54 Mbps (dBm). - * - * returns: 0 on Success - */ - -int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, - int8_t p1, int8_t p2) -{ - struct cmd_ds_802_11_pa_cfg cmd; - int ret; - - memset(&cmd, 0, sizeof(cmd)); - cmd.hdr.size = cpu_to_le16(sizeof(cmd)); - cmd.action = cpu_to_le16(CMD_ACT_SET); - cmd.enable = !!enable; - cmd.P0 = p0; - cmd.P1 = p1; - cmd.P2 = p2; - - ret = lbs_cmd_with_response(priv, CMD_802_11_PA_CFG , &cmd); - - return ret; -} - - struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, uint16_t command, struct cmd_header *in_cmd, int in_cmd_size, int (*callback)(struct lbs_private *, unsigned long, struct cmd_header *), @@ -1520,12 +1387,10 @@ struct cmd_ctrl_node *__lbs_cmd_async(struct lbs_private *priv, /* No commands are allowed in Deep Sleep until we toggle the GPIO * to wake up the card and it has signaled that it's ready. */ - if (!priv->is_auto_deep_sleep_enabled) { - if (priv->is_deep_sleep) { - lbs_deb_cmd("command not allowed in deep sleep\n"); - cmdnode = ERR_PTR(-EBUSY); - goto done; - } + if (priv->is_deep_sleep) { + lbs_deb_cmd("command not allowed in deep sleep\n"); + cmdnode = ERR_PTR(-EBUSY); + goto done; } cmdnode = lbs_get_free_cmd_node(priv); diff --git a/drivers/net/wireless/marvell/libertas/cmd.h b/drivers/net/wireless/marvell/libertas/cmd.h index d7be232f5739..a95c2651e67f 100644 --- a/drivers/net/wireless/marvell/libertas/cmd.h +++ b/drivers/net/wireless/marvell/libertas/cmd.h @@ -105,19 +105,9 @@ int lbs_get_tx_power(struct lbs_private *priv, s16 *curlevel, s16 *minlevel, int lbs_set_snmp_mib(struct lbs_private *priv, u32 oid, u16 val); -int lbs_get_snmp_mib(struct lbs_private *priv, u32 oid, u16 *out_val); - /* Commands only used in wext.c, assoc. and scan.c */ -int lbs_set_power_adapt_cfg(struct lbs_private *priv, int enable, int8_t p0, - int8_t p1, int8_t p2); - -int lbs_set_tpc_cfg(struct lbs_private *priv, int enable, int8_t p0, int8_t p1, - int8_t p2, int usesnr); - -int lbs_set_tx_power(struct lbs_private *priv, s16 dbm); - int lbs_set_deep_sleep(struct lbs_private *priv, int deep_sleep); int lbs_set_host_sleep(struct lbs_private *priv, int host_sleep); diff --git a/drivers/net/wireless/marvell/libertas/cmdresp.c b/drivers/net/wireless/marvell/libertas/cmdresp.c index f2aa659e7714..9742d3dba31c 100644 --- a/drivers/net/wireless/marvell/libertas/cmdresp.c +++ b/drivers/net/wireless/marvell/libertas/cmdresp.c @@ -119,7 +119,7 @@ int lbs_process_command_response(struct lbs_private *priv, u8 *data, u32 len) } /* Now we got response from FW, cancel the command timer */ - del_timer(&priv->command_timer); + timer_delete(&priv->command_timer); priv->cmd_timed_out = 0; if (respcmd == CMD_RET(CMD_802_11_PS_MODE)) { @@ -279,7 +279,6 @@ void lbs_process_event(struct lbs_private *priv, u32 event) priv->reset_deep_sleep_wakeup(priv); lbs_deb_cmd("EVENT: ds awake\n"); priv->is_deep_sleep = 0; - priv->wakeup_dev_required = 0; wake_up_interruptible(&priv->ds_awake_q); break; diff --git a/drivers/net/wireless/marvell/libertas/decl.h b/drivers/net/wireless/marvell/libertas/decl.h index c1e0388ef01d..ea69007a2958 100644 --- a/drivers/net/wireless/marvell/libertas/decl.h +++ b/drivers/net/wireless/marvell/libertas/decl.h @@ -64,11 +64,7 @@ int lbs_resume(struct lbs_private *priv); void lbs_queue_event(struct lbs_private *priv, u32 event); void lbs_notify_command_response(struct lbs_private *priv, u8 resp_idx); -int lbs_enter_auto_deep_sleep(struct lbs_private *priv); -int lbs_exit_auto_deep_sleep(struct lbs_private *priv); - u32 lbs_fw_index_to_data_rate(u8 index); -u8 lbs_data_rate_to_fw_index(u32 rate); int lbs_get_firmware(struct device *dev, u32 card_model, const struct lbs_fw_table *fw_table, diff --git a/drivers/net/wireless/marvell/libertas/dev.h b/drivers/net/wireless/marvell/libertas/dev.h index 4b6e05a8e5d5..c4708ce4eb83 100644 --- a/drivers/net/wireless/marvell/libertas/dev.h +++ b/drivers/net/wireless/marvell/libertas/dev.h @@ -83,12 +83,8 @@ struct lbs_private { /* Deep sleep */ int is_deep_sleep; int deep_sleep_required; - int is_auto_deep_sleep_enabled; - int wakeup_dev_required; int is_activity_detected; - int auto_deep_sleep_timeout; /* in ms */ wait_queue_head_t ds_awake_q; - struct timer_list auto_deepsleep_timer; /* Host sleep*/ int is_host_sleep_configured; diff --git a/drivers/net/wireless/marvell/libertas/if_usb.c b/drivers/net/wireless/marvell/libertas/if_usb.c index 2240b4db8c03..ea3cc2eaec36 100644 --- a/drivers/net/wireless/marvell/libertas/if_usb.c +++ b/drivers/net/wireless/marvell/libertas/if_usb.c @@ -897,7 +897,7 @@ restart: /* ... and wait for the process to complete */ wait_event_interruptible(cardp->fw_wq, cardp->surprise_removed || cardp->fwdnldover); - del_timer_sync(&cardp->fw_timeout); + timer_delete_sync(&cardp->fw_timeout); usb_kill_urb(cardp->rx_urb); if (!cardp->fwdnldover) { diff --git a/drivers/net/wireless/marvell/libertas/main.c b/drivers/net/wireless/marvell/libertas/main.c index 78e8b5aecec0..26d13e9b3c95 100644 --- a/drivers/net/wireless/marvell/libertas/main.c +++ b/drivers/net/wireless/marvell/libertas/main.c @@ -79,26 +79,6 @@ u32 lbs_fw_index_to_data_rate(u8 idx) return fw_data_rates[idx]; } -/** - * lbs_data_rate_to_fw_index - use rate to get the index - * - * @rate: data rate - * returns: index or 0 - */ -u8 lbs_data_rate_to_fw_index(u32 rate) -{ - u8 i; - - if (!rate) - return 0; - - for (i = 0; i < sizeof(fw_data_rates); i++) { - if (rate == fw_data_rates[i]) - return i; - } - return 0; -} - int lbs_set_iface_type(struct lbs_private *priv, enum nl80211_iftype type) { int ret = 0; @@ -222,7 +202,7 @@ int lbs_stop_iface(struct lbs_private *priv) spin_unlock_irqrestore(&priv->driver_lock, flags); cancel_work_sync(&priv->mcast_work); - del_timer_sync(&priv->tx_lockup_timer); + timer_delete_sync(&priv->tx_lockup_timer); /* Disable command processing, and wait for all commands to complete */ lbs_deb_main("waiting for commands to complete\n"); @@ -270,14 +250,13 @@ void lbs_host_to_card_done(struct lbs_private *priv) unsigned long flags; spin_lock_irqsave(&priv->driver_lock, flags); - del_timer(&priv->tx_lockup_timer); + timer_delete(&priv->tx_lockup_timer); priv->dnld_sent = DNLD_RES_RECEIVED; /* Wake main thread if commands are pending */ if (!priv->cur_cmd || priv->tx_pending_len > 0) { - if (!priv->wakeup_dev_required) - wake_up(&priv->waitq); + wake_up(&priv->waitq); } spin_unlock_irqrestore(&priv->driver_lock, flags); @@ -468,8 +447,7 @@ static int lbs_thread(void *data) shouldsleep = 0; /* We have a command response */ else if (priv->cur_cmd) shouldsleep = 1; /* Can't send a command; one already running */ - else if (!list_empty(&priv->cmdpendingq) && - !(priv->wakeup_dev_required)) + else if (!list_empty(&priv->cmdpendingq)) shouldsleep = 0; /* We have a command to send */ else if (kfifo_len(&priv->event_fifo)) shouldsleep = 0; /* We have an event to process */ @@ -536,14 +514,6 @@ static int lbs_thread(void *data) } spin_unlock_irq(&priv->driver_lock); - if (priv->wakeup_dev_required) { - lbs_deb_thread("Waking up device...\n"); - /* Wake up device */ - if (priv->exit_deep_sleep(priv)) - lbs_deb_thread("Wakeup device failed\n"); - continue; - } - /* command timeout stuff */ if (priv->cmd_timed_out && priv->cur_cmd) { struct cmd_ctrl_node *cmdnode = priv->cur_cmd; @@ -624,9 +594,8 @@ static int lbs_thread(void *data) spin_unlock_irq(&priv->driver_lock); } - del_timer(&priv->command_timer); - del_timer(&priv->tx_lockup_timer); - del_timer(&priv->auto_deepsleep_timer); + timer_delete(&priv->command_timer); + timer_delete(&priv->tx_lockup_timer); return 0; } @@ -773,55 +742,6 @@ static void lbs_tx_lockup_handler(struct timer_list *t) spin_unlock_irqrestore(&priv->driver_lock, flags); } -/** - * auto_deepsleep_timer_fn - put the device back to deep sleep mode when - * timer expires and no activity (command, event, data etc.) is detected. - * @t: Context from which to retrieve a &struct lbs_private pointer - * returns: N/A - */ -static void auto_deepsleep_timer_fn(struct timer_list *t) -{ - struct lbs_private *priv = from_timer(priv, t, auto_deepsleep_timer); - - if (priv->is_activity_detected) { - priv->is_activity_detected = 0; - } else { - if (priv->is_auto_deep_sleep_enabled && - (!priv->wakeup_dev_required) && - (priv->connect_status != LBS_CONNECTED)) { - struct cmd_header cmd; - - lbs_deb_main("Entering auto deep sleep mode...\n"); - memset(&cmd, 0, sizeof(cmd)); - cmd.size = cpu_to_le16(sizeof(cmd)); - lbs_cmd_async(priv, CMD_802_11_DEEP_SLEEP, &cmd, - sizeof(cmd)); - } - } - mod_timer(&priv->auto_deepsleep_timer , jiffies + - (priv->auto_deep_sleep_timeout * HZ)/1000); -} - -int lbs_enter_auto_deep_sleep(struct lbs_private *priv) -{ - priv->is_auto_deep_sleep_enabled = 1; - if (priv->is_deep_sleep) - priv->wakeup_dev_required = 1; - mod_timer(&priv->auto_deepsleep_timer , - jiffies + (priv->auto_deep_sleep_timeout * HZ)/1000); - - return 0; -} - -int lbs_exit_auto_deep_sleep(struct lbs_private *priv) -{ - priv->is_auto_deep_sleep_enabled = 0; - priv->auto_deep_sleep_timeout = 0; - del_timer(&priv->auto_deepsleep_timer); - - return 0; -} - static int lbs_init_adapter(struct lbs_private *priv) { int ret; @@ -835,9 +755,7 @@ static int lbs_init_adapter(struct lbs_private *priv) priv->psmode = LBS802_11POWERMODECAM; priv->psstate = PS_STATE_FULL_POWER; priv->is_deep_sleep = 0; - priv->is_auto_deep_sleep_enabled = 0; priv->deep_sleep_required = 0; - priv->wakeup_dev_required = 0; init_waitqueue_head(&priv->ds_awake_q); init_waitqueue_head(&priv->scan_q); priv->authtype_auto = 1; @@ -849,7 +767,6 @@ static int lbs_init_adapter(struct lbs_private *priv) timer_setup(&priv->command_timer, lbs_cmd_timeout_handler, 0); timer_setup(&priv->tx_lockup_timer, lbs_tx_lockup_handler, 0); - timer_setup(&priv->auto_deepsleep_timer, auto_deepsleep_timer_fn, 0); INIT_LIST_HEAD(&priv->cmdfreeq); INIT_LIST_HEAD(&priv->cmdpendingq); @@ -881,9 +798,8 @@ static void lbs_free_adapter(struct lbs_private *priv) { lbs_free_cmd_buffer(priv); kfifo_free(&priv->event_fifo); - del_timer(&priv->command_timer); - del_timer(&priv->tx_lockup_timer); - del_timer(&priv->auto_deepsleep_timer); + timer_delete(&priv->command_timer); + timer_delete(&priv->tx_lockup_timer); } static const struct net_device_ops lbs_netdev_ops = { diff --git a/drivers/net/wireless/marvell/libertas_tf/cmd.c b/drivers/net/wireless/marvell/libertas_tf/cmd.c index efb98304555a..7fc1bdb6c458 100644 --- a/drivers/net/wireless/marvell/libertas_tf/cmd.c +++ b/drivers/net/wireless/marvell/libertas_tf/cmd.c @@ -757,7 +757,7 @@ int lbtf_process_rx_command(struct lbtf_private *priv) } /* Now we got response from FW, cancel the command timer */ - del_timer(&priv->command_timer); + timer_delete(&priv->command_timer); priv->cmd_timed_out = 0; if (priv->nr_retries) priv->nr_retries = 0; diff --git a/drivers/net/wireless/marvell/libertas_tf/if_usb.c b/drivers/net/wireless/marvell/libertas_tf/if_usb.c index 1750f5e93de2..7c413dc81f9a 100644 --- a/drivers/net/wireless/marvell/libertas_tf/if_usb.c +++ b/drivers/net/wireless/marvell/libertas_tf/if_usb.c @@ -875,7 +875,7 @@ restart: wait_event_interruptible(cardp->fw_wq, cardp->priv->surpriseremoved || cardp->fwdnldover); - del_timer_sync(&cardp->fw_timeout); + timer_delete_sync(&cardp->fw_timeout); usb_kill_urb(cardp->rx_urb); if (!cardp->fwdnldover) { diff --git a/drivers/net/wireless/marvell/libertas_tf/main.c b/drivers/net/wireless/marvell/libertas_tf/main.c index b47a832b9ae2..a57a11be57d8 100644 --- a/drivers/net/wireless/marvell/libertas_tf/main.c +++ b/drivers/net/wireless/marvell/libertas_tf/main.c @@ -174,7 +174,7 @@ static void lbtf_free_adapter(struct lbtf_private *priv) { lbtf_deb_enter(LBTF_DEB_MAIN); lbtf_free_cmd_buffer(priv); - del_timer(&priv->command_timer); + timer_delete(&priv->command_timer); lbtf_deb_leave(LBTF_DEB_MAIN); } @@ -642,7 +642,7 @@ int lbtf_remove_card(struct lbtf_private *priv) lbtf_deb_enter(LBTF_DEB_MAIN); priv->surpriseremoved = 1; - del_timer(&priv->command_timer); + timer_delete(&priv->command_timer); lbtf_free_adapter(priv); priv->hw = NULL; ieee80211_unregister_hw(hw); diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c index 66f0f5377ac1..738bafc3749b 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.c +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -403,12 +403,14 @@ mwifiex_cmd_append_11n_tlv(struct mwifiex_private *priv, if (sband->ht_cap.cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40 && bss_desc->bcn_ht_oper->ht_param & - IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) + IEEE80211_HT_PARAM_CHAN_WIDTH_ANY) { + chan_list->chan_scan_param[0].radio_type |= + CHAN_BW_40MHZ << 2; SET_SECONDARYCHAN(chan_list->chan_scan_param[0]. radio_type, (bss_desc->bcn_ht_oper->ht_param & IEEE80211_HT_PARAM_CHA_SEC_OFFSET)); - + } *buffer += struct_size(chan_list, chan_scan_param, 1); ret_len += struct_size(chan_list, chan_scan_param, 1); } diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index cb948ca34373..8aff1df09b40 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -206,7 +206,7 @@ mwifiex_del_rx_reorder_entry(struct mwifiex_private *priv, start_win = (tbl->start_win + tbl->win_size) & (MAX_TID_VALUE - 1); mwifiex_11n_dispatch_pkt_until_start_win(priv, tbl, start_win); - del_timer_sync(&tbl->timer_context.timer); + timer_delete_sync(&tbl->timer_context.timer); tbl->timer_context.timer_is_set = false; spin_lock_bh(&priv->rx_reorder_tbl_lock); diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index a099fdaafa45..60c12328c2f3 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -1126,7 +1126,7 @@ mwifiex_change_vif_to_p2p(struct net_device *dev, HostCmd_ACT_GEN_SET, 0, NULL, true)) return -1; - if (mwifiex_sta_init_cmd(priv, false, false)) + if (mwifiex_sta_init_cmd(priv, false)) return -1; return 0; @@ -1167,7 +1167,7 @@ mwifiex_change_vif_to_sta_adhoc(struct net_device *dev, if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, HostCmd_ACT_GEN_SET, 0, NULL, true)) return -1; - if (mwifiex_sta_init_cmd(priv, false, false)) + if (mwifiex_sta_init_cmd(priv, false)) return -1; return 0; @@ -1204,7 +1204,7 @@ mwifiex_change_vif_to_ap(struct net_device *dev, if (mwifiex_send_cmd(priv, HostCmd_CMD_SET_BSS_MODE, HostCmd_ACT_GEN_SET, 0, NULL, true)) return -1; - if (mwifiex_sta_init_cmd(priv, false, false)) + if (mwifiex_sta_init_cmd(priv, false)) return -1; return 0; @@ -2906,16 +2906,12 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, struct mwifiex_private *priv) { int rx_mcs_supp; - struct ieee80211_mcs_info mcs_set; - u8 *mcs = (u8 *)&mcs_set; struct mwifiex_adapter *adapter = priv->adapter; ht_info->ht_supported = true; ht_info->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ht_info->ampdu_density = IEEE80211_HT_MPDU_DENSITY_NONE; - memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); - /* Fill HT capability information */ if (ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) ht_info->cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; @@ -2961,17 +2957,15 @@ mwifiex_setup_ht_caps(struct ieee80211_sta_ht_cap *ht_info, ht_info->cap |= IEEE80211_HT_CAP_SM_PS; rx_mcs_supp = GET_RXMCSSUPP(adapter->user_dev_mcs_support); + + memset(&ht_info->mcs, 0, sizeof(ht_info->mcs)); /* Set MCS for 1x1/2x2 */ - memset(mcs, 0xff, rx_mcs_supp); - /* Clear all the other values */ - memset(&mcs[rx_mcs_supp], 0, - sizeof(struct ieee80211_mcs_info) - rx_mcs_supp); + memset(ht_info->mcs.rx_mask, 0xff, rx_mcs_supp); + if (priv->bss_mode == NL80211_IFTYPE_STATION || ISSUPP_CHANWIDTH40(adapter->hw_dot_11n_dev_cap)) /* Set MCS32 for infra mode or ad-hoc mode with 40MHz support */ - SETHT_MCS32(mcs_set.rx_mask); - - memcpy((u8 *) &ht_info->mcs, mcs, sizeof(struct ieee80211_mcs_info)); + SETHT_MCS32(ht_info->mcs.rx_mask); ht_info->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; } @@ -3013,7 +3007,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, return ERR_PTR(-EFAULT); } - priv->wdev.wiphy = wiphy; priv->wdev.iftype = NL80211_IFTYPE_STATION; if (type == NL80211_IFTYPE_UNSPECIFIED) @@ -3022,8 +3015,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, priv->bss_mode = type; priv->bss_type = MWIFIEX_BSS_TYPE_STA; - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_STA; break; @@ -3043,14 +3034,10 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, return ERR_PTR(-EFAULT); } - priv->wdev.wiphy = wiphy; priv->wdev.iftype = NL80211_IFTYPE_AP; priv->bss_type = MWIFIEX_BSS_TYPE_UAP; - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_UAP; - priv->bss_started = 0; priv->bss_mode = type; break; @@ -3070,7 +3057,6 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, return ERR_PTR(-EFAULT); } - priv->wdev.wiphy = wiphy; /* At start-up, wpa_supplicant tries to change the interface * to NL80211_IFTYPE_STATION if it is not managed mode. */ @@ -3083,10 +3069,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, */ priv->bss_type = MWIFIEX_BSS_TYPE_P2P; - priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; - priv->bss_priority = 0; priv->bss_role = MWIFIEX_BSS_ROLE_STA; - priv->bss_started = 0; if (mwifiex_cfg80211_init_p2p_client(priv)) { memset(&priv->wdev, 0, sizeof(priv->wdev)); @@ -3100,6 +3083,11 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, return ERR_PTR(-EINVAL); } + priv->wdev.wiphy = wiphy; + priv->bss_priority = 0; + priv->bss_started = 0; + priv->frame_type = MWIFIEX_DATA_FRAME_TYPE_ETH_II; + dev = alloc_netdev_mqs(sizeof(struct mwifiex_private *), name, name_assign_type, ether_setup, IEEE80211_NUM_ACS, 1); @@ -3122,7 +3110,7 @@ struct wireless_dev *mwifiex_add_virtual_intf(struct wiphy *wiphy, if (ret) goto err_set_bss_mode; - ret = mwifiex_sta_init_cmd(priv, false, false); + ret = mwifiex_sta_init_cmd(priv, false); if (ret) goto err_sta_init; } @@ -4703,7 +4691,7 @@ int mwifiex_register_cfg80211(struct mwifiex_adapter *adapter) void *wdev_priv; struct wiphy *wiphy; struct mwifiex_private *priv = adapter->priv[MWIFIEX_BSS_TYPE_STA]; - u8 *country_code; + const u8 *country_code; u32 thr, retry; struct cfg80211_ops *ops; diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c index d39092b99212..686bf12b6c26 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfp.c +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -150,10 +150,10 @@ static const u16 ac_mcs_rate_nss2[8][10] = { struct region_code_mapping { u8 code; - u8 region[IEEE80211_COUNTRY_STRING_LEN]; + u8 region[IEEE80211_COUNTRY_STRING_LEN] __nonstring; }; -static struct region_code_mapping region_code_mapping_t[] = { +static const struct region_code_mapping region_code_mapping_t[] = { { 0x10, "US " }, /* US FCC */ { 0x20, "CA " }, /* IC Canada */ { 0x30, "FR " }, /* France */ @@ -165,7 +165,7 @@ static struct region_code_mapping region_code_mapping_t[] = { }; /* This function converts integer code to region string */ -u8 *mwifiex_11d_code_2_region(u8 code) +const u8 *mwifiex_11d_code_2_region(u8 code) { u8 i; diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index b30ed321c625..3bf27efe4537 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -159,11 +159,9 @@ static int mwifiex_cmd_host_cmd(struct mwifiex_private *priv, * sending. Afterwards, it logs the command ID and action for debugging * and sets up the command timeout timer. */ -static int mwifiex_dnld_cmd_to_fw(struct mwifiex_private *priv, +static int mwifiex_dnld_cmd_to_fw(struct mwifiex_adapter *adapter, struct cmd_ctrl_node *cmd_node) { - - struct mwifiex_adapter *adapter = priv->adapter; int ret; struct host_cmd_ds_command *host_cmd; uint16_t cmd_code; @@ -367,8 +365,7 @@ static int mwifiex_dnld_sleep_confirm_cmd(struct mwifiex_adapter *adapter) (test_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags) && !adapter->sleep_period.period)) { adapter->pm_wakeup_card_req = true; - mwifiex_hs_activated_event(mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), true); + mwifiex_hs_activated_event(adapter, true); } return ret; @@ -473,8 +470,7 @@ void mwifiex_free_cmd_buffer(struct mwifiex_adapter *adapter) int mwifiex_process_event(struct mwifiex_adapter *adapter) { int ret, i; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + struct mwifiex_private *priv; struct sk_buff *skb = adapter->event_skb; u32 eventcause; struct mwifiex_rxinfo *rx_info; @@ -744,7 +740,6 @@ mwifiex_insert_cmd_to_pending_q(struct mwifiex_adapter *adapter, */ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) { - struct mwifiex_private *priv; struct cmd_ctrl_node *cmd_node; int ret = 0; struct host_cmd_ds_command *host_cmd; @@ -768,7 +763,6 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) struct cmd_ctrl_node, list); host_cmd = (struct host_cmd_ds_command *) (cmd_node->cmd_skb->data); - priv = cmd_node->priv; if (adapter->ps_state != PS_STATE_AWAKE) { mwifiex_dbg(adapter, ERROR, @@ -783,18 +777,17 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) spin_unlock_bh(&adapter->cmd_pending_q_lock); spin_unlock_bh(&adapter->mwifiex_cmd_lock); - ret = mwifiex_dnld_cmd_to_fw(priv, cmd_node); - priv = mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + ret = mwifiex_dnld_cmd_to_fw(adapter, cmd_node); + /* Any command sent to the firmware when host is in sleep * mode should de-configure host sleep. We should skip the * host sleep configuration command itself though */ - if (priv && (host_cmd->command != - cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH))) { + if (host_cmd->command != cpu_to_le16(HostCmd_CMD_802_11_HS_CFG_ENH)) { if (adapter->hs_activated) { clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags); - mwifiex_hs_activated_event(priv, false); + mwifiex_hs_activated_event(adapter, false); } } @@ -810,8 +803,7 @@ int mwifiex_exec_next_cmd(struct mwifiex_adapter *adapter) int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) { struct host_cmd_ds_command *resp; - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + struct mwifiex_private *priv; int ret = 0; uint16_t orig_cmdresp_no; uint16_t cmdresp_no; @@ -836,7 +828,7 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) return -1; } /* Now we got response from FW, cancel the command timer */ - del_timer_sync(&adapter->cmd_timer); + timer_delete_sync(&adapter->cmd_timer); clear_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags); if (adapter->curr_cmd->cmd_flag & CMD_F_HOSTCMD) { @@ -900,18 +892,6 @@ int mwifiex_process_cmdresp(struct mwifiex_adapter *adapter) ret = mwifiex_process_sta_cmdresp(priv, cmdresp_no, resp); } - /* Check init command response */ - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { - if (ret) { - mwifiex_dbg(adapter, ERROR, - "%s: cmd %#x failed during\t" - "initialization\n", __func__, cmdresp_no); - mwifiex_init_fw_complete(adapter); - return -1; - } else if (adapter->last_init_cmd == cmdresp_no) - adapter->hw_status = MWIFIEX_HW_STATUS_INIT_DONE; - } - if (adapter->curr_cmd) { if (adapter->curr_cmd->wait_q_enabled) adapter->cmd_wait_q.status = ret; @@ -1030,10 +1010,6 @@ mwifiex_cmd_timeout_func(struct timer_list *t) mwifiex_cancel_pending_ioctl(adapter); } } - if (adapter->hw_status == MWIFIEX_HW_STATUS_INITIALIZING) { - mwifiex_init_fw_complete(adapter); - return; - } if (adapter->if_ops.device_dump) adapter->if_ops.device_dump(adapter); @@ -1160,27 +1136,27 @@ mwifiex_check_ps_cond(struct mwifiex_adapter *adapter) * This event is generated by the driver, with a blank event body. */ void -mwifiex_hs_activated_event(struct mwifiex_private *priv, u8 activated) +mwifiex_hs_activated_event(struct mwifiex_adapter *adapter, u8 activated) { if (activated) { if (test_bit(MWIFIEX_IS_HS_CONFIGURED, - &priv->adapter->work_flags)) { - priv->adapter->hs_activated = true; - mwifiex_update_rxreor_flags(priv->adapter, + &adapter->work_flags)) { + adapter->hs_activated = true; + mwifiex_update_rxreor_flags(adapter, RXREOR_FORCE_NO_DROP); - mwifiex_dbg(priv->adapter, EVENT, + mwifiex_dbg(adapter, EVENT, "event: hs_activated\n"); - priv->adapter->hs_activate_wait_q_woken = true; + adapter->hs_activate_wait_q_woken = true; wake_up_interruptible( - &priv->adapter->hs_activate_wait_q); + &adapter->hs_activate_wait_q); } else { - mwifiex_dbg(priv->adapter, EVENT, + mwifiex_dbg(adapter, EVENT, "event: HS not configured\n"); } } else { - mwifiex_dbg(priv->adapter, EVENT, + mwifiex_dbg(adapter, EVENT, "event: hs_deactivated\n"); - priv->adapter->hs_activated = false; + adapter->hs_activated = false; } } @@ -1204,7 +1180,7 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, if (phs_cfg->action == cpu_to_le16(HS_ACTIVATE) && adapter->iface_type != MWIFIEX_USB) { - mwifiex_hs_activated_event(priv, true); + mwifiex_hs_activated_event(adapter, true); return 0; } else { mwifiex_dbg(adapter, CMD, @@ -1217,11 +1193,11 @@ int mwifiex_ret_802_11_hs_cfg(struct mwifiex_private *priv, if (conditions != HS_CFG_CANCEL) { set_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags); if (adapter->iface_type == MWIFIEX_USB) - mwifiex_hs_activated_event(priv, true); + mwifiex_hs_activated_event(adapter, true); } else { clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags); if (adapter->hs_activated) - mwifiex_hs_activated_event(priv, false); + mwifiex_hs_activated_event(adapter, false); } return 0; @@ -1250,9 +1226,7 @@ mwifiex_process_hs_config(struct mwifiex_adapter *adapter) adapter->hs_activated = false; clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags); clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); - mwifiex_hs_activated_event(mwifiex_get_priv(adapter, - MWIFIEX_BSS_ROLE_ANY), - false); + mwifiex_hs_activated_event(adapter, false); } EXPORT_SYMBOL_GPL(mwifiex_process_hs_config); @@ -1302,9 +1276,7 @@ mwifiex_process_sleep_confirm_resp(struct mwifiex_adapter *adapter, } adapter->pm_wakeup_card_req = true; if (test_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags)) - mwifiex_hs_activated_event(mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - true); + mwifiex_hs_activated_event(adapter, true); adapter->ps_state = PS_STATE_SLEEP; cmd->command = cpu_to_le16(command); cmd->seq_num = cpu_to_le16(seq_num); diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 4a96281792cc..91458f3bd14a 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -454,6 +454,11 @@ enum mwifiex_channel_flags { #define HostCmd_RET_BIT 0x8000 #define HostCmd_ACT_GEN_GET 0x0000 #define HostCmd_ACT_GEN_SET 0x0001 +#define HOST_CMD_ACT_GEN_SET 0x0001 +/* Add this non-CamelCase-style macro to comply with checkpatch requirements. + * This macro will eventually replace all existing CamelCase-style macros in + * the future for consistency. + */ #define HostCmd_ACT_GEN_REMOVE 0x0004 #define HostCmd_ACT_BITWISE_SET 0x0002 #define HostCmd_ACT_BITWISE_CLR 0x0003 @@ -2352,6 +2357,14 @@ struct host_cmd_ds_add_station { u8 tlv[]; } __packed; +#define MWIFIEX_CFG_TYPE_CAL 0x2 + +struct host_cmd_ds_802_11_cfg_data { + __le16 action; + __le16 type; + __le16 data_len; +} __packed; + struct host_cmd_ds_command { __le16 command; __le16 size; @@ -2431,6 +2444,7 @@ struct host_cmd_ds_command { struct host_cmd_ds_pkt_aggr_ctrl pkt_aggr_ctrl; struct host_cmd_ds_sta_configure sta_cfg; struct host_cmd_ds_add_station sta_info; + struct host_cmd_ds_802_11_cfg_data cfg_data; } params; } __packed; diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index 8b61e45cd667..32c374e47794 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -390,7 +390,7 @@ static void mwifiex_invalidate_lists(struct mwifiex_adapter *adapter) static void mwifiex_adapter_cleanup(struct mwifiex_adapter *adapter) { - del_timer(&adapter->wakeup_timer); + timer_delete(&adapter->wakeup_timer); cancel_delayed_work_sync(&adapter->devdump_work); mwifiex_cancel_all_pending_cmd(adapter); wake_up_interruptible(&adapter->cmd_wait_q.wait); @@ -480,14 +480,12 @@ int mwifiex_init_lock_list(struct mwifiex_adapter *adapter) * - Initialize the private structure * - Add BSS priority tables to the adapter structure * - For each interface, send the init commands to firmware - * - Send the first command in command pending queue, if available */ int mwifiex_init_fw(struct mwifiex_adapter *adapter) { int ret; struct mwifiex_private *priv; u8 i, first_sta = true; - int is_cmd_pend_q_empty; adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; @@ -509,11 +507,10 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter) } if (adapter->mfg_mode) { adapter->hw_status = MWIFIEX_HW_STATUS_READY; - ret = -EINPROGRESS; } else { for (i = 0; i < adapter->priv_num; i++) { ret = mwifiex_sta_init_cmd(adapter->priv[i], - first_sta, true); + first_sta); if (ret == -1) return -1; @@ -522,17 +519,15 @@ int mwifiex_init_fw(struct mwifiex_adapter *adapter) } spin_lock_bh(&adapter->cmd_pending_q_lock); - is_cmd_pend_q_empty = list_empty(&adapter->cmd_pending_q); + WARN_ON(!list_empty(&adapter->cmd_pending_q)); spin_unlock_bh(&adapter->cmd_pending_q_lock); - if (!is_cmd_pend_q_empty) { - /* Send the first command in queue and return */ - if (mwifiex_main_process(adapter) != -1) - ret = -EINPROGRESS; - } else { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - } - return ret; + adapter->hw_status = MWIFIEX_HW_STATUS_READY; + + if (adapter->if_ops.init_fw_port) + adapter->if_ops.init_fw_port(adapter); + + return 0; } /* @@ -613,7 +608,7 @@ mwifiex_shutdown_drv(struct mwifiex_adapter *adapter) if (adapter->curr_cmd) { mwifiex_dbg(adapter, WARN, "curr_cmd is still in processing\n"); - del_timer_sync(&adapter->cmd_timer); + timer_delete_sync(&adapter->cmd_timer); mwifiex_recycle_cmd_node(adapter, adapter->curr_cmd); adapter->curr_cmd = NULL; } diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index 855019fe5485..7b50a88a18e5 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -6,6 +6,7 @@ */ #include <linux/suspend.h> +#include <net/sock.h> #include "main.h" #include "wmm.h" @@ -54,7 +55,7 @@ const u16 mwifiex_1d_to_wmm_queue[8] = { 1, 0, 0, 1, 2, 2, 3, 3 }; * proper cleanup before exiting. */ static int mwifiex_register(void *card, struct device *dev, - struct mwifiex_if_ops *if_ops, void **padapter) + const struct mwifiex_if_ops *if_ops, void **padapter) { struct mwifiex_adapter *adapter; int i; @@ -307,7 +308,7 @@ process_start: if (IS_CARD_RX_RCVD(adapter)) { adapter->data_received = false; adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); + timer_delete(&adapter->wakeup_timer); if (adapter->ps_state == PS_STATE_SLEEP) adapter->ps_state = PS_STATE_AWAKE; } else { @@ -354,13 +355,6 @@ process_start: if (adapter->cmd_resp_received) { adapter->cmd_resp_received = false; mwifiex_process_cmdresp(adapter); - - /* call mwifiex back when init_fw is done */ - if (adapter->hw_status == MWIFIEX_HW_STATUS_INIT_DONE) { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - mwifiex_init_fw_complete(adapter); - maybe_quirk_fw_disable_ds(adapter); - } } /* Check if we need to confirm Sleep Request @@ -415,10 +409,7 @@ process_start: if (adapter->hs_activated) { clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags); - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); + mwifiex_hs_activated_event(adapter, false); } } @@ -438,10 +429,7 @@ process_start: if (adapter->hs_activated) { clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags); - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); + mwifiex_hs_activated_event(adapter, false); } } @@ -460,10 +448,7 @@ process_start: if (adapter->hs_activated) { clear_bit(MWIFIEX_IS_HS_CONFIGURED, &adapter->work_flags); - mwifiex_hs_activated_event - (mwifiex_get_priv - (adapter, MWIFIEX_BSS_ROLE_ANY), - false); + mwifiex_hs_activated_event(adapter, false); } } @@ -587,21 +572,11 @@ static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context) goto err_dnld_fw; } - adapter->init_wait_q_woken = false; ret = mwifiex_init_fw(adapter); - if (ret == -1) { + if (ret == -1) goto err_init_fw; - } else if (!ret) { - adapter->hw_status = MWIFIEX_HW_STATUS_READY; - goto done; - } - /* Wait for mwifiex_init to complete */ - if (!adapter->mfg_mode) { - wait_event_interruptible(adapter->init_wait_q, - adapter->init_wait_q_woken); - if (adapter->hw_status != MWIFIEX_HW_STATUS_READY) - goto err_init_fw; - } + + maybe_quirk_fw_disable_ds(adapter); if (!adapter->wiphy) { if (mwifiex_register_cfg80211(adapter)) { @@ -691,10 +666,6 @@ err_dnld_fw: init_failed = true; done: - if (adapter->cal_data) { - release_firmware(adapter->cal_data); - adapter->cal_data = NULL; - } if (adapter->firmware) { release_firmware(adapter->firmware); adapter->firmware = NULL; @@ -942,8 +913,7 @@ mwifiex_hard_start_xmit(struct sk_buff *skb, struct net_device *dev) multicast = is_multicast_ether_addr(skb->data); - if (unlikely(!multicast && skb->sk && - skb_shinfo(skb)->tx_flags & SKBTX_WIFI_STATUS && + if (unlikely(!multicast && sk_requests_wifi_status(skb->sk) && priv->adapter->fw_api_ver == MWIFIEX_FW_V15)) skb = mwifiex_clone_skb_for_tx_status(priv, skb, @@ -1562,7 +1532,6 @@ mwifiex_reinit_sw(struct mwifiex_adapter *adapter) adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags); - init_waitqueue_head(&adapter->init_wait_q); clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); adapter->hs_activated = false; clear_bit(MWIFIEX_IS_CMD_TIMEDOUT, &adapter->work_flags); @@ -1713,7 +1682,7 @@ err_exit: */ int mwifiex_add_card(void *card, struct completion *fw_done, - struct mwifiex_if_ops *if_ops, u8 iface_type, + const struct mwifiex_if_ops *if_ops, u8 iface_type, struct device *dev) { struct mwifiex_adapter *adapter; @@ -1730,7 +1699,6 @@ mwifiex_add_card(void *card, struct completion *fw_done, adapter->hw_status = MWIFIEX_HW_STATUS_INITIALIZING; clear_bit(MWIFIEX_SURPRISE_REMOVED, &adapter->work_flags); - init_waitqueue_head(&adapter->init_wait_q); clear_bit(MWIFIEX_IS_SUSPENDED, &adapter->work_flags); adapter->hs_activated = false; init_waitqueue_head(&adapter->hs_activate_wait_q); diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 0674dcf7a537..9ac36bef980e 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -239,7 +239,6 @@ struct mwifiex_dbg { enum MWIFIEX_HARDWARE_STATUS { MWIFIEX_HW_STATUS_READY, MWIFIEX_HW_STATUS_INITIALIZING, - MWIFIEX_HW_STATUS_INIT_DONE, MWIFIEX_HW_STATUS_RESET, MWIFIEX_HW_STATUS_NOT_READY }; @@ -865,8 +864,6 @@ struct mwifiex_adapter { unsigned long work_flags; u32 fw_release_number; u8 intf_hdr_len; - u16 init_wait_q_woken; - wait_queue_head_t init_wait_q; void *card; struct mwifiex_if_ops if_ops; atomic_t bypass_tx_pending; @@ -919,7 +916,6 @@ struct mwifiex_adapter { struct cmd_ctrl_node *curr_cmd; /* spin lock for command */ spinlock_t mwifiex_cmd_lock; - u16 last_init_cmd; struct timer_list cmd_timer; struct list_head cmd_free_q; /* spin lock for cmd_free_q */ @@ -1060,8 +1056,6 @@ void mwifiex_free_priv(struct mwifiex_private *priv); int mwifiex_init_fw(struct mwifiex_adapter *adapter); -int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter); - void mwifiex_shutdown_drv(struct mwifiex_adapter *adapter); int mwifiex_dnld_fw(struct mwifiex_adapter *, struct mwifiex_fw_image *); @@ -1125,7 +1119,7 @@ int mwifiex_ret_enh_power_mode(struct mwifiex_private *priv, struct host_cmd_ds_command *resp, struct mwifiex_ds_pm_cfg *pm_cfg); void mwifiex_process_hs_config(struct mwifiex_adapter *adapter); -void mwifiex_hs_activated_event(struct mwifiex_private *priv, +void mwifiex_hs_activated_event(struct mwifiex_adapter *adapter, u8 activated); int mwifiex_set_hs_params(struct mwifiex_private *priv, u16 action, int cmd_type, struct mwifiex_ds_hs_cfg *hs_cfg); @@ -1156,7 +1150,7 @@ void mwifiex_process_sta_txpd(struct mwifiex_private *priv, struct sk_buff *skb); void mwifiex_process_uap_txpd(struct mwifiex_private *priv, struct sk_buff *skb); -int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta, bool init); +int mwifiex_sta_init_cmd(struct mwifiex_private *, u8 first_sta); int mwifiex_cmd_802_11_scan(struct host_cmd_ds_command *cmd, struct mwifiex_scan_cmd_config *scan_cfg); void mwifiex_queue_scan_cmd(struct mwifiex_private *priv, @@ -1470,7 +1464,7 @@ int mwifiex_init_shutdown_fw(struct mwifiex_private *priv, u32 func_init_shutdown); int mwifiex_add_card(void *card, struct completion *fw_done, - struct mwifiex_if_ops *if_ops, u8 iface_type, + const struct mwifiex_if_ops *if_ops, u8 iface_type, struct device *dev); int mwifiex_remove_card(struct mwifiex_adapter *adapter); @@ -1565,14 +1559,12 @@ int mwifiex_add_wowlan_magic_pkt_filter(struct mwifiex_adapter *adapter); int mwifiex_set_mgmt_ies(struct mwifiex_private *priv, struct cfg80211_beacon_data *data); int mwifiex_del_mgmt_ies(struct mwifiex_private *priv); -u8 *mwifiex_11d_code_2_region(u8 code); +const u8 *mwifiex_11d_code_2_region(u8 code); void mwifiex_uap_set_channel(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_cfg, struct cfg80211_chan_def chandef); int mwifiex_config_start_uap(struct mwifiex_private *priv, struct mwifiex_uap_bss_param *bss_cfg); -void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, - struct mwifiex_sta_node *node); void mwifiex_config_uap_11d(struct mwifiex_private *priv, struct cfg80211_beacon_data *beacon_data); @@ -1600,7 +1592,6 @@ mwifiex_add_sta_entry(struct mwifiex_private *priv, const u8 *mac); struct mwifiex_sta_node * mwifiex_get_sta_entry(struct mwifiex_private *priv, const u8 *mac); u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv); -u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv); u8 mwifiex_is_send_cmd_allowed(struct mwifiex_private *priv); int mwifiex_send_tdls_data_frame(struct mwifiex_private *priv, const u8 *peer, u8 action_code, u8 dialog_token, diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 5f997becdbaa..a760de191fce 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -21,7 +21,7 @@ #define PCIE_VERSION "1.0" #define DRV_NAME "Marvell mwifiex PCIe" -static struct mwifiex_if_ops pcie_ops; +static const struct mwifiex_if_ops pcie_ops; static const struct mwifiex_pcie_card_reg mwifiex_reg_8766 = { .cmd_addr_lo = PCIE_SCRATCH_0_REG, @@ -2437,7 +2437,7 @@ static void mwifiex_interrupt_status(struct mwifiex_adapter *adapter, */ adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); + timer_delete(&adapter->wakeup_timer); } spin_lock_irqsave(&adapter->int_lock, flags); @@ -2527,7 +2527,7 @@ static int mwifiex_process_int_status(struct mwifiex_adapter *adapter) adapter->ps_state == PS_STATE_SLEEP) { adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); + timer_delete(&adapter->wakeup_timer); } } } @@ -2971,7 +2971,7 @@ static int mwifiex_init_pcie(struct mwifiex_adapter *adapter) goto err_iomap2; } - pr_notice("PCI memory map Virt0: %pK PCI memory map Virt2: %pK\n", + pr_notice("PCI memory map Virt0: %p PCI memory map Virt2: %p\n", card->pci_mmap, card->pci_mmap1); ret = mwifiex_pcie_alloc_buffers(adapter); @@ -3240,7 +3240,7 @@ static void mwifiex_pcie_down_dev(struct mwifiex_adapter *adapter) mwifiex_pcie_free_buffers(adapter); } -static struct mwifiex_if_ops pcie_ops = { +static const struct mwifiex_if_ops pcie_ops = { .init_if = mwifiex_init_pcie, .cleanup_if = mwifiex_cleanup_pcie, .check_fw_status = mwifiex_check_fw_status, diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 490ffd981164..c1fe48448839 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -21,7 +21,7 @@ static void mwifiex_sdio_work(struct work_struct *work); -static struct mwifiex_if_ops sdio_ops; +static const struct mwifiex_if_ops sdio_ops; static const struct mwifiex_sdio_card_reg mwifiex_reg_sd87xx = { .start_rd_port = 1, @@ -3167,7 +3167,7 @@ static void mwifiex_sdio_up_dev(struct mwifiex_adapter *adapter) dev_err(&card->func->dev, "error enabling SDIO port\n"); } -static struct mwifiex_if_ops sdio_ops = { +static const struct mwifiex_if_ops sdio_ops = { .init_if = mwifiex_init_sdio, .cleanup_if = mwifiex_cleanup_sdio, .check_fw_status = mwifiex_check_fw_status, diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index e2800a831c8e..c93281f5a47c 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -157,7 +157,7 @@ mwifiex_cmd_802_11_get_log(struct host_cmd_ds_command *cmd) */ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, struct host_cmd_ds_command *cmd, - u16 cmd_action, u16 *pbitmap_rates) + u16 cmd_action, const u16 *pbitmap_rates) { struct host_cmd_ds_tx_rate_cfg *rate_cfg = &cmd->params.tx_rate_cfg; struct mwifiex_rate_scope *rate_scope; @@ -174,34 +174,19 @@ static int mwifiex_cmd_tx_rate_cfg(struct mwifiex_private *priv, rate_scope->type = cpu_to_le16(TLV_TYPE_RATE_SCOPE); rate_scope->length = cpu_to_le16 (sizeof(*rate_scope) - sizeof(struct mwifiex_ie_types_header)); - if (pbitmap_rates != NULL) { - rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); - rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); - for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) - rate_scope->ht_mcs_rate_bitmap[i] = - cpu_to_le16(pbitmap_rates[2 + i]); - if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { - for (i = 0; - i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); - i++) - rate_scope->vht_mcs_rate_bitmap[i] = - cpu_to_le16(pbitmap_rates[10 + i]); - } - } else { - rate_scope->hr_dsss_rate_bitmap = - cpu_to_le16(priv->bitmap_rates[0]); - rate_scope->ofdm_rate_bitmap = - cpu_to_le16(priv->bitmap_rates[1]); - for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) - rate_scope->ht_mcs_rate_bitmap[i] = - cpu_to_le16(priv->bitmap_rates[2 + i]); - if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { - for (i = 0; - i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); - i++) - rate_scope->vht_mcs_rate_bitmap[i] = - cpu_to_le16(priv->bitmap_rates[10 + i]); - } + if (!pbitmap_rates) + pbitmap_rates = priv->bitmap_rates; + + rate_scope->hr_dsss_rate_bitmap = cpu_to_le16(pbitmap_rates[0]); + rate_scope->ofdm_rate_bitmap = cpu_to_le16(pbitmap_rates[1]); + + for (i = 0; i < ARRAY_SIZE(rate_scope->ht_mcs_rate_bitmap); i++) + rate_scope->ht_mcs_rate_bitmap[i] = cpu_to_le16(pbitmap_rates[2 + i]); + + if (priv->adapter->fw_api_ver == MWIFIEX_FW_V15) { + for (i = 0; i < ARRAY_SIZE(rate_scope->vht_mcs_rate_bitmap); i++) + rate_scope->vht_mcs_rate_bitmap[i] = + cpu_to_le16(pbitmap_rates[10 + i]); } rate_drop = (struct mwifiex_rate_drop_pattern *) ((u8 *) rate_scope + @@ -1507,6 +1492,7 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, u32 len; u8 *data = (u8 *)cmd + S_DS_GEN; int ret; + struct host_cmd_ds_802_11_cfg_data *pcfg_data; if (prop) { len = prop->length; @@ -1514,12 +1500,20 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, data, len); if (ret) return ret; + + cmd->size = cpu_to_le16(S_DS_GEN + len); mwifiex_dbg(adapter, INFO, "download cfg_data from device tree: %s\n", prop->name); } else if (adapter->cal_data->data && adapter->cal_data->size > 0) { len = mwifiex_parse_cal_cfg((u8 *)adapter->cal_data->data, - adapter->cal_data->size, data); + adapter->cal_data->size, + data + sizeof(*pcfg_data)); + pcfg_data = &cmd->params.cfg_data; + pcfg_data->action = cpu_to_le16(HOST_CMD_ACT_GEN_SET); + pcfg_data->type = cpu_to_le16(MWIFIEX_CFG_TYPE_CAL); + pcfg_data->data_len = cpu_to_le16(len); + cmd->size = cpu_to_le16(S_DS_GEN + sizeof(*pcfg_data) + len); mwifiex_dbg(adapter, INFO, "download cfg_data from config file\n"); } else { @@ -1527,7 +1521,6 @@ static int mwifiex_cmd_cfg_data(struct mwifiex_private *priv, } cmd->command = cpu_to_le16(HostCmd_CMD_CFG_DATA); - cmd->size = cpu_to_le16(S_DS_GEN + len); return 0; } @@ -2250,7 +2243,7 @@ int mwifiex_sta_prepare_cmd(struct mwifiex_private *priv, uint16_t cmd_no, * - Set 11d control * - Set MAC control (this must be the last command to initialize firmware) */ -int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) +int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta) { struct mwifiex_adapter *adapter = priv->adapter; int ret; @@ -2293,9 +2286,13 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) "marvell,caldata"); } - if (adapter->cal_data) + if (adapter->cal_data) { mwifiex_send_cmd(priv, HostCmd_CMD_CFG_DATA, HostCmd_ACT_GEN_SET, 0, NULL, true); + release_firmware(adapter->cal_data); + adapter->cal_data = NULL; + } + /* Read MAC address from HW */ ret = mwifiex_send_cmd(priv, HostCmd_CMD_GET_HW_SPEC, @@ -2421,11 +2418,5 @@ int mwifiex_sta_init_cmd(struct mwifiex_private *priv, u8 first_sta, bool init) ret = mwifiex_send_cmd(priv, HostCmd_CMD_11N_CFG, HostCmd_ACT_GEN_SET, 0, &tx_cfg, true); - if (init) { - /* set last_init_cmd before sending the command */ - priv->adapter->last_init_cmd = HostCmd_CMD_11N_CFG; - ret = -EINPROGRESS; - } - return ret; } diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c index 400348abeee5..fecd88967ceb 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_event.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c @@ -789,7 +789,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); + timer_delete(&adapter->wakeup_timer); break; } if (!mwifiex_send_null_packet @@ -804,7 +804,7 @@ int mwifiex_process_sta_event(struct mwifiex_private *priv) adapter->ps_state = PS_STATE_AWAKE; adapter->pm_wakeup_card_req = false; adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); + timer_delete(&adapter->wakeup_timer); break; diff --git a/drivers/net/wireless/marvell/mwifiex/tdls.c b/drivers/net/wireless/marvell/mwifiex/tdls.c index 0a5f340876c3..18e8c04d14c4 100644 --- a/drivers/net/wireless/marvell/mwifiex/tdls.c +++ b/drivers/net/wireless/marvell/mwifiex/tdls.c @@ -1490,7 +1490,7 @@ void mwifiex_clean_auto_tdls(struct mwifiex_private *priv) priv->adapter->auto_tdls && priv->bss_type == MWIFIEX_BSS_TYPE_STA) { priv->auto_tdls_timer_active = false; - del_timer(&priv->auto_tdls_timer); + timer_delete(&priv->auto_tdls_timer); mwifiex_flush_auto_tdls_list(priv); } } diff --git a/drivers/net/wireless/marvell/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c index bd91678d26b4..f44e22f24511 100644 --- a/drivers/net/wireless/marvell/mwifiex/txrx.c +++ b/drivers/net/wireless/marvell/mwifiex/txrx.c @@ -24,8 +24,7 @@ int mwifiex_handle_rx_packet(struct mwifiex_adapter *adapter, struct sk_buff *skb) { - struct mwifiex_private *priv = - mwifiex_get_priv(adapter, MWIFIEX_BSS_ROLE_ANY); + struct mwifiex_private *priv; struct rxpd *local_rx_pd; struct mwifiex_rxinfo *rx_info = MWIFIEX_SKB_RXCB(skb); int ret; diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c index 58ef5020a46a..245cb99a3daa 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_event.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -325,19 +325,3 @@ int mwifiex_process_uap_event(struct mwifiex_private *priv) return 0; } - -/* This function deletes station entry from associated station list. - * Also if both AP and STA are 11n enabled, RxReorder tables and TxBA stream - * tables created for this station are deleted. - */ -void mwifiex_uap_del_sta_data(struct mwifiex_private *priv, - struct mwifiex_sta_node *node) -{ - if (priv->ap_11n_enabled && node->is_11n_enabled) { - mwifiex_11n_del_rx_reorder_tbl_by_ta(priv, node->mac_addr); - mwifiex_del_tx_ba_stream_tbl_by_ra(priv, node->mac_addr); - } - mwifiex_del_sta_entry(priv, node->mac_addr); - - return; -} diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 6085cd50970d..2f565397cf36 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -10,7 +10,7 @@ #define USB_VERSION "1.0" -static struct mwifiex_if_ops usb_ops; +static const struct mwifiex_if_ops usb_ops; static const struct usb_device_id mwifiex_usb_table[] = { /* 8766 */ @@ -877,7 +877,7 @@ static int mwifiex_usb_prepare_tx_aggr_skb(struct mwifiex_adapter *adapter, * write complete, delete the tx_aggr timer */ if (port->tx_aggr.timer_cnxt.is_hold_timer_set) { - del_timer(&port->tx_aggr.timer_cnxt.hold_timer); + timer_delete(&port->tx_aggr.timer_cnxt.hold_timer); port->tx_aggr.timer_cnxt.is_hold_timer_set = false; port->tx_aggr.timer_cnxt.hold_tmo_msecs = 0; } @@ -1354,7 +1354,7 @@ static void mwifiex_usb_cleanup_tx_aggr(struct mwifiex_adapter *adapter) mwifiex_write_data_complete(adapter, skb_tmp, 0, -1); if (port->tx_aggr.timer_cnxt.hold_timer.function) - del_timer_sync(&port->tx_aggr.timer_cnxt.hold_timer); + timer_delete_sync(&port->tx_aggr.timer_cnxt.hold_timer); port->tx_aggr.timer_cnxt.is_hold_timer_set = false; port->tx_aggr.timer_cnxt.hold_tmo_msecs = 0; } @@ -1557,7 +1557,7 @@ static int mwifiex_pm_wakeup_card(struct mwifiex_adapter *adapter) { /* Simulation of HS_AWAKE event */ adapter->pm_wakeup_fw_try = false; - del_timer(&adapter->wakeup_timer); + timer_delete(&adapter->wakeup_timer); adapter->pm_wakeup_card_req = false; adapter->ps_state = PS_STATE_AWAKE; @@ -1585,7 +1585,7 @@ mwifiex_pm_wakeup_card_complete(struct mwifiex_adapter *adapter) return 0; } -static struct mwifiex_if_ops usb_ops = { +static const struct mwifiex_if_ops usb_ops = { .register_dev = mwifiex_register_dev, .unregister_dev = mwifiex_unregister_dev, .wakeup = mwifiex_pm_wakeup_card, diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c index 1f1f6280a0f2..4c5b1de0e936 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.c +++ b/drivers/net/wireless/marvell/mwifiex/util.c @@ -116,24 +116,6 @@ static struct mwifiex_debug_data items[] = { static int num_of_items = ARRAY_SIZE(items); /* - * Firmware initialization complete callback handler. - * - * This function wakes up the function waiting on the init - * wait queue for the firmware initialization to complete. - */ -int mwifiex_init_fw_complete(struct mwifiex_adapter *adapter) -{ - - if (adapter->hw_status == MWIFIEX_HW_STATUS_READY) - if (adapter->if_ops.init_fw_port) - adapter->if_ops.init_fw_port(adapter); - - adapter->init_wait_q_woken = true; - wake_up_interruptible(&adapter->init_wait_q); - return 0; -} - -/* * This function sends init/shutdown command * to firmware. */ @@ -663,7 +645,7 @@ u8 mwifiex_is_tdls_chan_switching(struct mwifiex_private *priv) return false; } -u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv) +static u8 mwifiex_is_tdls_off_chan(struct mwifiex_private *priv) { struct mwifiex_sta_node *sta_ptr; diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index bcb61dab7dc8..1b1222c73728 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -1428,13 +1428,13 @@ mwifiex_dequeue_tx_packet(struct mwifiex_adapter *adapter) } if (!ptr->is_11n_enabled || - ptr->ba_status || - priv->wps.session_enable) { + ptr->ba_status || + priv->wps.session_enable) { if (ptr->is_11n_enabled && - ptr->ba_status && - ptr->amsdu_in_ampdu && - mwifiex_is_amsdu_allowed(priv, tid) && - mwifiex_is_11n_aggragation_possible(priv, ptr, + ptr->ba_status && + ptr->amsdu_in_ampdu && + mwifiex_is_amsdu_allowed(priv, tid) && + mwifiex_is_11n_aggragation_possible(priv, ptr, adapter->tx_buf_size)) mwifiex_11n_aggregate_pkt(priv, ptr, ptr_index); /* ra_list_spinlock has been freed in diff --git a/drivers/net/wireless/mediatek/mt76/channel.c b/drivers/net/wireless/mediatek/mt76/channel.c index 6a35c6ebd823..cc2d888e3f17 100644 --- a/drivers/net/wireless/mediatek/mt76/channel.c +++ b/drivers/net/wireless/mediatek/mt76/channel.c @@ -293,6 +293,7 @@ struct mt76_vif_link *mt76_get_vif_phy_link(struct mt76_phy *phy, kfree(mlink); return ERR_PTR(ret); } + rcu_assign_pointer(mvif->offchannel_link, mlink); return mlink; } @@ -301,10 +302,14 @@ void mt76_put_vif_phy_link(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink) { struct mt76_dev *dev = phy->dev; + struct mt76_vif_data *mvif; if (IS_ERR_OR_NULL(mlink) || !mlink->offchannel) return; + mvif = mlink->mvif; + + rcu_assign_pointer(mvif->offchannel_link, NULL); dev->drv->vif_link_remove(phy, vif, &vif->bss_conf, mlink); kfree(mlink); } diff --git a/drivers/net/wireless/mediatek/mt76/dma.c b/drivers/net/wireless/mediatek/mt76/dma.c index 844af16ee551..35b4ec91979e 100644 --- a/drivers/net/wireless/mediatek/mt76/dma.c +++ b/drivers/net/wireless/mediatek/mt76/dma.c @@ -1011,6 +1011,7 @@ void mt76_dma_cleanup(struct mt76_dev *dev) int i; mt76_worker_disable(&dev->tx_worker); + napi_disable(&dev->tx_napi); netif_napi_del(&dev->tx_napi); for (i = 0; i < ARRAY_SIZE(dev->phys); i++) { diff --git a/drivers/net/wireless/mediatek/mt76/eeprom.c b/drivers/net/wireless/mediatek/mt76/eeprom.c index 0bc66cc19acd..443517d06c9f 100644 --- a/drivers/net/wireless/mediatek/mt76/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/eeprom.c @@ -95,6 +95,10 @@ int mt76_get_of_data_from_mtd(struct mt76_dev *dev, void *eep, int offset, int l #ifdef CONFIG_NL80211_TESTMODE dev->test_mtd.name = devm_kstrdup(dev->dev, part, GFP_KERNEL); + if (!dev->test_mtd.name) { + ret = -ENOMEM; + goto out_put_node; + } dev->test_mtd.offset = offset; #endif diff --git a/drivers/net/wireless/mediatek/mt76/mac80211.c b/drivers/net/wireless/mediatek/mt76/mac80211.c index 508b472408c2..45c8db939d55 100644 --- a/drivers/net/wireless/mediatek/mt76/mac80211.c +++ b/drivers/net/wireless/mediatek/mt76/mac80211.c @@ -449,8 +449,10 @@ mt76_phy_init(struct mt76_phy *phy, struct ieee80211_hw *hw) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AIRTIME_FAIRNESS); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_AQL); - wiphy->available_antennas_tx = phy->antenna_mask; - wiphy->available_antennas_rx = phy->antenna_mask; + if (!wiphy->available_antennas_tx) + wiphy->available_antennas_tx = phy->antenna_mask; + if (!wiphy->available_antennas_rx) + wiphy->available_antennas_rx = phy->antenna_mask; wiphy->sar_capa = &mt76_sar_capa; phy->frp = devm_kcalloc(dev->dev, wiphy->sar_capa->num_freq_ranges, @@ -816,8 +818,8 @@ void mt76_free_device(struct mt76_dev *dev) } EXPORT_SYMBOL_GPL(mt76_free_device); -static struct mt76_phy * -mt76_vif_phy(struct ieee80211_hw *hw, struct ieee80211_vif *vif) +struct mt76_phy *mt76_vif_phy(struct ieee80211_hw *hw, + struct ieee80211_vif *vif) { struct mt76_vif_link *mlink = (struct mt76_vif_link *)vif->drv_priv; struct mt76_chanctx *ctx; @@ -831,6 +833,7 @@ mt76_vif_phy(struct ieee80211_hw *hw, struct ieee80211_vif *vif) ctx = (struct mt76_chanctx *)mlink->ctx->drv_priv; return ctx->phy; } +EXPORT_SYMBOL_GPL(mt76_vif_phy); static void mt76_rx_release_amsdu(struct mt76_phy *phy, enum mt76_rxq_id q) { @@ -1697,6 +1700,17 @@ void mt76_wcid_add_poll(struct mt76_dev *dev, struct mt76_wcid *wcid) } EXPORT_SYMBOL_GPL(mt76_wcid_add_poll); +s8 mt76_get_power_bound(struct mt76_phy *phy, s8 txpower) +{ + int n_chains = hweight16(phy->chainmask); + + txpower = mt76_get_sar_power(phy, phy->chandef.chan, txpower * 2); + txpower -= mt76_tx_power_path_delta(n_chains); + + return txpower; +} +EXPORT_SYMBOL_GPL(mt76_get_power_bound); + int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned int link_id, int *dbm) { @@ -1707,7 +1721,7 @@ int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return -EINVAL; n_chains = hweight16(phy->chainmask); - delta = mt76_tx_power_nss_delta(n_chains); + delta = mt76_tx_power_path_delta(n_chains); *dbm = DIV_ROUND_UP(phy->txpower_cur + delta, 2); return 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt76.h b/drivers/net/wireless/mediatek/mt76/mt76.h index 132148f7b107..5f8d81cda6cd 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76.h +++ b/drivers/net/wireless/mediatek/mt76/mt76.h @@ -162,6 +162,16 @@ enum mt76_dfs_state { MT_DFS_STATE_ACTIVE, }; +#define MT76_RNR_SCAN_MAX_BSSIDS 16 +struct mt76_scan_rnr_param { + u8 bssid[MT76_RNR_SCAN_MAX_BSSIDS][ETH_ALEN]; + u8 channel[MT76_RNR_SCAN_MAX_BSSIDS]; + u8 random_mac[ETH_ALEN]; + u8 seq_num; + u8 bssid_num; + u32 sreq_flag; +}; + struct mt76_queue_buf { dma_addr_t addr; u16 len:15, @@ -351,6 +361,7 @@ struct mt76_wcid { u8 hw_key_idx; u8 hw_key_idx2; + u8 offchannel:1; u8 sta:1; u8 sta_disabled:1; u8 amsdu:1; @@ -491,6 +502,7 @@ struct mt76_hw_cap { #define MT_DRV_RX_DMA_HDR BIT(3) #define MT_DRV_HW_MGMT_TXQ BIT(4) #define MT_DRV_AMSDU_OFFLOAD BIT(5) +#define MT_DRV_IGNORE_TXS_FAILED BIT(6) struct mt76_driver_ops { u32 drv_flags; @@ -769,6 +781,7 @@ struct mt76_testmode_data { struct mt76_vif_link { u8 idx; + u8 link_idx; u8 omac_idx; u8 band_idx; u8 wmm_idx; @@ -786,6 +799,7 @@ struct mt76_vif_link { struct mt76_vif_data { struct mt76_vif_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + struct mt76_vif_link __rcu *offchannel_link; struct mt76_phy *roc_phy; u16 valid_links; @@ -937,6 +951,8 @@ struct mt76_dev { char alpha2[3]; enum nl80211_dfs_regions region; + struct mt76_scan_rnr_param rnr; + u32 debugfs_reg; u8 csa_complete; @@ -1224,6 +1240,8 @@ struct mt76_phy *mt76_alloc_phy(struct mt76_dev *dev, unsigned int size, u8 band_idx); int mt76_register_phy(struct mt76_phy *phy, bool vht, struct ieee80211_rate *rates, int n_rates); +struct mt76_phy *mt76_vif_phy(struct ieee80211_hw *hw, + struct ieee80211_vif *vif); struct dentry *mt76_register_debugfs_fops(struct mt76_phy *phy, const struct file_operations *ops); @@ -1380,12 +1398,12 @@ static inline bool mt76_is_skb_pktid(u8 pktid) return pktid >= MT_PACKET_ID_FIRST; } -static inline u8 mt76_tx_power_nss_delta(u8 nss) +static inline u8 mt76_tx_power_path_delta(u8 path) { - static const u8 nss_delta[4] = { 0, 6, 9, 12 }; - u8 idx = nss - 1; + static const u8 path_delta[5] = { 0, 6, 9, 12, 14 }; + u8 idx = path - 1; - return (idx < ARRAY_SIZE(nss_delta)) ? nss_delta[idx] : 0; + return (idx < ARRAY_SIZE(path_delta)) ? path_delta[idx] : 0; } static inline bool mt76_testmode_enabled(struct mt76_phy *phy) @@ -1482,6 +1500,8 @@ void mt76_sta_pre_rcu_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif, int mt76_get_min_avg_rssi(struct mt76_dev *dev, u8 phy_idx); +s8 mt76_get_power_bound(struct mt76_phy *phy, s8 txpower); + int mt76_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, unsigned int link_id, int *dbm); int mt76_init_sar_power(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/init.c b/drivers/net/wireless/mediatek/mt76/mt7615/init.c index 66ba3be27343..aae80005a3c1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/init.c @@ -273,7 +273,7 @@ void mt7615_init_txpower(struct mt7615_dev *dev, struct ieee80211_supported_band *sband) { int i, n_chains = hweight8(dev->mphy.antenna_mask), target_chains; - int delta_idx, delta = mt76_tx_power_nss_delta(n_chains); + int delta_idx, delta = mt76_tx_power_path_delta(n_chains); u8 *eep = (u8 *)dev->mt76.eeprom.data; enum nl80211_band band = sband->band; struct mt76_power_limits limits; diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/main.c b/drivers/net/wireless/mediatek/mt76/mt7615/main.c index 2e7b05eeef7a..c54005df08ca 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/main.c @@ -97,7 +97,7 @@ static void mt7615_stop(struct ieee80211_hw *hw, bool suspend) struct mt7615_phy *phy = mt7615_hw_phy(hw); cancel_delayed_work_sync(&phy->mt76->mac_work); - del_timer_sync(&phy->roc_timer); + timer_delete_sync(&phy->roc_timer); cancel_work_sync(&phy->roc_work); cancel_delayed_work_sync(&dev->pm.ps_work); @@ -1194,7 +1194,7 @@ static int mt7615_cancel_remain_on_channel(struct ieee80211_hw *hw, if (!test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) return 0; - del_timer_sync(&phy->roc_timer); + timer_delete_sync(&phy->roc_timer); cancel_work_sync(&phy->roc_work); mt7615_mutex_acquire(phy->dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c index b8fcd4eb3fbb..4064e193d4de 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/mcu.c @@ -2067,7 +2067,7 @@ static void mt7615_mcu_set_txpower_sku(struct mt7615_phy *phy, u8 *sku) }; tx_power = mt76_get_sar_power(mphy, mphy->chandef.chan, tx_power); - tx_power -= mt76_tx_power_nss_delta(n_chains); + tx_power -= mt76_tx_power_path_delta(n_chains); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &limits, tx_power); mphy->txpower_cur = tx_power; @@ -2084,8 +2084,8 @@ static void mt7615_mcu_set_txpower_sku(struct mt7615_phy *phy, u8 *sku) int delta = 0; if (i < n_chains - 1) - delta = mt76_tx_power_nss_delta(n_chains) - - mt76_tx_power_nss_delta(i + 1); + delta = mt76_tx_power_path_delta(n_chains) - + mt76_tx_power_path_delta(i + 1); sku[MT_SKU_1SS_DELTA + i] = delta; } } diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c index c2e4e6aabd9f..b795d11d943d 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/pci_mac.c @@ -220,12 +220,12 @@ void mt7615_mac_reset_work(struct work_struct *work) set_bit(MT76_MCU_RESET, &dev->mphy.state); wake_up(&dev->mt76.mcu.wait); cancel_delayed_work_sync(&dev->mphy.mac_work); - del_timer_sync(&dev->phy.roc_timer); + timer_delete_sync(&dev->phy.roc_timer); cancel_work_sync(&dev->phy.roc_work); if (phy2) { set_bit(MT76_RESET, &phy2->mt76->state); cancel_delayed_work_sync(&phy2->mt76->mac_work); - del_timer_sync(&phy2->roc_timer); + timer_delete_sync(&phy2->roc_timer); cancel_work_sync(&phy2->roc_work); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c index 4aa9fa1c4a23..d96e06b4fee1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7615/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt7615/usb.c @@ -85,7 +85,7 @@ static void mt7663u_stop(struct ieee80211_hw *hw, bool suspend) struct mt7615_dev *dev = hw->priv; clear_bit(MT76_STATE_RUNNING, &dev->mphy.state); - del_timer_sync(&phy->roc_timer); + timer_delete_sync(&phy->roc_timer); cancel_work_sync(&phy->roc_work); cancel_delayed_work_sync(&phy->scan_work); cancel_delayed_work_sync(&phy->mt76->mac_work); diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac.h index 455979476d11..192dcc374a64 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac.h @@ -232,9 +232,14 @@ static inline bool is_mt7992(struct mt76_dev *dev) return mt76_chip(dev) == 0x7992; } +static inline bool is_mt7990(struct mt76_dev *dev) +{ + return mt76_chip(dev) == 0x7993; +} + static inline bool is_mt799x(struct mt76_dev *dev) { - return is_mt7996(dev) || is_mt7992(dev); + return is_mt7996(dev) || is_mt7992(dev) || is_mt7990(dev); } static inline bool is_mt7622(struct mt76_dev *dev) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h index db0c29e65185..1013cad57a7f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac3_mac.h @@ -273,6 +273,7 @@ enum tx_frag_idx { #define MT_TXD6_TX_RATE GENMASK(21, 16) #define MT_TXD6_TIMESTAMP_OFS_EN BIT(15) #define MT_TXD6_TIMESTAMP_OFS_IDX GENMASK(14, 10) +#define MT_TXD6_TID_ADDBA GENMASK(10, 8) #define MT_TXD6_MSDU_CNT GENMASK(9, 4) #define MT_TXD6_MSDU_CNT_V2 GENMASK(15, 10) #define MT_TXD6_DIS_MAT BIT(3) @@ -314,6 +315,9 @@ enum tx_frag_idx { #define MT_TXFREE_INFO_COUNT GENMASK(27, 24) #define MT_TXFREE_INFO_STAT GENMASK(29, 28) +#define MT_TXS_HDR_SIZE 4 /* Unit: DW */ +#define MT_TXS_SIZE 12 /* Unit: DW */ + #define MT_TXS0_BW GENMASK(31, 29) #define MT_TXS0_TID GENMASK(28, 26) #define MT_TXS0_AMPDU BIT(25) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c index f30cf9e71610..cb13d0a76878 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.c @@ -67,8 +67,7 @@ int mt76_connac_mcu_init_download(struct mt76_dev *dev, u32 addr, u32 len, if ((!is_connac_v1(dev) && addr == MCU_PATCH_ADDRESS) || (is_mt7921(dev) && addr == 0x900000) || (is_mt7925(dev) && (addr == 0x900000 || addr == 0xe0002800)) || - (is_mt7996(dev) && addr == 0x900000) || - (is_mt7992(dev) && addr == 0x900000)) + (is_mt799x(dev) && addr == 0x900000)) cmd = MCU_CMD(PATCH_START_REQ); else cmd = MCU_CMD(TARGET_ADDRESS_LEN_REQ); @@ -1168,7 +1167,7 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, .tag = cpu_to_le16(DEV_INFO_ACTIVE), .len = cpu_to_le16(sizeof(struct req_tlv)), .active = enable, - .link_idx = mvif->idx, + .link_idx = mvif->link_idx, }, }; struct { @@ -1191,7 +1190,7 @@ int mt76_connac_mcu_uni_add_dev(struct mt76_phy *phy, .bmc_tx_wlan_idx = cpu_to_le16(wcid->idx), .sta_idx = cpu_to_le16(wcid->idx), .conn_state = 1, - .link_idx = mvif->idx, + .link_idx = mvif->link_idx, }, }; int err, idx, cmd, len; @@ -1667,6 +1666,44 @@ int mt76_connac_mcu_uni_add_bss(struct mt76_phy *phy, } EXPORT_SYMBOL_GPL(mt76_connac_mcu_uni_add_bss); +void mt76_connac_mcu_build_rnr_scan_param(struct mt76_dev *mdev, + struct cfg80211_scan_request *sreq) +{ + struct ieee80211_channel **scan_list = sreq->channels; + int i, bssid_index = 0; + + /* clear 6G active Scan BSSID table */ + memset(&mdev->rnr, 0, sizeof(mdev->rnr)); + + for (i = 0; i < sreq->n_6ghz_params; i++) { + u8 ch_idx = sreq->scan_6ghz_params[i].channel_idx; + int k = 0; + + /* Remove the duplicated BSSID */ + for (k = 0; k < bssid_index; k++) { + if (!memcmp(&mdev->rnr.bssid[k], + sreq->scan_6ghz_params[i].bssid, + ETH_ALEN)) + break; + } + + if (k == bssid_index && + bssid_index < MT76_RNR_SCAN_MAX_BSSIDS) { + memcpy(&mdev->rnr.bssid[bssid_index++], + sreq->scan_6ghz_params[i].bssid, ETH_ALEN); + mdev->rnr.channel[k] = scan_list[ch_idx]->hw_value; + } + } + + mdev->rnr.bssid_num = bssid_index; + + if (sreq->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) { + memcpy(mdev->rnr.random_mac, sreq->mac_addr, ETH_ALEN); + mdev->rnr.sreq_flag = sreq->flags; + } +} +EXPORT_SYMBOL_GPL(mt76_connac_mcu_build_rnr_scan_param); + #define MT76_CONNAC_SCAN_CHANNEL_TIME 60 int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct ieee80211_scan_request *scan_req) diff --git a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h index 43237e518373..27daf419560a 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt76_connac_mcu.h @@ -869,6 +869,7 @@ enum { #define NETWORK_WDS BIT(21) #define SCAN_FUNC_RANDOM_MAC BIT(0) +#define SCAN_FUNC_RNR_SCAN BIT(3) #define SCAN_FUNC_SPLIT_SCAN BIT(5) #define CONNECTION_INFRA_STA (STA_TYPE_STA | NETWORK_INFRA) @@ -1065,6 +1066,7 @@ enum { MCU_UNI_EVENT_WED_RRO = 0x57, MCU_UNI_EVENT_PER_STA_INFO = 0x6d, MCU_UNI_EVENT_ALL_STA_INFO = 0x6e, + MCU_UNI_EVENT_SDO = 0x83, }; #define MCU_UNI_CMD_EVENT BIT(1) @@ -1182,6 +1184,11 @@ enum { #define MCU_UNI_CMD(_t) (__MCU_CMD_FIELD_UNI | \ FIELD_PREP(__MCU_CMD_FIELD_ID, \ MCU_UNI_CMD_##_t)) + +#define MCU_UNI_QUERY(_t) (__MCU_CMD_FIELD_UNI | __MCU_CMD_FIELD_QUERY | \ + FIELD_PREP(__MCU_CMD_FIELD_ID, \ + MCU_UNI_CMD_##_t)) + #define MCU_CE_CMD(_t) (__MCU_CMD_FIELD_CE | \ FIELD_PREP(__MCU_CMD_FIELD_ID, \ MCU_CE_CMD_##_t)) @@ -1287,16 +1294,20 @@ enum { MCU_UNI_CMD_EFUSE_CTRL = 0x2d, MCU_UNI_CMD_RA = 0x2f, MCU_UNI_CMD_MURU = 0x31, + MCU_UNI_CMD_TESTMODE_RX_STAT = 0x32, MCU_UNI_CMD_BF = 0x33, MCU_UNI_CMD_CHANNEL_SWITCH = 0x34, MCU_UNI_CMD_THERMAL = 0x35, MCU_UNI_CMD_VOW = 0x37, MCU_UNI_CMD_FIXED_RATE_TABLE = 0x40, + MCU_UNI_CMD_TESTMODE_CTRL = 0x46, MCU_UNI_CMD_RRO = 0x57, MCU_UNI_CMD_OFFCH_SCAN_CTRL = 0x58, MCU_UNI_CMD_PER_STA_INFO = 0x6d, MCU_UNI_CMD_ALL_STA_INFO = 0x6e, MCU_UNI_CMD_ASSERT_DUMP = 0x6f, + MCU_UNI_CMD_RADIO_STATUS = 0x80, + MCU_UNI_CMD_SDO = 0x88, }; enum { @@ -1369,6 +1380,7 @@ enum { UNI_BSS_INFO_OFFLOAD = 25, UNI_BSS_INFO_MLD = 26, UNI_BSS_INFO_PM_DISABLE = 27, + UNI_BSS_INFO_EHT = 30, }; enum { @@ -1969,6 +1981,8 @@ int mt76_connac_mcu_start_patch(struct mt76_dev *dev); int mt76_connac_mcu_patch_sem_ctrl(struct mt76_dev *dev, bool get); int mt76_connac_mcu_start_firmware(struct mt76_dev *dev, u32 addr, u32 option); +void mt76_connac_mcu_build_rnr_scan_param(struct mt76_dev *mdev, + struct cfg80211_scan_request *sreq); int mt76_connac_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct ieee80211_scan_request *scan_req); int mt76_connac_mcu_cancel_hw_scan(struct mt76_phy *phy, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c index b456ccd00d58..11c16d1fc70f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/pci.c @@ -156,7 +156,8 @@ mt76x0e_probe(struct pci_dev *pdev, const struct pci_device_id *id) static const struct mt76_driver_ops drv_ops = { .txwi_size = sizeof(struct mt76x02_txwi), .drv_flags = MT_DRV_TX_ALIGNED4_SKBS | - MT_DRV_SW_RX_AIRTIME, + MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, .survey_flags = SURVEY_INFO_TIME_TX, .update_survey = mt76x02_update_channel, .set_channel = mt76x0_set_channel, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c index b031c500b741..90e5666c0857 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x0/usb.c @@ -214,7 +214,8 @@ static int mt76x0u_probe(struct usb_interface *usb_intf, const struct usb_device_id *id) { static const struct mt76_driver_ops drv_ops = { - .drv_flags = MT_DRV_SW_RX_AIRTIME, + .drv_flags = MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, .survey_flags = SURVEY_INFO_TIME_TX, .update_survey = mt76x02_update_channel, .set_channel = mt76x0_set_channel, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c index a82c75ba26e6..a683d53c7ceb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_mmio.c @@ -174,7 +174,6 @@ static int mt76x02_poll_tx(struct napi_struct *napi, int budget) int mt76x02_dma_init(struct mt76x02_dev *dev) { - struct mt76_txwi_cache __maybe_unused *t; int i, ret, fifo_size; struct mt76_queue *q; void *status_fifo; diff --git a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c index 0e1ede9314d8..4840d0b500b3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x02_usb_core.c @@ -264,8 +264,8 @@ void mt76x02u_init_beacon_config(struct mt76x02_dev *dev) }; dev->beacon_ops = &beacon_ops; - hrtimer_init(&dev->pre_tbtt_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); - dev->pre_tbtt_timer.function = mt76x02u_pre_tbtt_interrupt; + hrtimer_setup(&dev->pre_tbtt_timer, mt76x02u_pre_tbtt_interrupt, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); INIT_WORK(&dev->pre_tbtt_work, mt76x02u_pre_tbtt_work); mt76x02_init_beacon_config(dev); diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c index 727bfdd00b40..2303019670e2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/pci.c @@ -22,7 +22,8 @@ mt76x2e_probe(struct pci_dev *pdev, const struct pci_device_id *id) static const struct mt76_driver_ops drv_ops = { .txwi_size = sizeof(struct mt76x02_txwi), .drv_flags = MT_DRV_TX_ALIGNED4_SKBS | - MT_DRV_SW_RX_AIRTIME, + MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, .survey_flags = SURVEY_INFO_TIME_TX, .update_survey = mt76x02_update_channel, .set_channel = mt76x2e_set_channel, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c index e832ad53e239..96cecc576a98 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb.c @@ -17,11 +17,14 @@ static const struct usb_device_id mt76x2u_device_table[] = { { USB_DEVICE(0x057c, 0x8503) }, /* Avm FRITZ!WLAN AC860 */ { USB_DEVICE(0x7392, 0xb711) }, /* Edimax EW 7722 UAC */ { USB_DEVICE(0x0e8d, 0x7632) }, /* HC-M7662BU1 */ + { USB_DEVICE(0x0471, 0x2126) }, /* LiteOn WN4516R module, nonstandard USB connector */ + { USB_DEVICE(0x0471, 0x7600) }, /* LiteOn WN4519R module, nonstandard USB connector */ { USB_DEVICE(0x2c4e, 0x0103) }, /* Mercury UD13 */ { USB_DEVICE(0x0846, 0x9014) }, /* Netgear WNDA3100v3 */ { USB_DEVICE(0x0846, 0x9053) }, /* Netgear A6210 */ { USB_DEVICE(0x045e, 0x02e6) }, /* XBox One Wireless Adapter */ { USB_DEVICE(0x045e, 0x02fe) }, /* XBox One Wireless Adapter */ + { USB_DEVICE(0x2357, 0x0137) }, /* TP-Link TL-WDN6200 */ { }, }; @@ -29,7 +32,8 @@ static int mt76x2u_probe(struct usb_interface *intf, const struct usb_device_id *id) { static const struct mt76_driver_ops drv_ops = { - .drv_flags = MT_DRV_SW_RX_AIRTIME, + .drv_flags = MT_DRV_SW_RX_AIRTIME | + MT_DRV_IGNORE_TXS_FAILED, .survey_flags = SURVEY_INFO_TIME_TX, .update_survey = mt76x02_update_channel, .set_channel = mt76x2u_set_channel, diff --git a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c index 33a14365ec9b..3b5562811511 100644 --- a/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c +++ b/drivers/net/wireless/mediatek/mt76/mt76x2/usb_init.c @@ -191,6 +191,7 @@ int mt76x2u_register_device(struct mt76x02_dev *dev) { struct ieee80211_hw *hw = mt76_hw(dev); struct mt76_usb *usb = &dev->mt76.usb; + bool vht; int err; INIT_DELAYED_WORK(&dev->cal_work, mt76x2u_phy_calibrate); @@ -217,7 +218,17 @@ int mt76x2u_register_device(struct mt76x02_dev *dev) /* check hw sg support in order to enable AMSDU */ hw->max_tx_fragments = dev->mt76.usb.sg_en ? MT_TX_SG_MAX_SIZE : 1; - err = mt76_register_device(&dev->mt76, true, mt76x02_rates, + switch (dev->mt76.rev) { + case 0x76320044: + /* these ASIC revisions do not support VHT */ + vht = false; + break; + default: + vht = true; + break; + } + + err = mt76_register_device(&dev->mt76, vht, mt76x02_rates, ARRAY_SIZE(mt76x02_rates)); if (err) goto fail; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c index 578013884e43..b287b7d9394e 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/debugfs.c @@ -211,13 +211,28 @@ static const struct file_operations mt7915_sys_recovery_ops = { static int mt7915_radar_trigger(void *data, u64 val) { - struct mt7915_dev *dev = data; +#define RADAR_MAIN_CHAIN 1 +#define RADAR_BACKGROUND 2 + struct mt7915_phy *phy = data; + struct mt7915_dev *dev = phy->dev; + int rdd_idx; + + if (!val || val > RADAR_BACKGROUND) + return -EINVAL; - if (val > MT_RX_SEL2) + if (val == RADAR_BACKGROUND && !dev->rdd2_phy) { + dev_err(dev->mt76.dev, "Background radar is not enabled\n"); return -EINVAL; + } + + rdd_idx = mt7915_get_rdd_idx(phy, val == RADAR_BACKGROUND); + if (rdd_idx < 0) { + dev_err(dev->mt76.dev, "No RDD found\n"); + return -EINVAL; + } return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_RADAR_EMULATE, - val, 0, 0); + rdd_idx, 0, 0); } DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL, @@ -303,9 +318,9 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data) phy->mib.dl_vht_3mu_cnt, phy->mib.dl_vht_4mu_cnt); - sub_total_cnt = phy->mib.dl_vht_2mu_cnt + - phy->mib.dl_vht_3mu_cnt + - phy->mib.dl_vht_4mu_cnt; + sub_total_cnt = (u64)phy->mib.dl_vht_2mu_cnt + + phy->mib.dl_vht_3mu_cnt + + phy->mib.dl_vht_4mu_cnt; seq_printf(file, "\nTotal non-HE MU-MIMO DL PPDU count: %lld", sub_total_cnt); @@ -353,26 +368,27 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data) phy->mib.dl_he_9to16ru_cnt, phy->mib.dl_he_gtr16ru_cnt); - sub_total_cnt = phy->mib.dl_he_2mu_cnt + - phy->mib.dl_he_3mu_cnt + - phy->mib.dl_he_4mu_cnt; + sub_total_cnt = (u64)phy->mib.dl_he_2mu_cnt + + phy->mib.dl_he_3mu_cnt + + phy->mib.dl_he_4mu_cnt; total_ppdu_cnt = sub_total_cnt; seq_printf(file, "\nTotal HE MU-MIMO DL PPDU count: %lld", sub_total_cnt); - sub_total_cnt = phy->mib.dl_he_2ru_cnt + - phy->mib.dl_he_3ru_cnt + - phy->mib.dl_he_4ru_cnt + - phy->mib.dl_he_5to8ru_cnt + - phy->mib.dl_he_9to16ru_cnt + - phy->mib.dl_he_gtr16ru_cnt; + sub_total_cnt = (u64)phy->mib.dl_he_2ru_cnt + + phy->mib.dl_he_3ru_cnt + + phy->mib.dl_he_4ru_cnt + + phy->mib.dl_he_5to8ru_cnt + + phy->mib.dl_he_9to16ru_cnt + + phy->mib.dl_he_gtr16ru_cnt; total_ppdu_cnt += sub_total_cnt; seq_printf(file, "\nTotal HE OFDMA DL PPDU count: %lld", sub_total_cnt); - total_ppdu_cnt += phy->mib.dl_he_su_cnt + phy->mib.dl_he_ext_su_cnt; + total_ppdu_cnt += (u64)phy->mib.dl_he_su_cnt + + phy->mib.dl_he_ext_su_cnt; seq_printf(file, "\nAll HE DL PPDU count: %lld", total_ppdu_cnt); @@ -404,20 +420,20 @@ static int mt7915_muru_stats_show(struct seq_file *file, void *data) phy->mib.ul_hetrig_9to16ru_cnt, phy->mib.ul_hetrig_gtr16ru_cnt); - sub_total_cnt = phy->mib.ul_hetrig_2mu_cnt + - phy->mib.ul_hetrig_3mu_cnt + - phy->mib.ul_hetrig_4mu_cnt; + sub_total_cnt = (u64)phy->mib.ul_hetrig_2mu_cnt + + phy->mib.ul_hetrig_3mu_cnt + + phy->mib.ul_hetrig_4mu_cnt; total_ppdu_cnt = sub_total_cnt; seq_printf(file, "\nTotal HE MU-MIMO UL TB PPDU count: %lld", sub_total_cnt); - sub_total_cnt = phy->mib.ul_hetrig_2ru_cnt + - phy->mib.ul_hetrig_3ru_cnt + - phy->mib.ul_hetrig_4ru_cnt + - phy->mib.ul_hetrig_5to8ru_cnt + - phy->mib.ul_hetrig_9to16ru_cnt + - phy->mib.ul_hetrig_gtr16ru_cnt; + sub_total_cnt = (u64)phy->mib.ul_hetrig_2ru_cnt + + phy->mib.ul_hetrig_3ru_cnt + + phy->mib.ul_hetrig_4ru_cnt + + phy->mib.ul_hetrig_5to8ru_cnt + + phy->mib.ul_hetrig_9to16ru_cnt + + phy->mib.ul_hetrig_gtr16ru_cnt; total_ppdu_cnt += sub_total_cnt; seq_printf(file, "\nTotal HE OFDMA UL TB PPDU count: %lld", @@ -444,6 +460,11 @@ mt7915_rdd_monitor(struct seq_file *s, void *data) mutex_lock(&dev->mt76.mutex); + if (!mt7915_eeprom_has_background_radar(dev)) { + seq_puts(s, "no background radar capability\n"); + goto out; + } + if (!cfg80211_chandef_valid(chandef)) { ret = -EINVAL; goto out; @@ -1084,13 +1105,13 @@ mt7915_rate_txpower_set(struct file *file, const char __user *user_buf, return -EINVAL; if (pwr160) - pwr160 = mt7915_get_power_bound(phy, pwr160); + pwr160 = mt76_get_power_bound(mphy, pwr160); if (pwr80) - pwr80 = mt7915_get_power_bound(phy, pwr80); + pwr80 = mt76_get_power_bound(mphy, pwr80); if (pwr40) - pwr40 = mt7915_get_power_bound(phy, pwr40); + pwr40 = mt76_get_power_bound(mphy, pwr40); if (pwr20) - pwr20 = mt7915_get_power_bound(phy, pwr20); + pwr20 = mt76_get_power_bound(mphy, pwr20); if (pwr160 < 0 || pwr80 < 0 || pwr40 < 0 || pwr20 < 0) return -EINVAL; @@ -1241,7 +1262,7 @@ int mt7915_init_debugfs(struct mt7915_phy *phy) if (!dev->dbdc_support || phy->mt76->band_idx) { debugfs_create_u32("dfs_hw_pattern", 0400, dir, &dev->hw_pattern); - debugfs_create_file("radar_trigger", 0200, dir, dev, + debugfs_create_file("radar_trigger", 0200, dir, phy, &fops_radar_trigger); debugfs_create_devm_seqfile(dev->mt76.dev, "rdd_monitor", dir, mt7915_rdd_monitor); diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c index 928e0b07a9bf..c0f3402d30bb 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.c @@ -147,7 +147,7 @@ static int mt7915_eeprom_load(struct mt7915_dev *dev) /* read eeprom data from efuse */ block_num = DIV_ROUND_UP(eeprom_size, eeprom_blk_size); for (i = 0; i < block_num; i++) { - ret = mt7915_mcu_get_eeprom(dev, i * eeprom_blk_size); + ret = mt7915_mcu_get_eeprom(dev, i * eeprom_blk_size, NULL); if (ret < 0) return ret; } @@ -361,6 +361,37 @@ s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band) return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta; } +bool +mt7915_eeprom_has_background_radar(struct mt7915_dev *dev) +{ + u8 val, buf[MT7915_EEPROM_BLOCK_SIZE]; + u8 band_sel, tx_path, rx_path; + int offs = MT_EE_WIFI_CONF + 1; + + switch (mt76_chip(&dev->mt76)) { + case 0x7915: + return true; + case 0x7906: + /* read efuse to check background radar capability */ + if (mt7915_mcu_get_eeprom(dev, offs, buf)) + break; + + val = buf[offs % MT7915_EEPROM_BLOCK_SIZE]; + band_sel = u8_get_bits(val, MT_EE_WIFI_CONF0_BAND_SEL); + tx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_TX_PATH); + rx_path = u8_get_bits(val, MT_EE_WIFI_CONF0_RX_PATH); + + return (band_sel == MT_EE_V2_BAND_SEL_5GHZ && + tx_path == rx_path && rx_path == 2); + case 0x7981: + case 0x7986: + default: + break; + } + + return false; +} + const u8 mt7915_sku_group_len[] = { [SKU_CCK] = 4, [SKU_OFDM] = 8, diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h index 509fb43d8a68..31aec0f40232 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/eeprom.h @@ -55,6 +55,7 @@ enum mt7915_eeprom_field { #define MT_EE_CAL_DPD_SIZE_V2_7981 (102 * MT_EE_CAL_UNIT) /* no 6g dpd data */ #define MT_EE_WIFI_CONF0_TX_PATH GENMASK(2, 0) +#define MT_EE_WIFI_CONF0_RX_PATH GENMASK(5, 3) #define MT_EE_WIFI_CONF0_BAND_SEL GENMASK(7, 6) #define MT_EE_WIFI_CONF1_BAND_SEL GENMASK(7, 6) #define MT_EE_WIFI_CONF_STREAM_NUM GENMASK(7, 5) diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/init.c b/drivers/net/wireless/mediatek/mt76/mt7915/init.c index bee4beabc4eb..3e30ca5155d2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/init.c @@ -285,7 +285,7 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy, { struct mt7915_dev *dev = phy->dev; int i, n_chains = hweight16(phy->mt76->chainmask); - int nss_delta = mt76_tx_power_nss_delta(n_chains); + int path_delta = mt76_tx_power_path_delta(n_chains); int pwr_delta = mt7915_eeprom_get_power_delta(dev, sband->band); struct mt76_power_limits limits; @@ -305,7 +305,7 @@ static void __mt7915_init_txpower(struct mt7915_phy *phy, target_power = mt76_get_rate_power_limits(phy->mt76, chan, &limits, target_power); - target_power += nss_delta; + target_power += path_delta; target_power = DIV_ROUND_UP(target_power, 2); chan->max_power = min_t(int, chan->max_reg_power, target_power); @@ -392,9 +392,10 @@ mt7915_init_wiphy(struct mt7915_phy *phy) if (!is_mt7915(&dev->mt76)) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_STA_TX_PWR); - if (!mdev->dev->of_node || - !of_property_read_bool(mdev->dev->of_node, - "mediatek,disable-radar-background")) + if (mt7915_eeprom_has_background_radar(phy->dev) && + (!mdev->dev->of_node || + !of_property_read_bool(mdev->dev->of_node, + "mediatek,disable-radar-background"))) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_RADAR_BACKGROUND); @@ -924,8 +925,7 @@ mt7915_set_stream_he_txbf_caps(struct mt7915_phy *phy, c = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US; if (!is_mt7915(&dev->mt76)) - c |= IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | - IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + c |= IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO; elem->phy_cap_info[2] |= c; c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE | diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c index 2ba6eb3038ce..9400e4af2a04 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mac.c @@ -2035,16 +2035,15 @@ void mt7915_mac_work(struct work_struct *work) static void mt7915_dfs_stop_radar_detector(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; + int rdd_idx = mt7915_get_rdd_idx(phy, false); - if (phy->rdd_state & BIT(0)) - mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 0, - MT_RX_SEL0, 0); - if (phy->rdd_state & BIT(1)) - mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, 1, - MT_RX_SEL0, 0); + if (rdd_idx < 0) + return; + + mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, rdd_idx, 0, 0); } -static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int chain) +static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int rdd_idx) { int err, region; @@ -2061,52 +2060,38 @@ static int mt7915_dfs_start_rdd(struct mt7915_dev *dev, int chain) break; } - err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, chain, - MT_RX_SEL0, region); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, rdd_idx, 0, region); if (err < 0) return err; if (is_mt7915(&dev->mt76)) { - err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, chain, + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, rdd_idx, 0, dev->dbdc_support ? 2 : 0); if (err < 0) return err; } - return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, chain, - MT_RX_SEL0, 1); + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_DET_MODE, rdd_idx, 0, 1); } static int mt7915_dfs_start_radar_detector(struct mt7915_phy *phy) { - struct cfg80211_chan_def *chandef = &phy->mt76->chandef; struct mt7915_dev *dev = phy->dev; - int err; + int err, rdd_idx; + + rdd_idx = mt7915_get_rdd_idx(phy, false); + if (rdd_idx < 0) + return -EINVAL; /* start CAC */ - err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, - phy->mt76->band_idx, MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_START, rdd_idx, 0, 0); if (err < 0) return err; - err = mt7915_dfs_start_rdd(dev, phy->mt76->band_idx); + err = mt7915_dfs_start_rdd(dev, rdd_idx); if (err < 0) return err; - phy->rdd_state |= BIT(phy->mt76->band_idx); - - if (!is_mt7915(&dev->mt76)) - return 0; - - if (chandef->width == NL80211_CHAN_WIDTH_160 || - chandef->width == NL80211_CHAN_WIDTH_80P80) { - err = mt7915_dfs_start_rdd(dev, 1); - if (err < 0) - return err; - - phy->rdd_state |= BIT(1); - } - return 0; } @@ -2148,12 +2133,12 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) { struct mt7915_dev *dev = phy->dev; enum mt76_dfs_state dfs_state, prev_state; - int err; + int err, rdd_idx = mt7915_get_rdd_idx(phy, false); prev_state = phy->mt76->dfs_state; dfs_state = mt76_phy_dfs_state(phy->mt76); - if (prev_state == dfs_state) + if (prev_state == dfs_state || rdd_idx < 0) return 0; if (prev_state == MT_DFS_STATE_UNKNOWN) @@ -2177,8 +2162,7 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) if (dfs_state == MT_DFS_STATE_CAC) return 0; - err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, - phy->mt76->band_idx, MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_CAC_END, rdd_idx, 0, 0); if (err < 0) { phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; return err; @@ -2188,15 +2172,13 @@ int mt7915_dfs_init_radar_detector(struct mt7915_phy *phy) return 0; stop: - err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, - phy->mt76->band_idx, MT_RX_SEL0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_NORMAL_START, rdd_idx, 0, 0); if (err < 0) return err; if (is_mt7915(&dev->mt76)) { err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_SET_WF_ANT, - phy->mt76->band_idx, 0, - dev->dbdc_support ? 2 : 0); + rdd_idx, 0, dev->dbdc_support ? 2 : 0); if (err < 0) return err; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c index 9d790f234e82..427542777abc 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.c @@ -303,17 +303,35 @@ mt7915_mcu_rx_radar_detected(struct mt7915_dev *dev, struct sk_buff *skb) { struct mt76_phy *mphy = &dev->mt76.phy; struct mt7915_mcu_rdd_report *r; + u32 sku; r = (struct mt7915_mcu_rdd_report *)skb->data; - if (r->band_idx > MT_RX_SEL2) + switch (r->rdd_idx) { + case MT_RDD_IDX_BAND0: + break; + case MT_RDD_IDX_BAND1: + sku = mt7915_check_adie(dev, true); + /* the main phy is bound to band 1 for this sku */ + if (is_mt7986(&dev->mt76) && + (sku == MT7975_ONE_ADIE || sku == MT7976_ONE_ADIE)) + break; + mphy = dev->mt76.phys[MT_BAND1]; + break; + case MT_RDD_IDX_BACKGROUND: + if (!dev->rdd2_phy) + return; + mphy = dev->rdd2_phy->mt76; + break; + default: + dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx); return; + } - if ((r->band_idx && !dev->phy.mt76->band_idx) && - dev->mt76.phys[MT_BAND1]) - mphy = dev->mt76.phys[MT_BAND1]; + if (!mphy) + return; - if (r->band_idx == MT_RX_SEL2) + if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) cfg80211_background_radar_event(mphy->hw->wiphy, &dev->rdd2_chandef, GFP_ATOMIC); @@ -2697,11 +2715,14 @@ int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy, struct cfg80211_chan_def *chandef) { struct mt7915_dev *dev = phy->dev; - int err, region; + int err, region, rdd_idx; + + rdd_idx = mt7915_get_rdd_idx(phy, true); + if (rdd_idx < 0) + return -EINVAL; if (!chandef) { /* disable offchain */ - err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, MT_RX_SEL2, - 0, 0); + err = mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_STOP, rdd_idx, 0, 0); if (err) return err; @@ -2727,8 +2748,7 @@ int mt7915_mcu_rdd_background_enable(struct mt7915_phy *phy, break; } - return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, MT_RX_SEL2, - 0, region); + return mt76_connac_mcu_rdd_cmd(&dev->mt76, RDD_START, rdd_idx, 0, region); } int mt7915_mcu_set_chan_info(struct mt7915_phy *phy, int cmd) @@ -2859,7 +2879,7 @@ int mt7915_mcu_set_eeprom(struct mt7915_dev *dev) &req, sizeof(req), true); } -int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset) +int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf) { struct mt7915_mcu_eeprom_info req = { .addr = cpu_to_le32(round_down(offset, @@ -2867,8 +2887,8 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset) }; struct mt7915_mcu_eeprom_info *res; struct sk_buff *skb; + u8 *buf = read_buf; int ret; - u8 *buf; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_EXT_QUERY(EFUSE_ACCESS), @@ -2877,8 +2897,10 @@ int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset) return ret; res = (struct mt7915_mcu_eeprom_info *)skb->data; - buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr); + if (!buf) + buf = dev->mt76.eeprom.data + le32_to_cpu(res->addr); memcpy(buf, res->data, MT7915_EEPROM_BLOCK_SIZE); + dev_kfree_skb(skb); return 0; @@ -3323,7 +3345,7 @@ int mt7915_mcu_set_txpower_frame(struct mt7915_phy *phy, if (ret) return ret; - txpower = mt7915_get_power_bound(phy, txpower); + txpower = mt76_get_power_bound(mphy, txpower); if (txpower > mphy->txpower_cur || txpower < 0) return -EINVAL; @@ -3373,7 +3395,7 @@ int mt7915_mcu_set_txpower_sku(struct mt7915_phy *phy) int i, idx; int tx_power; - tx_power = mt7915_get_power_bound(phy, hw->conf.power_level); + tx_power = mt76_get_power_bound(mphy, hw->conf.power_level); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &limits_array, tx_power); mphy->txpower_cur = tx_power; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h index 49476a4182fd..086ad89ecd91 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mcu.h @@ -57,7 +57,7 @@ struct mt7915_mcu_bcc_notify { struct mt7915_mcu_rdd_report { struct mt76_connac2_mcu_rxd_hdr rxd; - u8 band_idx; + u8 rdd_idx; u8 long_detected; u8 constant_prf_detected; u8 staggered_prf_detected; @@ -515,16 +515,4 @@ enum { sizeof(struct bss_info_bmc_rate) +\ sizeof(struct bss_info_ext_bss)) -static inline s8 -mt7915_get_power_bound(struct mt7915_phy *phy, s8 txpower) -{ - struct mt76_phy *mphy = phy->mt76; - int n_chains = hweight16(mphy->chainmask); - - txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2); - txpower -= mt76_tx_power_nss_delta(n_chains); - - return txpower; -} - #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c index 876f0692850a..9c4d5cea0c42 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mmio.c @@ -651,6 +651,9 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.base = devm_ioremap(dev->mt76.dev, pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); + if (!wed->wlan.base) + return -ENOMEM; + wed->wlan.phy_base = pci_resource_start(pci_dev, 0); wed->wlan.wpdma_int = pci_resource_start(pci_dev, 0) + MT_INT_WED_SOURCE_CSR; @@ -678,6 +681,9 @@ int mt7915_mmio_wed_init(struct mt7915_dev *dev, void *pdev_ptr, wed->wlan.bus_type = MTK_WED_BUS_AXI; wed->wlan.base = devm_ioremap(dev->mt76.dev, res->start, resource_size(res)); + if (!wed->wlan.base) + return -ENOMEM; + wed->wlan.phy_base = res->start; wed->wlan.wpdma_int = res->start + MT_INT_SOURCE_CSR; wed->wlan.wpdma_mask = res->start + MT_INT_MASK_CSR; diff --git a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h index 533939f2b7ed..2e94347c46d6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h +++ b/drivers/net/wireless/mediatek/mt76/mt7915/mt7915.h @@ -215,8 +215,6 @@ struct mt7915_phy { s16 coverage_class; u8 slottime; - u8 rdd_state; - u32 trb_ts; u32 rx_ampdu_ts; @@ -331,10 +329,10 @@ enum { __MT_WFDMA_MAX, }; -enum { - MT_RX_SEL0, - MT_RX_SEL1, - MT_RX_SEL2, /* monitor chain */ +enum rdd_idx { + MT_RDD_IDX_BAND0, /* RDD idx for band idx 0 (single-band) */ + MT_RDD_IDX_BAND1, /* RDD idx for band idx 1 */ + MT_RDD_IDX_BACKGROUND, /* RDD idx for background chain */ }; enum mt7915_rdd_cmd { @@ -354,6 +352,18 @@ enum mt7915_rdd_cmd { RDD_IRQ_OFF, }; +static inline int +mt7915_get_rdd_idx(struct mt7915_phy *phy, bool is_background) +{ + if (!phy->mt76->cap.has_5ghz) + return -1; + + if (is_background) + return MT_RDD_IDX_BACKGROUND; + + return phy->mt76->band_idx; +} + static inline struct mt7915_phy * mt7915_hw_phy(struct ieee80211_hw *hw) { @@ -425,6 +435,7 @@ int mt7915_eeprom_get_target_power(struct mt7915_dev *dev, struct ieee80211_channel *chan, u8 chain_idx); s8 mt7915_eeprom_get_power_delta(struct mt7915_dev *dev, int band); +bool mt7915_eeprom_has_background_radar(struct mt7915_dev *dev); int mt7915_dma_init(struct mt7915_dev *dev, struct mt7915_phy *phy2); void mt7915_dma_prefetch(struct mt7915_dev *dev); void mt7915_dma_cleanup(struct mt7915_dev *dev); @@ -473,7 +484,7 @@ int mt7915_mcu_set_fixed_rate_ctrl(struct mt7915_dev *dev, struct ieee80211_sta *sta, void *data, u32 field); int mt7915_mcu_set_eeprom(struct mt7915_dev *dev); -int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset); +int mt7915_mcu_get_eeprom(struct mt7915_dev *dev, u32 offset, u8 *read_buf); int mt7915_mcu_get_eeprom_free_block(struct mt7915_dev *dev, u8 *block_num); int mt7915_mcu_set_mac(struct mt7915_dev *dev, int band, bool enable, bool hdr_trans); diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/main.c b/drivers/net/wireless/mediatek/mt76/mt7921/main.c index 13e58c328aff..1fffa43379b2 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/main.c @@ -83,6 +83,11 @@ mt7921_init_he_caps(struct mt792x_phy *phy, enum nl80211_band band, he_cap_elem->phy_cap_info[9] |= IEEE80211_HE_PHY_CAP9_TX_1024_QAM_LESS_THAN_242_TONE_RU | IEEE80211_HE_PHY_CAP9_RX_1024_QAM_LESS_THAN_242_TONE_RU; + + if (is_mt7922(phy->mt76->dev)) { + he_cap_elem->phy_cap_info[0] |= + IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G; + } break; case NL80211_IFTYPE_STATION: he_cap_elem->mac_cap_info[1] |= @@ -364,7 +369,7 @@ void mt7921_roc_abort_sync(struct mt792x_dev *dev) { struct mt792x_phy *phy = &dev->phy; - del_timer_sync(&phy->roc_timer); + timer_delete_sync(&phy->roc_timer); cancel_work_sync(&phy->roc_work); if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) ieee80211_iterate_interfaces(mt76_hw(dev), @@ -395,7 +400,7 @@ static int mt7921_abort_roc(struct mt792x_phy *phy, struct mt792x_vif *vif) { int err = 0; - del_timer_sync(&phy->roc_timer); + timer_delete_sync(&phy->roc_timer); cancel_work_sync(&phy->roc_work); mt792x_mutex_acquire(phy->dev); @@ -811,6 +816,7 @@ int mt7921_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, msta->deflink.wcid.phy_idx = mvif->bss_conf.mt76.band_idx; msta->deflink.wcid.tx_info |= MT_WCID_TX_INFO_SET; msta->deflink.last_txs = jiffies; + msta->deflink.sta = msta; ret = mt76_connac_pm_wake(&dev->mphy, &dev->pm); if (ret) @@ -1475,7 +1481,7 @@ static void mt7921_abort_channel_switch(struct ieee80211_hw *hw, { struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - del_timer_sync(&mvif->csa_timer); + timer_delete_sync(&mvif->csa_timer); cancel_work_sync(&mvif->csa_work); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile index d321e4ed732f..ade5e647c941 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/Makefile +++ b/drivers/net/wireless/mediatek/mt76/mt7925/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_MT7925E) += mt7925e.o obj-$(CONFIG_MT7925U) += mt7925u.o mt7925-common-y := mac.o mcu.o main.o init.o debugfs.o +mt7925-common-$(CONFIG_NL80211_TESTMODE) += testmode.o mt7925e-y := pci.o pci_mac.o pci_mcu.o mt7925u-y := usb.o diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/init.c b/drivers/net/wireless/mediatek/mt76/mt7925/init.c index f41ca4248497..2a83ff59a968 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/init.c @@ -58,15 +58,110 @@ static int mt7925_thermal_init(struct mt792x_phy *phy) return PTR_ERR_OR_ZERO(hwmon); } +void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2) +{ + struct mt792x_phy *phy = &dev->phy; + struct mt7925_clc_rule_v2 *rule; + struct mt7925_clc *clc; + bool old = dev->has_eht, new = true; + u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, alpha2); + u8 *pos; + + if (mtcl_conf != MT792X_ACPI_MTCL_INVALID && + (((mtcl_conf >> 4) & 0x3) == 0)) { + new = false; + goto out; + } + + if (!phy->clc[MT792x_CLC_BE_CTRL]) + goto out; + + clc = (struct mt7925_clc *)phy->clc[MT792x_CLC_BE_CTRL]; + pos = clc->data; + + while (1) { + rule = (struct mt7925_clc_rule_v2 *)pos; + + if (rule->alpha2[0] == alpha2[0] && + rule->alpha2[1] == alpha2[1]) { + new = false; + break; + } + + /* Check the last one */ + if (rule->flag & BIT(0)) + break; + + pos += sizeof(*rule); + } + +out: + if (old == new) + return; + + dev->has_eht = new; + mt7925_set_stream_he_eht_caps(phy); +} + +static void +mt7925_regd_channel_update(struct wiphy *wiphy, struct mt792x_dev *dev) +{ +#define IS_UNII_INVALID(idx, sfreq, efreq, cfreq) \ + (!(dev->phy.clc_chan_conf & BIT(idx)) && (cfreq) >= (sfreq) && (cfreq) <= (efreq)) +#define MT7925_UNII_59G_IS_VALID 0x1 +#define MT7925_UNII_6G_IS_VALID 0x1e + struct ieee80211_supported_band *sband; + struct mt76_dev *mdev = &dev->mt76; + struct ieee80211_channel *ch; + u32 mtcl_conf = mt792x_acpi_get_mtcl_conf(&dev->phy, mdev->alpha2); + int i; + + if (mtcl_conf != MT792X_ACPI_MTCL_INVALID) { + if ((mtcl_conf & 0x3) == 0) + dev->phy.clc_chan_conf &= ~MT7925_UNII_59G_IS_VALID; + if (((mtcl_conf >> 2) & 0x3) == 0) + dev->phy.clc_chan_conf &= ~MT7925_UNII_6G_IS_VALID; + } + + sband = wiphy->bands[NL80211_BAND_5GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + /* UNII-4 */ + if (IS_UNII_INVALID(0, 5845, 5925, ch->center_freq)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } + + sband = wiphy->bands[NL80211_BAND_6GHZ]; + if (!sband) + return; + + for (i = 0; i < sband->n_channels; i++) { + ch = &sband->channels[i]; + + /* UNII-5/6/7/8 */ + if (IS_UNII_INVALID(1, 5925, 6425, ch->center_freq) || + IS_UNII_INVALID(2, 6425, 6525, ch->center_freq) || + IS_UNII_INVALID(3, 6525, 6875, ch->center_freq) || + IS_UNII_INVALID(4, 6875, 7125, ch->center_freq)) + ch->flags |= IEEE80211_CHAN_DISABLED; + } +} + void mt7925_regd_update(struct mt792x_dev *dev) { struct mt76_dev *mdev = &dev->mt76; struct ieee80211_hw *hw = mdev->hw; + struct wiphy *wiphy = hw->wiphy; if (!dev->regd_change) return; mt7925_mcu_set_clc(dev, mdev->alpha2, dev->country_ie_env); + mt7925_regd_channel_update(wiphy, dev); mt7925_mcu_set_channel_domain(hw->priv); mt7925_set_tx_sar_pwr(hw, NULL); dev->regd_change = false; @@ -227,6 +322,12 @@ static void mt7925_init_work(struct work_struct *work) return; } + ret = mt7925_mcu_set_thermal_protect(dev); + if (ret) { + dev_err(dev->mt76.dev, "thermal protection enable failed\n"); + return; + } + /* we support chip reset now */ dev->hw_init_done = true; @@ -244,6 +345,7 @@ int mt7925_register_device(struct mt792x_dev *dev) dev->mt76.tx_worker.fn = mt792x_tx_worker; INIT_DELAYED_WORK(&dev->pm.ps_work, mt792x_pm_power_save_work); + INIT_DELAYED_WORK(&dev->mlo_pm_work, mt7925_mlo_pm_work); INIT_WORK(&dev->pm.wake_work, mt792x_pm_wake_work); spin_lock_init(&dev->pm.wake.lock); mutex_init(&dev->pm.mutex); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c index 98daf80ac131..94b0099dcd41 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c @@ -251,12 +251,12 @@ int mt7925_init_mlo_caps(struct mt792x_phy *phy) }, }; - if (!(phy->chip_cap & MT792x_CHIP_CAP_MLO_EVT_EN)) + if (!(phy->chip_cap & MT792x_CHIP_CAP_MLO_EN)) return 0; ext_capab[0].eml_capabilities = phy->eml_cap; ext_capab[0].mld_capa_and_ops = - u16_encode_bits(1, IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS); + u16_encode_bits(0, IEEE80211_MLD_CAP_OP_MAX_SIMUL_LINKS); wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; wiphy->iftype_ext_capab = ext_capab; @@ -334,6 +334,9 @@ int __mt7925_start(struct mt792x_phy *phy) ieee80211_queue_delayed_work(mphy->hw, &mphy->mac_work, MT792x_WATCHDOG_TIME); + if (phy->chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) + wiphy_rfkill_start_polling(mphy->hw->wiphy); + return 0; } EXPORT_SYMBOL_GPL(__mt7925_start); @@ -360,10 +363,15 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, struct mt76_txq *mtxq; int idx, ret = 0; - mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask); - if (mconf->mt76.idx >= MT792x_MAX_INTERFACES) { - ret = -ENOSPC; - goto out; + if (vif->type == NL80211_IFTYPE_P2P_DEVICE) { + mconf->mt76.idx = MT792x_MAX_INTERFACES; + } else { + mconf->mt76.idx = __ffs64(~dev->mt76.vif_mask); + + if (mconf->mt76.idx >= MT792x_MAX_INTERFACES) { + ret = -ENOSPC; + goto out; + } } mconf->mt76.omac_idx = ieee80211_vif_is_mld(vif) ? @@ -371,6 +379,7 @@ static int mt7925_mac_link_bss_add(struct mt792x_dev *dev, mconf->mt76.band_idx = 0xff; mconf->mt76.wmm_idx = ieee80211_vif_is_mld(vif) ? 0 : mconf->mt76.idx % MT76_CONNAC_MAX_WMM_SETS; + mconf->mt76.link_idx = hweight16(mvif->valid_links); if (mvif->phy->mt76->chandef.chan->band != NL80211_BAND_2GHZ) mconf->mt76.basic_rates_idx = MT792x_BASIC_RATES_TBL + 4; @@ -421,6 +430,7 @@ mt7925_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) mvif->bss_conf.vif = mvif; mvif->sta.vif = mvif; mvif->deflink_id = IEEE80211_LINK_UNSPECIFIED; + mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC; ret = mt7925_mac_link_bss_add(dev, &vif->bss_conf, &mvif->sta.deflink); if (ret < 0) @@ -446,7 +456,7 @@ void mt7925_roc_abort_sync(struct mt792x_dev *dev) { struct mt792x_phy *phy = &dev->phy; - del_timer_sync(&phy->roc_timer); + timer_delete_sync(&phy->roc_timer); cancel_work_sync(&phy->roc_work); if (test_and_clear_bit(MT76_STATE_ROC, &phy->mt76->state)) ieee80211_iterate_interfaces(mt76_hw(dev), @@ -478,7 +488,7 @@ static int mt7925_abort_roc(struct mt792x_phy *phy, { int err = 0; - del_timer_sync(&phy->roc_timer); + timer_delete_sync(&phy->roc_timer); cancel_work_sync(&phy->roc_work); mt792x_mutex_acquire(phy->dev); @@ -1149,7 +1159,12 @@ static void mt7925_mac_link_sta_remove(struct mt76_dev *mdev, struct mt792x_bss_conf *mconf; mconf = mt792x_link_conf_to_mconf(link_conf); - mt792x_mac_link_bss_remove(dev, mconf, mlink); + + if (ieee80211_vif_is_mld(vif)) + mt792x_mac_link_bss_remove(dev, mconf, mlink); + else + mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf, + link_sta, false); } spin_lock_bh(&mdev->sta_poll_lock); @@ -1169,6 +1184,31 @@ mt7925_mac_sta_remove_links(struct mt792x_dev *dev, struct ieee80211_vif *vif, struct mt76_wcid *wcid; unsigned int link_id; + /* clean up bss before starec */ + for_each_set_bit(link_id, &old_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_link_sta *link_sta; + struct ieee80211_bss_conf *link_conf; + struct mt792x_bss_conf *mconf; + struct mt792x_link_sta *mlink; + + link_sta = mt792x_sta_to_link_sta(vif, sta, link_id); + if (!link_sta) + continue; + + mlink = mt792x_sta_to_link(msta, link_id); + if (!mlink) + continue; + + link_conf = mt792x_vif_to_bss_conf(vif, link_id); + if (!link_conf) + continue; + + mconf = mt792x_link_conf_to_mconf(link_conf); + + mt7925_mcu_add_bss_info(&dev->phy, mconf->mt76.ctx, link_conf, + link_sta, false); + } + for_each_set_bit(link_id, &old_links, IEEE80211_MLD_MAX_NUM_LINKS) { struct ieee80211_link_sta *link_sta; struct mt792x_link_sta *mlink; @@ -1206,51 +1246,22 @@ void mt7925_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, { struct mt792x_dev *dev = container_of(mdev, struct mt792x_dev, mt76); struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv; - struct { - struct { - u8 omac_idx; - u8 band_idx; - __le16 pad; - } __packed hdr; - struct req_tlv { - __le16 tag; - __le16 len; - u8 active; - u8 link_idx; /* hw link idx */ - u8 omac_addr[ETH_ALEN]; - } __packed tlv; - } dev_req = { - .hdr = { - .omac_idx = 0, - .band_idx = 0, - }, - .tlv = { - .tag = cpu_to_le16(DEV_INFO_ACTIVE), - .len = cpu_to_le16(sizeof(struct req_tlv)), - .active = true, - }, - }; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; unsigned long rem; rem = ieee80211_vif_is_mld(vif) ? msta->valid_links : BIT(0); mt7925_mac_sta_remove_links(dev, vif, sta, rem); - if (ieee80211_vif_is_mld(vif)) { - mt7925_mcu_set_dbdc(&dev->mphy, false); - - /* recovery omac address for the legacy interface */ - memcpy(dev_req.tlv.omac_addr, vif->addr, ETH_ALEN); - mt76_mcu_send_msg(mdev, MCU_UNI_CMD(DEV_INFO_UPDATE), - &dev_req, sizeof(dev_req), true); - } + if (ieee80211_vif_is_mld(vif)) + mt7925_mcu_del_dev(mdev, vif); if (vif->type == NL80211_IFTYPE_STATION) { - struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - mvif->wep_sta = NULL; ewma_rssi_init(&mvif->bss_conf.rssi); } + + mvif->mlo_pm_state = MT792x_MLO_LINK_DISASSOC; } EXPORT_SYMBOL_GPL(mt7925_mac_sta_remove); @@ -1289,22 +1300,22 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_RX_START: mt76_rx_aggr_start(&dev->mt76, &msta->deflink.wcid, tid, ssn, params->buf_size); - mt7925_mcu_uni_rx_ba(dev, vif, params, true); + mt7925_mcu_uni_rx_ba(dev, params, true); break; case IEEE80211_AMPDU_RX_STOP: mt76_rx_aggr_stop(&dev->mt76, &msta->deflink.wcid, tid); - mt7925_mcu_uni_rx_ba(dev, vif, params, false); + mt7925_mcu_uni_rx_ba(dev, params, false); break; case IEEE80211_AMPDU_TX_OPERATIONAL: mtxq->aggr = true; mtxq->send_bar = false; - mt7925_mcu_uni_tx_ba(dev, vif, params, true); + mt7925_mcu_uni_tx_ba(dev, params, true); break; case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: mtxq->aggr = false; clear_bit(tid, &msta->deflink.wcid.ampdu_state); - mt7925_mcu_uni_tx_ba(dev, vif, params, false); + mt7925_mcu_uni_tx_ba(dev, params, false); break; case IEEE80211_AMPDU_TX_START: set_bit(tid, &msta->deflink.wcid.ampdu_state); @@ -1313,7 +1324,7 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, case IEEE80211_AMPDU_TX_STOP_CONT: mtxq->aggr = false; clear_bit(tid, &msta->deflink.wcid.ampdu_state); - mt7925_mcu_uni_tx_ba(dev, vif, params, false); + mt7925_mcu_uni_tx_ba(dev, params, false); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; } @@ -1322,6 +1333,38 @@ mt7925_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, return ret; } +static void +mt7925_mlo_pm_iter(void *priv, u8 *mac, struct ieee80211_vif *vif) +{ + struct mt792x_dev *dev = priv; + struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; + unsigned long valid = ieee80211_vif_is_mld(vif) ? + mvif->valid_links : BIT(0); + struct ieee80211_bss_conf *bss_conf; + int i; + + if (mvif->mlo_pm_state != MT792x_MLO_CHANGED_PS) + return; + + mt792x_mutex_acquire(dev); + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + mt7925_mcu_uni_bss_ps(dev, bss_conf); + } + mt792x_mutex_release(dev); +} + +void mt7925_mlo_pm_work(struct work_struct *work) +{ + struct mt792x_dev *dev = container_of(work, struct mt792x_dev, + mlo_pm_work.work); + struct ieee80211_hw *hw = mt76_hw(dev); + + ieee80211_iterate_active_interfaces(hw, + IEEE80211_IFACE_ITER_RESUME_ALL, + mt7925_mlo_pm_iter, dev); +} + static bool is_valid_alpha2(const char *alpha2) { if (!alpha2) @@ -1378,6 +1421,8 @@ void mt7925_scan_work(struct work_struct *work) if (!is_valid_alpha2(evt->alpha2)) break; + mt7925_regd_be_ctrl(phy->dev, evt->alpha2); + if (mdev->alpha2[0] != '0' && mdev->alpha2[1] != '0') break; @@ -1823,6 +1868,10 @@ mt7925_change_chanctx(struct ieee80211_hw *hw, link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id); mt7925_mcu_set_chctx(mvif->phy->mt76, &mconf->mt76, link_conf, ctx); + + if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING) + mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76, + link_conf, ctx); } } @@ -1871,6 +1920,9 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw, mt7925_mcu_sta_update(dev, NULL, vif, true, MT76_STA_INFO_STATE_ASSOC); mt7925_mcu_set_beacon_filter(dev, vif, vif->cfg.assoc); + + if (ieee80211_vif_is_mld(vif)) + mvif->mlo_pm_state = MT792x_MLO_LINK_ASSOC; } if (changed & BSS_CHANGED_ARP_FILTER) { @@ -1881,9 +1933,19 @@ static void mt7925_vif_cfg_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_PS) { - for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { - bss_conf = mt792x_vif_to_bss_conf(vif, i); + if (hweight16(mvif->valid_links) < 2) { + /* legacy */ + bss_conf = &vif->bss_conf; mt7925_mcu_uni_bss_ps(dev, bss_conf); + } else { + if (mvif->mlo_pm_state == MT792x_MLO_LINK_ASSOC) { + mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS_PENDING; + } else if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS) { + for_each_set_bit(i, &valid, IEEE80211_MLD_MAX_NUM_LINKS) { + bss_conf = mt792x_vif_to_bss_conf(vif, i); + mt7925_mcu_uni_bss_ps(dev, bss_conf); + } + } } } @@ -1899,8 +1961,10 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw, struct mt792x_phy *phy = mt792x_hw_phy(hw); struct mt792x_dev *dev = mt792x_hw_dev(hw); struct mt792x_bss_conf *mconf; + struct ieee80211_bss_conf *link_conf; mconf = mt792x_vif_to_link(mvif, info->link_id); + link_conf = mt792x_vif_to_bss_conf(vif, mconf->link_id); mt792x_mutex_acquire(dev); @@ -1934,13 +1998,18 @@ static void mt7925_link_info_changed(struct ieee80211_hw *hw, if (changed & (BSS_CHANGED_QOS | BSS_CHANGED_BEACON_ENABLED)) mt7925_mcu_set_tx(dev, info); - if (changed & BSS_CHANGED_BSSID) { - if (ieee80211_vif_is_mld(vif) && - hweight16(mvif->valid_links) == 2) - /* Indicate the secondary setup done */ - mt7925_mcu_uni_bss_bcnft(dev, info, true); + if (mvif->mlo_pm_state == MT792x_MLO_CHANGED_PS_PENDING) { + /* Indicate the secondary setup done */ + mt7925_mcu_uni_bss_bcnft(dev, info, true); + + ieee80211_queue_delayed_work(hw, &dev->mlo_pm_work, 5 * HZ); + mvif->mlo_pm_state = MT792x_MLO_CHANGED_PS; } + if (changed & IEEE80211_CHANCTX_CHANGE_PUNCTURING) + mt7925_mcu_set_eht_pp(mvif->phy->mt76, &mconf->mt76, + link_conf, NULL); + mt792x_mutex_release(dev); } @@ -2022,8 +2091,6 @@ mt7925_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, goto free; if (mconf != &mvif->bss_conf) { - mt7925_mcu_set_bss_pm(dev, link_conf, true); - err = mt7925_set_mlo_roc(phy, &mvif->bss_conf, vif->active_links); if (err < 0) @@ -2141,6 +2208,18 @@ static void mt7925_unassign_vif_chanctx(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); } +static void mt7925_rfkill_poll(struct ieee80211_hw *hw) +{ + struct mt792x_phy *phy = mt792x_hw_phy(hw); + int ret; + + mt792x_mutex_acquire(phy->dev); + ret = mt7925_mcu_wf_rf_pin_ctrl(phy); + mt792x_mutex_release(phy->dev); + + wiphy_rfkill_set_hw_state(hw->wiphy, ret == 0); +} + const struct ieee80211_ops mt7925_ops = { .tx = mt792x_tx, .start = mt7925_start, @@ -2180,6 +2259,8 @@ const struct ieee80211_ops mt7925_ops = { .sta_statistics = mt792x_sta_statistics, .sched_scan_start = mt7925_start_sched_scan, .sched_scan_stop = mt7925_stop_sched_scan, + CFG80211_TESTMODE_CMD(mt7925_testmode_cmd) + CFG80211_TESTMODE_DUMP(mt7925_testmode_dump) #ifdef CONFIG_PM .suspend = mt7925_suspend, .resume = mt7925_resume, @@ -2201,6 +2282,7 @@ const struct ieee80211_ops mt7925_ops = { .link_info_changed = mt7925_link_info_changed, .change_vif_links = mt7925_change_vif_links, .change_sta_links = mt7925_change_sta_links, + .rfkill_poll = mt7925_rfkill_poll, }; EXPORT_SYMBOL_GPL(mt7925_ops); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c index 15815ad84713..b8542be0d945 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.c @@ -348,14 +348,10 @@ mt7925_mcu_handle_hif_ctrl_basic(struct mt792x_dev *dev, struct tlv *tlv) basic = (struct mt7925_mcu_hif_ctrl_basic_tlv *)tlv; if (basic->hifsuspend) { - if (basic->hif_tx_traffic_status == HIF_TRAFFIC_IDLE && - basic->hif_rx_traffic_status == HIF_TRAFFIC_IDLE) - /* success */ - dev->hif_idle = true; - else - /* busy */ - /* invalid */ - dev->hif_idle = false; + dev->hif_idle = true; + if (!(basic->hif_tx_traffic_status == HIF_TRAFFIC_IDLE && + basic->hif_rx_traffic_status == HIF_TRAFFIC_IDLE)) + dev_info(dev->mt76.dev, "Hif traffic not idle.\n"); } else { dev->hif_resumed = true; } @@ -576,10 +572,10 @@ void mt7925_mcu_rx_event(struct mt792x_dev *dev, struct sk_buff *skb) static int mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, - struct mt76_wcid *wcid, struct ieee80211_ampdu_params *params, bool enable, bool tx) { + struct mt76_wcid *wcid = (struct mt76_wcid *)params->sta->drv_priv; struct sta_rec_ba_uni *ba; struct sk_buff *skb; struct tlv *tlv; @@ -607,60 +603,76 @@ mt7925_mcu_sta_ba(struct mt76_dev *dev, struct mt76_vif_link *mvif, /** starec & wtbl **/ int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params, bool enable) { struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; - struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - struct mt792x_link_sta *mlink; - struct mt792x_bss_conf *mconf; - unsigned long usable_links = ieee80211_vif_usable_links(vif); - struct mt76_wcid *wcid; - u8 link_id, ret; - - for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { - mconf = mt792x_vif_to_link(mvif, link_id); - mlink = mt792x_sta_to_link(msta, link_id); - wcid = &mlink->wcid; + struct mt792x_vif *mvif = msta->vif; - if (enable && !params->amsdu) - mlink->wcid.amsdu = false; + if (enable && !params->amsdu) + msta->deflink.wcid.amsdu = false; - ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, wcid, params, - enable, true); - if (ret < 0) - break; - } - - return ret; + return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, + enable, true); } int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params, bool enable) { struct mt792x_sta *msta = (struct mt792x_sta *)params->sta->drv_priv; - struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv; - struct mt792x_link_sta *mlink; - struct mt792x_bss_conf *mconf; - unsigned long usable_links = ieee80211_vif_usable_links(vif); - struct mt76_wcid *wcid; - u8 link_id, ret; + struct mt792x_vif *mvif = msta->vif; - for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { - mconf = mt792x_vif_to_link(mvif, link_id); - mlink = mt792x_sta_to_link(msta, link_id); - wcid = &mlink->wcid; + return mt7925_mcu_sta_ba(&dev->mt76, &mvif->bss_conf.mt76, params, + enable, false); +} - ret = mt7925_mcu_sta_ba(&dev->mt76, &mconf->mt76, wcid, params, - enable, false); - if (ret < 0) - break; - } +static int mt7925_mcu_read_eeprom(struct mt792x_dev *dev, u32 offset, u8 *val) +{ + struct { + u8 rsv[4]; - return ret; + __le16 tag; + __le16 len; + + __le32 addr; + __le32 valid; + u8 data[MT7925_EEPROM_BLOCK_SIZE]; + } __packed req = { + .tag = cpu_to_le16(1), + .len = cpu_to_le16(sizeof(req) - 4), + .addr = cpu_to_le32(round_down(offset, + MT7925_EEPROM_BLOCK_SIZE)), + }; + struct evt { + u8 rsv[4]; + + __le16 tag; + __le16 len; + + __le32 ver; + __le32 addr; + __le32 valid; + __le32 size; + __le32 magic_num; + __le32 type; + __le32 rsv1[4]; + u8 data[32]; + } __packed *res; + struct sk_buff *skb; + int ret; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_WM_UNI_CMD_QUERY(EFUSE_CTRL), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + res = (struct evt *)skb->data; + *val = res->data[offset % MT7925_EEPROM_BLOCK_SIZE]; + + dev_kfree_skb(skb); + + return 0; } static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) @@ -671,13 +683,21 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) struct mt76_dev *mdev = &dev->mt76; struct mt792x_phy *phy = &dev->phy; const struct firmware *fw; + u8 *clc_base = NULL, hw_encap = 0; int ret, i, len, offset = 0; - u8 *clc_base = NULL; + dev->phy.clc_chan_conf = 0xff; if (mt7925_disable_clc || mt76_is_usb(&dev->mt76)) return 0; + if (mt76_is_mmio(&dev->mt76)) { + ret = mt7925_mcu_read_eeprom(dev, MT_EE_HW_TYPE, &hw_encap); + if (ret) + return ret; + hw_encap = u8_get_bits(hw_encap, MT_EE_HW_TYPE_ENCAP); + } + ret = request_firmware(&fw, fw_name, mdev->dev); if (ret) return ret; @@ -722,6 +742,12 @@ static int mt7925_load_clc(struct mt792x_dev *dev, const char *fw_name) if (phy->clc[clc->idx]) continue; + /* header content sanity */ + if ((clc->idx == MT792x_CLC_BE_CTRL && + u8_get_bits(clc->t2.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) || + u8_get_bits(clc->t0.type, MT_EE_HW_TYPE_ENCAP) != hw_encap) + continue; + phy->clc[clc->idx] = devm_kmemdup(mdev->dev, clc, le32_to_cpu(clc->len), GFP_KERNEL); @@ -757,7 +783,7 @@ int mt7925_mcu_fw_log_2_host(struct mt792x_dev *dev, u8 ctrl) int ret; ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(WSYS_CONFIG), - &req, sizeof(req), false, NULL); + &req, sizeof(req), true, NULL); return ret; } @@ -828,7 +854,6 @@ mt7925_mcu_parse_phy_cap(struct mt792x_dev *dev, char *data) mdev->phy.chainmask = mdev->phy.antenna_mask; mdev->phy.cap.has_2ghz = cap->hw_path & BIT(WF0_24G); mdev->phy.cap.has_5ghz = cap->hw_path & BIT(WF0_5G); - dev->has_eht = cap->eht; } static void @@ -949,6 +974,23 @@ int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable) } EXPORT_SYMBOL_GPL(mt7925_mcu_set_deep_sleep); +int mt7925_mcu_set_thermal_protect(struct mt792x_dev *dev) +{ + char cmd[64]; + int ret = 0; + + snprintf(cmd, sizeof(cmd), "ThermalProtGband %d %d %d %d %d %d %d %d %d %d", + 0, 100, 90, 80, 30, 1, 1, 115, 105, 5); + ret = mt7925_mcu_chip_config(dev, cmd); + + snprintf(cmd, sizeof(cmd), "ThermalProtAband %d %d %d %d %d %d %d %d %d %d", + 1, 100, 90, 80, 30, 1, 1, 115, 105, 5); + ret |= mt7925_mcu_chip_config(dev, cmd); + + return ret; +} +EXPORT_SYMBOL_GPL(mt7925_mcu_set_thermal_protect); + int mt7925_run_firmware(struct mt792x_dev *dev) { int err; @@ -1399,7 +1441,7 @@ int mt7925_mcu_set_eeprom(struct mt792x_dev *dev) }; return mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_CMD(EFUSE_CTRL), - &req, sizeof(req), false, NULL); + &req, sizeof(req), true, NULL); } EXPORT_SYMBOL_GPL(mt7925_mcu_set_eeprom); @@ -1850,49 +1892,6 @@ mt7925_mcu_sta_mld_tlv(struct sk_buff *skb, } } -static int -mt7925_mcu_sta_cmd(struct mt76_phy *phy, - struct mt76_sta_cmd_info *info) -{ - struct mt76_vif_link *mvif = (struct mt76_vif_link *)info->vif->drv_priv; - struct mt76_dev *dev = phy->dev; - struct sk_buff *skb; - int conn_state; - - skb = __mt76_connac_mcu_alloc_sta_req(dev, mvif, info->wcid, - MT7925_STA_UPDATE_MAX_SIZE); - if (IS_ERR(skb)) - return PTR_ERR(skb); - - conn_state = info->enable ? CONN_STATE_PORT_SECURE : - CONN_STATE_DISCONNECT; - if (info->link_sta) - mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf, - info->link_sta, - conn_state, info->newly); - if (info->link_sta && info->enable) { - mt7925_mcu_sta_phy_tlv(skb, info->vif, info->link_sta); - mt7925_mcu_sta_ht_tlv(skb, info->link_sta); - mt7925_mcu_sta_vht_tlv(skb, info->link_sta); - mt76_connac_mcu_sta_uapsd(skb, info->vif, info->link_sta->sta); - mt7925_mcu_sta_amsdu_tlv(skb, info->vif, info->link_sta); - mt7925_mcu_sta_he_tlv(skb, info->link_sta); - mt7925_mcu_sta_he_6g_tlv(skb, info->link_sta); - mt7925_mcu_sta_eht_tlv(skb, info->link_sta); - mt7925_mcu_sta_rate_ctrl_tlv(skb, info->vif, - info->link_sta); - mt7925_mcu_sta_state_v2_tlv(phy, skb, info->link_sta, - info->vif, info->rcpi, - info->state); - mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta); - } - - if (info->enable) - mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta); - - return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); -} - static void mt7925_mcu_sta_remove_tlv(struct sk_buff *skb) { @@ -1905,8 +1904,8 @@ mt7925_mcu_sta_remove_tlv(struct sk_buff *skb) } static int -mt7925_mcu_mlo_sta_cmd(struct mt76_phy *phy, - struct mt76_sta_cmd_info *info) +mt7925_mcu_sta_cmd(struct mt76_phy *phy, + struct mt76_sta_cmd_info *info) { struct mt792x_vif *mvif = (struct mt792x_vif *)info->vif->drv_priv; struct mt76_dev *dev = phy->dev; @@ -1920,12 +1919,10 @@ mt7925_mcu_mlo_sta_cmd(struct mt76_phy *phy, if (IS_ERR(skb)) return PTR_ERR(skb); - if (info->enable) + if (info->enable && info->link_sta) { mt76_connac_mcu_sta_basic_tlv(dev, skb, info->link_conf, info->link_sta, info->enable, info->newly); - - if (info->enable && info->link_sta) { mt7925_mcu_sta_phy_tlv(skb, info->vif, info->link_sta); mt7925_mcu_sta_ht_tlv(skb, info->link_sta); mt7925_mcu_sta_vht_tlv(skb, info->link_sta); @@ -1944,14 +1941,14 @@ mt7925_mcu_mlo_sta_cmd(struct mt76_phy *phy, mt7925_mcu_sta_mld_tlv(skb, info->vif, info->link_sta->sta); mt7925_mcu_sta_eht_mld_tlv(skb, info->vif, info->link_sta->sta); } - - mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta); } if (!info->enable) { mt7925_mcu_sta_remove_tlv(skb); mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv)); + } else { + mt7925_mcu_sta_hdr_trans_tlv(skb, info->vif, info->link_sta); } return mt76_mcu_skb_send_msg(dev, skb, info->cmd, true); @@ -1976,25 +1973,15 @@ int mt7925_mcu_sta_update(struct mt792x_dev *dev, }; struct mt792x_sta *msta; struct mt792x_link_sta *mlink; - int err; if (link_sta) { msta = (struct mt792x_sta *)link_sta->sta->drv_priv; mlink = mt792x_sta_to_link(msta, link_sta->link_id); } info.wcid = link_sta ? &mlink->wcid : &mvif->sta.deflink.wcid; + info.newly = state != MT76_STA_INFO_STATE_ASSOC; - if (link_sta) - info.newly = state != MT76_STA_INFO_STATE_ASSOC; - else - info.newly = state == MT76_STA_INFO_STATE_ASSOC ? false : true; - - if (ieee80211_vif_is_mld(vif)) - err = mt7925_mcu_mlo_sta_cmd(&dev->mphy, &info); - else - err = mt7925_mcu_sta_cmd(&dev->mphy, &info); - - return err; + return mt7925_mcu_sta_cmd(&dev->mphy, &info); } int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, @@ -2076,8 +2063,6 @@ int mt7925_mcu_set_sniffer(struct mt792x_dev *dev, struct ieee80211_vif *vif, }, }; - mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true); - return mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(SNIFFER), &req, sizeof(req), true); } @@ -2328,6 +2313,40 @@ __mt7925_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int return skb; } +static +void mt7925_mcu_bss_eht_tlv(struct sk_buff *skb, struct mt76_phy *phy, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct cfg80211_chan_def *chandef = ctx ? &ctx->def : + &link_conf->chanreq.oper; + + struct bss_eht_tlv *req; + struct tlv *tlv; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_BSS_INFO_EHT, sizeof(*req)); + req = (struct bss_eht_tlv *)tlv; + req->is_eth_dscb_present = chandef->punctured ? 1 : 0; + req->eht_dis_sub_chan_bitmap = cpu_to_le16(chandef->punctured); +} + +int mt7925_mcu_set_eht_pp(struct mt76_phy *phy, struct mt76_vif_link *mvif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx) +{ + struct sk_buff *skb; + + skb = __mt7925_mcu_alloc_bss_req(phy->dev, mvif, + MT7925_BSS_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt7925_mcu_bss_eht_tlv(skb, phy, link_conf, ctx); + + return mt76_mcu_skb_send_msg(phy->dev, skb, + MCU_UNI_CMD(BSS_INFO_UPDATE), true); +} + int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx) @@ -2559,6 +2578,7 @@ mt7925_mcu_bss_mld_tlv(struct sk_buff *skb, struct ieee80211_vif *vif = link_conf->vif; struct mt792x_bss_conf *mconf = mt792x_link_conf_to_mconf(link_conf); struct mt792x_vif *mvif = (struct mt792x_vif *)link_conf->vif->drv_priv; + struct mt792x_phy *phy = mvif->phy; struct bss_mld_tlv *mld; struct tlv *tlv; bool is_mld; @@ -2574,8 +2594,13 @@ mt7925_mcu_bss_mld_tlv(struct sk_buff *skb, mld->group_mld_id = is_mld ? mvif->bss_conf.mt76.idx : 0xff; mld->own_mld_id = mconf->mt76.idx + 32; mld->remap_idx = 0xff; - mld->eml_enable = !!(link_conf->vif->cfg.eml_cap & - IEEE80211_EML_CAP_EMLSR_SUPP); + + if (phy->chip_cap & MT792x_CHIP_CAP_MLO_EML_EN) { + mld->eml_enable = !!(link_conf->vif->cfg.eml_cap & + IEEE80211_EML_CAP_EMLSR_SUPP); + } else { + mld->eml_enable = 0; + } memcpy(mld->mac_addr, vif->addr, ETH_ALEN); } @@ -2668,6 +2693,62 @@ int mt7925_mcu_set_timing(struct mt792x_phy *phy, MCU_UNI_CMD(BSS_INFO_UPDATE), true); } +void mt7925_mcu_del_dev(struct mt76_dev *mdev, + struct ieee80211_vif *vif) +{ + struct mt76_vif_link *mvif = (struct mt76_vif_link *)vif->drv_priv; + struct { + struct { + u8 omac_idx; + u8 band_idx; + __le16 pad; + } __packed hdr; + struct req_tlv { + __le16 tag; + __le16 len; + u8 active; + u8 link_idx; /* hw link idx */ + u8 omac_addr[ETH_ALEN]; + } __packed tlv; + } dev_req = { + .tlv = { + .tag = cpu_to_le16(DEV_INFO_ACTIVE), + .len = cpu_to_le16(sizeof(struct req_tlv)), + .active = true, + }, + }; + struct { + struct { + u8 bss_idx; + u8 pad[3]; + } __packed hdr; + struct mt76_connac_bss_basic_tlv basic; + } basic_req = { + .basic = { + .tag = cpu_to_le16(UNI_BSS_INFO_BASIC), + .len = cpu_to_le16(sizeof(struct mt76_connac_bss_basic_tlv)), + .active = true, + .conn_state = 1, + }, + }; + + dev_req.hdr.omac_idx = mvif->omac_idx; + dev_req.hdr.band_idx = mvif->band_idx; + + basic_req.hdr.bss_idx = mvif->idx; + basic_req.basic.omac_idx = mvif->omac_idx; + basic_req.basic.band_idx = mvif->band_idx; + basic_req.basic.link_idx = mvif->link_idx; + + mt76_mcu_send_msg(mdev, MCU_UNI_CMD(BSS_INFO_UPDATE), + &basic_req, sizeof(basic_req), true); + + /* recovery omac address for the legacy interface */ + memcpy(dev_req.tlv.omac_addr, vif->addr, ETH_ALEN); + mt76_mcu_send_msg(mdev, MCU_UNI_CMD(DEV_INFO_UPDATE), + &dev_req, sizeof(dev_req), true); +} + int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, struct ieee80211_chanctx_conf *ctx, struct ieee80211_bss_conf *link_conf, @@ -2732,7 +2813,7 @@ int mt7925_mcu_set_dbdc(struct mt76_phy *phy, bool enable) conf->band = 0; /* unused */ err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SET_DBDC_PARMS), - false); + true); return err; } @@ -2747,7 +2828,6 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct mt76_dev *mdev = phy->dev; struct mt76_connac_mcu_scan_channel *chan; struct sk_buff *skb; - struct scan_hdr_tlv *hdr; struct scan_req_tlv *req; struct scan_ssid_tlv *ssid; @@ -2758,9 +2838,12 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, struct tlv *tlv; int max_len; + if (test_bit(MT76_HW_SCANNING, &phy->state)) + return -EBUSY; + max_len = sizeof(*hdr) + sizeof(*req) + sizeof(*ssid) + - sizeof(*bssid) + sizeof(*chan_info) + - sizeof(*misc) + sizeof(*ie); + sizeof(*bssid) * MT7925_RNR_SCAN_MAX_BSSIDS + + sizeof(*chan_info) + sizeof(*misc) + sizeof(*ie); skb = mt76_mcu_msg_alloc(mdev, NULL, max_len); if (!skb) @@ -2783,6 +2866,8 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, for (i = 0; i < sreq->n_ssids; i++) { if (!sreq->ssids[i].ssid_len) continue; + if (i > MT7925_RNR_SCAN_MAX_BSSIDS) + break; ssid->ssids[i].ssid_len = cpu_to_le32(sreq->ssids[i].ssid_len); memcpy(ssid->ssids[i].ssid, sreq->ssids[i].ssid, @@ -2792,10 +2877,31 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, ssid->ssid_type = n_ssids ? BIT(2) : BIT(0); ssid->ssids_num = n_ssids; - tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, sizeof(*bssid)); - bssid = (struct scan_bssid_tlv *)tlv; + if (sreq->n_6ghz_params) { + u8 j; + + mt76_connac_mcu_build_rnr_scan_param(mdev, sreq); + + for (j = 0; j < mdev->rnr.bssid_num; j++) { + if (j > MT7925_RNR_SCAN_MAX_BSSIDS) + break; + + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, + sizeof(*bssid)); + bssid = (struct scan_bssid_tlv *)tlv; - memcpy(bssid->bssid, sreq->bssid, ETH_ALEN); + ether_addr_copy(bssid->bssid, mdev->rnr.bssid[j]); + bssid->match_ch = mdev->rnr.channel[j]; + bssid->match_ssid_ind = MT7925_RNR_SCAN_MAX_BSSIDS; + bssid->match_short_ssid_ind = MT7925_RNR_SCAN_MAX_BSSIDS; + } + req->scan_func |= SCAN_FUNC_RNR_SCAN; + } else { + tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_BSSID, sizeof(*bssid)); + bssid = (struct scan_bssid_tlv *)tlv; + + ether_addr_copy(bssid->bssid, sreq->bssid); + } tlv = mt76_connac_mcu_add_tlv(skb, UNI_SCAN_CHANNEL, sizeof(*chan_info)); chan_info = (struct scan_chan_info_tlv *)tlv; @@ -2837,7 +2943,7 @@ int mt7925_mcu_hw_scan(struct mt76_phy *phy, struct ieee80211_vif *vif, } err = mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), - false); + true); if (err < 0) clear_bit(MT76_HW_SCANNING, &phy->state); @@ -2943,7 +3049,7 @@ int mt7925_mcu_sched_scan_req(struct mt76_phy *phy, } return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), - false); + true); } EXPORT_SYMBOL_GPL(mt7925_mcu_sched_scan_req); @@ -2979,7 +3085,7 @@ mt7925_mcu_sched_scan_enable(struct mt76_phy *phy, clear_bit(MT76_HW_SCHED_SCANNING, &phy->state); return mt76_mcu_skb_send_msg(mdev, skb, MCU_UNI_CMD(SCAN_REQ), - false); + true); } int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy, @@ -3018,7 +3124,7 @@ int mt7925_mcu_cancel_hw_scan(struct mt76_phy *phy, } return mt76_mcu_send_msg(phy->dev, MCU_UNI_CMD(SCAN_REQ), - &req, sizeof(req), false); + &req, sizeof(req), true); } EXPORT_SYMBOL_GPL(mt7925_mcu_cancel_hw_scan); @@ -3123,7 +3229,7 @@ int mt7925_mcu_set_channel_domain(struct mt76_phy *phy) memcpy(__skb_push(skb, sizeof(req)), &req, sizeof(req)); return mt76_mcu_skb_send_msg(dev, skb, MCU_UNI_CMD(SET_DOMAIN_INFO), - false); + true); } EXPORT_SYMBOL_GPL(mt7925_mcu_set_channel_domain); @@ -3155,16 +3261,17 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, .idx = idx, .env = env_cap, - .acpi_conf = mt792x_acpi_get_flags(&dev->phy), }; int ret, valid_cnt = 0; - u8 i, *pos; + u8 *pos, *last_pos; if (!clc) return 0; - pos = clc->data + sizeof(*seg) * clc->nr_seg; - for (i = 0; i < clc->nr_country; i++) { + req.ver = clc->ver; + pos = clc->data + sizeof(*seg) * clc->t0.nr_seg; + last_pos = clc->data + le32_to_cpu(*(__le32 *)(clc->data + 4)); + while (pos < last_pos) { struct mt7925_clc_rule *rule = (struct mt7925_clc_rule *)pos; pos += sizeof(*rule); @@ -3179,6 +3286,7 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, memcpy(req.type, rule->type, 2); req.size = cpu_to_le16(seg->len); + dev->phy.clc_chan_conf = clc->ver == 1 ? 0xff : rule->flag; skb = __mt76_mcu_msg_alloc(&dev->mt76, &req, le16_to_cpu(req.size) + sizeof(req), sizeof(req), GFP_KERNEL); @@ -3194,8 +3302,10 @@ __mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, valid_cnt++; } - if (!valid_cnt) + if (!valid_cnt) { + dev->phy.clc_chan_conf = 0xff; return -ENOENT; + } return 0; } @@ -3208,6 +3318,9 @@ int mt7925_mcu_set_clc(struct mt792x_dev *dev, u8 *alpha2, /* submit all clc config */ for (i = 0; i < ARRAY_SIZE(phy->clc); i++) { + if (i == MT792x_CLC_BE_CTRL) + continue; + ret = __mt7925_mcu_set_clc(dev, alpha2, env_cap, phy->clc[i], i); @@ -3266,6 +3379,18 @@ int mt7925_mcu_fill_message(struct mt76_dev *mdev, struct sk_buff *skb, else uni_txd->option = MCU_CMD_UNI_EXT_ACK; + if (cmd == MCU_UNI_CMD(HIF_CTRL) || + cmd == MCU_UNI_CMD(CHIP_CONFIG)) + uni_txd->option &= ~MCU_CMD_ACK; + + if (mcu_cmd == MCU_UNI_CMD_TESTMODE_CTRL || + mcu_cmd == MCU_UNI_CMD_TESTMODE_RX_STAT) { + if (cmd & __MCU_CMD_FIELD_QUERY) + uni_txd->option = 0x2; + else + uni_txd->option = 0x6; + } + goto exit; } @@ -3557,6 +3682,43 @@ int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy) return 0; } +int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy) +{ +#define UNI_CMD_RADIO_STATUS_GET 0 + struct mt792x_dev *dev = phy->dev; + struct sk_buff *skb; + int ret; + struct { + __le16 tag; + __le16 len; + u8 rsv[4]; + } __packed req = { + .tag = UNI_CMD_RADIO_STATUS_GET, + .len = cpu_to_le16(sizeof(req)), + }; + struct mt7925_radio_status_event { + __le16 tag; + __le16 len; + + u8 data; + u8 rsv[3]; + } __packed *status; + + ret = mt76_mcu_send_and_get_msg(&dev->mt76, + MCU_UNI_CMD(RADIO_STATUS), + &req, sizeof(req), true, &skb); + if (ret) + return ret; + + skb_pull(skb, sizeof(struct tlv)); + status = (struct mt7925_radio_status_event *)skb->data; + ret = status->data; + + dev_kfree_skb(skb); + + return ret; +} + int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, u8 bit_op, u32 bit_map) { diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h index 1e47d2c61b54..ee6fb16e83c5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mcu.h @@ -104,13 +104,6 @@ enum { MT7925_TM_WIFISPECTRUM, }; -struct mt7925_rftest_cmd { - u8 action; - u8 rsv[3]; - __le32 param0; - __le32 param1; -} __packed; - struct mt7925_rftest_evt { __le32 param0; __le32 param1; @@ -196,6 +189,7 @@ enum { UNI_SNIFFER_CONFIG, }; +#define MT7925_RNR_SCAN_MAX_BSSIDS 10 struct scan_hdr_tlv { /* fixed field */ u8 seq_num; @@ -223,7 +217,7 @@ struct scan_req_tlv { __le16 timeout_value; __le16 probe_delay_time; __le32 func_mask_ext; -}; +} __packed; struct scan_ssid_tlv { __le16 tag; @@ -235,9 +229,10 @@ struct scan_ssid_tlv { * BIT(2) + ssid_type_ext BIT(0) specified SSID only */ u8 ssids_num; - u8 pad[2]; - struct mt76_connac_mcu_scan_ssid ssids[4]; -}; + u8 is_short_ssid; + u8 pad; + struct mt76_connac_mcu_scan_ssid ssids[MT7925_RNR_SCAN_MAX_BSSIDS]; +} __packed; struct scan_bssid_tlv { __le16 tag; @@ -247,8 +242,9 @@ struct scan_bssid_tlv { u8 match_ch; u8 match_ssid_ind; u8 rcpi; - u8 pad[3]; -}; + u8 match_short_ssid_ind; + u8 pad[2]; +} __packed; struct scan_chan_info_tlv { __le16 tag; @@ -264,7 +260,7 @@ struct scan_chan_info_tlv { u8 channels_num; /* valid when channel_type is 4 */ u8 pad[2]; struct mt76_connac_mcu_scan_channel channels[64]; -}; +} __packed; struct scan_ie_tlv { __le16 tag; @@ -372,6 +368,19 @@ struct bss_mld_tlv { u8 __rsv[3]; } __packed; +struct bss_eht_tlv { + __le16 tag; + __le16 len; + u8 is_eht_op_present; + u8 is_eth_dscb_present; + u8 eht_ctrl; + u8 eht_ccfs0; + u8 eht_ccfs1; + u8 pad1; + __le16 eht_dis_sub_chan_bitmap; + u8 pad2[4]; +} __packed; + struct sta_rec_ba_uni { __le16 tag; __le16 len; @@ -566,8 +575,8 @@ struct mt7925_wow_pattern_tlv { u8 offset; u8 mask[MT76_CONNAC_WOW_MASK_MAX_LEN]; u8 pattern[MT76_CONNAC_WOW_PATTEN_MAX_LEN]; - u8 rsv[7]; -} __packed; + u8 rsv[4]; +}; struct roc_acquire_tlv { __le16 tag; @@ -589,6 +598,47 @@ struct roc_acquire_tlv { u8 rsv[3]; } __packed; +enum ENUM_CMD_TEST_CTRL_ACT { + CMD_TEST_CTRL_ACT_SWITCH_MODE = 0, + CMD_TEST_CTRL_ACT_SET_AT = 1, + CMD_TEST_CTRL_ACT_GET_AT = 2, + CMD_TEST_CTRL_ACT_SET_AT_ENG = 3, + CMD_TEST_CTRL_ACT_GET_AT_ENG = 4, + CMD_TEST_CTRL_ACT_NUM +}; + +enum ENUM_CMD_TEST_CTRL_ACT_SWITCH_MODE_OP { + CMD_TEST_CTRL_ACT_SWITCH_MODE_NORMAL = 0, + CMD_TEST_CTRL_ACT_SWITCH_MODE_RF_TEST = 1, + CMD_TEST_CTRL_ACT_SWITCH_MODE_ICAP = 2, + CMD_TEST_CTRL_ACT_SWITCH_MODE_NUM +}; + +union testmode_data { + __le32 op_mode; + __le32 channel_freq; + u8 rf_at_info[84]; +}; + +union testmode_evt { + __le32 op_mode; + __le32 channel_freq; + u8 rf_at_info[1024]; +}; + +struct uni_cmd_testmode_ctrl { + u16 tag; + u16 length; + u8 action; + u8 reserved[3]; + union testmode_data data; +} __packed; + +struct mt7925_rftest_cmd { + u8 padding[4]; + struct uni_cmd_testmode_ctrl ctrl; +} __packed; + static inline enum connac3_mcu_cipher_type mt7925_mcu_get_cipher(int cipher) { @@ -627,6 +677,8 @@ int mt7925_mcu_sched_scan_req(struct mt76_phy *phy, int mt7925_mcu_sched_scan_enable(struct mt76_phy *phy, struct ieee80211_vif *vif, bool enable); +void mt7925_mcu_del_dev(struct mt76_dev *mdev, + struct ieee80211_vif *vif); int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, struct ieee80211_chanctx_conf *ctx, struct ieee80211_bss_conf *link_conf, @@ -635,11 +687,15 @@ int mt7925_mcu_add_bss_info(struct mt792x_phy *phy, int mt7925_mcu_set_timing(struct mt792x_phy *phy, struct ieee80211_bss_conf *link_conf); int mt7925_mcu_set_deep_sleep(struct mt792x_dev *dev, bool enable); +int mt7925_mcu_set_thermal_protect(struct mt792x_dev *dev); int mt7925_mcu_set_channel_domain(struct mt76_phy *phy); int mt7925_mcu_set_radio_en(struct mt792x_phy *phy, bool enable); int mt7925_mcu_set_chctx(struct mt76_phy *phy, struct mt76_vif_link *mvif, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *ctx); +int mt7925_mcu_set_eht_pp(struct mt76_phy *phy, struct mt76_vif_link *mvif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_chanctx_conf *ctx); int mt7925_mcu_set_rate_txpower(struct mt76_phy *phy); int mt7925_mcu_update_arp_filter(struct mt76_dev *dev, struct ieee80211_bss_conf *link_conf); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h index 8707b5d04743..1b165d0d8bd3 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/mt7925.h @@ -137,11 +137,18 @@ enum { MT7925_CLC_MAX_NUM, }; +struct mt7925_clc_rule_v2 { + u32 flag; + u8 alpha2[2]; + u8 rsv[10]; +} __packed; + struct mt7925_clc_rule { u8 alpha2[2]; u8 type[2]; u8 seg_idx; - u8 rsv[3]; + u8 flag; /* UNII4~8 ctrl flag */ + u8 rsv[2]; } __packed; struct mt7925_clc_segment { @@ -152,14 +159,26 @@ struct mt7925_clc_segment { u8 rsv2[4]; } __packed; -struct mt7925_clc { - __le32 len; - u8 idx; - u8 ver; +struct mt7925_clc_type0 { u8 nr_country; u8 type; u8 nr_seg; u8 rsv[7]; +} __packed; + +struct mt7925_clc_type2 { + u8 type; + u8 rsv[9]; +} __packed; + +struct mt7925_clc { + __le32 len; + u8 idx; + u8 ver; + union { + struct mt7925_clc_type0 t0; + struct mt7925_clc_type2 t2; + }; u8 data[]; } __packed; @@ -167,9 +186,12 @@ enum mt7925_eeprom_field { MT_EE_CHIP_ID = 0x000, MT_EE_VERSION = 0x002, MT_EE_MAC_ADDR = 0x004, + MT_EE_HW_TYPE = 0xa71, __MT_EE_MAX = 0x9ff }; +#define MT_EE_HW_TYPE_ENCAP GENMASK(1, 0) + enum { TXPWR_USER, TXPWR_EEPROM, @@ -235,6 +257,7 @@ int mt7925_mcu_chip_config(struct mt792x_dev *dev, const char *cmd); int mt7925_mcu_set_rxfilter(struct mt792x_dev *dev, u32 fif, u8 bit_op, u32 bit_map); +void mt7925_regd_be_ctrl(struct mt792x_dev *dev, u8 *alpha2); void mt7925_regd_update(struct mt792x_dev *dev); int mt7925_mac_init(struct mt792x_dev *dev); int mt7925_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, @@ -263,13 +286,12 @@ int mt7925_mcu_set_beacon_filter(struct mt792x_dev *dev, struct ieee80211_vif *vif, bool enable); int mt7925_mcu_uni_tx_ba(struct mt792x_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params, bool enable); int mt7925_mcu_uni_rx_ba(struct mt792x_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_ampdu_params *params, bool enable); +void mt7925_mlo_pm_work(struct work_struct *work); void mt7925_scan_work(struct work_struct *work); void mt7925_roc_work(struct work_struct *work); int mt7925_mcu_uni_bss_ps(struct mt792x_dev *dev, @@ -343,5 +365,11 @@ int mt7925_mcu_wtbl_update_hdr_trans(struct mt792x_dev *dev, struct ieee80211_vif *vif, struct ieee80211_sta *sta, int link_id); +int mt7925_mcu_wf_rf_pin_ctrl(struct mt792x_phy *phy); + +int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len); +int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, + struct netlink_callback *cb, void *data, int len); #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c index c7b5dc1dbb34..89dc30f7c6b7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7925/pci.c @@ -31,6 +31,10 @@ static void mt7925e_unregister_device(struct mt792x_dev *dev) { int i; struct mt76_connac_pm *pm = &dev->pm; + struct ieee80211_hw *hw = mt76_hw(dev); + + if (dev->phy.chip_cap & MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN) + wiphy_rfkill_stop_polling(hw->wiphy); cancel_work_sync(&dev->init_work); mt76_unregister_device(&dev->mt76); @@ -490,9 +494,6 @@ static int mt7925_pci_suspend(struct device *device) /* disable interrupt */ mt76_wr(dev, dev->irq_map->host_irq_enable, 0); - mt76_wr(dev, MT_WFDMA0_HOST_INT_DIS, - dev->irq_map->tx.all_complete_mask | - MT_INT_RX_DONE_ALL | MT_INT_MCU_CMD); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0x0); diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h index 985794a40c1a..547489092c29 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7925/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7925/regs.h @@ -28,7 +28,7 @@ #define MT_MDP_TO_HIF 0 #define MT_MDP_TO_WM 1 -#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x228) +#define MT_WFDMA0_HOST_INT_ENA MT_WFDMA0(0x204) #define MT_WFDMA0_HOST_INT_DIS MT_WFDMA0(0x22c) #define HOST_RX_DONE_INT_ENA4 BIT(12) #define HOST_RX_DONE_INT_ENA5 BIT(13) diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c new file mode 100644 index 000000000000..a3c97164ba21 --- /dev/null +++ b/drivers/net/wireless/mediatek/mt76/mt7925/testmode.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: ISC + +#include "mt7925.h" +#include "mcu.h" + +#define MT7925_EVT_RSP_LEN 512 + +enum mt7925_testmode_attr { + MT7925_TM_ATTR_UNSPEC, + MT7925_TM_ATTR_SET, + MT7925_TM_ATTR_QUERY, + MT7925_TM_ATTR_RSP, + + /* keep last */ + NUM_MT7925_TM_ATTRS, + MT7925_TM_ATTR_MAX = NUM_MT7925_TM_ATTRS - 1, +}; + +struct mt7925_tm_cmd { + u8 padding[4]; + struct uni_cmd_testmode_ctrl c; +} __packed; + +struct mt7925_tm_evt { + u32 param0; + u32 param1; +} __packed; + +static const struct nla_policy mt7925_tm_policy[NUM_MT7925_TM_ATTRS] = { + [MT7925_TM_ATTR_SET] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)), + [MT7925_TM_ATTR_QUERY] = NLA_POLICY_EXACT_LEN(sizeof(struct mt7925_tm_cmd)), +}; + +static int +mt7925_tm_set(struct mt792x_dev *dev, struct mt7925_tm_cmd *req) +{ + struct mt7925_rftest_cmd cmd; + struct mt7925_rftest_cmd *pcmd = &cmd; + bool testmode = false, normal = false; + struct mt76_connac_pm *pm = &dev->pm; + struct mt76_phy *phy = &dev->mphy; + int ret = -ENOTCONN; + + memset(pcmd, 0, sizeof(*pcmd)); + memcpy(pcmd, req, sizeof(struct mt7925_tm_cmd)); + + mutex_lock(&dev->mt76.mutex); + + if (pcmd->ctrl.action == CMD_TEST_CTRL_ACT_SWITCH_MODE) { + if (pcmd->ctrl.data.op_mode == CMD_TEST_CTRL_ACT_SWITCH_MODE_NORMAL) + normal = true; + else + testmode = true; + } + + if (testmode) { + /* Make sure testmode running on full power mode */ + pm->enable = false; + cancel_delayed_work_sync(&pm->ps_work); + cancel_work_sync(&pm->wake_work); + __mt792x_mcu_drv_pmctrl(dev); + + phy->test.state = MT76_TM_STATE_ON; + } + + if (!mt76_testmode_enabled(phy)) + goto out; + + ret = mt76_mcu_send_msg(&dev->mt76, MCU_UNI_CMD(TESTMODE_CTRL), &cmd, + sizeof(cmd), false); + + if (ret) + goto out; + + if (normal) { + /* Switch back to the normal world */ + phy->test.state = MT76_TM_STATE_OFF; + pm->enable = true; + } +out: + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7925_tm_query(struct mt792x_dev *dev, struct mt7925_tm_cmd *req, + char *evt_resp) +{ + struct mt7925_rftest_cmd cmd; + char *pcmd = (char *)&cmd; + struct sk_buff *skb = NULL; + int ret = 1; + + memset(pcmd, 0, sizeof(*pcmd)); + memcpy(pcmd + 4, (char *)&req->c, sizeof(struct uni_cmd_testmode_ctrl)); + + if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_CTRL) + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_CTRL), + &cmd, sizeof(cmd), true, &skb); + else if (*((uint16_t *)req->padding) == MCU_UNI_CMD_TESTMODE_RX_STAT) + ret = mt76_mcu_send_and_get_msg(&dev->mt76, MCU_UNI_QUERY(TESTMODE_RX_STAT), + &cmd, sizeof(cmd), true, &skb); + + if (ret) + goto out; + + memcpy((char *)evt_resp, (char *)skb->data + 8, MT7925_EVT_RSP_LEN); + +out: + dev_kfree_skb(skb); + + return ret; +} + +int mt7925_testmode_cmd(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + void *data, int len) +{ + struct nlattr *tb[NUM_MT76_TM_ATTRS]; + struct mt76_phy *mphy = hw->priv; + struct mt792x_phy *phy = mphy->priv; + int err; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || + !(hw->conf.flags & IEEE80211_CONF_MONITOR)) + return -ENOTCONN; + + err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, + mt76_tm_policy, NULL); + if (err) + return err; + + if (tb[MT76_TM_ATTR_DRV_DATA]) { + struct nlattr *drv_tb[NUM_MT7925_TM_ATTRS], *data; + int ret; + + data = tb[MT76_TM_ATTR_DRV_DATA]; + ret = nla_parse_nested_deprecated(drv_tb, + MT7925_TM_ATTR_MAX, + data, mt7925_tm_policy, + NULL); + if (ret) + return ret; + + data = drv_tb[MT7925_TM_ATTR_SET]; + if (data) + return mt7925_tm_set(phy->dev, nla_data(data)); + } + + return -EINVAL; +} + +int mt7925_testmode_dump(struct ieee80211_hw *hw, struct sk_buff *msg, + struct netlink_callback *cb, void *data, int len) +{ + struct nlattr *tb[NUM_MT76_TM_ATTRS]; + struct mt76_phy *mphy = hw->priv; + struct mt792x_phy *phy = mphy->priv; + int err; + + if (!test_bit(MT76_STATE_RUNNING, &mphy->state) || + !(hw->conf.flags & IEEE80211_CONF_MONITOR) || + !mt76_testmode_enabled(mphy)) + return -ENOTCONN; + + if (cb->args[2]++ > 0) + return -ENOENT; + + err = nla_parse_deprecated(tb, MT76_TM_ATTR_MAX, data, len, + mt76_tm_policy, NULL); + if (err) + return err; + + if (tb[MT76_TM_ATTR_DRV_DATA]) { + struct nlattr *drv_tb[NUM_MT7925_TM_ATTRS], *data; + int ret; + + data = tb[MT76_TM_ATTR_DRV_DATA]; + ret = nla_parse_nested_deprecated(drv_tb, + MT7925_TM_ATTR_MAX, + data, mt7925_tm_policy, + NULL); + if (ret) + return ret; + + data = drv_tb[MT7925_TM_ATTR_QUERY]; + if (data) { + char evt_resp[MT7925_EVT_RSP_LEN]; + + err = mt7925_tm_query(phy->dev, nla_data(data), + evt_resp); + if (err) + return err; + + return nla_put(msg, MT7925_TM_ATTR_RSP, + sizeof(evt_resp), evt_resp); + } + } + + return -EINVAL; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt792x.h b/drivers/net/wireless/mediatek/mt76/mt792x.h index 32ed01a96bf7..e0359d431eca 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x.h @@ -27,8 +27,9 @@ #define MT792x_CHIP_CAP_CLC_EVT_EN BIT(0) #define MT792x_CHIP_CAP_RSSI_NOTIFY_EVT_EN BIT(1) -#define MT792x_CHIP_CAP_MLO_EVT_EN BIT(2) #define MT792x_CHIP_CAP_WF_RF_PIN_CTRL_EVT_EN BIT(3) +#define MT792x_CHIP_CAP_MLO_EN BIT(8) +#define MT792x_CHIP_CAP_MLO_EML_EN BIT(9) /* NOTE: used to map mt76_rates. idx may change if firmware expands table */ #define MT792x_BASIC_RATES_TBL 11 @@ -70,6 +71,7 @@ struct mt792x_fw_features { enum { MT792x_CLC_POWER, MT792x_CLC_POWER_EXT, + MT792x_CLC_BE_CTRL, MT792x_CLC_MAX_NUM, }; @@ -81,6 +83,13 @@ enum mt792x_reg_power_type { MT_AP_VLP, }; +enum mt792x_mlo_pm_state { + MT792x_MLO_LINK_DISASSOC, + MT792x_MLO_LINK_ASSOC, + MT792x_MLO_CHANGED_PS_PENDING, + MT792x_MLO_CHANGED_PS, +}; + DECLARE_EWMA(avg_signal, 10, 8) struct mt792x_link_sta { @@ -134,6 +143,7 @@ struct mt792x_vif { struct mt792x_phy *phy; u16 valid_links; u8 deflink_id; + enum mt792x_mlo_pm_state mlo_pm_state; struct work_struct csa_work; struct timer_list csa_timer; @@ -239,6 +249,7 @@ struct mt792x_dev { const struct mt792x_irq_map *irq_map; struct work_struct ipv6_ns_work; + struct delayed_work mlo_pm_work; /* IPv6 addresses for WoWLAN */ struct sk_buff_head ipv6_ns_list; @@ -497,7 +508,7 @@ int mt792xe_mcu_fw_pmctrl(struct mt792x_dev *dev); int mt792x_init_acpi_sar(struct mt792x_dev *dev); int mt792x_init_acpi_sar_power(struct mt792x_phy *phy, bool set_default); u8 mt792x_acpi_get_flags(struct mt792x_phy *phy); -u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2); +u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2); #else static inline int mt792x_init_acpi_sar(struct mt792x_dev *dev) { @@ -515,9 +526,9 @@ static inline u8 mt792x_acpi_get_flags(struct mt792x_phy *phy) return 0; } -static inline u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) +static inline u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) { - return 0xf; + return MT792X_ACPI_MTCL_INVALID; } #endif diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c index 9317f8ff2070..d1aebadd50aa 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.c @@ -4,6 +4,28 @@ #include <linux/acpi.h> #include "mt792x.h" +static const char * const cc_list_all[] = { + "00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR", + "CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME", + "MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR", + "TW", "TH", "UA", "GB", "US", "VN", "KH", "PY", +}; + +static const char * const cc_list_eu[] = { + "AD", "AT", "BE", "BG", "CY", "CZ", "HR", "DK", + "EE", "FI", "FR", "DE", "GR", "HU", "IS", "IE", + "IT", "LV", "LI", "LT", "LU", "MC", "MT", "NL", + "NO", "PL", "PT", "RO", "SK", "SI", "ES", "SE", + "CH", +}; + +static const char * const cc_list_be[] = { + "AR", "BR", "BY", "CL", "IQ", "MX", "OM", "RU", + "RW", "VN", "KR", "UA", "", "", "", "", + "EU", "AT", "CN", "CA", "TW", "NZ", "PH", "UK", + "US", +}; + static int mt792x_acpi_read(struct mt792x_dev *dev, u8 *method, u8 **tbl, u32 *len) { @@ -66,13 +88,22 @@ free: } /* MTCL : Country List Table for 6G band */ +/* MTCL : Country List Table for 6G band and 11BE */ static int mt792x_asar_acpi_read_mtcl(struct mt792x_dev *dev, u8 **table, u8 *version) { - int ret; + int len, ret; - *version = ((ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, NULL)) < 0) - ? 1 : 2; + ret = mt792x_acpi_read(dev, MT792x_ACPI_MTCL, table, &len); + if (ret) + return ret; + + if (len == sizeof(struct mt792x_asar_cl)) + *version = ((struct mt792x_asar_cl *)*table)->version; + else if (len == sizeof(struct mt792x_asar_cl_v3)) + *version = ((struct mt792x_asar_cl_v3 *)*table)->version; + else + return -EINVAL; return ret; } @@ -351,10 +382,24 @@ u8 mt792x_acpi_get_flags(struct mt792x_phy *phy) } EXPORT_SYMBOL_GPL(mt792x_acpi_get_flags); -static u8 +static u32 +mt792x_acpi_get_mtcl_map_v3(int row, int column, struct mt792x_asar_cl_v3 *cl) +{ + u32 config = 0; + u8 mode_be = 0; + + mode_be = (cl->mode_be > 0x02) ? 0 : cl->mode_be; + + if (cl->version > 2 && cl->clbe[row] & BIT(column)) + config |= (mode_be & 0x3) << 4; + + return config; +} + +static u32 mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl) { - u8 config = 0; + u32 config = 0; u8 mode_6g, mode_5g9; mode_6g = (cl->mode_6g > 0x02) ? 0 : cl->mode_6g; @@ -368,30 +413,44 @@ mt792x_acpi_get_mtcl_map(int row, int column, struct mt792x_asar_cl *cl) return config; } -u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) +static u32 +mt792x_acpi_parse_mtcl_tbl_v3(struct mt792x_phy *phy, char *alpha2) { - static const char * const cc_list_all[] = { - "00", "EU", "AR", "AU", "AZ", "BY", "BO", "BR", - "CA", "CL", "CN", "ID", "JP", "MY", "MX", "ME", - "MA", "NZ", "NG", "PH", "RU", "RS", "SG", "KR", - "TW", "TH", "UA", "GB", "US", "VN", "KH", "PY", - }; - static const char * const cc_list_eu[] = { - "AT", "BE", "BG", "CY", "CZ", "HR", "DK", "EE", - "FI", "FR", "DE", "GR", "HU", "IS", "IE", "IT", - "LV", "LI", "LT", "LU", "MT", "NL", "NO", "PL", - "PT", "RO", "SK", "SI", "ES", "SE", "CH", - }; struct mt792x_acpi_sar *sar = phy->acpisar; - struct mt792x_asar_cl *cl; + struct mt792x_asar_cl_v3 *cl = sar->countrylist_v3; int col, row, i; - if (!sar) - return 0xf; + if (sar->ver != 3) + goto out; - cl = sar->countrylist; if (!cl) - return 0xc; + return MT792X_ACPI_MTCL_INVALID; + + for (i = 0; i < ARRAY_SIZE(cc_list_be); i++) { + col = 7 - i % 8; + row = i / 8; + if (!memcmp(cc_list_be[i], alpha2, 2)) + return mt792x_acpi_get_mtcl_map_v3(row, col, cl); + } + for (i = 0; i < ARRAY_SIZE(cc_list_eu); i++) { + if (!memcmp(cc_list_eu[i], alpha2, 2)) + return mt792x_acpi_get_mtcl_map_v3(3, 7, cl); + } + +out: + /* Depends on driver */ + return 0x20; +} + +static u32 +mt792x_acpi_parse_mtcl_tbl(struct mt792x_phy *phy, char *alpha2) +{ + struct mt792x_acpi_sar *sar = phy->acpisar; + struct mt792x_asar_cl *cl = sar->countrylist; + int col, row, i; + + if (!cl) + return MT792X_ACPI_MTCL_INVALID; for (i = 0; i < ARRAY_SIZE(cc_list_all); i++) { col = 7 - i % 8; @@ -406,4 +465,22 @@ u8 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) return mt792x_acpi_get_mtcl_map(0, 7, cl); } + +u32 mt792x_acpi_get_mtcl_conf(struct mt792x_phy *phy, char *alpha2) +{ + struct mt792x_acpi_sar *sar = phy->acpisar; + u32 config = 0; + + if (!sar) + return MT792X_ACPI_MTCL_INVALID; + + config = mt792x_acpi_parse_mtcl_tbl_v3(phy, alpha2); + + if (config == MT792X_ACPI_MTCL_INVALID) + return MT792X_ACPI_MTCL_INVALID; + + config |= mt792x_acpi_parse_mtcl_tbl(phy, alpha2); + + return config; +} EXPORT_SYMBOL_GPL(mt792x_acpi_get_mtcl_conf); diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h index 2298983b6342..e45dcd7fbdb1 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h +++ b/drivers/net/wireless/mediatek/mt76/mt792x_acpi_sar.h @@ -15,6 +15,8 @@ #define MT792x_ACPI_MTGS "MTGS" #define MT792x_ACPI_MTFG "MTFG" +#define MT792X_ACPI_MTCL_INVALID 0xffffffff + struct mt792x_asar_dyn_limit { u8 idx; u8 frp[5]; @@ -72,6 +74,17 @@ struct mt792x_asar_geo_v2 { DECLARE_FLEX_ARRAY(struct mt792x_asar_geo_limit_v2, tbl); } __packed; +struct mt792x_asar_cl_v3 { + u8 names[4]; + u8 version; + u8 mode_6g; + u8 cl6g[6]; + u8 mode_5g9; + u8 cl5g9[6]; + u8 mode_be; + u8 clbe[6]; +} __packed; + struct mt792x_asar_cl { u8 names[4]; u8 version; @@ -100,7 +113,10 @@ struct mt792x_acpi_sar { struct mt792x_asar_geo *geo; struct mt792x_asar_geo_v2 *geo_v2; }; - struct mt792x_asar_cl *countrylist; + union { + struct mt792x_asar_cl *countrylist; + struct mt792x_asar_cl_v3 *countrylist_v3; + }; struct mt792x_asar_fg *fg; }; diff --git a/drivers/net/wireless/mediatek/mt76/mt792x_core.c b/drivers/net/wireless/mediatek/mt76/mt792x_core.c index 8799627f6292..38dd58f6e493 100644 --- a/drivers/net/wireless/mediatek/mt76/mt792x_core.c +++ b/drivers/net/wireless/mediatek/mt76/mt792x_core.c @@ -340,7 +340,7 @@ void mt792x_unassign_vif_chanctx(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); if (vif->bss_conf.csa_active) { - del_timer_sync(&mvif->csa_timer); + timer_delete_sync(&mvif->csa_timer); cancel_work_sync(&mvif->csa_work); } } @@ -665,7 +665,8 @@ int mt792x_init_wiphy(struct ieee80211_hw *hw) ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(hw, SUPPORTS_VHT_EXT_NSS_BW); ieee80211_hw_set(hw, CONNECTION_MONITOR); - ieee80211_hw_set(hw, CHANCTX_STA_CSA); + if (is_mt7921(&dev->mt76)) + ieee80211_hw_set(hw, CHANCTX_STA_CSA); if (dev->pm.enable) ieee80211_hw_set(hw, CONNECTION_MONITOR); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c index ccab0d7b9be4..303d6e80a666 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/coredump.c @@ -48,8 +48,8 @@ const struct mt7996_mem_region* mt7996_coredump_get_mem_layout(struct mt7996_dev *dev, u32 *num) { switch (mt76_chip(&dev->mt76)) { - case 0x7990: - case 0x7991: + case MT7996_DEVICE_ID: + case MT7996_DEVICE_ID_2: *num = ARRAY_SIZE(mt7996_mem_regions); return &mt7996_mem_regions[0]; default: diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c index 7b2bb72b407d..0ab827f52fd7 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/debugfs.c @@ -222,18 +222,27 @@ static const struct file_operations mt7996_sys_recovery_ops = { static int mt7996_radar_trigger(void *data, u64 val) { +#define RADAR_MAIN_CHAIN 1 +#define RADAR_BACKGROUND 2 struct mt7996_dev *dev = data; + struct mt7996_phy *phy = mt7996_band_phy(dev, NL80211_BAND_5GHZ); + int rdd_idx; - if (val > MT_RX_SEL2) + if (!phy || !val || val > RADAR_BACKGROUND) return -EINVAL; - if (val == MT_RX_SEL2 && !dev->rdd2_phy) { + if (val == RADAR_BACKGROUND && !dev->rdd2_phy) { dev_err(dev->mt76.dev, "Background radar is not enabled\n"); return -EINVAL; } - return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, - val, 0, 0); + rdd_idx = mt7996_get_rdd_idx(phy, val == RADAR_BACKGROUND); + if (rdd_idx < 0) { + dev_err(dev->mt76.dev, "No RDD found\n"); + return -EINVAL; + } + + return mt7996_mcu_rdd_cmd(dev, RDD_RADAR_EMULATE, rdd_idx, 0); } DEFINE_DEBUGFS_ATTRIBUTE(fops_radar_trigger, NULL, @@ -616,28 +625,51 @@ static void mt7996_sta_hw_queue_read(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_dev *dev = msta->vif->deflink.phy->dev; + struct mt7996_vif *mvif = msta->vif; + struct mt7996_dev *dev = mvif->deflink.phy->dev; + struct ieee80211_link_sta *link_sta; struct seq_file *s = data; - u8 ac; + struct ieee80211_vif *vif; + unsigned int link_id; - for (ac = 0; ac < 4; ac++) { - u32 qlen, ctrl, val; - u32 idx = msta->wcid.idx >> 5; - u8 offs = msta->wcid.idx & GENMASK(4, 0); + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); - ctrl = BIT(31) | BIT(11) | (ac << 24); - val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); + rcu_read_lock(); + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt76_vif_link *mlink; + u8 ac; - if (val & BIT(offs)) + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (!mlink) continue; - mt76_wr(dev, MT_FL_Q0_CTRL, ctrl | msta->wcid.idx); - qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, - GENMASK(11, 0)); - seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", - sta->addr, msta->wcid.idx, - msta->vif->deflink.mt76.wmm_idx, ac, qlen); + msta_link = rcu_dereference(msta->link[link_id]); + if (!msta_link) + continue; + + for (ac = 0; ac < 4; ac++) { + u32 idx = msta_link->wcid.idx >> 5, qlen, ctrl, val; + u8 offs = msta_link->wcid.idx & GENMASK(4, 0); + + ctrl = BIT(31) | BIT(11) | (ac << 24); + val = mt76_rr(dev, MT_PLE_AC_QEMPTY(ac, idx)); + + if (val & BIT(offs)) + continue; + + mt76_wr(dev, + MT_FL_Q0_CTRL, ctrl | msta_link->wcid.idx); + qlen = mt76_get_field(dev, MT_FL_Q3_CTRL, + GENMASK(11, 0)); + seq_printf(s, "\tSTA %pM wcid %d: AC%d%d queued:%d\n", + sta->addr, msta_link->wcid.idx, + mlink->wmm_idx, ac, qlen); + } } + + rcu_read_unlock(); } static int @@ -930,6 +962,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file, struct ieee80211_sta *sta = file->private_data; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_dev *dev = msta->vif->deflink.phy->dev; + struct mt7996_sta_link *msta_link = &msta->deflink; struct ra_rate phy = {}; char buf[100]; int ret; @@ -964,7 +997,7 @@ static ssize_t mt7996_sta_fixed_rate_set(struct file *file, goto out; } - phy.wlan_idx = cpu_to_le16(msta->wcid.idx); + phy.wlan_idx = cpu_to_le16(msta_link->wcid.idx); phy.gi = cpu_to_le16(gi); phy.ltf = cpu_to_le16(ltf); phy.ldpc = phy.ldpc ? 7 : 0; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c index 69a7d9b2e38b..c8bef0b2a144 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/dma.c @@ -55,20 +55,32 @@ static void mt7996_dma_config(struct mt7996_dev *dev) /* rx queue */ RXQ_CONFIG(MT_RXQ_MCU, WFDMA0, MT_INT_RX_DONE_WM, MT7996_RXQ_MCU_WM); + /* for mt7990, RX ring 1 is for SDO instead */ RXQ_CONFIG(MT_RXQ_MCU_WA, WFDMA0, MT_INT_RX_DONE_WA, MT7996_RXQ_MCU_WA); - - /* mt7996: band0 and band1, mt7992: band0 */ RXQ_CONFIG(MT_RXQ_MAIN, WFDMA0, MT_INT_RX_DONE_BAND0, MT7996_RXQ_BAND0); - RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN, MT7996_RXQ_MCU_WA_MAIN); + if (mt7996_has_wa(dev)) + RXQ_CONFIG(MT_RXQ_MAIN_WA, WFDMA0, MT_INT_RX_DONE_WA_MAIN, + MT7996_RXQ_MCU_WA_MAIN); - if (is_mt7996(&dev->mt76)) { + switch (mt76_chip(&dev->mt76)) { + case MT7992_DEVICE_ID: + RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT, MT7996_RXQ_MCU_WA_EXT); + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7996_RXQ_BAND1); + break; + case MT7990_DEVICE_ID: + RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7996_RXQ_BAND1); + RXQ_CONFIG(MT_RXQ_TXFREE_BAND0, WFDMA0, + MT_INT_RX_TXFREE_BAND0_MT7990, MT7990_RXQ_TXFREE0); + if (dev->hif2) + RXQ_CONFIG(MT_RXQ_TXFREE_BAND1, WFDMA0, + MT_INT_RX_TXFREE_BAND1_MT7990, MT7990_RXQ_TXFREE1); + break; + case MT7996_DEVICE_ID: + default: /* mt7996 band2 */ - RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2); RXQ_CONFIG(MT_RXQ_BAND2_WA, WFDMA0, MT_INT_RX_DONE_WA_TRI, MT7996_RXQ_MCU_WA_TRI); - } else { - /* mt7992 band1 */ - RXQ_CONFIG(MT_RXQ_BAND1, WFDMA0, MT_INT_RX_DONE_BAND1, MT7996_RXQ_BAND1); - RXQ_CONFIG(MT_RXQ_BAND1_WA, WFDMA0, MT_INT_RX_DONE_WA_EXT, MT7996_RXQ_MCU_WA_EXT); + RXQ_CONFIG(MT_RXQ_BAND2, WFDMA0, MT_INT_RX_DONE_BAND2, MT7996_RXQ_BAND2); + break; } if (dev->has_rro) { @@ -104,9 +116,11 @@ static void mt7996_dma_config(struct mt7996_dev *dev) } /* mcu tx queue */ - MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7996_TXQ_MCU_WM); - MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA, MT7996_TXQ_MCU_WA); MCUQ_CONFIG(MT_MCUQ_FWDL, WFDMA0, MT_INT_TX_DONE_FWDL, MT7996_TXQ_FWDL); + MCUQ_CONFIG(MT_MCUQ_WM, WFDMA0, MT_INT_TX_DONE_MCU_WM, MT7996_TXQ_MCU_WM); + if (mt7996_has_wa(dev)) + MCUQ_CONFIG(MT_MCUQ_WA, WFDMA0, MT_INT_TX_DONE_MCU_WA, + MT7996_TXQ_MCU_WA); } static u32 __mt7996_dma_prefetch_base(u16 *base, u8 depth) @@ -121,43 +135,62 @@ static u32 __mt7996_dma_prefetch_base(u16 *base, u8 depth) static void __mt7996_dma_prefetch(struct mt7996_dev *dev, u32 ofs) { u16 base = 0; - u8 queue; + u8 queue, val; #define PREFETCH(_depth) (__mt7996_dma_prefetch_base(&base, (_depth))) /* prefetch SRAM wrapping boundary for tx/rx ring. */ - mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(0x2)); - mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(0x2)); + /* Tx Command Rings */ + val = is_mt7996(&dev->mt76) ? 2 : 4; + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_FWDL) + ofs, PREFETCH(val)); + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WM) + ofs, PREFETCH(val)); + if (mt7996_has_wa(dev)) + mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(val)); + + /* Tx Data Rings */ mt76_wr(dev, MT_TXQ_EXT_CTRL(0) + ofs, PREFETCH(0x8)); - mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0x8)); - mt76_wr(dev, MT_MCUQ_EXT_CTRL(MT_MCUQ_WA) + ofs, PREFETCH(0x2)); - mt76_wr(dev, MT_TXQ_EXT_CTRL(2) + ofs, PREFETCH(0x8)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(0x2)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(0x2)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(0x2)); - - queue = is_mt7996(&dev->mt76) ? MT_RXQ_BAND2_WA : MT_RXQ_BAND1_WA; - mt76_wr(dev, MT_RXQ_BAND1_CTRL(queue) + ofs, PREFETCH(0x2)); - - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x10)); + if (!is_mt7996(&dev->mt76) || dev->hif2) + mt76_wr(dev, MT_TXQ_EXT_CTRL(1) + ofs, PREFETCH(0x8)); + if (is_mt7996(&dev->mt76)) + mt76_wr(dev, MT_TXQ_EXT_CTRL(2) + ofs, PREFETCH(0x8)); + + /* Rx Event Rings */ + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MCU) + ofs, PREFETCH(val)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MCU_WA) + ofs, PREFETCH(val)); + + /* Rx TxFreeDone From WA Rings */ + if (mt7996_has_wa(dev)) { + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MAIN_WA) + ofs, PREFETCH(val)); + queue = is_mt7996(&dev->mt76) ? MT_RXQ_BAND2_WA : MT_RXQ_BAND1_WA; + mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(val)); + } + /* Rx TxFreeDone From MAC Rings */ + val = is_mt7996(&dev->mt76) ? 4 : 8; + if (is_mt7990(&dev->mt76) || (is_mt7996(&dev->mt76) && dev->has_rro)) + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND0) + ofs, PREFETCH(val)); + if (is_mt7990(&dev->mt76) && dev->hif2) + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND1) + ofs, PREFETCH(val)); + else if (is_mt7996(&dev->mt76) && dev->has_rro) + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_TXFREE_BAND2) + ofs, PREFETCH(val)); + + /* Rx Data Rings */ + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MAIN) + ofs, PREFETCH(0x10)); queue = is_mt7996(&dev->mt76) ? MT_RXQ_BAND2 : MT_RXQ_BAND1; - mt76_wr(dev, MT_RXQ_BAND1_CTRL(queue) + ofs, PREFETCH(0x10)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(0x10)); + /* Rx RRO Rings */ if (dev->has_rro) { - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND0) + ofs, - PREFETCH(0x10)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_RRO_BAND2) + ofs, - PREFETCH(0x10)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs, - PREFETCH(0x4)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs, - PREFETCH(0x4)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_MSDU_PAGE_BAND2) + ofs, - PREFETCH(0x4)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND0) + ofs, - PREFETCH(0x4)); - mt76_wr(dev, MT_RXQ_BAND1_CTRL(MT_RXQ_TXFREE_BAND2) + ofs, - PREFETCH(0x4)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_RRO_BAND0) + ofs, PREFETCH(0x10)); + queue = is_mt7996(&dev->mt76) ? MT_RXQ_RRO_BAND2 : MT_RXQ_RRO_BAND1; + mt76_wr(dev, MT_RXQ_EXT_CTRL(queue) + ofs, PREFETCH(0x10)); + + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND0) + ofs, PREFETCH(val)); + if (is_mt7996(&dev->mt76)) { + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND1) + ofs, + PREFETCH(val)); + mt76_wr(dev, MT_RXQ_EXT_CTRL(MT_RXQ_MSDU_PAGE_BAND2) + ofs, + PREFETCH(val)); + } } #undef PREFETCH @@ -269,6 +302,9 @@ void mt7996_dma_start(struct mt7996_dev *dev, bool reset, bool wed_reset) mtk_wed_device_start(wed, wed_irq_mask); } + if (!mt7996_has_wa(dev)) + irq_mask &= ~(MT_INT_RX(MT_RXQ_MAIN_WA) | + MT_INT_RX(MT_RXQ_BAND1_WA)); irq_mask = reset ? MT_INT_MCU_CMD : irq_mask; mt7996_irq_enable(dev, irq_mask); @@ -474,12 +510,14 @@ int mt7996_dma_init(struct mt7996_dev *dev) return ret; /* command to WA */ - ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA, - MT_MCUQ_ID(MT_MCUQ_WA), - MT7996_TX_MCU_RING_SIZE, - MT_MCUQ_RING_BASE(MT_MCUQ_WA)); - if (ret) - return ret; + if (mt7996_has_wa(dev)) { + ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_WA, + MT_MCUQ_ID(MT_MCUQ_WA), + MT7996_TX_MCU_RING_SIZE, + MT_MCUQ_RING_BASE(MT_MCUQ_WA)); + if (ret) + return ret; + } /* firmware download */ ret = mt76_init_mcu_queue(&dev->mt76, MT_MCUQ_FWDL, @@ -493,16 +531,16 @@ int mt7996_dma_init(struct mt7996_dev *dev) ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU], MT_RXQ_ID(MT_RXQ_MCU), MT7996_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, + MT7996_RX_MCU_BUF_SIZE, MT_RXQ_RING_BASE(MT_RXQ_MCU)); if (ret) return ret; - /* event from WA */ + /* event from WA, or SDO event for mt7990 */ ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MCU_WA], MT_RXQ_ID(MT_RXQ_MCU_WA), MT7996_RX_MCU_RING_SIZE_WA, - MT_RX_BUF_SIZE, + MT7996_RX_MCU_BUF_SIZE, MT_RXQ_RING_BASE(MT_RXQ_MCU_WA)); if (ret) return ret; @@ -527,13 +565,41 @@ int mt7996_dma_init(struct mt7996_dev *dev) dev->mt76.q_rx[MT_RXQ_MAIN_WA].wed = wed; } - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], - MT_RXQ_ID(MT_RXQ_MAIN_WA), - MT7996_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, - MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA)); - if (ret) - return ret; + if (mt7996_has_wa(dev)) { + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_MAIN_WA], + MT_RXQ_ID(MT_RXQ_MAIN_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_MAIN_WA)); + if (ret) + return ret; + } else { + if (mtk_wed_device_active(wed)) { + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].flags = MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0].wed = wed; + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND0], + MT_RXQ_ID(MT_RXQ_TXFREE_BAND0), + MT7996_RX_MCU_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND0)); + if (ret) + return ret; + } + + if (!mt7996_has_wa(dev) && dev->hif2) { + if (mtk_wed_device_active(wed)) { + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1].flags = MT_WED_Q_TXFREE; + dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1].wed = wed; + } + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_TXFREE_BAND1], + MT_RXQ_ID(MT_RXQ_TXFREE_BAND1), + MT7996_RX_MCU_RING_SIZE, + MT7996_RX_BUF_SIZE, + MT_RXQ_RING_BASE(MT_RXQ_TXFREE_BAND1)); + if (ret) + return ret; + } if (mt7996_band_valid(dev, MT_BAND2)) { /* rx data queue for mt7996 band2 */ @@ -573,14 +639,16 @@ int mt7996_dma_init(struct mt7996_dev *dev) return ret; /* tx free notify event from WA for mt7992 band1 */ - rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs; - ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA], - MT_RXQ_ID(MT_RXQ_BAND1_WA), - MT7996_RX_MCU_RING_SIZE, - MT_RX_BUF_SIZE, - rx_base); - if (ret) - return ret; + if (mt7996_has_wa(dev)) { + rx_base = MT_RXQ_RING_BASE(MT_RXQ_BAND1_WA) + hif1_ofs; + ret = mt76_queue_alloc(dev, &dev->mt76.q_rx[MT_RXQ_BAND1_WA], + MT_RXQ_ID(MT_RXQ_BAND1_WA), + MT7996_RX_MCU_RING_SIZE, + MT_RX_BUF_SIZE, + rx_base); + if (ret) + return ret; + } } if (mtk_wed_device_active(wed) && mtk_wed_get_rx_capa(wed) && diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c index 53dfac02f8af..87c6192b6384 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/eeprom.c @@ -13,10 +13,12 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev) u16 val = get_unaligned_le16(eeprom); switch (val) { - case 0x7990: + case MT7996_DEVICE_ID: return is_mt7996(&dev->mt76) ? 0 : -EINVAL; - case 0x7992: + case MT7992_DEVICE_ID: return is_mt7992(&dev->mt76) ? 0 : -EINVAL; + case MT7990_DEVICE_ID: + return is_mt7990(&dev->mt76) ? 0 : -EINVAL; default: return -EINVAL; } @@ -25,7 +27,7 @@ static int mt7996_check_eeprom(struct mt7996_dev *dev) static char *mt7996_eeprom_name(struct mt7996_dev *dev) { switch (mt76_chip(&dev->mt76)) { - case 0x7992: + case MT7992_DEVICE_ID: switch (dev->var.type) { case MT7992_VAR_TYPE_23: if (dev->var.fem == MT7996_FEM_INT) @@ -39,7 +41,11 @@ static char *mt7996_eeprom_name(struct mt7996_dev *dev) return MT7992_EEPROM_DEFAULT_MIX; return MT7992_EEPROM_DEFAULT; } - case 0x7990: + case MT7990_DEVICE_ID: + if (dev->var.fem == MT7996_FEM_INT) + return MT7990_EEPROM_DEFAULT_INT; + return MT7990_EEPROM_DEFAULT; + case MT7996_DEVICE_ID: default: switch (dev->var.type) { case MT7996_VAR_TYPE_233: @@ -304,6 +310,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy) phy->has_aux_rx = true; mphy->antenna_mask = BIT(nss) - 1; + phy->orig_antenna_mask = mphy->antenna_mask; mphy->chainmask = (BIT(path) - 1) << dev->chainshift[band_idx]; phy->orig_chainmask = mphy->chainmask; dev->chainmask |= mphy->chainmask; @@ -370,3 +377,30 @@ s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band) return val & MT_EE_RATE_DELTA_SIGN ? delta : -delta; } + +bool mt7996_eeprom_has_background_radar(struct mt7996_dev *dev) +{ + switch (mt76_chip(&dev->mt76)) { + case MT7996_DEVICE_ID: + if (dev->var.type == MT7996_VAR_TYPE_233) + return false; + break; + case MT7992_DEVICE_ID: + if (dev->var.type == MT7992_VAR_TYPE_23) + return false; + break; + case MT7990_DEVICE_ID: { + u8 path, rx_path, nss, *eeprom = dev->mt76.eeprom.data; + + mt7996_eeprom_parse_stream(eeprom, MT_BAND1, &path, &rx_path, &nss); + /* Disable background radar capability in 3T3R */ + if (path == 3 || rx_path == 3) + return false; + break; + } + default: + return false; + } + + return true; +} diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/init.c b/drivers/net/wireless/mediatek/mt76/mt7996/init.c index 6b660424aedc..a9599c286328 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/init.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/init.c @@ -217,6 +217,9 @@ static int mt7996_thermal_init(struct mt7996_phy *phy) name = devm_kasprintf(&wiphy->dev, GFP_KERNEL, "mt7996_%s.%d", wiphy_name(wiphy), phy->mt76->band_idx); + if (!name) + return -ENOMEM; + snprintf(cname, sizeof(cname), "cooling_device%d", phy->mt76->band_idx); cdev = thermal_cooling_device_register(name, phy, &mt7996_thermal_ops); @@ -314,8 +317,8 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy, struct ieee80211_supported_band *sband) { struct mt7996_dev *dev = phy->dev; - int i, nss = hweight16(phy->mt76->chainmask); - int nss_delta = mt76_tx_power_nss_delta(nss); + int i, n_chains = hweight16(phy->mt76->chainmask); + int path_delta = mt76_tx_power_path_delta(n_chains); int pwr_delta = mt7996_eeprom_get_power_delta(dev, sband->band); struct mt76_power_limits limits; @@ -327,7 +330,7 @@ static void __mt7996_init_txpower(struct mt7996_phy *phy, target_power = mt76_get_rate_power_limits(phy->mt76, chan, &limits, target_power); - target_power += nss_delta; + target_power += path_delta; target_power = DIV_ROUND_UP(target_power, 2); chan->max_power = min_t(int, chan->max_reg_power, target_power); @@ -440,6 +443,9 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed) hw->queues = 4; hw->max_rx_aggregation_subframes = max_subframes; hw->max_tx_aggregation_subframes = max_subframes; + if (is_mt7990(mdev) && dev->has_eht) + hw->max_tx_aggregation_subframes = 512; + hw->netdev_features = NETIF_F_RXCSUM; if (mtk_wed_device_active(wed)) hw->netdev_features |= NETIF_F_HW_TC; @@ -472,7 +478,7 @@ mt7996_init_wiphy(struct ieee80211_hw *hw, struct mtk_wed_device *wed) wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CAN_REPLACE_PTK0); wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_MU_MIMO_AIR_SNIFFER); - if (mt7996_has_background_radar(dev) && + if (mt7996_eeprom_has_background_radar(dev) && (!mdev->dev->of_node || !of_property_read_bool(mdev->dev->of_node, "mediatek,disable-radar-background"))) @@ -929,13 +935,13 @@ static int mt7996_variant_type_init(struct mt7996_dev *dev) u8 var_type; switch (mt76_chip(&dev->mt76)) { - case 0x7990: + case MT7996_DEVICE_ID: if (val & MT_PAD_GPIO_2ADIE_TBTC) var_type = MT7996_VAR_TYPE_233; else var_type = MT7996_VAR_TYPE_444; break; - case 0x7992: + case MT7992_DEVICE_ID: if (val & MT_PAD_GPIO_ADIE_SINGLE) var_type = MT7992_VAR_TYPE_23; else if (u32_get_bits(val, MT_PAD_GPIO_ADIE_COMB_7992)) @@ -943,6 +949,9 @@ static int mt7996_variant_type_init(struct mt7996_dev *dev) else return -EINVAL; break; + case MT7990_DEVICE_ID: + var_type = MT7990_VAR_TYPE_23; + break; default: return -EINVAL; } @@ -1061,10 +1070,10 @@ void mt7996_set_stream_vht_txbf_caps(struct mt7996_phy *phy) *cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE | IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; - if (is_mt7996(phy->mt76->dev)) - *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 3); - else + if (is_mt7992(phy->mt76->dev)) *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 4); + else + *cap |= FIELD_PREP(IEEE80211_VHT_CAP_BEAMFORMEE_STS_MASK, 3); *cap &= ~(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK | IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE | @@ -1107,18 +1116,17 @@ mt7996_set_stream_he_txbf_caps(struct mt7996_phy *phy, elem->phy_cap_info[7] &= ~IEEE80211_HE_PHY_CAP7_MAX_NC_MASK; c = IEEE80211_HE_PHY_CAP2_NDP_4x_LTF_AND_3_2US | - IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO | - IEEE80211_HE_PHY_CAP2_UL_MU_PARTIAL_MU_MIMO; + IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO; elem->phy_cap_info[2] |= c; c = IEEE80211_HE_PHY_CAP4_SU_BEAMFORMEE; - if (is_mt7996(phy->mt76->dev)) - c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 | - (IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 * non_2g); - else + if (is_mt7992(phy->mt76->dev)) c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_5 | (IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_5 * non_2g); + else + c |= IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_UNDER_80MHZ_4 | + (IEEE80211_HE_PHY_CAP4_BEAMFORMEE_MAX_STS_ABOVE_80MHZ_4 * non_2g); elem->phy_cap_info[4] |= c; @@ -1318,6 +1326,9 @@ mt7996_init_eht_caps(struct mt7996_phy *phy, enum nl80211_band band, u8_encode_bits(IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_11454, IEEE80211_EHT_MAC_CAP0_MAX_MPDU_LEN_MASK); + eht_cap_elem->mac_cap_info[1] |= + IEEE80211_EHT_MAC_CAP1_MAX_AMPDU_LEN_MASK; + eht_cap_elem->phy_cap_info[0] = IEEE80211_EHT_PHY_CAP0_NDP_4_EHT_LFT_32_GI | IEEE80211_EHT_PHY_CAP0_SU_BEAMFORMER | diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c index 019c925ae600..0dbd4662bc84 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mac.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mac.c @@ -53,26 +53,48 @@ static const struct mt7996_dfs_radar_spec jp_radar_specs = { }; static struct mt76_wcid *mt7996_rx_get_wcid(struct mt7996_dev *dev, - u16 idx, bool unicast) + u16 idx, u8 band_idx) { - struct mt7996_sta *sta; + struct mt7996_sta_link *msta_link; + struct mt7996_sta *msta; + struct mt7996_vif *mvif; struct mt76_wcid *wcid; + int i; if (idx >= ARRAY_SIZE(dev->mt76.wcid)) return NULL; wcid = rcu_dereference(dev->mt76.wcid[idx]); - if (unicast || !wcid) - return wcid; + if (!wcid) + return NULL; - if (!wcid->sta) + if (!mt7996_band_valid(dev, band_idx)) return NULL; - sta = container_of(wcid, struct mt7996_sta, wcid); - if (!sta->vif) + if (wcid->phy_idx == band_idx) + return wcid; + + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); + msta = msta_link->sta; + if (!msta || !msta->vif) return NULL; - return &sta->vif->deflink.sta.wcid; + mvif = msta->vif; + for (i = 0; i < ARRAY_SIZE(mvif->mt76.link); i++) { + struct mt76_vif_link *mlink; + + mlink = rcu_dereference(mvif->mt76.link[i]); + if (!mlink) + continue; + + if (mlink->band_idx != band_idx) + continue; + + msta_link = rcu_dereference(msta->link[i]); + break; + } + + return &msta_link->wcid; } bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask) @@ -100,10 +122,13 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) [IEEE80211_AC_VI] = 4, [IEEE80211_AC_VO] = 6 }; + struct mt7996_sta_link *msta_link; + struct mt76_vif_link *mlink; struct ieee80211_sta *sta; struct mt7996_sta *msta; u32 tx_time[IEEE80211_NUM_ACS], rx_time[IEEE80211_NUM_ACS]; LIST_HEAD(sta_poll_list); + struct mt76_wcid *wcid; int i; spin_lock_bh(&dev->mt76.sta_poll_lock); @@ -123,25 +148,28 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) spin_unlock_bh(&dev->mt76.sta_poll_lock); break; } - msta = list_first_entry(&sta_poll_list, - struct mt7996_sta, wcid.poll_list); - list_del_init(&msta->wcid.poll_list); + msta_link = list_first_entry(&sta_poll_list, + struct mt7996_sta_link, + wcid.poll_list); + msta = msta_link->sta; + wcid = &msta_link->wcid; + list_del_init(&wcid->poll_list); spin_unlock_bh(&dev->mt76.sta_poll_lock); - idx = msta->wcid.idx; + idx = wcid->idx; /* refresh peer's airtime reporting */ addr = mt7996_mac_wtbl_lmac_addr(dev, idx, 20); for (i = 0; i < IEEE80211_NUM_ACS; i++) { - u32 tx_last = msta->airtime_ac[i]; - u32 rx_last = msta->airtime_ac[i + 4]; + u32 tx_last = msta_link->airtime_ac[i]; + u32 rx_last = msta_link->airtime_ac[i + 4]; - msta->airtime_ac[i] = mt76_rr(dev, addr); - msta->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); + msta_link->airtime_ac[i] = mt76_rr(dev, addr); + msta_link->airtime_ac[i + 4] = mt76_rr(dev, addr + 4); - tx_time[i] = msta->airtime_ac[i] - tx_last; - rx_time[i] = msta->airtime_ac[i + 4] - rx_last; + tx_time[i] = msta_link->airtime_ac[i] - tx_last; + rx_time[i] = msta_link->airtime_ac[i + 4] - rx_last; if ((tx_last | rx_last) & BIT(30)) clear = true; @@ -152,10 +180,11 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) if (clear) { mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - memset(msta->airtime_ac, 0, sizeof(msta->airtime_ac)); + memset(msta_link->airtime_ac, 0, + sizeof(msta_link->airtime_ac)); } - if (!msta->wcid.sta) + if (!wcid->sta) continue; sta = container_of((void *)msta, struct ieee80211_sta, @@ -181,28 +210,23 @@ static void mt7996_mac_sta_poll(struct mt7996_dev *dev) rssi[2] = to_rssi(GENMASK(23, 16), val); rssi[3] = to_rssi(GENMASK(31, 14), val); - msta->ack_signal = - mt76_rx_signal(msta->vif->deflink.phy->mt76->antenna_mask, rssi); + mlink = rcu_dereference(msta->vif->mt76.link[wcid->link_id]); + if (mlink) { + struct mt76_phy *mphy = mt76_vif_link_phy(mlink); - ewma_avg_signal_add(&msta->avg_ack_signal, -msta->ack_signal); + if (mphy) + msta_link->ack_signal = + mt76_rx_signal(mphy->antenna_mask, + rssi); + } + + ewma_avg_signal_add(&msta_link->avg_ack_signal, + -msta_link->ack_signal); } rcu_read_unlock(); } -void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, - struct ieee80211_vif *vif, bool enable) -{ - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - u32 addr; - - addr = mt7996_mac_wtbl_lmac_addr(dev, mvif->deflink.sta.wcid.idx, 5); - if (enable) - mt76_set(dev, addr, BIT(5)); - else - mt76_clear(dev, addr, BIT(5)); -} - /* The HW does not translate the mac header to 802.3 for mesh point */ static int mt7996_reverse_frag0_hdr_trans(struct sk_buff *skb, u16 hdr_gap) { @@ -474,11 +498,15 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q, unicast = FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) == MT_RXD3_NORMAL_U2M; idx = FIELD_GET(MT_RXD1_NORMAL_WLAN_IDX, rxd1); - status->wcid = mt7996_rx_get_wcid(dev, idx, unicast); + status->wcid = mt7996_rx_get_wcid(dev, idx, band_idx); if (status->wcid) { - msta = container_of(status->wcid, struct mt7996_sta, wcid); - mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + struct mt7996_sta_link *msta_link; + + msta_link = container_of(status->wcid, struct mt7996_sta_link, + wcid); + msta = msta_link->sta; + mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid); } status->freq = mphy->chandef.chan->center_freq; @@ -619,6 +647,14 @@ mt7996_mac_fill_rx(struct mt7996_dev *dev, enum mt76_rxq_id q, status->last_amsdu = amsdu_info == MT_RXD4_LAST_AMSDU_FRAME; } + /* IEEE 802.11 fragmentation can only be applied to unicast frames. + * Hence, drop fragments with multicast/broadcast RA. + * This check fixes vulnerabilities, like CVE-2020-26145. + */ + if ((ieee80211_has_morefrags(fc) || seq_ctrl & IEEE80211_SCTL_FRAG) && + FIELD_GET(MT_RXD3_NORMAL_ADDR_TYPE, rxd3) != MT_RXD3_NORMAL_U2M) + return -EINVAL; + hdr_gap = (u8 *)rxd - skb->data + 2 * remove_pad; if (hdr_trans && ieee80211_has_morefrags(fc)) { if (mt7996_reverse_frag0_hdr_trans(skb, hdr_gap)) @@ -717,9 +753,8 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi, u32 val; if (wcid->sta) { - struct ieee80211_sta *sta; + struct ieee80211_sta *sta = wcid_to_sta(wcid); - sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); wmm = sta->wme; } @@ -746,7 +781,9 @@ mt7996_mac_write_txwi_8023(struct mt7996_dev *dev, __le32 *txwi, static void mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, - struct sk_buff *skb, struct ieee80211_key_conf *key) + struct sk_buff *skb, + struct ieee80211_key_conf *key, + struct mt76_wcid *wcid) { struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; @@ -754,15 +791,19 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, bool multicast = is_multicast_ether_addr(hdr->addr1); u8 tid = skb->priority & IEEE80211_QOS_CTL_TID_MASK; __le16 fc = hdr->frame_control, sc = hdr->seq_ctrl; + u16 seqno = le16_to_cpu(sc); u8 fc_type, fc_stype; u32 val; if (ieee80211_is_action(fc) && mgmt->u.action.category == WLAN_CATEGORY_BACK && - mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ) + mgmt->u.action.u.addba_req.action_code == WLAN_ACTION_ADDBA_REQ) { + if (is_mt7990(&dev->mt76)) + txwi[6] |= cpu_to_le32(FIELD_PREP(MT_TXD6_TID_ADDBA, tid)); tid = MT_TX_ADDBA; - else if (ieee80211_is_mgmt(hdr->frame_control)) + } else if (ieee80211_is_mgmt(hdr->frame_control)) { tid = MT_TX_NORMAL; + } val = FIELD_PREP(MT_TXD1_HDR_FORMAT, MT_HDR_FORMAT_802_11) | FIELD_PREP(MT_TXD1_HDR_INFO, @@ -773,8 +814,7 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, info->flags & IEEE80211_TX_CTL_USE_MINRATE) val |= MT_TXD1_FIXED_RATE; - if (key && multicast && ieee80211_is_robust_mgmt_frame(skb) && - key->cipher == WLAN_CIPHER_SUITE_AES_CMAC) { + if (key && multicast && ieee80211_is_robust_mgmt_frame(skb)) { val |= MT_TXD1_BIP; txwi[3] &= ~cpu_to_le32(MT_TXD3_PROTECT_FRAME); } @@ -804,9 +844,13 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] |= cpu_to_le32(MT_TXD3_REM_TX_COUNT); } - if (info->flags & IEEE80211_TX_CTL_INJECTED) { - u16 seqno = le16_to_cpu(sc); + if (multicast && ieee80211_vif_is_mld(info->control.vif)) { + val = MT_TXD3_SN_VALID | + FIELD_PREP(MT_TXD3_SEQ, IEEE80211_SEQ_TO_SN(seqno)); + txwi[3] |= cpu_to_le32(val); + } + if (info->flags & IEEE80211_TX_CTL_INJECTED) { if (ieee80211_is_back_req(hdr->frame_control)) { struct ieee80211_bar *bar; @@ -819,6 +863,19 @@ mt7996_mac_write_txwi_80211(struct mt7996_dev *dev, __le32 *txwi, txwi[3] |= cpu_to_le32(val); txwi[3] &= ~cpu_to_le32(MT_TXD3_HW_AMSDU); } + + if (ieee80211_vif_is_mld(info->control.vif) && + (multicast || unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE)))) + txwi[5] |= cpu_to_le32(MT_TXD5_FL); + + if (ieee80211_is_nullfunc(fc) && ieee80211_has_a4(fc) && + ieee80211_vif_is_mld(info->control.vif)) { + txwi[5] |= cpu_to_le32(MT_TXD5_FL); + txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); + } + + if (!wcid->sta && ieee80211_is_mgmt(fc)) + txwi[6] |= cpu_to_le32(MT_TXD6_DIS_MAT); } void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, @@ -832,7 +889,9 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, u8 band_idx = (info->hw_queue & MT_TX_HW_QUEUE_PHY) >> 2; u8 p_fmt, q_idx, omac_idx = 0, wmm_idx = 0; bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; - struct mt76_vif_link *mvif; + struct mt76_vif_link *mlink = NULL; + struct mt7996_vif *mvif; + unsigned int link_id; u16 tx_count = 15; u32 val; bool inband_disc = !!(changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | @@ -840,11 +899,20 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, bool beacon = !!(changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) && (!inband_disc); - mvif = vif ? (struct mt76_vif_link *)vif->drv_priv : NULL; - if (mvif) { - omac_idx = mvif->omac_idx; - wmm_idx = mvif->wmm_idx; - band_idx = mvif->band_idx; + if (wcid != &dev->mt76.global_wcid) + link_id = wcid->link_id; + else + link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + mvif = vif ? (struct mt7996_vif *)vif->drv_priv : NULL; + if (mvif) + mlink = rcu_dereference(mvif->mt76.link[link_id]); + + if (mlink) { + omac_idx = mlink->omac_idx; + wmm_idx = mlink->wmm_idx; + band_idx = mlink->band_idx; } if (inband_disc) { @@ -891,7 +959,10 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, val |= MT_TXD5_TX_STATUS_HOST; txwi[5] = cpu_to_le32(val); - val = MT_TXD6_DIS_MAT | MT_TXD6_DAS; + val = MT_TXD6_DAS; + if (q_idx >= MT_LMAC_ALTX0 && q_idx <= MT_LMAC_BCN0) + val |= MT_TXD6_DIS_MAT; + if (is_mt7996(&dev->mt76)) val |= FIELD_PREP(MT_TXD6_MSDU_CNT, 1); else if (is_8023 || !ieee80211_is_mgmt(hdr->frame_control)) @@ -903,34 +974,56 @@ void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, if (is_8023) mt7996_mac_write_txwi_8023(dev, txwi, skb, wcid); else - mt7996_mac_write_txwi_80211(dev, txwi, skb, key); + mt7996_mac_write_txwi_80211(dev, txwi, skb, key, wcid); if (txwi[1] & cpu_to_le32(MT_TXD1_FIXED_RATE)) { bool mcast = ieee80211_is_data(hdr->frame_control) && is_multicast_ether_addr(hdr->addr1); u8 idx = MT7996_BASIC_RATES_TBL; - if (mvif) { - if (mcast && mvif->mcast_rates_idx) - idx = mvif->mcast_rates_idx; - else if (beacon && mvif->beacon_rates_idx) - idx = mvif->beacon_rates_idx; + if (mlink) { + if (mcast && mlink->mcast_rates_idx) + idx = mlink->mcast_rates_idx; + else if (beacon && mlink->beacon_rates_idx) + idx = mlink->beacon_rates_idx; else - idx = mvif->basic_rates_idx; + idx = mlink->basic_rates_idx; } val = FIELD_PREP(MT_TXD6_TX_RATE, idx) | MT_TXD6_FIXED_BW; + if (mcast) + val |= MT_TXD6_DIS_MAT; txwi[6] |= cpu_to_le32(val); txwi[3] |= cpu_to_le32(MT_TXD3_BA_DISABLE); } } +static bool +mt7996_tx_use_mgmt(struct mt7996_dev *dev, struct sk_buff *skb) +{ + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; + + if (ieee80211_is_mgmt(hdr->frame_control)) + return true; + + /* for SDO to bypass specific data frame */ + if (!mt7996_has_wa(dev)) { + if (unlikely(skb->protocol == cpu_to_be16(ETH_P_PAE))) + return true; + + if (ieee80211_has_a4(hdr->frame_control) && + !ieee80211_is_data_present(hdr->frame_control)) + return true; + } + + return false; +} + int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, enum mt76_txq_id qid, struct mt76_wcid *wcid, struct ieee80211_sta *sta, struct mt76_tx_info *tx_info) { - struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)tx_info->skb->data; struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct ieee80211_tx_info *info = IEEE80211_SKB_CB(tx_info->skb); struct ieee80211_key_conf *key = info->control.hw_key; @@ -955,8 +1048,11 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, return id; pid = mt76_tx_status_skb_add(mdev, wcid, tx_info->skb); - mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key, - pid, qid, 0); + memset(txwi_ptr, 0, MT_TXD_SIZE); + /* Transmit non qos data by 802.11 header and need to fill txd by host*/ + if (!is_8023 || pid >= MT_PACKET_ID_FIRST) + mt7996_mac_write_txwi(dev, txwi_ptr, tx_info->skb, wcid, key, + pid, qid, 0); txp = (struct mt76_connac_txp_common *)(txwi + MT_TXD_SIZE); for (i = 0; i < nbuf; i++) { @@ -973,19 +1069,27 @@ int mt7996_tx_prepare_skb(struct mt76_dev *mdev, void *txwi_ptr, } txp->fw.nbuf = nbuf; - txp->fw.flags = - cpu_to_le16(MT_CT_INFO_FROM_HOST | MT_CT_INFO_APPLY_TXD); + txp->fw.flags = cpu_to_le16(MT_CT_INFO_FROM_HOST); + + if (!is_8023 || pid >= MT_PACKET_ID_FIRST) + txp->fw.flags |= cpu_to_le16(MT_CT_INFO_APPLY_TXD); if (!key) txp->fw.flags |= cpu_to_le16(MT_CT_INFO_NONE_CIPHER_FRAME); - if (!is_8023 && ieee80211_is_mgmt(hdr->frame_control)) + if (!is_8023 && mt7996_tx_use_mgmt(dev, tx_info->skb)) txp->fw.flags |= cpu_to_le16(MT_CT_INFO_MGMT_FRAME); if (vif) { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt76_vif_link *mlink = NULL; - txp->fw.bss_idx = mvif->deflink.mt76.idx; + if (wcid->offchannel) + mlink = rcu_dereference(mvif->mt76.offchannel_link); + if (!mlink) + mlink = &mvif->deflink.mt76; + + txp->fw.bss_idx = mlink->idx; } txp->fw.token = cpu_to_le16(id); @@ -1027,9 +1131,10 @@ u32 mt7996_wed_init_buf(void *ptr, dma_addr_t phys, int token_id) static void mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb) { - struct mt7996_sta *msta; struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); bool is_8023 = info->flags & IEEE80211_TX_CTL_HW_80211_ENCAP; + struct mt7996_sta_link *msta_link; + struct mt7996_sta *msta; u16 fc, tid; if (!sta || !(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) @@ -1058,7 +1163,9 @@ mt7996_tx_check_aggr(struct ieee80211_sta *sta, struct sk_buff *skb) return; msta = (struct mt7996_sta *)sta->drv_priv; - if (!test_and_set_bit(tid, &msta->wcid.ampdu_state)) + msta_link = &msta->deflink; + + if (!test_and_set_bit(tid, &msta_link->wcid.ampdu_state)) ieee80211_start_tx_ba_session(sta, tid, 0); } @@ -1108,6 +1215,7 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) void *end = data + len; bool wake = false; u16 total, count = 0; + u8 ver; /* clean DMA queues and unmap buffers first */ mt76_queue_tx_cleanup(dev, dev->mphy.q_tx[MT_TXQ_PSD], false); @@ -1121,7 +1229,8 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) mt76_queue_tx_cleanup(dev, phy3->q_tx[MT_TXQ_BE], false); } - if (WARN_ON_ONCE(le32_get_bits(tx_free[1], MT_TXFREE1_VER) < 5)) + ver = le32_get_bits(tx_free[1], MT_TXFREE1_VER); + if (WARN_ON_ONCE(ver < 5)) return; total = le32_get_bits(tx_free[0], MT_TXFREE0_MSDU_CNT); @@ -1136,17 +1245,23 @@ mt7996_mac_tx_free(struct mt7996_dev *dev, void *data, int len) */ info = le32_to_cpu(*cur_info); if (info & MT_TXFREE_INFO_PAIR) { - struct mt7996_sta *msta; + struct mt7996_sta_link *msta_link; u16 idx; idx = FIELD_GET(MT_TXFREE_INFO_WLAN_ID, info); wcid = rcu_dereference(dev->mt76.wcid[idx]); sta = wcid_to_sta(wcid); if (!sta) - continue; - - msta = container_of(wcid, struct mt7996_sta, wcid); - mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + goto next; + + msta_link = container_of(wcid, struct mt7996_sta_link, + wcid); + mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid); +next: + /* ver 7 has a new DW with pair = 1, skip it */ + if (ver == 7 && ((void *)(cur_info + 1) < end) && + (le32_to_cpu(*(cur_info + 1)) & MT_TXFREE_INFO_PAIR)) + cur_info++; continue; } else if (info & MT_TXFREE_INFO_HEADER) { u32 tx_retries = 0, tx_failed = 0; @@ -1230,7 +1345,7 @@ mt7996_mac_add_txs_skb(struct mt7996_dev *dev, struct mt76_wcid *wcid, struct ieee80211_sta *sta; u8 tid; - sta = container_of((void *)wcid, struct ieee80211_sta, drv_priv); + sta = wcid_to_sta(wcid); tid = FIELD_GET(MT_TXS0_TID, txs); ieee80211_refresh_tx_agg_session_timer(sta, tid); } @@ -1344,7 +1459,7 @@ out: static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data) { - struct mt7996_sta *msta = NULL; + struct mt7996_sta_link *msta_link; struct mt76_wcid *wcid; __le32 *txs_data = data; u16 wcidx; @@ -1365,14 +1480,13 @@ static void mt7996_mac_add_txs(struct mt7996_dev *dev, void *data) if (!wcid) goto out; - msta = container_of(wcid, struct mt7996_sta, wcid); - mt7996_mac_add_txs_skb(dev, wcid, pid, txs_data); if (!wcid->sta) goto out; - mt76_wcid_add_poll(&dev->mt76, &msta->wcid); + msta_link = container_of(wcid, struct mt7996_sta_link, wcid); + mt76_wcid_add_poll(&dev->mt76, &msta_link->wcid); out: rcu_read_unlock(); @@ -1399,7 +1513,7 @@ bool mt7996_rx_check(struct mt76_dev *mdev, void *data, int len) mt7996_mac_tx_free(dev, data, len); return false; case PKT_TYPE_TXS: - for (rxd += 4; rxd + 8 <= end; rxd += 8) + for (rxd += MT_TXS_HDR_SIZE; rxd + MT_TXS_SIZE <= end; rxd += MT_TXS_SIZE) mt7996_mac_add_txs(dev, rxd); return false; case PKT_TYPE_RX_FW_MONITOR: @@ -1442,7 +1556,7 @@ void mt7996_queue_rx_skb(struct mt76_dev *mdev, enum mt76_rxq_id q, mt7996_mcu_rx_event(dev, skb); break; case PKT_TYPE_TXS: - for (rxd += 4; rxd + 8 <= end; rxd += 8) + for (rxd += MT_TXS_HDR_SIZE; rxd + MT_TXS_SIZE <= end; rxd += MT_TXS_SIZE) mt7996_mac_add_txs(dev, rxd); dev_kfree_skb(skb); break; @@ -2239,38 +2353,70 @@ void mt7996_mac_update_stats(struct mt7996_phy *phy) void mt7996_mac_sta_rc_work(struct work_struct *work) { struct mt7996_dev *dev = container_of(work, struct mt7996_dev, rc_work); + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + struct mt76_vif_link *mlink; struct ieee80211_sta *sta; struct ieee80211_vif *vif; struct mt7996_sta *msta; - u32 changed; + struct mt7996_vif *mvif; LIST_HEAD(list); + u32 changed; + u8 link_id; + rcu_read_lock(); spin_lock_bh(&dev->mt76.sta_poll_lock); list_splice_init(&dev->sta_rc_list, &list); while (!list_empty(&list)) { - msta = list_first_entry(&list, struct mt7996_sta, rc_list); - list_del_init(&msta->rc_list); - changed = msta->changed; - msta->changed = 0; + msta_link = list_first_entry(&list, struct mt7996_sta_link, + rc_list); + list_del_init(&msta_link->rc_list); + + changed = msta_link->changed; + msta_link->changed = 0; + + sta = wcid_to_sta(&msta_link->wcid); + link_id = msta_link->wcid.link_id; + msta = msta_link->sta; + mvif = msta->vif; + vif = container_of((void *)mvif, struct ieee80211_vif, drv_priv); + + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (!mlink) + continue; + + link_sta = rcu_dereference(sta->link[link_id]); + if (!link_sta) + continue; + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (!link_conf) + continue; + spin_unlock_bh(&dev->mt76.sta_poll_lock); - sta = container_of((void *)msta, struct ieee80211_sta, drv_priv); - vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + link = (struct mt7996_vif_link *)mlink; if (changed & (IEEE80211_RC_SUPP_RATES_CHANGED | IEEE80211_RC_NSS_CHANGED | IEEE80211_RC_BW_CHANGED)) - mt7996_mcu_add_rate_ctrl(dev, vif, sta, true); + mt7996_mcu_add_rate_ctrl(dev, vif, link_conf, + link_sta, link, msta_link, + true); if (changed & IEEE80211_RC_SMPS_CHANGED) - mt7996_mcu_set_fixed_field(dev, vif, sta, NULL, + mt7996_mcu_set_fixed_field(dev, link_sta, link, + msta_link, NULL, RATE_PARAM_MMPS_UPDATE); spin_lock_bh(&dev->mt76.sta_poll_lock); } spin_unlock_bh(&dev->mt76.sta_poll_lock); + rcu_read_unlock(); } void mt7996_mac_work(struct work_struct *work) @@ -2308,16 +2454,15 @@ void mt7996_mac_work(struct work_struct *work) static void mt7996_dfs_stop_radar_detector(struct mt7996_phy *phy) { struct mt7996_dev *dev = phy->dev; + int rdd_idx = mt7996_get_rdd_idx(phy, false); - if (phy->rdd_state & BIT(0)) - mt7996_mcu_rdd_cmd(dev, RDD_STOP, 0, - MT_RX_SEL0, 0); - if (phy->rdd_state & BIT(1)) - mt7996_mcu_rdd_cmd(dev, RDD_STOP, 1, - MT_RX_SEL0, 0); + if (rdd_idx < 0) + return; + + mt7996_mcu_rdd_cmd(dev, RDD_STOP, rdd_idx, 0); } -static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int chain) +static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int rdd_idx) { int err, region; @@ -2334,44 +2479,30 @@ static int mt7996_dfs_start_rdd(struct mt7996_dev *dev, int chain) break; } - err = mt7996_mcu_rdd_cmd(dev, RDD_START, chain, - MT_RX_SEL0, region); + err = mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region); if (err < 0) return err; - return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, chain, - MT_RX_SEL0, 1); + return mt7996_mcu_rdd_cmd(dev, RDD_DET_MODE, rdd_idx, 1); } static int mt7996_dfs_start_radar_detector(struct mt7996_phy *phy) { - struct cfg80211_chan_def *chandef = &phy->mt76->chandef; struct mt7996_dev *dev = phy->dev; - u8 band_idx = phy->mt76->band_idx; - int err; + int err, rdd_idx; - /* start CAC */ - err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_START, band_idx, - MT_RX_SEL0, 0); - if (err < 0) - return err; + rdd_idx = mt7996_get_rdd_idx(phy, false); + if (rdd_idx < 0) + return -EINVAL; - err = mt7996_dfs_start_rdd(dev, band_idx); + /* start CAC */ + err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_START, rdd_idx, 0); if (err < 0) return err; - phy->rdd_state |= BIT(band_idx); + err = mt7996_dfs_start_rdd(dev, rdd_idx); - if (chandef->width == NL80211_CHAN_WIDTH_160 || - chandef->width == NL80211_CHAN_WIDTH_80P80) { - err = mt7996_dfs_start_rdd(dev, 1); - if (err < 0) - return err; - - phy->rdd_state |= BIT(1); - } - - return 0; + return err; } static int @@ -2412,12 +2543,12 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy) { struct mt7996_dev *dev = phy->dev; enum mt76_dfs_state dfs_state, prev_state; - int err; + int err, rdd_idx = mt7996_get_rdd_idx(phy, false); prev_state = phy->mt76->dfs_state; dfs_state = mt76_phy_dfs_state(phy->mt76); - if (prev_state == dfs_state) + if (prev_state == dfs_state || rdd_idx < 0) return 0; if (prev_state == MT_DFS_STATE_UNKNOWN) @@ -2441,8 +2572,7 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy) if (dfs_state == MT_DFS_STATE_CAC) return 0; - err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_END, - phy->mt76->band_idx, MT_RX_SEL0, 0); + err = mt7996_mcu_rdd_cmd(dev, RDD_CAC_END, rdd_idx, 0); if (err < 0) { phy->mt76->dfs_state = MT_DFS_STATE_UNKNOWN; return err; @@ -2452,8 +2582,7 @@ int mt7996_dfs_init_radar_detector(struct mt7996_phy *phy) return 0; stop: - err = mt7996_mcu_rdd_cmd(dev, RDD_NORMAL_START, - phy->mt76->band_idx, MT_RX_SEL0, 0); + err = mt7996_mcu_rdd_cmd(dev, RDD_NORMAL_START, rdd_idx, 0); if (err < 0) return err; @@ -2538,7 +2667,7 @@ static int mt7996_mac_check_twt_req(struct ieee80211_twt_setup *twt) } static bool -mt7996_mac_twt_param_equal(struct mt7996_sta *msta, +mt7996_mac_twt_param_equal(struct mt7996_sta_link *msta_link, struct ieee80211_twt_params *twt_agrt) { u16 type = le16_to_cpu(twt_agrt->req_type); @@ -2549,10 +2678,10 @@ mt7996_mac_twt_param_equal(struct mt7996_sta *msta, for (i = 0; i < MT7996_MAX_STA_TWT_AGRT; i++) { struct mt7996_twt_flow *f; - if (!(msta->twt.flowid_mask & BIT(i))) + if (!(msta_link->twt.flowid_mask & BIT(i))) continue; - f = &msta->twt.flow[i]; + f = &msta_link->twt.flow[i]; if (f->duration == twt_agrt->min_twt_dur && f->mantissa == twt_agrt->mantissa && f->exp == exp && @@ -2572,6 +2701,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, enum ieee80211_twt_setup_cmd setup_cmd = TWT_SETUP_CMD_REJECT; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct ieee80211_twt_params *twt_agrt = (void *)twt->params; + struct mt7996_sta_link *msta_link = &msta->deflink; u16 req_type = le16_to_cpu(twt_agrt->req_type); enum ieee80211_twt_setup_cmd sta_setup_cmd; struct mt7996_dev *dev = mt7996_hw_dev(hw); @@ -2586,7 +2716,8 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, if (dev->twt.n_agrt == MT7996_MAX_TWT_AGRT) goto unlock; - if (hweight8(msta->twt.flowid_mask) == ARRAY_SIZE(msta->twt.flow)) + if (hweight8(msta_link->twt.flowid_mask) == + ARRAY_SIZE(msta_link->twt.flow)) goto unlock; if (twt_agrt->min_twt_dur < MT7996_MIN_TWT_DUR) { @@ -2595,10 +2726,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, goto unlock; } - if (mt7996_mac_twt_param_equal(msta, twt_agrt)) + if (mt7996_mac_twt_param_equal(msta_link, twt_agrt)) goto unlock; - flowid = ffs(~msta->twt.flowid_mask) - 1; + flowid = ffs(~msta_link->twt.flowid_mask) - 1; twt_agrt->req_type &= ~cpu_to_le16(IEEE80211_TWT_REQTYPE_FLOWID); twt_agrt->req_type |= le16_encode_bits(flowid, IEEE80211_TWT_REQTYPE_FLOWID); @@ -2607,10 +2738,10 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, exp = FIELD_GET(IEEE80211_TWT_REQTYPE_WAKE_INT_EXP, req_type); sta_setup_cmd = FIELD_GET(IEEE80211_TWT_REQTYPE_SETUP_CMD, req_type); - flow = &msta->twt.flow[flowid]; + flow = &msta_link->twt.flow[flowid]; memset(flow, 0, sizeof(*flow)); INIT_LIST_HEAD(&flow->list); - flow->wcid = msta->wcid.idx; + flow->wcid = msta_link->wcid.idx; flow->table_id = table_id; flow->id = flowid; flow->duration = twt_agrt->min_twt_dur; @@ -2628,7 +2759,7 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, flow->sched = true; flow->start_tsf = mt7996_mac_twt_sched_list_add(dev, flow); - curr_tsf = __mt7996_get_tsf(hw, msta->vif); + curr_tsf = __mt7996_get_tsf(hw, &msta->vif->deflink); div_u64_rem(curr_tsf - flow->start_tsf, interval, &rem); flow_tsf = curr_tsf + interval - rem; twt_agrt->twt = cpu_to_le64(flow_tsf); @@ -2637,12 +2768,13 @@ void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, } flow->tsf = le64_to_cpu(twt_agrt->twt); - if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, MCU_TWT_AGRT_ADD)) + if (mt7996_mcu_twt_agrt_update(dev, &msta->vif->deflink, flow, + MCU_TWT_AGRT_ADD)) goto unlock; setup_cmd = TWT_SETUP_CMD_ACCEPT; dev->twt.table_mask |= BIT(table_id); - msta->twt.flowid_mask |= BIT(flowid); + msta_link->twt.flowid_mask |= BIT(flowid); dev->twt.n_agrt++; unlock: @@ -2655,26 +2787,26 @@ out: } void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, - struct mt7996_sta *msta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, u8 flowid) { struct mt7996_twt_flow *flow; lockdep_assert_held(&dev->mt76.mutex); - if (flowid >= ARRAY_SIZE(msta->twt.flow)) + if (flowid >= ARRAY_SIZE(msta_link->twt.flow)) return; - if (!(msta->twt.flowid_mask & BIT(flowid))) + if (!(msta_link->twt.flowid_mask & BIT(flowid))) return; - flow = &msta->twt.flow[flowid]; - if (mt7996_mcu_twt_agrt_update(dev, msta->vif, flow, - MCU_TWT_AGRT_DELETE)) + flow = &msta_link->twt.flow[flowid]; + if (mt7996_mcu_twt_agrt_update(dev, link, flow, MCU_TWT_AGRT_DELETE)) return; list_del_init(&flow->list); - msta->twt.flowid_mask &= ~BIT(flowid); + msta_link->twt.flowid_mask &= ~BIT(flowid); dev->twt.table_mask &= ~BIT(flow->table_id); dev->twt.n_agrt--; } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/main.c b/drivers/net/wireless/mediatek/mt76/mt7996/main.c index 69dd565d8319..78ae9f5cb176 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/main.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/main.c @@ -68,11 +68,13 @@ static int mt7996_start(struct ieee80211_hw *hw) static void mt7996_stop_phy(struct mt7996_phy *phy) { - struct mt7996_dev *dev = phy->dev; + struct mt7996_dev *dev; if (!phy || !test_bit(MT76_STATE_RUNNING, &phy->mt76->state)) return; + dev = phy->dev; + cancel_delayed_work_sync(&phy->mt76->mac_work); mutex_lock(&dev->mt76.mutex); @@ -158,58 +160,101 @@ mt7996_init_bitrate_mask(struct ieee80211_vif *vif, struct mt7996_vif_link *mlin static int mt7996_set_hw_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_vif *vif, struct ieee80211_sta *sta, - struct mt7996_vif_link *mlink, struct ieee80211_key_conf *key) + struct ieee80211_key_conf *key) { struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_sta *msta = sta ? (struct mt7996_sta *)sta->drv_priv : - &mlink->sta; - struct mt76_wcid *wcid = &msta->wcid; - u8 *wcid_keyidx = &wcid->hw_key_idx; - struct mt7996_phy *phy; int idx = key->keyidx; + unsigned int link_id; + unsigned long links; + + if (key->link_id >= 0) + links = BIT(key->link_id); + else if (sta && sta->valid_links) + links = sta->valid_links; + else if (vif->valid_links) + links = vif->valid_links; + else + links = BIT(0); - phy = mt7996_vif_link_phy(mlink); - if (!phy) - return -EINVAL; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + u8 *wcid_keyidx; + int err; - if (sta && !wcid->sta) - return -EOPNOTSUPP; + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; - switch (key->cipher) { - case WLAN_CIPHER_SUITE_AES_CMAC: - case WLAN_CIPHER_SUITE_BIP_CMAC_256: - case WLAN_CIPHER_SUITE_BIP_GMAC_128: - case WLAN_CIPHER_SUITE_BIP_GMAC_256: - if (key->keyidx == 6 || key->keyidx == 7) { - wcid_keyidx = &wcid->hw_key_idx2; - key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + if (sta) { + struct mt7996_sta *msta; + + msta = (struct mt7996_sta *)sta->drv_priv; + msta_link = mt76_dereference(msta->link[link_id], + &dev->mt76); + if (!msta_link) + continue; + + if (!msta_link->wcid.sta) + return -EOPNOTSUPP; + } else { + msta_link = &link->msta_link; + } + wcid_keyidx = &msta_link->wcid.hw_key_idx; + + switch (key->cipher) { + case WLAN_CIPHER_SUITE_AES_CMAC: + case WLAN_CIPHER_SUITE_BIP_CMAC_256: + case WLAN_CIPHER_SUITE_BIP_GMAC_128: + case WLAN_CIPHER_SUITE_BIP_GMAC_256: + if (key->keyidx == 6 || key->keyidx == 7) { + wcid_keyidx = &msta_link->wcid.hw_key_idx2; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIE; + } + break; + default: + break; } - break; - default: - break; - } - if (cmd == SET_KEY && !sta && !mlink->mt76.cipher) { - mlink->mt76.cipher = mt76_connac_mcu_get_cipher(key->cipher); - mt7996_mcu_add_bss_info(phy, vif, &vif->bss_conf, &mlink->mt76, true); - } + if (cmd == SET_KEY && !sta && !link->mt76.cipher) { + struct ieee80211_bss_conf *link_conf; - if (cmd == SET_KEY) { - *wcid_keyidx = idx; - } else { - if (idx == *wcid_keyidx) - *wcid_keyidx = -1; - return 0; - } + link_conf = link_conf_dereference_protected(vif, + link_id); + if (!link_conf) + link_conf = &vif->bss_conf; - mt76_wcid_key_setup(&dev->mt76, wcid, key); + link->mt76.cipher = + mt76_connac_mcu_get_cipher(key->cipher); + mt7996_mcu_add_bss_info(link->phy, vif, link_conf, + &link->mt76, msta_link, true); + } - if (key->keyidx == 6 || key->keyidx == 7) - return mt7996_mcu_bcn_prot_enable(dev, vif, key); + if (cmd == SET_KEY) { + *wcid_keyidx = idx; + } else { + if (idx == *wcid_keyidx) + *wcid_keyidx = -1; + continue; + } - return mt7996_mcu_add_key(&dev->mt76, vif, key, - MCU_WMWA_UNI_CMD(STA_REC_UPDATE), - &msta->wcid, cmd); + mt76_wcid_key_setup(&dev->mt76, &msta_link->wcid, key); + + if (key->keyidx == 6 || key->keyidx == 7) { + err = mt7996_mcu_bcn_prot_enable(dev, link, + msta_link, key); + if (err) + return err; + } + + err = mt7996_mcu_add_key(&dev->mt76, vif, key, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), + &msta_link->wcid, cmd); + if (err) + return err; + } + + return 0; } static void @@ -217,12 +262,10 @@ mt7996_key_iter(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_sta *sta, struct ieee80211_key_conf *key, void *data) { - struct mt7996_vif_link *mlink = data; - if (sta) return; - WARN_ON(mt7996_set_hw_key(hw, SET_KEY, vif, NULL, mlink, key)); + WARN_ON(mt7996_set_hw_key(hw, SET_KEY, vif, NULL, key)); } int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, @@ -230,6 +273,8 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink) { struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &link->msta_link; struct mt7996_phy *phy = mphy->priv; struct mt7996_dev *dev = phy->dev; u8 band_idx = phy->mt76->band_idx; @@ -248,7 +293,8 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, mlink->omac_idx = idx; mlink->band_idx = band_idx; mlink->wmm_idx = vif->type == NL80211_IFTYPE_AP ? 0 : 3; - mlink->wcid = &link->sta.wcid; + mlink->wcid = &msta_link->wcid; + mlink->wcid->offchannel = mlink->offchannel; ret = mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, true); if (ret) @@ -259,10 +305,11 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, idx = MT7996_WTBL_RESERVED - mlink->idx; - INIT_LIST_HEAD(&link->sta.rc_list); - link->sta.wcid.idx = idx; - link->sta.wcid.tx_info |= MT_WCID_TX_INFO_SET; - mt76_wcid_init(&link->sta.wcid, band_idx); + INIT_LIST_HEAD(&msta_link->rc_list); + msta_link->wcid.idx = idx; + msta_link->wcid.link_id = link_conf->link_id; + msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; + mt76_wcid_init(&msta_link->wcid, band_idx); mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); @@ -283,15 +330,19 @@ int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, mt7996_init_bitrate_mask(vif, link); - mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, true); + mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, true); /* defer the first STA_REC of BMC entry to BSS_CHANGED_BSSID for STA * interface, since firmware only records BSSID when the entry is new */ if (vif->type != NL80211_IFTYPE_STATION) - mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_PORT_SECURE, true); - rcu_assign_pointer(dev->mt76.wcid[idx], &link->sta.wcid); + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_PORT_SECURE, true); + rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); + + ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, NULL); - ieee80211_iter_keys(mphy->hw, vif, mt7996_key_iter, link); + if (mvif->mt76.deflink_id == IEEE80211_LINK_UNSPECIFIED) + mvif->mt76.deflink_id = link_conf->link_id; return 0; } @@ -301,29 +352,42 @@ void mt7996_vif_link_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink) { struct mt7996_vif_link *link = container_of(mlink, struct mt7996_vif_link, mt76); + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link = &link->msta_link; struct mt7996_phy *phy = mphy->priv; struct mt7996_dev *dev = phy->dev; - struct mt7996_sta *msta; - int idx; + int idx = msta_link->wcid.idx; - msta = &link->sta; - idx = msta->wcid.idx; - mt7996_mcu_add_sta(dev, vif, mlink, NULL, CONN_STATE_DISCONNECT, false); - mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, false); + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_DISCONNECT, false); + mt7996_mcu_add_bss_info(phy, vif, link_conf, mlink, msta_link, false); mt7996_mcu_add_dev_info(phy, vif, link_conf, mlink, false); rcu_assign_pointer(dev->mt76.wcid[idx], NULL); + if (mvif->mt76.deflink_id == link_conf->link_id) { + struct ieee80211_bss_conf *iter; + unsigned int link_id; + + mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED; + for_each_vif_active_link(vif, iter, link_id) { + if (link_id != IEEE80211_LINK_UNSPECIFIED) { + mvif->mt76.deflink_id = link_id; + break; + } + } + } + dev->mt76.vif_mask &= ~BIT_ULL(mlink->idx); phy->omac_mask &= ~BIT_ULL(mlink->omac_idx); spin_lock_bh(&dev->mt76.sta_poll_lock); - if (!list_empty(&msta->wcid.poll_list)) - list_del_init(&msta->wcid.poll_list); + if (!list_empty(&msta_link->wcid.poll_list)) + list_del_init(&msta_link->wcid.poll_list); spin_unlock_bh(&dev->mt76.sta_poll_lock); - mt76_wcid_cleanup(&dev->mt76, &msta->wcid); + mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); } static void mt7996_phy_set_rxfilter(struct mt7996_phy *phy) @@ -352,11 +416,13 @@ static void mt7996_phy_set_rxfilter(struct mt7996_phy *phy) static void mt7996_set_monitor(struct mt7996_phy *phy, bool enabled) { - struct mt7996_dev *dev = phy->dev; + struct mt7996_dev *dev; if (!phy) return; + dev = phy->dev; + if (enabled == !(phy->rxfilter & MT_WF_RFCR_DROP_OTHER_UC)) return; @@ -399,6 +465,7 @@ static int mt7996_add_interface(struct ieee80211_hw *hw, mt76_vif_init(vif, &mvif->mt76); vif->offload_flags |= IEEE80211_OFFLOAD_ENCAP_4ADDR; + mvif->mt76.deflink_id = IEEE80211_LINK_UNSPECIFIED; out: mutex_unlock(&dev->mt76.mutex); @@ -480,7 +547,6 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_vif_link *mlink = &mvif->deflink; int err; /* The hardware does not support per-STA RX GTK, fallback @@ -515,11 +581,11 @@ static int mt7996_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, return -EOPNOTSUPP; } - if (!mt7996_vif_link_phy(mlink)) - return 0; /* defer until after link add */ + if (!mt7996_vif_link_phy(&mvif->deflink)) + return 0; /* defer until after link add */ mutex_lock(&dev->mt76.mutex); - err = mt7996_set_hw_key(hw, cmd, vif, sta, mlink, key); + err = mt7996_set_hw_key(hw, cmd, vif, sta, key); mutex_unlock(&dev->mt76.mutex); return err; @@ -601,6 +667,33 @@ static void mt7996_configure_filter(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); } +static int +mt7996_get_txpower(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + unsigned int link_id, int *dbm) +{ + struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct wireless_dev *wdev; + int n_chains, delta, i; + + if (!phy) { + wdev = ieee80211_vif_to_wdev(vif); + for (i = 0; i < hw->wiphy->n_radio; i++) + if (wdev->radio_mask & BIT(i)) + phy = dev->radio_phy[i]; + + if (!phy) + return -EINVAL; + } + + n_chains = hweight16(phy->mt76->chainmask); + delta = mt76_tx_power_path_delta(n_chains); + *dbm = DIV_ROUND_UP(phy->mt76->txpower_cur + delta, 2); + + return 0; +} + static u8 mt7996_get_rates_table(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf, bool beacon, bool mcast) @@ -630,12 +723,11 @@ mt7996_get_rates_table(struct mt7996_phy *phy, struct ieee80211_bss_conf *conf, } static void -mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif, +mt7996_update_mu_group(struct ieee80211_hw *hw, struct mt7996_vif_link *link, struct ieee80211_bss_conf *info) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_dev *dev = mt7996_hw_dev(hw); - u8 band = mvif->deflink.mt76.band_idx; + u8 band = link->mt76.band_idx; u32 *mu; mu = (u32 *)info->mu_group.membership; @@ -649,23 +741,56 @@ mt7996_update_mu_group(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mt76_wr(dev, MT_WF_PHYRX_BAND_GID_TAB_POS3(band), mu[3]); } -static void mt7996_bss_info_changed(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_bss_conf *info, - u64 changed) +static void +mt7996_vif_cfg_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u64 changed) +{ + struct mt7996_dev *dev = mt7996_hw_dev(hw); + + mutex_lock(&dev->mt76.mutex); + + if ((changed & BSS_CHANGED_ASSOC) && vif->cfg.assoc) { + struct ieee80211_bss_conf *link_conf; + unsigned long link_id; + + for_each_vif_active_link(vif, link_conf, link_id) { + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + if (!link->phy) + continue; + + mt7996_mcu_add_bss_info(link->phy, vif, link_conf, + &link->mt76, &link->msta_link, + true); + mt7996_mcu_add_sta(dev, link_conf, NULL, link, NULL, + CONN_STATE_PORT_SECURE, + !!(changed & BSS_CHANGED_BSSID)); + } + } + + mutex_unlock(&dev->mt76.mutex); +} + +static void +mt7996_link_info_changed(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *info, u64 changed) { struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt76_vif_link *mvif; + struct mt7996_vif_link *link; struct mt7996_phy *phy; struct mt76_phy *mphy; mutex_lock(&dev->mt76.mutex); - mvif = mt76_vif_conf_link(&dev->mt76, vif, info); - if (!mvif) + link = mt7996_vif_conf_link(dev, vif, info); + if (!link) goto out; - mphy = mt76_vif_link_phy(mvif); + mphy = mt76_vif_link_phy(&link->mt76); if (!mphy) goto out; @@ -675,16 +800,14 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, * and then peer references bss_info_rfch to set bandwidth cap. */ if ((changed & BSS_CHANGED_BSSID && !is_zero_ether_addr(info->bssid)) || - (changed & BSS_CHANGED_ASSOC && vif->cfg.assoc) || (changed & BSS_CHANGED_BEACON_ENABLED && info->enable_beacon)) { - mt7996_mcu_add_bss_info(phy, vif, info, mvif, true); - mt7996_mcu_add_sta(dev, vif, mvif, NULL, CONN_STATE_PORT_SECURE, + mt7996_mcu_add_bss_info(phy, vif, info, &link->mt76, + &link->msta_link, true); + mt7996_mcu_add_sta(dev, info, NULL, link, NULL, + CONN_STATE_PORT_SECURE, !!(changed & BSS_CHANGED_BSSID)); } - if (changed & BSS_CHANGED_ERP_CTS_PROT) - mt7996_mac_enable_rtscts(dev, vif, info->use_cts_prot); - if (changed & BSS_CHANGED_ERP_SLOT) { int slottime = info->use_short_slot ? 9 : 20; @@ -695,11 +818,11 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, } if (changed & BSS_CHANGED_MCAST_RATE) - mvif->mcast_rates_idx = + link->mt76.mcast_rates_idx = mt7996_get_rates_table(phy, info, false, true); if (changed & BSS_CHANGED_BASIC_RATES) - mvif->basic_rates_idx = + link->mt76.basic_rates_idx = mt7996_get_rates_table(phy, info, false, false); /* ensure that enable txcmd_mode after bss_info */ @@ -707,19 +830,19 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, mt7996_mcu_set_tx(dev, vif, info); if (changed & BSS_CHANGED_HE_OBSS_PD) - mt7996_mcu_add_obss_spr(phy, vif, &info->he_obss_pd); + mt7996_mcu_add_obss_spr(phy, link, &info->he_obss_pd); if (changed & BSS_CHANGED_HE_BSS_COLOR) { if ((vif->type == NL80211_IFTYPE_AP && - mvif->omac_idx <= HW_BSSID_MAX) || + link->mt76.omac_idx <= HW_BSSID_MAX) || vif->type == NL80211_IFTYPE_STATION) - mt7996_mcu_update_bss_color(dev, mvif, + mt7996_mcu_update_bss_color(dev, &link->mt76, &info->he_bss_color); } if (changed & (BSS_CHANGED_BEACON | BSS_CHANGED_BEACON_ENABLED)) { - mvif->beacon_rates_idx = + link->mt76.beacon_rates_idx = mt7996_get_rates_table(phy, info, true, false); mt7996_mcu_add_beacon(hw, vif, info); @@ -727,10 +850,10 @@ static void mt7996_bss_info_changed(struct ieee80211_hw *hw, if (changed & (BSS_CHANGED_UNSOL_BCAST_PROBE_RESP | BSS_CHANGED_FILS_DISCOVERY)) - mt7996_mcu_beacon_inband_discov(dev, vif, changed); + mt7996_mcu_beacon_inband_discov(dev, info, link, changed); if (changed & BSS_CHANGED_MU_GROUPS) - mt7996_update_mu_group(hw, vif, info); + mt7996_update_mu_group(hw, link, info); if (changed & BSS_CHANGED_TXPOWER && info->txpower != phy->txpower) { @@ -754,96 +877,328 @@ mt7996_channel_switch_beacon(struct ieee80211_hw *hw, mutex_unlock(&dev->mt76.mutex); } -int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +static int +mt7996_mac_sta_init_link(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, unsigned int link_id) { - struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); + struct ieee80211_sta *sta = link_sta->sta; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_vif_link *link = &mvif->deflink; - u8 band_idx = link->phy->mt76->band_idx; + struct mt7996_phy *phy = link->phy; + struct mt7996_sta_link *msta_link; int idx; idx = mt76_wcid_alloc(dev->mt76.wcid_mask, MT7996_WTBL_STA); if (idx < 0) return -ENOSPC; - INIT_LIST_HEAD(&msta->rc_list); - INIT_LIST_HEAD(&msta->wcid.poll_list); - msta->vif = mvif; - msta->wcid.sta = 1; - msta->wcid.idx = idx; - msta->wcid.phy_idx = band_idx; + if (msta->deflink_id == IEEE80211_LINK_UNSPECIFIED) { + int i; - ewma_avg_signal_init(&msta->avg_ack_signal); + msta_link = &msta->deflink; + msta->deflink_id = link_id; - mt7996_mac_wtbl_update(dev, idx, + for (i = 0; i < ARRAY_SIZE(sta->txq); i++) { + struct mt76_txq *mtxq; + + if (!sta->txq[i]) + continue; + + mtxq = (struct mt76_txq *)sta->txq[i]->drv_priv; + mtxq->wcid = idx; + } + } else { + msta_link = kzalloc(sizeof(*msta_link), GFP_KERNEL); + if (!msta_link) + return -ENOMEM; + } + + INIT_LIST_HEAD(&msta_link->rc_list); + INIT_LIST_HEAD(&msta_link->wcid.poll_list); + msta_link->sta = msta; + msta_link->wcid.sta = 1; + msta_link->wcid.idx = idx; + msta_link->wcid.link_id = link_id; + + ewma_avg_signal_init(&msta_link->avg_ack_signal); + ewma_signal_init(&msta_link->wcid.rssi); + + rcu_assign_pointer(msta->link[link_id], msta_link); + + mt7996_mac_wtbl_update(dev, idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + mt7996_mcu_add_sta(dev, link_conf, link_sta, link, msta_link, + CONN_STATE_DISCONNECT, true); + + rcu_assign_pointer(dev->mt76.wcid[idx], &msta_link->wcid); + mt76_wcid_init(&msta_link->wcid, phy->mt76->band_idx); + + return 0; +} + +static void +mt7996_mac_sta_deinit_link(struct mt7996_dev *dev, + struct mt7996_sta_link *msta_link) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(msta_link->wcid.aggr); i++) + mt76_rx_aggr_stop(&dev->mt76, &msta_link->wcid, i); + + mt7996_mac_wtbl_update(dev, msta_link->wcid.idx, MT_WTBL_UPDATE_ADM_COUNT_CLEAR); - mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, CONN_STATE_DISCONNECT, - true); + + spin_lock_bh(&dev->mt76.sta_poll_lock); + if (!list_empty(&msta_link->wcid.poll_list)) + list_del_init(&msta_link->wcid.poll_list); + if (!list_empty(&msta_link->rc_list)) + list_del_init(&msta_link->rc_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); + + mt76_wcid_cleanup(&dev->mt76, &msta_link->wcid); + mt76_wcid_mask_clear(dev->mt76.wcid_mask, msta_link->wcid.idx); +} + +static void +mt7996_mac_sta_remove_links(struct mt7996_dev *dev, struct ieee80211_sta *sta, + unsigned long links) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt76_dev *mdev = &dev->mt76; + unsigned int link_id; + + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_sta_link *msta_link = NULL; + + msta_link = rcu_replace_pointer(msta->link[link_id], msta_link, + lockdep_is_held(&mdev->mutex)); + if (!msta_link) + continue; + + mt7996_mac_sta_deinit_link(dev, msta_link); + if (msta->deflink_id == link_id) { + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + continue; + } + + kfree_rcu(msta_link, rcu_head); + } +} + +static int +mt7996_mac_sta_add_links(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, unsigned long new_links) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned int link_id; + int err = 0; + + for_each_set_bit(link_id, &new_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf; + struct ieee80211_link_sta *link_sta; + struct mt7996_vif_link *link; + + if (rcu_access_pointer(msta->link[link_id])) + continue; + + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) { + err = -EINVAL; + goto error_unlink; + } + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) { + err = -EINVAL; + goto error_unlink; + } + + link_sta = link_sta_dereference_protected(sta, link_id); + if (!link_sta) { + err = -EINVAL; + goto error_unlink; + } + + err = mt7996_mac_sta_init_link(dev, link_conf, link_sta, link, + link_id); + if (err) + goto error_unlink; + } return 0; + +error_unlink: + mt7996_mac_sta_remove_links(dev, sta, new_links); + + return err; } -int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, enum mt76_sta_event ev) +static int +mt7996_mac_sta_change_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, u16 old_links, + u16 new_links) { + struct mt7996_dev *dev = mt7996_hw_dev(hw); + unsigned long add = new_links & ~old_links; + unsigned long rem = old_links & ~new_links; + int ret; + + mutex_lock(&dev->mt76.mutex); + + mt7996_mac_sta_remove_links(dev, sta, rem); + ret = mt7996_mac_sta_add_links(dev, vif, sta, add); + + mutex_unlock(&dev->mt76.mutex); + + return ret; +} + +static int +mt7996_mac_sta_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) +{ + struct mt76_dev *mdev = mphy->dev; struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_vif_link *link = &mvif->deflink; - int i, ret; + unsigned long links = sta->mlo ? sta->valid_links : BIT(0); + int err; - switch (ev) { - case MT76_STA_EVENT_ASSOC: - ret = mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, - CONN_STATE_CONNECT, true); - if (ret) - return ret; + mutex_lock(&mdev->mutex); - ret = mt7996_mcu_add_rate_ctrl(dev, vif, sta, false); - if (ret) - return ret; + msta->deflink_id = IEEE80211_LINK_UNSPECIFIED; + msta->vif = mvif; + err = mt7996_mac_sta_add_links(dev, vif, sta, links); + if (!err) + mphy->num_sta++; - msta->wcid.tx_info |= MT_WCID_TX_INFO_SET; - msta->wcid.sta = 1; + mutex_unlock(&mdev->mutex); - return 0; + return err; +} - case MT76_STA_EVENT_AUTHORIZE: - return mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, - CONN_STATE_PORT_SECURE, false); +static int +mt7996_mac_sta_event(struct mt7996_dev *dev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum mt76_sta_event ev) +{ + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned long links = sta->valid_links; + struct ieee80211_link_sta *link_sta; + unsigned int link_id; + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct ieee80211_bss_conf *link_conf; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + int i, err; + + link_conf = link_conf_dereference_protected(vif, link_id); + if (!link_conf) + continue; - case MT76_STA_EVENT_DISASSOC: - for (i = 0; i < ARRAY_SIZE(msta->twt.flow); i++) - mt7996_mac_twt_teardown_flow(dev, msta, i); + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; - mt7996_mcu_add_sta(dev, vif, &link->mt76, sta, - CONN_STATE_DISCONNECT, false); - msta->wcid.sta_disabled = 1; - msta->wcid.sta = 0; + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; - return 0; + switch (ev) { + case MT76_STA_EVENT_ASSOC: + err = mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, + CONN_STATE_CONNECT, true); + if (err) + return err; + + err = mt7996_mcu_add_rate_ctrl(dev, vif, link_conf, + link_sta, link, + msta_link, false); + if (err) + return err; + + msta_link->wcid.tx_info |= MT_WCID_TX_INFO_SET; + msta_link->wcid.sta = 1; + break; + case MT76_STA_EVENT_AUTHORIZE: + err = mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, + CONN_STATE_PORT_SECURE, false); + if (err) + return err; + break; + case MT76_STA_EVENT_DISASSOC: + for (i = 0; i < ARRAY_SIZE(msta_link->twt.flow); i++) + mt7996_mac_twt_teardown_flow(dev, link, + msta_link, i); + + if (sta->mlo && links == BIT(link_id)) /* last link */ + mt7996_mcu_teardown_mld_sta(dev, link, + msta_link); + else + mt7996_mcu_add_sta(dev, link_conf, link_sta, + link, msta_link, + CONN_STATE_DISCONNECT, false); + msta_link->wcid.sta_disabled = 1; + msta_link->wcid.sta = 0; + links = links & ~BIT(link_id); + break; + } } return 0; } -void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +static void +mt7996_mac_sta_remove(struct mt76_phy *mphy, struct ieee80211_vif *vif, + struct ieee80211_sta *sta) { + struct mt76_dev *mdev = mphy->dev; struct mt7996_dev *dev = container_of(mdev, struct mt7996_dev, mt76); - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned long links = sta->mlo ? sta->valid_links : BIT(0); - mt7996_mac_wtbl_update(dev, msta->wcid.idx, - MT_WTBL_UPDATE_ADM_COUNT_CLEAR); + mutex_lock(&mdev->mutex); - spin_lock_bh(&mdev->sta_poll_lock); - if (!list_empty(&msta->wcid.poll_list)) - list_del_init(&msta->wcid.poll_list); - if (!list_empty(&msta->rc_list)) - list_del_init(&msta->rc_list); - spin_unlock_bh(&mdev->sta_poll_lock); + mt7996_mac_sta_remove_links(dev, sta, links); + mphy->num_sta--; + + mutex_unlock(&mdev->mutex); +} + +static int +mt7996_sta_state(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, enum ieee80211_sta_state old_state, + enum ieee80211_sta_state new_state) +{ + struct mt76_phy *mphy = mt76_vif_phy(hw, vif); + struct mt7996_dev *dev = mt7996_hw_dev(hw); + enum mt76_sta_event ev; + + if (!mphy) + return -EINVAL; + + if (old_state == IEEE80211_STA_NOTEXIST && + new_state == IEEE80211_STA_NONE) + return mt7996_mac_sta_add(mphy, vif, sta); + + if (old_state == IEEE80211_STA_NONE && + new_state == IEEE80211_STA_NOTEXIST) + mt7996_mac_sta_remove(mphy, vif, sta); + + if (old_state == IEEE80211_STA_AUTH && + new_state == IEEE80211_STA_ASSOC) + ev = MT76_STA_EVENT_ASSOC; + else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTHORIZED) + ev = MT76_STA_EVENT_AUTHORIZE; + else if (old_state == IEEE80211_STA_ASSOC && + new_state == IEEE80211_STA_AUTH) + ev = MT76_STA_EVENT_DISASSOC; + else + return 0; + + return mt7996_mac_sta_event(dev, vif, sta, ev); } static void mt7996_tx(struct ieee80211_hw *hw, @@ -855,12 +1210,18 @@ static void mt7996_tx(struct ieee80211_hw *hw, struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb); struct ieee80211_vif *vif = info->control.vif; struct mt76_wcid *wcid = &dev->mt76.global_wcid; + u8 link_id = u32_get_bits(info->control.flags, + IEEE80211_TX_CTRL_MLO_LINK); + + rcu_read_lock(); if (vif) { - struct mt7996_vif *mvif; + struct mt7996_vif *mvif = (void *)vif->drv_priv; + struct mt76_vif_link *mlink; - mvif = (struct mt7996_vif *)vif->drv_priv; - wcid = &mvif->deflink.sta.wcid; + mlink = rcu_dereference(mvif->mt76.link[link_id]); + if (mlink && mlink->wcid) + wcid = mlink->wcid; if (mvif->mt76.roc_phy && (info->flags & IEEE80211_TX_CTL_TX_OFFCHAN)) { @@ -872,25 +1233,28 @@ static void mt7996_tx(struct ieee80211_hw *hw, } } - if (control->sta) { - struct mt7996_sta *sta; - - sta = (struct mt7996_sta *)control->sta->drv_priv; - wcid = &sta->wcid; - } - if (!mphy) { ieee80211_free_txskb(hw, skb); - return; + goto unlock; } + if (control->sta) { + struct mt7996_sta *msta = (void *)control->sta->drv_priv; + struct mt7996_sta_link *msta_link; + + msta_link = rcu_dereference(msta->link[link_id]); + if (msta_link) + wcid = &msta_link->wcid; + } mt76_tx(mphy, control->sta, wcid, skb); +unlock: + rcu_read_unlock(); } static int mt7996_set_rts_threshold(struct ieee80211_hw *hw, u32 val) { struct mt7996_dev *dev = mt7996_hw_dev(hw); - int i, ret; + int i, ret = 0; mutex_lock(&dev->mt76.mutex); @@ -914,11 +1278,13 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, enum ieee80211_ampdu_mlme_action action = params->action; struct mt7996_dev *dev = mt7996_hw_dev(hw); struct ieee80211_sta *sta = params->sta; - struct ieee80211_txq *txq = sta->txq[params->tid]; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct ieee80211_txq *txq = sta->txq[params->tid]; + struct ieee80211_link_sta *link_sta; u16 tid = params->tid; u16 ssn = params->ssn; struct mt76_txq *mtxq; + unsigned int link_id; int ret = 0; if (!txq) @@ -927,38 +1293,61 @@ mt7996_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_vif *vif, mtxq = (struct mt76_txq *)txq->drv_priv; mutex_lock(&dev->mt76.mutex); - switch (action) { - case IEEE80211_AMPDU_RX_START: - mt76_rx_aggr_start(&dev->mt76, &msta->wcid, tid, ssn, - params->buf_size); - ret = mt7996_mcu_add_rx_ba(dev, params, true); - break; - case IEEE80211_AMPDU_RX_STOP: - mt76_rx_aggr_stop(&dev->mt76, &msta->wcid, tid); - ret = mt7996_mcu_add_rx_ba(dev, params, false); - break; - case IEEE80211_AMPDU_TX_OPERATIONAL: - mtxq->aggr = true; - mtxq->send_bar = false; - ret = mt7996_mcu_add_tx_ba(dev, params, true); - break; - case IEEE80211_AMPDU_TX_STOP_FLUSH: - case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - mtxq->aggr = false; - clear_bit(tid, &msta->wcid.ampdu_state); - ret = mt7996_mcu_add_tx_ba(dev, params, false); - break; - case IEEE80211_AMPDU_TX_START: - set_bit(tid, &msta->wcid.ampdu_state); - ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; - break; - case IEEE80211_AMPDU_TX_STOP_CONT: - mtxq->aggr = false; - clear_bit(tid, &msta->wcid.ampdu_state); - ret = mt7996_mcu_add_tx_ba(dev, params, false); - ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); - break; + + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + switch (action) { + case IEEE80211_AMPDU_RX_START: + mt76_rx_aggr_start(&dev->mt76, &msta_link->wcid, tid, + ssn, params->buf_size); + ret = mt7996_mcu_add_rx_ba(dev, params, link, true); + break; + case IEEE80211_AMPDU_RX_STOP: + mt76_rx_aggr_stop(&dev->mt76, &msta_link->wcid, tid); + ret = mt7996_mcu_add_rx_ba(dev, params, link, false); + break; + case IEEE80211_AMPDU_TX_OPERATIONAL: + mtxq->aggr = true; + mtxq->send_bar = false; + ret = mt7996_mcu_add_tx_ba(dev, params, link, + msta_link, true); + break; + case IEEE80211_AMPDU_TX_STOP_FLUSH: + case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta_link->wcid.ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, link, + msta_link, false); + break; + case IEEE80211_AMPDU_TX_START: + set_bit(tid, &msta_link->wcid.ampdu_state); + ret = IEEE80211_AMPDU_TX_START_IMMEDIATE; + break; + case IEEE80211_AMPDU_TX_STOP_CONT: + mtxq->aggr = false; + clear_bit(tid, &msta_link->wcid.ampdu_state); + ret = mt7996_mcu_add_tx_ba(dev, params, link, + msta_link, false); + break; + } + + if (ret) + break; } + + if (action == IEEE80211_AMPDU_TX_STOP_CONT) + ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); + mutex_unlock(&dev->mt76.mutex); return ret; @@ -989,10 +1378,10 @@ mt7996_get_stats(struct ieee80211_hw *hw, return 0; } -u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif) +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link) { struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_phy *phy = link->phy; union { u64 t64; u32 t32[2]; @@ -1004,8 +1393,8 @@ u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif) lockdep_assert_held(&dev->mt76.mutex); - n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 - : mvif->deflink.mt76.omac_idx; + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; /* TSF software read */ mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, MT_LPON_TCR_SW_READ); @@ -1023,7 +1412,7 @@ mt7996_get_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif) u64 ret; mutex_lock(&dev->mt76.mutex); - ret = __mt7996_get_tsf(hw, mvif); + ret = __mt7996_get_tsf(hw, &mvif->deflink); mutex_unlock(&dev->mt76.mutex); return ret; @@ -1035,26 +1424,33 @@ mt7996_set_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_vif_link *link; + struct mt7996_phy *phy; union { u64 t64; u32 t32[2]; } tsf = { .t64 = timestamp, }; u16 n; - if (!phy) - return; - mutex_lock(&dev->mt76.mutex); - n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 - : mvif->deflink.mt76.omac_idx; + link = mt7996_vif_link(dev, vif, mvif->mt76.deflink_id); + if (!link) + goto unlock; + + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; + phy = link->phy; + if (!phy) + goto unlock; + mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); /* TSF software overwrite */ mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, MT_LPON_TCR_SW_WRITE); +unlock: mutex_unlock(&dev->mt76.mutex); } @@ -1064,26 +1460,33 @@ mt7996_offset_tsf(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_dev *dev = mt7996_hw_dev(hw); - struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_vif_link *link; + struct mt7996_phy *phy; union { u64 t64; u32 t32[2]; } tsf = { .t64 = timestamp, }; u16 n; - if (!phy) - return; - mutex_lock(&dev->mt76.mutex); - n = mvif->deflink.mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 - : mvif->deflink.mt76.omac_idx; + link = mt7996_vif_link(dev, vif, mvif->mt76.deflink_id); + if (!link) + goto unlock; + + phy = link->phy; + if (!phy) + goto unlock; + + n = link->mt76.omac_idx > HW_BSSID_MAX ? HW_BSSID_0 + : link->mt76.omac_idx; mt76_wr(dev, MT_LPON_UTTR0(phy->mt76->band_idx), tsf.t32[0]); mt76_wr(dev, MT_LPON_UTTR1(phy->mt76->band_idx), tsf.t32[1]); /* TSF software adjust*/ mt76_rmw(dev, MT_LPON_TCR(phy->mt76->band_idx, n), MT_LPON_TCR_SW_MODE, MT_LPON_TCR_SW_ADJUST); +unlock: mutex_unlock(&dev->mt76.mutex); } @@ -1125,7 +1528,8 @@ mt7996_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) u8 shift = dev->chainshift[band_idx]; phy->mt76->chainmask = tx_ant & phy->orig_chainmask; - phy->mt76->antenna_mask = phy->mt76->chainmask >> shift; + phy->mt76->antenna_mask = (phy->mt76->chainmask >> shift) & + phy->orig_antenna_mask; mt76_set_stream_caps(phy->mt76, true); mt7996_set_stream_vht_txbf_caps(phy); @@ -1145,7 +1549,8 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct rate_info *txrate = &msta->wcid.rate; + struct mt7996_sta_link *msta_link = &msta->deflink; + struct rate_info *txrate = &msta_link->wcid.rate; if (txrate->legacy || txrate->flags) { if (txrate->legacy) { @@ -1165,55 +1570,67 @@ static void mt7996_sta_statistics(struct ieee80211_hw *hw, sinfo->txrate.flags = txrate->flags; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); - sinfo->tx_failed = msta->wcid.stats.tx_failed; + sinfo->tx_failed = msta_link->wcid.stats.tx_failed; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); - sinfo->tx_retries = msta->wcid.stats.tx_retries; + sinfo->tx_retries = msta_link->wcid.stats.tx_retries; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_RETRIES); - sinfo->ack_signal = (s8)msta->ack_signal; + sinfo->ack_signal = (s8)msta_link->ack_signal; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL); - sinfo->avg_ack_signal = -(s8)ewma_avg_signal_read(&msta->avg_ack_signal); + sinfo->avg_ack_signal = + -(s8)ewma_avg_signal_read(&msta_link->avg_ack_signal); sinfo->filled |= BIT_ULL(NL80211_STA_INFO_ACK_SIGNAL_AVG); if (mtk_wed_device_active(&dev->mt76.mmio.wed)) { - sinfo->tx_bytes = msta->wcid.stats.tx_bytes; + sinfo->tx_bytes = msta_link->wcid.stats.tx_bytes; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES64); - sinfo->rx_bytes = msta->wcid.stats.rx_bytes; + sinfo->rx_bytes = msta_link->wcid.stats.rx_bytes; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES64); - sinfo->tx_packets = msta->wcid.stats.tx_packets; + sinfo->tx_packets = msta_link->wcid.stats.tx_packets; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); - sinfo->rx_packets = msta->wcid.stats.rx_packets; + sinfo->rx_packets = msta_link->wcid.stats.rx_packets; sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); } } -static void mt7996_sta_rc_work(void *data, struct ieee80211_sta *sta) +static void mt7996_link_rate_ctrl_update(void *data, struct ieee80211_sta *sta) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct mt7996_dev *dev = msta->vif->deflink.phy->dev; + struct mt7996_sta_link *msta_link; u32 *changed = data; + rcu_read_lock(); + + msta_link = rcu_dereference(msta->link[msta->deflink_id]); + if (!msta_link) + goto out; + spin_lock_bh(&dev->mt76.sta_poll_lock); - msta->changed |= *changed; - if (list_empty(&msta->rc_list)) - list_add_tail(&msta->rc_list, &dev->sta_rc_list); + + msta_link->changed |= *changed; + if (list_empty(&msta_link->rc_list)) + list_add_tail(&msta_link->rc_list, &dev->sta_rc_list); + spin_unlock_bh(&dev->mt76.sta_poll_lock); +out: + rcu_read_unlock(); } -static void mt7996_sta_rc_update(struct ieee80211_hw *hw, - struct ieee80211_vif *vif, - struct ieee80211_link_sta *link_sta, - u32 changed) +static void mt7996_link_sta_rc_update(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + u32 changed) { struct mt7996_dev *dev = mt7996_hw_dev(hw); struct ieee80211_sta *sta = link_sta->sta; - mt7996_sta_rc_work(&changed, sta); + mt7996_link_rate_ctrl_update(&changed, sta); ieee80211_queue_work(hw, &dev->rc_work); } @@ -1235,7 +1652,8 @@ mt7996_set_bitrate_mask(struct ieee80211_hw *hw, struct ieee80211_vif *vif, * - multiple rates: if it's not in range format i.e 0-{7,8,9} for VHT * then multiple MCS setting (MCS 4,5,6) is not supported. */ - ieee80211_iterate_stations_atomic(hw, mt7996_sta_rc_work, &changed); + ieee80211_iterate_stations_atomic(hw, mt7996_link_rate_ctrl_update, + &changed); ieee80211_queue_work(hw, &dev->rc_work); return 0; @@ -1246,18 +1664,37 @@ static void mt7996_sta_set_4addr(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool enabled) { - struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - if (enabled) - set_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); - else - clear_bit(MT_WCID_FLAG_4ADDR, &msta->wcid.flags); + mutex_lock(&dev->mt76.mutex); - if (!msta->wcid.sta) - return; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; + + if (enabled) + set_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); + else + clear_bit(MT_WCID_FLAG_4ADDR, &msta_link->wcid.flags); + + if (!msta_link->wcid.sta) + continue; + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link); + } - mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); + mutex_unlock(&dev->mt76.mutex); } static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, @@ -1265,18 +1702,39 @@ static void mt7996_sta_set_decap_offload(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool enabled) { - struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_dev *dev = mt7996_hw_dev(hw); + struct ieee80211_link_sta *link_sta; + unsigned int link_id; - if (enabled) - set_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); - else - clear_bit(MT_WCID_FLAG_HDR_TRANS, &msta->wcid.flags); + mutex_lock(&dev->mt76.mutex); - if (!msta->wcid.sta) - return; + for_each_sta_active_link(vif, sta, link_sta, link_id) { + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; + + if (enabled) + set_bit(MT_WCID_FLAG_HDR_TRANS, + &msta_link->wcid.flags); + else + clear_bit(MT_WCID_FLAG_HDR_TRANS, + &msta_link->wcid.flags); - mt7996_mcu_wtbl_update_hdr_trans(dev, vif, sta); + if (!msta_link->wcid.sta) + continue; + + mt7996_mcu_wtbl_update_hdr_trans(dev, vif, link, msta_link); + } + + mutex_unlock(&dev->mt76.mutex); } static const char mt7996_gstrings_stats[][ETH_GSTRING_LEN] = { @@ -1408,11 +1866,12 @@ static void mt7996_ethtool_worker(void *wi_data, struct ieee80211_sta *sta) { struct mt76_ethtool_worker_info *wi = wi_data; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; if (msta->vif->deflink.mt76.idx != wi->idx) return; - mt76_ethtool_worker(wi, &msta->wcid.stats, true); + mt76_ethtool_worker(wi, &msta_link->wcid.stats, true); } static @@ -1516,10 +1975,12 @@ mt7996_twt_teardown_request(struct ieee80211_hw *hw, u8 flowid) { struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta_link *msta_link = &msta->deflink; + struct mt7996_vif_link *link = &msta->vif->deflink; struct mt7996_dev *dev = mt7996_hw_dev(hw); mutex_lock(&dev->mt76.mutex); - mt7996_mac_twt_teardown_flow(dev, msta, flowid); + mt7996_mac_twt_teardown_flow(dev, link, msta_link, flowid); mutex_unlock(&dev->mt76.mutex); } @@ -1589,12 +2050,26 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, { struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - struct mt7996_vif_link *mlink = &mvif->deflink; struct mt7996_dev *dev = mt7996_hw_dev(hw); struct mtk_wed_device *wed = &dev->mt76.mmio.wed; + struct mt7996_sta_link *msta_link; + struct mt7996_vif_link *link; + struct mt76_vif_link *mlink; struct mt7996_phy *phy; - phy = mt7996_vif_link_phy(mlink); + mlink = rcu_dereference(mvif->mt76.link[msta->deflink_id]); + if (!mlink) + return -EIO; + + msta_link = rcu_dereference(msta->link[msta->deflink_id]); + if (!msta_link) + return -EIO; + + if (!msta_link->wcid.sta || msta_link->wcid.idx > MT7996_WTBL_STA) + return -EIO; + + link = (struct mt7996_vif_link *)mlink; + phy = mt7996_vif_link_phy(link); if (!phy) return -ENODEV; @@ -1604,15 +2079,12 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, if (!mtk_wed_device_active(wed)) return -ENODEV; - if (!msta->wcid.sta || msta->wcid.idx > MT7996_WTBL_STA) - return -EIO; - path->type = DEV_PATH_MTK_WDMA; path->dev = ctx->dev; path->mtk_wdma.wdma_idx = wed->wdma_idx; - path->mtk_wdma.bss = mvif->deflink.mt76.idx; + path->mtk_wdma.bss = mlink->idx; path->mtk_wdma.queue = 0; - path->mtk_wdma.wcid = msta->wcid.idx; + path->mtk_wdma.wcid = msta_link->wcid.idx; path->mtk_wdma.amsdu = mtk_wed_is_amsdu_supported(wed); ctx->dev = NULL; @@ -1622,6 +2094,14 @@ mt7996_net_fill_forward_path(struct ieee80211_hw *hw, #endif +static int +mt7996_change_vif_links(struct ieee80211_hw *hw, struct ieee80211_vif *vif, + u16 old_links, u16 new_links, + struct ieee80211_bss_conf *old[IEEE80211_MLD_MAX_NUM_LINKS]) +{ + return 0; +} + const struct ieee80211_ops mt7996_ops = { .add_chanctx = mt76_add_chanctx, .remove_chanctx = mt76_remove_chanctx, @@ -1637,10 +2117,11 @@ const struct ieee80211_ops mt7996_ops = { .config = mt7996_config, .conf_tx = mt7996_conf_tx, .configure_filter = mt7996_configure_filter, - .bss_info_changed = mt7996_bss_info_changed, - .sta_state = mt76_sta_state, + .vif_cfg_changed = mt7996_vif_cfg_changed, + .link_info_changed = mt7996_link_info_changed, + .sta_state = mt7996_sta_state, .sta_pre_rcu_remove = mt76_sta_pre_rcu_remove, - .link_sta_rc_update = mt7996_sta_rc_update, + .link_sta_rc_update = mt7996_link_sta_rc_update, .set_key = mt7996_set_key, .ampdu_action = mt7996_ampdu_action, .set_rts_threshold = mt7996_set_rts_threshold, @@ -1650,7 +2131,7 @@ const struct ieee80211_ops mt7996_ops = { .remain_on_channel = mt76_remain_on_channel, .cancel_remain_on_channel = mt76_cancel_remain_on_channel, .release_buffered_frames = mt76_release_buffered_frames, - .get_txpower = mt76_get_txpower, + .get_txpower = mt7996_get_txpower, .channel_switch_beacon = mt7996_channel_switch_beacon, .get_stats = mt7996_get_stats, .get_et_sset_count = mt7996_get_et_sset_count, @@ -1677,4 +2158,6 @@ const struct ieee80211_ops mt7996_ops = { .net_fill_forward_path = mt7996_net_fill_forward_path, .net_setup_tc = mt76_wed_net_setup_tc, #endif + .change_vif_links = mt7996_change_vif_links, + .change_sta_links = mt7996_mac_sta_change_links, }; diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c index e4569d032221..f0adc0b4b8b6 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.c @@ -13,7 +13,7 @@ #define fw_name(_dev, name, ...) ({ \ char *_fw; \ switch (mt76_chip(&(_dev)->mt76)) { \ - case 0x7992: \ + case MT7992_DEVICE_ID: \ switch ((_dev)->var.type) { \ case MT7992_VAR_TYPE_23: \ _fw = MT7992_##name##_23; \ @@ -22,7 +22,10 @@ _fw = MT7992_##name; \ } \ break; \ - case 0x7990: \ + case MT7990_DEVICE_ID: \ + _fw = MT7990_##name; \ + break; \ + case MT7996_DEVICE_ID: \ default: \ switch ((_dev)->var.type) { \ case MT7996_VAR_TYPE_233: \ @@ -118,13 +121,13 @@ mt7996_mcu_get_sta_nss(u16 mcs_map) } static void -mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, - u16 mcs_map) +mt7996_mcu_set_sta_he_mcs(struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + __le16 *he_mcs, u16 mcs_map) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; - enum nl80211_band band = msta->vif->deflink.phy->mt76->chandef.chan->band; - const u16 *mask = msta->vif->deflink.bitrate_mask.control[band].he_mcs; - int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; + enum nl80211_band band = link->phy->mt76->chandef.chan->band; + const u16 *mask = link->bitrate_mask.control[band].he_mcs; for (nss = 0; nss < max_nss; nss++) { int mcs; @@ -167,11 +170,11 @@ mt7996_mcu_set_sta_he_mcs(struct ieee80211_sta *sta, __le16 *he_mcs, } static void -mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, - const u16 *mask) +mt7996_mcu_set_sta_vht_mcs(struct ieee80211_link_sta *link_sta, + __le16 *vht_mcs, const u16 *mask) { - u16 mcs, mcs_map = le16_to_cpu(sta->deflink.vht_cap.vht_mcs.rx_mcs_map); - int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + u16 mcs, mcs_map = le16_to_cpu(link_sta->vht_cap.vht_mcs.rx_mcs_map); + int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; for (nss = 0; nss < max_nss; nss++, mcs_map >>= 2) { switch (mcs_map & 0x3) { @@ -193,13 +196,13 @@ mt7996_mcu_set_sta_vht_mcs(struct ieee80211_sta *sta, __le16 *vht_mcs, } static void -mt7996_mcu_set_sta_ht_mcs(struct ieee80211_sta *sta, u8 *ht_mcs, - const u8 *mask) +mt7996_mcu_set_sta_ht_mcs(struct ieee80211_link_sta *link_sta, + u8 *ht_mcs, const u8 *mask) { - int nss, max_nss = sta->deflink.rx_nss > 3 ? 4 : sta->deflink.rx_nss; + int nss, max_nss = link_sta->rx_nss > 3 ? 4 : link_sta->rx_nss; for (nss = 0; nss < max_nss; nss++) - ht_mcs[nss] = sta->deflink.ht_cap.mcs.rx_mask[nss] & mask[nss]; + ht_mcs[nss] = link_sta->ht_cap.mcs.rx_mask[nss] & mask[nss]; } static int @@ -265,7 +268,7 @@ mt7996_mcu_send_message(struct mt76_dev *mdev, struct sk_buff *skb, txd_len = cmd & __MCU_CMD_FIELD_UNI ? sizeof(*uni_txd) : sizeof(*mcu_txd); txd = (__le32 *)skb_push(skb, txd_len); - if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state)) + if (test_bit(MT76_STATE_MCU_RUNNING, &dev->mphy.state) && mt7996_has_wa(dev)) qid = MT_MCUQ_WA; else qid = MT_MCUQ_WM; @@ -335,8 +338,12 @@ exit: int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) { struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; __le32 args[3]; - } req = { + } __packed req = { .args = { cpu_to_le32(a1), cpu_to_le32(a2), @@ -344,7 +351,16 @@ int mt7996_mcu_wa_cmd(struct mt7996_dev *dev, int cmd, u32 a1, u32 a2, u32 a3) }, }; - return mt76_mcu_send_msg(&dev->mt76, cmd, &req, sizeof(req), false); + if (mt7996_has_wa(dev)) + return mt76_mcu_send_msg(&dev->mt76, cmd, &req.args, + sizeof(req.args), false); + + req.tag = cpu_to_le16(cmd == MCU_WA_PARAM_CMD(QUERY) ? UNI_CMD_SDO_QUERY : + UNI_CMD_SDO_SET); + req.len = cpu_to_le16(sizeof(req) - 4); + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(SDO), &req, + sizeof(req), false); } static void @@ -364,21 +380,27 @@ mt7996_mcu_rx_radar_detected(struct mt7996_dev *dev, struct sk_buff *skb) r = (struct mt7996_mcu_rdd_report *)skb->data; - if (r->band_idx >= ARRAY_SIZE(dev->mt76.phys)) - return; - - if (r->band_idx == MT_RX_SEL2 && !dev->rdd2_phy) - return; - - if (r->band_idx == MT_RX_SEL2) + switch (r->rdd_idx) { + case MT_RDD_IDX_BAND2: + mphy = dev->mt76.phys[MT_BAND2]; + break; + case MT_RDD_IDX_BAND1: + mphy = dev->mt76.phys[MT_BAND1]; + break; + case MT_RDD_IDX_BACKGROUND: + if (!dev->rdd2_phy) + return; mphy = dev->rdd2_phy->mt76; - else - mphy = dev->mt76.phys[r->band_idx]; + break; + default: + dev_err(dev->mt76.dev, "Unknown RDD idx %d\n", r->rdd_idx); + return; + } if (!mphy) return; - if (r->band_idx == MT_RX_SEL2) + if (r->rdd_idx == MT_RDD_IDX_BACKGROUND) cfg80211_background_radar_event(mphy->hw->wiphy, &dev->rdd2_chandef, GFP_ATOMIC); @@ -1064,7 +1086,8 @@ __mt7996_mcu_alloc_bss_req(struct mt76_dev *dev, struct mt76_vif_link *mvif, int int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, - struct mt76_vif_link *mlink, int enable) + struct mt76_vif_link *mlink, + struct mt7996_sta_link *msta_link, int enable) { struct mt7996_dev *dev = phy->dev; struct sk_buff *skb; @@ -1081,7 +1104,7 @@ int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, /* bss_basic must be first */ mt7996_mcu_bss_basic_tlv(skb, vif, link_conf, mlink, phy->mt76, - mlink->wcid->idx, enable); + msta_link->wcid.idx, enable); mt7996_mcu_bss_sec_tlv(skb, mlink); if (vif->type == NL80211_IFTYPE_MONITOR) @@ -1159,37 +1182,34 @@ mt7996_mcu_sta_ba(struct mt7996_dev *dev, struct mt76_vif_link *mvif, /** starec & wtbl **/ int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, - bool enable) + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, bool enable) { - struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; - struct mt7996_vif *mvif = msta->vif; - if (enable && !params->amsdu) - msta->wcid.amsdu = false; + msta_link->wcid.amsdu = false; - return mt7996_mcu_sta_ba(dev, &mvif->deflink.mt76, params, enable, true); + return mt7996_mcu_sta_ba(dev, &link->mt76, params, enable, true); } int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, - bool enable) + struct mt7996_vif_link *link, bool enable) { - struct mt7996_sta *msta = (struct mt7996_sta *)params->sta->drv_priv; - struct mt7996_vif *mvif = msta->vif; - - return mt7996_mcu_sta_ba(dev, &mvif->deflink.mt76, params, enable, false); + return mt7996_mcu_sta_ba(dev, &link->mt76, params, enable, false); } static void -mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_he_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) { - struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; + struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem; struct ieee80211_he_mcs_nss_supp mcs_map; struct sta_rec_he_v2 *he; struct tlv *tlv; int i = 0; - if (!sta->deflink.he_cap.has_he) + if (!link_sta->he_cap.has_he) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_V2, sizeof(*he)); @@ -1201,21 +1221,21 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) he->he_phy_cap[i] = elem->phy_cap_info[i]; } - mcs_map = sta->deflink.he_cap.he_mcs_nss_supp; - switch (sta->deflink.bandwidth) { + mcs_map = link_sta->he_cap.he_mcs_nss_supp; + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: if (elem->phy_cap_info[0] & IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_80PLUS80_MHZ_IN_5G) - mt7996_mcu_set_sta_he_mcs(sta, + mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW8080], le16_to_cpu(mcs_map.rx_mcs_80p80)); - mt7996_mcu_set_sta_he_mcs(sta, + mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW160], le16_to_cpu(mcs_map.rx_mcs_160)); fallthrough; default: - mt7996_mcu_set_sta_he_mcs(sta, + mt7996_mcu_set_sta_he_mcs(link_sta, link, &he->max_nss_mcs[CMD_HE_MCS_BW80], le16_to_cpu(mcs_map.rx_mcs_80)); break; @@ -1225,24 +1245,26 @@ mt7996_mcu_sta_he_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) } static void -mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_he_6g_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta) { struct sta_rec_he_6g_capa *he_6g; struct tlv *tlv; - if (!sta->deflink.he_6ghz_capa.capa) + if (!link_sta->he_6ghz_capa.capa) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HE_6G, sizeof(*he_6g)); he_6g = (struct sta_rec_he_6g_capa *)tlv; - he_6g->capa = sta->deflink.he_6ghz_capa.capa; + he_6g->capa = link_sta->he_6ghz_capa.capa; } static void -mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, + struct ieee80211_link_sta *link_sta) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + struct mt7996_sta *msta = (struct mt7996_sta *)link_sta->sta->drv_priv; struct ieee80211_vif *vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); struct ieee80211_eht_mcs_nss_supp *mcs_map; @@ -1250,11 +1272,11 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) struct sta_rec_eht *eht; struct tlv *tlv; - if (!sta->deflink.eht_cap.has_eht) + if (!link_sta->eht_cap.has_eht) return; - mcs_map = &sta->deflink.eht_cap.eht_mcs_nss_supp; - elem = &sta->deflink.eht_cap.eht_cap_elem; + mcs_map = &link_sta->eht_cap.eht_mcs_nss_supp; + elem = &link_sta->eht_cap.eht_cap_elem; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT, sizeof(*eht)); @@ -1265,7 +1287,7 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) eht->phy_cap_ext = cpu_to_le64(elem->phy_cap_info[8]); if (vif->type != NL80211_IFTYPE_STATION && - (sta->deflink.he_cap.he_cap_elem.phy_cap_info[0] & + (link_sta->he_cap.he_cap_elem.phy_cap_info[0] & (IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G | IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G | @@ -1281,47 +1303,48 @@ mt7996_mcu_sta_eht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) } static void -mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_ht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_ht_uni *ht; struct tlv *tlv; - if (!sta->deflink.ht_cap.ht_supported) + if (!link_sta->ht_cap.ht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HT, sizeof(*ht)); ht = (struct sta_rec_ht_uni *)tlv; - ht->ht_cap = cpu_to_le16(sta->deflink.ht_cap.cap); - ht->ampdu_param = u8_encode_bits(sta->deflink.ht_cap.ampdu_factor, + ht->ht_cap = cpu_to_le16(link_sta->ht_cap.cap); + ht->ampdu_param = u8_encode_bits(link_sta->ht_cap.ampdu_factor, IEEE80211_HT_AMPDU_PARM_FACTOR) | - u8_encode_bits(sta->deflink.ht_cap.ampdu_density, + u8_encode_bits(link_sta->ht_cap.ampdu_density, IEEE80211_HT_AMPDU_PARM_DENSITY); } static void -mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_sta *sta) +mt7996_mcu_sta_vht_tlv(struct sk_buff *skb, struct ieee80211_link_sta *link_sta) { struct sta_rec_vht *vht; struct tlv *tlv; /* For 6G band, this tlv is necessary to let hw work normally */ - if (!sta->deflink.he_6ghz_capa.capa && !sta->deflink.vht_cap.vht_supported) + if (!link_sta->he_6ghz_capa.capa && !link_sta->vht_cap.vht_supported) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_VHT, sizeof(*vht)); vht = (struct sta_rec_vht *)tlv; - vht->vht_cap = cpu_to_le32(sta->deflink.vht_cap.cap); - vht->vht_rx_mcs_map = sta->deflink.vht_cap.vht_mcs.rx_mcs_map; - vht->vht_tx_mcs_map = sta->deflink.vht_cap.vht_mcs.tx_mcs_map; + vht->vht_cap = cpu_to_le32(link_sta->vht_cap.cap); + vht->vht_rx_mcs_map = link_sta->vht_cap.vht_mcs.rx_mcs_map; + vht->vht_tx_mcs_map = link_sta->vht_cap.vht_mcs.tx_mcs_map; } static void mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_vif *vif, + struct ieee80211_link_sta *link_sta, + struct mt7996_sta_link *msta_link) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct sta_rec_amsdu *amsdu; struct tlv *tlv; @@ -1330,16 +1353,16 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, vif->type != NL80211_IFTYPE_AP) return; - if (!sta->deflink.agg.max_amsdu_len) + if (!link_sta->agg.max_amsdu_len) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_HW_AMSDU, sizeof(*amsdu)); amsdu = (struct sta_rec_amsdu *)tlv; amsdu->max_amsdu_num = 8; amsdu->amsdu_en = true; - msta->wcid.amsdu = true; + msta_link->wcid.amsdu = true; - switch (sta->deflink.agg.max_amsdu_len) { + switch (link_sta->agg.max_amsdu_len) { case IEEE80211_MAX_MPDU_LEN_VHT_11454: amsdu->max_mpdu_size = IEEE80211_VHT_CAP_MAX_MPDU_LENGTH_11454; @@ -1356,30 +1379,31 @@ mt7996_mcu_sta_amsdu_tlv(struct mt7996_dev *dev, struct sk_buff *skb, static void mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta) { - struct ieee80211_he_cap_elem *elem = &sta->deflink.he_cap.he_cap_elem; + struct ieee80211_he_cap_elem *elem = &link_sta->he_cap.he_cap_elem; struct sta_rec_muru *muru; struct tlv *tlv; - if (vif->type != NL80211_IFTYPE_STATION && - vif->type != NL80211_IFTYPE_AP) + if (link_conf->vif->type != NL80211_IFTYPE_STATION && + link_conf->vif->type != NL80211_IFTYPE_AP) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MURU, sizeof(*muru)); muru = (struct sta_rec_muru *)tlv; - muru->cfg.mimo_dl_en = vif->bss_conf.eht_mu_beamformer || - vif->bss_conf.he_mu_beamformer || - vif->bss_conf.vht_mu_beamformer || - vif->bss_conf.vht_mu_beamformee; + muru->cfg.mimo_dl_en = link_conf->eht_mu_beamformer || + link_conf->he_mu_beamformer || + link_conf->vht_mu_beamformer || + link_conf->vht_mu_beamformee; muru->cfg.ofdma_dl_en = true; - if (sta->deflink.vht_cap.vht_supported) + if (link_sta->vht_cap.vht_supported) muru->mimo_dl.vht_mu_bfee = - !!(sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); + !!(link_sta->vht_cap.cap & IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE); - if (!sta->deflink.he_cap.has_he) + if (!link_sta->he_cap.has_he) return; muru->mimo_dl.partial_bw_dl_mimo = @@ -1410,49 +1434,50 @@ mt7996_mcu_sta_muru_tlv(struct mt7996_dev *dev, struct sk_buff *skb, } static inline bool -mt7996_is_ebf_supported(struct mt7996_phy *phy, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool bfee) +mt7996_is_ebf_supported(struct mt7996_phy *phy, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, bool bfee) { int sts = hweight16(phy->mt76->chainmask); - if (vif->type != NL80211_IFTYPE_STATION && - vif->type != NL80211_IFTYPE_AP) + if (link_conf->vif->type != NL80211_IFTYPE_STATION && + link_conf->vif->type != NL80211_IFTYPE_AP) return false; if (!bfee && sts < 2) return false; - if (sta->deflink.eht_cap.has_eht) { - struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap; + if (link_sta->eht_cap.has_eht) { + struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; if (bfee) - return vif->bss_conf.eht_su_beamformee && + return link_conf->eht_su_beamformee && EHT_PHY(CAP0_SU_BEAMFORMER, pe->phy_cap_info[0]); else - return vif->bss_conf.eht_su_beamformer && + return link_conf->eht_su_beamformer && EHT_PHY(CAP0_SU_BEAMFORMEE, pe->phy_cap_info[0]); } - if (sta->deflink.he_cap.has_he) { - struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + if (link_sta->he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem; if (bfee) - return vif->bss_conf.he_su_beamformee && + return link_conf->he_su_beamformee && HE_PHY(CAP3_SU_BEAMFORMER, pe->phy_cap_info[3]); else - return vif->bss_conf.he_su_beamformer && + return link_conf->he_su_beamformer && HE_PHY(CAP4_SU_BEAMFORMEE, pe->phy_cap_info[4]); } - if (sta->deflink.vht_cap.vht_supported) { - u32 cap = sta->deflink.vht_cap.cap; + if (link_sta->vht_cap.vht_supported) { + u32 cap = link_sta->vht_cap.cap; if (bfee) - return vif->bss_conf.vht_su_beamformee && + return link_conf->vht_su_beamformee && (cap & IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE); else - return vif->bss_conf.vht_su_beamformer && + return link_conf->vht_su_beamformer && (cap & IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE); } @@ -1473,10 +1498,11 @@ mt7996_mcu_sta_sounding_rate(struct sta_rec_bf *bf, struct mt7996_phy *phy) } static void -mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy, - struct sta_rec_bf *bf, bool explicit) +mt7996_mcu_sta_bfer_ht(struct ieee80211_link_sta *link_sta, + struct mt7996_phy *phy, struct sta_rec_bf *bf, + bool explicit) { - struct ieee80211_mcs_info *mcs = &sta->deflink.ht_cap.mcs; + struct ieee80211_mcs_info *mcs = &link_sta->ht_cap.mcs; u8 n = 0; bf->tx_mode = MT_PHY_TYPE_HT; @@ -1499,10 +1525,11 @@ mt7996_mcu_sta_bfer_ht(struct ieee80211_sta *sta, struct mt7996_phy *phy, } static void -mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy, - struct sta_rec_bf *bf, bool explicit) +mt7996_mcu_sta_bfer_vht(struct ieee80211_link_sta *link_sta, + struct mt7996_phy *phy, struct sta_rec_bf *bf, + bool explicit) { - struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap; struct ieee80211_sta_vht_cap *vc = &phy->mt76->sband_5g.sband.vht_cap; u16 mcs_map = le16_to_cpu(pc->vht_mcs.rx_mcs_map); u8 nss_mcs = mt7996_mcu_get_sta_nss(mcs_map); @@ -1523,24 +1550,24 @@ mt7996_mcu_sta_bfer_vht(struct ieee80211_sta *sta, struct mt7996_phy *phy, bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, bf->ncol); - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) bf->nrow = 1; } else { bf->nrow = tx_ant; bf->ncol = min_t(u8, nss_mcs, bf->nrow); bf->ibf_ncol = min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); - if (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth == IEEE80211_STA_RX_BW_160) bf->ibf_nrow = 1; } } static void -mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, - struct mt7996_phy *phy, struct sta_rec_bf *bf, - bool explicit) +mt7996_mcu_sta_bfer_he(struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, struct mt7996_phy *phy, + struct sta_rec_bf *bf, bool explicit) { - struct ieee80211_sta_he_cap *pc = &sta->deflink.he_cap; + struct ieee80211_sta_he_cap *pc = &link_sta->he_cap; struct ieee80211_he_cap_elem *pe = &pc->he_cap_elem; const struct ieee80211_sta_he_cap *vc = mt76_connac_get_he_phy_cap(phy->mt76, vif); @@ -1569,7 +1596,7 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); - if (sta->deflink.bandwidth != IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth != IEEE80211_STA_RX_BW_160) return; /* go over for 160MHz and 80p80 */ @@ -1601,11 +1628,11 @@ mt7996_mcu_sta_bfer_he(struct ieee80211_sta *sta, struct ieee80211_vif *vif, } static void -mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif, - struct mt7996_phy *phy, struct sta_rec_bf *bf, - bool explicit) +mt7996_mcu_sta_bfer_eht(struct ieee80211_link_sta *link_sta, + struct ieee80211_vif *vif, struct mt7996_phy *phy, + struct sta_rec_bf *bf, bool explicit) { - struct ieee80211_sta_eht_cap *pc = &sta->deflink.eht_cap; + struct ieee80211_sta_eht_cap *pc = &link_sta->eht_cap; struct ieee80211_eht_cap_elem_fixed *pe = &pc->eht_cap_elem; struct ieee80211_eht_mcs_nss_supp *eht_nss = &pc->eht_mcs_nss_supp; const struct ieee80211_sta_eht_cap *vc = @@ -1629,10 +1656,10 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif, bf->ibf_ncol = explicit ? min_t(u8, MT7996_IBF_MAX_NC, bf->ncol) : min_t(u8, MT7996_IBF_MAX_NC, nss_mcs); - if (sta->deflink.bandwidth < IEEE80211_STA_RX_BW_160) + if (link_sta->bandwidth < IEEE80211_STA_RX_BW_160) return; - switch (sta->deflink.bandwidth) { + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: snd_dim = EHT_PHY(CAP2_SOUNDING_DIM_160MHZ_MASK, ve->phy_cap_info[2]); sts = EHT_PHY(CAP1_BEAMFORMEE_SS_160MHZ_MASK, pe->phy_cap_info[1]); @@ -1660,13 +1687,15 @@ mt7996_mcu_sta_bfer_eht(struct ieee80211_sta *sta, struct ieee80211_vif *vif, static void mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) { #define EBF_MODE BIT(0) #define IBF_MODE BIT(1) #define BF_MAT_ORDER 4 - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_phy *phy = mvif->deflink.phy; + struct ieee80211_vif *vif = link_conf->vif; + struct mt7996_phy *phy = link->phy; int tx_ant = hweight16(phy->mt76->chainmask) - 1; struct sta_rec_bf *bf; struct tlv *tlv; @@ -1678,10 +1707,10 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, }; bool ebf; - if (!(sta->deflink.ht_cap.ht_supported || sta->deflink.he_cap.has_he)) + if (!(link_sta->ht_cap.ht_supported || link_sta->he_cap.has_he)) return; - ebf = mt7996_is_ebf_supported(phy, vif, sta, false); + ebf = mt7996_is_ebf_supported(phy, link_conf, link_sta, false); if (!ebf && !dev->ibf) return; @@ -1692,28 +1721,29 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, * vht: support eBF and iBF * ht: iBF only, since mac80211 lacks of eBF support */ - if (sta->deflink.eht_cap.has_eht) - mt7996_mcu_sta_bfer_eht(sta, vif, phy, bf, ebf); - else if (sta->deflink.he_cap.has_he) - mt7996_mcu_sta_bfer_he(sta, vif, phy, bf, ebf); - else if (sta->deflink.vht_cap.vht_supported) - mt7996_mcu_sta_bfer_vht(sta, phy, bf, ebf); - else if (sta->deflink.ht_cap.ht_supported) - mt7996_mcu_sta_bfer_ht(sta, phy, bf, ebf); + if (link_sta->eht_cap.has_eht) + mt7996_mcu_sta_bfer_eht(link_sta, vif, link->phy, bf, ebf); + else if (link_sta->he_cap.has_he) + mt7996_mcu_sta_bfer_he(link_sta, vif, link->phy, bf, ebf); + else if (link_sta->vht_cap.vht_supported) + mt7996_mcu_sta_bfer_vht(link_sta, link->phy, bf, ebf); + else if (link_sta->ht_cap.ht_supported) + mt7996_mcu_sta_bfer_ht(link_sta, link->phy, bf, ebf); else return; bf->bf_cap = ebf ? EBF_MODE : (dev->ibf ? IBF_MODE : 0); if (is_mt7992(&dev->mt76) && tx_ant == 4) bf->bf_cap |= IBF_MODE; - bf->bw = sta->deflink.bandwidth; - bf->ibf_dbw = sta->deflink.bandwidth; + + bf->bw = link_sta->bandwidth; + bf->ibf_dbw = link_sta->bandwidth; bf->ibf_nrow = tx_ant; - if (sta->deflink.eht_cap.has_eht || sta->deflink.he_cap.has_he) + if (link_sta->eht_cap.has_eht || link_sta->he_cap.has_he) bf->ibf_timeout = is_mt7996(&dev->mt76) ? MT7996_IBF_TIMEOUT : MT7992_IBF_TIMEOUT; - else if (!ebf && sta->deflink.bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) + else if (!ebf && link_sta->bandwidth <= IEEE80211_STA_RX_BW_40 && !bf->ncol) bf->ibf_timeout = MT7996_IBF_TIMEOUT_LEGACY; else bf->ibf_timeout = MT7996_IBF_TIMEOUT; @@ -1727,7 +1757,7 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, matrix[bf->nrow][bf->ncol] : 0; } - switch (sta->deflink.bandwidth) { + switch (link_sta->bandwidth) { case IEEE80211_STA_RX_BW_160: case IEEE80211_STA_RX_BW_80: bf->mem_total = bf->mem_20m * 2; @@ -1743,31 +1773,32 @@ mt7996_mcu_sta_bfer_tlv(struct mt7996_dev *dev, struct sk_buff *skb, static void mt7996_mcu_sta_bfee_tlv(struct mt7996_dev *dev, struct sk_buff *skb, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_phy *phy = mvif->deflink.phy; + struct mt7996_phy *phy = link->phy; int tx_ant = hweight8(phy->mt76->antenna_mask) - 1; struct sta_rec_bfee *bfee; struct tlv *tlv; u8 nrow = 0; - if (!(sta->deflink.vht_cap.vht_supported || sta->deflink.he_cap.has_he)) + if (!(link_sta->vht_cap.vht_supported || link_sta->he_cap.has_he)) return; - if (!mt7996_is_ebf_supported(phy, vif, sta, true)) + if (!mt7996_is_ebf_supported(phy, link_conf, link_sta, true)) return; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_BFEE, sizeof(*bfee)); bfee = (struct sta_rec_bfee *)tlv; - if (sta->deflink.he_cap.has_he) { - struct ieee80211_he_cap_elem *pe = &sta->deflink.he_cap.he_cap_elem; + if (link_sta->he_cap.has_he) { + struct ieee80211_he_cap_elem *pe = &link_sta->he_cap.he_cap_elem; nrow = HE_PHY(CAP5_BEAMFORMEE_NUM_SND_DIM_UNDER_80MHZ_MASK, pe->phy_cap_info[5]); - } else if (sta->deflink.vht_cap.vht_supported) { - struct ieee80211_sta_vht_cap *pc = &sta->deflink.vht_cap; + } else if (link_sta->vht_cap.vht_supported) { + struct ieee80211_sta_vht_cap *pc = &link_sta->vht_cap; nrow = FIELD_GET(IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_MASK, pc->cap); @@ -1874,18 +1905,19 @@ int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, MCU_WM_UNI_CMD(RA), true); } -int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, void *data, u32 field) +int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + void *data, u32 field) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct sta_phy_uni *phy = data; struct sta_rec_ra_fixed_uni *ra; struct sk_buff *skb; struct tlv *tlv; - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, - &msta->wcid, + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -1904,7 +1936,7 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif ra->phy = *phy; break; case RATE_PARAM_MMPS_UPDATE: - ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode); + ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode); break; default: break; @@ -1916,12 +1948,13 @@ int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif } static int -mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) +mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct cfg80211_chan_def *chandef = &mvif->deflink.phy->mt76->chandef; - struct cfg80211_bitrate_mask *mask = &mvif->deflink.bitrate_mask; + struct cfg80211_chan_def *chandef = &link->phy->mt76->chandef; + struct cfg80211_bitrate_mask *mask = &link->bitrate_mask; enum nl80211_band band = chandef->chan->band; struct sta_phy_uni phy = {}; int ret, nrates = 0; @@ -1942,11 +1975,11 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif } \ } while (0) - if (sta->deflink.he_cap.has_he) { + if (link_sta->he_cap.has_he) { __sta_phy_bitrate_mask_check(he_mcs, he_gi, 0, 1); - } else if (sta->deflink.vht_cap.vht_supported) { + } else if (link_sta->vht_cap.vht_supported) { __sta_phy_bitrate_mask_check(vht_mcs, gi, 0, 0); - } else if (sta->deflink.ht_cap.ht_supported) { + } else if (link_sta->ht_cap.ht_supported) { __sta_phy_bitrate_mask_check(ht_mcs, gi, 1, 0); } else { nrates = hweight32(mask->control[band].legacy); @@ -1963,7 +1996,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif /* fixed single rate */ if (nrates == 1) { - ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy, + ret = mt7996_mcu_set_fixed_field(dev, link_sta, link, + msta_link, &phy, RATE_PARAM_FIXED_MCS); if (ret) return ret; @@ -1972,20 +2006,20 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif /* fixed GI */ if (mask->control[band].gi != NL80211_TXRATE_DEFAULT_GI || mask->control[band].he_gi != GENMASK(7, 0)) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; u32 addr; /* firmware updates only TXCMD but doesn't take WTBL into * account, so driver should update here to reflect the * actual txrate hardware sends out. */ - addr = mt7996_mac_wtbl_lmac_addr(dev, msta->wcid.idx, 7); - if (sta->deflink.he_cap.has_he) + addr = mt7996_mac_wtbl_lmac_addr(dev, msta_link->wcid.idx, 7); + if (link_sta->he_cap.has_he) mt76_rmw_field(dev, addr, GENMASK(31, 24), phy.sgi); else mt76_rmw_field(dev, addr, GENMASK(15, 12), phy.sgi); - ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy, + ret = mt7996_mcu_set_fixed_field(dev, link_sta, link, + msta_link, &phy, RATE_PARAM_FIXED_GI); if (ret) return ret; @@ -1993,7 +2027,8 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif /* fixed HE_LTF */ if (mask->control[band].he_ltf != GENMASK(7, 0)) { - ret = mt7996_mcu_set_fixed_field(dev, vif, sta, &phy, + ret = mt7996_mcu_set_fixed_field(dev, link_sta, link, + msta_link, &phy, RATE_PARAM_FIXED_HE_LTF); if (ret) return ret; @@ -2004,30 +2039,32 @@ mt7996_mcu_add_rate_ctrl_fixed(struct mt7996_dev *dev, struct ieee80211_vif *vif static void mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, - struct ieee80211_vif *vif, struct ieee80211_sta *sta) + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link) { #define INIT_RCPI 180 - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt76_phy *mphy = mvif->deflink.phy->mt76; + struct mt76_phy *mphy = link->phy->mt76; struct cfg80211_chan_def *chandef = &mphy->chandef; - struct cfg80211_bitrate_mask *mask = &mvif->deflink.bitrate_mask; + struct cfg80211_bitrate_mask *mask = &link->bitrate_mask; + u32 cap = link_sta->sta->wme ? STA_CAP_WMM : 0; enum nl80211_band band = chandef->chan->band; struct sta_rec_ra_uni *ra; struct tlv *tlv; - u32 supp_rate = sta->deflink.supp_rates[band]; - u32 cap = sta->wme ? STA_CAP_WMM : 0; + u32 supp_rate = link_sta->supp_rates[band]; tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_RA, sizeof(*ra)); ra = (struct sta_rec_ra_uni *)tlv; ra->valid = true; ra->auto_rate = true; - ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, &sta->deflink); + ra->phy_mode = mt76_connac_get_phy_mode(mphy, vif, band, link_sta); ra->channel = chandef->chan->hw_value; - ra->bw = (sta->deflink.bandwidth == IEEE80211_STA_RX_BW_320) ? - CMD_CBW_320MHZ : sta->deflink.bandwidth; + ra->bw = (link_sta->bandwidth == IEEE80211_STA_RX_BW_320) ? + CMD_CBW_320MHZ : link_sta->bandwidth; ra->phy.bw = ra->bw; - ra->mmps_mode = mt7996_mcu_get_mmps_mode(sta->deflink.smps_mode); + ra->mmps_mode = mt7996_mcu_get_mmps_mode(link_sta->smps_mode); if (supp_rate) { supp_rate &= mask->control[band].legacy; @@ -2047,60 +2084,60 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, } } - if (sta->deflink.ht_cap.ht_supported) { + if (link_sta->ht_cap.ht_supported) { ra->supp_mode |= MODE_HT; - ra->af = sta->deflink.ht_cap.ampdu_factor; - ra->ht_gf = !!(sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); + ra->af = link_sta->ht_cap.ampdu_factor; + ra->ht_gf = !!(link_sta->ht_cap.cap & IEEE80211_HT_CAP_GRN_FLD); cap |= STA_CAP_HT; - if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_20) + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_20) cap |= STA_CAP_SGI_20; - if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_SGI_40) + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_SGI_40) cap |= STA_CAP_SGI_40; - if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_TX_STBC) cap |= STA_CAP_TX_STBC; - if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) + if (link_sta->ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) cap |= STA_CAP_RX_STBC; - if (vif->bss_conf.ht_ldpc && - (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) + if (link_conf->ht_ldpc && + (link_sta->ht_cap.cap & IEEE80211_HT_CAP_LDPC_CODING)) cap |= STA_CAP_LDPC; - mt7996_mcu_set_sta_ht_mcs(sta, ra->ht_mcs, + mt7996_mcu_set_sta_ht_mcs(link_sta, ra->ht_mcs, mask->control[band].ht_mcs); ra->supp_ht_mcs = *(__le32 *)ra->ht_mcs; } - if (sta->deflink.vht_cap.vht_supported) { + if (link_sta->vht_cap.vht_supported) { u8 af; ra->supp_mode |= MODE_VHT; af = FIELD_GET(IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK, - sta->deflink.vht_cap.cap); + link_sta->vht_cap.cap); ra->af = max_t(u8, ra->af, af); cap |= STA_CAP_VHT; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_80) cap |= STA_CAP_VHT_SGI_80; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_SHORT_GI_160) cap |= STA_CAP_VHT_SGI_160; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_TXSTBC) cap |= STA_CAP_VHT_TX_STBC; - if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) + if (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXSTBC_1) cap |= STA_CAP_VHT_RX_STBC; - if ((vif->type != NL80211_IFTYPE_AP || vif->bss_conf.vht_ldpc) && - (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) + if ((vif->type != NL80211_IFTYPE_AP || link_conf->vht_ldpc) && + (link_sta->vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC)) cap |= STA_CAP_VHT_LDPC; - mt7996_mcu_set_sta_vht_mcs(sta, ra->supp_vht_mcs, + mt7996_mcu_set_sta_vht_mcs(link_sta, ra->supp_vht_mcs, mask->control[band].vht_mcs); } - if (sta->deflink.he_cap.has_he) { + if (link_sta->he_cap.has_he) { ra->supp_mode |= MODE_HE; cap |= STA_CAP_HE; - if (sta->deflink.he_6ghz_capa.capa) - ra->af = le16_get_bits(sta->deflink.he_6ghz_capa.capa, + if (link_sta->he_6ghz_capa.capa) + ra->af = le16_get_bits(link_sta->he_6ghz_capa.capa, IEEE80211_HE_6GHZ_CAP_MAX_AMPDU_LEN_EXP); } ra->sta_cap = cpu_to_le32(cap); @@ -2108,16 +2145,18 @@ mt7996_mcu_sta_rate_ctrl_tlv(struct sk_buff *skb, struct mt7996_dev *dev, memset(ra->rx_rcpi, INIT_RCPI, sizeof(ra->rx_rcpi)); } -int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool changed) +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, bool changed) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; struct sk_buff *skb; int ret; - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, - &msta->wcid, + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2127,19 +2166,19 @@ int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, * update sta_rec_he here. */ if (changed) - mt7996_mcu_sta_he_tlv(skb, sta); + mt7996_mcu_sta_he_tlv(skb, link_sta, link); /* sta_rec_ra accommodates BW, NSS and only MCS range format * i.e 0-{7,8,9} for VHT. */ - mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, sta); + mt7996_mcu_sta_rate_ctrl_tlv(skb, dev, vif, link_conf, link_sta, link); ret = mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); if (ret) return ret; - return mt7996_mcu_add_rate_ctrl_fixed(dev, vif, sta); + return mt7996_mcu_add_rate_ctrl_fixed(dev, link_sta, link, msta_link); } static int @@ -2148,6 +2187,7 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, { #define MT_STA_BSS_GROUP 1 struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; + struct mt7996_sta_link *msta_link; struct mt7996_sta *msta; struct { u8 __rsv1[4]; @@ -2166,73 +2206,151 @@ mt7996_mcu_add_group(struct mt7996_dev *dev, struct ieee80211_vif *vif, .val = cpu_to_le32(mvif->deflink.mt76.idx % 16), }; - msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->deflink.sta; - req.wlan_idx = cpu_to_le16(msta->wcid.idx); + msta = sta ? (struct mt7996_sta *)sta->drv_priv : NULL; + msta_link = msta ? &msta->deflink : &mvif->deflink.msta_link; + req.wlan_idx = cpu_to_le16(msta_link->wcid.idx); return mt76_mcu_send_msg(&dev->mt76, MCU_WM_UNI_CMD(VOW), &req, sizeof(req), true); } -int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct mt76_vif_link *mlink, - struct ieee80211_sta *sta, int conn_state, bool newly) +static void +mt7996_mcu_sta_mld_setup_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta) { - struct ieee80211_link_sta *link_sta = NULL; - struct mt76_wcid *wcid = mlink->wcid; - struct sk_buff *skb; - int ret; + struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + unsigned long links = sta->valid_links; + unsigned int nlinks = hweight16(links); + struct mld_setup_link *mld_setup_link; + struct sta_rec_mld_setup *mld_setup; + struct mt7996_sta_link *msta_link; + struct ieee80211_vif *vif; + unsigned int link_id; + struct tlv *tlv; - if (sta) { - struct mt7996_sta *msta = (struct mt7996_sta *)sta->drv_priv; + msta_link = mt76_dereference(msta->link[msta->deflink_id], &dev->mt76); + if (!msta_link) + return; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_MLD, + sizeof(struct sta_rec_mld_setup) + + sizeof(struct mld_setup_link) * nlinks); - wcid = &msta->wcid; - link_sta = &sta->deflink; + mld_setup = (struct sta_rec_mld_setup *)tlv; + memcpy(mld_setup->mld_addr, sta->addr, ETH_ALEN); + mld_setup->setup_wcid = cpu_to_le16(msta_link->wcid.idx); + mld_setup->primary_id = cpu_to_le16(msta_link->wcid.idx); + + if (nlinks > 1) { + link_id = __ffs(links & ~BIT(msta->deflink_id)); + msta_link = mt76_dereference(msta->link[msta->deflink_id], + &dev->mt76); + if (!msta_link) + return; } + mld_setup->seconed_id = cpu_to_le16(msta_link->wcid.idx); + mld_setup->link_num = nlinks; - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, mlink, wcid, + vif = container_of((void *)msta->vif, struct ieee80211_vif, drv_priv); + mld_setup_link = (struct mld_setup_link *)mld_setup->link_info; + for_each_set_bit(link_id, &links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct mt7996_vif_link *link; + + msta_link = mt76_dereference(msta->link[link_id], &dev->mt76); + if (!msta_link) + continue; + + link = mt7996_vif_link(dev, vif, link_id); + if (!link) + continue; + + mld_setup_link->wcid = cpu_to_le16(msta_link->wcid.idx); + mld_setup_link->bss_idx = link->mt76.idx; + mld_setup_link++; + } +} + +static void +mt7996_mcu_sta_eht_mld_tlv(struct mt7996_dev *dev, struct sk_buff *skb, + struct ieee80211_sta *sta) +{ + struct sta_rec_eht_mld *eht_mld; + struct tlv *tlv; + int i; + + tlv = mt76_connac_mcu_add_tlv(skb, STA_REC_EHT_MLD, sizeof(*eht_mld)); + eht_mld = (struct sta_rec_eht_mld *)tlv; + + for (i = 0; i < ARRAY_SIZE(eht_mld->str_cap); i++) + eht_mld->str_cap[i] = 0x7; +} + +int mt7996_mcu_add_sta(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + int conn_state, bool newly) +{ + struct mt76_wcid *wcid = msta_link ? &msta_link->wcid : link->mt76.wcid; + struct ieee80211_sta *sta = link_sta ? link_sta->sta : NULL; + struct sk_buff *skb; + int ret; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec basic */ - mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, &vif->bss_conf, link_sta, + mt76_connac_mcu_sta_basic_tlv(&dev->mt76, skb, link_conf, link_sta, conn_state, newly); if (conn_state == CONN_STATE_DISCONNECT) goto out; /* starec hdr trans */ - mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, wcid); + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, link_conf->vif, wcid); /* starec tx proc */ mt7996_mcu_sta_tx_proc_tlv(skb); /* tag order is in accordance with firmware dependency. */ - if (sta) { + if (link_sta) { /* starec hdrt mode */ mt7996_mcu_sta_hdrt_tlv(dev, skb); - /* starec bfer */ - mt7996_mcu_sta_bfer_tlv(dev, skb, vif, sta); + if (conn_state == CONN_STATE_CONNECT) { + /* starec bfer */ + mt7996_mcu_sta_bfer_tlv(dev, skb, link_conf, link_sta, + link); + /* starec bfee */ + mt7996_mcu_sta_bfee_tlv(dev, skb, link_conf, link_sta, + link); + } /* starec ht */ - mt7996_mcu_sta_ht_tlv(skb, sta); + mt7996_mcu_sta_ht_tlv(skb, link_sta); /* starec vht */ - mt7996_mcu_sta_vht_tlv(skb, sta); + mt7996_mcu_sta_vht_tlv(skb, link_sta); /* starec uapsd */ - mt76_connac_mcu_sta_uapsd(skb, vif, sta); + mt76_connac_mcu_sta_uapsd(skb, link_conf->vif, sta); /* starec amsdu */ - mt7996_mcu_sta_amsdu_tlv(dev, skb, vif, sta); + mt7996_mcu_sta_amsdu_tlv(dev, skb, link_conf->vif, link_sta, + msta_link); /* starec he */ - mt7996_mcu_sta_he_tlv(skb, sta); + mt7996_mcu_sta_he_tlv(skb, link_sta, link); /* starec he 6g*/ - mt7996_mcu_sta_he_6g_tlv(skb, sta); + mt7996_mcu_sta_he_6g_tlv(skb, link_sta); /* starec eht */ - mt7996_mcu_sta_eht_tlv(skb, sta); + mt7996_mcu_sta_eht_tlv(skb, link_sta); /* starec muru */ - mt7996_mcu_sta_muru_tlv(dev, skb, vif, sta); - /* starec bfee */ - mt7996_mcu_sta_bfee_tlv(dev, skb, vif, sta); + mt7996_mcu_sta_muru_tlv(dev, skb, link_conf, link_sta); + + if (sta->mlo) { + mt7996_mcu_sta_mld_setup_tlv(dev, skb, sta); + mt7996_mcu_sta_eht_mld_tlv(dev, skb, sta); + } } - ret = mt7996_mcu_add_group(dev, vif, sta); + ret = mt7996_mcu_add_group(dev, link_conf->vif, sta); if (ret) { dev_kfree_skb(skb); return ret; @@ -2242,6 +2360,24 @@ out: MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } +int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) +{ + struct sk_buff *skb; + + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, + MT7996_STA_UPDATE_MAX_SIZE); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + mt76_connac_mcu_add_tlv(skb, STA_REC_MLD_OFF, sizeof(struct tlv)); + + return mt76_mcu_skb_send_msg(&dev->mt76, skb, + MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); +} + static int mt7996_mcu_sta_key_tlv(struct mt76_wcid *wcid, struct sk_buff *skb, @@ -2307,17 +2443,18 @@ int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, return mt76_mcu_skb_send_msg(dev, skb, mcu_cmd, true); } -static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif, - u8 *pn) +static int mt7996_mcu_get_pn(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, u8 *pn) { #define TSC_TYPE_BIGTK_PN 2 - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct sta_rec_pn_info *pn_info; struct sk_buff *skb, *rskb; struct tlv *tlv; int ret; - skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, &mvif->deflink.sta.wcid); + skb = mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2341,10 +2478,11 @@ static int mt7996_mcu_get_pn(struct mt7996_dev *dev, struct ieee80211_vif *vif, return 0; } -int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif, +int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, struct ieee80211_key_conf *key) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_mcu_bcn_prot_tlv *bcn_prot; struct sk_buff *skb; struct tlv *tlv; @@ -2353,7 +2491,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif sizeof(struct mt7996_mcu_bcn_prot_tlv); int ret; - skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->deflink.mt76, len); + skb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, len); if (IS_ERR(skb)) return PTR_ERR(skb); @@ -2361,7 +2499,7 @@ int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif bcn_prot = (struct mt7996_mcu_bcn_prot_tlv *)tlv; - ret = mt7996_mcu_get_pn(dev, vif, pn); + ret = mt7996_mcu_get_pn(dev, link, msta_link, pn); if (ret) { dev_kfree_skb(skb); return ret; @@ -2592,13 +2730,14 @@ out: } int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, - struct ieee80211_vif *vif, u32 changed) + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, u32 changed) { #define OFFLOAD_TX_MODE_SU BIT(0) #define OFFLOAD_TX_MODE_MU BIT(1) + struct ieee80211_vif *vif = link_conf->vif; struct ieee80211_hw *hw = mt76_hw(dev); - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_phy *phy = mt7996_vif_link_phy(&mvif->deflink); + struct mt7996_phy *phy = link->phy; struct mt76_wcid *wcid = &dev->mt76.global_wcid; struct bss_inband_discovery_tlv *discov; struct ieee80211_tx_info *info; @@ -2615,21 +2754,21 @@ int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, chandef = &phy->mt76->chandef; band = chandef->chan->band; - if (vif->bss_conf.nontransmitted) + if (link_conf->nontransmitted) return 0; - rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &mvif->deflink.mt76, + rskb = __mt7996_mcu_alloc_bss_req(&dev->mt76, &link->mt76, MT7996_MAX_BSS_OFFLOAD_SIZE); if (IS_ERR(rskb)) return PTR_ERR(rskb); if (changed & BSS_CHANGED_FILS_DISCOVERY && - vif->bss_conf.fils_discovery.max_interval) { - interval = vif->bss_conf.fils_discovery.max_interval; + link_conf->fils_discovery.max_interval) { + interval = link_conf->fils_discovery.max_interval; skb = ieee80211_get_fils_discovery_tmpl(hw, vif); } else if (changed & BSS_CHANGED_UNSOL_BCAST_PROBE_RESP && - vif->bss_conf.unsol_bcast_probe_resp_interval) { - interval = vif->bss_conf.unsol_bcast_probe_resp_interval; + link_conf->unsol_bcast_probe_resp_interval) { + interval = link_conf->unsol_bcast_probe_resp_interval; skb = ieee80211_get_unsol_bcast_probe_resp_tmpl(hw, vif); } @@ -2891,6 +3030,9 @@ static int mt7996_load_ram(struct mt7996_dev *dev) if (ret) return ret; + if (!mt7996_has_wa(dev)) + return 0; + ret = __mt7996_load_ram(dev, "DSP", fw_name(dev, FIRMWARE_DSP), MT7996_RAM_TYPE_DSP); if (ret) @@ -2901,10 +3043,9 @@ static int mt7996_load_ram(struct mt7996_dev *dev) } static int -mt7996_firmware_state(struct mt7996_dev *dev, bool wa) +mt7996_firmware_state(struct mt7996_dev *dev, u8 fw_state) { - u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, - wa ? FW_STATE_RDY : FW_STATE_FW_DOWNLOAD); + u32 state = FIELD_PREP(MT_TOP_MISC_FW_STATE, fw_state); if (!mt76_poll_msec(dev, MT_TOP_MISC, MT_TOP_MISC_FW_STATE, state, 1000)) { @@ -2936,13 +3077,14 @@ mt7996_mcu_restart(struct mt76_dev *dev) static int mt7996_load_firmware(struct mt7996_dev *dev) { + u8 fw_state; int ret; /* make sure fw is download state */ - if (mt7996_firmware_state(dev, false)) { + if (mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD)) { /* restart firmware once */ mt7996_mcu_restart(&dev->mt76); - ret = mt7996_firmware_state(dev, false); + ret = mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD); if (ret) { dev_err(dev->mt76.dev, "Firmware is not ready for download\n"); @@ -2958,7 +3100,8 @@ static int mt7996_load_firmware(struct mt7996_dev *dev) if (ret) return ret; - ret = mt7996_firmware_state(dev, true); + fw_state = mt7996_has_wa(dev) ? FW_STATE_RDY : FW_STATE_NORMAL_TRX; + ret = mt7996_firmware_state(dev, fw_state); if (ret) return ret; @@ -3096,13 +3239,15 @@ int mt7996_mcu_init_firmware(struct mt7996_dev *dev) if (ret) return ret; - ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0); - if (ret) - return ret; + if (mt7996_has_wa(dev)) { + ret = mt7996_mcu_fw_log_2_host(dev, MCU_FW_LOG_WA, 0); + if (ret) + return ret; - ret = mt7996_mcu_set_mwds(dev, 1); - if (ret) - return ret; + ret = mt7996_mcu_set_mwds(dev, 1); + if (ret) + return ret; + } ret = mt7996_mcu_init_rx_airtime(dev); if (ret) @@ -3128,7 +3273,7 @@ int mt7996_mcu_init(struct mt7996_dev *dev) void mt7996_mcu_exit(struct mt7996_dev *dev) { mt7996_mcu_restart(&dev->mt76); - if (mt7996_firmware_state(dev, false)) { + if (mt7996_firmware_state(dev, FW_STATE_FW_DOWNLOAD)) { dev_err(dev->mt76.dev, "Failed to exit mcu\n"); goto out; } @@ -3415,11 +3560,10 @@ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef) { struct mt7996_dev *dev = phy->dev; - int err, region; + int err, region, rdd_idx = mt7996_get_rdd_idx(phy, true); if (!chandef) { /* disable offchain */ - err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, MT_RX_SEL2, - 0, 0); + err = mt7996_mcu_rdd_cmd(dev, RDD_STOP, rdd_idx, 0); if (err) return err; @@ -3445,8 +3589,7 @@ int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, break; } - return mt7996_mcu_rdd_cmd(dev, RDD_START, MT_RX_SEL2, - 0, region); + return mt7996_mcu_rdd_cmd(dev, RDD_START, rdd_idx, region); } int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag) @@ -4068,12 +4211,12 @@ mt7996_mcu_set_obss_spr_pd(struct mt7996_phy *phy, } static int -mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, struct ieee80211_vif *vif, +mt7996_mcu_set_obss_spr_siga(struct mt7996_phy *phy, + struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; struct mt7996_dev *dev = phy->dev; - u8 omac = mvif->deflink.mt76.omac_idx; + u8 omac = link->mt76.omac_idx; struct { u8 band_idx; u8 __rsv[3]; @@ -4145,7 +4288,8 @@ mt7996_mcu_set_obss_spr_bitmap(struct mt7996_phy *phy, sizeof(req), true); } -int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, + struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd) { int ret; @@ -4179,7 +4323,7 @@ int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, return ret; /* Set SR prohibit */ - ret = mt7996_mcu_set_obss_spr_siga(phy, vif, he_obss_pd); + ret = mt7996_mcu_set_obss_spr_siga(phy, link, he_obss_pd); if (ret) return ret; @@ -4215,7 +4359,7 @@ int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, #define TWT_AGRT_PROTECT BIT(2) int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, - struct mt7996_vif *mvif, + struct mt7996_vif_link *link, struct mt7996_twt_flow *flow, int cmd) { @@ -4246,12 +4390,12 @@ int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, .len = cpu_to_le16(sizeof(req) - 4), .tbl_idx = flow->table_id, .cmd = cmd, - .own_mac_idx = mvif->deflink.mt76.omac_idx, + .own_mac_idx = link->mt76.omac_idx, .flowid = flow->id, .peer_id = cpu_to_le16(flow->wcid), .duration = flow->duration, - .bss = mvif->deflink.mt76.idx, - .bss_idx = mvif->deflink.mt76.idx, + .bss = link->mt76.idx, + .bss_idx = link->mt76.idx, .start_tsf = cpu_to_le64(flow->tsf), .mantissa = flow->mantissa, .exponent = flow->exp, @@ -4312,8 +4456,7 @@ int mt7996_mcu_set_radio_en(struct mt7996_phy *phy, bool enable) &req, sizeof(req), true); } -int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, - u8 rx_sel, u8 val) +int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val) { struct { u8 _rsv[4]; @@ -4330,8 +4473,7 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, .tag = cpu_to_le16(UNI_RDD_CTRL_PARM), .len = cpu_to_le16(sizeof(req) - 4), .ctrl = cmd, - .rdd_idx = index, - .rdd_rx_sel = rx_sel, + .rdd_idx = rdd_idx, .val = val, }; @@ -4341,22 +4483,19 @@ int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta) + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link) { - struct mt7996_vif *mvif = (struct mt7996_vif *)vif->drv_priv; - struct mt7996_sta *msta; struct sk_buff *skb; - msta = sta ? (struct mt7996_sta *)sta->drv_priv : &mvif->deflink.sta; - - skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &mvif->deflink.mt76, - &msta->wcid, + skb = __mt76_connac_mcu_alloc_sta_req(&dev->mt76, &link->mt76, + &msta_link->wcid, MT7996_STA_UPDATE_MAX_SIZE); if (IS_ERR(skb)) return PTR_ERR(skb); /* starec hdr trans */ - mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, &msta->wcid); + mt7996_mcu_sta_hdr_trans_tlv(dev, skb, vif, &msta_link->wcid); return mt76_mcu_skb_send_msg(&dev->mt76, skb, MCU_WMWA_UNI_CMD(STA_REC_UPDATE), true); } @@ -4579,7 +4718,7 @@ int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy) struct sk_buff *skb; int i, tx_power; - tx_power = mt7996_get_power_bound(phy, phy->txpower); + tx_power = mt76_get_power_bound(mphy, phy->txpower); tx_power = mt76_get_rate_power_limits(mphy, mphy->chandef.chan, &la, tx_power); mphy->txpower_cur = tx_power; @@ -4624,7 +4763,26 @@ int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode) mode > mt76_connac_lmac_mapping(IEEE80211_AC_VO)) return -EINVAL; + if (!mt7996_has_wa(dev)) { + struct { + u8 _rsv[4]; + + __le16 tag; + __le16 len; + u8 cp_mode; + u8 rsv[3]; + } __packed req = { + .tag = cpu_to_le16(UNI_CMD_SDO_CP_MODE), + .len = cpu_to_le16(sizeof(req) - 4), + .cp_mode = mode, + }; + + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_UNI_CMD(SDO), + &req, sizeof(req), false); + } + cp_mode = cpu_to_le32(mode); + return mt76_mcu_send_msg(&dev->mt76, MCU_WA_EXT_CMD(CP_SUPPORT), &cp_mode, sizeof(cp_mode), true); } diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h index 43468bcaffc6..130ea95626d5 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mcu.h @@ -69,7 +69,7 @@ struct mt7996_mcu_rdd_report { __le16 tag; __le16 len; - u8 band_idx; + u8 rdd_idx; u8 long_detected; u8 constant_prf_detected; u8 staggered_prf_detected; @@ -625,6 +625,35 @@ struct sta_rec_hdr_trans { u8 mesh; } __packed; +struct sta_rec_mld_setup { + __le16 tag; + __le16 len; + u8 mld_addr[ETH_ALEN]; + __le16 primary_id; + __le16 seconed_id; + __le16 setup_wcid; + u8 link_num; + u8 info; + u8 __rsv[2]; + u8 link_info[]; +} __packed; + +struct sta_rec_eht_mld { + __le16 tag; + __le16 len; + u8 nsep; + u8 __rsv1[2]; + u8 str_cap[__MT_MAX_BAND]; + __le16 eml_cap; + u8 __rsv2[4]; +} __packed; + +struct mld_setup_link { + __le16 wcid; + u8 bss_idx; + u8 __rsv; +} __packed; + struct hdr_trans_en { __le16 tag; __le16 len; @@ -798,29 +827,20 @@ enum { sizeof(struct sta_rec_eht) + \ sizeof(struct sta_rec_hdrt) + \ sizeof(struct sta_rec_hdr_trans) + \ + sizeof(struct sta_rec_mld_setup) + \ + sizeof(struct mld_setup_link) * 3 + \ + sizeof(struct sta_rec_eht_mld) + \ sizeof(struct tlv)) -#define MT7996_MAX_BEACON_SIZE 1338 #define MT7996_BEACON_UPDATE_SIZE (sizeof(struct bss_req_hdr) + \ sizeof(struct bss_bcn_content_tlv) + \ 4 + MT_TXD_SIZE + \ sizeof(struct bss_bcn_cntdwn_tlv) + \ sizeof(struct bss_bcn_mbss_tlv)) -#define MT7996_MAX_BSS_OFFLOAD_SIZE (MT7996_MAX_BEACON_SIZE + \ +#define MT7996_MAX_BSS_OFFLOAD_SIZE 2048 +#define MT7996_MAX_BEACON_SIZE (MT7996_MAX_BSS_OFFLOAD_SIZE - \ MT7996_BEACON_UPDATE_SIZE) -static inline s8 -mt7996_get_power_bound(struct mt7996_phy *phy, s8 txpower) -{ - struct mt76_phy *mphy = phy->mt76; - int n_chains = hweight16(mphy->chainmask); - - txpower = mt76_get_sar_power(mphy, mphy->chandef.chan, txpower * 2); - txpower -= mt76_tx_power_nss_delta(n_chains); - - return txpower; -} - enum { UNI_BAND_CONFIG_RADIO_ENABLE, UNI_BAND_CONFIG_RTS_THRESHOLD = 0x08, @@ -908,7 +928,8 @@ enum { UNI_CMD_SER_SET_RECOVER_L3_TX_DISABLE, UNI_CMD_SER_SET_RECOVER_L3_BF, UNI_CMD_SER_SET_RECOVER_L4_MDP, - UNI_CMD_SER_SET_RECOVER_FULL, + UNI_CMD_SER_SET_RECOVER_FROM_ETH, + UNI_CMD_SER_SET_RECOVER_FULL = 8, UNI_CMD_SER_SET_SYSTEM_ASSERT, /* action */ UNI_CMD_SER_ENABLE = 1, @@ -917,6 +938,12 @@ enum { }; enum { + UNI_CMD_SDO_SET = 1, + UNI_CMD_SDO_QUERY, + UNI_CMD_SDO_CP_MODE = 6, +}; + +enum { MT7996_SEC_MODE_PLAIN, MT7996_SEC_MODE_AES, MT7996_SEC_MODE_SCRAMBLE, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c index 7a8ee6c75cf2..30b40f4a91be 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mmio.c @@ -54,6 +54,17 @@ static const u32 mt7996_offs[] = { [MIB_BSCR7] = 0x9e8, [MIB_BSCR17] = 0xa10, [MIB_TRDR1] = 0xa28, + [HIF_REMAP_L1] = 0x24, + [HIF_REMAP_BASE_L1] = 0x130000, + [HIF_REMAP_L2] = 0x1b4, + [HIF_REMAP_BASE_L2] = 0x1000, + [CBTOP1_PHY_END] = 0x77ffffff, + [INFRA_MCU_END] = 0x7c3fffff, + [WTBLON_WDUCR] = 0x370, + [WTBL_UPDATE] = 0x380, + [WTBL_ITCR] = 0x3b0, + [WTBL_ITCR0] = 0x3b8, + [WTBL_ITCR1] = 0x3bc, }; static const u32 mt7992_offs[] = { @@ -80,6 +91,54 @@ static const u32 mt7992_offs[] = { [MIB_BSCR7] = 0xae4, [MIB_BSCR17] = 0xb0c, [MIB_TRDR1] = 0xb24, + [HIF_REMAP_L1] = 0x8, + [HIF_REMAP_BASE_L1] = 0x40000, + [HIF_REMAP_L2] = 0x1b4, + [HIF_REMAP_BASE_L2] = 0x1000, + [CBTOP1_PHY_END] = 0x77ffffff, + [INFRA_MCU_END] = 0x7c3fffff, + [WTBLON_WDUCR] = 0x370, + [WTBL_UPDATE] = 0x380, + [WTBL_ITCR] = 0x3b0, + [WTBL_ITCR0] = 0x3b8, + [WTBL_ITCR1] = 0x3bc, +}; + +static const u32 mt7990_offs[] = { + [MIB_RVSR0] = 0x800, + [MIB_RVSR1] = 0x804, + [MIB_BTSCR5] = 0x868, + [MIB_BTSCR6] = 0x878, + [MIB_RSCR1] = 0x890, + [MIB_RSCR27] = 0xa38, + [MIB_RSCR28] = 0xa3c, + [MIB_RSCR29] = 0xa40, + [MIB_RSCR30] = 0xa44, + [MIB_RSCR31] = 0xa48, + [MIB_RSCR33] = 0xa50, + [MIB_RSCR35] = 0xa58, + [MIB_RSCR36] = 0xa5c, + [MIB_BSCR0] = 0xbb8, + [MIB_BSCR1] = 0xbbc, + [MIB_BSCR2] = 0xbc0, + [MIB_BSCR3] = 0xbc4, + [MIB_BSCR4] = 0xbc8, + [MIB_BSCR5] = 0xbcc, + [MIB_BSCR6] = 0xbd0, + [MIB_BSCR7] = 0xbd4, + [MIB_BSCR17] = 0xbfc, + [MIB_TRDR1] = 0xc14, + [HIF_REMAP_L1] = 0x8, + [HIF_REMAP_BASE_L1] = 0x40000, + [HIF_REMAP_L2] = 0x1b8, + [HIF_REMAP_BASE_L2] = 0x110000, + [CBTOP1_PHY_END] = 0x7fffffff, + [INFRA_MCU_END] = 0x7cffffff, + [WTBLON_WDUCR] = 0x400, + [WTBL_UPDATE] = 0x410, + [WTBL_ITCR] = 0x440, + [WTBL_ITCR0] = 0x448, + [WTBL_ITCR1] = 0x44c, }; static const struct __map mt7996_reg_map[] = { @@ -135,14 +194,83 @@ static const struct __map mt7996_reg_map[] = { { 0x0, 0x0, 0x0 }, /* imply end of search */ }; +static const struct __map mt7990_reg_map[] = { + {0x54000000, 0x02000, 0x1000}, /* WFDMA_0 (PCIE0 MCU DMA0) */ + {0x55000000, 0x03000, 0x1000}, /* WFDMA_1 (PCIE0 MCU DMA1) */ + {0x56000000, 0x04000, 0x1000}, /* WFDMA_2 (Reserved) */ + {0x57000000, 0x05000, 0x1000}, /* WFDMA_3 (MCU wrap CR) */ + {0x58000000, 0x06000, 0x1000}, /* WFDMA_4 (PCIE1 MCU DMA0 (MEM_DMA)) */ + {0x59000000, 0x07000, 0x1000}, /* WFDMA_5 (PCIE1 MCU DMA1) */ + {0x820c0000, 0x08000, 0x4000}, /* WF_UMAC_TOP (PLE) */ + {0x820c8000, 0x0c000, 0x2000}, /* WF_UMAC_TOP (PSE) */ + {0x820cc000, 0x0e000, 0x2000}, /* WF_UMAC_TOP (PP) */ + {0x820e0000, 0x20000, 0x0400}, /* WF_LMAC_TOP BN0 (WF_CFG) */ + {0x820e1000, 0x20400, 0x0200}, /* WF_LMAC_TOP BN0 (WF_TRB) */ + {0x820e2000, 0x20800, 0x0400}, /* WF_LMAC_TOP BN0 (WF_AGG) */ + {0x820e3000, 0x20c00, 0x0400}, /* WF_LMAC_TOP BN0 (WF_ARB) */ + {0x820e4000, 0x21000, 0x0400}, /* WF_LMAC_TOP BN0 (WF_TMAC) */ + {0x820e5000, 0x21400, 0x0800}, /* WF_LMAC_TOP BN0 (WF_RMAC) */ + {0x820ce000, 0x21c00, 0x0200}, /* WF_LMAC_TOP (WF_SEC) */ + {0x820e7000, 0x21e00, 0x0200}, /* WF_LMAC_TOP BN0 (WF_DMA) */ + {0x820cf000, 0x22000, 0x1000}, /* WF_LMAC_TOP (WF_PF) */ + {0x820e9000, 0x23400, 0x0200}, /* WF_LMAC_TOP BN0 (WF_WTBLOFF) */ + {0x820ea000, 0x24000, 0x0200}, /* WF_LMAC_TOP BN0 (WF_ETBF) */ + {0x820eb000, 0x24200, 0x0400}, /* WF_LMAC_TOP BN0 (WF_LPON) */ + {0x820ec000, 0x24600, 0x0200}, /* WF_LMAC_TOP BN0 (WF_INT) */ + {0x820ed000, 0x24800, 0x0800}, /* WF_LMAC_TOP BN0 (WF_MIB) */ + {0x820ca000, 0x26000, 0x2000}, /* WF_LMAC_TOP BN0 (WF_MUCOP) */ + {0x820d0000, 0x30000, 0x10000}, /* WF_LMAC_TOP (WF_WTBLON) */ + {0x00400000, 0x80000, 0x10000}, /* WF_MCU_SYSRAM */ + {0x820f0000, 0xa0000, 0x0400}, /* WF_LMAC_TOP BN1 (WF_CFG) */ + {0x820f1000, 0xa0600, 0x0200}, /* WF_LMAC_TOP BN1 (WF_TRB) */ + {0x820f2000, 0xa0800, 0x0400}, /* WF_LMAC_TOP BN1 (WF_AGG) */ + {0x820f3000, 0xa0c00, 0x0400}, /* WF_LMAC_TOP BN1 (WF_ARB) */ + {0x820f4000, 0xa1000, 0x0400}, /* WF_LMAC_TOP BN1 (WF_TMAC) */ + {0x820f5000, 0xa1400, 0x0800}, /* WF_LMAC_TOP BN1 (WF_RMAC) */ + {0x820f7000, 0xa1e00, 0x0200}, /* WF_LMAC_TOP BN1 (WF_DMA) */ + {0x820f9000, 0xa3400, 0x0200}, /* WF_LMAC_TOP BN1 (WF_WTBLOFF) */ + {0x820fa000, 0xa4000, 0x0200}, /* WF_LMAC_TOP BN1 (WF_ETBF) */ + {0x820fb000, 0xa4200, 0x0400}, /* WF_LMAC_TOP BN1 (WF_LPON) */ + {0x820fc000, 0xa4600, 0x0200}, /* WF_LMAC_TOP BN1 (WF_INT) */ + {0x820fd000, 0xa4800, 0x0800}, /* WF_LMAC_TOP BN1 (WF_MIB) */ + {0x820cc000, 0xa5000, 0x2000}, /* WF_LMAC_TOP BN1 (WF_MUCOP) */ + {0x820c4000, 0xa8000, 0x4000}, /* WF_LMAC_TOP (WF_UWTBL) */ + {0x81030000, 0xae000, 0x100}, /* WFSYS_AON part 1 */ + {0x81031000, 0xae100, 0x100}, /* WFSYS_AON part 2 */ + {0x81032000, 0xae200, 0x100}, /* WFSYS_AON part 3 */ + {0x81033000, 0xae300, 0x100}, /* WFSYS_AON part 4 */ + {0x81034000, 0xae400, 0x100}, /* WFSYS_AON part 5 */ + {0x80020000, 0xb0000, 0x10000}, /* WF_TOP_MISC_OFF */ + {0x81020000, 0xc0000, 0x10000}, /* WF_TOP_MISC_ON */ + {0x81040000, 0x120000, 0x1000}, /* WF_MCU_CFG_ON */ + {0x81050000, 0x121000, 0x1000}, /* WF_MCU_EINT */ + {0x81060000, 0x122000, 0x1000}, /* WF_MCU_GPT */ + {0x81070000, 0x123000, 0x1000}, /* WF_MCU_WDT */ + {0x80010000, 0x124000, 0x1000}, /* WF_AXIDMA */ + {0x7c020000, 0xd0000, 0x10000}, /* CONN_INFRA, wfdma for from CODA flow use */ + {0x7c060000, 0xe0000, 0x10000}, /* CONN_INFRA, conn_host_csr_top for from CODA flow use */ + {0x20020000, 0xd0000, 0x10000}, /* CONN_INFRA, wfdma */ + {0x20060000, 0xe0000, 0x10000}, /* CONN_INFRA, conn_host_csr_top */ + {0x7c000000, 0xf0000, 0x10000}, /* CONN_INFRA */ + {0x70020000, 0x1f0000, 0x9000}, /* PCIE remapping (AP2CONN) */ + {0x0, 0x0, 0x0}, /* imply end of search */ +}; + static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr) { u32 offset = FIELD_GET(MT_HIF_REMAP_L1_OFFSET, addr); u32 base = FIELD_GET(MT_HIF_REMAP_L1_BASE, addr); + u32 l1_mask, val; - dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, - MT_HIF_REMAP_L1_MASK, - FIELD_PREP(MT_HIF_REMAP_L1_MASK, base)); + if (is_mt7996(&dev->mt76)) { + l1_mask = MT_HIF_REMAP_L1_MASK_7996; + val = FIELD_PREP(MT_HIF_REMAP_L1_MASK_7996, base); + } else { + l1_mask = MT_HIF_REMAP_L1_MASK; + val = FIELD_PREP(MT_HIF_REMAP_L1_MASK, base); + } + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L1, l1_mask, val); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L1); @@ -151,18 +279,41 @@ static u32 mt7996_reg_map_l1(struct mt7996_dev *dev, u32 addr) static u32 mt7996_reg_map_l2(struct mt7996_dev *dev, u32 addr) { - u32 offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); - u32 base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); + u32 offset, base, l2_mask, val; - dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, - MT_HIF_REMAP_L2_MASK, - FIELD_PREP(MT_HIF_REMAP_L2_MASK, base)); + if (is_mt7990(&dev->mt76)) { + offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET_7990, addr); + base = FIELD_GET(MT_HIF_REMAP_L2_BASE_7990, addr); + l2_mask = MT_HIF_REMAP_L2_MASK_7990; + val = FIELD_PREP(MT_HIF_REMAP_L2_MASK_7990, base); + } else { + offset = FIELD_GET(MT_HIF_REMAP_L2_OFFSET, addr); + base = FIELD_GET(MT_HIF_REMAP_L2_BASE, addr); + l2_mask = MT_HIF_REMAP_L2_MASK; + val = FIELD_PREP(MT_HIF_REMAP_L2_MASK, base); + } + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_L2, l2_mask, val); /* use read to push write */ dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_L2); return MT_HIF_REMAP_BASE_L2 + offset; } +static u32 mt7996_reg_map_cbtop(struct mt7996_dev *dev, u32 addr) +{ + u32 offset = FIELD_GET(MT_HIF_REMAP_CBTOP_OFFSET, addr); + u32 base = FIELD_GET(MT_HIF_REMAP_CBTOP_BASE, addr); + + dev->bus_ops->rmw(&dev->mt76, MT_HIF_REMAP_CBTOP, + MT_HIF_REMAP_CBTOP_MASK, + FIELD_PREP(MT_HIF_REMAP_CBTOP_MASK, base)); + /* use read to push write */ + dev->bus_ops->rr(&dev->mt76, MT_HIF_REMAP_CBTOP); + + return MT_HIF_REMAP_BASE_CBTOP + offset; +} + static u32 __mt7996_reg_addr(struct mt7996_dev *dev, u32 addr) { int i; @@ -193,17 +344,20 @@ static u32 __mt7996_reg_remap_addr(struct mt7996_dev *dev, u32 addr) (addr >= MT_WFSYS1_PHY_START && addr <= MT_WFSYS1_PHY_END)) return mt7996_reg_map_l1(dev, addr); - if (dev_is_pci(dev->mt76.dev) && - ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || - addr >= MT_CBTOP2_PHY_START)) - return mt7996_reg_map_l1(dev, addr); - /* CONN_INFRA: covert to phyiscal addr and use layer 1 remap */ if (addr >= MT_INFRA_MCU_START && addr <= MT_INFRA_MCU_END) { addr = addr - MT_INFRA_MCU_START + MT_INFRA_BASE; return mt7996_reg_map_l1(dev, addr); } + if (dev_is_pci(dev->mt76.dev) && + ((addr >= MT_CBTOP1_PHY_START && addr <= MT_CBTOP1_PHY_END) || + addr >= MT_CBTOP2_PHY_START)) { + if (is_mt7990(&dev->mt76)) + return mt7996_reg_map_cbtop(dev, addr); + return mt7996_reg_map_l1(dev, addr); + } + return mt7996_reg_map_l2(dev, addr); } @@ -281,7 +435,7 @@ static int mt7996_mmio_wed_reset(struct mtk_wed_device *wed) if (test_and_set_bit(MT76_STATE_WED_RESET, &mphy->state)) return -EBUSY; - ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_L1, + ret = mt7996_mcu_set_ser(dev, UNI_CMD_SER_TRIGGER, UNI_CMD_SER_SET_RECOVER_FROM_ETH, mphy->band_idx); if (ret) goto out; @@ -323,6 +477,9 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, wed->wlan.base = devm_ioremap(dev->mt76.dev, pci_resource_start(pci_dev, 0), pci_resource_len(pci_dev, 0)); + if (!wed->wlan.base) + return -ENOMEM; + wed->wlan.phy_base = pci_resource_start(pci_dev, 0); if (hif2) { @@ -350,7 +507,7 @@ int mt7996_mmio_wed_init(struct mt7996_dev *dev, void *pdev_ptr, MT_RXQ_RING_BASE(MT7996_RXQ_BAND0) + MT7996_RXQ_BAND0 * MT_RING_SIZE; - wed->wlan.id = 0x7991; + wed->wlan.id = MT7996_DEVICE_ID_2; wed->wlan.tx_tbit[0] = ffs(MT_INT_TX_DONE_BAND2) - 1; } else { wed->wlan.hw_rro = dev->has_rro; /* default on */ @@ -443,18 +600,24 @@ static int mt7996_mmio_init(struct mt76_dev *mdev, spin_lock_init(&dev->reg_lock); switch (device_id) { - case 0x7990: + case MT7996_DEVICE_ID: dev->reg.base = mt7996_reg_base; dev->reg.offs_rev = mt7996_offs; dev->reg.map = mt7996_reg_map; dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map); break; - case 0x7992: + case MT7992_DEVICE_ID: dev->reg.base = mt7996_reg_base; dev->reg.offs_rev = mt7992_offs; dev->reg.map = mt7996_reg_map; dev->reg.map_size = ARRAY_SIZE(mt7996_reg_map); break; + case MT7990_DEVICE_ID: + dev->reg.base = mt7996_reg_base; + dev->reg.offs_rev = mt7990_offs; + dev->reg.map = mt7990_reg_map; + dev->reg.map_size = ARRAY_SIZE(mt7990_reg_map); + break; default: return -EINVAL; } @@ -618,9 +781,6 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, .rx_skb = mt7996_queue_rx_skb, .rx_check = mt7996_rx_check, .rx_poll_complete = mt7996_rx_poll_complete, - .sta_add = mt7996_mac_sta_add, - .sta_event = mt7996_mac_sta_event, - .sta_remove = mt7996_mac_sta_remove, .update_survey = mt7996_update_channel, .set_channel = mt7996_set_channel, .vif_link_add = mt7996_vif_link_add, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h index 29fabb9b04ae..1ad6bc046f7c 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/mt7996.h @@ -14,7 +14,7 @@ #define MT7996_MAX_RADIOS 3 #define MT7996_MAX_INTERFACES 19 /* per-band */ #define MT7996_MAX_WMM_SETS 4 -#define MT7996_WTBL_BMC_SIZE (is_mt7992(&dev->mt76) ? 32 : 64) +#define MT7996_WTBL_BMC_SIZE (is_mt7996(&dev->mt76) ? 64 : 32) #define MT7996_WTBL_RESERVED (mt7996_wtbl_size(dev) - 1) #define MT7996_WTBL_STA (MT7996_WTBL_RESERVED - \ mt7996_max_interface_num(dev)) @@ -29,6 +29,16 @@ #define MT7996_RX_RING_SIZE 1536 #define MT7996_RX_MCU_RING_SIZE 512 #define MT7996_RX_MCU_RING_SIZE_WA 1024 +/* scatter-gather of mcu event is not supported in connac3 */ +#define MT7996_RX_MCU_BUF_SIZE (2048 + \ + SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) + +#define MT7996_DEVICE_ID 0x7990 +#define MT7996_DEVICE_ID_2 0x7991 +#define MT7992_DEVICE_ID 0x7992 +#define MT7992_DEVICE_ID_2 0x799a +#define MT7990_DEVICE_ID 0x7993 +#define MT7990_DEVICE_ID_2 0x799b #define MT7996_FIRMWARE_WA "mediatek/mt7996/mt7996_wa.bin" #define MT7996_FIRMWARE_WM "mediatek/mt7996/mt7996_wm.bin" @@ -50,6 +60,11 @@ #define MT7992_FIRMWARE_DSP_23 "mediatek/mt7996/mt7992_dsp_23.bin" #define MT7992_ROM_PATCH_23 "mediatek/mt7996/mt7992_rom_patch_23.bin" +#define MT7990_FIRMWARE_WA "" +#define MT7990_FIRMWARE_WM "mediatek/mt7996/mt7990_wm.bin" +#define MT7990_FIRMWARE_DSP "" +#define MT7990_ROM_PATCH "mediatek/mt7996/mt7990_rom_patch.bin" + #define MT7996_EEPROM_DEFAULT "mediatek/mt7996/mt7996_eeprom.bin" #define MT7996_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7996_eeprom_2i5i6i.bin" #define MT7996_EEPROM_DEFAULT_233 "mediatek/mt7996/mt7996_eeprom_233.bin" @@ -61,6 +76,9 @@ #define MT7992_EEPROM_DEFAULT_23 "mediatek/mt7996/mt7992_eeprom_23.bin" #define MT7992_EEPROM_DEFAULT_23_INT "mediatek/mt7996/mt7992_eeprom_23_2i5i.bin" +#define MT7990_EEPROM_DEFAULT "mediatek/mt7996/mt7990_eeprom.bin" +#define MT7990_EEPROM_DEFAULT_INT "mediatek/mt7996/mt7990_eeprom_2i5i.bin" + #define MT7996_EEPROM_SIZE 7680 #define MT7996_EEPROM_BLOCK_SIZE 16 #define MT7996_TOKEN_SIZE 16384 @@ -131,6 +149,10 @@ enum mt7992_var_type { MT7992_VAR_TYPE_23, }; +enum mt7990_var_type { + MT7990_VAR_TYPE_23, +}; + enum mt7996_fem_type { MT7996_FEM_EXT, MT7996_FEM_INT, @@ -165,6 +187,8 @@ enum mt7996_rxq_id { MT7996_RXQ_TXFREE1 = 9, MT7996_RXQ_TXFREE2 = 7, MT7996_RXQ_RRO_IND = 0, + MT7990_RXQ_TXFREE0 = 6, + MT7990_RXQ_TXFREE1 = 7, }; struct mt7996_twt_flow { @@ -185,10 +209,10 @@ struct mt7996_twt_flow { DECLARE_EWMA(avg_signal, 10, 8) -struct mt7996_sta { +struct mt7996_sta_link { struct mt76_wcid wcid; /* must be first */ - struct mt7996_vif *vif; + struct mt7996_sta *sta; struct list_head rc_list; u32 airtime_ac[8]; @@ -204,12 +228,22 @@ struct mt7996_sta { u8 flowid_mask; struct mt7996_twt_flow flow[MT7996_MAX_STA_TWT_AGRT]; } twt; + + struct rcu_head rcu_head; +}; + +struct mt7996_sta { + struct mt7996_sta_link deflink; /* must be first */ + struct mt7996_sta_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS]; + u8 deflink_id; + + struct mt7996_vif *vif; }; struct mt7996_vif_link { struct mt76_vif_link mt76; /* must be first */ - struct mt7996_sta sta; + struct mt7996_sta_link msta_link; struct mt7996_phy *phy; struct ieee80211_tx_queue_params queue_params[IEEE80211_NUM_ACS]; @@ -271,8 +305,6 @@ struct mt7996_phy { s16 coverage_class; u8 slottime; - u8 rdd_state; - u16 beacon_rate; u32 rx_ampdu_ts; @@ -283,6 +315,7 @@ struct mt7996_phy { struct mt76_channel_state state_ts; u16 orig_chainmask; + u16 orig_antenna_mask; bool has_aux_rx; bool counter_reset; @@ -395,10 +428,10 @@ enum { __MT_WFDMA_MAX, }; -enum { - MT_RX_SEL0, - MT_RX_SEL1, - MT_RX_SEL2, /* monitor chain */ +enum rdd_idx { + MT_RDD_IDX_BAND2, /* RDD idx for band idx 2 */ + MT_RDD_IDX_BAND1, /* RDD idx for band idx 1 */ + MT_RDD_IDX_BACKGROUND, /* RDD idx for background chain */ }; enum mt7996_rdd_cmd { @@ -417,6 +450,21 @@ enum mt7996_rdd_cmd { RDD_IRQ_OFF, }; +static inline int +mt7996_get_rdd_idx(struct mt7996_phy *phy, bool is_background) +{ + if (!phy->mt76->cap.has_5ghz) + return -1; + + if (is_background) + return MT_RDD_IDX_BACKGROUND; + + if (phy->mt76->band_idx == MT_BAND2) + return MT_RDD_IDX_BAND2; + + return MT_RDD_IDX_BAND1; +} + static inline struct mt7996_dev * mt7996_hw_dev(struct ieee80211_hw *hw) { @@ -451,31 +499,12 @@ mt7996_phy3(struct mt7996_dev *dev) static inline bool mt7996_band_valid(struct mt7996_dev *dev, u8 band) { - if (is_mt7992(&dev->mt76)) + if (!is_mt7996(&dev->mt76)) return band <= MT_BAND1; return band <= MT_BAND2; } -static inline bool -mt7996_has_background_radar(struct mt7996_dev *dev) -{ - switch (mt76_chip(&dev->mt76)) { - case 0x7990: - if (dev->var.type == MT7996_VAR_TYPE_233) - return false; - break; - case 0x7992: - if (dev->var.type == MT7992_VAR_TYPE_23) - return false; - break; - default: - return false; - } - - return true; -} - static inline struct mt7996_phy * mt7996_band_phy(struct mt7996_dev *dev, enum nl80211_band band) { @@ -525,7 +554,7 @@ struct mt7996_dev *mt7996_mmio_probe(struct device *pdev, void __iomem *mem_base, u32 device_id); void mt7996_wfsys_reset(struct mt7996_dev *dev); irqreturn_t mt7996_irq_handler(int irq, void *dev_instance); -u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif *mvif); +u64 __mt7996_get_tsf(struct ieee80211_hw *hw, struct mt7996_vif_link *link); int mt7996_register_device(struct mt7996_dev *dev); void mt7996_unregister_device(struct mt7996_dev *dev); int mt7996_vif_link_add(struct mt76_phy *mphy, struct ieee80211_vif *vif, @@ -539,6 +568,7 @@ int mt7996_eeprom_parse_hw_cap(struct mt7996_dev *dev, struct mt7996_phy *phy); int mt7996_eeprom_get_target_power(struct mt7996_dev *dev, struct ieee80211_channel *chan); s8 mt7996_eeprom_get_power_delta(struct mt7996_dev *dev, int band); +bool mt7996_eeprom_has_background_radar(struct mt7996_dev *dev); int mt7996_dma_init(struct mt7996_dev *dev); void mt7996_dma_reset(struct mt7996_dev *dev, bool force); void mt7996_dma_prefetch(struct mt7996_dev *dev); @@ -553,7 +583,7 @@ int mt7996_run(struct mt7996_phy *phy); int mt7996_mcu_init(struct mt7996_dev *dev); int mt7996_mcu_init_firmware(struct mt7996_dev *dev); int mt7996_mcu_twt_agrt_update(struct mt7996_dev *dev, - struct mt7996_vif *mvif, + struct mt7996_vif_link *link, struct mt7996_twt_flow *flow, int cmd); int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, @@ -561,35 +591,52 @@ int mt7996_mcu_add_dev_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct mt76_vif_link *mlink, bool enable); int mt7996_mcu_add_bss_info(struct mt7996_phy *phy, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, - struct mt76_vif_link *mlink, int enable); -int mt7996_mcu_add_sta(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct mt76_vif_link *mlink, - struct ieee80211_sta *sta, int conn_state, bool newly); + struct mt76_vif_link *mlink, + struct mt7996_sta_link *msta_link, int enable); +int mt7996_mcu_add_sta(struct mt7996_dev *dev, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + int conn_state, bool newly); +int mt7996_mcu_teardown_mld_sta(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); int mt7996_mcu_add_tx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, - bool add); + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, bool enable); int mt7996_mcu_add_rx_ba(struct mt7996_dev *dev, struct ieee80211_ampdu_params *params, - bool add); + struct mt7996_vif_link *link, bool enable); int mt7996_mcu_update_bss_color(struct mt7996_dev *dev, struct mt76_vif_link *mlink, struct cfg80211_he_bss_color *he_bss_color); int mt7996_mcu_add_beacon(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); int mt7996_mcu_beacon_inband_discov(struct mt7996_dev *dev, - struct ieee80211_vif *vif, u32 changed); -int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct mt7996_vif_link *link, u32 changed); +int mt7996_mcu_add_obss_spr(struct mt7996_phy *phy, + struct mt7996_vif_link *link, struct ieee80211_he_obss_pd *he_obss_pd); -int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, bool changed); +int mt7996_mcu_add_rate_ctrl(struct mt7996_dev *dev, + struct ieee80211_vif *vif, + struct ieee80211_bss_conf *link_conf, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, bool changed); int mt7996_set_channel(struct mt76_phy *mphy); int mt7996_mcu_set_chan_info(struct mt7996_phy *phy, u16 tag); int mt7996_mcu_set_tx(struct mt7996_dev *dev, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf); int mt7996_mcu_set_fixed_rate_ctrl(struct mt7996_dev *dev, void *data, u16 version); -int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, void *data, u32 field); +int mt7996_mcu_set_fixed_field(struct mt7996_dev *dev, + struct ieee80211_link_sta *link_sta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, + void *data, u32 field); int mt7996_mcu_set_eeprom(struct mt7996_dev *dev); int mt7996_mcu_get_eeprom(struct mt7996_dev *dev, u32 offset, u8 *buf, u32 buf_len); int mt7996_mcu_get_eeprom_free_block(struct mt7996_dev *dev, u8 *block_num); @@ -610,8 +657,7 @@ int mt7996_mcu_get_temperature(struct mt7996_phy *phy); int mt7996_mcu_set_thermal_throttling(struct mt7996_phy *phy, u8 state); int mt7996_mcu_set_thermal_protect(struct mt7996_phy *phy, bool enable); int mt7996_mcu_set_txpower_sku(struct mt7996_phy *phy); -int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 index, - u8 rx_sel, u8 val); +int mt7996_mcu_rdd_cmd(struct mt7996_dev *dev, int cmd, u8 rdd_idx, u8 val); int mt7996_mcu_rdd_background_enable(struct mt7996_phy *phy, struct cfg80211_chan_def *chandef); int mt7996_mcu_set_fixed_rate_table(struct mt7996_phy *phy, u8 table_idx, @@ -677,32 +723,30 @@ static inline u16 mt7996_rx_chainmask(struct mt7996_phy *phy) return tx_chainmask | (BIT(fls(tx_chainmask)) * phy->has_aux_rx); } +static inline bool mt7996_has_wa(struct mt7996_dev *dev) +{ + return !is_mt7990(&dev->mt76); +} + void mt7996_mac_init(struct mt7996_dev *dev); u32 mt7996_mac_wtbl_lmac_addr(struct mt7996_dev *dev, u16 wcid, u8 dw); bool mt7996_mac_wtbl_update(struct mt7996_dev *dev, int idx, u32 mask); void mt7996_mac_reset_counters(struct mt7996_phy *phy); void mt7996_mac_cca_stats_reset(struct mt7996_phy *phy); void mt7996_mac_enable_nf(struct mt7996_dev *dev, u8 band); -void mt7996_mac_enable_rtscts(struct mt7996_dev *dev, - struct ieee80211_vif *vif, bool enable); void mt7996_mac_write_txwi(struct mt7996_dev *dev, __le32 *txwi, struct sk_buff *skb, struct mt76_wcid *wcid, struct ieee80211_key_conf *key, int pid, enum mt76_txq_id qid, u32 changed); void mt7996_mac_set_coverage_class(struct mt7996_phy *phy); -int mt7996_mac_sta_add(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); -int mt7996_mac_sta_event(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, enum mt76_sta_event ev); -void mt7996_mac_sta_remove(struct mt76_dev *mdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); void mt7996_mac_work(struct work_struct *work); void mt7996_mac_reset_work(struct work_struct *work); void mt7996_mac_dump_work(struct work_struct *work); void mt7996_mac_sta_rc_work(struct work_struct *work); void mt7996_mac_update_stats(struct mt7996_phy *phy); void mt7996_mac_twt_teardown_flow(struct mt7996_dev *dev, - struct mt7996_sta *msta, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, u8 flowid); void mt7996_mac_add_twt_setup(struct ieee80211_hw *hw, struct ieee80211_sta *sta, @@ -727,11 +771,14 @@ bool mt7996_debugfs_rx_log(struct mt7996_dev *dev, const void *data, int len); int mt7996_mcu_add_key(struct mt76_dev *dev, struct ieee80211_vif *vif, struct ieee80211_key_conf *key, int mcu_cmd, struct mt76_wcid *wcid, enum set_key_cmd cmd); -int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, struct ieee80211_vif *vif, +int mt7996_mcu_bcn_prot_enable(struct mt7996_dev *dev, + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link, struct ieee80211_key_conf *key); int mt7996_mcu_wtbl_update_hdr_trans(struct mt7996_dev *dev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta); + struct mt7996_vif_link *link, + struct mt7996_sta_link *msta_link); int mt7996_mcu_cp_support(struct mt7996_dev *dev, u8 mode); #ifdef CONFIG_MAC80211_DEBUGFS void mt7996_sta_add_debugfs(struct ieee80211_hw *hw, struct ieee80211_vif *vif, diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c index 04056181368a..19e99bc1c6c4 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7996/pci.c @@ -16,14 +16,16 @@ static DEFINE_SPINLOCK(hif_lock); static u32 hif_idx; static const struct pci_device_id mt7996_pci_device_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7990) }, - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7992) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID) }, { }, }; static const struct pci_device_id mt7996_hif_device_table[] = { - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x7991) }, - { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, 0x799a) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2) }, + { PCI_DEVICE(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2) }, { }, }; @@ -63,8 +65,9 @@ static struct mt7996_hif *mt7996_pci_init_hif2(struct pci_dev *pdev) { hif_idx++; - if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x7991, NULL) && - !pci_get_device(PCI_VENDOR_ID_MEDIATEK, 0x799a, NULL)) + if (!pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7996_DEVICE_ID_2, NULL) && + !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7992_DEVICE_ID_2, NULL) && + !pci_get_device(PCI_VENDOR_ID_MEDIATEK, MT7990_DEVICE_ID_2, NULL)) return NULL; writel(hif_idx | MT_PCIE_RECOG_ID_SEM, @@ -121,7 +124,9 @@ static int mt7996_pci_probe(struct pci_dev *pdev, mt76_pci_disable_aspm(pdev); - if (id->device == 0x7991 || id->device == 0x799a) + if (id->device == MT7996_DEVICE_ID_2 || + id->device == MT7992_DEVICE_ID_2 || + id->device == MT7990_DEVICE_ID_2) return mt7996_pci_hif2_probe(pdev); dev = mt7996_mmio_probe(&pdev->dev, pcim_iomap_table(pdev)[0], @@ -256,3 +261,5 @@ MODULE_FIRMWARE(MT7992_FIRMWARE_WA); MODULE_FIRMWARE(MT7992_FIRMWARE_WM); MODULE_FIRMWARE(MT7992_FIRMWARE_DSP); MODULE_FIRMWARE(MT7992_ROM_PATCH); +MODULE_FIRMWARE(MT7990_FIRMWARE_WM); +MODULE_FIRMWARE(MT7990_ROM_PATCH); diff --git a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h index 1876a968c92d..e942c0058731 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7996/regs.h +++ b/drivers/net/wireless/mediatek/mt76/mt7996/regs.h @@ -64,6 +64,17 @@ enum offs_rev { MIB_BSCR7, MIB_BSCR17, MIB_TRDR1, + HIF_REMAP_L1, + HIF_REMAP_BASE_L1, + HIF_REMAP_L2, + HIF_REMAP_BASE_L2, + CBTOP1_PHY_END, + INFRA_MCU_END, + WTBLON_WDUCR, + WTBL_UPDATE, + WTBL_ITCR, + WTBL_ITCR0, + WTBL_ITCR1, __MT_OFFS_MAX, }; @@ -291,19 +302,19 @@ enum offs_rev { /* WTBLON TOP */ #define MT_WTBLON_TOP_BASE 0x820d4000 #define MT_WTBLON_TOP(ofs) (MT_WTBLON_TOP_BASE + (ofs)) -#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(0x370) +#define MT_WTBLON_TOP_WDUCR MT_WTBLON_TOP(__OFFS(WTBLON_WDUCR)) #define MT_WTBLON_TOP_WDUCR_GROUP GENMASK(4, 0) -#define MT_WTBL_UPDATE MT_WTBLON_TOP(0x380) +#define MT_WTBL_UPDATE MT_WTBLON_TOP(__OFFS(WTBL_UPDATE)) #define MT_WTBL_UPDATE_WLAN_IDX GENMASK(11, 0) #define MT_WTBL_UPDATE_ADM_COUNT_CLEAR BIT(14) #define MT_WTBL_UPDATE_BUSY BIT(31) -#define MT_WTBL_ITCR MT_WTBLON_TOP(0x3b0) +#define MT_WTBL_ITCR MT_WTBLON_TOP(__OFFS(WTBL_ITCR)) #define MT_WTBL_ITCR_WR BIT(16) #define MT_WTBL_ITCR_EXEC BIT(31) -#define MT_WTBL_ITDR0 MT_WTBLON_TOP(0x3b8) -#define MT_WTBL_ITDR1 MT_WTBLON_TOP(0x3bc) +#define MT_WTBL_ITDR0 MT_WTBLON_TOP(__OFFS(WTBL_ITCR0)) +#define MT_WTBL_ITDR1 MT_WTBLON_TOP(__OFFS(WTBL_ITCR1)) #define MT_WTBL_SPE_IDX_SEL BIT(6) /* WTBL */ @@ -483,7 +494,7 @@ enum offs_rev { #define MT_MCUQ_EXT_CTRL(q) (MT_Q_BASE(q) + 0x600 + \ MT_MCUQ_ID(q) * 0x4) -#define MT_RXQ_BAND1_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \ +#define MT_RXQ_EXT_CTRL(q) (MT_Q_BASE(__RXQ(q)) + 0x680 + \ MT_RXQ_ID(q) * 0x4) #define MT_TXQ_EXT_CTRL(q) (MT_Q_BASE(__TXQ(q)) + 0x600 + \ MT_TXQ_ID(q) * 0x4) @@ -504,6 +515,8 @@ enum offs_rev { #define MT_INT_RX_DONE_WA_TRI BIT(3) #define MT_INT_RX_TXFREE_MAIN BIT(17) #define MT_INT_RX_TXFREE_TRI BIT(15) +#define MT_INT_RX_TXFREE_BAND0_MT7990 BIT(14) +#define MT_INT_RX_TXFREE_BAND1_MT7990 BIT(15) #define MT_INT_RX_DONE_BAND2_EXT BIT(23) #define MT_INT_RX_TXFREE_EXT BIT(26) #define MT_INT_MCU_CMD BIT(29) @@ -576,27 +589,39 @@ enum offs_rev { #define MT_MCU_CMD_WDT_MASK GENMASK(31, 30) /* l1/l2 remap */ -#define MT_HIF_REMAP_L1 0x155024 -#define MT_HIF_REMAP_L1_MASK GENMASK(31, 16) +#define CONN_BUS_CR_VON_BASE 0x155000 +#define MT_HIF_REMAP_L1 (CONN_BUS_CR_VON_BASE + __OFFS(HIF_REMAP_L1)) +#define MT_HIF_REMAP_L1_MASK_7996 GENMASK(31, 16) +#define MT_HIF_REMAP_L1_MASK GENMASK(15, 0) #define MT_HIF_REMAP_L1_OFFSET GENMASK(15, 0) #define MT_HIF_REMAP_L1_BASE GENMASK(31, 16) -#define MT_HIF_REMAP_BASE_L1 0x130000 +#define MT_HIF_REMAP_BASE_L1 __OFFS(HIF_REMAP_BASE_L1) -#define MT_HIF_REMAP_L2 0x1b4 +#define MT_HIF_REMAP_L2 __OFFS(HIF_REMAP_L2) #define MT_HIF_REMAP_L2_MASK GENMASK(19, 0) #define MT_HIF_REMAP_L2_OFFSET GENMASK(11, 0) #define MT_HIF_REMAP_L2_BASE GENMASK(31, 12) -#define MT_HIF_REMAP_BASE_L2 0x1000 +#define MT_HIF_REMAP_L2_MASK_7990 GENMASK(15, 0) +#define MT_HIF_REMAP_L2_OFFSET_7990 GENMASK(15, 0) +#define MT_HIF_REMAP_L2_BASE_7990 GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_L2 __OFFS(HIF_REMAP_BASE_L2) + +/* for mt7990 only */ +#define MT_HIF_REMAP_CBTOP 0x1f6554 +#define MT_HIF_REMAP_CBTOP_MASK GENMASK(15, 0) +#define MT_HIF_REMAP_CBTOP_OFFSET GENMASK(15, 0) +#define MT_HIF_REMAP_CBTOP_BASE GENMASK(31, 16) +#define MT_HIF_REMAP_BASE_CBTOP 0x1c0000 #define MT_INFRA_BASE 0x18000000 #define MT_WFSYS0_PHY_START 0x18400000 #define MT_WFSYS1_PHY_START 0x18800000 #define MT_WFSYS1_PHY_END 0x18bfffff #define MT_CBTOP1_PHY_START 0x70000000 -#define MT_CBTOP1_PHY_END 0x77ffffff +#define MT_CBTOP1_PHY_END __OFFS(CBTOP1_PHY_END) #define MT_CBTOP2_PHY_START 0xf0000000 #define MT_INFRA_MCU_START 0x7c000000 -#define MT_INFRA_MCU_END 0x7c3fffff +#define MT_INFRA_MCU_END __OFFS(INFRA_MCU_END) /* FW MODE SYNC */ #define MT_FW_ASSERT_CNT 0x02208274 diff --git a/drivers/net/wireless/mediatek/mt76/scan.c b/drivers/net/wireless/mediatek/mt76/scan.c index 1c4f9deaaada..9b20ccbeb8cf 100644 --- a/drivers/net/wireless/mediatek/mt76/scan.c +++ b/drivers/net/wireless/mediatek/mt76/scan.c @@ -52,11 +52,6 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid) ether_addr_copy(hdr->addr3, req->bssid); } - info = IEEE80211_SKB_CB(skb); - if (req->no_cck) - info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; - info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK; - if (req->ie_len) skb_put_data(skb, req->ie, req->ie_len); @@ -64,10 +59,20 @@ mt76_scan_send_probe(struct mt76_dev *dev, struct cfg80211_ssid *ssid) skb_set_queue_mapping(skb, IEEE80211_AC_VO); rcu_read_lock(); - if (ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL)) - mt76_tx(phy, NULL, mvif->wcid, skb); - else + + if (!ieee80211_tx_prepare_skb(phy->hw, vif, skb, band, NULL)) { ieee80211_free_txskb(phy->hw, skb); + goto out; + } + + info = IEEE80211_SKB_CB(skb); + if (req->no_cck) + info->flags |= IEEE80211_TX_CTL_NO_CCK_RATE; + info->control.flags |= IEEE80211_TX_CTRL_DONT_USE_RATE_MASK; + + mt76_tx(phy, NULL, mvif->wcid, skb); + +out: rcu_read_unlock(); } diff --git a/drivers/net/wireless/mediatek/mt76/tx.c b/drivers/net/wireless/mediatek/mt76/tx.c index af0c50c983ec..513916469ca2 100644 --- a/drivers/net/wireless/mediatek/mt76/tx.c +++ b/drivers/net/wireless/mediatek/mt76/tx.c @@ -100,7 +100,8 @@ __mt76_tx_status_skb_done(struct mt76_dev *dev, struct sk_buff *skb, u8 flags, return; /* Tx status can be unreliable. if it fails, mark the frame as ACKed */ - if (flags & MT_TX_CB_TXS_FAILED) { + if (flags & MT_TX_CB_TXS_FAILED && + (dev->drv->drv_flags & MT_DRV_IGNORE_TXS_FAILED)) { info->status.rates[0].count = 0; info->status.rates[0].idx = -1; info->flags |= IEEE80211_TX_STAT_ACK; diff --git a/drivers/net/wireless/microchip/wilc1000/hif.c b/drivers/net/wireless/microchip/wilc1000/hif.c index bba53307b960..cb46a39ef757 100644 --- a/drivers/net/wireless/microchip/wilc1000/hif.c +++ b/drivers/net/wireless/microchip/wilc1000/hif.c @@ -643,7 +643,7 @@ static inline void host_int_parse_assoc_resp_info(struct wilc_vif *vif, } } - del_timer(&hif_drv->connect_timer); + timer_delete(&hif_drv->connect_timer); conn_info->conn_result(CONN_DISCONN_EVENT_CONN_RESP, mac_status, hif_drv->conn_info.priv); @@ -669,7 +669,7 @@ void wilc_handle_disconnect(struct wilc_vif *vif) struct host_if_drv *hif_drv = vif->hif_drv; if (hif_drv->usr_scan_req.scan_result) { - del_timer(&hif_drv->scan_timer); + timer_delete(&hif_drv->scan_timer); handle_scan_done(vif, SCAN_EVENT_ABORTED); } @@ -713,7 +713,7 @@ static void handle_rcvd_gnrl_async_info(struct work_struct *work) if (hif_drv->hif_state == HOST_IF_CONNECTED) { wilc_handle_disconnect(vif); } else if (hif_drv->usr_scan_req.scan_result) { - del_timer(&hif_drv->scan_timer); + timer_delete(&hif_drv->scan_timer); handle_scan_done(vif, SCAN_EVENT_ABORTED); } } @@ -746,7 +746,7 @@ int wilc_disconnect(struct wilc_vif *vif) conn_info = &hif_drv->conn_info; if (scan_req->scan_result) { - del_timer(&hif_drv->scan_timer); + timer_delete(&hif_drv->scan_timer); scan_req->scan_result(SCAN_EVENT_ABORTED, NULL, scan_req->priv); scan_req->scan_result = NULL; } @@ -754,7 +754,7 @@ int wilc_disconnect(struct wilc_vif *vif) if (conn_info->conn_result) { if (hif_drv->hif_state == HOST_IF_WAITING_CONN_RESP || hif_drv->hif_state == HOST_IF_EXTERNAL_AUTH) - del_timer(&hif_drv->connect_timer); + timer_delete(&hif_drv->connect_timer); conn_info->conn_result(CONN_DISCONN_EVENT_DISCONN_NOTIF, 0, conn_info->priv); @@ -959,7 +959,7 @@ static void listen_timer_cb(struct timer_list *t) int result; struct host_if_msg *msg; - del_timer(&vif->hif_drv->remain_on_ch_timer); + timer_delete(&vif->hif_drv->remain_on_ch_timer); msg = wilc_alloc_work(vif, wilc_handle_listen_state_expired, false); if (IS_ERR(msg)) @@ -1066,7 +1066,7 @@ static void handle_scan_complete(struct work_struct *work) { struct host_if_msg *msg = container_of(work, struct host_if_msg, work); - del_timer(&msg->vif->hif_drv->scan_timer); + timer_delete(&msg->vif->hif_drv->scan_timer); handle_scan_done(msg->vif, SCAN_EVENT_DONE); @@ -1551,7 +1551,7 @@ int wilc_deinit(struct wilc_vif *vif) timer_shutdown_sync(&hif_drv->scan_timer); timer_shutdown_sync(&hif_drv->connect_timer); - del_timer_sync(&vif->periodic_rssi); + timer_delete_sync(&vif->periodic_rssi); timer_shutdown_sync(&hif_drv->remain_on_ch_timer); if (hif_drv->usr_scan_req.scan_result) { @@ -1718,7 +1718,7 @@ int wilc_listen_state_expired(struct wilc_vif *vif, u64 cookie) return -EFAULT; } - del_timer(&vif->hif_drv->remain_on_ch_timer); + timer_delete(&vif->hif_drv->remain_on_ch_timer); return wilc_handle_roc_expired(vif, cookie); } diff --git a/drivers/net/wireless/purelifi/plfxlc/mac.c b/drivers/net/wireless/purelifi/plfxlc/mac.c index eae93efa6150..82d1bf7edba2 100644 --- a/drivers/net/wireless/purelifi/plfxlc/mac.c +++ b/drivers/net/wireless/purelifi/plfxlc/mac.c @@ -102,7 +102,6 @@ int plfxlc_mac_init_hw(struct ieee80211_hw *hw) void plfxlc_mac_release(struct plfxlc_mac *mac) { plfxlc_chip_release(&mac->chip); - lockdep_assert_held(&mac->lock); } int plfxlc_op_start(struct ieee80211_hw *hw) diff --git a/drivers/net/wireless/purelifi/plfxlc/usb.c b/drivers/net/wireless/purelifi/plfxlc/usb.c index 56d1139ba8bc..c2a1234b59db 100644 --- a/drivers/net/wireless/purelifi/plfxlc/usb.c +++ b/drivers/net/wireless/purelifi/plfxlc/usb.c @@ -503,8 +503,10 @@ int plfxlc_usb_wreq_async(struct plfxlc_usb *usb, const u8 *buffer, (void *)buffer, buffer_len, complete_fn, context); r = usb_submit_urb(urb, GFP_ATOMIC); - if (r) + if (r) { + usb_free_urb(urb); dev_err(&udev->dev, "Async write submit failed (%d)\n", r); + } return r; } @@ -714,8 +716,8 @@ static void disconnect(struct usb_interface *intf) mac = plfxlc_hw_mac(hw); usb = &mac->chip.usb; - del_timer_sync(&usb->tx.tx_retry_timer); - del_timer_sync(&usb->sta_queue_cleanup); + timer_delete_sync(&usb->tx.tx_retry_timer); + timer_delete_sync(&usb->sta_queue_cleanup); ieee80211_unregister_hw(hw); diff --git a/drivers/net/wireless/quantenna/qtnfmac/qlink.h b/drivers/net/wireless/quantenna/qtnfmac/qlink.h index 674461fa7fb3..eae35b678952 100644 --- a/drivers/net/wireless/quantenna/qtnfmac/qlink.h +++ b/drivers/net/wireless/quantenna/qtnfmac/qlink.h @@ -1510,10 +1510,15 @@ enum qlink_tlv_id { }; struct qlink_tlv_hdr { - __le16 type; - __le16 len; + /* New members MUST be added within the struct_group() macro below. */ + __struct_group(qlink_tlv_hdr_fixed, __hdr, __packed, + __le16 type; + __le16 len; + ); u8 val[]; } __packed; +static_assert(offsetof(struct qlink_tlv_hdr, val) == sizeof(struct qlink_tlv_hdr_fixed), + "struct member likely outside of __struct_group()"); struct qlink_iface_limit { __le16 max_num; @@ -1567,7 +1572,7 @@ enum qlink_reg_rule_flags { * @dfs_cac_ms: DFS CAC period. */ struct qlink_tlv_reg_rule { - struct qlink_tlv_hdr hdr; + struct qlink_tlv_hdr_fixed hdr; __le32 start_freq_khz; __le32 end_freq_khz; __le32 max_bandwidth_khz; @@ -1606,7 +1611,7 @@ enum qlink_dfs_state { * @channel: ieee80211 channel settings. */ struct qlink_tlv_channel { - struct qlink_tlv_hdr hdr; + struct qlink_tlv_hdr_fixed hdr; struct qlink_channel chan; } __packed; @@ -1618,7 +1623,7 @@ struct qlink_tlv_channel { * @chan: channel definition data. */ struct qlink_tlv_chandef { - struct qlink_tlv_hdr hdr; + struct qlink_tlv_hdr_fixed hdr; struct qlink_chandef chdef; } __packed; @@ -1643,7 +1648,7 @@ enum qlink_ie_set_type { * @ie_data: IEs data. */ struct qlink_tlv_ie_set { - struct qlink_tlv_hdr hdr; + struct qlink_tlv_hdr_fixed hdr; u8 type; u8 flags; u8 rsvd[2]; @@ -1657,7 +1662,7 @@ struct qlink_tlv_ie_set { * @ie_data: IEs data. */ struct qlink_tlv_ext_ie { - struct qlink_tlv_hdr hdr; + struct qlink_tlv_hdr_fixed hdr; u8 eid_ext; u8 rsvd[3]; u8 ie_data[]; @@ -1678,7 +1683,7 @@ struct qlink_sband_iftype_data { * @iftype_data: interface type data entries. */ struct qlink_tlv_iftype_data { - struct qlink_tlv_hdr hdr; + struct qlink_tlv_hdr_fixed hdr; u8 n_iftype_data; u8 rsvd[3]; struct qlink_sband_iftype_data iftype_data[]; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c index e5f553a1ea24..b7ea606bda08 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800lib.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800lib.c @@ -9393,7 +9393,7 @@ static void rt2800_loft_search(struct rt2x00_dev *rt2x00dev, u8 ch_idx, p0, p1, pf, idx0, idx1, ibit); if (bidx != 5 && pf <= p0 && pf < p1) { - idxf[iorq] = idxf[iorq]; + /* no need to adjust idxf[] */; } else if (p0 < p1) { pf = p0; idxf[iorq] = idx0 & 0x3F; diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c index 5323acff962a..45775ecdf221 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800mmio.c @@ -842,7 +842,7 @@ int rt2800mmio_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Set txstatus timer function. */ - rt2x00dev->txstatus_timer.function = rt2800mmio_tx_sta_fifo_timeout; + hrtimer_update_function(&rt2x00dev->txstatus_timer, rt2800mmio_tx_sta_fifo_timeout); /* * Overwrite TX done handler diff --git a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c index 160bef79acdb..b51a23300ba2 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2800usb.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2800usb.c @@ -618,7 +618,7 @@ static int rt2800usb_probe_hw(struct rt2x00_dev *rt2x00dev) /* * Set txstatus timer function. */ - rt2x00dev->txstatus_timer.function = rt2800usb_tx_sta_fifo_timeout; + hrtimer_update_function(&rt2x00dev->txstatus_timer, rt2800usb_tx_sta_fifo_timeout); /* * Overwrite TX done handler diff --git a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c index 9e7d9dbe954c..432ddfac2c33 100644 --- a/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c +++ b/drivers/net/wireless/ralink/rt2x00/rt2x00dev.c @@ -1391,8 +1391,8 @@ int rt2x00lib_probe_dev(struct rt2x00_dev *rt2x00dev) mutex_init(&rt2x00dev->conf_mutex); INIT_LIST_HEAD(&rt2x00dev->bar_list); spin_lock_init(&rt2x00dev->bar_list_lock); - hrtimer_init(&rt2x00dev->txstatus_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_REL); + hrtimer_setup(&rt2x00dev->txstatus_timer, hrtimer_dummy_timeout, CLOCK_MONOTONIC, + HRTIMER_MODE_REL); set_bit(DEVICE_STATE_PRESENT, &rt2x00dev->flags); diff --git a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c index 0abb1b092bc2..73034e7e41d1 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/8192c.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/8192c.c @@ -644,6 +644,8 @@ struct rtl8xxxu_fileops rtl8192cu_fops = { .rx_agg_buf_size = 16000, .tx_desc_size = sizeof(struct rtl8xxxu_txdesc32), .rx_desc_size = sizeof(struct rtl8xxxu_rxdesc16), + .supports_ap = 1, + .max_macid_num = 32, .max_sec_cam_num = 32, .adda_1t_init = 0x0b1b25a0, .adda_1t_path_on = 0x0bdb25a0, diff --git a/drivers/net/wireless/realtek/rtl8xxxu/core.c b/drivers/net/wireless/realtek/rtl8xxxu/core.c index 4ce0c05c5129..569856ca677f 100644 --- a/drivers/net/wireless/realtek/rtl8xxxu/core.c +++ b/drivers/net/wireless/realtek/rtl8xxxu/core.c @@ -860,9 +860,10 @@ rtl8xxxu_writeN(struct rtl8xxxu_priv *priv, u16 addr, u8 *buf, u16 len) return len; write_error: - dev_info(&udev->dev, - "%s: Failed to write block at addr: %04x size: %04x\n", - __func__, addr, blocksize); + if (rtl8xxxu_debug & RTL8XXXU_DEBUG_REG_WRITE) + dev_info(&udev->dev, + "%s: Failed to write block at addr: %04x size: %04x\n", + __func__, addr, blocksize); return -EAGAIN; } @@ -4064,8 +4065,14 @@ static int rtl8xxxu_init_device(struct ieee80211_hw *hw) */ rtl8xxxu_write16(priv, REG_TRXFF_BNDY + 2, fops->trxff_boundary); - ret = rtl8xxxu_download_firmware(priv); - dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret); + for (int retry = 5; retry >= 0 ; retry--) { + ret = rtl8xxxu_download_firmware(priv); + dev_dbg(dev, "%s: download_firmware %i\n", __func__, ret); + if (ret != -EAGAIN) + break; + if (retry) + dev_dbg(dev, "%s: retry firmware download\n", __func__); + } if (ret) goto exit; ret = rtl8xxxu_start_firmware(priv); diff --git a/drivers/net/wireless/realtek/rtlwifi/base.c b/drivers/net/wireless/realtek/rtlwifi/base.c index ff61867d142f..6189edc1d8d7 100644 --- a/drivers/net/wireless/realtek/rtlwifi/base.c +++ b/drivers/net/wireless/realtek/rtlwifi/base.c @@ -473,7 +473,7 @@ void rtl_deinit_deferred_work(struct ieee80211_hw *hw, bool ips_wq) { struct rtl_priv *rtlpriv = rtl_priv(hw); - del_timer_sync(&rtlpriv->works.watchdog_timer); + timer_delete_sync(&rtlpriv->works.watchdog_timer); cancel_delayed_work_sync(&rtlpriv->works.watchdog_wq); if (ips_wq) diff --git a/drivers/net/wireless/realtek/rtlwifi/core.c b/drivers/net/wireless/realtek/rtlwifi/core.c index 7537f04b1930..819cf519e66e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.c +++ b/drivers/net/wireless/realtek/rtlwifi/core.c @@ -58,17 +58,6 @@ void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr, } EXPORT_SYMBOL(rtl_rfreg_delay); -void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data) -{ - if (addr >= 0xf9 && addr <= 0xfe) { - rtl_addr_delay(addr); - } else { - rtl_set_bbreg(hw, addr, MASKDWORD, data); - udelay(1); - } -} -EXPORT_SYMBOL(rtl_bb_delay); - static void rtl_fw_do_work(const struct firmware *firmware, void *context, bool is_wow) { diff --git a/drivers/net/wireless/realtek/rtlwifi/core.h b/drivers/net/wireless/realtek/rtlwifi/core.h index 42c2d9e13bb8..45225d89ac5e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/core.h +++ b/drivers/net/wireless/realtek/rtlwifi/core.h @@ -58,7 +58,6 @@ void rtl_wowlan_fw_cb(const struct firmware *firmware, void *context); void rtl_addr_delay(u32 addr); void rtl_rfreg_delay(struct ieee80211_hw *hw, enum radio_path rfpath, u32 addr, u32 mask, u32 data); -void rtl_bb_delay(struct ieee80211_hw *hw, u32 addr, u32 data); bool rtl_cmd_send_packet(struct ieee80211_hw *hw, struct sk_buff *skb); bool rtl_btc_status_false(void); void rtl_dm_diginit(struct ieee80211_hw *hw, u32 cur_igval); diff --git a/drivers/net/wireless/realtek/rtlwifi/pci.c b/drivers/net/wireless/realtek/rtlwifi/pci.c index 0eafc4d125f9..898f597f70a9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/pci.c +++ b/drivers/net/wireless/realtek/rtlwifi/pci.c @@ -155,6 +155,16 @@ static void _rtl_pci_update_default_setting(struct ieee80211_hw *hw) ((u8)init_aspm) == (PCI_EXP_LNKCTL_ASPM_L0S | PCI_EXP_LNKCTL_ASPM_L1 | PCI_EXP_LNKCTL_CCC)) ppsc->support_aspm = false; + + /* RTL8723BE found on some ASUSTek laptops, such as F441U and + * X555UQ with subsystem ID 11ad:1723 are known to output large + * amounts of PCIe AER errors during and after boot up, causing + * heavy lags, poor network throughput, and occasional lock-ups. + */ + if (rtlpriv->rtlhal.hw_type == HARDWARE_TYPE_RTL8723BE && + (rtlpci->pdev->subsystem_vendor == 0x11ad && + rtlpci->pdev->subsystem_device == 0x1723)) + ppsc->support_aspm = false; } static bool _rtl_pci_platform_switch_device_pci_aspm( diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c index 35875cda30fc..2ad4523d1bef 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8188ee/sw.c @@ -179,9 +179,9 @@ static void rtl88e_deinit_sw_vars(struct ieee80211_hw *hw) } if (rtlpriv->psc.low_power_enable) - del_timer_sync(&rtlpriv->works.fw_clockoff_timer); + timer_delete_sync(&rtlpriv->works.fw_clockoff_timer); - del_timer_sync(&rtlpriv->works.fast_antenna_training_timer); + timer_delete_sync(&rtlpriv->works.fast_antenna_training_timer); } /* get bt coexist status */ diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c index d429560009bb..68f890050afb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.c @@ -484,7 +484,7 @@ bool rtl92d_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, * pathA or mac1 has to set phy0&phy1 pathA */ if ((content == radiob_txt) && (rfpath == RF90_PATH_A)) { rtl_dbg(rtlpriv, COMP_INIT, DBG_LOUD, - " ===> althougth Path A, we load radiob.txt\n"); + " ===> although Path A, we load radiob.txt\n"); radioa_arraylen = radiob_arraylen; radioa_array_table = radiob_array_table; } @@ -750,7 +750,7 @@ static void _rtl92d_phy_switch_rf_setting(struct ieee80211_hw *hw, u8 channel) && rtlhal->interfaceindex == 1) { need_pwr_down = rtl92d_phy_enable_anotherphy(hw, false); rtlhal->during_mac1init_radioa = true; - /* asume no this case */ + /* assume no this case */ if (need_pwr_down) rtl92d_phy_enable_rf_env(hw, path, &u4regvalue); @@ -1885,7 +1885,7 @@ static void _rtl92d_phy_reload_lck_setting(struct ieee80211_hw *hw, bneed_powerdown_radio = rtl92d_phy_enable_anotherphy(hw, false); rtlpriv->rtlhal.during_mac1init_radioa = true; - /* asume no this case */ + /* assume no this case */ if (bneed_powerdown_radio) rtl92d_phy_enable_rf_env(hw, erfpath, &u4regvalue); @@ -2055,11 +2055,6 @@ void rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) RTPRINT(rtlpriv, FINIT, INIT_IQK, "LCK:Finish!!!\n"); } -void rtl92d_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta) -{ - return; -} - static bool _rtl92d_phy_set_sw_chnl_cmdarray(struct swchnlcmd *cmdtable, u32 cmdtableidx, u32 cmdtablesz, enum swchnlcmd_id cmdid, u32 para1, u32 para2, u32 msdelay) diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h index bbe9ef77225e..a9bfe54f2802 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192de/phy.h @@ -83,7 +83,6 @@ void rtl92d_phy_set_poweron(struct ieee80211_hw *hw); bool rtl92d_phy_check_poweroff(struct ieee80211_hw *hw); void rtl92d_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); void rtl92d_update_bbrf_configuration(struct ieee80211_hw *hw); -void rtl92d_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta); void rtl92d_phy_iq_calibrate(struct ieee80211_hw *hw); void rtl92d_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c index 289ec71ce3e5..8c2167cc1f13 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.c @@ -2445,11 +2445,6 @@ void rtl92du_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t) RTPRINT(rtlpriv, FINIT, INIT_IQK, "LCK:Finish!!!\n"); } -void rtl92du_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta) -{ - /* Nothing to do. */ -} - u8 rtl92du_phy_sw_chnl(struct ieee80211_hw *hw) { struct rtl_priv *rtlpriv = rtl_priv(hw); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h index 090a6203db7e..a107a5a76beb 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192du/phy.h @@ -24,7 +24,6 @@ void rtl92du_phy_set_poweron(struct ieee80211_hw *hw); bool rtl92du_phy_check_poweroff(struct ieee80211_hw *hw); void rtl92du_phy_lc_calibrate(struct ieee80211_hw *hw, bool is2t); void rtl92du_update_bbrf_configuration(struct ieee80211_hw *hw); -void rtl92du_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta); void rtl92du_phy_iq_calibrate(struct ieee80211_hw *hw); void rtl92du_phy_reload_iqk_setting(struct ieee80211_hw *hw, u8 channel); void rtl92du_phy_init_pa_bias(struct ieee80211_hw *hw); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c index 73ef602bfb01..1e7f0cd1c86e 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.c @@ -2926,10 +2926,6 @@ void rtl92ee_phy_lc_calibrate(struct ieee80211_hw *hw) rtlphy->lck_inprogress = false; } -void rtl92ee_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta) -{ -} - void rtl92ee_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain) { _rtl92ee_phy_set_rfpath_switch(hw, bmain, false); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h index 1a5dbc628379..ec4c26b81c48 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8192ee/phy.h @@ -119,7 +119,6 @@ void rtl92ee_phy_set_bw_mode(struct ieee80211_hw *hw, void rtl92ee_phy_sw_chnl_callback(struct ieee80211_hw *hw); u8 rtl92ee_phy_sw_chnl(struct ieee80211_hw *hw); void rtl92ee_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); -void rtl92ee_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta); void rtl92ee_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl92ee_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl92ee_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c index 9eddbada8af1..13a05066e8a6 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.c @@ -4586,10 +4586,6 @@ void rtl8821ae_phy_lc_calibrate(struct ieee80211_hw *hw) { } -void rtl8821ae_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta) -{ -} - void rtl8821ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain) { _rtl8821ae_phy_set_rfpath_switch(hw, bmain); diff --git a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h index 35b7d0f70125..90bf5462a3f8 100644 --- a/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h +++ b/drivers/net/wireless/realtek/rtlwifi/rtl8821ae/phy.h @@ -214,7 +214,6 @@ void rtl8821ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); void rtl8812ae_phy_iq_calibrate(struct ieee80211_hw *hw, bool b_recovery); -void rtl8821ae_phy_ap_calibrate(struct ieee80211_hw *hw, s8 delta); void rtl8821ae_phy_lc_calibrate(struct ieee80211_hw *hw); void rtl8821ae_phy_set_rfpath_switch(struct ieee80211_hw *hw, bool bmain); bool rtl8812ae_phy_config_rf_with_headerfile(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.c b/drivers/net/wireless/realtek/rtlwifi/usb.c index f5718e570011..d35ed56d6db9 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.c +++ b/drivers/net/wireless/realtek/rtlwifi/usb.c @@ -1077,15 +1077,3 @@ void rtl_usb_disconnect(struct usb_interface *intf) ieee80211_free_hw(hw); } EXPORT_SYMBOL(rtl_usb_disconnect); - -int rtl_usb_suspend(struct usb_interface *pusb_intf, pm_message_t message) -{ - return 0; -} -EXPORT_SYMBOL(rtl_usb_suspend); - -int rtl_usb_resume(struct usb_interface *pusb_intf) -{ - return 0; -} -EXPORT_SYMBOL(rtl_usb_resume); diff --git a/drivers/net/wireless/realtek/rtlwifi/usb.h b/drivers/net/wireless/realtek/rtlwifi/usb.h index b66d6f9ae564..b873bbc9c4c2 100644 --- a/drivers/net/wireless/realtek/rtlwifi/usb.h +++ b/drivers/net/wireless/realtek/rtlwifi/usb.h @@ -138,7 +138,5 @@ int rtl_usb_probe(struct usb_interface *intf, const struct usb_device_id *id, const struct rtl_hal_cfg *rtl92cu_hal_cfg); void rtl_usb_disconnect(struct usb_interface *intf); -int rtl_usb_suspend(struct usb_interface *pusb_intf, pm_message_t message); -int rtl_usb_resume(struct usb_interface *pusb_intf); #endif diff --git a/drivers/net/wireless/realtek/rtw88/Kconfig b/drivers/net/wireless/realtek/rtw88/Kconfig index ab21b8059e0b..3736f290bd42 100644 --- a/drivers/net/wireless/realtek/rtw88/Kconfig +++ b/drivers/net/wireless/realtek/rtw88/Kconfig @@ -54,6 +54,9 @@ config RTW88_8812A tristate select RTW88_88XXA +config RTW88_8814A + tristate + config RTW88_8822BE tristate "Realtek 8822BE PCI wireless network adapter" depends on PCI @@ -222,6 +225,28 @@ config RTW88_8812AU 802.11ac USB wireless network adapter +config RTW88_8814AE + tristate "Realtek 8814AE PCI wireless network adapter" + depends on PCI + select RTW88_CORE + select RTW88_PCI + select RTW88_8814A + help + Select this option will enable support for 8814AE chipset + + 802.11ac PCIe wireless network adapter + +config RTW88_8814AU + tristate "Realtek 8814AU USB wireless network adapter" + depends on USB + select RTW88_CORE + select RTW88_USB + select RTW88_8814A + help + Select this option will enable support for 8814AU chipset + + 802.11ac USB wireless network adapter + config RTW88_DEBUG bool "Realtek rtw88 debug support" depends on RTW88_CORE diff --git a/drivers/net/wireless/realtek/rtw88/Makefile b/drivers/net/wireless/realtek/rtw88/Makefile index 0cbbb366e393..0b3da05a2938 100644 --- a/drivers/net/wireless/realtek/rtw88/Makefile +++ b/drivers/net/wireless/realtek/rtw88/Makefile @@ -94,6 +94,15 @@ rtw88_8821au-objs := rtw8821au.o obj-$(CONFIG_RTW88_8812AU) += rtw88_8812au.o rtw88_8812au-objs := rtw8812au.o +obj-$(CONFIG_RTW88_8814A) += rtw88_8814a.o +rtw88_8814a-objs := rtw8814a.o rtw8814a_table.o + +obj-$(CONFIG_RTW88_8814AE) += rtw88_8814ae.o +rtw88_8814ae-objs := rtw8814ae.o + +obj-$(CONFIG_RTW88_8814AU) += rtw88_8814au.o +rtw88_8814au-objs := rtw8814au.o + obj-$(CONFIG_RTW88_PCI) += rtw88_pci.o rtw88_pci-objs := pci.o diff --git a/drivers/net/wireless/realtek/rtw88/coex.c b/drivers/net/wireless/realtek/rtw88/coex.c index c929db1e53ca..64904278ddad 100644 --- a/drivers/net/wireless/realtek/rtw88/coex.c +++ b/drivers/net/wireless/realtek/rtw88/coex.c @@ -309,7 +309,7 @@ static void rtw_coex_tdma_timer_base(struct rtw_dev *rtwdev, u8 type) { struct rtw_coex *coex = &rtwdev->coex; struct rtw_coex_stat *coex_stat = &coex->stat; - u8 para[2] = {0}; + u8 para[6] = {}; u8 times; u16 tbtt_interval = coex_stat->wl_beacon_interval; diff --git a/drivers/net/wireless/realtek/rtw88/debug.c b/drivers/net/wireless/realtek/rtw88/debug.c index 364ec0436d0f..b67d69b01f87 100644 --- a/drivers/net/wireless/realtek/rtw88/debug.c +++ b/drivers/net/wireless/realtek/rtw88/debug.c @@ -654,10 +654,10 @@ static void rtw_print_rate(struct seq_file *m, u8 rate) case DESC_RATE6M...DESC_RATE54M: rtw_print_ofdm_rate_txt(m, rate); break; - case DESC_RATEMCS0...DESC_RATEMCS15: + case DESC_RATEMCS0...DESC_RATEMCS31: rtw_print_ht_rate_txt(m, rate); break; - case DESC_RATEVHT1SS_MCS0...DESC_RATEVHT2SS_MCS9: + case DESC_RATEVHT1SS_MCS0...DESC_RATEVHT4SS_MCS9: rtw_print_vht_rate_txt(m, rate); break; default: @@ -692,9 +692,11 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v) { struct rtw_debugfs_priv *debugfs_priv = m->private; struct rtw_dev *rtwdev = debugfs_priv->rtwdev; + struct rtw_power_params pwr_param = {0}; struct rtw_hal *hal = &rtwdev->hal; + u8 nss = rtwdev->efuse.hw_cap.nss; u8 path, rate, bw, ch, regd; - struct rtw_power_params pwr_param = {0}; + u8 max_ht_rate, max_rate; mutex_lock(&rtwdev->mutex); bw = hal->current_band_width; @@ -707,19 +709,23 @@ static int rtw_debugfs_get_tx_pwr_tbl(struct seq_file *m, void *v) seq_printf(m, "%-4s %-10s %-9s %-9s (%-4s %-4s %-4s) %-4s\n", "path", "rate", "pwr", "base", "byr", "lmt", "sar", "rem"); + max_ht_rate = DESC_RATEMCS0 + nss * 8 - 1; + + if (rtwdev->chip->vht_supported) + max_rate = DESC_RATEVHT1SS_MCS0 + nss * 10 - 1; + else + max_rate = max_ht_rate; + mutex_lock(&hal->tx_power_mutex); - for (path = RF_PATH_A; path <= RF_PATH_B; path++) { + for (path = RF_PATH_A; path < hal->rf_path_num; path++) { /* there is no CCK rates used in 5G */ if (hal->current_band_type == RTW_BAND_5G) rate = DESC_RATE6M; else rate = DESC_RATE1M; - /* now, not support vht 3ss and vht 4ss*/ - for (; rate <= DESC_RATEVHT2SS_MCS9; rate++) { - /* now, not support ht 3ss and ht 4ss*/ - if (rate > DESC_RATEMCS15 && - rate < DESC_RATEVHT1SS_MCS0) + for (; rate <= max_rate; rate++) { + if (rate > max_ht_rate && rate <= DESC_RATEMCS31) continue; rtw_get_tx_power_params(rtwdev, path, rate, bw, @@ -849,20 +855,28 @@ static int rtw_debugfs_get_phy_info(struct seq_file *m, void *v) last_cnt->num_qry_pkt[rate_id + 9]); } - seq_printf(m, "[RSSI(dBm)] = {%d, %d}\n", + seq_printf(m, "[RSSI(dBm)] = {%d, %d, %d, %d}\n", dm_info->rssi[RF_PATH_A] - 100, - dm_info->rssi[RF_PATH_B] - 100); - seq_printf(m, "[Rx EVM(dB)] = {-%d, -%d}\n", + dm_info->rssi[RF_PATH_B] - 100, + dm_info->rssi[RF_PATH_C] - 100, + dm_info->rssi[RF_PATH_D] - 100); + seq_printf(m, "[Rx EVM(dB)] = {-%d, -%d, -%d, -%d}\n", dm_info->rx_evm_dbm[RF_PATH_A], - dm_info->rx_evm_dbm[RF_PATH_B]); - seq_printf(m, "[Rx SNR] = {%d, %d}\n", + dm_info->rx_evm_dbm[RF_PATH_B], + dm_info->rx_evm_dbm[RF_PATH_C], + dm_info->rx_evm_dbm[RF_PATH_D]); + seq_printf(m, "[Rx SNR] = {%d, %d, %d, %d}\n", dm_info->rx_snr[RF_PATH_A], - dm_info->rx_snr[RF_PATH_B]); - seq_printf(m, "[CFO_tail(KHz)] = {%d, %d}\n", + dm_info->rx_snr[RF_PATH_B], + dm_info->rx_snr[RF_PATH_C], + dm_info->rx_snr[RF_PATH_D]); + seq_printf(m, "[CFO_tail(KHz)] = {%d, %d, %d, %d}\n", dm_info->cfo_tail[RF_PATH_A], - dm_info->cfo_tail[RF_PATH_B]); + dm_info->cfo_tail[RF_PATH_B], + dm_info->cfo_tail[RF_PATH_C], + dm_info->cfo_tail[RF_PATH_D]); - if (dm_info->curr_rx_rate >= DESC_RATE11M) { + if (dm_info->curr_rx_rate >= DESC_RATE6M) { seq_puts(m, "[Rx Average Status]:\n"); seq_printf(m, " * OFDM, EVM: {-%d}, SNR: {%d}\n", (u8)ewma_evm_read(&ewma_evm[RTW_EVM_OFDM]), @@ -875,6 +889,13 @@ static int rtw_debugfs_get_phy_info(struct seq_file *m, void *v) (u8)ewma_evm_read(&ewma_evm[RTW_EVM_2SS_B]), (u8)ewma_snr_read(&ewma_snr[RTW_SNR_2SS_A]), (u8)ewma_snr_read(&ewma_snr[RTW_SNR_2SS_B])); + seq_printf(m, " * 3SS, EVM: {-%d, -%d, -%d}, SNR: {%d, %d, %d}\n", + (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_A]), + (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_B]), + (u8)ewma_evm_read(&ewma_evm[RTW_EVM_3SS_C]), + (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_A]), + (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_B]), + (u8)ewma_snr_read(&ewma_snr[RTW_SNR_3SS_C])); } seq_puts(m, "[Rx Counter]:\n"); diff --git a/drivers/net/wireless/realtek/rtw88/fw.c b/drivers/net/wireless/realtek/rtw88/fw.c index 02389b7c6876..4fc78b882080 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.c +++ b/drivers/net/wireless/realtek/rtw88/fw.c @@ -735,6 +735,7 @@ void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, { u8 h2c_pkt[H2C_PKT_SIZE] = {0}; bool disable_pt = true; + u32 mask_hi; SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO); @@ -755,6 +756,20 @@ void rtw_fw_send_ra_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, si->init_ra_lv = 0; rtw_fw_send_h2c_command(rtwdev, h2c_pkt); + + if (rtwdev->chip->id != RTW_CHIP_TYPE_8814A) + return; + + SET_H2C_CMD_ID_CLASS(h2c_pkt, H2C_CMD_RA_INFO_HI); + + mask_hi = si->ra_mask >> 32; + + SET_RA_INFO_RA_MASK0(h2c_pkt, (mask_hi & 0xff)); + SET_RA_INFO_RA_MASK1(h2c_pkt, (mask_hi & 0xff00) >> 8); + SET_RA_INFO_RA_MASK2(h2c_pkt, (mask_hi & 0xff0000) >> 16); + SET_RA_INFO_RA_MASK3(h2c_pkt, (mask_hi & 0xff000000) >> 24); + + rtw_fw_send_h2c_command(rtwdev, h2c_pkt); } void rtw_fw_media_status_report(struct rtw_dev *rtwdev, u8 mac_id, bool connect) @@ -1451,7 +1466,7 @@ void rtw_add_rsvd_page_sta(struct rtw_dev *rtwdev, int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, u8 *buf, u32 size) { - u8 bckp[2]; + u8 bckp[3]; u8 val; u16 rsvd_pg_head; u32 bcn_valid_addr; @@ -1463,6 +1478,8 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, if (!size) return -EINVAL; + bckp[2] = rtw_read8(rtwdev, REG_BCN_CTRL); + if (rtw_chip_wcpu_11n(rtwdev)) { rtw_write32_set(rtwdev, REG_DWBCN0_CTRL, BIT_BCN_VALID); } else { @@ -1476,6 +1493,9 @@ int rtw_fw_write_data_rsvd_page(struct rtw_dev *rtwdev, u16 pg_addr, val |= BIT_ENSWBCN >> 8; rtw_write8(rtwdev, REG_CR + 1, val); + rtw_write8(rtwdev, REG_BCN_CTRL, + (bckp[2] & ~BIT_EN_BCN_FUNCTION) | BIT_DIS_TSF_UDT); + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) { val = rtw_read8(rtwdev, REG_FWHW_TXQ_CTRL + 2); bckp[1] = val; @@ -1506,6 +1526,7 @@ restore: rsvd_pg_head = rtwdev->fifo.rsvd_boundary; rtw_write16(rtwdev, REG_FIFOPAGE_CTRL_2, rsvd_pg_head | BIT_BCN_VALID_V1); + rtw_write8(rtwdev, REG_BCN_CTRL, bckp[2]); if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 2, bckp[1]); rtw_write8(rtwdev, REG_CR + 1, bckp[0]); diff --git a/drivers/net/wireless/realtek/rtw88/fw.h b/drivers/net/wireless/realtek/rtw88/fw.h index 404de1b0c407..48ad9ceab6ea 100644 --- a/drivers/net/wireless/realtek/rtw88/fw.h +++ b/drivers/net/wireless/realtek/rtw88/fw.h @@ -557,6 +557,7 @@ static inline void rtw_h2c_pkt_set_header(u8 *h2c_pkt, u8 sub_id) #define H2C_CMD_DEFAULT_PORT 0x2c #define H2C_CMD_RA_INFO 0x40 #define H2C_CMD_RSSI_MONITOR 0x42 +#define H2C_CMD_RA_INFO_HI 0x46 #define H2C_CMD_BCN_FILTER_OFFLOAD_P0 0x56 #define H2C_CMD_BCN_FILTER_OFFLOAD_P1 0x57 #define H2C_CMD_WL_PHY_INFO 0x58 diff --git a/drivers/net/wireless/realtek/rtw88/hci.h b/drivers/net/wireless/realtek/rtw88/hci.h index 96aeda26014e..d4bee9c3ecfe 100644 --- a/drivers/net/wireless/realtek/rtw88/hci.h +++ b/drivers/net/wireless/realtek/rtw88/hci.h @@ -19,6 +19,8 @@ struct rtw_hci_ops { void (*link_ps)(struct rtw_dev *rtwdev, bool enter); void (*interface_cfg)(struct rtw_dev *rtwdev); void (*dynamic_rx_agg)(struct rtw_dev *rtwdev, bool enable); + void (*write_firmware_page)(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size); int (*write_data_rsvd_page)(struct rtw_dev *rtwdev, u8 *buf, u32 size); int (*write_data_h2c)(struct rtw_dev *rtwdev, u8 *buf, u32 size); @@ -79,6 +81,12 @@ static inline void rtw_hci_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable) rtwdev->hci.ops->dynamic_rx_agg(rtwdev, enable); } +static inline void rtw_hci_write_firmware_page(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size) +{ + rtwdev->hci.ops->write_firmware_page(rtwdev, page, data, size); +} + static inline int rtw_hci_write_data_rsvd_page(struct rtw_dev *rtwdev, u8 *buf, u32 size) { diff --git a/drivers/net/wireless/realtek/rtw88/mac.c b/drivers/net/wireless/realtek/rtw88/mac.c index cae9cca6dca3..f66d1b302dc5 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.c +++ b/drivers/net/wireless/realtek/rtw88/mac.c @@ -291,6 +291,7 @@ static int rtw_mac_power_switch(struct rtw_dev *rtwdev, bool pwr_on) if (rtw_read8(rtwdev, REG_CR) == 0xea) cur_pwr = false; else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && + chip->id != RTW_CHIP_TYPE_8814A && (rtw_read8(rtwdev, REG_SYS_STATUS1 + 1) & BIT(0))) cur_pwr = false; else @@ -784,7 +785,8 @@ static int __rtw_download_firmware(struct rtw_dev *rtwdev, if (!check_firmware_size(data, size)) return -EINVAL; - if (!ltecoex_read_reg(rtwdev, 0x38, <ecoex_bckp)) + if (rtwdev->chip->ltecoex_addr && + !ltecoex_read_reg(rtwdev, 0x38, <ecoex_bckp)) return -EBUSY; wlan_cpu_enable(rtwdev, false); @@ -802,7 +804,8 @@ static int __rtw_download_firmware(struct rtw_dev *rtwdev, wlan_cpu_enable(rtwdev, true); - if (!ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) { + if (rtwdev->chip->ltecoex_addr && + !ltecoex_reg_write(rtwdev, 0x38, ltecoex_bckp)) { ret = -EBUSY; goto dlfw_fail; } @@ -853,8 +856,8 @@ fwdl_ready: } } -static void -write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size) +void rtw_write_firmware_page(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size) { u32 val32; u32 block_nr; @@ -884,6 +887,7 @@ write_firmware_page(struct rtw_dev *rtwdev, u32 page, const u8 *data, u32 size) rtw_write32(rtwdev, write_addr, le32_to_cpu(remain_data)); } } +EXPORT_SYMBOL(rtw_write_firmware_page); static int download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size) @@ -901,11 +905,13 @@ download_firmware_legacy(struct rtw_dev *rtwdev, const u8 *data, u32 size) rtw_write8_set(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT); for (page = 0; page < total_page; page++) { - write_firmware_page(rtwdev, page, data, DLFW_PAGE_SIZE_LEGACY); + rtw_hci_write_firmware_page(rtwdev, page, data, + DLFW_PAGE_SIZE_LEGACY); data += DLFW_PAGE_SIZE_LEGACY; } if (last_page_size) - write_firmware_page(rtwdev, page, data, last_page_size); + rtw_hci_write_firmware_page(rtwdev, page, data, + last_page_size); if (!check_hw_ready(rtwdev, REG_MCUFW_CTRL, BIT_FWDL_CHK_RPT, 1)) { rtw_err(rtwdev, "failed to check download firmware report\n"); diff --git a/drivers/net/wireless/realtek/rtw88/mac.h b/drivers/net/wireless/realtek/rtw88/mac.h index 6905e2747372..e92b1483728d 100644 --- a/drivers/net/wireless/realtek/rtw88/mac.h +++ b/drivers/net/wireless/realtek/rtw88/mac.h @@ -34,6 +34,8 @@ int rtw_pwr_seq_parser(struct rtw_dev *rtwdev, const struct rtw_pwr_seq_cmd * const *cmd_seq); int rtw_mac_power_on(struct rtw_dev *rtwdev); void rtw_mac_power_off(struct rtw_dev *rtwdev); +void rtw_write_firmware_page(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size); int rtw_download_firmware(struct rtw_dev *rtwdev, struct rtw_fw_state *fw); int rtw_mac_init(struct rtw_dev *rtwdev); void rtw_mac_flush_queues(struct rtw_dev *rtwdev, u32 queues, bool drop); diff --git a/drivers/net/wireless/realtek/rtw88/mac80211.c b/drivers/net/wireless/realtek/rtw88/mac80211.c index 026fbf4ad9cc..77f9fbe1870c 100644 --- a/drivers/net/wireless/realtek/rtw88/mac80211.c +++ b/drivers/net/wireless/realtek/rtw88/mac80211.c @@ -396,6 +396,8 @@ static void rtw_ops_bss_info_changed(struct ieee80211_hw *hw, if (rtw_bf_support) rtw_bf_assoc(rtwdev, vif, conf); + rtw_set_ampdu_factor(rtwdev, vif, conf); + rtw_fw_beacon_filter_config(rtwdev, true, vif); } else { rtw_leave_lps(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw88/main.c b/drivers/net/wireless/realtek/rtw88/main.c index 0cee0fd8c0ef..c4de5d114eda 100644 --- a/drivers/net/wireless/realtek/rtw88/main.c +++ b/drivers/net/wireless/realtek/rtw88/main.c @@ -136,7 +136,7 @@ u16 rtw_desc_to_bitrate(u8 desc_rate) return rate.bitrate; } -static struct ieee80211_supported_band rtw_band_2ghz = { +static const struct ieee80211_supported_band rtw_band_2ghz = { .band = NL80211_BAND_2GHZ, .channels = rtw_channeltable_2g, @@ -149,7 +149,7 @@ static struct ieee80211_supported_band rtw_band_2ghz = { .vht_cap = {0}, }; -static struct ieee80211_supported_band rtw_band_5ghz = { +static const struct ieee80211_supported_band rtw_band_5ghz = { .band = NL80211_BAND_5GHZ, .channels = rtw_channeltable_5g, @@ -1234,7 +1234,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, if (sta->deflink.vht_cap.cap & IEEE80211_VHT_CAP_RXLDPC) ldpc_en = VHT_LDPC_EN; } else if (sta->deflink.ht_cap.ht_supported) { - ra_mask |= (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) | + ra_mask |= ((u64)sta->deflink.ht_cap.mcs.rx_mask[3] << 36) | + ((u64)sta->deflink.ht_cap.mcs.rx_mask[2] << 28) | + (sta->deflink.ht_cap.mcs.rx_mask[1] << 20) | (sta->deflink.ht_cap.mcs.rx_mask[0] << 12); if (sta->deflink.ht_cap.cap & IEEE80211_HT_CAP_RX_STBC) stbc_en = HT_STBC_EN; @@ -1244,6 +1246,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, if (efuse->hw_cap.nss == 1 || rtwdev->hal.txrx_1ss) ra_mask &= RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS; + else if (efuse->hw_cap.nss == 2) + ra_mask &= RA_MASK_VHT_RATES_2SS | RA_MASK_HT_RATES_2SS | + RA_MASK_VHT_RATES_1SS | RA_MASK_HT_RATES_1SS; if (hal->current_band_type == RTW_BAND_5G) { ra_mask |= (u64)sta->deflink.supp_rates[NL80211_BAND_5GHZ] << 4; @@ -1302,10 +1307,9 @@ void rtw_update_sta_info(struct rtw_dev *rtwdev, struct rtw_sta_info *si, break; } - if (sta->deflink.vht_cap.vht_supported && ra_mask & 0xffc00000) - tx_num = 2; - else if (sta->deflink.ht_cap.ht_supported && ra_mask & 0xfff00000) - tx_num = 2; + if (sta->deflink.vht_cap.vht_supported || + sta->deflink.ht_cap.ht_supported) + tx_num = efuse->hw_cap.nss; rate_id = get_rate_id(wireless_set, bw_mode, tx_num); @@ -1561,6 +1565,7 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev, { const struct rtw_chip_info *chip = rtwdev->chip; struct rtw_efuse *efuse = &rtwdev->efuse; + int i; ht_cap->ht_supported = true; ht_cap->cap = 0; @@ -1580,25 +1585,20 @@ static void rtw_init_ht_cap(struct rtw_dev *rtwdev, ht_cap->ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; ht_cap->ampdu_density = chip->ampdu_density; ht_cap->mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; - if (efuse->hw_cap.nss > 1) { - ht_cap->mcs.rx_mask[0] = 0xFF; - ht_cap->mcs.rx_mask[1] = 0xFF; - ht_cap->mcs.rx_mask[4] = 0x01; - ht_cap->mcs.rx_highest = cpu_to_le16(300); - } else { - ht_cap->mcs.rx_mask[0] = 0xFF; - ht_cap->mcs.rx_mask[1] = 0x00; - ht_cap->mcs.rx_mask[4] = 0x01; - ht_cap->mcs.rx_highest = cpu_to_le16(150); - } + + for (i = 0; i < efuse->hw_cap.nss; i++) + ht_cap->mcs.rx_mask[i] = 0xFF; + ht_cap->mcs.rx_mask[4] = 0x01; + ht_cap->mcs.rx_highest = cpu_to_le16(150 * efuse->hw_cap.nss); } static void rtw_init_vht_cap(struct rtw_dev *rtwdev, struct ieee80211_sta_vht_cap *vht_cap) { struct rtw_efuse *efuse = &rtwdev->efuse; - u16 mcs_map; + u16 mcs_map = 0; __le16 highest; + int i; if (efuse->hw_cap.ptcl != EFUSE_HW_CAP_IGNORE && efuse->hw_cap.ptcl != EFUSE_HW_CAP_PTCL_VHT) @@ -1621,21 +1621,15 @@ static void rtw_init_vht_cap(struct rtw_dev *rtwdev, if (rtw_chip_has_rx_ldpc(rtwdev)) vht_cap->cap |= IEEE80211_VHT_CAP_RXLDPC; - mcs_map = IEEE80211_VHT_MCS_SUPPORT_0_9 << 0 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 4 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 6 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 8 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 10 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 12 | - IEEE80211_VHT_MCS_NOT_SUPPORTED << 14; - if (efuse->hw_cap.nss > 1) { - highest = cpu_to_le16(780); - mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << 2; - } else { - highest = cpu_to_le16(390); - mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << 2; + for (i = 0; i < 8; i++) { + if (i < efuse->hw_cap.nss) + mcs_map |= IEEE80211_VHT_MCS_SUPPORT_0_9 << (i * 2); + else + mcs_map |= IEEE80211_VHT_MCS_NOT_SUPPORTED << (i * 2); } + highest = cpu_to_le16(390 * efuse->hw_cap.nss); + vht_cap->vht_mcs.rx_mcs_map = cpu_to_le16(mcs_map); vht_cap->vht_mcs.tx_mcs_map = cpu_to_le16(mcs_map); vht_cap->vht_mcs.rx_highest = highest; @@ -2248,7 +2242,8 @@ int rtw_register_hw(struct rtw_dev *rtwdev, struct ieee80211_hw *hw) ieee80211_hw_set(hw, SUPPORTS_PS); ieee80211_hw_set(hw, SUPPORTS_DYNAMIC_PS); ieee80211_hw_set(hw, SUPPORT_FAST_XMIT); - ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); + if (rtwdev->chip->amsdu_in_ampdu) + ieee80211_hw_set(hw, SUPPORTS_AMSDU_IN_AMPDU); ieee80211_hw_set(hw, HAS_RATE_CONTROL); ieee80211_hw_set(hw, TX_AMSDU); ieee80211_hw_set(hw, SINGLE_SCAN_ON_ALL_BANDS); @@ -2453,6 +2448,38 @@ void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable) } } +void rtw_set_ampdu_factor(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf) +{ + const struct rtw_chip_ops *ops = rtwdev->chip->ops; + struct ieee80211_sta *sta; + u8 factor = 0xff; + + if (!ops->set_ampdu_factor) + return; + + rcu_read_lock(); + + sta = ieee80211_find_sta(vif, bss_conf->bssid); + if (!sta) { + rcu_read_unlock(); + rtw_warn(rtwdev, "%s: failed to find station %pM\n", + __func__, bss_conf->bssid); + return; + } + + if (sta->deflink.vht_cap.vht_supported) + factor = u32_get_bits(sta->deflink.vht_cap.cap, + IEEE80211_VHT_CAP_MAX_A_MPDU_LENGTH_EXPONENT_MASK); + else if (sta->deflink.ht_cap.ht_supported) + factor = sta->deflink.ht_cap.ampdu_factor; + + rcu_read_unlock(); + + if (factor != 0xff) + ops->set_ampdu_factor(rtwdev, factor); +} + MODULE_AUTHOR("Realtek Corporation"); MODULE_DESCRIPTION("Realtek 802.11ac wireless core module"); MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw88/main.h b/drivers/net/wireless/realtek/rtw88/main.h index 62cd4c526301..b0f1fabe9554 100644 --- a/drivers/net/wireless/realtek/rtw88/main.h +++ b/drivers/net/wireless/realtek/rtw88/main.h @@ -61,7 +61,7 @@ enum rtw_hci_type { }; struct rtw_hci { - struct rtw_hci_ops *ops; + const struct rtw_hci_ops *ops; enum rtw_hci_type type; u32 rpwm_addr; @@ -166,9 +166,14 @@ enum rtw_rate_section { RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_1S, RTW_RATE_SECTION_VHT_2S, + __RTW_RATE_SECTION_2SS_MAX = RTW_RATE_SECTION_VHT_2S, + RTW_RATE_SECTION_HT_3S, + RTW_RATE_SECTION_HT_4S, + RTW_RATE_SECTION_VHT_3S, + RTW_RATE_SECTION_VHT_4S, /* keep last */ - RTW_RATE_SECTION_MAX, + RTW_RATE_SECTION_NUM, }; enum rtw_wireless_set { @@ -191,6 +196,7 @@ enum rtw_chip_type { RTW_CHIP_TYPE_8703B, RTW_CHIP_TYPE_8821A, RTW_CHIP_TYPE_8812A, + RTW_CHIP_TYPE_8814A, }; enum rtw_tx_queue_type { @@ -380,6 +386,9 @@ enum rtw_evm { RTW_EVM_1SS, RTW_EVM_2SS_A, RTW_EVM_2SS_B, + RTW_EVM_3SS_A, + RTW_EVM_3SS_B, + RTW_EVM_3SS_C, /* keep it last */ RTW_EVM_NUM }; @@ -397,6 +406,10 @@ enum rtw_snr { RTW_SNR_2SS_B, RTW_SNR_2SS_C, RTW_SNR_2SS_D, + RTW_SNR_3SS_A, + RTW_SNR_3SS_B, + RTW_SNR_3SS_C, + RTW_SNR_3SS_D, /* keep it last */ RTW_SNR_NUM }; @@ -822,7 +835,7 @@ struct rtw_vif { }; struct rtw_regulatory { - char alpha2[2]; + char alpha2[2] __nonstring; u8 txpwr_regd_2g; u8 txpwr_regd_5g; }; @@ -865,6 +878,7 @@ struct rtw_chip_ops { u32 antenna_rx); void (*cfg_ldo25)(struct rtw_dev *rtwdev, bool enable); void (*efuse_grant)(struct rtw_dev *rtwdev, bool enable); + void (*set_ampdu_factor)(struct rtw_dev *rtwdev, u8 factor); void (*false_alarm_statistics)(struct rtw_dev *rtwdev); void (*phy_calibration)(struct rtw_dev *rtwdev); void (*dpk_track)(struct rtw_dev *rtwdev); @@ -1130,14 +1144,26 @@ struct rtw_rfe_def { * For 2G there are cck rate and ofdm rate with different settings. */ struct rtw_pwr_track_tbl { + const u8 *pwrtrk_5gd_n[RTW_PWR_TRK_5G_NUM]; + const u8 *pwrtrk_5gd_p[RTW_PWR_TRK_5G_NUM]; + const u8 *pwrtrk_5gc_n[RTW_PWR_TRK_5G_NUM]; + const u8 *pwrtrk_5gc_p[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM]; const u8 *pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM]; + const u8 *pwrtrk_2gd_n; + const u8 *pwrtrk_2gd_p; + const u8 *pwrtrk_2gc_n; + const u8 *pwrtrk_2gc_p; const u8 *pwrtrk_2gb_n; const u8 *pwrtrk_2gb_p; const u8 *pwrtrk_2ga_n; const u8 *pwrtrk_2ga_p; + const u8 *pwrtrk_2g_cckd_n; + const u8 *pwrtrk_2g_cckd_p; + const u8 *pwrtrk_2g_cckc_n; + const u8 *pwrtrk_2g_cckc_p; const u8 *pwrtrk_2g_cckb_n; const u8 *pwrtrk_2g_cckb_p; const u8 *pwrtrk_2g_ccka_n; @@ -1204,6 +1230,7 @@ struct rtw_chip_info { u16 fw_fifo_addr[RTW_FW_FIFO_MAX]; const struct rtw_fwcd_segs *fwcd_segs; + bool amsdu_in_ampdu; u8 usb_tx_agg_desc_num; bool hw_feature_report; u8 c2h_ra_report_size; @@ -1227,8 +1254,8 @@ struct rtw_chip_info { const struct rtw_hw_reg *dig; const struct rtw_hw_reg *dig_cck; - u32 rf_base_addr[2]; - u32 rf_sipi_addr[2]; + u32 rf_base_addr[RTW_RF_PATH_MAX]; + u32 rf_sipi_addr[RTW_RF_PATH_MAX]; const struct rtw_rf_sipi_addr *rf_sipi_read_addr; u8 fix_rf_phy_num; const struct rtw_ltecoex_addr *ltecoex_addr; @@ -1924,7 +1951,7 @@ union rtw_sar_cfg { struct rtw_sar { enum rtw_sar_sources src; - union rtw_sar_cfg cfg[RTW_RF_PATH_MAX][RTW_RATE_SECTION_MAX]; + union rtw_sar_cfg cfg[RTW_RF_PATH_MAX][RTW_RATE_SECTION_NUM]; }; struct rtw_hal { @@ -1968,16 +1995,16 @@ struct rtw_hal { s8 tx_pwr_by_rate_offset_5g[RTW_RF_PATH_MAX] [DESC_RATE_MAX]; s8 tx_pwr_by_rate_base_2g[RTW_RF_PATH_MAX] - [RTW_RATE_SECTION_MAX]; + [RTW_RATE_SECTION_NUM]; s8 tx_pwr_by_rate_base_5g[RTW_RF_PATH_MAX] - [RTW_RATE_SECTION_MAX]; + [RTW_RATE_SECTION_NUM]; s8 tx_pwr_limit_2g[RTW_REGD_MAX] [RTW_CHANNEL_WIDTH_MAX] - [RTW_RATE_SECTION_MAX] + [RTW_RATE_SECTION_NUM] [RTW_MAX_CHANNEL_NUM_2G]; s8 tx_pwr_limit_5g[RTW_REGD_MAX] [RTW_CHANNEL_WIDTH_MAX] - [RTW_RATE_SECTION_MAX] + [RTW_RATE_SECTION_NUM] [RTW_MAX_CHANNEL_NUM_5G]; s8 tx_pwr_tbl[RTW_RF_PATH_MAX] [DESC_RATE_MAX]; @@ -2247,4 +2274,6 @@ void rtw_update_channel(struct rtw_dev *rtwdev, u8 center_channel, void rtw_core_port_switch(struct rtw_dev *rtwdev, struct ieee80211_vif *vif); bool rtw_core_check_sta_active(struct rtw_dev *rtwdev); void rtw_core_enable_beacon(struct rtw_dev *rtwdev, bool enable); +void rtw_set_ampdu_factor(struct rtw_dev *rtwdev, struct ieee80211_vif *vif, + struct ieee80211_bss_conf *bss_conf); #endif diff --git a/drivers/net/wireless/realtek/rtw88/pci.c b/drivers/net/wireless/realtek/rtw88/pci.c index 0ecaefc4c83d..7f2b6dc21f56 100644 --- a/drivers/net/wireless/realtek/rtw88/pci.c +++ b/drivers/net/wireless/realtek/rtw88/pci.c @@ -12,6 +12,7 @@ #include "fw.h" #include "ps.h" #include "debug.h" +#include "mac.h" static bool rtw_disable_msi; static bool rtw_pci_disable_aspm; @@ -20,7 +21,7 @@ module_param_named(disable_aspm, rtw_pci_disable_aspm, bool, 0644); MODULE_PARM_DESC(disable_msi, "Set Y to disable MSI interrupt support"); MODULE_PARM_DESC(disable_aspm, "Set Y to disable PCI ASPM support"); -static u32 rtw_pci_tx_queue_idx_addr[] = { +static const u32 rtw_pci_tx_queue_idx_addr[] = { [RTW_TX_QUEUE_BK] = RTK_PCI_TXBD_IDX_BKQ, [RTW_TX_QUEUE_BE] = RTK_PCI_TXBD_IDX_BEQ, [RTW_TX_QUEUE_VI] = RTK_PCI_TXBD_IDX_VIQ, @@ -1591,7 +1592,7 @@ static void rtw_pci_destroy(struct rtw_dev *rtwdev, struct pci_dev *pdev) rtw_pci_io_unmapping(rtwdev, pdev); } -static struct rtw_hci_ops rtw_pci_ops = { +static const struct rtw_hci_ops rtw_pci_ops = { .tx_write = rtw_pci_tx_write, .tx_kick_off = rtw_pci_tx_kick_off, .flush_queues = rtw_pci_flush_queues, @@ -1602,6 +1603,7 @@ static struct rtw_hci_ops rtw_pci_ops = { .link_ps = rtw_pci_link_ps, .interface_cfg = rtw_pci_interface_cfg, .dynamic_rx_agg = NULL, + .write_firmware_page = rtw_write_firmware_page, .read8 = rtw_pci_read8, .read16 = rtw_pci_read16, diff --git a/drivers/net/wireless/realtek/rtw88/phy.c b/drivers/net/wireless/realtek/rtw88/phy.c index 8ed20c89d216..55be0d8e0c28 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.c +++ b/drivers/net/wireless/realtek/rtw88/phy.c @@ -52,60 +52,93 @@ static const u32 db_invert_table[12][8] = { 1995262315, 2511886432U, 3162277660U, 3981071706U} }; -u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M }; -u8 rtw_ofdm_rates[] = { +const u8 rtw_cck_rates[] = { DESC_RATE1M, DESC_RATE2M, DESC_RATE5_5M, DESC_RATE11M }; + +const u8 rtw_ofdm_rates[] = { DESC_RATE6M, DESC_RATE9M, DESC_RATE12M, DESC_RATE18M, DESC_RATE24M, DESC_RATE36M, DESC_RATE48M, DESC_RATE54M }; -u8 rtw_ht_1s_rates[] = { + +const u8 rtw_ht_1s_rates[] = { DESC_RATEMCS0, DESC_RATEMCS1, DESC_RATEMCS2, DESC_RATEMCS3, DESC_RATEMCS4, DESC_RATEMCS5, DESC_RATEMCS6, DESC_RATEMCS7 }; -u8 rtw_ht_2s_rates[] = { + +const u8 rtw_ht_2s_rates[] = { DESC_RATEMCS8, DESC_RATEMCS9, DESC_RATEMCS10, DESC_RATEMCS11, DESC_RATEMCS12, DESC_RATEMCS13, DESC_RATEMCS14, DESC_RATEMCS15 }; -u8 rtw_vht_1s_rates[] = { + +const u8 rtw_vht_1s_rates[] = { DESC_RATEVHT1SS_MCS0, DESC_RATEVHT1SS_MCS1, DESC_RATEVHT1SS_MCS2, DESC_RATEVHT1SS_MCS3, DESC_RATEVHT1SS_MCS4, DESC_RATEVHT1SS_MCS5, DESC_RATEVHT1SS_MCS6, DESC_RATEVHT1SS_MCS7, DESC_RATEVHT1SS_MCS8, DESC_RATEVHT1SS_MCS9 }; -u8 rtw_vht_2s_rates[] = { + +const u8 rtw_vht_2s_rates[] = { DESC_RATEVHT2SS_MCS0, DESC_RATEVHT2SS_MCS1, DESC_RATEVHT2SS_MCS2, DESC_RATEVHT2SS_MCS3, DESC_RATEVHT2SS_MCS4, DESC_RATEVHT2SS_MCS5, DESC_RATEVHT2SS_MCS6, DESC_RATEVHT2SS_MCS7, DESC_RATEVHT2SS_MCS8, DESC_RATEVHT2SS_MCS9 }; -u8 *rtw_rate_section[RTW_RATE_SECTION_MAX] = { + +const u8 rtw_ht_3s_rates[] = { + DESC_RATEMCS16, DESC_RATEMCS17, DESC_RATEMCS18, + DESC_RATEMCS19, DESC_RATEMCS20, DESC_RATEMCS21, + DESC_RATEMCS22, DESC_RATEMCS23 +}; + +const u8 rtw_ht_4s_rates[] = { + DESC_RATEMCS24, DESC_RATEMCS25, DESC_RATEMCS26, + DESC_RATEMCS27, DESC_RATEMCS28, DESC_RATEMCS29, + DESC_RATEMCS30, DESC_RATEMCS31 +}; + +const u8 rtw_vht_3s_rates[] = { + DESC_RATEVHT3SS_MCS0, DESC_RATEVHT3SS_MCS1, + DESC_RATEVHT3SS_MCS2, DESC_RATEVHT3SS_MCS3, + DESC_RATEVHT3SS_MCS4, DESC_RATEVHT3SS_MCS5, + DESC_RATEVHT3SS_MCS6, DESC_RATEVHT3SS_MCS7, + DESC_RATEVHT3SS_MCS8, DESC_RATEVHT3SS_MCS9 +}; + +const u8 rtw_vht_4s_rates[] = { + DESC_RATEVHT4SS_MCS0, DESC_RATEVHT4SS_MCS1, + DESC_RATEVHT4SS_MCS2, DESC_RATEVHT4SS_MCS3, + DESC_RATEVHT4SS_MCS4, DESC_RATEVHT4SS_MCS5, + DESC_RATEVHT4SS_MCS6, DESC_RATEVHT4SS_MCS7, + DESC_RATEVHT4SS_MCS8, DESC_RATEVHT4SS_MCS9 +}; + +const u8 * const rtw_rate_section[RTW_RATE_SECTION_NUM] = { rtw_cck_rates, rtw_ofdm_rates, rtw_ht_1s_rates, rtw_ht_2s_rates, - rtw_vht_1s_rates, rtw_vht_2s_rates + rtw_vht_1s_rates, rtw_vht_2s_rates, + rtw_ht_3s_rates, rtw_ht_4s_rates, + rtw_vht_3s_rates, rtw_vht_4s_rates }; EXPORT_SYMBOL(rtw_rate_section); -u8 rtw_rate_size[RTW_RATE_SECTION_MAX] = { +const u8 rtw_rate_size[RTW_RATE_SECTION_NUM] = { ARRAY_SIZE(rtw_cck_rates), ARRAY_SIZE(rtw_ofdm_rates), ARRAY_SIZE(rtw_ht_1s_rates), ARRAY_SIZE(rtw_ht_2s_rates), ARRAY_SIZE(rtw_vht_1s_rates), - ARRAY_SIZE(rtw_vht_2s_rates) + ARRAY_SIZE(rtw_vht_2s_rates), + ARRAY_SIZE(rtw_ht_3s_rates), + ARRAY_SIZE(rtw_ht_4s_rates), + ARRAY_SIZE(rtw_vht_3s_rates), + ARRAY_SIZE(rtw_vht_4s_rates) }; EXPORT_SYMBOL(rtw_rate_size); -static const u8 rtw_cck_size = ARRAY_SIZE(rtw_cck_rates); -static const u8 rtw_ofdm_size = ARRAY_SIZE(rtw_ofdm_rates); -static const u8 rtw_ht_1s_size = ARRAY_SIZE(rtw_ht_1s_rates); -static const u8 rtw_ht_2s_size = ARRAY_SIZE(rtw_ht_2s_rates); -static const u8 rtw_vht_1s_size = ARRAY_SIZE(rtw_vht_1s_rates); -static const u8 rtw_vht_2s_size = ARRAY_SIZE(rtw_vht_2s_rates); - enum rtw_phy_band_type { PHY_BAND_2G = 0, PHY_BAND_5G = 1, @@ -1590,7 +1623,7 @@ static void rtw_phy_set_tx_power_limit(struct rtw_dev *rtwdev, u8 regd, u8 band, ch_idx = rtw_channel_to_idx(band, ch); if (regd >= RTW_REGD_MAX || bw >= RTW_CHANNEL_WIDTH_MAX || - rs >= RTW_RATE_SECTION_MAX || ch_idx < 0) { + rs >= RTW_RATE_SECTION_NUM || ch_idx < 0) { WARN(1, "wrong txpwr_lmt regd=%u, band=%u bw=%u, rs=%u, ch_idx=%u, pwr_limit=%d\n", regd, band, bw, rs, ch_idx, pwr_limit); @@ -1634,11 +1667,15 @@ rtw_xref_5g_txpwr_lmt(struct rtw_dev *rtwdev, u8 regd, static void rtw_xref_txpwr_lmt_by_rs(struct rtw_dev *rtwdev, u8 regd, u8 bw, u8 ch_idx) { + static const u8 rs_cmp[4][2] = { + {RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_VHT_1S}, + {RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_2S}, + {RTW_RATE_SECTION_HT_3S, RTW_RATE_SECTION_VHT_3S}, + {RTW_RATE_SECTION_HT_4S, RTW_RATE_SECTION_VHT_4S} + }; u8 rs_idx, rs_ht, rs_vht; - u8 rs_cmp[2][2] = {{RTW_RATE_SECTION_HT_1S, RTW_RATE_SECTION_VHT_1S}, - {RTW_RATE_SECTION_HT_2S, RTW_RATE_SECTION_VHT_2S} }; - for (rs_idx = 0; rs_idx < 2; rs_idx++) { + for (rs_idx = 0; rs_idx < 4; rs_idx++) { rs_ht = rs_cmp[rs_idx][0]; rs_vht = rs_cmp[rs_idx][1]; @@ -1695,7 +1732,7 @@ rtw_cfg_txpwr_lmt_by_alt(struct rtw_dev *rtwdev, u8 regd, u8 regd_alt) u8 bw, rs; for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) __cfg_txpwr_lmt_by_alt(&rtwdev->hal, regd, regd_alt, bw, rs); } @@ -1959,10 +1996,10 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, u8 rate, u8 group) { const struct rtw_chip_info *chip = rtwdev->chip; - u8 tx_power; - bool mcs_rate; - bool above_2ss; + bool above_2ss, above_3ss, above_4ss; u8 factor = chip->txgi_factor; + bool mcs_rate; + u8 tx_power; if (rate <= DESC_RATE11M) tx_power = pwr_idx_2g->cck_base[group]; @@ -1972,11 +2009,15 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, if (rate >= DESC_RATE6M && rate <= DESC_RATE54M) tx_power += pwr_idx_2g->ht_1s_diff.ofdm * factor; - mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) || + mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31) || (rate >= DESC_RATEVHT1SS_MCS0 && - rate <= DESC_RATEVHT2SS_MCS9); - above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) || + rate <= DESC_RATEVHT4SS_MCS9); + above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS31) || (rate >= DESC_RATEVHT2SS_MCS0); + above_3ss = (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS31) || + (rate >= DESC_RATEVHT3SS_MCS0); + above_4ss = (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31) || + (rate >= DESC_RATEVHT4SS_MCS0); if (!mcs_rate) return tx_power; @@ -1989,11 +2030,19 @@ static u8 rtw_phy_get_2g_tx_power_index(struct rtw_dev *rtwdev, tx_power += pwr_idx_2g->ht_1s_diff.bw20 * factor; if (above_2ss) tx_power += pwr_idx_2g->ht_2s_diff.bw20 * factor; + if (above_3ss) + tx_power += pwr_idx_2g->ht_3s_diff.bw20 * factor; + if (above_4ss) + tx_power += pwr_idx_2g->ht_4s_diff.bw20 * factor; break; case RTW_CHANNEL_WIDTH_40: /* bw40 is the base power */ if (above_2ss) tx_power += pwr_idx_2g->ht_2s_diff.bw40 * factor; + if (above_3ss) + tx_power += pwr_idx_2g->ht_3s_diff.bw40 * factor; + if (above_4ss) + tx_power += pwr_idx_2g->ht_4s_diff.bw40 * factor; break; } @@ -2006,19 +2055,23 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, u8 rate, u8 group) { const struct rtw_chip_info *chip = rtwdev->chip; - u8 tx_power; + bool above_2ss, above_3ss, above_4ss; + u8 factor = chip->txgi_factor; u8 upper, lower; bool mcs_rate; - bool above_2ss; - u8 factor = chip->txgi_factor; + u8 tx_power; tx_power = pwr_idx_5g->bw40_base[group]; - mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS15) || + mcs_rate = (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31) || (rate >= DESC_RATEVHT1SS_MCS0 && - rate <= DESC_RATEVHT2SS_MCS9); - above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) || + rate <= DESC_RATEVHT4SS_MCS9); + above_2ss = (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS31) || (rate >= DESC_RATEVHT2SS_MCS0); + above_3ss = (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS31) || + (rate >= DESC_RATEVHT3SS_MCS0); + above_4ss = (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31) || + (rate >= DESC_RATEVHT4SS_MCS0); if (!mcs_rate) { tx_power += pwr_idx_5g->ht_1s_diff.ofdm * factor; @@ -2033,11 +2086,19 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, tx_power += pwr_idx_5g->ht_1s_diff.bw20 * factor; if (above_2ss) tx_power += pwr_idx_5g->ht_2s_diff.bw20 * factor; + if (above_3ss) + tx_power += pwr_idx_5g->ht_3s_diff.bw20 * factor; + if (above_4ss) + tx_power += pwr_idx_5g->ht_4s_diff.bw20 * factor; break; case RTW_CHANNEL_WIDTH_40: /* bw40 is the base power */ if (above_2ss) tx_power += pwr_idx_5g->ht_2s_diff.bw40 * factor; + if (above_3ss) + tx_power += pwr_idx_5g->ht_3s_diff.bw40 * factor; + if (above_4ss) + tx_power += pwr_idx_5g->ht_4s_diff.bw40 * factor; break; case RTW_CHANNEL_WIDTH_80: /* the base idx of bw80 is the average of bw40+/bw40- */ @@ -2048,13 +2109,17 @@ static u8 rtw_phy_get_5g_tx_power_index(struct rtw_dev *rtwdev, tx_power += pwr_idx_5g->vht_1s_diff.bw80 * factor; if (above_2ss) tx_power += pwr_idx_5g->vht_2s_diff.bw80 * factor; + if (above_3ss) + tx_power += pwr_idx_5g->vht_3s_diff.bw80 * factor; + if (above_4ss) + tx_power += pwr_idx_5g->vht_4s_diff.bw80 * factor; break; } return tx_power; } -/* return RTW_RATE_SECTION_MAX to indicate rate is invalid */ +/* return RTW_RATE_SECTION_NUM to indicate rate is invalid */ static u8 rtw_phy_rate_to_rate_section(u8 rate) { if (rate >= DESC_RATE1M && rate <= DESC_RATE11M) @@ -2065,12 +2130,20 @@ static u8 rtw_phy_rate_to_rate_section(u8 rate) return RTW_RATE_SECTION_HT_1S; else if (rate >= DESC_RATEMCS8 && rate <= DESC_RATEMCS15) return RTW_RATE_SECTION_HT_2S; + else if (rate >= DESC_RATEMCS16 && rate <= DESC_RATEMCS23) + return RTW_RATE_SECTION_HT_3S; + else if (rate >= DESC_RATEMCS24 && rate <= DESC_RATEMCS31) + return RTW_RATE_SECTION_HT_4S; else if (rate >= DESC_RATEVHT1SS_MCS0 && rate <= DESC_RATEVHT1SS_MCS9) return RTW_RATE_SECTION_VHT_1S; else if (rate >= DESC_RATEVHT2SS_MCS0 && rate <= DESC_RATEVHT2SS_MCS9) return RTW_RATE_SECTION_VHT_2S; + else if (rate >= DESC_RATEVHT3SS_MCS0 && rate <= DESC_RATEVHT3SS_MCS9) + return RTW_RATE_SECTION_VHT_3S; + else if (rate >= DESC_RATEVHT4SS_MCS0 && rate <= DESC_RATEVHT4SS_MCS9) + return RTW_RATE_SECTION_VHT_4S; else - return RTW_RATE_SECTION_MAX; + return RTW_RATE_SECTION_NUM; } static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, @@ -2088,7 +2161,7 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, if (regd > RTW_REGD_WW) return power_limit; - if (rs == RTW_RATE_SECTION_MAX) + if (rs == RTW_RATE_SECTION_NUM) goto err; /* only 20M BW with cck and ofdm */ @@ -2096,7 +2169,7 @@ static s8 rtw_phy_get_tx_power_limit(struct rtw_dev *rtwdev, u8 band, bw = RTW_CHANNEL_WIDTH_20; /* only 20/40M BW with ht */ - if (rs == RTW_RATE_SECTION_HT_1S || rs == RTW_RATE_SECTION_HT_2S) + if (rate >= DESC_RATEMCS0 && rate <= DESC_RATEMCS31) bw = min_t(u8, bw, RTW_CHANNEL_WIDTH_40); /* select min power limit among [20M BW ~ current BW] */ @@ -2132,7 +2205,7 @@ static s8 rtw_phy_get_tx_power_sar(struct rtw_dev *rtwdev, u8 sar_band, .rs = rs, }; - if (rs == RTW_RATE_SECTION_MAX) + if (rs == RTW_RATE_SECTION_NUM) goto err; return rtw_query_sar(rtwdev, &arg); @@ -2214,14 +2287,14 @@ static void rtw_phy_set_tx_power_index_by_rs(struct rtw_dev *rtwdev, { struct rtw_hal *hal = &rtwdev->hal; u8 regd = rtw_regd_get(rtwdev); - u8 *rates; + const u8 *rates; u8 size; u8 rate; u8 pwr_idx; u8 bw; int i; - if (rs >= RTW_RATE_SECTION_MAX) + if (rs >= RTW_RATE_SECTION_NUM) return; rates = rtw_rate_section[rs]; @@ -2252,7 +2325,7 @@ static void rtw_phy_set_tx_power_level_by_path(struct rtw_dev *rtwdev, else rs = RTW_RATE_SECTION_OFDM; - for (; rs < RTW_RATE_SECTION_MAX; rs++) + for (; rs < RTW_RATE_SECTION_NUM; rs++) rtw_phy_set_tx_power_index_by_rs(rtwdev, ch, path, rs); } @@ -2274,13 +2347,13 @@ EXPORT_SYMBOL(rtw_phy_set_tx_power_level); static void rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path, - u8 rs, u8 size, u8 *rates) + u8 rs, u8 size, const u8 *rates) { u8 rate; u8 base_idx, rate_idx; s8 base_2g, base_5g; - if (rs >= RTW_RATE_SECTION_VHT_1S) + if (size == 10) /* VHT rates */ base_idx = rates[size - 3]; else base_idx = rates[size - 1]; @@ -2297,28 +2370,12 @@ rtw_phy_tx_power_by_rate_config_by_path(struct rtw_hal *hal, u8 path, void rtw_phy_tx_power_by_rate_config(struct rtw_hal *hal) { - u8 path; + u8 path, rs; - for (path = 0; path < RTW_RF_PATH_MAX; path++) { - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_CCK, - rtw_cck_size, rtw_cck_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_OFDM, - rtw_ofdm_size, rtw_ofdm_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_HT_1S, - rtw_ht_1s_size, rtw_ht_1s_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_HT_2S, - rtw_ht_2s_size, rtw_ht_2s_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_VHT_1S, - rtw_vht_1s_size, rtw_vht_1s_rates); - rtw_phy_tx_power_by_rate_config_by_path(hal, path, - RTW_RATE_SECTION_VHT_2S, - rtw_vht_2s_size, rtw_vht_2s_rates); - } + for (path = 0; path < RTW_RF_PATH_MAX; path++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) + rtw_phy_tx_power_by_rate_config_by_path(hal, path, rs, + rtw_rate_size[rs], rtw_rate_section[rs]); } static void @@ -2347,7 +2404,7 @@ void rtw_phy_tx_power_limit_config(struct rtw_hal *hal) for (regd = 0; regd < RTW_REGD_MAX; regd++) for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) __rtw_phy_tx_power_limit_config(hal, regd, bw, rs); } @@ -2383,7 +2440,7 @@ void rtw_phy_init_tx_power(struct rtw_dev *rtwdev) /* init tx power limit */ for (regd = 0; regd < RTW_REGD_MAX; regd++) for (bw = 0; bw < RTW_CHANNEL_WIDTH_MAX; bw++) - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) + for (rs = 0; rs < RTW_RATE_SECTION_NUM; rs++) rtw_phy_init_tx_power_limit(rtwdev, regd, bw, rs); } @@ -2401,32 +2458,56 @@ void rtw_phy_config_swing_table(struct rtw_dev *rtwdev, swing_table->n[RF_PATH_A] = tbl->pwrtrk_2g_ccka_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2g_cckb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2g_cckb_n; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_2g_cckc_p; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_2g_cckc_n; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_2g_cckd_p; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_2g_cckd_n; } else { swing_table->p[RF_PATH_A] = tbl->pwrtrk_2ga_p; swing_table->n[RF_PATH_A] = tbl->pwrtrk_2ga_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2gb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2gb_n; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_2gc_p; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_2gc_n; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_2gd_p; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_2gd_n; } } else if (IS_CH_5G_BAND_1(channel) || IS_CH_5G_BAND_2(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_1]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_1]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_1]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_1]; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_1]; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_1]; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_1]; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_1]; } else if (IS_CH_5G_BAND_3(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_2]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_2]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_2]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_2]; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_2]; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_2]; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_2]; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_2]; } else if (IS_CH_5G_BAND_4(channel)) { swing_table->p[RF_PATH_A] = tbl->pwrtrk_5ga_p[RTW_PWR_TRK_5G_3]; swing_table->n[RF_PATH_A] = tbl->pwrtrk_5ga_n[RTW_PWR_TRK_5G_3]; swing_table->p[RF_PATH_B] = tbl->pwrtrk_5gb_p[RTW_PWR_TRK_5G_3]; swing_table->n[RF_PATH_B] = tbl->pwrtrk_5gb_n[RTW_PWR_TRK_5G_3]; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_5gc_p[RTW_PWR_TRK_5G_3]; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_5gc_n[RTW_PWR_TRK_5G_3]; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_5gd_p[RTW_PWR_TRK_5G_3]; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_5gd_n[RTW_PWR_TRK_5G_3]; } else { swing_table->p[RF_PATH_A] = tbl->pwrtrk_2ga_p; swing_table->n[RF_PATH_A] = tbl->pwrtrk_2ga_n; swing_table->p[RF_PATH_B] = tbl->pwrtrk_2gb_p; swing_table->n[RF_PATH_B] = tbl->pwrtrk_2gb_n; + swing_table->p[RF_PATH_C] = tbl->pwrtrk_2gc_p; + swing_table->n[RF_PATH_C] = tbl->pwrtrk_2gc_n; + swing_table->p[RF_PATH_D] = tbl->pwrtrk_2gd_p; + swing_table->n[RF_PATH_D] = tbl->pwrtrk_2gd_n; } } EXPORT_SYMBOL(rtw_phy_config_swing_table); diff --git a/drivers/net/wireless/realtek/rtw88/phy.h b/drivers/net/wireless/realtek/rtw88/phy.h index ccfcbd3ced03..c9e6b869661d 100644 --- a/drivers/net/wireless/realtek/rtw88/phy.h +++ b/drivers/net/wireless/realtek/rtw88/phy.h @@ -7,14 +7,18 @@ #include "debug.h" -extern u8 rtw_cck_rates[]; -extern u8 rtw_ofdm_rates[]; -extern u8 rtw_ht_1s_rates[]; -extern u8 rtw_ht_2s_rates[]; -extern u8 rtw_vht_1s_rates[]; -extern u8 rtw_vht_2s_rates[]; -extern u8 *rtw_rate_section[]; -extern u8 rtw_rate_size[]; +extern const u8 rtw_cck_rates[]; +extern const u8 rtw_ofdm_rates[]; +extern const u8 rtw_ht_1s_rates[]; +extern const u8 rtw_ht_2s_rates[]; +extern const u8 rtw_vht_1s_rates[]; +extern const u8 rtw_vht_2s_rates[]; +extern const u8 rtw_ht_3s_rates[]; +extern const u8 rtw_ht_4s_rates[]; +extern const u8 rtw_vht_3s_rates[]; +extern const u8 rtw_vht_4s_rates[]; +extern const u8 * const rtw_rate_section[]; +extern const u8 rtw_rate_size[]; void rtw_phy_init(struct rtw_dev *rtwdev); void rtw_phy_dynamic_mechanism(struct rtw_dev *rtwdev); diff --git a/drivers/net/wireless/realtek/rtw88/reg.h b/drivers/net/wireless/realtek/rtw88/reg.h index e438405fba56..08e9494977e0 100644 --- a/drivers/net/wireless/realtek/rtw88/reg.h +++ b/drivers/net/wireless/realtek/rtw88/reg.h @@ -8,6 +8,7 @@ #define REG_SYS_FUNC_EN 0x0002 #define BIT_FEN_EN_25_1 BIT(13) #define BIT_FEN_ELDR BIT(12) +#define BIT_FEN_PCIEA BIT(6) #define BIT_FEN_CPUEN BIT(2) #define BIT_FEN_USBA BIT(2) #define BIT_FEN_BB_GLB_RST BIT(1) @@ -39,6 +40,9 @@ #define BIT_RF_RSTB BIT(1) #define BIT_RF_EN BIT(0) +#define REG_RF_CTRL1 0x0020 +#define REG_RF_CTRL2 0x0021 + #define REG_AFE_CTRL1 0x0024 #define BIT_MAC_CLK_SEL (BIT(20) | BIT(21)) #define REG_EFUSE_CTRL 0x0030 @@ -73,6 +77,8 @@ #define BIT_BT_PTA_EN BIT(5) #define BIT_WLRFE_4_5_EN BIT(2) +#define REG_GPIO_PIN_CTRL 0x0044 + #define REG_LED_CFG 0x004C #define BIT_LNAON_SEL_EN BIT(26) #define BIT_PAPE_SEL_EN BIT(25) @@ -110,6 +116,7 @@ #define BIT_SDIO_PAD_E5 BIT(18) #define REG_RF_B_CTRL 0x76 +#define REG_RF_CTRL3 0x0076 #define REG_AFE_CTRL_4 0x0078 #define BIT_CK320M_AFE_EN BIT(4) @@ -130,6 +137,7 @@ #define BIT_SHIFT_ROM_PGE 16 #define BIT_FW_INIT_RDY BIT(15) #define BIT_FW_DW_RDY BIT(14) +#define BIT_CPU_CLK_SEL (BIT(12) | BIT(13)) #define BIT_RPWM_TOGGLE BIT(7) #define BIT_RAM_DL_SEL BIT(7) /* legacy only */ #define BIT_DMEM_CHKSUM_OK BIT(6) @@ -147,7 +155,7 @@ BIT_CHECK_SUM_OK) #define FW_READY_LEGACY (BIT_MCUFWDL_RDY | BIT_FWDL_CHK_RPT | \ BIT_WINTINI_RDY | BIT_RAM_DL_SEL) -#define FW_READY_MASK 0xffff +#define FW_READY_MASK (0xffff & ~BIT_CPU_CLK_SEL) #define REG_MCU_TST_CFG 0x84 #define VAL_FW_TRIGGER 0x1 @@ -602,15 +610,25 @@ #define REG_CCA2ND 0x0838 #define REG_L1PKTH 0x0848 #define REG_CLKTRK 0x0860 +#define REG_CSI_MASK_SETTING1 0x0874 +#define REG_NBI_SETTING 0x087c +#define BIT_NBI_ENABLE BIT(13) +#define REG_CSI_FIX_MASK0 0x0880 +#define REG_CSI_FIX_MASK1 0x0884 +#define REG_CSI_FIX_MASK6 0x0898 +#define REG_CSI_FIX_MASK7 0x089c #define REG_ADCCLK 0x08AC #define REG_HSSI_READ 0x08B0 #define REG_FPGA0_XCD_RF_PARA 0x08B4 #define REG_RX_MCS_LIMIT 0x08BC #define REG_ADC160 0x08C4 +#define REG_DBGSEL 0x08fc #define REG_ANTSEL_SW 0x0900 #define REG_DAC_RSTB 0x090c +#define REG_PSD 0x0910 +#define BIT_PSD_INI GENMASK(23, 22) #define REG_SINGLE_TONE_CONT_TX 0x0914 - +#define REG_AGC_TABLE 0x0958 #define REG_RFE_CTRL_E 0x0974 #define REG_2ND_CCA_CTRL 0x0976 #define REG_IQK_COM00 0x0978 @@ -620,10 +638,18 @@ #define REG_FAS 0x09a4 #define REG_RXSB 0x0a00 +#define BIT_RXSB_ANA_DIV BIT(15) #define REG_CCK_RX 0x0a04 #define REG_CCK_PD_TH 0x0a0a - -#define REG_CCK0_FAREPORT 0xa2c +#define REG_PRECTRL 0x0a14 +#define BIT_DIS_CO_PATHSEL BIT(7) +#define BIT_IQ_WGT GENMASK(9, 8) +#define REG_CCA_MF 0x0a20 +#define BIT_MBC_WIN GENMASK(5, 4) +#define REG_CCK0_TX_FILTER1 0x0a20 +#define REG_CCK0_TX_FILTER2 0x0a24 +#define REG_CCK0_DEBUG_PORT 0x0a28 +#define REG_CCK0_FAREPORT 0x0a2c #define BIT_CCK0_2RX BIT(18) #define BIT_CCK0_MRC BIT(22) #define REG_FA_CCK 0x0a5c @@ -642,10 +668,18 @@ #define DIS_DPD_RATEVHT2SS_MCS1 BIT(9) #define DIS_DPD_RATEALL GENMASK(9, 0) +#define REG_CCA 0x0a70 +#define BIT_CCA_CO BIT(7) +#define REG_ANTSEL 0x0a74 +#define BIT_ANT_BYCO BIT(8) +#define REG_CCKTX 0x0a84 +#define BIT_CMB_CCA_2R BIT(28) + #define REG_CNTRST 0x0b58 #define REG_3WIRE_SWA 0x0c00 #define REG_RX_IQC_AB_A 0x0c10 +#define REG_RX_IQC_CD_A 0x0c14 #define REG_TXSCALE_A 0x0c1c #define BB_SWING_MASK GENMASK(31, 21) #define REG_TX_AGC_A_CCK_11_CCK_1 0xc20 @@ -673,7 +707,7 @@ #define REG_LSSI_WRITE_A 0x0c90 #define REG_PREDISTA 0x0c90 #define REG_TXAGCIDX 0x0c94 - +#define REG_TX_AGC_A 0x0c94 #define REG_RFE_PINMUX_A 0x0cb0 #define REG_RFE_INV_A 0x0cb4 #define REG_RFE_CTRL8 0x0cb4 @@ -682,6 +716,7 @@ #define DPDT_CTRL_PIN 0x77 #define RFE_INV_MASK 0x3ff00000 #define REG_RFECTL_A 0x0cb8 +#define REG_RFE_INV0 0x0cbc #define REG_RFE_INV8 0x0cbd #define BIT_MASK_RFE_INV89 GENMASK(1, 0) #define REG_RFE_INV16 0x0cbe @@ -702,6 +737,7 @@ #define REG_3WIRE_SWB 0x0e00 #define REG_RX_IQC_AB_B 0x0e10 +#define REG_RX_IQC_CD_B 0x0e14 #define REG_TXSCALE_B 0x0e1c #define REG_TX_AGC_B_CCK_11_CCK_1 0xe20 #define REG_TX_AGC_B_OFDM18_OFDM6 0xe24 @@ -728,6 +764,7 @@ #define REG_LSSI_WRITE_B 0x0e90 #define REG_PREDISTB 0x0e90 #define REG_INIDLYB 0x0e94 +#define REG_TX_AGC_B 0x0e94 #define REG_RFE_PINMUX_B 0x0eb0 #define REG_RFE_INV_B 0x0eb4 #define REG_RFECTL_B 0x0eb8 @@ -743,8 +780,11 @@ #define REG_CRC_HT 0x0f10 #define REG_CRC_OFDM 0x0f14 #define REG_FA_OFDM 0x0f48 +#define REG_DBGRPT 0x0fa0 #define REG_CCA_CCK 0x0fcc +#define REG_SYS_CFG3_8814A 0x1000 + #define REG_ANAPARSW_MAC_0 0x1010 #define BIT_CF_L_V2 GENMASK(29, 28) @@ -862,9 +902,27 @@ #define LTECOEX_WRITE_DATA REG_WL2LTECOEX_INDIRECT_ACCESS_WRITE_DATA_V1 #define LTECOEX_READ_DATA REG_WL2LTECOEX_INDIRECT_ACCESS_READ_DATA_V1 +#define REG_RX_IQC_AB_C 0x1810 +#define REG_RX_IQC_CD_C 0x1814 +#define REG_TXSCALE_C 0x181c +#define REG_CK_MONHC 0x185c +#define REG_AFE_PWR1_C 0x1860 #define REG_IGN_GNT_BT1 0x1860 +#define REG_TX_AGC_C 0x1894 +#define REG_RFE_PINMUX_C 0x18b4 #define REG_RFESEL_CTRL 0x1990 +#define REG_AGC_TBL 0x1998 + +#define REG_RX_IQC_AB_D 0x1a10 +#define REG_RX_IQC_CD_D 0x1a14 +#define REG_TXSCALE_D 0x1a1c +#define REG_CK_MONHD 0x1a5c +#define REG_AFE_PWR1_D 0x1a60 +#define REG_TX_AGC_D 0x1a94 +#define REG_RFE_PINMUX_D 0x1ab4 +#define REG_RFE_INVSEL_D 0x1abc +#define BIT_RFE_SELSW0_D GENMASK(27, 20) #define REG_NOMASK_TXBT 0x1ca7 #define REG_ANAPAR 0x1c30 @@ -905,6 +963,7 @@ #define RF18_BAND_MASK (BIT(16) | BIT(9) | BIT(8)) #define RF18_CHANNEL_MASK (MASKBYTE0) #define RF18_RFSI_MASK (BIT(18) | BIT(17)) +#define RF_RCK1_V1 0x1c #define RF_RCK 0x1d #define RF_MODE_TABLE_ADDR 0x30 #define RF_MODE_TABLE_DATA0 0x31 diff --git a/drivers/net/wireless/realtek/rtw88/rtw8703b.c b/drivers/net/wireless/realtek/rtw88/rtw8703b.c index 1d232adbdd7e..9e6700c43a63 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8703b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8703b.c @@ -519,15 +519,6 @@ static const struct rtw_rqpn rqpn_table_8703b[] = { RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, }; -/* Default power index table for RTL8703B, used if EFUSE does not - * contain valid data. Replaces EFUSE data from offset 0x10 (start of - * txpwr_idx_table). - */ -static const u8 rtw8703b_txpwr_idx_table[] = { - 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, - 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x02 -}; - static void try_mac_from_devicetree(struct rtw_dev *rtwdev) { struct device_node *node = rtwdev->dev->of_node; @@ -544,15 +535,9 @@ static void try_mac_from_devicetree(struct rtw_dev *rtwdev) } } -#define DBG_EFUSE_FIX(rtwdev, name) \ - rtw_dbg(rtwdev, RTW_DBG_EFUSE, "Fixed invalid EFUSE value: " \ - # name "=0x%x\n", rtwdev->efuse.name) - static int rtw8703b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; - u8 *pwr = (u8 *)efuse->txpwr_idx_table; - bool valid = false; int ret; ret = rtw8723x_read_efuse(rtwdev, log_map); @@ -562,51 +547,6 @@ static int rtw8703b_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) if (!is_valid_ether_addr(efuse->addr)) try_mac_from_devicetree(rtwdev); - /* If TX power index table in EFUSE is invalid, fall back to - * built-in table. - */ - for (int i = 0; i < ARRAY_SIZE(rtw8703b_txpwr_idx_table); i++) - if (pwr[i] != 0xff) { - valid = true; - break; - } - if (!valid) { - for (int i = 0; i < ARRAY_SIZE(rtw8703b_txpwr_idx_table); i++) - pwr[i] = rtw8703b_txpwr_idx_table[i]; - rtw_dbg(rtwdev, RTW_DBG_EFUSE, - "Replaced invalid EFUSE TX power index table."); - rtw8723x_debug_txpwr_limit(rtwdev, - efuse->txpwr_idx_table, 2); - } - - /* Override invalid antenna settings. */ - if (efuse->bt_setting == 0xff) { - /* shared antenna */ - efuse->bt_setting |= BIT(0); - /* RF path A */ - efuse->bt_setting &= ~BIT(6); - DBG_EFUSE_FIX(rtwdev, bt_setting); - } - - /* Override invalid board options: The coex code incorrectly - * assumes that if bits 6 & 7 are set the board doesn't - * support coex. Regd is also derived from rf_board_option and - * should be 0 if there's no valid data. - */ - if (efuse->rf_board_option == 0xff) { - efuse->regd = 0; - efuse->rf_board_option &= GENMASK(5, 0); - DBG_EFUSE_FIX(rtwdev, rf_board_option); - } - - /* Override invalid crystal cap setting, default comes from - * vendor driver. Chip specific. - */ - if (efuse->crystal_cap == 0xff) { - efuse->crystal_cap = 0x20; - DBG_EFUSE_FIX(rtwdev, crystal_cap); - } - return 0; } @@ -1904,6 +1844,7 @@ static const struct rtw_chip_ops rtw8703b_ops = { .set_antenna = NULL, .cfg_ldo25 = rtw8723x_cfg_ldo25, .efuse_grant = rtw8723x_efuse_grant, + .set_ampdu_factor = NULL, .false_alarm_statistics = rtw8723x_false_alarm_statistics, .phy_calibration = rtw8703b_phy_calibration, .dpk_track = NULL, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723cs.c b/drivers/net/wireless/realtek/rtw88/rtw8723cs.c index 8d38d36be8c0..1f98d35a8dd1 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723cs.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723cs.c @@ -19,7 +19,7 @@ static const struct sdio_device_id rtw_8723cs_id_table[] = { MODULE_DEVICE_TABLE(sdio, rtw_8723cs_id_table); static struct sdio_driver rtw_8723cs_driver = { - .name = "rtw8723cs", + .name = KBUILD_MODNAME, .id_table = rtw_8723cs_id_table, .probe = rtw_sdio_probe, .remove = rtw_sdio_remove, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723d.c b/drivers/net/wireless/realtek/rtw88/rtw8723d.c index eeca31bf71f1..31876e708f9e 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723d.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723d.c @@ -444,7 +444,7 @@ static u8 rtw8723d_iqk_check_tx_failed(struct rtw_dev *rtwdev, rtw_read32(rtwdev, REG_IQK_RES_TX), rtw_read32(rtwdev, REG_IQK_RES_TY)); rtw_dbg(rtwdev, RTW_DBG_RFK, - "[IQK] 0xe90(before IQK)= 0x%x, 0xe98(afer IQK) = 0x%x\n", + "[IQK] 0xe90(before IQK)= 0x%x, 0xe98(after IQK) = 0x%x\n", rtw_read32(rtwdev, 0xe90), rtw_read32(rtwdev, 0xe98)); @@ -472,7 +472,7 @@ static u8 rtw8723d_iqk_check_rx_failed(struct rtw_dev *rtwdev, rtw_read32(rtwdev, REG_IQK_RES_RY)); rtw_dbg(rtwdev, RTW_DBG_RFK, - "[IQK] 0xea0(before IQK)= 0x%x, 0xea8(afer IQK) = 0x%x\n", + "[IQK] 0xea0(before IQK)= 0x%x, 0xea8(after IQK) = 0x%x\n", rtw_read32(rtwdev, 0xea0), rtw_read32(rtwdev, 0xea8)); @@ -1404,6 +1404,7 @@ static const struct rtw_chip_ops rtw8723d_ops = { .set_antenna = NULL, .cfg_ldo25 = rtw8723x_cfg_ldo25, .efuse_grant = rtw8723x_efuse_grant, + .set_ampdu_factor = NULL, .false_alarm_statistics = rtw8723x_false_alarm_statistics, .phy_calibration = rtw8723d_phy_calibration, .cck_pd_set = rtw8723d_phy_cck_pd_set, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723de.c b/drivers/net/wireless/realtek/rtw88/rtw8723de.c index abbaafa32851..87c8bc9d18a9 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723de.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723de.c @@ -17,7 +17,7 @@ static const struct pci_device_id rtw_8723de_id_table[] = { MODULE_DEVICE_TABLE(pci, rtw_8723de_id_table); static struct pci_driver rtw_8723de_driver = { - .name = "rtw_8723de", + .name = KBUILD_MODNAME, .id_table = rtw_8723de_id_table, .probe = rtw_pci_probe, .remove = rtw_pci_remove, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723ds.c b/drivers/net/wireless/realtek/rtw88/rtw8723ds.c index e5b6960ba0a0..206b77e5b98e 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723ds.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723ds.c @@ -25,7 +25,7 @@ static const struct sdio_device_id rtw_8723ds_id_table[] = { MODULE_DEVICE_TABLE(sdio, rtw_8723ds_id_table); static struct sdio_driver rtw_8723ds_driver = { - .name = "rtw_8723ds", + .name = KBUILD_MODNAME, .probe = rtw_sdio_probe, .remove = rtw_sdio_remove, .id_table = rtw_8723ds_id_table, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723du.c b/drivers/net/wireless/realtek/rtw88/rtw8723du.c index 322a805da76b..b661a26e0e22 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723du.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723du.c @@ -24,7 +24,7 @@ static int rtw8723du_probe(struct usb_interface *intf, } static struct usb_driver rtw_8723du_driver = { - .name = "rtw_8723du", + .name = KBUILD_MODNAME, .id_table = rtw_8723du_id_table, .probe = rtw8723du_probe, .disconnect = rtw_usb_disconnect, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8723x.c b/drivers/net/wireless/realtek/rtw88/rtw8723x.c index 69f73cb5b4cd..4c77963fdd37 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8723x.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8723x.c @@ -69,6 +69,9 @@ static void __rtw8723x_lck(struct rtw_dev *rtwdev) #define DBG_EFUSE_2BYTE(rtwdev, map, name) \ rtw_dbg(rtwdev, RTW_DBG_EFUSE, # name "=0x%02x%02x\n", \ (map)->name[0], (map)->name[1]) +#define DBG_EFUSE_FIX(rtwdev, name) \ + rtw_dbg(rtwdev, RTW_DBG_EFUSE, "Fixed invalid EFUSE value: " \ + # name "=0x%x\n", rtwdev->efuse.name) static void rtw8723xe_efuse_debug(struct rtw_dev *rtwdev, struct rtw8723x_efuse *map) @@ -238,10 +241,21 @@ static void rtw8723xs_efuse_parsing(struct rtw_efuse *efuse, ether_addr_copy(efuse->addr, map->s.mac_addr); } +/* Default power index table for RTL8703B/RTL8723D, used if EFUSE does + * not contain valid data. Replaces EFUSE data from offset 0x10 (start + * of txpwr_idx_table). + */ +static const u8 rtw8723x_txpwr_idx_table[] = { + 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, + 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x02 +}; + static int __rtw8723x_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) { struct rtw_efuse *efuse = &rtwdev->efuse; + u8 *pwr = (u8 *)efuse->txpwr_idx_table; struct rtw8723x_efuse *map; + bool valid = false; int i; map = (struct rtw8723x_efuse *)log_map; @@ -279,6 +293,51 @@ static int __rtw8723x_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) return -EOPNOTSUPP; } + /* If TX power index table in EFUSE is invalid, fall back to + * built-in table. + */ + for (i = 0; i < ARRAY_SIZE(rtw8723x_txpwr_idx_table); i++) + if (pwr[i] != 0xff) { + valid = true; + break; + } + if (!valid) { + for (i = 0; i < ARRAY_SIZE(rtw8723x_txpwr_idx_table); i++) + pwr[i] = rtw8723x_txpwr_idx_table[i]; + rtw_dbg(rtwdev, RTW_DBG_EFUSE, + "Replaced invalid EFUSE TX power index table."); + rtw8723x_debug_txpwr_limit(rtwdev, + efuse->txpwr_idx_table, 2); + } + + /* Override invalid antenna settings. */ + if (efuse->bt_setting == 0xff) { + /* shared antenna */ + efuse->bt_setting |= BIT(0); + /* RF path A */ + efuse->bt_setting &= ~BIT(6); + DBG_EFUSE_FIX(rtwdev, bt_setting); + } + + /* Override invalid board options: The coex code incorrectly + * assumes that if bits 6 & 7 are set the board doesn't + * support coex. Regd is also derived from rf_board_option and + * should be 0 if there's no valid data. + */ + if (efuse->rf_board_option == 0xff) { + efuse->regd = 0; + efuse->rf_board_option &= GENMASK(5, 0); + DBG_EFUSE_FIX(rtwdev, rf_board_option); + } + + /* Override invalid crystal cap setting, default comes from + * vendor driver. Chip specific. + */ + if (efuse->crystal_cap == 0xff) { + efuse->crystal_cap = 0x20; + DBG_EFUSE_FIX(rtwdev, crystal_cap); + } + return 0; } diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812a.c b/drivers/net/wireless/realtek/rtw88/rtw8812a.c index f9ba2aa2928a..c2ef41767ff9 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8812a.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8812a.c @@ -925,6 +925,7 @@ static const struct rtw_chip_ops rtw8812a_ops = { .set_tx_power_index = rtw88xxa_set_tx_power_index, .cfg_ldo25 = rtw8812a_cfg_ldo25, .efuse_grant = rtw88xxa_efuse_grant, + .set_ampdu_factor = NULL, .false_alarm_statistics = rtw88xxa_false_alarm_statistics, .phy_calibration = rtw8812a_phy_calibration, .cck_pd_set = rtw88xxa_phy_cck_pd_set, @@ -1075,6 +1076,7 @@ const struct rtw_chip_info rtw8812a_hw_spec = { .rfe_defs = rtw8812a_rfe_defs, .rfe_defs_size = ARRAY_SIZE(rtw8812a_rfe_defs), .rx_ldpc = false, + .amsdu_in_ampdu = true, .hw_feature_report = false, .c2h_ra_report_size = 4, .old_datarate_fb_limit = true, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8812au.c b/drivers/net/wireless/realtek/rtw88/rtw8812au.c index e18995f4cc78..dfea89670372 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8812au.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8812au.c @@ -82,7 +82,7 @@ static const struct usb_device_id rtw_8812au_id_table[] = { MODULE_DEVICE_TABLE(usb, rtw_8812au_id_table); static struct usb_driver rtw_8812au_driver = { - .name = "rtw_8812au", + .name = KBUILD_MODNAME, .id_table = rtw_8812au_id_table, .probe = rtw_usb_probe, .disconnect = rtw_usb_disconnect, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.c b/drivers/net/wireless/realtek/rtw88/rtw8814a.c new file mode 100644 index 000000000000..44dd3090484b --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.c @@ -0,0 +1,2269 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include <linux/usb.h> +#include "main.h" +#include "coex.h" +#include "tx.h" +#include "phy.h" +#include "rtw8814a.h" +#include "rtw8814a_table.h" +#include "rtw88xxa.h" +#include "reg.h" +#include "debug.h" +#include "efuse.h" +#include "regd.h" +#include "usb.h" + +static void rtw8814a_efuse_grant(struct rtw_dev *rtwdev, bool on) +{ + if (on) { + rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON); + + rtw_write16_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_ELDR); + rtw_write16_set(rtwdev, REG_SYS_CLKR, + BIT_LOADER_CLK_EN | BIT_ANA8M); + } else { + rtw_write8(rtwdev, REG_EFUSE_ACCESS, EFUSE_ACCESS_OFF); + } +} + +static void rtw8814a_read_rfe_type(struct rtw_dev *rtwdev) +{ + struct rtw_efuse *efuse = &rtwdev->efuse; + + if (!(efuse->rfe_option & BIT(7))) + return; + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) + efuse->rfe_option = 0; + else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) + efuse->rfe_option = 1; +} + +static void rtw8814a_read_amplifier_type(struct rtw_dev *rtwdev) +{ + struct rtw_efuse *efuse = &rtwdev->efuse; + + switch (efuse->rfe_option) { + case 1: + /* Internal 2G */ + efuse->pa_type_2g = 0; + efuse->lna_type_2g = 0; + /* External 5G */ + efuse->pa_type_5g = BIT(0); + efuse->lna_type_5g = BIT(3); + break; + case 2 ... 5: + /* External everything */ + efuse->pa_type_2g = BIT(4); + efuse->lna_type_2g = BIT(3); + efuse->pa_type_5g = BIT(0); + efuse->lna_type_5g = BIT(3); + break; + case 6: + efuse->lna_type_5g = BIT(3); + break; + default: + break; + } +} + +static void rtw8814a_read_rf_type(struct rtw_dev *rtwdev, + struct rtw8814a_efuse *map) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct rtw_hal *hal = &rtwdev->hal; + + switch (map->trx_antenna_option) { + case 0xff: /* 4T4R */ + case 0xee: /* 3T3R */ + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && + rtwusb->udev->speed != USB_SPEED_SUPER) + hal->rf_type = RF_2T2R; + else + hal->rf_type = RF_3T3R; + + break; + case 0x66: /* 2T2R */ + case 0x6f: /* 2T4R */ + default: + hal->rf_type = RF_2T2R; + break; + } + + hal->rf_path_num = 4; + hal->rf_phy_num = 4; + + if (hal->rf_type == RF_3T3R) { + hal->antenna_rx = BB_PATH_ABC; + hal->antenna_tx = BB_PATH_ABC; + } else { + hal->antenna_rx = BB_PATH_AB; + hal->antenna_tx = BB_PATH_AB; + } +} + +static void rtw8814a_init_hwcap(struct rtw_dev *rtwdev) +{ + struct rtw_efuse *efuse = &rtwdev->efuse; + struct rtw_hal *hal = &rtwdev->hal; + + efuse->hw_cap.bw = BIT(RTW_CHANNEL_WIDTH_20) | + BIT(RTW_CHANNEL_WIDTH_40) | + BIT(RTW_CHANNEL_WIDTH_80); + efuse->hw_cap.ptcl = EFUSE_HW_CAP_PTCL_VHT; + + if (hal->rf_type == RF_3T3R) + efuse->hw_cap.nss = 3; + else + efuse->hw_cap.nss = 2; + + rtw_dbg(rtwdev, RTW_DBG_EFUSE, + "hw cap: hci=0x%02x, bw=0x%02x, ptcl=0x%02x, ant_num=%d, nss=%d\n", + efuse->hw_cap.hci, efuse->hw_cap.bw, efuse->hw_cap.ptcl, + efuse->hw_cap.ant_num, efuse->hw_cap.nss); +} + +static int rtw8814a_read_efuse(struct rtw_dev *rtwdev, u8 *log_map) +{ + struct rtw_efuse *efuse = &rtwdev->efuse; + struct rtw8814a_efuse *map; + int i; + + if (rtw_dbg_is_enabled(rtwdev, RTW_DBG_EFUSE)) + print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, + log_map, rtwdev->chip->log_efuse_size, true); + + map = (struct rtw8814a_efuse *)log_map; + + efuse->usb_mode_switch = u8_get_bits(map->usb_mode, BIT(4)); + efuse->rfe_option = map->rfe_option; + efuse->rf_board_option = map->rf_board_option; + efuse->crystal_cap = map->xtal_k; + efuse->channel_plan = map->channel_plan; + efuse->country_code[0] = map->country_code[0]; + efuse->country_code[1] = map->country_code[1]; + efuse->bt_setting = map->rf_bt_setting; + efuse->regd = map->rf_board_option & 0x7; + efuse->thermal_meter[RF_PATH_A] = map->thermal_meter; + efuse->thermal_meter_k = map->thermal_meter; + efuse->tx_bb_swing_setting_2g = map->tx_bb_swing_setting_2g; + efuse->tx_bb_swing_setting_5g = map->tx_bb_swing_setting_5g; + + rtw8814a_read_rfe_type(rtwdev); + rtw8814a_read_amplifier_type(rtwdev); + + /* Override rtw_chip_parameter_setup() */ + rtw8814a_read_rf_type(rtwdev, map); + + rtw8814a_init_hwcap(rtwdev); + + for (i = 0; i < 4; i++) + efuse->txpwr_idx_table[i] = map->txpwr_idx_table[i]; + + switch (rtw_hci_type(rtwdev)) { + case RTW_HCI_TYPE_USB: + ether_addr_copy(efuse->addr, map->u.mac_addr); + break; + case RTW_HCI_TYPE_PCIE: + ether_addr_copy(efuse->addr, map->e.mac_addr); + break; + case RTW_HCI_TYPE_SDIO: + default: + /* unsupported now */ + return -EOPNOTSUPP; + } + + return 0; +} + +static void rtw8814a_init_rfe_reg(struct rtw_dev *rtwdev) +{ + u8 rfe_option = rtwdev->efuse.rfe_option; + + if (rfe_option == 2 || rfe_option == 1) { + rtw_write32_mask(rtwdev, 0x1994, 0xf, 0xf); + rtw_write8_set(rtwdev, REG_GPIO_MUXCFG + 2, 0xf0); + } else if (rfe_option == 0) { + rtw_write32_mask(rtwdev, 0x1994, 0xf, 0xf); + rtw_write8_set(rtwdev, REG_GPIO_MUXCFG + 2, 0xc0); + } +} + +#define RTW_TXSCALE_SIZE 37 +static const u32 rtw8814a_txscale_tbl[RTW_TXSCALE_SIZE] = { + 0x081, 0x088, 0x090, 0x099, 0x0a2, 0x0ac, 0x0b6, 0x0c0, 0x0cc, 0x0d8, + 0x0e5, 0x0f2, 0x101, 0x110, 0x120, 0x131, 0x143, 0x156, 0x16a, 0x180, + 0x197, 0x1af, 0x1c8, 0x1e3, 0x200, 0x21e, 0x23e, 0x261, 0x285, 0x2ab, + 0x2d3, 0x2fe, 0x32b, 0x35c, 0x38e, 0x3c4, 0x3fe +}; + +static u32 rtw8814a_get_bb_swing(struct rtw_dev *rtwdev, u8 band, u8 rf_path) +{ + static const u32 swing2setting[4] = {0x200, 0x16a, 0x101, 0x0b6}; + struct rtw_efuse *efuse = &rtwdev->efuse; + u8 tx_bb_swing; + + if (band == RTW_BAND_2G) + tx_bb_swing = efuse->tx_bb_swing_setting_2g; + else + tx_bb_swing = efuse->tx_bb_swing_setting_5g; + + tx_bb_swing >>= 2 * rf_path; + tx_bb_swing &= 0x3; + + return swing2setting[tx_bb_swing]; +} + +static u8 rtw8814a_get_swing_index(struct rtw_dev *rtwdev) +{ + u32 swing, table_value; + u8 i; + + swing = rtw8814a_get_bb_swing(rtwdev, rtwdev->hal.current_band_type, + RF_PATH_A); + + for (i = 0; i < ARRAY_SIZE(rtw8814a_txscale_tbl); i++) { + table_value = rtw8814a_txscale_tbl[i]; + if (swing == table_value) + return i; + } + + return 24; +} + +static void rtw8814a_pwrtrack_init(struct rtw_dev *rtwdev) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + u8 path; + + dm_info->default_ofdm_index = rtw8814a_get_swing_index(rtwdev); + + for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) { + ewma_thermal_init(&dm_info->avg_thermal[path]); + dm_info->delta_power_index[path] = 0; + dm_info->delta_power_index_last[path] = 0; + } + dm_info->pwr_trk_triggered = false; + dm_info->pwr_trk_init_trigger = true; + dm_info->thermal_meter_k = rtwdev->efuse.thermal_meter_k; +} + +static void rtw8814a_config_trx_path(struct rtw_dev *rtwdev) +{ + /* RX CCK disable 2R CCA */ + rtw_write32_clr(rtwdev, REG_CCK0_FAREPORT, + BIT_CCK0_2RX | BIT_CCK0_MRC); + /* pathB tx on, path A/C/D tx off */ + rtw_write32_mask(rtwdev, REG_CCK_RX, 0xf0000000, 0x4); + /* pathB rx */ + rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0x5); +} + +static void rtw8814a_config_cck_rx_antenna_init(struct rtw_dev *rtwdev) +{ + /* CCK 2R CCA parameters */ + + /* Disable Ant diversity */ + rtw_write32_mask(rtwdev, REG_RXSB, BIT_RXSB_ANA_DIV, 0x0); + /* Concurrent CCA at LSB & USB */ + rtw_write32_mask(rtwdev, REG_CCA, BIT_CCA_CO, 0); + /* RX path diversity enable */ + rtw_write32_mask(rtwdev, REG_ANTSEL, BIT_ANT_BYCO, 0); + /* r_en_mrc_antsel */ + rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_DIS_CO_PATHSEL, 0); + /* MBC weighting */ + rtw_write32_mask(rtwdev, REG_CCA_MF, BIT_MBC_WIN, 1); + /* 2R CCA only */ + rtw_write32_mask(rtwdev, REG_CCKTX, BIT_CMB_CCA_2R, 1); +} + +static void rtw8814a_phy_set_param(struct rtw_dev *rtwdev) +{ + u32 crystal_cap, val32; + u8 val8, rf_path; + + /* power on BB/RF domain */ + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) + rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_USBA); + else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) + rtw_write8_set(rtwdev, REG_SYS_FUNC_EN, BIT_FEN_PCIEA); + + rtw_write8_set(rtwdev, REG_SYS_CFG3_8814A + 2, + BIT_FEN_BB_GLB_RST | BIT_FEN_BB_RSTB); + + /* Power on RF paths A..D */ + val8 = BIT_RF_EN | BIT_RF_RSTB | BIT_RF_SDM_RSTB; + rtw_write8(rtwdev, REG_RF_CTRL, val8); + rtw_write8(rtwdev, REG_RF_CTRL1, val8); + rtw_write8(rtwdev, REG_RF_CTRL2, val8); + rtw_write8(rtwdev, REG_RF_CTRL3, val8); + + rtw_load_table(rtwdev, rtwdev->chip->bb_tbl); + rtw_load_table(rtwdev, rtwdev->chip->agc_tbl); + + crystal_cap = rtwdev->efuse.crystal_cap & 0x3F; + crystal_cap |= crystal_cap << 6; + rtw_write32_mask(rtwdev, REG_AFE_CTRL3, 0x07ff8000, crystal_cap); + + rtw8814a_config_trx_path(rtwdev); + + for (rf_path = 0; rf_path < rtwdev->hal.rf_path_num; rf_path++) + rtw_load_table(rtwdev, rtwdev->chip->rf_tbl[rf_path]); + + val32 = rtw_read_rf(rtwdev, RF_PATH_A, RF_RCK1_V1, RFREG_MASK); + rtw_write_rf(rtwdev, RF_PATH_B, RF_RCK1_V1, RFREG_MASK, val32); + rtw_write_rf(rtwdev, RF_PATH_C, RF_RCK1_V1, RFREG_MASK, val32); + rtw_write_rf(rtwdev, RF_PATH_D, RF_RCK1_V1, RFREG_MASK, val32); + + rtw_write32_set(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST); + + rtw_write8(rtwdev, REG_HWSEQ_CTRL, 0xFF); + + rtw_write32(rtwdev, REG_BAR_MODE_CTRL, 0x0201ffff); + + rtw_write8(rtwdev, REG_MISC_CTRL, BIT_DIS_SECOND_CCA); + + rtw_write8(rtwdev, REG_NAV_CTRL + 2, 0); + + rtw_write8_clr(rtwdev, REG_GPIO_MUXCFG, BIT(5)); + + rtw8814a_config_cck_rx_antenna_init(rtwdev); + + rtw_phy_init(rtwdev); + rtw8814a_pwrtrack_init(rtwdev); + + rtw8814a_init_rfe_reg(rtwdev); + + rtw_write8_clr(rtwdev, REG_QUEUE_CTRL, BIT(3)); + + rtw_write8(rtwdev, REG_NAV_CTRL + 2, 235); + + /* enable Tx report. */ + rtw_write8(rtwdev, REG_FWHW_TXQ_CTRL + 1, 0x1F); + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) { + /* Reset USB mode switch setting */ + rtw_write8(rtwdev, REG_SYS_SDIO_CTRL, 0x0); + rtw_write8(rtwdev, REG_ACLK_MON, 0x0); + } +} + +static void rtw8814ae_enable_rf_1_2v(struct rtw_dev *rtwdev) +{ + /* This is for fullsize card, because GPIO7 there is floating. + * We should pull GPIO7 high to enable RF 1.2V Switch Power Supply + */ + + /* 1. set 0x40[1:0] to 0, BIT_GPIOSEL=0, select pin as GPIO */ + rtw_write8_clr(rtwdev, REG_GPIO_MUXCFG, BIT(1) | BIT(0)); + + /* 2. set 0x44[31] to 0 + * mode=0: data port; + * mode=1 and BIT_GPIO_IO_SEL=0: interrupt mode; + */ + rtw_write8_clr(rtwdev, REG_GPIO_PIN_CTRL + 3, BIT(7)); + + /* 3. data mode + * 3.1 set 0x44[23] to 1 + * sel=0: input; + * sel=1: output; + */ + rtw_write8_set(rtwdev, REG_GPIO_PIN_CTRL + 2, BIT(7)); + + /* 3.2 set 0x44[15] to 1 + * output high value; + */ + rtw_write8_set(rtwdev, REG_GPIO_PIN_CTRL + 1, BIT(7)); +} + +static int rtw8814a_mac_init(struct rtw_dev *rtwdev) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + + rtw_write16(rtwdev, REG_CR, + MAC_TRX_ENABLE | BIT_MAC_SEC_EN | BIT_32K_CAL_TMR_EN); + + rtw_load_table(rtwdev, rtwdev->chip->mac_tbl); + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB) + rtw_write8(rtwdev, REG_AUTO_LLT_V1 + 3, + rtwdev->chip->usb_tx_agg_desc_num << 1); + + rtw_write32(rtwdev, REG_HIMR0, 0); + rtw_write32(rtwdev, REG_HIMR1, 0); + + rtw_write32_mask(rtwdev, REG_RRSR, 0xfffff, 0xfffff); + + rtw_write16(rtwdev, REG_RETRY_LIMIT, 0x3030); + + rtw_write16(rtwdev, REG_RXFLTMAP0, 0xffff); + rtw_write16(rtwdev, REG_RXFLTMAP1, 0x0400); + rtw_write16(rtwdev, REG_RXFLTMAP2, 0xffff); + + rtw_write8(rtwdev, REG_MAX_AGGR_NUM, 0x36); + rtw_write8(rtwdev, REG_MAX_AGGR_NUM + 1, 0x36); + + /* Set Spec SIFS (used in NAV) */ + rtw_write16(rtwdev, REG_SPEC_SIFS, 0x100a); + rtw_write16(rtwdev, REG_MAC_SPEC_SIFS, 0x100a); + + /* Set SIFS for CCK */ + rtw_write16(rtwdev, REG_SIFS, 0x100a); + + /* Set SIFS for OFDM */ + rtw_write16(rtwdev, REG_SIFS + 2, 0x100a); + + /* TXOP */ + rtw_write32(rtwdev, REG_EDCA_BE_PARAM, 0x005EA42B); + rtw_write32(rtwdev, REG_EDCA_BK_PARAM, 0x0000A44F); + rtw_write32(rtwdev, REG_EDCA_VI_PARAM, 0x005EA324); + rtw_write32(rtwdev, REG_EDCA_VO_PARAM, 0x002FA226); + + rtw_write8_set(rtwdev, REG_FWHW_TXQ_CTRL, BIT(7)); + + rtw_write8(rtwdev, REG_ACKTO, 0x80); + + rtw_write16(rtwdev, REG_BCN_CTRL, + BIT_DIS_TSF_UDT | (BIT_DIS_TSF_UDT << 8)); + rtw_write32_mask(rtwdev, REG_TBTT_PROHIBIT, 0xfffff, WLAN_TBTT_TIME); + rtw_write8(rtwdev, REG_DRVERLYINT, 0x05); + rtw_write8(rtwdev, REG_BCNDMATIM, WLAN_BCN_DMA_TIME); + rtw_write16(rtwdev, REG_BCNTCFG, 0x4413); + rtw_write8(rtwdev, REG_BCN_MAX_ERR, 0xFF); + + rtw_write32(rtwdev, REG_FAST_EDCA_VOVI_SETTING, 0x08070807); + rtw_write32(rtwdev, REG_FAST_EDCA_BEBK_SETTING, 0x08070807); + + if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_USB && + rtwusb->udev->speed == USB_SPEED_SUPER) { + /* Disable U1/U2 Mode to avoid 2.5G spur in USB3.0. */ + rtw_write8_clr(rtwdev, REG_USB_MOD, BIT(4) | BIT(3)); + /* To avoid usb 3.0 H2C fail. */ + rtw_write16(rtwdev, 0xf002, 0); + + rtw_write8_clr(rtwdev, REG_SW_AMPDU_BURST_MODE_CTRL, + BIT_PRE_TX_CMD); + } else if (rtw_hci_type(rtwdev) == RTW_HCI_TYPE_PCIE) { + rtw8814ae_enable_rf_1_2v(rtwdev); + + /* Force the antenna b to wifi. */ + rtw_write8_set(rtwdev, REG_PAD_CTRL1, BIT(2)); + rtw_write8_set(rtwdev, REG_PAD_CTRL1 + 1, BIT(0)); + rtw_write8_set(rtwdev, REG_LED_CFG + 3, + (BIT(27) | BIT_DPDT_WL_SEL) >> 24); + } + + return 0; +} + +static void rtw8814a_set_rfe_reg_24g(struct rtw_dev *rtwdev) +{ + switch (rtwdev->efuse.rfe_option) { + case 2: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x72707270); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x72707270); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x72707270); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77707770); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x72); + + break; + case 1: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77777777); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x77); + + break; + case 0: + default: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777); + /* Is it not necessary to set REG_RFE_PINMUX_D ? */ + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x77); + + break; + } +} + +static void rtw8814a_set_rfe_reg_5g(struct rtw_dev *rtwdev) +{ + switch (rtwdev->efuse.rfe_option) { + case 2: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x37173717); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x37173717); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x37173717); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77177717); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x37); + + break; + case 1: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x33173317); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x33173317); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x33173317); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77177717); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x33); + + break; + case 0: + default: + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x54775477); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x54775477); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x54775477); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x54775477); + + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, + BIT_RFE_SELSW0_D, 0x54); + + break; + } +} + +static void rtw8814a_set_channel_bb_swing(struct rtw_dev *rtwdev, u8 band) +{ + rtw_write32_mask(rtwdev, REG_TXSCALE_A, BB_SWING_MASK, + rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_A)); + rtw_write32_mask(rtwdev, REG_TXSCALE_B, BB_SWING_MASK, + rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_B)); + rtw_write32_mask(rtwdev, REG_TXSCALE_C, BB_SWING_MASK, + rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_C)); + rtw_write32_mask(rtwdev, REG_TXSCALE_D, BB_SWING_MASK, + rtw8814a_get_bb_swing(rtwdev, band, RF_PATH_D)); + rtw8814a_pwrtrack_init(rtwdev); +} + +static void rtw8814a_set_bw_reg_adc(struct rtw_dev *rtwdev, u8 bw) +{ + u32 adc = 0; + + if (bw == RTW_CHANNEL_WIDTH_20) + adc = 0; + else if (bw == RTW_CHANNEL_WIDTH_40) + adc = 1; + else if (bw == RTW_CHANNEL_WIDTH_80) + adc = 2; + + rtw_write32_mask(rtwdev, REG_ADCCLK, BIT(1) | BIT(0), adc); +} + +static void rtw8814a_set_bw_reg_agc(struct rtw_dev *rtwdev, u8 new_band, u8 bw) +{ + u32 agc = 7; + + if (bw == RTW_CHANNEL_WIDTH_20) { + agc = 6; + } else if (bw == RTW_CHANNEL_WIDTH_40) { + if (new_band == RTW_BAND_5G) + agc = 8; + else + agc = 7; + } else if (bw == RTW_CHANNEL_WIDTH_80) { + agc = 3; + } + + rtw_write32_mask(rtwdev, REG_CCASEL, 0xf000, agc); +} + +static void rtw8814a_switch_band(struct rtw_dev *rtwdev, u8 new_band, u8 bw) +{ + /* Clear 0x1000[16], When this bit is set to 0, CCK and OFDM + * are disabled, and clock are gated. Otherwise, CCK and OFDM + * are enabled. + */ + rtw_write8_clr(rtwdev, REG_SYS_CFG3_8814A + 2, BIT_FEN_BB_RSTB); + + if (new_band == RTW_BAND_2G) { + rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 0); + + rtw8814a_set_rfe_reg_24g(rtwdev); + + rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0x2); + rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0x5); + + rtw_write32_mask(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST, 0x3); + + rtw_write8(rtwdev, REG_CCK_CHECK, 0); + + rtw_write32_mask(rtwdev, 0xa80, BIT(18), 0); + } else { + rtw_write8(rtwdev, REG_CCK_CHECK, BIT_CHECK_CCK_EN); + + /* Enable CCK Tx function, even when CCK is off */ + rtw_write32_mask(rtwdev, 0xa80, BIT(18), 1); + + rtw8814a_set_rfe_reg_5g(rtwdev); + + rtw_write32_mask(rtwdev, REG_TXPSEL, 0xf0, 0x0); + rtw_write32_mask(rtwdev, REG_CCK_RX, 0x0f000000, 0xf); + + rtw_write32_mask(rtwdev, REG_RXPSEL, BIT_RX_PSEL_RST, 0x2); + } + + rtw8814a_set_channel_bb_swing(rtwdev, new_band); + + rtw8814a_set_bw_reg_adc(rtwdev, bw); + rtw8814a_set_bw_reg_agc(rtwdev, new_band, bw); + + rtw_write8_set(rtwdev, REG_SYS_CFG3_8814A + 2, BIT_FEN_BB_RSTB); +} + +static void rtw8814a_switch_channel(struct rtw_dev *rtwdev, u8 channel) +{ + struct rtw_hal *hal = &rtwdev->hal; + u32 fc_area, rf_mod_ag, cfgch; + u8 path; + + switch (channel) { + case 36 ... 48: + fc_area = 0x494; + break; + case 50 ... 64: + fc_area = 0x453; + break; + case 100 ... 116: + fc_area = 0x452; + break; + default: + if (channel >= 118) + fc_area = 0x412; + else + fc_area = 0x96a; + break; + } + + rtw_write32_mask(rtwdev, REG_CLKTRK, 0x1ffe0000, fc_area); + + for (path = 0; path < hal->rf_path_num; path++) { + switch (channel) { + case 36 ... 64: + rf_mod_ag = 0x101; + break; + case 100 ... 140: + rf_mod_ag = 0x301; + break; + default: + if (channel > 140) + rf_mod_ag = 0x501; + else + rf_mod_ag = 0x000; + break; + } + + cfgch = (rf_mod_ag << 8) | channel; + + rtw_write_rf(rtwdev, path, RF_CFGCH, + RF18_RFSI_MASK | RF18_BAND_MASK | RF18_CHANNEL_MASK, cfgch); + } + + switch (channel) { + case 36 ... 64: + rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 1); + break; + case 100 ... 144: + rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 2); + break; + default: + if (channel >= 149) + rtw_write32_mask(rtwdev, REG_AGC_TABLE, 0x1f, 3); + + break; + } +} + +static void rtw8814a_24g_cck_tx_dfir(struct rtw_dev *rtwdev, u8 channel) +{ + if (channel >= 1 && channel <= 11) { + rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030); + rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x090e1317); + rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000204); + } else if (channel >= 12 && channel <= 13) { + rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030); + rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x090e1217); + rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000305); + } else if (channel == 14) { + rtw_write32(rtwdev, REG_CCK0_TX_FILTER1, 0x1a1b0030); + rtw_write32(rtwdev, REG_CCK0_TX_FILTER2, 0x00000E17); + rtw_write32(rtwdev, REG_CCK0_DEBUG_PORT, 0x00000000); + } +} + +static void rtw8814a_set_bw_reg_mac(struct rtw_dev *rtwdev, u8 bw) +{ + u16 val16 = rtw_read16(rtwdev, REG_WMAC_TRXPTCL_CTL); + + val16 &= ~BIT_RFMOD; + if (bw == RTW_CHANNEL_WIDTH_80) + val16 |= BIT_RFMOD_80M; + else if (bw == RTW_CHANNEL_WIDTH_40) + val16 |= BIT_RFMOD_40M; + + rtw_write16(rtwdev, REG_WMAC_TRXPTCL_CTL, val16); +} + +static void rtw8814a_set_bw_rf(struct rtw_dev *rtwdev, u8 bw) +{ + u8 path; + + for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) { + switch (bw) { + case RTW_CHANNEL_WIDTH_5: + case RTW_CHANNEL_WIDTH_10: + case RTW_CHANNEL_WIDTH_20: + default: + rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 3); + break; + case RTW_CHANNEL_WIDTH_40: + rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 1); + break; + case RTW_CHANNEL_WIDTH_80: + rtw_write_rf(rtwdev, path, RF_CFGCH, RF18_BW_MASK, 0); + break; + } + } +} + +static void rtw8814a_adc_clk(struct rtw_dev *rtwdev) +{ + static const u32 rxiqc_reg[2][4] = { + { REG_RX_IQC_AB_A, REG_RX_IQC_AB_B, + REG_RX_IQC_AB_C, REG_RX_IQC_AB_D }, + { REG_RX_IQC_CD_A, REG_RX_IQC_CD_B, + REG_RX_IQC_CD_C, REG_RX_IQC_CD_D } + }; + u32 bb_reg_8fc, bb_reg_808, rxiqc[4]; + u32 i = 0, mac_active = 1; + u8 mac_reg_522; + + if (rtwdev->hal.cut_version != RTW_CHIP_VER_CUT_A) + return; + + /* 1 Step1. MAC TX pause */ + mac_reg_522 = rtw_read8(rtwdev, REG_TXPAUSE); + bb_reg_8fc = rtw_read32(rtwdev, REG_DBGSEL); + bb_reg_808 = rtw_read32(rtwdev, REG_RXPSEL); + rtw_write8(rtwdev, REG_TXPAUSE, 0x3f); + + /* 1 Step 2. Backup rxiqc & rxiqc = 0 */ + for (i = 0; i < 4; i++) { + rxiqc[i] = rtw_read32(rtwdev, rxiqc_reg[0][i]); + rtw_write32(rtwdev, rxiqc_reg[0][i], 0x0); + rtw_write32(rtwdev, rxiqc_reg[1][i], 0x0); + } + rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_IQ_WGT, 0x3); + i = 0; + + /* 1 Step 3. Monitor MAC IDLE */ + rtw_write32(rtwdev, REG_DBGSEL, 0x0); + while (mac_active) { + mac_active = rtw_read32(rtwdev, REG_DBGRPT) & 0x803e0008; + i++; + if (i > 1000) + break; + } + + /* 1 Step 4. ADC clk flow */ + rtw_write8(rtwdev, REG_RXPSEL, 0x11); + rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x1); + rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2) | BIT(1), 0x3); + rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x1); + + /* 0xc1c/0xe1c/0x181c/0x1a1c[4] must=1 to ensure table can be + * written when bbrstb=0 + * 0xc60/0xe60/0x1860/0x1a60[15] always = 1 after this line + * 0xc60/0xe60/0x1860/0x1a60[14] always = 0 bcz its error in A-cut + */ + + /* power_off/clk_off @ anapar_state=idle mode */ + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x15800002); + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x01808003); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x15800002); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x01808003); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x15800002); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x01808003); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x15800002); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x01808003); + + rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2), 0x0); + rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x0); + /* [19] = 1 to turn off ADC */ + rtw_write32(rtwdev, REG_CK_MONHA, 0x0D080058); + rtw_write32(rtwdev, REG_CK_MONHB, 0x0D080058); + rtw_write32(rtwdev, REG_CK_MONHC, 0x0D080058); + rtw_write32(rtwdev, REG_CK_MONHD, 0x0D080058); + + /* power_on/clk_off */ + /* [19] = 0 to turn on ADC */ + rtw_write32(rtwdev, REG_CK_MONHA, 0x0D000058); + rtw_write32(rtwdev, REG_CK_MONHB, 0x0D000058); + rtw_write32(rtwdev, REG_CK_MONHC, 0x0D000058); + rtw_write32(rtwdev, REG_CK_MONHD, 0x0D000058); + + /* power_on/clk_on @ anapar_state=BT mode */ + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05808032); + rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2), 0x1); + rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x1); + + /* recover original setting @ anapar_state=BT mode */ + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05808032); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05808032); + + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x05800002); + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x05800002); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x05800002); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x05800002); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x07808003); + + rtw_write8_mask(rtwdev, REG_GNT_BT, BIT(2) | BIT(1), 0x0); + rtw_write32_mask(rtwdev, REG_CCK_RPT_FORMAT, BIT(2), 0x0); + rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x0); + + /* 1 Step 5. Recover MAC TX & IQC */ + rtw_write8(rtwdev, REG_TXPAUSE, mac_reg_522); + rtw_write32(rtwdev, REG_DBGSEL, bb_reg_8fc); + rtw_write32(rtwdev, REG_RXPSEL, bb_reg_808); + for (i = 0; i < 4; i++) { + rtw_write32(rtwdev, rxiqc_reg[0][i], rxiqc[i]); + rtw_write32(rtwdev, rxiqc_reg[1][i], 0x01000000); + } + rtw_write32_mask(rtwdev, REG_PRECTRL, BIT_IQ_WGT, 0x0); +} + +static void rtw8814a_spur_calibration_ch140(struct rtw_dev *rtwdev, u8 channel) +{ + struct rtw_hal *hal = &rtwdev->hal; + + /* Add for 8814AE module ch140 MP Rx */ + if (channel == 140) { + if (hal->ch_param[0] == 0) + hal->ch_param[0] = rtw_read32(rtwdev, REG_CCASEL); + if (hal->ch_param[1] == 0) + hal->ch_param[1] = rtw_read32(rtwdev, REG_PDMFTH); + + rtw_write32(rtwdev, REG_CCASEL, 0x75438170); + rtw_write32(rtwdev, REG_PDMFTH, 0x79a18a0a); + } else { + if (rtw_read32(rtwdev, REG_CCASEL) == 0x75438170 && + hal->ch_param[0] != 0) + rtw_write32(rtwdev, REG_CCASEL, hal->ch_param[0]); + + if (rtw_read32(rtwdev, REG_PDMFTH) == 0x79a18a0a && + hal->ch_param[1] != 0) + rtw_write32(rtwdev, REG_PDMFTH, hal->ch_param[1]); + + hal->ch_param[0] = rtw_read32(rtwdev, REG_CCASEL); + hal->ch_param[1] = rtw_read32(rtwdev, REG_PDMFTH); + } +} + +static void rtw8814a_set_nbi_reg(struct rtw_dev *rtwdev, u32 tone_idx) +{ + /* tone_idx X 10 */ + static const u32 nbi_128[] = { + 25, 55, 85, 115, 135, + 155, 185, 205, 225, 245, + 265, 285, 305, 335, 355, + 375, 395, 415, 435, 455, + 485, 505, 525, 555, 585, 615, 635 + }; + u32 reg_idx = 0; + u32 i; + + for (i = 0; i < ARRAY_SIZE(nbi_128); i++) { + if (tone_idx < nbi_128[i]) { + reg_idx = i + 1; + break; + } + } + + rtw_write32_mask(rtwdev, REG_NBI_SETTING, 0xfc000, reg_idx); +} + +static void rtw8814a_nbi_setting(struct rtw_dev *rtwdev, u32 ch, u32 f_intf) +{ + u32 fc, int_distance, tone_idx; + + fc = 2412 + (ch - 1) * 5; + int_distance = abs_diff(fc, f_intf); + + /* 10 * (int_distance / 0.3125) */ + tone_idx = int_distance << 5; + + rtw8814a_set_nbi_reg(rtwdev, tone_idx); + + rtw_write32_mask(rtwdev, REG_NBI_SETTING, BIT_NBI_ENABLE, 1); +} + +static void rtw8814a_spur_nbi_setting(struct rtw_dev *rtwdev) +{ + u8 primary_channel = rtwdev->hal.primary_channel; + u8 rfe_type = rtwdev->efuse.rfe_option; + + if (rfe_type != 0 && rfe_type != 1 && rfe_type != 6 && rfe_type != 7) + return; + + if (primary_channel == 14) + rtw8814a_nbi_setting(rtwdev, primary_channel, 2480); + else if (primary_channel >= 4 && primary_channel <= 8) + rtw8814a_nbi_setting(rtwdev, primary_channel, 2440); + else + rtw_write32_mask(rtwdev, REG_NBI_SETTING, BIT_NBI_ENABLE, 0); +} + +/* A workaround to eliminate the 5280 MHz & 5600 MHz & 5760 MHz spur of 8814A */ +static void rtw8814a_spur_calibration(struct rtw_dev *rtwdev, u8 channel, u8 bw) +{ + u8 rfe_type = rtwdev->efuse.rfe_option; + bool reset_nbi_csi = true; + + if (rfe_type == 0) { + switch (bw) { + case RTW_CHANNEL_WIDTH_40: + if (channel == 54 || channel == 118) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x3e >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } else if (channel == 151) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x1e >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK0, + BIT(16), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } + break; + case RTW_CHANNEL_WIDTH_80: + if (channel == 58 || channel == 122) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x3a >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7, + BIT(0), 1); + + reset_nbi_csi = false; + } else if (channel == 155) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x5a >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK6, + BIT(16), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } + break; + case RTW_CHANNEL_WIDTH_20: + if (channel == 153) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x1e >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7, + BIT(16), 1); + + reset_nbi_csi = false; + } + + rtw8814a_spur_calibration_ch140(rtwdev, channel); + break; + default: + break; + } + } else if (rfe_type == 1 || rfe_type == 2) { + switch (bw) { + case RTW_CHANNEL_WIDTH_20: + if (channel == 153) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x1E >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK7, + BIT(16), 1); + + reset_nbi_csi = false; + } + break; + case RTW_CHANNEL_WIDTH_40: + if (channel == 151) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x1e >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK0, + BIT(16), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } + break; + case RTW_CHANNEL_WIDTH_80: + if (channel == 155) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0x5a >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, + BIT(0), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32_mask(rtwdev, REG_CSI_FIX_MASK6, + BIT(16), 1); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + + reset_nbi_csi = false; + } + break; + default: + break; + } + } + + if (reset_nbi_csi) { + rtw_write32_mask(rtwdev, REG_NBI_SETTING, + 0x000fe000, 0xfc >> 1); + rtw_write32_mask(rtwdev, REG_CSI_MASK_SETTING1, BIT(0), 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK0, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK1, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK6, 0); + rtw_write32(rtwdev, REG_CSI_FIX_MASK7, 0); + } + + rtw8814a_spur_nbi_setting(rtwdev); +} + +static void rtw8814a_set_bw_mode(struct rtw_dev *rtwdev, u8 new_band, + u8 channel, u8 bw, u8 primary_chan_idx) +{ + u8 txsc40 = 0, txsc20, txsc; + + rtw8814a_set_bw_reg_mac(rtwdev, bw); + + txsc20 = primary_chan_idx; + if (bw == RTW_CHANNEL_WIDTH_80) { + if (txsc20 == RTW_SC_20_UPPER || txsc20 == RTW_SC_20_UPMOST) + txsc40 = RTW_SC_40_UPPER; + else + txsc40 = RTW_SC_40_LOWER; + } + + txsc = BIT_TXSC_20M(txsc20) | BIT_TXSC_40M(txsc40); + rtw_write8(rtwdev, REG_DATA_SC, txsc); + + rtw8814a_set_bw_reg_adc(rtwdev, bw); + rtw8814a_set_bw_reg_agc(rtwdev, new_band, bw); + + if (bw == RTW_CHANNEL_WIDTH_80) { + rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3c, txsc); + } else if (bw == RTW_CHANNEL_WIDTH_40) { + rtw_write32_mask(rtwdev, REG_ADCCLK, 0x3c, txsc); + + if (txsc == RTW_SC_20_UPPER) + rtw_write32_set(rtwdev, REG_RXSB, BIT(4)); + else + rtw_write32_clr(rtwdev, REG_RXSB, BIT(4)); + } + + rtw8814a_set_bw_rf(rtwdev, bw); + + rtw8814a_adc_clk(rtwdev); + + rtw8814a_spur_calibration(rtwdev, channel, bw); +} + +static void rtw8814a_set_channel(struct rtw_dev *rtwdev, u8 channel, u8 bw, + u8 primary_chan_idx) +{ + u8 old_band, new_band; + + if (rtw_read8(rtwdev, REG_CCK_CHECK) & BIT_CHECK_CCK_EN) + old_band = RTW_BAND_5G; + else + old_band = RTW_BAND_2G; + + if (channel > 14) + new_band = RTW_BAND_5G; + else + new_band = RTW_BAND_2G; + + if (new_band != old_band) + rtw8814a_switch_band(rtwdev, new_band, bw); + + rtw8814a_switch_channel(rtwdev, channel); + + rtw8814a_24g_cck_tx_dfir(rtwdev, channel); + + rtw8814a_set_bw_mode(rtwdev, new_band, channel, bw, primary_chan_idx); +} + +static s8 rtw8814a_cck_rx_pwr(u8 lna_idx, u8 vga_idx) +{ + s8 rx_pwr_all = 0; + + switch (lna_idx) { + case 7: + rx_pwr_all = -38 - 2 * vga_idx; + break; + case 5: + rx_pwr_all = -28 - 2 * vga_idx; + break; + case 3: + rx_pwr_all = -8 - 2 * vga_idx; + break; + case 2: + rx_pwr_all = -1 - 2 * vga_idx; + break; + default: + break; + } + + return rx_pwr_all; +} + +static void rtw8814a_query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, + struct rtw_rx_pkt_stat *pkt_stat) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + struct rtw_jaguar_phy_status_rpt *rpt; + u8 gain[RTW_RF_PATH_MAX], rssi, i; + s8 rx_pwr_db, middle1, middle2; + s8 snr[RTW_RF_PATH_MAX]; + s8 evm[RTW_RF_PATH_MAX]; + u8 rfmode, subchannel; + u8 lna, vga; + s8 cfo[2]; + + rpt = (struct rtw_jaguar_phy_status_rpt *)phy_status; + + pkt_stat->bw = RTW_CHANNEL_WIDTH_20; + + if (pkt_stat->rate <= DESC_RATE11M) { + lna = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_LNA_IDX); + vga = le32_get_bits(rpt->w1, RTW_JGRPHY_W1_AGC_RPT_VGA_IDX); + + rx_pwr_db = rtw8814a_cck_rx_pwr(lna, vga); + + pkt_stat->rx_power[RF_PATH_A] = rx_pwr_db; + pkt_stat->rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, 1); + dm_info->rssi[RF_PATH_A] = pkt_stat->rssi; + pkt_stat->signal_power = rx_pwr_db; + } else { /* OFDM rate */ + gain[RF_PATH_A] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_A); + gain[RF_PATH_B] = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_GAIN_B); + gain[RF_PATH_C] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_GAIN_C); + gain[RF_PATH_D] = le32_get_bits(rpt->w6, RTW_JGRPHY_W6_GAIN_D); + + snr[RF_PATH_A] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXSNR_A); + snr[RF_PATH_B] = le32_get_bits(rpt->w4, RTW_JGRPHY_W4_RXSNR_B); + snr[RF_PATH_C] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXSNR_C); + snr[RF_PATH_D] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXSNR_D); + + evm[RF_PATH_A] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXEVM_1); + evm[RF_PATH_B] = le32_get_bits(rpt->w3, RTW_JGRPHY_W3_RXEVM_2); + evm[RF_PATH_C] = le32_get_bits(rpt->w4, RTW_JGRPHY_W4_RXEVM_3); + evm[RF_PATH_D] = le32_get_bits(rpt->w5, RTW_JGRPHY_W5_RXEVM_4); + + if (pkt_stat->rate <= DESC_RATE54M) + evm[RF_PATH_A] = le32_get_bits(rpt->w6, + RTW_JGRPHY_W6_SIGEVM); + + for (i = RF_PATH_A; i < RTW_RF_PATH_MAX; i++) { + pkt_stat->rx_power[i] = gain[i] - 110; + + rssi = rtw_phy_rf_power_2_rssi(&pkt_stat->rx_power[i], 1); + dm_info->rssi[i] = rssi; + + pkt_stat->rx_snr[i] = snr[i]; + dm_info->rx_snr[i] = snr[i] >> 1; + + pkt_stat->rx_evm[i] = evm[i]; + evm[i] = max_t(s8, -127, evm[i]); + dm_info->rx_evm_dbm[i] = abs(evm[i]) >> 1; + } + + rssi = rtw_phy_rf_power_2_rssi(pkt_stat->rx_power, + RTW_RF_PATH_MAX); + pkt_stat->rssi = rssi; + + /* When power saving is enabled the hardware sometimes + * reports unbelievably high gain for paths A and C + * (e.g. one frame 64 68 68 72, the next frame 106 66 88 72, + * the next 66 66 68 72), so use the second lowest gain + * instead of the highest. + */ + middle1 = max(min(gain[RF_PATH_A], gain[RF_PATH_B]), + min(gain[RF_PATH_C], gain[RF_PATH_D])); + middle2 = min(max(gain[RF_PATH_A], gain[RF_PATH_B]), + max(gain[RF_PATH_C], gain[RF_PATH_D])); + rx_pwr_db = min(middle1, middle2); + rx_pwr_db -= 110; + pkt_stat->signal_power = rx_pwr_db; + + rfmode = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_R_RFMOD); + subchannel = le32_get_bits(rpt->w0, RTW_JGRPHY_W0_SUB_CHNL); + + if (rfmode == 1 && subchannel == 0) { + pkt_stat->bw = RTW_CHANNEL_WIDTH_40; + } else if (rfmode == 2) { + if (subchannel == 0) + pkt_stat->bw = RTW_CHANNEL_WIDTH_80; + else if (subchannel == 9 || subchannel == 10) + pkt_stat->bw = RTW_CHANNEL_WIDTH_40; + } + + cfo[RF_PATH_A] = le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_A); + cfo[RF_PATH_B] = le32_get_bits(rpt->w2, RTW_JGRPHY_W2_CFO_TAIL_B); + + for (i = RF_PATH_A; i < 2; i++) { + pkt_stat->cfo_tail[i] = cfo[i]; + dm_info->cfo_tail[i] = (cfo[i] * 5) >> 1; + } + } +} + +static void +rtw8814a_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) +{ + struct rtw_hal *hal = &rtwdev->hal; + u32 txagc_table_wd; + u8 rate, pwr_index; + int j; + + for (j = 0; j < rtw_rate_size[rs]; j++) { + rate = rtw_rate_section[rs][j]; + + pwr_index = hal->tx_pwr_tbl[path][rate] + 2; + if (pwr_index > rtwdev->chip->max_power_index) + pwr_index = rtwdev->chip->max_power_index; + + txagc_table_wd = 0x00801000; + txagc_table_wd |= (pwr_index << 24) | (path << 8) | rate; + + rtw_write32(rtwdev, REG_AGC_TBL, txagc_table_wd); + + /* first time to turn on the txagc table + * second to write the addr0 + */ + if (rate == DESC_RATE1M) + rtw_write32(rtwdev, REG_AGC_TBL, txagc_table_wd); + } +} + +static void rtw8814a_set_tx_power_index(struct rtw_dev *rtwdev) +{ + struct rtw_hal *hal = &rtwdev->hal; + int path; + + for (path = 0; path < hal->rf_path_num; path++) { + if (hal->current_band_type == RTW_BAND_2G) + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_CCK); + + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_OFDM); + + if (test_bit(RTW_FLAG_SCANNING, rtwdev->flags)) + continue; + + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_HT_1S); + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_VHT_1S); + + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_HT_2S); + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_VHT_2S); + + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_HT_3S); + rtw8814a_set_tx_power_index_by_rate(rtwdev, path, + RTW_RATE_SECTION_VHT_3S); + } +} + +static void rtw8814a_cfg_ldo25(struct rtw_dev *rtwdev, bool enable) +{ +} + +/* Without this RTL8814A sends too many frames and (some?) 11n AP + * can't handle it, resulting in low TX speed. Other chips seem fine. + */ +static void rtw8814a_set_ampdu_factor(struct rtw_dev *rtwdev, u8 factor) +{ + factor = min_t(u8, factor, IEEE80211_VHT_MAX_AMPDU_256K); + + rtw_write32(rtwdev, REG_AMPDU_MAX_LENGTH, (8192 << factor) - 1); +} + +static void rtw8814a_false_alarm_statistics(struct rtw_dev *rtwdev) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + u32 cck_fa_cnt, ofdm_fa_cnt; + u32 crc32_cnt, cca32_cnt; + u32 cck_enable; + + cck_enable = rtw_read32(rtwdev, REG_RXPSEL) & BIT(28); + cck_fa_cnt = rtw_read16(rtwdev, REG_FA_CCK); + ofdm_fa_cnt = rtw_read16(rtwdev, REG_FA_OFDM); + + dm_info->cck_fa_cnt = cck_fa_cnt; + dm_info->ofdm_fa_cnt = ofdm_fa_cnt; + dm_info->total_fa_cnt = ofdm_fa_cnt; + if (cck_enable) + dm_info->total_fa_cnt += cck_fa_cnt; + + crc32_cnt = rtw_read32(rtwdev, REG_CRC_CCK); + dm_info->cck_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD); + dm_info->cck_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD); + + crc32_cnt = rtw_read32(rtwdev, REG_CRC_OFDM); + dm_info->ofdm_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD); + dm_info->ofdm_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD); + + crc32_cnt = rtw_read32(rtwdev, REG_CRC_HT); + dm_info->ht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD); + dm_info->ht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD); + + crc32_cnt = rtw_read32(rtwdev, REG_CRC_VHT); + dm_info->vht_ok_cnt = u32_get_bits(crc32_cnt, MASKLWORD); + dm_info->vht_err_cnt = u32_get_bits(crc32_cnt, MASKHWORD); + + cca32_cnt = rtw_read32(rtwdev, REG_CCA_OFDM); + dm_info->ofdm_cca_cnt = u32_get_bits(cca32_cnt, MASKHWORD); + dm_info->total_cca_cnt = dm_info->ofdm_cca_cnt; + if (cck_enable) { + cca32_cnt = rtw_read32(rtwdev, REG_CCA_CCK); + dm_info->cck_cca_cnt = u32_get_bits(cca32_cnt, MASKLWORD); + dm_info->total_cca_cnt += dm_info->cck_cca_cnt; + } + + rtw_write32_set(rtwdev, REG_FAS, BIT(17)); + rtw_write32_clr(rtwdev, REG_FAS, BIT(17)); + rtw_write32_clr(rtwdev, REG_CCK0_FAREPORT, BIT(15)); + rtw_write32_set(rtwdev, REG_CCK0_FAREPORT, BIT(15)); + rtw_write32_set(rtwdev, REG_CNTRST, BIT(0)); + rtw_write32_clr(rtwdev, REG_CNTRST, BIT(0)); +} + +#define MAC_REG_NUM_8814 2 +#define BB_REG_NUM_8814 14 +#define RF_REG_NUM_8814 1 + +static void rtw8814a_iqk_backup_mac_bb(struct rtw_dev *rtwdev, + u32 *mac_backup, u32 *bb_backup, + const u32 *mac_regs, + const u32 *bb_regs) +{ + u32 i; + + /* save MACBB default value */ + for (i = 0; i < MAC_REG_NUM_8814; i++) + mac_backup[i] = rtw_read32(rtwdev, mac_regs[i]); + + for (i = 0; i < BB_REG_NUM_8814; i++) + bb_backup[i] = rtw_read32(rtwdev, bb_regs[i]); +} + +static void rtw8814a_iqk_backup_rf(struct rtw_dev *rtwdev, + u32 rf_backup[][4], const u32 *rf_regs) +{ + u32 i; + + /* Save RF Parameters */ + for (i = 0; i < RF_REG_NUM_8814; i++) { + rf_backup[i][RF_PATH_A] = rtw_read_rf(rtwdev, RF_PATH_A, + rf_regs[i], RFREG_MASK); + rf_backup[i][RF_PATH_B] = rtw_read_rf(rtwdev, RF_PATH_B, + rf_regs[i], RFREG_MASK); + rf_backup[i][RF_PATH_C] = rtw_read_rf(rtwdev, RF_PATH_C, + rf_regs[i], RFREG_MASK); + rf_backup[i][RF_PATH_D] = rtw_read_rf(rtwdev, RF_PATH_D, + rf_regs[i], RFREG_MASK); + } +} + +static void rtw8814a_iqk_afe_setting(struct rtw_dev *rtwdev, bool do_iqk) +{ + if (do_iqk) { + /* IQK AFE setting RX_WAIT_CCA mode */ + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x0e808003); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x0e808003); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x0e808003); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x0e808003); + } else { + rtw_write32(rtwdev, REG_AFE_PWR1_A, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_B, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_C, 0x07808003); + rtw_write32(rtwdev, REG_AFE_PWR1_D, 0x07808003); + } + + rtw_write32_mask(rtwdev, REG_DAC_RSTB, BIT(13), 0x1); + + rtw_write8_set(rtwdev, REG_GNT_BT, BIT(2) | BIT(1)); + rtw_write8_clr(rtwdev, REG_GNT_BT, BIT(2) | BIT(1)); + + rtw_write32_set(rtwdev, REG_CCK_RPT_FORMAT, BIT(2)); + rtw_write32_clr(rtwdev, REG_CCK_RPT_FORMAT, BIT(2)); +} + +static void rtw8814a_iqk_restore_mac_bb(struct rtw_dev *rtwdev, + u32 *mac_backup, u32 *bb_backup, + const u32 *mac_regs, + const u32 *bb_regs) +{ + u32 i; + + /* Reload MacBB Parameters */ + for (i = 0; i < MAC_REG_NUM_8814; i++) + rtw_write32(rtwdev, mac_regs[i], mac_backup[i]); + + for (i = 0; i < BB_REG_NUM_8814; i++) + rtw_write32(rtwdev, bb_regs[i], bb_backup[i]); +} + +static void rtw8814a_iqk_restore_rf(struct rtw_dev *rtwdev, + const u32 rf_backup[][4], + const u32 *rf_regs) +{ + u32 i; + + rtw_write_rf(rtwdev, RF_PATH_A, RF_LUTWE, RFREG_MASK, 0x0); + rtw_write_rf(rtwdev, RF_PATH_B, RF_LUTWE, RFREG_MASK, 0x0); + rtw_write_rf(rtwdev, RF_PATH_C, RF_LUTWE, RFREG_MASK, 0x0); + rtw_write_rf(rtwdev, RF_PATH_D, RF_LUTWE, RFREG_MASK, 0x0); + + rtw_write_rf(rtwdev, RF_PATH_A, RF_RXBB2, RFREG_MASK, 0x88001); + rtw_write_rf(rtwdev, RF_PATH_B, RF_RXBB2, RFREG_MASK, 0x88001); + rtw_write_rf(rtwdev, RF_PATH_C, RF_RXBB2, RFREG_MASK, 0x88001); + rtw_write_rf(rtwdev, RF_PATH_D, RF_RXBB2, RFREG_MASK, 0x88001); + + for (i = 0; i < RF_REG_NUM_8814; i++) { + rtw_write_rf(rtwdev, RF_PATH_A, rf_regs[i], + RFREG_MASK, rf_backup[i][RF_PATH_A]); + rtw_write_rf(rtwdev, RF_PATH_B, rf_regs[i], + RFREG_MASK, rf_backup[i][RF_PATH_B]); + rtw_write_rf(rtwdev, RF_PATH_C, rf_regs[i], + RFREG_MASK, rf_backup[i][RF_PATH_C]); + rtw_write_rf(rtwdev, RF_PATH_D, rf_regs[i], + RFREG_MASK, rf_backup[i][RF_PATH_D]); + } +} + +static void rtw8814a_iqk_reset_nctl(struct rtw_dev *rtwdev) +{ + rtw_write32(rtwdev, 0x1b00, 0xf8000000); + rtw_write32(rtwdev, 0x1b80, 0x00000006); + + rtw_write32(rtwdev, 0x1b00, 0xf8000000); + rtw_write32(rtwdev, 0x1b80, 0x00000002); +} + +static void rtw8814a_iqk_configure_mac(struct rtw_dev *rtwdev) +{ + rtw_write8(rtwdev, REG_TXPAUSE, 0x3f); + rtw_write32_clr(rtwdev, REG_BCN_CTRL, + (BIT_EN_BCN_FUNCTION << 8) | BIT_EN_BCN_FUNCTION); + + /* RX ante off */ + rtw_write8(rtwdev, REG_RXPSEL, 0x00); + /* CCA off */ + rtw_write32_mask(rtwdev, REG_CCA2ND, 0xf, 0xe); + /* CCK RX path off */ + rtw_write32_set(rtwdev, REG_PRECTRL, BIT_IQ_WGT); + rtw_write32(rtwdev, REG_RFE_PINMUX_A, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_B, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_C, 0x77777777); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, 0x77777777); + rtw_write32_mask(rtwdev, REG_RFE_INVSEL_D, BIT_RFE_SELSW0_D, 0x77); + rtw_write32_mask(rtwdev, REG_PSD, BIT_PSD_INI, 0x0); + + rtw_write32_mask(rtwdev, REG_RFE_INV0, 0xf, 0x0); +} + +static void rtw8814a_lok_one_shot(struct rtw_dev *rtwdev, u8 path) +{ + u32 lok_temp1, lok_temp2; + bool lok_ready; + u8 ii; + + /* ADC Clock source */ + rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path); + /* LOK: CMD ID = 0 + * {0xf8000011, 0xf8000021, 0xf8000041, 0xf8000081} + */ + rtw_write32(rtwdev, 0x1b00, 0xf8000001 | (BIT(path) << 4)); + + usleep_range(1000, 1100); + + if (read_poll_timeout(!rtw_read32_mask, lok_ready, lok_ready, + 1000, 10000, false, + rtwdev, 0x1b00, BIT(0))) { + rtw_dbg(rtwdev, RTW_DBG_RFK, "==>S%d LOK timed out\n", path); + + rtw8814a_iqk_reset_nctl(rtwdev); + + rtw_write_rf(rtwdev, path, RF_DTXLOK, RFREG_MASK, 0x08400); + + return; + } + + rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1)); + rtw_write32(rtwdev, 0x1bd4, 0x003f0001); + + lok_temp2 = rtw_read32_mask(rtwdev, 0x1bfc, 0x003e0000); + lok_temp2 = (lok_temp2 + 0x10) & 0x1f; + + lok_temp1 = rtw_read32_mask(rtwdev, 0x1bfc, 0x0000003e); + lok_temp1 = (lok_temp1 + 0x10) & 0x1f; + + for (ii = 1; ii < 5; ii++) { + lok_temp1 += (lok_temp1 & BIT(4 - ii)) << (ii * 2); + lok_temp2 += (lok_temp2 & BIT(4 - ii)) << (ii * 2); + } + + rtw_dbg(rtwdev, RTW_DBG_RFK, + "path %d lok_temp1 = %#x, lok_temp2 = %#x\n", + path, lok_temp1 >> 4, lok_temp2 >> 4); + + rtw_write_rf(rtwdev, path, RF_DTXLOK, 0x07c00, lok_temp1 >> 4); + rtw_write_rf(rtwdev, path, RF_DTXLOK, 0xf8000, lok_temp2 >> 4); +} + +static void rtw8814a_iqk_tx_one_shot(struct rtw_dev *rtwdev, u8 path, + u32 *tx_matrix, bool *tx_ok) +{ + u8 bw = rtwdev->hal.current_band_width; + u8 cal_retry; + u32 iqk_cmd; + + for (cal_retry = 0; cal_retry < 4; cal_retry++) { + rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path); + + iqk_cmd = 0xf8000001 | ((bw + 3) << 8) | (BIT(path) << 4); + + rtw_dbg(rtwdev, RTW_DBG_RFK, "TXK_Trigger = %#x\n", iqk_cmd); + + rtw_write32(rtwdev, 0x1b00, iqk_cmd); + + usleep_range(10000, 11000); + + if (read_poll_timeout(!rtw_read32_mask, *tx_ok, *tx_ok, + 1000, 20000, false, + rtwdev, 0x1b00, BIT(0))) { + rtw_dbg(rtwdev, RTW_DBG_RFK, + "tx iqk S%d timed out\n", path); + + rtw8814a_iqk_reset_nctl(rtwdev); + } else { + *tx_ok = !rtw_read32_mask(rtwdev, 0x1b08, BIT(26)); + + if (*tx_ok) + break; + } + } + + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> 0x1b00 = 0x%x\n", + path, rtw_read32(rtwdev, 0x1b00)); + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> 0x1b08 = 0x%x\n", + path, rtw_read32(rtwdev, 0x1b08)); + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d tx ==> cal_retry = %x\n", + path, cal_retry); + + rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1)); + + if (*tx_ok) { + *tx_matrix = rtw_read32(rtwdev, 0x1b38); + + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d_IQC = 0x%x\n", + path, *tx_matrix); + } +} + +static void rtw8814a_iqk_rx_one_shot(struct rtw_dev *rtwdev, u8 path, + u32 *tx_matrix, bool *tx_ok) +{ + static const u16 iqk_apply[RTW_RF_PATH_MAX] = { + REG_TXAGCIDX, REG_TX_AGC_B, REG_TX_AGC_C, REG_TX_AGC_D + }; + u8 band = rtwdev->hal.current_band_type; + u8 bw = rtwdev->hal.current_band_width; + u32 rx_matrix; + u8 cal_retry; + u32 iqk_cmd; + bool rx_ok; + + for (cal_retry = 0; cal_retry < 4; cal_retry++) { + rtw_write32_mask(rtwdev, REG_FAS, BIT(21) | BIT(20), path); + + if (band == RTW_BAND_2G) { + rtw_write_rf(rtwdev, path, RF_LUTDBG, BIT(11), 0x1); + rtw_write_rf(rtwdev, path, RF_GAINTX, 0xfffff, 0x51ce1); + + switch (path) { + case 0: + case 1: + rtw_write32(rtwdev, REG_RFE_PINMUX_B, + 0x54775477); + break; + case 2: + rtw_write32(rtwdev, REG_RFE_PINMUX_C, + 0x54775477); + break; + case 3: + rtw_write32(rtwdev, REG_RFE_INVSEL_D, 0x75400000); + rtw_write32(rtwdev, REG_RFE_PINMUX_D, + 0x77777777); + break; + } + } + + iqk_cmd = 0xf8000001 | ((9 - bw) << 8) | (BIT(path) << 4); + + rtw_dbg(rtwdev, RTW_DBG_RFK, "RXK_Trigger = 0x%x\n", iqk_cmd); + + rtw_write32(rtwdev, 0x1b00, iqk_cmd); + + usleep_range(10000, 11000); + + if (read_poll_timeout(!rtw_read32_mask, rx_ok, rx_ok, + 1000, 20000, false, + rtwdev, 0x1b00, BIT(0))) { + rtw_dbg(rtwdev, RTW_DBG_RFK, + "rx iqk S%d timed out\n", path); + + rtw8814a_iqk_reset_nctl(rtwdev); + } else { + rx_ok = !rtw_read32_mask(rtwdev, 0x1b08, BIT(26)); + + if (rx_ok) + break; + } + } + + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> 0x1b00 = 0x%x\n", + path, rtw_read32(rtwdev, 0x1b00)); + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> 0x1b08 = 0x%x\n", + path, rtw_read32(rtwdev, 0x1b08)); + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d rx ==> cal_retry = %x\n", + path, cal_retry); + + rtw_write32(rtwdev, 0x1b00, 0xf8000000 | (path << 1)); + + if (rx_ok) { + rtw_write32(rtwdev, 0x1b3c, 0x20000000); + rx_matrix = rtw_read32(rtwdev, 0x1b3c); + + rtw_dbg(rtwdev, RTW_DBG_RFK, "S%d_IQC = 0x%x\n", + path, rx_matrix); + } + + if (*tx_ok) + rtw_write32(rtwdev, 0x1b38, *tx_matrix); + else + rtw_write32_mask(rtwdev, iqk_apply[path], BIT(0), 0x0); + + if (!rx_ok) + rtw_write32_mask(rtwdev, iqk_apply[path], + BIT(11) | BIT(10), 0x0); + + if (band == RTW_BAND_2G) + rtw_write_rf(rtwdev, path, RF_LUTDBG, BIT(11), 0x0); +} + +static void rtw8814a_iqk(struct rtw_dev *rtwdev) +{ + u8 band = rtwdev->hal.current_band_type; + u8 bw = rtwdev->hal.current_band_width; + u32 tx_matrix[RTW_RF_PATH_MAX]; + bool tx_ok[RTW_RF_PATH_MAX]; + u8 path; + + rtw_dbg(rtwdev, RTW_DBG_RFK, "IQK band = %d GHz bw = %d MHz\n", + band == RTW_BAND_2G ? 2 : 5, (1 << (bw + 1)) * 10); + + rtw_write_rf(rtwdev, RF_PATH_A, RF_TXMOD, BIT(19), 0x1); + rtw_write_rf(rtwdev, RF_PATH_B, RF_TXMOD, BIT(19), 0x1); + rtw_write_rf(rtwdev, RF_PATH_C, RF_TXMOD, BIT(19), 0x1); + rtw_write_rf(rtwdev, RF_PATH_D, RF_TXMOD, BIT(19), 0x1); + + rtw_write32_mask(rtwdev, REG_TXAGCIDX, + (BIT(11) | BIT(10) | BIT(0)), 0x401); + rtw_write32_mask(rtwdev, REG_TX_AGC_B, + (BIT(11) | BIT(10) | BIT(0)), 0x401); + rtw_write32_mask(rtwdev, REG_TX_AGC_C, + (BIT(11) | BIT(10) | BIT(0)), 0x401); + rtw_write32_mask(rtwdev, REG_TX_AGC_D, + (BIT(11) | BIT(10) | BIT(0)), 0x401); + + if (band == RTW_BAND_5G) + rtw_write32(rtwdev, 0x1b00, 0xf8000ff1); + else + rtw_write32(rtwdev, 0x1b00, 0xf8000ef1); + + usleep_range(1000, 1100); + + rtw_write32(rtwdev, 0x810, 0x20101063); + rtw_write32(rtwdev, REG_DAC_RSTB, 0x0B00C000); + + for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++) + rtw8814a_lok_one_shot(rtwdev, path); + + for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++) + rtw8814a_iqk_tx_one_shot(rtwdev, path, + &tx_matrix[path], &tx_ok[path]); + + for (path = RF_PATH_A; path < RTW_RF_PATH_MAX; path++) + rtw8814a_iqk_rx_one_shot(rtwdev, path, + &tx_matrix[path], &tx_ok[path]); +} + +static void rtw8814a_do_iqk(struct rtw_dev *rtwdev) +{ + static const u32 backup_mac_reg[MAC_REG_NUM_8814] = {0x520, 0x550}; + static const u32 backup_bb_reg[BB_REG_NUM_8814] = { + 0xa14, 0x808, 0x838, 0x90c, 0x810, 0xcb0, 0xeb0, + 0x18b4, 0x1ab4, 0x1abc, 0x9a4, 0x764, 0xcbc, 0x910 + }; + static const u32 backup_rf_reg[RF_REG_NUM_8814] = {0x0}; + u32 rf_backup[RF_REG_NUM_8814][RTW_RF_PATH_MAX]; + u32 mac_backup[MAC_REG_NUM_8814]; + u32 bb_backup[BB_REG_NUM_8814]; + + rtw8814a_iqk_backup_mac_bb(rtwdev, mac_backup, bb_backup, + backup_mac_reg, backup_bb_reg); + rtw8814a_iqk_afe_setting(rtwdev, true); + rtw8814a_iqk_backup_rf(rtwdev, rf_backup, backup_rf_reg); + rtw8814a_iqk_configure_mac(rtwdev); + rtw8814a_iqk(rtwdev); + rtw8814a_iqk_reset_nctl(rtwdev); /* for 3-wire to BB use */ + rtw8814a_iqk_afe_setting(rtwdev, false); + rtw8814a_iqk_restore_mac_bb(rtwdev, mac_backup, bb_backup, + backup_mac_reg, backup_bb_reg); + rtw8814a_iqk_restore_rf(rtwdev, rf_backup, backup_rf_reg); +} + +static void rtw8814a_phy_calibration(struct rtw_dev *rtwdev) +{ + rtw8814a_do_iqk(rtwdev); +} + +static void rtw8814a_coex_cfg_init(struct rtw_dev *rtwdev) +{ +} + +static void rtw8814a_coex_cfg_ant_switch(struct rtw_dev *rtwdev, u8 ctrl_type, + u8 pos_type) +{ + /* Override rtw_coex_coex_ctrl_owner(). RF path C does not + * function when BIT_LTE_MUX_CTRL_PATH is set. + */ + rtw_write8_clr(rtwdev, REG_SYS_SDIO_CTRL + 3, + BIT_LTE_MUX_CTRL_PATH >> 24); +} + +static void rtw8814a_coex_cfg_gnt_fix(struct rtw_dev *rtwdev) +{ +} + +static void rtw8814a_coex_cfg_gnt_debug(struct rtw_dev *rtwdev) +{ +} + +static void rtw8814a_coex_cfg_rfe_type(struct rtw_dev *rtwdev) +{ + struct rtw_coex *coex = &rtwdev->coex; + struct rtw_coex_rfe *coex_rfe = &coex->rfe; + + /* Only needed to make rtw8814a_coex_cfg_ant_switch() run. */ + coex_rfe->ant_switch_exist = true; +} + +static void rtw8814a_coex_cfg_wl_tx_power(struct rtw_dev *rtwdev, u8 wl_pwr) +{ +} + +static void rtw8814a_coex_cfg_wl_rx_gain(struct rtw_dev *rtwdev, bool low_gain) +{ +} + +static void rtw8814a_txagc_swing_offset(struct rtw_dev *rtwdev, u8 path, + u8 tx_pwr_idx_offset, + s8 *txagc_idx, u8 *swing_idx) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + u8 swing_upper_bound = dm_info->default_ofdm_index + 10; + s8 delta_pwr_idx = dm_info->delta_power_index[path]; + u8 swing_index = dm_info->default_ofdm_index; + u8 max_tx_pwr_idx_offset = 0xf; + u8 swing_lower_bound = 0; + s8 agc_index = 0; + + tx_pwr_idx_offset = min_t(u8, tx_pwr_idx_offset, max_tx_pwr_idx_offset); + + if (delta_pwr_idx >= 0) { + if (delta_pwr_idx <= tx_pwr_idx_offset) { + agc_index = delta_pwr_idx; + swing_index = dm_info->default_ofdm_index; + } else if (delta_pwr_idx > tx_pwr_idx_offset) { + agc_index = tx_pwr_idx_offset; + swing_index = dm_info->default_ofdm_index + + delta_pwr_idx - tx_pwr_idx_offset; + swing_index = min_t(u8, swing_index, swing_upper_bound); + } + } else { + if (dm_info->default_ofdm_index > abs(delta_pwr_idx)) + swing_index = + dm_info->default_ofdm_index + delta_pwr_idx; + else + swing_index = swing_lower_bound; + swing_index = max_t(u8, swing_index, swing_lower_bound); + + agc_index = 0; + } + + if (swing_index >= RTW_TXSCALE_SIZE) { + rtw_warn(rtwdev, "swing index overflow\n"); + swing_index = RTW_TXSCALE_SIZE - 1; + } + *txagc_idx = agc_index; + *swing_idx = swing_index; +} + +static void rtw8814a_pwrtrack_set_pwr(struct rtw_dev *rtwdev, u8 path, + u8 pwr_idx_offset) +{ + static const u32 txagc_reg[RTW_RF_PATH_MAX] = { + REG_TX_AGC_A, REG_TX_AGC_B, REG_TX_AGC_C, REG_TX_AGC_D + }; + static const u32 txscale_reg[RTW_RF_PATH_MAX] = { + REG_TXSCALE_A, REG_TXSCALE_B, REG_TXSCALE_C, REG_TXSCALE_D + }; + s8 txagc_idx; + u8 swing_idx; + + rtw8814a_txagc_swing_offset(rtwdev, path, pwr_idx_offset, + &txagc_idx, &swing_idx); + rtw_write32_mask(rtwdev, txagc_reg[path], GENMASK(29, 25), + txagc_idx); + rtw_write32_mask(rtwdev, txscale_reg[path], BB_SWING_MASK, + rtw8814a_txscale_tbl[swing_idx]); +} + +static void rtw8814a_pwrtrack_set(struct rtw_dev *rtwdev, u8 path) +{ + u8 max_pwr_idx = rtwdev->chip->max_power_index; + u8 band_width = rtwdev->hal.current_band_width; + u8 channel = rtwdev->hal.current_channel; + u8 tx_rate = rtwdev->dm_info.tx_rate; + u8 regd = rtw_regd_get(rtwdev); + u8 pwr_idx_offset, tx_pwr_idx; + + tx_pwr_idx = rtw_phy_get_tx_power_index(rtwdev, path, tx_rate, + band_width, channel, regd); + + tx_pwr_idx = min_t(u8, tx_pwr_idx, max_pwr_idx); + + pwr_idx_offset = max_pwr_idx - tx_pwr_idx; + + rtw8814a_pwrtrack_set_pwr(rtwdev, path, pwr_idx_offset); +} + +static void rtw8814a_phy_pwrtrack_path(struct rtw_dev *rtwdev, + struct rtw_swing_table *swing_table, + u8 path) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + u8 power_idx_cur, power_idx_last; + u8 delta; + + /* 8814A only has one thermal meter at PATH A */ + delta = rtw_phy_pwrtrack_get_delta(rtwdev, RF_PATH_A); + + power_idx_last = dm_info->delta_power_index[path]; + power_idx_cur = rtw_phy_pwrtrack_get_pwridx(rtwdev, swing_table, + path, RF_PATH_A, delta); + + /* if delta of power indexes are the same, just skip */ + if (power_idx_cur == power_idx_last) + return; + + dm_info->delta_power_index[path] = power_idx_cur; + rtw8814a_pwrtrack_set(rtwdev, path); +} + +static void rtw8814a_phy_pwrtrack(struct rtw_dev *rtwdev) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + struct rtw_swing_table swing_table; + u8 thermal_value, path; + + rtw_phy_config_swing_table(rtwdev, &swing_table); + + if (rtwdev->efuse.thermal_meter[RF_PATH_A] == 0xff) + return; + + thermal_value = rtw_read_rf(rtwdev, RF_PATH_A, RF_T_METER, 0xfc00); + + rtw_phy_pwrtrack_avg(rtwdev, thermal_value, RF_PATH_A); + + if (dm_info->pwr_trk_init_trigger) + dm_info->pwr_trk_init_trigger = false; + else if (!rtw_phy_pwrtrack_thermal_changed(rtwdev, thermal_value, + RF_PATH_A)) + goto iqk; + + for (path = RF_PATH_A; path < rtwdev->hal.rf_path_num; path++) + rtw8814a_phy_pwrtrack_path(rtwdev, &swing_table, path); + +iqk: + if (rtw_phy_pwrtrack_need_iqk(rtwdev)) + rtw8814a_do_iqk(rtwdev); +} + +static void rtw8814a_pwr_track(struct rtw_dev *rtwdev) +{ + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + + if (!dm_info->pwr_trk_triggered) { + rtw_write_rf(rtwdev, RF_PATH_A, RF_T_METER, + GENMASK(17, 16), 0x03); + dm_info->pwr_trk_triggered = true; + return; + } + + rtw8814a_phy_pwrtrack(rtwdev); + dm_info->pwr_trk_triggered = false; +} + +static void rtw8814a_phy_cck_pd_set(struct rtw_dev *rtwdev, u8 new_lvl) +{ + static const u8 pd[CCK_PD_LV_MAX] = {0x40, 0x83, 0xcd, 0xdd, 0xed}; + struct rtw_dm_info *dm_info = &rtwdev->dm_info; + + /* Override rtw_phy_cck_pd_lv_link(). It implements something + * like type 2/3/4. We need type 1 here. + */ + if (rtw_is_assoc(rtwdev)) { + if (dm_info->min_rssi > 60) { + new_lvl = CCK_PD_LV3; + } else if (dm_info->min_rssi > 35) { + new_lvl = CCK_PD_LV2; + } else if (dm_info->min_rssi > 20) { + if (dm_info->cck_fa_avg > 500) + new_lvl = CCK_PD_LV2; + else if (dm_info->cck_fa_avg < 250) + new_lvl = CCK_PD_LV1; + else + return; + } else { + new_lvl = CCK_PD_LV1; + } + } + + rtw_dbg(rtwdev, RTW_DBG_PHY, "lv: (%d) -> (%d)\n", + dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A], new_lvl); + + if (dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] == new_lvl) + return; + + dm_info->cck_fa_avg = CCK_FA_AVG_RESET; + dm_info->cck_pd_lv[RTW_CHANNEL_WIDTH_20][RF_PATH_A] = new_lvl; + + rtw_write8(rtwdev, REG_CCK_PD_TH, pd[new_lvl]); +} + +static void rtw8814a_led_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct rtw_dev *rtwdev = container_of(led, struct rtw_dev, led_cdev); + u32 led_gpio_cfg; + + led_gpio_cfg = rtw_read32(rtwdev, REG_GPIO_PIN_CTRL_2); + led_gpio_cfg |= BIT(16) | BIT(17) | BIT(21) | BIT(22); + + if (brightness == LED_OFF) { + led_gpio_cfg |= BIT(8) | BIT(9) | BIT(13) | BIT(14); + } else { + led_gpio_cfg &= ~(BIT(8) | BIT(9) | BIT(13) | BIT(14)); + led_gpio_cfg &= ~(BIT(0) | BIT(1) | BIT(5) | BIT(6)); + } + + rtw_write32(rtwdev, REG_GPIO_PIN_CTRL_2, led_gpio_cfg); +} + +static void rtw8814a_fill_txdesc_checksum(struct rtw_dev *rtwdev, + struct rtw_tx_pkt_info *pkt_info, + u8 *txdesc) +{ + size_t words = 32 / 2; /* calculate the first 32 bytes (16 words) */ + + fill_txdesc_checksum_common(txdesc, words); +} + +static const struct rtw_chip_ops rtw8814a_ops = { + .power_on = rtw_power_on, + .power_off = rtw_power_off, + .phy_set_param = rtw8814a_phy_set_param, + .read_efuse = rtw8814a_read_efuse, + .query_phy_status = rtw8814a_query_phy_status, + .set_channel = rtw8814a_set_channel, + .mac_init = rtw8814a_mac_init, + .read_rf = rtw_phy_read_rf, + .write_rf = rtw_phy_write_rf_reg_sipi, + .set_tx_power_index = rtw8814a_set_tx_power_index, + .set_antenna = NULL, + .cfg_ldo25 = rtw8814a_cfg_ldo25, + .efuse_grant = rtw8814a_efuse_grant, + .set_ampdu_factor = rtw8814a_set_ampdu_factor, + .false_alarm_statistics = rtw8814a_false_alarm_statistics, + .phy_calibration = rtw8814a_phy_calibration, + .cck_pd_set = rtw8814a_phy_cck_pd_set, + .pwr_track = rtw8814a_pwr_track, + .config_bfee = NULL, + .set_gid_table = NULL, + .cfg_csi_rate = NULL, + .led_set = rtw8814a_led_set, + .fill_txdesc_checksum = rtw8814a_fill_txdesc_checksum, + + .coex_set_init = rtw8814a_coex_cfg_init, + .coex_set_ant_switch = rtw8814a_coex_cfg_ant_switch, + .coex_set_gnt_fix = rtw8814a_coex_cfg_gnt_fix, + .coex_set_gnt_debug = rtw8814a_coex_cfg_gnt_debug, + .coex_set_rfe_type = rtw8814a_coex_cfg_rfe_type, + .coex_set_wl_tx_power = rtw8814a_coex_cfg_wl_tx_power, + .coex_set_wl_rx_gain = rtw8814a_coex_cfg_wl_rx_gain, +}; + +static const struct rtw_rqpn rqpn_table_8814a[] = { + /* SDIO */ + {RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, /* vo vi */ + RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, /* be bk */ + RTW_DMA_MAPPING_EXTRA, RTW_DMA_MAPPING_HIGH}, /* mg hi */ + /* PCIE */ + {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL, + RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, + RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, + /* USB, 2 bulk out */ + {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH, + RTW_DMA_MAPPING_NORMAL, RTW_DMA_MAPPING_NORMAL, + RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, + /* USB, 3 bulk out */ + {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL, + RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, + RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, + /* USB, 4 bulk out */ + {RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_NORMAL, + RTW_DMA_MAPPING_LOW, RTW_DMA_MAPPING_LOW, + RTW_DMA_MAPPING_HIGH, RTW_DMA_MAPPING_HIGH}, +}; + +static const struct rtw_prioq_addrs prioq_addrs_8814a = { + .prio[RTW_DMA_MAPPING_EXTRA] = { + .rsvd = REG_FIFOPAGE_INFO_4, .avail = REG_FIFOPAGE_INFO_4 + 2, + }, + .prio[RTW_DMA_MAPPING_LOW] = { + .rsvd = REG_FIFOPAGE_INFO_2, .avail = REG_FIFOPAGE_INFO_2 + 2, + }, + .prio[RTW_DMA_MAPPING_NORMAL] = { + .rsvd = REG_FIFOPAGE_INFO_3, .avail = REG_FIFOPAGE_INFO_3 + 2, + }, + .prio[RTW_DMA_MAPPING_HIGH] = { + .rsvd = REG_FIFOPAGE_INFO_1, .avail = REG_FIFOPAGE_INFO_1 + 2, + }, + .wsize = true, +}; + +static const struct rtw_page_table page_table_8814a[] = { + /* SDIO */ + {0, 0, 0, 0, 0}, /* hq nq lq exq gapq */ + /* PCIE */ + {32, 32, 32, 32, 0}, + /* USB, 2 bulk out */ + {32, 32, 32, 32, 0}, + /* USB, 3 bulk out */ + {32, 32, 32, 32, 0}, + /* USB, 4 bulk out */ + {32, 32, 32, 32, 0}, +}; + +static const struct rtw_intf_phy_para_table phy_para_table_8814a = {}; + +static const struct rtw_hw_reg rtw8814a_dig[] = { + [0] = { .addr = 0xc50, .mask = 0x7f }, + [1] = { .addr = 0xe50, .mask = 0x7f }, + [2] = { .addr = 0x1850, .mask = 0x7f }, + [3] = { .addr = 0x1a50, .mask = 0x7f }, +}; + +static const struct rtw_rfe_def rtw8814a_rfe_defs[] = { + [0] = { .phy_pg_tbl = &rtw8814a_bb_pg_type0_tbl, + .txpwr_lmt_tbl = &rtw8814a_txpwr_lmt_type0_tbl, + .pwr_track_tbl = &rtw8814a_rtw_pwrtrk_type0_tbl }, + [1] = { .phy_pg_tbl = &rtw8814a_bb_pg_tbl, + .txpwr_lmt_tbl = &rtw8814a_txpwr_lmt_type1_tbl, + .pwr_track_tbl = &rtw8814a_rtw_pwrtrk_tbl }, +}; + +/* rssi in percentage % (dbm = % - 100) */ +static const u8 wl_rssi_step_8814a[] = {60, 50, 44, 30}; +static const u8 bt_rssi_step_8814a[] = {30, 30, 30, 30}; + +/* wl_tx_dec_power, bt_tx_dec_power, wl_rx_gain, bt_rx_lna_constrain */ +static const struct coex_rf_para rf_para_tx_8814a[] = { + {0, 0, false, 7}, /* for normal */ + {0, 16, false, 7}, /* for WL-CPT */ + {4, 0, true, 1}, + {3, 6, true, 1}, + {2, 9, true, 1}, + {1, 13, true, 1} +}; + +static const struct coex_rf_para rf_para_rx_8814a[] = { + {0, 0, false, 7}, /* for normal */ + {0, 16, false, 7}, /* for WL-CPT */ + {4, 0, true, 1}, + {3, 6, true, 1}, + {2, 9, true, 1}, + {1, 13, true, 1} +}; + +static_assert(ARRAY_SIZE(rf_para_tx_8814a) == ARRAY_SIZE(rf_para_rx_8814a)); + +const struct rtw_chip_info rtw8814a_hw_spec = { + .ops = &rtw8814a_ops, + .id = RTW_CHIP_TYPE_8814A, + .fw_name = "rtw88/rtw8814a_fw.bin", + .wlan_cpu = RTW_WCPU_11AC, + .tx_pkt_desc_sz = 40, + .tx_buf_desc_sz = 16, + .rx_pkt_desc_sz = 24, + .rx_buf_desc_sz = 8, + .phy_efuse_size = 1024, + .log_efuse_size = 512, + .ptct_efuse_size = 0, + .txff_size = (2048 - 10) * TX_PAGE_SIZE, + .rxff_size = 23552, + .rsvd_drv_pg_num = 8, + .band = RTW_BAND_2G | RTW_BAND_5G, + .page_size = TX_PAGE_SIZE, + .csi_buf_pg_num = 0, + .dig_min = 0x1c, + .txgi_factor = 1, + .is_pwr_by_rate_dec = true, + .rx_ldpc = true, + .max_power_index = 0x3f, + .ampdu_density = IEEE80211_HT_MPDU_DENSITY_2, + .amsdu_in_ampdu = false, /* RX speed is better without AMSDU */ + .usb_tx_agg_desc_num = 3, + .hw_feature_report = false, + .c2h_ra_report_size = 6, + .old_datarate_fb_limit = false, + .ht_supported = true, + .vht_supported = true, + .lps_deep_mode_supported = BIT(LPS_DEEP_MODE_LCLK), + .sys_func_en = 0xDC, + .pwr_on_seq = card_enable_flow_8814a, + .pwr_off_seq = card_disable_flow_8814a, + .rqpn_table = rqpn_table_8814a, + .prioq_addrs = &prioq_addrs_8814a, + .page_table = page_table_8814a, + .intf_table = &phy_para_table_8814a, + .dig = rtw8814a_dig, + .dig_cck = NULL, + .rf_base_addr = {0x2800, 0x2c00, 0x3800, 0x3c00}, + .rf_sipi_addr = {0xc90, 0xe90, 0x1890, 0x1a90}, + .ltecoex_addr = NULL, + .mac_tbl = &rtw8814a_mac_tbl, + .agc_tbl = &rtw8814a_agc_tbl, + .bb_tbl = &rtw8814a_bb_tbl, + .rf_tbl = {&rtw8814a_rf_a_tbl, &rtw8814a_rf_b_tbl, + &rtw8814a_rf_c_tbl, &rtw8814a_rf_d_tbl}, + .rfe_defs = rtw8814a_rfe_defs, + .rfe_defs_size = ARRAY_SIZE(rtw8814a_rfe_defs), + .iqk_threshold = 8, + .max_scan_ie_len = IEEE80211_MAX_DATA_LEN, + + .coex_para_ver = 0, + .bt_desired_ver = 0, + .scbd_support = false, + .new_scbd10_def = false, + .ble_hid_profile_support = false, + .wl_mimo_ps_support = false, + .pstdma_type = COEX_PSTDMA_FORCE_LPSOFF, + .bt_rssi_type = COEX_BTRSSI_RATIO, + .ant_isolation = 15, + .rssi_tolerance = 2, + .wl_rssi_step = wl_rssi_step_8814a, + .bt_rssi_step = bt_rssi_step_8814a, + .table_sant_num = 0, + .table_sant = NULL, + .table_nsant_num = 0, + .table_nsant = NULL, + .tdma_sant_num = 0, + .tdma_sant = NULL, + .tdma_nsant_num = 0, + .tdma_nsant = NULL, + .wl_rf_para_num = ARRAY_SIZE(rf_para_tx_8814a), + .wl_rf_para_tx = rf_para_tx_8814a, + .wl_rf_para_rx = rf_para_rx_8814a, + .bt_afh_span_bw20 = 0x24, + .bt_afh_span_bw40 = 0x36, + .afh_5g_num = 0, + .afh_5g = NULL, + .coex_info_hw_regs_num = 0, + .coex_info_hw_regs = NULL, +}; +EXPORT_SYMBOL(rtw8814a_hw_spec); + +MODULE_FIRMWARE("rtw88/rtw8814a_fw.bin"); + +MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814a driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a.h b/drivers/net/wireless/realtek/rtw88/rtw8814a.h new file mode 100644 index 000000000000..c57c7c8f915e --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2025 Realtek Corporation + */ + +#ifndef __RTW8814A_H__ +#define __RTW8814A_H__ + +struct rtw8814au_efuse { + u8 vid[2]; /* 0xd0 */ + u8 pid[2]; /* 0xd2 */ + u8 res[4]; /* 0xd4 */ + u8 mac_addr[ETH_ALEN]; /* 0xd8 */ +} __packed; + +struct rtw8814ae_efuse { + u8 mac_addr[ETH_ALEN]; /* 0xd0 */ + u8 vid[2]; /* 0xd6 */ + u8 did[2]; /* 0xd8 */ + u8 svid[2]; /* 0xda */ + u8 smid[2]; /* 0xdc */ +} __packed; + +struct rtw8814a_efuse { + __le16 rtl_id; + u8 res0[0x0c]; + u8 usb_mode; /* 0x0e */ + u8 res1; + + /* power index for four RF paths */ + struct rtw_txpwr_idx txpwr_idx_table[4]; + + u8 channel_plan; /* 0xb8 */ + u8 xtal_k; /* 0xb9 */ + u8 thermal_meter; /* 0xba */ + u8 iqk_lck; /* 0xbb */ + u8 pa_type; /* 0xbc */ + u8 lna_type_2g[2]; /* 0xbd */ + u8 lna_type_5g[2]; /* 0xbf */ + u8 rf_board_option; /* 0xc1 */ + u8 res2; + u8 rf_bt_setting; /* 0xc3 */ + u8 eeprom_version; /* 0xc4 */ + u8 eeprom_customer_id; /* 0xc5 */ + u8 tx_bb_swing_setting_2g; /* 0xc6 */ + u8 tx_bb_swing_setting_5g; /* 0xc7 */ + u8 res3; + u8 trx_antenna_option; /* 0xc9 */ + u8 rfe_option; /* 0xca */ + u8 country_code[2]; /* 0xcb */ + u8 res4[3]; + union { + struct rtw8814au_efuse u; + struct rtw8814ae_efuse e; + }; + u8 res5[0x122]; /* 0xde */ +} __packed; + +static_assert(sizeof(struct rtw8814a_efuse) == 512); + +extern const struct rtw_chip_info rtw8814a_hw_spec; + +#endif diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c new file mode 100644 index 000000000000..b9ce51a09fc8 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.c @@ -0,0 +1,23930 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include "main.h" +#include "phy.h" +#include "rtw8814a_table.h" + +static const u32 rtw8814a_mac[] = { + 0x010, 0x0000007C, + 0x014, 0x000000DB, + 0x016, 0x00000002, + 0x073, 0x00000010, + 0x420, 0x00000080, + 0x421, 0x0000000F, + 0x428, 0x0000000A, + 0x429, 0x00000010, + 0x430, 0x00000000, + 0x431, 0x00000000, + 0x432, 0x00000000, + 0x433, 0x00000001, + 0x434, 0x00000004, + 0x435, 0x00000005, + 0x436, 0x00000007, + 0x437, 0x00000008, + 0x43C, 0x00000004, + 0x43D, 0x00000005, + 0x43E, 0x00000007, + 0x43F, 0x00000008, + 0x440, 0x0000005D, + 0x441, 0x00000001, + 0x442, 0x00000000, + 0x444, 0x00000010, + 0x445, 0x000000F0, + 0x446, 0x00000001, + 0x447, 0x000000FE, + 0x448, 0x00000000, + 0x449, 0x00000000, + 0x44A, 0x00000000, + 0x44B, 0x00000040, + 0x44C, 0x00000010, + 0x44D, 0x000000F0, + 0x44E, 0x0000003F, + 0x44F, 0x00000000, + 0x450, 0x00000000, + 0x451, 0x00000000, + 0x452, 0x00000000, + 0x453, 0x00000040, + 0x45E, 0x00000004, + 0x49C, 0x00000010, + 0x49D, 0x000000F0, + 0x49E, 0x00000000, + 0x49F, 0x00000006, + 0x4A0, 0x000000E0, + 0x4A1, 0x00000003, + 0x4A2, 0x00000000, + 0x4A3, 0x00000040, + 0x4A4, 0x00000015, + 0x4A5, 0x000000F0, + 0x4A6, 0x00000000, + 0x4A7, 0x00000006, + 0x4A8, 0x000000E0, + 0x4A9, 0x00000000, + 0x4AA, 0x00000000, + 0x4AB, 0x00000000, + 0x7DA, 0x00000008, + 0x1448, 0x00000006, + 0x144A, 0x00000006, + 0x144C, 0x00000006, + 0x144E, 0x00000006, + 0x4C8, 0x000000FF, + 0x4C9, 0x00000008, + 0x4CA, 0x0000003C, + 0x4CB, 0x0000003C, + 0x4CC, 0x000000FF, + 0x4CD, 0x000000FF, + 0x4CE, 0x00000001, + 0x4CF, 0x00000008, + 0x500, 0x00000026, + 0x501, 0x000000A2, + 0x502, 0x0000002F, + 0x503, 0x00000000, + 0x504, 0x00000028, + 0x505, 0x000000A3, + 0x506, 0x0000005E, + 0x507, 0x00000000, + 0x508, 0x0000002B, + 0x509, 0x000000A4, + 0x50A, 0x0000005E, + 0x50B, 0x00000000, + 0x50C, 0x0000004F, + 0x50D, 0x000000A4, + 0x50E, 0x00000000, + 0x50F, 0x00000000, + 0x512, 0x0000001C, + 0x514, 0x0000000A, + 0x516, 0x0000000A, + 0x521, 0x0000002F, + 0x525, 0x00000047, + 0x550, 0x00000010, + 0x551, 0x00000010, + 0x559, 0x00000002, + 0x55C, 0x00000064, + 0x55D, 0x000000FF, + 0x577, 0x00000003, + 0x5BE, 0x00000064, + 0x604, 0x00000001, + 0x605, 0x00000030, + 0x607, 0x00000001, + 0x608, 0x0000000E, + 0x609, 0x0000002A, + 0x60A, 0x00000000, + 0x60C, 0x00000018, + 0x60D, 0x00000050, + 0x6A0, 0x000000FF, + 0x6A1, 0x000000FF, + 0x6A2, 0x000000FF, + 0x6A3, 0x000000FF, + 0x6A4, 0x000000FF, + 0x6A5, 0x000000FF, + 0x6DE, 0x00000084, + 0x620, 0x000000FF, + 0x621, 0x000000FF, + 0x622, 0x000000FF, + 0x623, 0x000000FF, + 0x624, 0x000000FF, + 0x625, 0x000000FF, + 0x626, 0x000000FF, + 0x627, 0x000000FF, + 0x638, 0x00000064, + 0x63C, 0x0000000A, + 0x63D, 0x0000000A, + 0x63E, 0x0000000E, + 0x63F, 0x0000000E, + 0x640, 0x00000040, + 0x642, 0x00000040, + 0x643, 0x00000000, + 0x652, 0x000000C8, + 0x66E, 0x00000005, + 0x700, 0x00000021, + 0x701, 0x00000043, + 0x702, 0x00000065, + 0x703, 0x00000087, + 0x708, 0x00000021, + 0x709, 0x00000043, + 0x70A, 0x00000065, + 0x70B, 0x00000087, + 0x718, 0x00000040, + 0x7D5, 0x000000BC, + 0x7D8, 0x00000028, + 0x7D9, 0x00000000, + 0x7DA, 0x0000000B, +}; + +RTW_DECL_TABLE_PHY_COND(rtw8814a_mac, rtw_phy_cfg_mac); + +static const u32 rtw8814a_agc[] = { + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0xA0000000, 0x00000000, + 0x81C, 0xFF000003, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFD020003, + 0x81C, 0xFC040003, + 0x81C, 0xFB060003, + 0x81C, 0xFA080003, + 0x81C, 0xF90A0003, + 0x81C, 0xF80C0003, + 0x81C, 0xF70E0003, + 0x81C, 0xF6100003, + 0x81C, 0xF5120003, + 0x81C, 0xF4140003, + 0x81C, 0xF3160003, + 0x81C, 0xF2180003, + 0x81C, 0xF11A0003, + 0x81C, 0xF01C0003, + 0x81C, 0xEF1E0003, + 0x81C, 0xEE200003, + 0x81C, 0xED220003, + 0x81C, 0xCF240003, + 0x81C, 0xCE260003, + 0x81C, 0xCD280003, + 0x81C, 0xCC2A0003, + 0x81C, 0xCB2C0003, + 0x81C, 0xCA2E0003, + 0x81C, 0xC9300003, + 0x81C, 0xC8320003, + 0x81C, 0xC7340003, + 0x81C, 0xC6360003, + 0x81C, 0xC5380003, + 0x81C, 0xC43A0003, + 0x81C, 0xA63C0003, + 0x81C, 0xA53E0003, + 0x81C, 0xA4400003, + 0x81C, 0xA3420003, + 0x81C, 0xA2440003, + 0x81C, 0xA1460003, + 0x81C, 0x86480003, + 0x81C, 0x854A0003, + 0x81C, 0x844C0003, + 0x81C, 0x834E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x435C0003, + 0x81C, 0x425E0003, + 0x81C, 0x41600003, + 0x81C, 0x27620003, + 0x81C, 0x26640003, + 0x81C, 0x25660003, + 0x81C, 0x24680003, + 0x81C, 0x236A0003, + 0x81C, 0x226C0003, + 0x81C, 0x216E0003, + 0x81C, 0x21700003, + 0x81C, 0x21720003, + 0x81C, 0x21740003, + 0x81C, 0x21760003, + 0x81C, 0x21780003, + 0x81C, 0x217A0003, + 0x81C, 0x217C0003, + 0x81C, 0x217E0003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0xEC260003, + 0x81C, 0xEB280003, + 0x81C, 0xEA2A0003, + 0x81C, 0xE92C0003, + 0x81C, 0xE82E0003, + 0x81C, 0xE7300003, + 0x81C, 0xE6320003, + 0x81C, 0xE5340003, + 0x81C, 0xE4360003, + 0x81C, 0xE3380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xC1420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0xA2500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFD020003, + 0x81C, 0xFC040003, + 0x81C, 0xFB060003, + 0x81C, 0xFA080003, + 0x81C, 0xF90A0003, + 0x81C, 0xF80C0003, + 0x81C, 0xF70E0003, + 0x81C, 0xF6100003, + 0x81C, 0xF5120003, + 0x81C, 0xF4140003, + 0x81C, 0xF3160003, + 0x81C, 0xF2180003, + 0x81C, 0xF11A0003, + 0x81C, 0xF01C0003, + 0x81C, 0xEF1E0003, + 0x81C, 0xEE200003, + 0x81C, 0xED220003, + 0x81C, 0xEC240003, + 0x81C, 0xEB260003, + 0x81C, 0xEA280003, + 0x81C, 0xE92A0003, + 0x81C, 0xE82C0003, + 0x81C, 0xE72E0003, + 0x81C, 0xE6300003, + 0x81C, 0xE5320003, + 0x81C, 0xE4340003, + 0x81C, 0xE3360003, + 0x81C, 0xC6380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xA9420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x49580003, + 0x81C, 0x485A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0xEC260003, + 0x81C, 0xEB280003, + 0x81C, 0xEA2A0003, + 0x81C, 0xE92C0003, + 0x81C, 0xE82E0003, + 0x81C, 0xE7300003, + 0x81C, 0xE6320003, + 0x81C, 0xE5340003, + 0x81C, 0xE4360003, + 0x81C, 0xE3380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xC1420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0xA2500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFD020003, + 0x81C, 0xFC040003, + 0x81C, 0xFB060003, + 0x81C, 0xFA080003, + 0x81C, 0xF90A0003, + 0x81C, 0xF80C0003, + 0x81C, 0xF70E0003, + 0x81C, 0xF6100003, + 0x81C, 0xF5120003, + 0x81C, 0xF4140003, + 0x81C, 0xF3160003, + 0x81C, 0xF2180003, + 0x81C, 0xF11A0003, + 0x81C, 0xF01C0003, + 0x81C, 0xEF1E0003, + 0x81C, 0xEE200003, + 0x81C, 0xED220003, + 0x81C, 0xEC240003, + 0x81C, 0xEB260003, + 0x81C, 0xEA280003, + 0x81C, 0xE92A0003, + 0x81C, 0xE82C0003, + 0x81C, 0xE72E0003, + 0x81C, 0xE6300003, + 0x81C, 0xE5320003, + 0x81C, 0xE4340003, + 0x81C, 0xE3360003, + 0x81C, 0xC6380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xA9420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x49580003, + 0x81C, 0x485A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xDF000003, + 0x81C, 0xDF020003, + 0x81C, 0xDF040003, + 0x81C, 0xDE060003, + 0x81C, 0xDD080003, + 0x81C, 0xDC0A0003, + 0x81C, 0xDB0C0003, + 0x81C, 0xDA0E0003, + 0x81C, 0xD9100003, + 0x81C, 0xD8120003, + 0x81C, 0xD7140003, + 0x81C, 0xD6160003, + 0x81C, 0xD5180003, + 0x81C, 0xD41A0003, + 0x81C, 0xD31C0003, + 0x81C, 0xD21E0003, + 0x81C, 0xD1200003, + 0x81C, 0xD0220003, + 0x81C, 0xCF240003, + 0x81C, 0xCE260003, + 0x81C, 0xCD280003, + 0x81C, 0xCC2A0003, + 0x81C, 0xCB2C0003, + 0x81C, 0xCA2E0003, + 0x81C, 0xC9300003, + 0x81C, 0xC8320003, + 0x81C, 0xC7340003, + 0x81C, 0xC6360003, + 0x81C, 0xC5380003, + 0x81C, 0xA73A0003, + 0x81C, 0xA63C0003, + 0x81C, 0xA53E0003, + 0x81C, 0xA4400003, + 0x81C, 0xA3420003, + 0x81C, 0xA2440003, + 0x81C, 0x87460003, + 0x81C, 0x86480003, + 0x81C, 0x854A0003, + 0x81C, 0x844C0003, + 0x81C, 0x834E0003, + 0x81C, 0x82500003, + 0x81C, 0x81520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x445C0003, + 0x81C, 0x435E0003, + 0x81C, 0x42600003, + 0x81C, 0x41620003, + 0x81C, 0x27640003, + 0x81C, 0x26660003, + 0x81C, 0x25680003, + 0x81C, 0x246A0003, + 0x81C, 0x236C0003, + 0x81C, 0x226E0003, + 0x81C, 0x21700003, + 0x81C, 0x21720003, + 0x81C, 0x21740003, + 0x81C, 0x21760003, + 0x81C, 0x21780003, + 0x81C, 0x217A0003, + 0x81C, 0x217C0003, + 0x81C, 0x217E0003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0xEC260003, + 0x81C, 0xEB280003, + 0x81C, 0xEA2A0003, + 0x81C, 0xE92C0003, + 0x81C, 0xE82E0003, + 0x81C, 0xE7300003, + 0x81C, 0xE6320003, + 0x81C, 0xE5340003, + 0x81C, 0xE4360003, + 0x81C, 0xE3380003, + 0x81C, 0xC53A0003, + 0x81C, 0xC43C0003, + 0x81C, 0xC33E0003, + 0x81C, 0xC2400003, + 0x81C, 0xC1420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0xA34E0003, + 0x81C, 0xA2500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xDF000003, + 0x81C, 0xDF020003, + 0x81C, 0xDF040003, + 0x81C, 0xDE060003, + 0x81C, 0xDD080003, + 0x81C, 0xDC0A0003, + 0x81C, 0xDB0C0003, + 0x81C, 0xDA0E0003, + 0x81C, 0xD9100003, + 0x81C, 0xD8120003, + 0x81C, 0xD7140003, + 0x81C, 0xD6160003, + 0x81C, 0xD5180003, + 0x81C, 0xD41A0003, + 0x81C, 0xD31C0003, + 0x81C, 0xD21E0003, + 0x81C, 0xD1200003, + 0x81C, 0xD0220003, + 0x81C, 0xCF240003, + 0x81C, 0xCE260003, + 0x81C, 0xCD280003, + 0x81C, 0xCC2A0003, + 0x81C, 0xCB2C0003, + 0x81C, 0xCA2E0003, + 0x81C, 0xC9300003, + 0x81C, 0xC8320003, + 0x81C, 0xC7340003, + 0x81C, 0xC6360003, + 0x81C, 0xC5380003, + 0x81C, 0xA73A0003, + 0x81C, 0xA63C0003, + 0x81C, 0xA53E0003, + 0x81C, 0xA4400003, + 0x81C, 0xA3420003, + 0x81C, 0xA2440003, + 0x81C, 0x87460003, + 0x81C, 0x86480003, + 0x81C, 0x854A0003, + 0x81C, 0x844C0003, + 0x81C, 0x834E0003, + 0x81C, 0x82500003, + 0x81C, 0x81520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x445C0003, + 0x81C, 0x435E0003, + 0x81C, 0x42600003, + 0x81C, 0x41620003, + 0x81C, 0x27640003, + 0x81C, 0x26660003, + 0x81C, 0x25680003, + 0x81C, 0x246A0003, + 0x81C, 0x236C0003, + 0x81C, 0x226E0003, + 0x81C, 0x21700003, + 0x81C, 0x21720003, + 0x81C, 0x21740003, + 0x81C, 0x21760003, + 0x81C, 0x21780003, + 0x81C, 0x217A0003, + 0x81C, 0x217C0003, + 0x81C, 0x217E0003, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0x0F260003, + 0x81C, 0x0E280003, + 0x81C, 0x0D2A0003, + 0x81C, 0x0C2C0003, + 0x81C, 0x0B2E0003, + 0x81C, 0x0A300003, + 0x81C, 0x09320003, + 0x81C, 0x08340003, + 0x81C, 0x07360003, + 0x81C, 0x06380003, + 0x81C, 0x053A0003, + 0x81C, 0x043C0003, + 0x81C, 0x033E0003, + 0x81C, 0x23400003, + 0x81C, 0x22420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0xA44C0003, + 0x81C, 0x684E0003, + 0x81C, 0x67500003, + 0x81C, 0x66520003, + 0x81C, 0x65540003, + 0x81C, 0x64560003, + 0x81C, 0x63580003, + 0x81C, 0x625A0003, + 0x81C, 0x615C0003, + 0x81C, 0x475E0003, + 0x81C, 0x46600003, + 0x81C, 0x45620003, + 0x81C, 0x44640003, + 0x81C, 0x43660003, + 0x81C, 0x42680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFE020003, + 0x81C, 0xFD040003, + 0x81C, 0xFC060003, + 0x81C, 0xFB080003, + 0x81C, 0xFA0A0003, + 0x81C, 0xF90C0003, + 0x81C, 0xF80E0003, + 0x81C, 0xF7100003, + 0x81C, 0xF6120003, + 0x81C, 0xF5140003, + 0x81C, 0xF4160003, + 0x81C, 0xF3180003, + 0x81C, 0xF21A0003, + 0x81C, 0xF11C0003, + 0x81C, 0xF01E0003, + 0x81C, 0xEF200003, + 0x81C, 0xEE220003, + 0x81C, 0xED240003, + 0x81C, 0xEC260003, + 0x81C, 0xEB280003, + 0x81C, 0xEA2A0003, + 0x81C, 0xE92C0003, + 0x81C, 0xE72E0003, + 0x81C, 0xE6300003, + 0x81C, 0xE5320003, + 0x81C, 0x08340003, + 0x81C, 0x07360003, + 0x81C, 0x06380003, + 0x81C, 0x053A0003, + 0x81C, 0x043C0003, + 0x81C, 0x033E0003, + 0x81C, 0x02400003, + 0x81C, 0xA9420003, + 0x81C, 0xA8440003, + 0x81C, 0xA7460003, + 0x81C, 0xA6480003, + 0x81C, 0xA54A0003, + 0x81C, 0x684C0003, + 0x81C, 0x674E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x475C0003, + 0x81C, 0x465E0003, + 0x81C, 0x45600003, + 0x81C, 0x44620003, + 0x81C, 0x43640003, + 0x81C, 0x42660003, + 0x81C, 0x41680003, + 0x81C, 0x416A0003, + 0x81C, 0x416C0003, + 0x81C, 0x416E0003, + 0x81C, 0x41700003, + 0x81C, 0x41720003, + 0x81C, 0x41740003, + 0x81C, 0x41760003, + 0x81C, 0x41780003, + 0x81C, 0x417A0003, + 0x81C, 0x417C0003, + 0x81C, 0x417E0003, + 0xA0000000, 0x00000000, + 0x81C, 0xFE000003, + 0x81C, 0xFD020003, + 0x81C, 0xFC040003, + 0x81C, 0xFB060003, + 0x81C, 0xFA080003, + 0x81C, 0xF90A0003, + 0x81C, 0xF80C0003, + 0x81C, 0xF70E0003, + 0x81C, 0xF6100003, + 0x81C, 0xF5120003, + 0x81C, 0xF4140003, + 0x81C, 0xF3160003, + 0x81C, 0xF2180003, + 0x81C, 0xF11A0003, + 0x81C, 0xF01C0003, + 0x81C, 0xEF1E0003, + 0x81C, 0xEE200003, + 0x81C, 0xED220003, + 0x81C, 0xCF240003, + 0x81C, 0xCE260003, + 0x81C, 0xCD280003, + 0x81C, 0xCC2A0003, + 0x81C, 0xCB2C0003, + 0x81C, 0xCA2E0003, + 0x81C, 0xC9300003, + 0x81C, 0xC8320003, + 0x81C, 0xC7340003, + 0x81C, 0xC6360003, + 0x81C, 0xC5380003, + 0x81C, 0xC43A0003, + 0x81C, 0xA63C0003, + 0x81C, 0xA53E0003, + 0x81C, 0xA4400003, + 0x81C, 0xA3420003, + 0x81C, 0xA2440003, + 0x81C, 0xA1460003, + 0x81C, 0x86480003, + 0x81C, 0x854A0003, + 0x81C, 0x844C0003, + 0x81C, 0x834E0003, + 0x81C, 0x66500003, + 0x81C, 0x65520003, + 0x81C, 0x64540003, + 0x81C, 0x63560003, + 0x81C, 0x62580003, + 0x81C, 0x615A0003, + 0x81C, 0x435C0003, + 0x81C, 0x425E0003, + 0x81C, 0x41600003, + 0x81C, 0x27620003, + 0x81C, 0x26640003, + 0x81C, 0x25660003, + 0x81C, 0x24680003, + 0x81C, 0x236A0003, + 0x81C, 0x226C0003, + 0x81C, 0x216E0003, + 0x81C, 0x21700003, + 0x81C, 0x21720003, + 0x81C, 0x21740003, + 0x81C, 0x21760003, + 0x81C, 0x21780003, + 0x81C, 0x217A0003, + 0x81C, 0x217C0003, + 0x81C, 0x217E0003, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000103, + 0x81C, 0xF8020103, + 0x81C, 0xF7040103, + 0x81C, 0xF6060103, + 0x81C, 0xF5080103, + 0x81C, 0xF40A0103, + 0x81C, 0xF30C0103, + 0x81C, 0xF20E0103, + 0x81C, 0xF1100103, + 0x81C, 0xF0120103, + 0x81C, 0xEF140103, + 0x81C, 0xEE160103, + 0x81C, 0xED180103, + 0x81C, 0xEC1A0103, + 0x81C, 0xEB1C0103, + 0x81C, 0xEA1E0103, + 0x81C, 0xE9200103, + 0x81C, 0xE8220103, + 0x81C, 0xE7240103, + 0x81C, 0xE6260103, + 0x81C, 0xE5280103, + 0x81C, 0xE42A0103, + 0x81C, 0xE32C0103, + 0x81C, 0xC32E0103, + 0x81C, 0xC2300103, + 0x81C, 0xC1320103, + 0x81C, 0xA5340103, + 0x81C, 0xA4360103, + 0x81C, 0xA3380103, + 0x81C, 0xA23A0103, + 0x81C, 0xA13C0103, + 0x81C, 0x843E0103, + 0x81C, 0x83400103, + 0x81C, 0x82420103, + 0x81C, 0x81440103, + 0x81C, 0x64460103, + 0x81C, 0x63480103, + 0x81C, 0x624A0103, + 0x81C, 0x614C0103, + 0x81C, 0x444E0103, + 0x81C, 0x43500103, + 0x81C, 0x42520103, + 0x81C, 0x41540103, + 0x81C, 0x25560103, + 0x81C, 0x24580103, + 0x81C, 0x235A0103, + 0x81C, 0x065C0103, + 0x81C, 0x055E0103, + 0x81C, 0x04600103, + 0x81C, 0x03620103, + 0x81C, 0x02640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000103, + 0x81C, 0xF7020103, + 0x81C, 0xF6040103, + 0x81C, 0xF5060103, + 0x81C, 0xF4080103, + 0x81C, 0xF30A0103, + 0x81C, 0xF20C0103, + 0x81C, 0xF10E0103, + 0x81C, 0xF0100103, + 0x81C, 0xEF120103, + 0x81C, 0xEE140103, + 0x81C, 0xED160103, + 0x81C, 0xEC180103, + 0x81C, 0xEB1A0103, + 0x81C, 0xEA1C0103, + 0x81C, 0xE91E0103, + 0x81C, 0xE8200103, + 0x81C, 0xE7220103, + 0x81C, 0xE6240103, + 0x81C, 0xE5260103, + 0x81C, 0xE4280103, + 0x81C, 0xE32A0103, + 0x81C, 0xE22C0103, + 0x81C, 0xE12E0103, + 0x81C, 0xA5300103, + 0x81C, 0xA4320103, + 0x81C, 0xA3340103, + 0x81C, 0xA2360103, + 0x81C, 0xA1380103, + 0x81C, 0x843A0103, + 0x81C, 0x833C0103, + 0x81C, 0x823E0103, + 0x81C, 0x81400103, + 0x81C, 0x64420103, + 0x81C, 0x63440103, + 0x81C, 0x62460103, + 0x81C, 0x61480103, + 0x81C, 0x454A0103, + 0x81C, 0x444C0103, + 0x81C, 0x434E0103, + 0x81C, 0x42500103, + 0x81C, 0x25520103, + 0x81C, 0x24540103, + 0x81C, 0x23560103, + 0x81C, 0x06580103, + 0x81C, 0x055A0103, + 0x81C, 0x045C0103, + 0x81C, 0x035E0103, + 0x81C, 0x02600103, + 0x81C, 0x01620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFC000103, + 0x81C, 0xFB020103, + 0x81C, 0xFA040103, + 0x81C, 0xF9060103, + 0x81C, 0xF8080103, + 0x81C, 0xF70A0103, + 0x81C, 0xF60C0103, + 0x81C, 0xF50E0103, + 0x81C, 0xF4100103, + 0x81C, 0xF3120103, + 0x81C, 0xF2140103, + 0x81C, 0xF1160103, + 0x81C, 0xF0180103, + 0x81C, 0xEF1A0103, + 0x81C, 0xEE1C0103, + 0x81C, 0xED1E0103, + 0x81C, 0xEC200103, + 0x81C, 0xEB220103, + 0x81C, 0xEA240103, + 0x81C, 0xE9260103, + 0x81C, 0xE8280103, + 0x81C, 0xE72A0103, + 0x81C, 0xE62C0103, + 0x81C, 0xE52E0103, + 0x81C, 0xE4300103, + 0x81C, 0xE3320103, + 0x81C, 0xE2340103, + 0x81C, 0xE1360103, + 0x81C, 0x87380103, + 0x81C, 0x863A0103, + 0x81C, 0x853C0103, + 0x81C, 0x843E0103, + 0x81C, 0x83400103, + 0x81C, 0x82420103, + 0x81C, 0x81440103, + 0x81C, 0x64460103, + 0x81C, 0x63480103, + 0x81C, 0x624A0103, + 0x81C, 0x464C0103, + 0x81C, 0x454E0103, + 0x81C, 0x44500103, + 0x81C, 0x43520103, + 0x81C, 0x26540103, + 0x81C, 0x25560103, + 0x81C, 0x24580103, + 0x81C, 0x075A0103, + 0x81C, 0x065C0103, + 0x81C, 0x055E0103, + 0x81C, 0x04600103, + 0x81C, 0x03620103, + 0x81C, 0x02640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000103, + 0x81C, 0xF8020103, + 0x81C, 0xF7040103, + 0x81C, 0xF6060103, + 0x81C, 0xF5080103, + 0x81C, 0xF40A0103, + 0x81C, 0xF30C0103, + 0x81C, 0xF20E0103, + 0x81C, 0xF1100103, + 0x81C, 0xF0120103, + 0x81C, 0xEF140103, + 0x81C, 0xEE160103, + 0x81C, 0xED180103, + 0x81C, 0xEC1A0103, + 0x81C, 0xEB1C0103, + 0x81C, 0xEA1E0103, + 0x81C, 0xE9200103, + 0x81C, 0xE8220103, + 0x81C, 0xE7240103, + 0x81C, 0xE6260103, + 0x81C, 0xE5280103, + 0x81C, 0xE42A0103, + 0x81C, 0xE32C0103, + 0x81C, 0xE22E0103, + 0x81C, 0xA6300103, + 0x81C, 0xA5320103, + 0x81C, 0xA4340103, + 0x81C, 0xA3360103, + 0x81C, 0xA2380103, + 0x81C, 0xA13A0103, + 0x81C, 0x843C0103, + 0x81C, 0x833E0103, + 0x81C, 0x82400103, + 0x81C, 0x81420103, + 0x81C, 0x64440103, + 0x81C, 0x63460103, + 0x81C, 0x62480103, + 0x81C, 0x614A0103, + 0x81C, 0x444C0103, + 0x81C, 0x434E0103, + 0x81C, 0x42500103, + 0x81C, 0x41520103, + 0x81C, 0x25540103, + 0x81C, 0x24560103, + 0x81C, 0x23580103, + 0x81C, 0x225A0103, + 0x81C, 0x055C0103, + 0x81C, 0x045E0103, + 0x81C, 0x03600103, + 0x81C, 0x02620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFD000103, + 0x81C, 0xFC020103, + 0x81C, 0xFB040103, + 0x81C, 0xFA060103, + 0x81C, 0xF9080103, + 0x81C, 0xF80A0103, + 0x81C, 0xF70C0103, + 0x81C, 0xF60E0103, + 0x81C, 0xF5100103, + 0x81C, 0xF4120103, + 0x81C, 0xF3140103, + 0x81C, 0xF2160103, + 0x81C, 0xF1180103, + 0x81C, 0xF01A0103, + 0x81C, 0xEF1C0103, + 0x81C, 0xEE1E0103, + 0x81C, 0xED200103, + 0x81C, 0xEC220103, + 0x81C, 0xEB240103, + 0x81C, 0xEA260103, + 0x81C, 0xE9280103, + 0x81C, 0xE82A0103, + 0x81C, 0xE72C0103, + 0x81C, 0xE62E0103, + 0x81C, 0xE5300103, + 0x81C, 0xE4320103, + 0x81C, 0xE3340103, + 0x81C, 0xE2360103, + 0x81C, 0xE1380103, + 0x81C, 0xA33A0103, + 0x81C, 0xA23C0103, + 0x81C, 0xA13E0103, + 0x81C, 0x84400103, + 0x81C, 0x83420103, + 0x81C, 0x82440103, + 0x81C, 0x81460103, + 0x81C, 0x64480103, + 0x81C, 0x634A0103, + 0x81C, 0x624C0103, + 0x81C, 0x614E0103, + 0x81C, 0x45500103, + 0x81C, 0x44520103, + 0x81C, 0x43540103, + 0x81C, 0x42560103, + 0x81C, 0x25580103, + 0x81C, 0x245A0103, + 0x81C, 0x235C0103, + 0x81C, 0x065E0103, + 0x81C, 0x05600103, + 0x81C, 0x04620103, + 0x81C, 0x03640103, + 0x81C, 0x02660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFA000103, + 0x81C, 0xF9020103, + 0x81C, 0xF8040103, + 0x81C, 0xF7060103, + 0x81C, 0xF6080103, + 0x81C, 0xF50A0103, + 0x81C, 0xF40C0103, + 0x81C, 0xF30E0103, + 0x81C, 0xF2100103, + 0x81C, 0xF1120103, + 0x81C, 0xF0140103, + 0x81C, 0xEF160103, + 0x81C, 0xEE180103, + 0x81C, 0xED1A0103, + 0x81C, 0xEC1C0103, + 0x81C, 0xEB1E0103, + 0x81C, 0xEA200103, + 0x81C, 0xE9220103, + 0x81C, 0xE8240103, + 0x81C, 0xE7260103, + 0x81C, 0xE6280103, + 0x81C, 0xE52A0103, + 0x81C, 0xE42C0103, + 0x81C, 0xE32E0103, + 0x81C, 0xE2300103, + 0x81C, 0xE1320103, + 0x81C, 0xA5340103, + 0x81C, 0xA4360103, + 0x81C, 0xA3380103, + 0x81C, 0xA23A0103, + 0x81C, 0xA13C0103, + 0x81C, 0x843E0103, + 0x81C, 0x83400103, + 0x81C, 0x82420103, + 0x81C, 0x81440103, + 0x81C, 0x64460103, + 0x81C, 0x63480103, + 0x81C, 0x624A0103, + 0x81C, 0x614C0103, + 0x81C, 0x454E0103, + 0x81C, 0x44500103, + 0x81C, 0x43520103, + 0x81C, 0x42540103, + 0x81C, 0x41560103, + 0x81C, 0x24580103, + 0x81C, 0x235A0103, + 0x81C, 0x225C0103, + 0x81C, 0x055E0103, + 0x81C, 0x04600103, + 0x81C, 0x03620103, + 0x81C, 0x02640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000103, + 0x81C, 0xFF020103, + 0x81C, 0xFE040103, + 0x81C, 0xFD060103, + 0x81C, 0xFC080103, + 0x81C, 0xFB0A0103, + 0x81C, 0xFA0C0103, + 0x81C, 0xF90E0103, + 0x81C, 0xF8100103, + 0x81C, 0xF7120103, + 0x81C, 0xF6140103, + 0x81C, 0xF5160103, + 0x81C, 0xF4180103, + 0x81C, 0xF31A0103, + 0x81C, 0xF21C0103, + 0x81C, 0xF11E0103, + 0x81C, 0xF0200103, + 0x81C, 0xEF220103, + 0x81C, 0xEE240103, + 0x81C, 0xED260103, + 0x81C, 0xEC280103, + 0x81C, 0xEB2A0103, + 0x81C, 0xEA2C0103, + 0x81C, 0xE92E0103, + 0x81C, 0xE8300103, + 0x81C, 0xE7320103, + 0x81C, 0xE6340103, + 0x81C, 0xE5360103, + 0x81C, 0xE4380103, + 0x81C, 0xE33A0103, + 0x81C, 0xA53C0103, + 0x81C, 0xA43E0103, + 0x81C, 0xA3400103, + 0x81C, 0xA2420103, + 0x81C, 0xA1440103, + 0x81C, 0x85460103, + 0x81C, 0x84480103, + 0x81C, 0x834A0103, + 0x81C, 0x824C0103, + 0x81C, 0x814E0103, + 0x81C, 0x64500103, + 0x81C, 0x63520103, + 0x81C, 0x62540103, + 0x81C, 0x44560103, + 0x81C, 0x43580103, + 0x81C, 0x425A0103, + 0x81C, 0x265C0103, + 0x81C, 0x255E0103, + 0x81C, 0x24600103, + 0x81C, 0x07620103, + 0x81C, 0x06640103, + 0x81C, 0x05660103, + 0x81C, 0x04680103, + 0x81C, 0x036A0103, + 0x81C, 0x026C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000103, + 0x81C, 0xF7020103, + 0x81C, 0xF6040103, + 0x81C, 0xF5060103, + 0x81C, 0xF4080103, + 0x81C, 0xF30A0103, + 0x81C, 0xF20C0103, + 0x81C, 0xF10E0103, + 0x81C, 0xF0100103, + 0x81C, 0xEF120103, + 0x81C, 0xEE140103, + 0x81C, 0xED160103, + 0x81C, 0xEC180103, + 0x81C, 0xEB1A0103, + 0x81C, 0xEA1C0103, + 0x81C, 0xE91E0103, + 0x81C, 0xE8200103, + 0x81C, 0xE7220103, + 0x81C, 0xE6240103, + 0x81C, 0xE5260103, + 0x81C, 0xE4280103, + 0x81C, 0xE32A0103, + 0x81C, 0xE22C0103, + 0x81C, 0xE12E0103, + 0x81C, 0xA4300103, + 0x81C, 0xA3320103, + 0x81C, 0xA2340103, + 0x81C, 0xA1360103, + 0x81C, 0x85380103, + 0x81C, 0x843A0103, + 0x81C, 0x833C0103, + 0x81C, 0x823E0103, + 0x81C, 0x65400103, + 0x81C, 0x64420103, + 0x81C, 0x63440103, + 0x81C, 0x62460103, + 0x81C, 0x45480103, + 0x81C, 0x444A0103, + 0x81C, 0x434C0103, + 0x81C, 0x264E0103, + 0x81C, 0x25500103, + 0x81C, 0x24520103, + 0x81C, 0x08540103, + 0x81C, 0x07560103, + 0x81C, 0x06580103, + 0x81C, 0x055A0103, + 0x81C, 0x045C0103, + 0x81C, 0x035E0103, + 0x81C, 0x02600103, + 0x81C, 0x01620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000103, + 0x81C, 0xFF020103, + 0x81C, 0xFE040103, + 0x81C, 0xFD060103, + 0x81C, 0xFC080103, + 0x81C, 0xFB0A0103, + 0x81C, 0xFA0C0103, + 0x81C, 0xF90E0103, + 0x81C, 0xF8100103, + 0x81C, 0xF7120103, + 0x81C, 0xF6140103, + 0x81C, 0xF5160103, + 0x81C, 0xF4180103, + 0x81C, 0xF31A0103, + 0x81C, 0xF21C0103, + 0x81C, 0xF11E0103, + 0x81C, 0xF0200103, + 0x81C, 0xEF220103, + 0x81C, 0xEE240103, + 0x81C, 0xED260103, + 0x81C, 0xEC280103, + 0x81C, 0xEB2A0103, + 0x81C, 0xEA2C0103, + 0x81C, 0xE92E0103, + 0x81C, 0xE8300103, + 0x81C, 0xE7320103, + 0x81C, 0xE6340103, + 0x81C, 0xE5360103, + 0x81C, 0xE4380103, + 0x81C, 0xE33A0103, + 0x81C, 0xA53C0103, + 0x81C, 0xA43E0103, + 0x81C, 0xA3400103, + 0x81C, 0xA2420103, + 0x81C, 0xA1440103, + 0x81C, 0x85460103, + 0x81C, 0x84480103, + 0x81C, 0x834A0103, + 0x81C, 0x824C0103, + 0x81C, 0x814E0103, + 0x81C, 0x64500103, + 0x81C, 0x63520103, + 0x81C, 0x62540103, + 0x81C, 0x44560103, + 0x81C, 0x43580103, + 0x81C, 0x425A0103, + 0x81C, 0x265C0103, + 0x81C, 0x255E0103, + 0x81C, 0x24600103, + 0x81C, 0x07620103, + 0x81C, 0x06640103, + 0x81C, 0x05660103, + 0x81C, 0x04680103, + 0x81C, 0x036A0103, + 0x81C, 0x026C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000103, + 0x81C, 0xF8020103, + 0x81C, 0xF7040103, + 0x81C, 0xF6060103, + 0x81C, 0xF5080103, + 0x81C, 0xF40A0103, + 0x81C, 0xF30C0103, + 0x81C, 0xF20E0103, + 0x81C, 0xF1100103, + 0x81C, 0xF0120103, + 0x81C, 0xEF140103, + 0x81C, 0xEE160103, + 0x81C, 0xED180103, + 0x81C, 0xEC1A0103, + 0x81C, 0xEB1C0103, + 0x81C, 0xEA1E0103, + 0x81C, 0xE9200103, + 0x81C, 0xE8220103, + 0x81C, 0xE7240103, + 0x81C, 0xE6260103, + 0x81C, 0xE5280103, + 0x81C, 0xE42A0103, + 0x81C, 0xE32C0103, + 0x81C, 0xE22E0103, + 0x81C, 0xA6300103, + 0x81C, 0xA5320103, + 0x81C, 0xA4340103, + 0x81C, 0xA3360103, + 0x81C, 0xA2380103, + 0x81C, 0xA13A0103, + 0x81C, 0x843C0103, + 0x81C, 0x833E0103, + 0x81C, 0x82400103, + 0x81C, 0x81420103, + 0x81C, 0x64440103, + 0x81C, 0x63460103, + 0x81C, 0x62480103, + 0x81C, 0x614A0103, + 0x81C, 0x444C0103, + 0x81C, 0x434E0103, + 0x81C, 0x42500103, + 0x81C, 0x41520103, + 0x81C, 0x25540103, + 0x81C, 0x24560103, + 0x81C, 0x23580103, + 0x81C, 0x225A0103, + 0x81C, 0x055C0103, + 0x81C, 0x045E0103, + 0x81C, 0x03600103, + 0x81C, 0x02620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFA000103, + 0x81C, 0xF9020103, + 0x81C, 0xF8040103, + 0x81C, 0xF7060103, + 0x81C, 0xF6080103, + 0x81C, 0xF50A0103, + 0x81C, 0xF40C0103, + 0x81C, 0xF30E0103, + 0x81C, 0xF2100103, + 0x81C, 0xF1120103, + 0x81C, 0xF0140103, + 0x81C, 0xEF160103, + 0x81C, 0xEE180103, + 0x81C, 0xED1A0103, + 0x81C, 0xCC1C0103, + 0x81C, 0xCB1E0103, + 0x81C, 0xCA200103, + 0x81C, 0xE9220103, + 0x81C, 0xE8240103, + 0x81C, 0xE7260103, + 0x81C, 0xE6280103, + 0x81C, 0xE42A0103, + 0x81C, 0xE32C0103, + 0x81C, 0xE22E0103, + 0x81C, 0xA7300103, + 0x81C, 0xA6320103, + 0x81C, 0xA5340103, + 0x81C, 0xA4360103, + 0x81C, 0xA3380103, + 0x81C, 0xA23A0103, + 0x81C, 0xA13C0103, + 0x81C, 0x843E0103, + 0x81C, 0x83400103, + 0x81C, 0x82420103, + 0x81C, 0x65440103, + 0x81C, 0x64460103, + 0x81C, 0x63480103, + 0x81C, 0x624A0103, + 0x81C, 0x614C0103, + 0x81C, 0x444E0103, + 0x81C, 0x43500103, + 0x81C, 0x42520103, + 0x81C, 0x41540103, + 0x81C, 0x24560103, + 0x81C, 0x23580103, + 0x81C, 0x055A0103, + 0x81C, 0x045C0103, + 0x81C, 0x035E0103, + 0x81C, 0x02600103, + 0x81C, 0x01620103, + 0x81C, 0x01640103, + 0x81C, 0x01660103, + 0x81C, 0x01680103, + 0x81C, 0x016A0103, + 0x81C, 0x016C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0xA0000000, 0x00000000, + 0x81C, 0xFF000103, + 0x81C, 0xFE020103, + 0x81C, 0xFD040103, + 0x81C, 0xFC060103, + 0x81C, 0xFB080103, + 0x81C, 0xFA0A0103, + 0x81C, 0xF90C0103, + 0x81C, 0xF80E0103, + 0x81C, 0xF7100103, + 0x81C, 0xF6120103, + 0x81C, 0xF5140103, + 0x81C, 0xF4160103, + 0x81C, 0xF3180103, + 0x81C, 0xF21A0103, + 0x81C, 0xF11C0103, + 0x81C, 0xF01E0103, + 0x81C, 0xEF200103, + 0x81C, 0xEE220103, + 0x81C, 0xED240103, + 0x81C, 0xEC260103, + 0x81C, 0xEB280103, + 0x81C, 0xEA2A0103, + 0x81C, 0xE92C0103, + 0x81C, 0xE82E0103, + 0x81C, 0xE7300103, + 0x81C, 0xE6320103, + 0x81C, 0xE5340103, + 0x81C, 0xE4360103, + 0x81C, 0xE3380103, + 0x81C, 0xE23A0103, + 0x81C, 0xE13C0103, + 0x81C, 0xA43E0103, + 0x81C, 0xA3400103, + 0x81C, 0xA2420103, + 0x81C, 0xA1440103, + 0x81C, 0x86460103, + 0x81C, 0x85480103, + 0x81C, 0x844A0103, + 0x81C, 0x834C0103, + 0x81C, 0x824E0103, + 0x81C, 0x81500103, + 0x81C, 0x64520103, + 0x81C, 0x63540103, + 0x81C, 0x62560103, + 0x81C, 0x61580103, + 0x81C, 0x435A0103, + 0x81C, 0x425C0103, + 0x81C, 0x415E0103, + 0x81C, 0x25600103, + 0x81C, 0x24620103, + 0x81C, 0x06640103, + 0x81C, 0x05660103, + 0x81C, 0x04680103, + 0x81C, 0x036A0103, + 0x81C, 0x026C0103, + 0x81C, 0x016E0103, + 0x81C, 0x01700103, + 0x81C, 0x01720103, + 0x81C, 0x01740103, + 0x81C, 0x01760103, + 0x81C, 0x01780103, + 0x81C, 0x017A0103, + 0x81C, 0x017C0103, + 0x81C, 0x017E0103, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFA000203, + 0x81C, 0xF9020203, + 0x81C, 0xF8040203, + 0x81C, 0xF7060203, + 0x81C, 0xF6080203, + 0x81C, 0xF50A0203, + 0x81C, 0xF40C0203, + 0x81C, 0xF30E0203, + 0x81C, 0xF2100203, + 0x81C, 0xF1120203, + 0x81C, 0xF0140203, + 0x81C, 0xEF160203, + 0x81C, 0xEE180203, + 0x81C, 0xED1A0203, + 0x81C, 0xEC1C0203, + 0x81C, 0xEB1E0203, + 0x81C, 0xEA200203, + 0x81C, 0xE9220203, + 0x81C, 0xE8240203, + 0x81C, 0xE7260203, + 0x81C, 0xE6280203, + 0x81C, 0xE52A0203, + 0x81C, 0xE42C0203, + 0x81C, 0xE32E0203, + 0x81C, 0xE2300203, + 0x81C, 0xE1320203, + 0x81C, 0xA5340203, + 0x81C, 0xA4360203, + 0x81C, 0xA3380203, + 0x81C, 0xA23A0203, + 0x81C, 0xA13C0203, + 0x81C, 0x843E0203, + 0x81C, 0x83400203, + 0x81C, 0x82420203, + 0x81C, 0x81440203, + 0x81C, 0x63460203, + 0x81C, 0x62480203, + 0x81C, 0x614A0203, + 0x81C, 0x464C0203, + 0x81C, 0x454E0203, + 0x81C, 0x44500203, + 0x81C, 0x43520203, + 0x81C, 0x42540203, + 0x81C, 0x41560203, + 0x81C, 0x24580203, + 0x81C, 0x235A0203, + 0x81C, 0x065C0203, + 0x81C, 0x055E0203, + 0x81C, 0x04600203, + 0x81C, 0x03620203, + 0x81C, 0x02640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000203, + 0x81C, 0xF7020203, + 0x81C, 0xF6040203, + 0x81C, 0xF5060203, + 0x81C, 0xF4080203, + 0x81C, 0xF30A0203, + 0x81C, 0xF20C0203, + 0x81C, 0xF10E0203, + 0x81C, 0xF0100203, + 0x81C, 0xEF120203, + 0x81C, 0xEE140203, + 0x81C, 0xED160203, + 0x81C, 0xEC180203, + 0x81C, 0xEB1A0203, + 0x81C, 0xEA1C0203, + 0x81C, 0xE91E0203, + 0x81C, 0xE8200203, + 0x81C, 0xE7220203, + 0x81C, 0xE6240203, + 0x81C, 0xE5260203, + 0x81C, 0xE4280203, + 0x81C, 0xE32A0203, + 0x81C, 0xE22C0203, + 0x81C, 0xE12E0203, + 0x81C, 0xA6300203, + 0x81C, 0xA5320203, + 0x81C, 0xA4340203, + 0x81C, 0xA3360203, + 0x81C, 0xA2380203, + 0x81C, 0x853A0203, + 0x81C, 0x843C0203, + 0x81C, 0x833E0203, + 0x81C, 0x82400203, + 0x81C, 0x81420203, + 0x81C, 0x64440203, + 0x81C, 0x63460203, + 0x81C, 0x62480203, + 0x81C, 0x614A0203, + 0x81C, 0x444C0203, + 0x81C, 0x434E0203, + 0x81C, 0x42500203, + 0x81C, 0x25520203, + 0x81C, 0x24540203, + 0x81C, 0x23560203, + 0x81C, 0x06580203, + 0x81C, 0x055A0203, + 0x81C, 0x045C0203, + 0x81C, 0x035E0203, + 0x81C, 0x02600203, + 0x81C, 0x01620203, + 0x81C, 0x01640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFC000203, + 0x81C, 0xFB020203, + 0x81C, 0xFA040203, + 0x81C, 0xF9060203, + 0x81C, 0xF8080203, + 0x81C, 0xF70A0203, + 0x81C, 0xF60C0203, + 0x81C, 0xF50E0203, + 0x81C, 0xF4100203, + 0x81C, 0xF3120203, + 0x81C, 0xF2140203, + 0x81C, 0xF1160203, + 0x81C, 0xF0180203, + 0x81C, 0xEF1A0203, + 0x81C, 0xEE1C0203, + 0x81C, 0xED1E0203, + 0x81C, 0xEC200203, + 0x81C, 0xEB220203, + 0x81C, 0xEA240203, + 0x81C, 0xE9260203, + 0x81C, 0xE8280203, + 0x81C, 0xE72A0203, + 0x81C, 0xE62C0203, + 0x81C, 0xE52E0203, + 0x81C, 0xE4300203, + 0x81C, 0xE3320203, + 0x81C, 0xE2340203, + 0x81C, 0xE1360203, + 0x81C, 0x87380203, + 0x81C, 0x863A0203, + 0x81C, 0x853C0203, + 0x81C, 0x843E0203, + 0x81C, 0x83400203, + 0x81C, 0x82420203, + 0x81C, 0x81440203, + 0x81C, 0x64460203, + 0x81C, 0x63480203, + 0x81C, 0x624A0203, + 0x81C, 0x474C0203, + 0x81C, 0x464E0203, + 0x81C, 0x45500203, + 0x81C, 0x44520203, + 0x81C, 0x43540203, + 0x81C, 0x42560203, + 0x81C, 0x24580203, + 0x81C, 0x235A0203, + 0x81C, 0x075C0203, + 0x81C, 0x065E0203, + 0x81C, 0x05600203, + 0x81C, 0x04620203, + 0x81C, 0x03640203, + 0x81C, 0x02660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000203, + 0x81C, 0xF7020203, + 0x81C, 0xF6040203, + 0x81C, 0xF5060203, + 0x81C, 0xF4080203, + 0x81C, 0xF30A0203, + 0x81C, 0xF20C0203, + 0x81C, 0xF10E0203, + 0x81C, 0xF0100203, + 0x81C, 0xEF120203, + 0x81C, 0xEE140203, + 0x81C, 0xED160203, + 0x81C, 0xEC180203, + 0x81C, 0xEB1A0203, + 0x81C, 0xEA1C0203, + 0x81C, 0xE91E0203, + 0x81C, 0xE8200203, + 0x81C, 0xE7220203, + 0x81C, 0xE6240203, + 0x81C, 0xE5260203, + 0x81C, 0xE4280203, + 0x81C, 0xE32A0203, + 0x81C, 0xE22C0203, + 0x81C, 0xE12E0203, + 0x81C, 0xA6300203, + 0x81C, 0xA5320203, + 0x81C, 0xA4340203, + 0x81C, 0xA3360203, + 0x81C, 0xA2380203, + 0x81C, 0xA13A0203, + 0x81C, 0x843C0203, + 0x81C, 0x833E0203, + 0x81C, 0x82400203, + 0x81C, 0x81420203, + 0x81C, 0x64440203, + 0x81C, 0x63460203, + 0x81C, 0x62480203, + 0x81C, 0x614A0203, + 0x81C, 0x444C0203, + 0x81C, 0x434E0203, + 0x81C, 0x42500203, + 0x81C, 0x41520203, + 0x81C, 0x25540203, + 0x81C, 0x24560203, + 0x81C, 0x23580203, + 0x81C, 0x065A0203, + 0x81C, 0x055C0203, + 0x81C, 0x045E0203, + 0x81C, 0x03600203, + 0x81C, 0x02620203, + 0x81C, 0x01640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFB000203, + 0x81C, 0xFA020203, + 0x81C, 0xF9040203, + 0x81C, 0xF8060203, + 0x81C, 0xF7080203, + 0x81C, 0xF60A0203, + 0x81C, 0xF50C0203, + 0x81C, 0xF40E0203, + 0x81C, 0xF3100203, + 0x81C, 0xF2120203, + 0x81C, 0xF1140203, + 0x81C, 0xF0160203, + 0x81C, 0xEF180203, + 0x81C, 0xEE1A0203, + 0x81C, 0xED1C0203, + 0x81C, 0xEC1E0203, + 0x81C, 0xEB200203, + 0x81C, 0xEA220203, + 0x81C, 0xE9240203, + 0x81C, 0xE8260203, + 0x81C, 0xE7280203, + 0x81C, 0xE62A0203, + 0x81C, 0xE52C0203, + 0x81C, 0xE42E0203, + 0x81C, 0xE3300203, + 0x81C, 0xE2320203, + 0x81C, 0xE1340203, + 0x81C, 0xA5360203, + 0x81C, 0xA4380203, + 0x81C, 0xA33A0203, + 0x81C, 0xA23C0203, + 0x81C, 0x843E0203, + 0x81C, 0x83400203, + 0x81C, 0x82420203, + 0x81C, 0x81440203, + 0x81C, 0x64460203, + 0x81C, 0x63480203, + 0x81C, 0x624A0203, + 0x81C, 0x614C0203, + 0x81C, 0x474E0203, + 0x81C, 0x46500203, + 0x81C, 0x45520203, + 0x81C, 0x44540203, + 0x81C, 0x43560203, + 0x81C, 0x25580203, + 0x81C, 0x245A0203, + 0x81C, 0x235C0203, + 0x81C, 0x075E0203, + 0x81C, 0x06600203, + 0x81C, 0x05620203, + 0x81C, 0x04640203, + 0x81C, 0x03660203, + 0x81C, 0x02680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFC000203, + 0x81C, 0xFB020203, + 0x81C, 0xFA040203, + 0x81C, 0xF9060203, + 0x81C, 0xF8080203, + 0x81C, 0xF70A0203, + 0x81C, 0xF60C0203, + 0x81C, 0xF50E0203, + 0x81C, 0xF4100203, + 0x81C, 0xF3120203, + 0x81C, 0xF2140203, + 0x81C, 0xF1160203, + 0x81C, 0xF0180203, + 0x81C, 0xEF1A0203, + 0x81C, 0xEE1C0203, + 0x81C, 0xED1E0203, + 0x81C, 0xEC200203, + 0x81C, 0xEB220203, + 0x81C, 0xEA240203, + 0x81C, 0xE9260203, + 0x81C, 0xE8280203, + 0x81C, 0xE72A0203, + 0x81C, 0xE62C0203, + 0x81C, 0xE52E0203, + 0x81C, 0xE4300203, + 0x81C, 0xE3320203, + 0x81C, 0xE2340203, + 0x81C, 0xE1360203, + 0x81C, 0xA5380203, + 0x81C, 0xA43A0203, + 0x81C, 0xA33C0203, + 0x81C, 0x853E0203, + 0x81C, 0x84400203, + 0x81C, 0x83420203, + 0x81C, 0x82440203, + 0x81C, 0x81460203, + 0x81C, 0x64480203, + 0x81C, 0x634A0203, + 0x81C, 0x624C0203, + 0x81C, 0x614E0203, + 0x81C, 0x46500203, + 0x81C, 0x45520203, + 0x81C, 0x44540203, + 0x81C, 0x43560203, + 0x81C, 0x25580203, + 0x81C, 0x245A0203, + 0x81C, 0x235C0203, + 0x81C, 0x075E0203, + 0x81C, 0x06600203, + 0x81C, 0x05620203, + 0x81C, 0x04640203, + 0x81C, 0x03660203, + 0x81C, 0x02680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000203, + 0x81C, 0xFF020203, + 0x81C, 0xFE040203, + 0x81C, 0xFD060203, + 0x81C, 0xFC080203, + 0x81C, 0xFB0A0203, + 0x81C, 0xFA0C0203, + 0x81C, 0xF90E0203, + 0x81C, 0xF8100203, + 0x81C, 0xF7120203, + 0x81C, 0xF6140203, + 0x81C, 0xF5160203, + 0x81C, 0xF4180203, + 0x81C, 0xF31A0203, + 0x81C, 0xF21C0203, + 0x81C, 0xF11E0203, + 0x81C, 0xF0200203, + 0x81C, 0xEF220203, + 0x81C, 0xEE240203, + 0x81C, 0xED260203, + 0x81C, 0xEC280203, + 0x81C, 0xEB2A0203, + 0x81C, 0xEA2C0203, + 0x81C, 0xE92E0203, + 0x81C, 0xE8300203, + 0x81C, 0xE7320203, + 0x81C, 0xE6340203, + 0x81C, 0xE5360203, + 0x81C, 0xE4380203, + 0x81C, 0xE33A0203, + 0x81C, 0xE23C0203, + 0x81C, 0xE13E0203, + 0x81C, 0xA4400203, + 0x81C, 0xA3420203, + 0x81C, 0xA2440203, + 0x81C, 0xA1460203, + 0x81C, 0x84480203, + 0x81C, 0x834A0203, + 0x81C, 0x824C0203, + 0x81C, 0x814E0203, + 0x81C, 0x64500203, + 0x81C, 0x63520203, + 0x81C, 0x62540203, + 0x81C, 0x61560203, + 0x81C, 0x45580203, + 0x81C, 0x445A0203, + 0x81C, 0x435C0203, + 0x81C, 0x425E0203, + 0x81C, 0x24600203, + 0x81C, 0x23620203, + 0x81C, 0x07640203, + 0x81C, 0x06660203, + 0x81C, 0x05680203, + 0x81C, 0x046A0203, + 0x81C, 0x036C0203, + 0x81C, 0x026E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000203, + 0x81C, 0xF6020203, + 0x81C, 0xF5040203, + 0x81C, 0xF4060203, + 0x81C, 0xF3080203, + 0x81C, 0xF20A0203, + 0x81C, 0xF10C0203, + 0x81C, 0xF00E0203, + 0x81C, 0xEF100203, + 0x81C, 0xEE120203, + 0x81C, 0xED140203, + 0x81C, 0xEC160203, + 0x81C, 0xEB180203, + 0x81C, 0xEA1A0203, + 0x81C, 0xE91C0203, + 0x81C, 0xE81E0203, + 0x81C, 0xE7200203, + 0x81C, 0xE6220203, + 0x81C, 0xE5240203, + 0x81C, 0xE4260203, + 0x81C, 0xE3280203, + 0x81C, 0xE22A0203, + 0x81C, 0xA62C0203, + 0x81C, 0xA52E0203, + 0x81C, 0xA4300203, + 0x81C, 0xA3320203, + 0x81C, 0xA2340203, + 0x81C, 0xA1360203, + 0x81C, 0x86380203, + 0x81C, 0x853A0203, + 0x81C, 0x843C0203, + 0x81C, 0x833E0203, + 0x81C, 0x65400203, + 0x81C, 0x64420203, + 0x81C, 0x63440203, + 0x81C, 0x46460203, + 0x81C, 0x45480203, + 0x81C, 0x444A0203, + 0x81C, 0x434C0203, + 0x81C, 0x264E0203, + 0x81C, 0x25500203, + 0x81C, 0x24520203, + 0x81C, 0x08540203, + 0x81C, 0x07560203, + 0x81C, 0x06580203, + 0x81C, 0x055A0203, + 0x81C, 0x045C0203, + 0x81C, 0x035E0203, + 0x81C, 0x02600203, + 0x81C, 0x01620203, + 0x81C, 0x01640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFF000203, + 0x81C, 0xFF020203, + 0x81C, 0xFE040203, + 0x81C, 0xFD060203, + 0x81C, 0xFC080203, + 0x81C, 0xFB0A0203, + 0x81C, 0xFA0C0203, + 0x81C, 0xF90E0203, + 0x81C, 0xF8100203, + 0x81C, 0xF7120203, + 0x81C, 0xF6140203, + 0x81C, 0xF5160203, + 0x81C, 0xF4180203, + 0x81C, 0xF31A0203, + 0x81C, 0xF21C0203, + 0x81C, 0xF11E0203, + 0x81C, 0xF0200203, + 0x81C, 0xEF220203, + 0x81C, 0xEE240203, + 0x81C, 0xED260203, + 0x81C, 0xEC280203, + 0x81C, 0xEB2A0203, + 0x81C, 0xEA2C0203, + 0x81C, 0xE92E0203, + 0x81C, 0xE8300203, + 0x81C, 0xE7320203, + 0x81C, 0xE6340203, + 0x81C, 0xE5360203, + 0x81C, 0xE4380203, + 0x81C, 0xE33A0203, + 0x81C, 0xE23C0203, + 0x81C, 0xE13E0203, + 0x81C, 0xA4400203, + 0x81C, 0xA3420203, + 0x81C, 0xA2440203, + 0x81C, 0xA1460203, + 0x81C, 0x84480203, + 0x81C, 0x834A0203, + 0x81C, 0x824C0203, + 0x81C, 0x814E0203, + 0x81C, 0x64500203, + 0x81C, 0x63520203, + 0x81C, 0x62540203, + 0x81C, 0x61560203, + 0x81C, 0x45580203, + 0x81C, 0x445A0203, + 0x81C, 0x435C0203, + 0x81C, 0x425E0203, + 0x81C, 0x24600203, + 0x81C, 0x23620203, + 0x81C, 0x07640203, + 0x81C, 0x06660203, + 0x81C, 0x05680203, + 0x81C, 0x046A0203, + 0x81C, 0x036C0203, + 0x81C, 0x026E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000203, + 0x81C, 0xF7020203, + 0x81C, 0xF6040203, + 0x81C, 0xF5060203, + 0x81C, 0xF4080203, + 0x81C, 0xF30A0203, + 0x81C, 0xF20C0203, + 0x81C, 0xF10E0203, + 0x81C, 0xF0100203, + 0x81C, 0xEF120203, + 0x81C, 0xEE140203, + 0x81C, 0xED160203, + 0x81C, 0xEC180203, + 0x81C, 0xEB1A0203, + 0x81C, 0xEA1C0203, + 0x81C, 0xE91E0203, + 0x81C, 0xE8200203, + 0x81C, 0xE7220203, + 0x81C, 0xE6240203, + 0x81C, 0xE5260203, + 0x81C, 0xE4280203, + 0x81C, 0xE32A0203, + 0x81C, 0xE22C0203, + 0x81C, 0xE12E0203, + 0x81C, 0xA6300203, + 0x81C, 0xA5320203, + 0x81C, 0xA4340203, + 0x81C, 0xA3360203, + 0x81C, 0xA2380203, + 0x81C, 0xA13A0203, + 0x81C, 0x843C0203, + 0x81C, 0x833E0203, + 0x81C, 0x82400203, + 0x81C, 0x81420203, + 0x81C, 0x64440203, + 0x81C, 0x63460203, + 0x81C, 0x62480203, + 0x81C, 0x614A0203, + 0x81C, 0x444C0203, + 0x81C, 0x434E0203, + 0x81C, 0x42500203, + 0x81C, 0x41520203, + 0x81C, 0x25540203, + 0x81C, 0x24560203, + 0x81C, 0x23580203, + 0x81C, 0x065A0203, + 0x81C, 0x055C0203, + 0x81C, 0x045E0203, + 0x81C, 0x03600203, + 0x81C, 0x02620203, + 0x81C, 0x01640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000203, + 0x81C, 0xF8020203, + 0x81C, 0xF7040203, + 0x81C, 0xF6060203, + 0x81C, 0xF5080203, + 0x81C, 0xF40A0203, + 0x81C, 0xF30C0203, + 0x81C, 0xF20E0203, + 0x81C, 0xF1100203, + 0x81C, 0xF0120203, + 0x81C, 0xEF140203, + 0x81C, 0xCE160203, + 0x81C, 0xCD180203, + 0x81C, 0xCC1A0203, + 0x81C, 0xCB1C0203, + 0x81C, 0xCA1E0203, + 0x81C, 0xC9200203, + 0x81C, 0xC8220203, + 0x81C, 0xC7240203, + 0x81C, 0xC6260203, + 0x81C, 0xC5280203, + 0x81C, 0xC42A0203, + 0x81C, 0xC32C0203, + 0x81C, 0xC22E0203, + 0x81C, 0xC1300203, + 0x81C, 0xA5320203, + 0x81C, 0xA4340203, + 0x81C, 0xA3360203, + 0x81C, 0xA2380203, + 0x81C, 0xA13A0203, + 0x81C, 0x853C0203, + 0x81C, 0x843E0203, + 0x81C, 0x83400203, + 0x81C, 0x82420203, + 0x81C, 0x81440203, + 0x81C, 0x64460203, + 0x81C, 0x63480203, + 0x81C, 0x624A0203, + 0x81C, 0x614C0203, + 0x81C, 0x444E0203, + 0x81C, 0x43500203, + 0x81C, 0x42520203, + 0x81C, 0x41540203, + 0x81C, 0x24560203, + 0x81C, 0x23580203, + 0x81C, 0x075A0203, + 0x81C, 0x065C0203, + 0x81C, 0x055E0203, + 0x81C, 0x04600203, + 0x81C, 0x03620203, + 0x81C, 0x02640203, + 0x81C, 0x01660203, + 0x81C, 0x01680203, + 0x81C, 0x016A0203, + 0x81C, 0x016C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0xA0000000, 0x00000000, + 0x81C, 0xFF000203, + 0x81C, 0xFF020203, + 0x81C, 0xFE040203, + 0x81C, 0xFD060203, + 0x81C, 0xFC080203, + 0x81C, 0xFB0A0203, + 0x81C, 0xFA0C0203, + 0x81C, 0xF90E0203, + 0x81C, 0xF8100203, + 0x81C, 0xF7120203, + 0x81C, 0xF6140203, + 0x81C, 0xF5160203, + 0x81C, 0xF4180203, + 0x81C, 0xF31A0203, + 0x81C, 0xF21C0203, + 0x81C, 0xF11E0203, + 0x81C, 0xF0200203, + 0x81C, 0xEF220203, + 0x81C, 0xEE240203, + 0x81C, 0xED260203, + 0x81C, 0xEC280203, + 0x81C, 0xEB2A0203, + 0x81C, 0xEA2C0203, + 0x81C, 0xE92E0203, + 0x81C, 0xE8300203, + 0x81C, 0xE7320203, + 0x81C, 0xE6340203, + 0x81C, 0xE5360203, + 0x81C, 0xE4380203, + 0x81C, 0xE33A0203, + 0x81C, 0xE23C0203, + 0x81C, 0xE13E0203, + 0x81C, 0xA4400203, + 0x81C, 0xA3420203, + 0x81C, 0xA2440203, + 0x81C, 0xA1460203, + 0x81C, 0x85480203, + 0x81C, 0x844A0203, + 0x81C, 0x834C0203, + 0x81C, 0x824E0203, + 0x81C, 0x81500203, + 0x81C, 0x64520203, + 0x81C, 0x63540203, + 0x81C, 0x62560203, + 0x81C, 0x61580203, + 0x81C, 0x445A0203, + 0x81C, 0x435C0203, + 0x81C, 0x425E0203, + 0x81C, 0x25600203, + 0x81C, 0x24620203, + 0x81C, 0x06640203, + 0x81C, 0x05660203, + 0x81C, 0x04680203, + 0x81C, 0x036A0203, + 0x81C, 0x026C0203, + 0x81C, 0x016E0203, + 0x81C, 0x01700203, + 0x81C, 0x01720203, + 0x81C, 0x01740203, + 0x81C, 0x01760203, + 0x81C, 0x01780203, + 0x81C, 0x017A0203, + 0x81C, 0x017C0203, + 0x81C, 0x017E0203, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF8000303, + 0x81C, 0xF7020303, + 0x81C, 0xF6040303, + 0x81C, 0xF5060303, + 0x81C, 0xF4080303, + 0x81C, 0xF30A0303, + 0x81C, 0xF20C0303, + 0x81C, 0xF10E0303, + 0x81C, 0xF0100303, + 0x81C, 0xEF120303, + 0x81C, 0xEE140303, + 0x81C, 0xED160303, + 0x81C, 0xEC180303, + 0x81C, 0xEB1A0303, + 0x81C, 0xEA1C0303, + 0x81C, 0xE91E0303, + 0x81C, 0xE8200303, + 0x81C, 0xE7220303, + 0x81C, 0xE6240303, + 0x81C, 0xE5260303, + 0x81C, 0xE4280303, + 0x81C, 0xE32A0303, + 0x81C, 0xE22C0303, + 0x81C, 0xE12E0303, + 0x81C, 0xA6300303, + 0x81C, 0xA5320303, + 0x81C, 0xA4340303, + 0x81C, 0xA3360303, + 0x81C, 0xA2380303, + 0x81C, 0xA13A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x454C0303, + 0x81C, 0x444E0303, + 0x81C, 0x43500303, + 0x81C, 0x42520303, + 0x81C, 0x41540303, + 0x81C, 0x24560303, + 0x81C, 0x23580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xE91C0303, + 0x81C, 0xE81E0303, + 0x81C, 0xE7200303, + 0x81C, 0xE6220303, + 0x81C, 0xE5240303, + 0x81C, 0xE4260303, + 0x81C, 0xE3280303, + 0x81C, 0xC32A0303, + 0x81C, 0xC22C0303, + 0x81C, 0xC12E0303, + 0x81C, 0xA5300303, + 0x81C, 0xA4320303, + 0x81C, 0xA3340303, + 0x81C, 0xA2360303, + 0x81C, 0xA1380303, + 0x81C, 0x853A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x454C0303, + 0x81C, 0x444E0303, + 0x81C, 0x43500303, + 0x81C, 0x25520303, + 0x81C, 0x24540303, + 0x81C, 0x23560303, + 0x81C, 0x06580303, + 0x81C, 0x055A0303, + 0x81C, 0x045C0303, + 0x81C, 0x035E0303, + 0x81C, 0x02600303, + 0x81C, 0x01620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000303, + 0x81C, 0xF8020303, + 0x81C, 0xF7040303, + 0x81C, 0xF6060303, + 0x81C, 0xF5080303, + 0x81C, 0xF40A0303, + 0x81C, 0xF30C0303, + 0x81C, 0xF20E0303, + 0x81C, 0xF1100303, + 0x81C, 0xF0120303, + 0x81C, 0xEF140303, + 0x81C, 0xEE160303, + 0x81C, 0xED180303, + 0x81C, 0xEC1A0303, + 0x81C, 0xEB1C0303, + 0x81C, 0xEA1E0303, + 0x81C, 0xE9200303, + 0x81C, 0xE8220303, + 0x81C, 0xE7240303, + 0x81C, 0xE6260303, + 0x81C, 0xE5280303, + 0x81C, 0xE42A0303, + 0x81C, 0xE32C0303, + 0x81C, 0xE22E0303, + 0x81C, 0xE1300303, + 0x81C, 0xA4320303, + 0x81C, 0xA3340303, + 0x81C, 0xA2360303, + 0x81C, 0xA1380303, + 0x81C, 0x853A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x444C0303, + 0x81C, 0x434E0303, + 0x81C, 0x42500303, + 0x81C, 0x25520303, + 0x81C, 0x24540303, + 0x81C, 0x23560303, + 0x81C, 0x07580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xE91C0303, + 0x81C, 0xE81E0303, + 0x81C, 0xE7200303, + 0x81C, 0xE6220303, + 0x81C, 0xE5240303, + 0x81C, 0xE4260303, + 0x81C, 0xE3280303, + 0x81C, 0xE22A0303, + 0x81C, 0xE12C0303, + 0x81C, 0xA72E0303, + 0x81C, 0xA6300303, + 0x81C, 0xA5320303, + 0x81C, 0xA4340303, + 0x81C, 0xA3360303, + 0x81C, 0xA2380303, + 0x81C, 0xA13A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x454C0303, + 0x81C, 0x444E0303, + 0x81C, 0x43500303, + 0x81C, 0x42520303, + 0x81C, 0x41540303, + 0x81C, 0x24560303, + 0x81C, 0x23580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFB000303, + 0x81C, 0xFA020303, + 0x81C, 0xF9040303, + 0x81C, 0xF8060303, + 0x81C, 0xF7080303, + 0x81C, 0xF60A0303, + 0x81C, 0xF50C0303, + 0x81C, 0xF40E0303, + 0x81C, 0xF3100303, + 0x81C, 0xF2120303, + 0x81C, 0xF1140303, + 0x81C, 0xF0160303, + 0x81C, 0xEF180303, + 0x81C, 0xEE1A0303, + 0x81C, 0xED1C0303, + 0x81C, 0xEC1E0303, + 0x81C, 0xEB200303, + 0x81C, 0xEA220303, + 0x81C, 0xE9240303, + 0x81C, 0xE8260303, + 0x81C, 0xE7280303, + 0x81C, 0xE62A0303, + 0x81C, 0xE52C0303, + 0x81C, 0xE42E0303, + 0x81C, 0xE3300303, + 0x81C, 0xE2320303, + 0x81C, 0xE1340303, + 0x81C, 0xC2360303, + 0x81C, 0xC1380303, + 0x81C, 0xA33A0303, + 0x81C, 0xA23C0303, + 0x81C, 0x853E0303, + 0x81C, 0x84400303, + 0x81C, 0x83420303, + 0x81C, 0x66440303, + 0x81C, 0x65460303, + 0x81C, 0x64480303, + 0x81C, 0x634A0303, + 0x81C, 0x624C0303, + 0x81C, 0x614E0303, + 0x81C, 0x45500303, + 0x81C, 0x44520303, + 0x81C, 0x43540303, + 0x81C, 0x42560303, + 0x81C, 0x25580303, + 0x81C, 0x245A0303, + 0x81C, 0x235C0303, + 0x81C, 0x065E0303, + 0x81C, 0x05600303, + 0x81C, 0x04620303, + 0x81C, 0x03640303, + 0x81C, 0x02660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF9000303, + 0x81C, 0xF8020303, + 0x81C, 0xF7040303, + 0x81C, 0xF6060303, + 0x81C, 0xF5080303, + 0x81C, 0xF40A0303, + 0x81C, 0xF30C0303, + 0x81C, 0xF20E0303, + 0x81C, 0xF1100303, + 0x81C, 0xF0120303, + 0x81C, 0xEF140303, + 0x81C, 0xEE160303, + 0x81C, 0xED180303, + 0x81C, 0xEC1A0303, + 0x81C, 0xEB1C0303, + 0x81C, 0xEA1E0303, + 0x81C, 0xE9200303, + 0x81C, 0xE8220303, + 0x81C, 0xE7240303, + 0x81C, 0xE6260303, + 0x81C, 0xE5280303, + 0x81C, 0xE42A0303, + 0x81C, 0xE32C0303, + 0x81C, 0xE22E0303, + 0x81C, 0xE1300303, + 0x81C, 0xA6320303, + 0x81C, 0xA5340303, + 0x81C, 0xA4360303, + 0x81C, 0xA3380303, + 0x81C, 0xA23A0303, + 0x81C, 0xA13C0303, + 0x81C, 0x853E0303, + 0x81C, 0x84400303, + 0x81C, 0x83420303, + 0x81C, 0x82440303, + 0x81C, 0x81460303, + 0x81C, 0x64480303, + 0x81C, 0x634A0303, + 0x81C, 0x624C0303, + 0x81C, 0x614E0303, + 0x81C, 0x44500303, + 0x81C, 0x43520303, + 0x81C, 0x42540303, + 0x81C, 0x41560303, + 0x81C, 0x25580303, + 0x81C, 0x245A0303, + 0x81C, 0x235C0303, + 0x81C, 0x055E0303, + 0x81C, 0x04600303, + 0x81C, 0x03620303, + 0x81C, 0x02640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000303, + 0x81C, 0xFD020303, + 0x81C, 0xFC040303, + 0x81C, 0xFB060303, + 0x81C, 0xFA080303, + 0x81C, 0xF90A0303, + 0x81C, 0xF80C0303, + 0x81C, 0xF70E0303, + 0x81C, 0xF6100303, + 0x81C, 0xF5120303, + 0x81C, 0xF4140303, + 0x81C, 0xF3160303, + 0x81C, 0xF2180303, + 0x81C, 0xF11A0303, + 0x81C, 0xF01C0303, + 0x81C, 0xEF1E0303, + 0x81C, 0xEE200303, + 0x81C, 0xED220303, + 0x81C, 0xEC240303, + 0x81C, 0xEB260303, + 0x81C, 0xEA280303, + 0x81C, 0xE92A0303, + 0x81C, 0xE82C0303, + 0x81C, 0xE72E0303, + 0x81C, 0xE6300303, + 0x81C, 0xE5320303, + 0x81C, 0xE4340303, + 0x81C, 0xE3360303, + 0x81C, 0xC3380303, + 0x81C, 0xC23A0303, + 0x81C, 0xC13C0303, + 0x81C, 0xA43E0303, + 0x81C, 0xA3400303, + 0x81C, 0xA2420303, + 0x81C, 0xA1440303, + 0x81C, 0x85460303, + 0x81C, 0x84480303, + 0x81C, 0x834A0303, + 0x81C, 0x824C0303, + 0x81C, 0x814E0303, + 0x81C, 0x64500303, + 0x81C, 0x63520303, + 0x81C, 0x62540303, + 0x81C, 0x61560303, + 0x81C, 0x44580303, + 0x81C, 0x435A0303, + 0x81C, 0x425C0303, + 0x81C, 0x265E0303, + 0x81C, 0x25600303, + 0x81C, 0x24620303, + 0x81C, 0x06640303, + 0x81C, 0x05660303, + 0x81C, 0x04680303, + 0x81C, 0x036A0303, + 0x81C, 0x026C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xE91C0303, + 0x81C, 0xE81E0303, + 0x81C, 0xE7200303, + 0x81C, 0xE6220303, + 0x81C, 0xE5240303, + 0x81C, 0xE4260303, + 0x81C, 0xE3280303, + 0x81C, 0xE22A0303, + 0x81C, 0xA62C0303, + 0x81C, 0xA52E0303, + 0x81C, 0xA4300303, + 0x81C, 0xA3320303, + 0x81C, 0xA2340303, + 0x81C, 0x87360303, + 0x81C, 0x86380303, + 0x81C, 0x853A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x66400303, + 0x81C, 0x65420303, + 0x81C, 0x64440303, + 0x81C, 0x45460303, + 0x81C, 0x44480303, + 0x81C, 0x434A0303, + 0x81C, 0x274C0303, + 0x81C, 0x264E0303, + 0x81C, 0x25500303, + 0x81C, 0x24520303, + 0x81C, 0x23540303, + 0x81C, 0x08560303, + 0x81C, 0x07580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xFE000303, + 0x81C, 0xFD020303, + 0x81C, 0xFC040303, + 0x81C, 0xFB060303, + 0x81C, 0xFA080303, + 0x81C, 0xF90A0303, + 0x81C, 0xF80C0303, + 0x81C, 0xF70E0303, + 0x81C, 0xF6100303, + 0x81C, 0xF5120303, + 0x81C, 0xF4140303, + 0x81C, 0xF3160303, + 0x81C, 0xF2180303, + 0x81C, 0xF11A0303, + 0x81C, 0xF01C0303, + 0x81C, 0xEF1E0303, + 0x81C, 0xEE200303, + 0x81C, 0xED220303, + 0x81C, 0xEC240303, + 0x81C, 0xEB260303, + 0x81C, 0xEA280303, + 0x81C, 0xE92A0303, + 0x81C, 0xE82C0303, + 0x81C, 0xE72E0303, + 0x81C, 0xE6300303, + 0x81C, 0xE5320303, + 0x81C, 0xE4340303, + 0x81C, 0xE3360303, + 0x81C, 0xC3380303, + 0x81C, 0xC23A0303, + 0x81C, 0xC13C0303, + 0x81C, 0xA43E0303, + 0x81C, 0xA3400303, + 0x81C, 0xA2420303, + 0x81C, 0xA1440303, + 0x81C, 0x85460303, + 0x81C, 0x84480303, + 0x81C, 0x834A0303, + 0x81C, 0x824C0303, + 0x81C, 0x814E0303, + 0x81C, 0x64500303, + 0x81C, 0x63520303, + 0x81C, 0x62540303, + 0x81C, 0x61560303, + 0x81C, 0x44580303, + 0x81C, 0x435A0303, + 0x81C, 0x425C0303, + 0x81C, 0x265E0303, + 0x81C, 0x25600303, + 0x81C, 0x24620303, + 0x81C, 0x06640303, + 0x81C, 0x05660303, + 0x81C, 0x04680303, + 0x81C, 0x036A0303, + 0x81C, 0x026C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xE91C0303, + 0x81C, 0xE81E0303, + 0x81C, 0xE7200303, + 0x81C, 0xE6220303, + 0x81C, 0xE5240303, + 0x81C, 0xE4260303, + 0x81C, 0xE3280303, + 0x81C, 0xE22A0303, + 0x81C, 0xE12C0303, + 0x81C, 0xA72E0303, + 0x81C, 0xA6300303, + 0x81C, 0xA5320303, + 0x81C, 0xA4340303, + 0x81C, 0xA3360303, + 0x81C, 0xA2380303, + 0x81C, 0xA13A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x454C0303, + 0x81C, 0x444E0303, + 0x81C, 0x43500303, + 0x81C, 0x42520303, + 0x81C, 0x41540303, + 0x81C, 0x24560303, + 0x81C, 0x23580303, + 0x81C, 0x065A0303, + 0x81C, 0x055C0303, + 0x81C, 0x045E0303, + 0x81C, 0x03600303, + 0x81C, 0x02620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x81C, 0xF7000303, + 0x81C, 0xF6020303, + 0x81C, 0xF5040303, + 0x81C, 0xF4060303, + 0x81C, 0xF3080303, + 0x81C, 0xF20A0303, + 0x81C, 0xF10C0303, + 0x81C, 0xF00E0303, + 0x81C, 0xEF100303, + 0x81C, 0xEE120303, + 0x81C, 0xED140303, + 0x81C, 0xEC160303, + 0x81C, 0xEB180303, + 0x81C, 0xEA1A0303, + 0x81C, 0xAF1C0303, + 0x81C, 0xAE1E0303, + 0x81C, 0xAD200303, + 0x81C, 0xAC220303, + 0x81C, 0xAB240303, + 0x81C, 0xAA260303, + 0x81C, 0xC5280303, + 0x81C, 0xC42A0303, + 0x81C, 0xC32C0303, + 0x81C, 0xC22E0303, + 0x81C, 0xA5300303, + 0x81C, 0xA4320303, + 0x81C, 0xA3340303, + 0x81C, 0xA2360303, + 0x81C, 0xA1380303, + 0x81C, 0x853A0303, + 0x81C, 0x843C0303, + 0x81C, 0x833E0303, + 0x81C, 0x82400303, + 0x81C, 0x81420303, + 0x81C, 0x64440303, + 0x81C, 0x63460303, + 0x81C, 0x62480303, + 0x81C, 0x614A0303, + 0x81C, 0x444C0303, + 0x81C, 0x434E0303, + 0x81C, 0x42500303, + 0x81C, 0x41520303, + 0x81C, 0x25540303, + 0x81C, 0x24560303, + 0x81C, 0x06580303, + 0x81C, 0x055A0303, + 0x81C, 0x045C0303, + 0x81C, 0x035E0303, + 0x81C, 0x02600303, + 0x81C, 0x01620303, + 0x81C, 0x01640303, + 0x81C, 0x01660303, + 0x81C, 0x01680303, + 0x81C, 0x016A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0xA0000000, 0x00000000, + 0x81C, 0xFD000303, + 0x81C, 0xFC020303, + 0x81C, 0xFB040303, + 0x81C, 0xFA060303, + 0x81C, 0xF9080303, + 0x81C, 0xF80A0303, + 0x81C, 0xF70C0303, + 0x81C, 0xF60E0303, + 0x81C, 0xF5100303, + 0x81C, 0xF4120303, + 0x81C, 0xF3140303, + 0x81C, 0xF2160303, + 0x81C, 0xF1180303, + 0x81C, 0xF01A0303, + 0x81C, 0xEF1C0303, + 0x81C, 0xEE1E0303, + 0x81C, 0xED200303, + 0x81C, 0xEC220303, + 0x81C, 0xEB240303, + 0x81C, 0xEA260303, + 0x81C, 0xE9280303, + 0x81C, 0xE82A0303, + 0x81C, 0xE72C0303, + 0x81C, 0xE62E0303, + 0x81C, 0xE5300303, + 0x81C, 0xE4320303, + 0x81C, 0xE3340303, + 0x81C, 0xE2360303, + 0x81C, 0xE1380303, + 0x81C, 0xA53A0303, + 0x81C, 0xA43C0303, + 0x81C, 0xA33E0303, + 0x81C, 0xA2400303, + 0x81C, 0xA1420303, + 0x81C, 0x87440303, + 0x81C, 0x86460303, + 0x81C, 0x85480303, + 0x81C, 0x844A0303, + 0x81C, 0x834C0303, + 0x81C, 0x824E0303, + 0x81C, 0x81500303, + 0x81C, 0x64520303, + 0x81C, 0x63540303, + 0x81C, 0x62560303, + 0x81C, 0x61580303, + 0x81C, 0x435A0303, + 0x81C, 0x425C0303, + 0x81C, 0x415E0303, + 0x81C, 0x07600303, + 0x81C, 0x06620303, + 0x81C, 0x05640303, + 0x81C, 0x04660303, + 0x81C, 0x03680303, + 0x81C, 0x026A0303, + 0x81C, 0x016C0303, + 0x81C, 0x016E0303, + 0x81C, 0x01700303, + 0x81C, 0x01720303, + 0x81C, 0x01740303, + 0x81C, 0x01760303, + 0x81C, 0x01780303, + 0x81C, 0x017A0303, + 0x81C, 0x017C0303, + 0x81C, 0x017E0303, + 0xB0000000, 0x00000000, + 0xC50, 0x00000022, + 0xC50, 0x00000020, + 0xE50, 0x00000022, + 0xE50, 0x00000020, + 0x1850, 0x00000022, + 0x1850, 0x00000020, + 0x1A50, 0x00000022, + 0x1A50, 0x00000020, +}; + +RTW_DECL_TABLE_PHY_COND(rtw8814a_agc, rtw_phy_cfg_agc); + +static const u32 rtw8814a_bb[] = { + 0x800, 0x9020D010, + 0x804, 0x08011280, + 0x808, 0x0E0282FF, + 0x80C, 0x1000002F, + 0x80000003, 0x00000000, 0x40000000, 0x00000000, + 0x810, 0x33303265, + 0xA0000000, 0x00000000, + 0x810, 0x33303265, + 0xB0000000, 0x00000000, + 0x814, 0x020C3D10, + 0x818, 0x04A10385, + 0x820, 0x00000000, + 0x824, 0x00033E40, + 0x828, 0x00000000, + 0x82C, 0x73985170, + 0x830, 0x79A0EA08, + 0x834, 0x042E708A, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x838, 0x86667640, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x838, 0x86667641, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x838, 0x86667641, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x838, 0x86667641, + 0xA0000000, 0x00000000, + 0x838, 0x86667640, + 0xB0000000, 0x00000000, + 0x83C, 0x9798B9B9, + 0x840, 0x17578F60, + 0x844, 0x4BBDFCDE, + 0x848, 0x5CD07F8B, + 0x84C, 0x6CFBF7B5, + 0x850, 0x28834706, + 0x854, 0x0001520C, + 0x858, 0x4060C000, + 0x85C, 0x74210368, + 0x860, 0x6929C321, + 0x864, 0x79727432, + 0x868, 0x8CA7A314, + 0x86C, 0x438C2878, + 0x870, 0x44444444, + 0x874, 0x21612C2E, + 0x878, 0x00003152, + 0x87C, 0x000FC000, + 0x8A0, 0x00000013, + 0x8A4, 0x7F7F7F7F, + 0x8A8, 0xA202033E, + 0x8AC, 0xF40F550A, + 0x8B0, 0x00000600, + 0x8B4, 0x000FC080, + 0x8B8, 0xEC0057FF, + 0x8BC, 0x8CA520C3, + 0x8C0, 0x3FF00020, + 0x8C4, 0x44C00000, + 0x80000009, 0x00000000, 0x40000000, 0x00000000, + 0x8C8, 0x80025969, + 0xA0000000, 0x00000000, + 0x8C8, 0x80025167, + 0xB0000000, 0x00000000, + 0x8CC, 0x08250492, + 0x8D0, 0x0000B800, + 0x8D4, 0x940008A0, + 0x8D8, 0x290B5612, + 0x8DC, 0x00000000, + 0x8E0, 0x32316407, + 0x8E4, 0x4A092925, + 0x8E8, 0xFFFFC42C, + 0x8EC, 0x99999999, + 0x8F0, 0x00009999, + 0x8F4, 0x00F80FA1, + 0x8F8, 0x400082C0, + 0x8FC, 0x00000000, + 0x900, 0x00400700, + 0x90C, 0x09004000, + 0x910, 0x0000FC00, + 0x914, 0xD6400404, + 0x918, 0x1C1028C0, + 0x91C, 0x64B11A1C, + 0x920, 0xE0767233, + 0x924, 0x055AA500, + 0x928, 0x4AB0E4E4, + 0x92C, 0xFFFE0000, + 0x930, 0xFFFFFFFE, + 0x934, 0x001FFFFF, + 0x938, 0x00008400, + 0x93C, 0x932C0642, + 0x940, 0x093E9360, + 0x944, 0x08000000, + 0x948, 0x04000000, + 0x950, 0x02010080, + 0x954, 0x86510080, + 0x960, 0x00000000, + 0x964, 0x00000000, + 0x968, 0x00000000, + 0x96C, 0x00000000, + 0x970, 0x801FFFFF, + 0x978, 0x00000000, + 0x97C, 0x00000000, + 0x980, 0x00000000, + 0x984, 0x00000000, + 0x988, 0x00000000, + 0x98C, 0x03440000, + 0x990, 0x27100000, + 0x994, 0xFFFF0100, + 0x998, 0xFFFFFF5C, + 0x99C, 0xFFFFFFFF, + 0x9A0, 0x000000FF, + 0x9A4, 0x00080080, + 0x9A8, 0x0C2F0000, + 0x9AC, 0x00560000, + 0x9B0, 0x81081008, + 0x9B4, 0x00000000, + 0x9B8, 0x01081008, + 0x9BC, 0x01081008, + 0x9D0, 0x00000000, + 0x9D4, 0x00000000, + 0x9D8, 0x00000000, + 0x9DC, 0x00000000, + 0x9E4, 0x00000002, + 0x9E8, 0x000022D5, + 0x9FC, 0xEFFFF7FF, + 0xB00, 0xE3100000, + 0xB04, 0x0000B000, + 0xB0C, 0x31EAA006, + 0xB5C, 0x41CFFFFF, + 0xC00, 0x00000007, + 0xC04, 0x00042020, + 0xC08, 0x80410231, + 0xC0C, 0x00000000, + 0xC10, 0x00000100, + 0xC14, 0x01000000, + 0xC1C, 0x40000053, + 0xC50, 0x00000020, + 0xC54, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0xC58, 0x3C0A0C14, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0xC58, 0x3C0A0C14, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0xC58, 0x3C0A0C14, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xC58, 0x3C0A0C14, + 0xA0000000, 0x00000000, + 0xC58, 0x3C020C14, + 0xB0000000, 0x00000000, + 0xC5C, 0x0D000058, + 0xC60, 0x1B800000, + 0xC60, 0x0B800001, + 0xC60, 0x05800002, + 0xC60, 0x07800003, + 0xC60, 0x1A800004, + 0xC60, 0x0B800005, + 0xC60, 0x05800006, + 0xC60, 0x0E800007, + 0xC60, 0x1A800008, + 0xC60, 0x0B800009, + 0xC60, 0x1580000A, + 0xC60, 0x0880000B, + 0xC60, 0x1A80000C, + 0xC60, 0x0B80000D, + 0xC60, 0x0580000E, + 0xC60, 0x0E80000F, + 0xC60, 0x1A800010, + 0xC60, 0x0B800011, + 0xC60, 0x15800012, + 0xC60, 0x08800013, + 0xC60, 0x1A800014, + 0xC60, 0x0B800015, + 0xC60, 0x05800016, + 0xC60, 0x07800017, + 0xC60, 0x1A800018, + 0xC60, 0x0B800019, + 0xC60, 0x1580001A, + 0xC60, 0x0880001B, + 0xC60, 0x1B80001C, + 0xC60, 0x0B80001D, + 0xC60, 0x0580001E, + 0xC60, 0x0780001F, + 0xC60, 0x1B800020, + 0xC60, 0x0B800021, + 0xC60, 0x05800022, + 0xC60, 0x07800023, + 0xC60, 0x1B800024, + 0xC60, 0x0B800025, + 0xC60, 0x05800026, + 0xC60, 0x07800027, + 0xC60, 0x1B800028, + 0xC60, 0x0B800029, + 0xC60, 0x0580002A, + 0xC60, 0x0780002B, + 0xC60, 0x1B800030, + 0xC60, 0x0B800031, + 0xC60, 0x05800032, + 0xC60, 0x00800033, + 0xC60, 0x1B800034, + 0xC60, 0x0B800035, + 0xC60, 0x05800036, + 0xC60, 0x00800037, + 0xC60, 0x1B800038, + 0xC60, 0x0B800039, + 0xC60, 0x0580003A, + 0xC60, 0x0E80803B, + 0xC94, 0x01000401, + 0xC98, 0x00188000, + 0xCA0, 0x00002929, + 0xCA4, 0x08040201, + 0xCA8, 0x80402010, + 0xCAC, 0x77777000, + 0xCB0, 0x54775477, + 0xCB4, 0x54775477, + 0xCB8, 0x00500000, + 0xCBC, 0x77700000, + 0xCC0, 0x00000010, + 0xCC8, 0x00000010, + 0xE00, 0x00000007, + 0xE04, 0x00042020, + 0xE08, 0x80410231, + 0xE0C, 0x00000000, + 0xE10, 0x00000100, + 0xE14, 0x01000000, + 0xE1C, 0x40000053, + 0xE50, 0x00000020, + 0xE54, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0xE58, 0x3C0A0C14, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0xE58, 0x3C0A0C14, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0xE58, 0x3C0A0C14, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xE58, 0x3C0A0C14, + 0xA0000000, 0x00000000, + 0xE58, 0x3C020C14, + 0xB0000000, 0x00000000, + 0xE5C, 0x0D000058, + 0xE60, 0x1B800000, + 0xE60, 0x0B800001, + 0xE60, 0x05800002, + 0xE60, 0x07800003, + 0xE60, 0x1A800004, + 0xE60, 0x0B800005, + 0xE60, 0x05800006, + 0xE60, 0x0E800007, + 0xE60, 0x1A800008, + 0xE60, 0x0B800009, + 0xE60, 0x1580000A, + 0xE60, 0x0880000B, + 0xE60, 0x1A80000C, + 0xE60, 0x0B80000D, + 0xE60, 0x0580000E, + 0xE60, 0x0E80000F, + 0xE60, 0x1A800010, + 0xE60, 0x0B800011, + 0xE60, 0x15800012, + 0xE60, 0x08800013, + 0xE60, 0x1A800014, + 0xE60, 0x0B800015, + 0xE60, 0x05800016, + 0xE60, 0x07800017, + 0xE60, 0x1A800018, + 0xE60, 0x0B800019, + 0xE60, 0x1580001A, + 0xE60, 0x0880001B, + 0xE60, 0x1B80001C, + 0xE60, 0x0B80001D, + 0xE60, 0x0580001E, + 0xE60, 0x0780001F, + 0xE60, 0x1B800020, + 0xE60, 0x0B800021, + 0xE60, 0x05800022, + 0xE60, 0x07800023, + 0xE60, 0x1B800024, + 0xE60, 0x0B800025, + 0xE60, 0x05800026, + 0xE60, 0x07800027, + 0xE60, 0x1B800028, + 0xE60, 0x0B800029, + 0xE60, 0x0580002A, + 0xE60, 0x0780002B, + 0xE60, 0x1B800030, + 0xE60, 0x0B800031, + 0xE60, 0x05800032, + 0xE60, 0x00800033, + 0xE60, 0x1B800034, + 0xE60, 0x0B800035, + 0xE60, 0x05800036, + 0xE60, 0x00800037, + 0xE60, 0x1B800038, + 0xE60, 0x0B800039, + 0xE60, 0x0580003A, + 0xE60, 0x0E80803B, + 0xE94, 0x01000401, + 0xE98, 0x00188000, + 0xEA0, 0x00002929, + 0xEA4, 0x08040201, + 0xEA8, 0x80402010, + 0xEAC, 0x77777000, + 0xEB0, 0x54775477, + 0xEB4, 0x54775477, + 0xEB8, 0x00500000, + 0xEBC, 0x77700000, + 0x1800, 0x00000007, + 0x1804, 0x00042020, + 0x1808, 0x80410231, + 0x180C, 0x00000000, + 0x1810, 0x00000100, + 0x1814, 0x01000000, + 0x181C, 0x40000053, + 0x1850, 0x00000020, + 0x1854, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x1858, 0x3C0A0C14, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1858, 0x3C0A0C14, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1858, 0x3C0A0C14, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1858, 0x3C0A0C14, + 0xA0000000, 0x00000000, + 0x1858, 0x3C020C14, + 0xB0000000, 0x00000000, + 0x185C, 0x0D000058, + 0x1860, 0x1B800000, + 0x1860, 0x0B800001, + 0x1860, 0x05800002, + 0x1860, 0x07800003, + 0x1860, 0x1A800004, + 0x1860, 0x0B800005, + 0x1860, 0x05800006, + 0x1860, 0x0E800007, + 0x1860, 0x1A800008, + 0x1860, 0x0B800009, + 0x1860, 0x1580000A, + 0x1860, 0x0880000B, + 0x1860, 0x1A80000C, + 0x1860, 0x0B80000D, + 0x1860, 0x0580000E, + 0x1860, 0x0E80000F, + 0x1860, 0x1A800010, + 0x1860, 0x0B800011, + 0x1860, 0x15800012, + 0x1860, 0x08800013, + 0x1860, 0x1A800014, + 0x1860, 0x0B800015, + 0x1860, 0x05800016, + 0x1860, 0x07800017, + 0x1860, 0x1A800018, + 0x1860, 0x0B800019, + 0x1860, 0x1580001A, + 0x1860, 0x0880001B, + 0x1860, 0x1B80001C, + 0x1860, 0x0B80001D, + 0x1860, 0x0580001E, + 0x1860, 0x0780001F, + 0x1860, 0x1B800020, + 0x1860, 0x0B800021, + 0x1860, 0x05800022, + 0x1860, 0x07800023, + 0x1860, 0x1B800024, + 0x1860, 0x0B800025, + 0x1860, 0x05800026, + 0x1860, 0x07800027, + 0x1860, 0x1B800028, + 0x1860, 0x0B800029, + 0x1860, 0x0580002A, + 0x1860, 0x0780002B, + 0x1860, 0x1B800030, + 0x1860, 0x0B800031, + 0x1860, 0x05800032, + 0x1860, 0x00800033, + 0x1860, 0x1B800034, + 0x1860, 0x0B800035, + 0x1860, 0x05800036, + 0x1860, 0x00800037, + 0x1860, 0x1B800038, + 0x1860, 0x0B800039, + 0x1860, 0x0580003A, + 0x1860, 0x0E80803B, + 0x1894, 0x01000401, + 0x1898, 0x00188000, + 0x18A0, 0x00002929, + 0x18A4, 0x08040201, + 0x18A8, 0x80402010, + 0x18AC, 0x77777000, + 0x18B0, 0x54775477, + 0x18B4, 0x54775477, + 0x18B8, 0x00500000, + 0x18BC, 0x77700000, + 0x1A00, 0x00000007, + 0x1A04, 0x00042020, + 0x1A08, 0x80410231, + 0x1A0C, 0x00000000, + 0x1A10, 0x00000100, + 0x1A14, 0x01000000, + 0x1A1C, 0x40000053, + 0x1A50, 0x00000020, + 0x1A54, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x1A58, 0x3C0A0C14, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1A58, 0x3C0A0C14, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1A58, 0x3C0A0C14, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1A58, 0x3C0A0C14, + 0xA0000000, 0x00000000, + 0x1A58, 0x3C020C14, + 0xB0000000, 0x00000000, + 0x1A5C, 0x0D000058, + 0x1A60, 0x1B800000, + 0x1A60, 0x0B800001, + 0x1A60, 0x05800002, + 0x1A60, 0x07800003, + 0x1A60, 0x1A800004, + 0x1A60, 0x0B800005, + 0x1A60, 0x05800006, + 0x1A60, 0x0E800007, + 0x1A60, 0x1A800008, + 0x1A60, 0x0B800009, + 0x1A60, 0x1580000A, + 0x1A60, 0x0880000B, + 0x1A60, 0x1A80000C, + 0x1A60, 0x0B80000D, + 0x1A60, 0x0580000E, + 0x1A60, 0x0E80000F, + 0x1A60, 0x1A800010, + 0x1A60, 0x0B800011, + 0x1A60, 0x15800012, + 0x1A60, 0x08800013, + 0x1A60, 0x1A800014, + 0x1A60, 0x0B800015, + 0x1A60, 0x05800016, + 0x1A60, 0x07800017, + 0x1A60, 0x1A800018, + 0x1A60, 0x0B800019, + 0x1A60, 0x1580001A, + 0x1A60, 0x0880001B, + 0x1A60, 0x1B80001C, + 0x1A60, 0x0B80001D, + 0x1A60, 0x0580001E, + 0x1A60, 0x0780001F, + 0x1A60, 0x1B800020, + 0x1A60, 0x0B800021, + 0x1A60, 0x05800022, + 0x1A60, 0x07800023, + 0x1A60, 0x1B800024, + 0x1A60, 0x0B800025, + 0x1A60, 0x05800026, + 0x1A60, 0x07800027, + 0x1A60, 0x1B800028, + 0x1A60, 0x0B800029, + 0x1A60, 0x0580002A, + 0x1A60, 0x0780002B, + 0x1A60, 0x1B800030, + 0x1A60, 0x0B800031, + 0x1A60, 0x05800032, + 0x1A60, 0x00800033, + 0x1A60, 0x1B800034, + 0x1A60, 0x0B800035, + 0x1A60, 0x05800036, + 0x1A60, 0x00800037, + 0x1A60, 0x1B800038, + 0x1A60, 0x0B800039, + 0x1A60, 0x0580003A, + 0x1A60, 0x0E80803B, + 0x1A94, 0x01000401, + 0x1A98, 0x00188000, + 0x1AA0, 0x00002929, + 0x1AA4, 0x08040201, + 0x1AA8, 0x80402010, + 0x1AAC, 0x77777000, + 0x1AB0, 0x54775477, + 0x1AB4, 0x54775477, + 0x1AB8, 0x00500000, + 0x1ABC, 0x77700000, + 0x1904, 0x00030000, + 0x1914, 0x00030000, + 0x1984, 0x03000000, + 0x1988, 0x00000087, + 0x198C, 0x00000007, + 0x1990, 0xFFAA5500, + 0x1994, 0x00000077, + 0x1998, 0x12801000, + 0x1998, 0x12801000, + 0x1998, 0x12801001, + 0x1998, 0x12801002, + 0x1998, 0x12801003, + 0x1998, 0x12801004, + 0x1998, 0x12801005, + 0x1998, 0x12801006, + 0x1998, 0x12801007, + 0x1998, 0x12801008, + 0x1998, 0x12801009, + 0x1998, 0x1280100A, + 0x1998, 0x1280100B, + 0x1998, 0x1280100C, + 0x1998, 0x1280100D, + 0x1998, 0x1280100E, + 0x1998, 0x1280100F, + 0x1998, 0x12801010, + 0x1998, 0x12801011, + 0x1998, 0x12801012, + 0x1998, 0x12801013, + 0x1998, 0x12801014, + 0x1998, 0x12801015, + 0x1998, 0x12801016, + 0x1998, 0x12801017, + 0x1998, 0x12801018, + 0x1998, 0x12801019, + 0x1998, 0x1280101A, + 0x1998, 0x1280101B, + 0x1998, 0x1280101C, + 0x1998, 0x1280101D, + 0x1998, 0x1280101E, + 0x1998, 0x1280101F, + 0x1998, 0x12801020, + 0x1998, 0x12801021, + 0x1998, 0x12801022, + 0x1998, 0x12801023, + 0x1998, 0x1280102C, + 0x1998, 0x1280102D, + 0x1998, 0x1280102E, + 0x1998, 0x1280102F, + 0x1998, 0x12801030, + 0x1998, 0x12801031, + 0x1998, 0x12801032, + 0x1998, 0x12801033, + 0x1998, 0x12801034, + 0x1998, 0x12801035, + 0x1998, 0x12801036, + 0x1998, 0x12801037, + 0x1998, 0x12801038, + 0x1998, 0x12801039, + 0x1998, 0x1280103A, + 0x1998, 0x1280103B, + 0x1998, 0x1280103C, + 0x1998, 0x1280103D, + 0x1998, 0x1280103E, + 0x1998, 0x1280103F, + 0x1998, 0x12801040, + 0x1998, 0x12801041, + 0x1998, 0x12801042, + 0x1998, 0x12801043, + 0x1998, 0x12801044, + 0x1998, 0x12801045, + 0x1998, 0x12801046, + 0x1998, 0x12801047, + 0x1998, 0x12801048, + 0x1998, 0x12801049, + 0x1998, 0x12801100, + 0x1998, 0x12801101, + 0x1998, 0x12801102, + 0x1998, 0x12801103, + 0x1998, 0x12801104, + 0x1998, 0x12801105, + 0x1998, 0x12801106, + 0x1998, 0x12801107, + 0x1998, 0x12801108, + 0x1998, 0x12801109, + 0x1998, 0x1280110A, + 0x1998, 0x1280110B, + 0x1998, 0x1280110C, + 0x1998, 0x1280110D, + 0x1998, 0x1280110E, + 0x1998, 0x1280110F, + 0x1998, 0x12801110, + 0x1998, 0x12801111, + 0x1998, 0x12801112, + 0x1998, 0x12801113, + 0x1998, 0x12801114, + 0x1998, 0x12801115, + 0x1998, 0x12801116, + 0x1998, 0x12801117, + 0x1998, 0x12801118, + 0x1998, 0x12801119, + 0x1998, 0x1280111A, + 0x1998, 0x1280111B, + 0x1998, 0x1280111C, + 0x1998, 0x1280111D, + 0x1998, 0x1280111E, + 0x1998, 0x1280111F, + 0x1998, 0x12801120, + 0x1998, 0x12801121, + 0x1998, 0x12801122, + 0x1998, 0x12801123, + 0x1998, 0x1280112C, + 0x1998, 0x1280112D, + 0x1998, 0x1280112E, + 0x1998, 0x1280112F, + 0x1998, 0x12801130, + 0x1998, 0x12801131, + 0x1998, 0x12801132, + 0x1998, 0x12801133, + 0x1998, 0x12801134, + 0x1998, 0x12801135, + 0x1998, 0x12801136, + 0x1998, 0x12801137, + 0x1998, 0x12801138, + 0x1998, 0x12801139, + 0x1998, 0x1280113A, + 0x1998, 0x1280113B, + 0x1998, 0x1280113C, + 0x1998, 0x1280113D, + 0x1998, 0x1280113E, + 0x1998, 0x1280113F, + 0x1998, 0x12801140, + 0x1998, 0x12801141, + 0x1998, 0x12801142, + 0x1998, 0x12801143, + 0x1998, 0x12801144, + 0x1998, 0x12801145, + 0x1998, 0x12801146, + 0x1998, 0x12801147, + 0x1998, 0x12801148, + 0x1998, 0x12801149, + 0x1998, 0x12801200, + 0x1998, 0x12801201, + 0x1998, 0x12801202, + 0x1998, 0x12801203, + 0x1998, 0x12801204, + 0x1998, 0x12801205, + 0x1998, 0x12801206, + 0x1998, 0x12801207, + 0x1998, 0x12801208, + 0x1998, 0x12801209, + 0x1998, 0x1280120A, + 0x1998, 0x1280120B, + 0x1998, 0x1280120C, + 0x1998, 0x1280120D, + 0x1998, 0x1280120E, + 0x1998, 0x1280120F, + 0x1998, 0x12801210, + 0x1998, 0x12801211, + 0x1998, 0x12801212, + 0x1998, 0x12801213, + 0x1998, 0x12801214, + 0x1998, 0x12801215, + 0x1998, 0x12801216, + 0x1998, 0x12801217, + 0x1998, 0x12801218, + 0x1998, 0x12801219, + 0x1998, 0x1280121A, + 0x1998, 0x1280121B, + 0x1998, 0x1280121C, + 0x1998, 0x1280121D, + 0x1998, 0x1280121E, + 0x1998, 0x1280121F, + 0x1998, 0x12801220, + 0x1998, 0x12801221, + 0x1998, 0x12801222, + 0x1998, 0x12801223, + 0x1998, 0x1280122C, + 0x1998, 0x1280122D, + 0x1998, 0x1280122E, + 0x1998, 0x1280122F, + 0x1998, 0x12801230, + 0x1998, 0x12801231, + 0x1998, 0x12801232, + 0x1998, 0x12801233, + 0x1998, 0x12801234, + 0x1998, 0x12801235, + 0x1998, 0x12801236, + 0x1998, 0x12801237, + 0x1998, 0x12801238, + 0x1998, 0x12801239, + 0x1998, 0x1280123A, + 0x1998, 0x1280123B, + 0x1998, 0x1280123C, + 0x1998, 0x1280123D, + 0x1998, 0x1280123E, + 0x1998, 0x1280123F, + 0x1998, 0x12801240, + 0x1998, 0x12801241, + 0x1998, 0x12801242, + 0x1998, 0x12801243, + 0x1998, 0x12801244, + 0x1998, 0x12801245, + 0x1998, 0x12801246, + 0x1998, 0x12801247, + 0x1998, 0x12801248, + 0x1998, 0x12801249, + 0x1998, 0x12801300, + 0x1998, 0x12801301, + 0x1998, 0x12801302, + 0x1998, 0x12801303, + 0x1998, 0x12801304, + 0x1998, 0x12801305, + 0x1998, 0x12801306, + 0x1998, 0x12801307, + 0x1998, 0x12801308, + 0x1998, 0x12801309, + 0x1998, 0x1280130A, + 0x1998, 0x1280130B, + 0x1998, 0x1280130C, + 0x1998, 0x1280130D, + 0x1998, 0x1280130E, + 0x1998, 0x1280130F, + 0x1998, 0x12801310, + 0x1998, 0x12801311, + 0x1998, 0x12801312, + 0x1998, 0x12801313, + 0x1998, 0x12801314, + 0x1998, 0x12801315, + 0x1998, 0x12801316, + 0x1998, 0x12801317, + 0x1998, 0x12801318, + 0x1998, 0x12801319, + 0x1998, 0x1280131A, + 0x1998, 0x1280131B, + 0x1998, 0x1280131C, + 0x1998, 0x1280131D, + 0x1998, 0x1280131E, + 0x1998, 0x1280131F, + 0x1998, 0x12801320, + 0x1998, 0x12801321, + 0x1998, 0x12801322, + 0x1998, 0x12801323, + 0x1998, 0x1280132C, + 0x1998, 0x1280132D, + 0x1998, 0x1280132E, + 0x1998, 0x1280132F, + 0x1998, 0x12801330, + 0x1998, 0x12801331, + 0x1998, 0x12801332, + 0x1998, 0x12801333, + 0x1998, 0x12801334, + 0x1998, 0x12801335, + 0x1998, 0x12801336, + 0x1998, 0x12801337, + 0x1998, 0x12801338, + 0x1998, 0x12801339, + 0x1998, 0x1280133A, + 0x1998, 0x1280133B, + 0x1998, 0x1280133C, + 0x1998, 0x1280133D, + 0x1998, 0x1280133E, + 0x1998, 0x1280133F, + 0x1998, 0x12801340, + 0x1998, 0x12801341, + 0x1998, 0x12801342, + 0x1998, 0x12801343, + 0x1998, 0x12801344, + 0x1998, 0x12801345, + 0x1998, 0x12801346, + 0x1998, 0x12801347, + 0x1998, 0x12801348, + 0x1998, 0x12801349, + 0x19D4, 0x88888888, + 0x19D8, 0x00000888, + 0xB00, 0xE3100100, + 0xB00, 0xE7100100, + 0xC60, 0x15808002, + 0xC60, 0x01808003, + 0xE60, 0x15808002, + 0xE60, 0x01808003, + 0x1860, 0x15808002, + 0x1860, 0x01808003, + 0x1A60, 0x15808002, + 0x1A60, 0x01808003, + 0xB00, 0xE3100100, + 0xC5C, 0x0D080058, + 0xE5C, 0x0D080058, + 0x185C, 0x0D080058, + 0x1A5C, 0x0D080058, + 0xC5C, 0x0D000058, + 0xE5C, 0x0D000058, + 0x185C, 0x0D000058, + 0x1A5C, 0x0D000058, + 0xC60, 0x05808002, + 0xC60, 0x0E808003, + 0xE60, 0x05808002, + 0xE60, 0x0E808003, + 0x1860, 0x05808002, + 0x1860, 0x0E808003, + 0x1A60, 0x05808002, + 0x1A60, 0x0E808003, + 0xB00, 0xE7100100, + 0xB00, 0xE3100100, + 0xB00, 0xE3100000, + 0x1C38, 0x00000002, + 0xA00, 0x00D047C8, + 0xA04, 0x46FF800C, + 0xA08, 0x8C838300, + 0xA0C, 0x2E7E000F, + 0xA10, 0x9500BB78, + 0xA14, 0x11144028, + 0xA18, 0x00881117, + 0xA1C, 0x89140F00, + 0xA20, 0x1A1B0030, + 0xA24, 0x090E1317, + 0xA28, 0x00000204, + 0xA2C, 0x00900000, + 0xA70, 0x101FFF00, + 0xA74, 0x00000128, + 0xA78, 0x00000900, + 0xA7C, 0x225B0606, + 0xA80, 0x218075B2, + 0xA84, 0x9C1F8C00, + 0x1B04, 0xE24628D2, + 0x1B10, 0x88010D46, + 0x1B14, 0x00000000, + 0x1B18, 0x00292903, + 0x1B00, 0xF8000000, + 0x1B00, 0xF800D000, + 0x1B00, 0xF801F000, + 0x1B1C, 0xA2123DB2, + 0x1B20, 0x07040001, + 0x1B24, 0x07060807, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0xA0000000, 0x00000000, + 0x1B28, 0xC0060348, + 0xB0000000, 0x00000000, + 0x1B2C, 0x20000003, + 0x1B30, 0x20000000, + 0x1B38, 0x20000000, + 0x1B3C, 0x20000000, + 0x1BD4, 0x00000001, + 0x1B94, 0x80000000, + 0x1B34, 0x00000000, + 0x1B34, 0x00000002, + 0x1B34, 0x00000000, + 0x1B00, 0xF8000002, + 0x1B00, 0xF800D002, + 0x1B00, 0xF801F002, + 0x1B1C, 0xA2123DB2, + 0x1B20, 0x07040001, + 0x1B24, 0x07060807, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0xA0000000, 0x00000000, + 0x1B28, 0xC0060348, + 0xB0000000, 0x00000000, + 0x1B2C, 0x20000003, + 0x1B30, 0x20000000, + 0x1B38, 0x20000000, + 0x1B3C, 0x20000000, + 0x1BD4, 0x00000001, + 0x1B94, 0x80000000, + 0x1B34, 0x00000000, + 0x1B34, 0x00000002, + 0x1B34, 0x00000000, + 0x1B00, 0xF8000004, + 0x1B00, 0xF800D004, + 0x1B00, 0xF801F004, + 0x1B1C, 0xA2123DB2, + 0x1B20, 0x07040001, + 0x1B24, 0x07060807, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0xA0000000, 0x00000000, + 0x1B28, 0xC0060348, + 0xB0000000, 0x00000000, + 0x1B2C, 0x20000003, + 0x1B30, 0x20000000, + 0x1B38, 0x20000000, + 0x1B3C, 0x20000000, + 0x1BD4, 0x00000001, + 0x1B94, 0x80000000, + 0x1B34, 0x00000000, + 0x1B34, 0x00000002, + 0x1B34, 0x00000000, + 0x1B00, 0xF8000006, + 0x1B00, 0xF800D006, + 0x1B00, 0xF801F006, + 0x1B1C, 0xA2123DB2, + 0x1B20, 0x07040001, + 0x1B24, 0x07060807, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B28, 0xC0060324, + 0xA0000000, 0x00000000, + 0x1B28, 0xC0060348, + 0xB0000000, 0x00000000, + 0x1B2C, 0x20000003, + 0x1B30, 0x20000000, + 0x1B38, 0x20000000, + 0x1B3C, 0x20000000, + 0x1BD4, 0x00000001, + 0x1B94, 0x80000000, + 0x1B34, 0x00000000, + 0x1B34, 0x00000002, + 0x1B34, 0x00000000, + 0x1B00, 0xF8000000, + 0x1B80, 0x00000007, + 0x1B80, 0x09060005, + 0x1B80, 0x09060007, + 0x1B80, 0x0FFE0015, + 0x1B80, 0x0FFE0017, + 0x1B80, 0x00240025, + 0x1B80, 0x00240027, + 0x1B80, 0x00040035, + 0x1B80, 0x00040037, + 0x1B80, 0x05C00045, + 0x1B80, 0x05C00047, + 0x1B80, 0x00070055, + 0x1B80, 0x00070057, + 0x1B80, 0x64000065, + 0x1B80, 0x64000067, + 0x1B80, 0x00020075, + 0x1B80, 0x00020077, + 0x1B80, 0x00080085, + 0x1B80, 0x00080087, + 0x1B80, 0x80000095, + 0x1B80, 0x80000097, + 0x1B80, 0x090100A5, + 0x1B80, 0x090100A7, + 0x1B80, 0x0F0200B5, + 0x1B80, 0x0F0200B7, + 0x1B80, 0x002400C5, + 0x1B80, 0x002400C7, + 0x1B80, 0x000400D5, + 0x1B80, 0x000400D7, + 0x1B80, 0x05C000E5, + 0x1B80, 0x05C000E7, + 0x1B80, 0x000700F5, + 0x1B80, 0x000700F7, + 0x1B80, 0x64020105, + 0x1B80, 0x64020107, + 0x1B80, 0x00020115, + 0x1B80, 0x00020117, + 0x1B80, 0x00040125, + 0x1B80, 0x00040127, + 0x1B80, 0x4A000135, + 0x1B80, 0x4A000137, + 0x1B80, 0x4B040145, + 0x1B80, 0x4B040147, + 0x1B80, 0x85030155, + 0x1B80, 0x85030157, + 0x1B80, 0x40010165, + 0x1B80, 0x40010167, + 0x1B80, 0xE0290175, + 0x1B80, 0xE0290177, + 0x1B80, 0x00040185, + 0x1B80, 0x00040187, + 0x1B80, 0x4B050195, + 0x1B80, 0x4B050197, + 0x1B80, 0x860301A5, + 0x1B80, 0x860301A7, + 0x1B80, 0x400301B5, + 0x1B80, 0x400301B7, + 0x1B80, 0xE02901C5, + 0x1B80, 0xE02901C7, + 0x1B80, 0x000401D5, + 0x1B80, 0x000401D7, + 0x1B80, 0x4B0601E5, + 0x1B80, 0x4B0601E7, + 0x1B80, 0x870301F5, + 0x1B80, 0x870301F7, + 0x1B80, 0x40050205, + 0x1B80, 0x40050207, + 0x1B80, 0xE0290215, + 0x1B80, 0xE0290217, + 0x1B80, 0x00040225, + 0x1B80, 0x00040227, + 0x1B80, 0x4B070235, + 0x1B80, 0x4B070237, + 0x1B80, 0x88030245, + 0x1B80, 0x88030247, + 0x1B80, 0x40070255, + 0x1B80, 0x40070257, + 0x1B80, 0xE0290265, + 0x1B80, 0xE0290267, + 0x1B80, 0x4B000275, + 0x1B80, 0x4B000277, + 0x1B80, 0x30000285, + 0x1B80, 0x30000287, + 0x1B80, 0xFE100295, + 0x1B80, 0xFE100297, + 0x1B80, 0xFF1002A5, + 0x1B80, 0xFF1002A7, + 0x1B80, 0xE18602B5, + 0x1B80, 0xE18602B7, + 0x1B80, 0xF00A02C5, + 0x1B80, 0xF00A02C7, + 0x1B80, 0xF10A02D5, + 0x1B80, 0xF10A02D7, + 0x1B80, 0xF20A02E5, + 0x1B80, 0xF20A02E7, + 0x1B80, 0xF30802F5, + 0x1B80, 0xF30802F7, + 0x1B80, 0xF4070305, + 0x1B80, 0xF4070307, + 0x1B80, 0xF5060315, + 0x1B80, 0xF5060317, + 0x1B80, 0xF7060325, + 0x1B80, 0xF7060327, + 0x1B80, 0xF8050335, + 0x1B80, 0xF8050337, + 0x1B80, 0xF9040345, + 0x1B80, 0xF9040347, + 0x1B80, 0x00010355, + 0x1B80, 0x00010357, + 0x1B80, 0x303B0365, + 0x1B80, 0x303B0367, + 0x1B80, 0x30500375, + 0x1B80, 0x30500377, + 0x1B80, 0x305C0385, + 0x1B80, 0x305C0387, + 0x1B80, 0x31D50395, + 0x1B80, 0x31D50397, + 0x1B80, 0x31C503A5, + 0x1B80, 0x31C503A7, + 0x1B80, 0x4D0403B5, + 0x1B80, 0x4D0403B7, + 0x1B80, 0x2EF003C5, + 0x1B80, 0x2EF003C7, + 0x1B80, 0x000203D5, + 0x1B80, 0x000203D7, + 0x1B80, 0x208003E5, + 0x1B80, 0x208003E7, + 0x1B80, 0x000003F5, + 0x1B80, 0x000003F7, + 0x1B80, 0x4D000405, + 0x1B80, 0x4D000407, + 0x1B80, 0x55070415, + 0x1B80, 0x55070417, + 0x1B80, 0xE1230425, + 0x1B80, 0xE1230427, + 0x1B80, 0xE1230435, + 0x1B80, 0xE1230437, + 0x1B80, 0x4D040445, + 0x1B80, 0x4D040447, + 0x1B80, 0x20800455, + 0x1B80, 0x20800457, + 0x1B80, 0x84000465, + 0x1B80, 0x84000467, + 0x1B80, 0x4D000475, + 0x1B80, 0x4D000477, + 0x1B80, 0x550F0485, + 0x1B80, 0x550F0487, + 0x1B80, 0xE1230495, + 0x1B80, 0xE1230497, + 0x1B80, 0x4F0204A5, + 0x1B80, 0x4F0204A7, + 0x1B80, 0x4E0004B5, + 0x1B80, 0x4E0004B7, + 0x1B80, 0x530204C5, + 0x1B80, 0x530204C7, + 0x1B80, 0x520104D5, + 0x1B80, 0x520104D7, + 0x1B80, 0xE12704E5, + 0x1B80, 0xE12704E7, + 0x1B80, 0x000104F5, + 0x1B80, 0x000104F7, + 0x1B80, 0x5C720505, + 0x1B80, 0x5C720507, + 0x1B80, 0xE1320515, + 0x1B80, 0xE1320517, + 0x1B80, 0x54E50525, + 0x1B80, 0x54E50527, + 0x1B80, 0x54BF0535, + 0x1B80, 0x54BF0537, + 0x1B80, 0x54C50545, + 0x1B80, 0x54C50547, + 0x1B80, 0x54BE0555, + 0x1B80, 0x54BE0557, + 0x1B80, 0x54DF0565, + 0x1B80, 0x54DF0567, + 0x1B80, 0x0BA60575, + 0x1B80, 0x0BA60577, + 0x1B80, 0xF3130585, + 0x1B80, 0xF3130587, + 0x1B80, 0xF41E0595, + 0x1B80, 0xF41E0597, + 0x1B80, 0xF53C05A5, + 0x1B80, 0xF53C05A7, + 0x1B80, 0x000105B5, + 0x1B80, 0x000105B7, + 0x1B80, 0x620605C5, + 0x1B80, 0x620605C7, + 0x1B80, 0x600605D5, + 0x1B80, 0x600605D7, + 0x1B80, 0xE1A905E5, + 0x1B80, 0xE1A905E7, + 0x1B80, 0x0C0005F5, + 0x1B80, 0x0C0005F7, + 0x1B80, 0x5C720605, + 0x1B80, 0x5C720607, + 0x1B80, 0xE1320615, + 0x1B80, 0xE1320617, + 0x1B80, 0x5CF10625, + 0x1B80, 0x5CF10627, + 0x1B80, 0x0C010635, + 0x1B80, 0x0C010637, + 0x1B80, 0xF2020645, + 0x1B80, 0xF2020647, + 0x1B80, 0x30D60655, + 0x1B80, 0x30D60657, + 0x1B80, 0x0AC60665, + 0x1B80, 0x0AC60667, + 0x1B80, 0xE1B60675, + 0x1B80, 0xE1B60677, + 0x1B80, 0xE1580685, + 0x1B80, 0xE1580687, + 0x1B80, 0x54E50695, + 0x1B80, 0x54E50697, + 0x1B80, 0x000106A5, + 0x1B80, 0x000106A7, + 0x1B80, 0x560106B5, + 0x1B80, 0x560106B7, + 0x1B80, 0x5CE206C5, + 0x1B80, 0x5CE206C7, + 0x1B80, 0x0AE106D5, + 0x1B80, 0x0AE106D7, + 0x1B80, 0x630C06E5, + 0x1B80, 0x630C06E7, + 0x1B80, 0xE13F06F5, + 0x1B80, 0xE13F06F7, + 0x1B80, 0x00270705, + 0x1B80, 0x00270707, + 0x1B80, 0xE16C0715, + 0x1B80, 0xE16C0717, + 0x1B80, 0x00020725, + 0x1B80, 0x00020727, + 0x1B80, 0x002A0735, + 0x1B80, 0x002A0737, + 0x1B80, 0x07140745, + 0x1B80, 0x07140747, + 0x1B80, 0x00020755, + 0x1B80, 0x00020757, + 0x1B80, 0x30C30765, + 0x1B80, 0x30C30767, + 0x1B80, 0x56010775, + 0x1B80, 0x56010777, + 0x1B80, 0x5CE20785, + 0x1B80, 0x5CE20787, + 0x1B80, 0x0AE10795, + 0x1B80, 0x0AE10797, + 0x1B80, 0x631707A5, + 0x1B80, 0x631707A7, + 0x1B80, 0xE13F07B5, + 0x1B80, 0xE13F07B7, + 0x1B80, 0x002507C5, + 0x1B80, 0x002507C7, + 0x1B80, 0xE16C07D5, + 0x1B80, 0xE16C07D7, + 0x1B80, 0x000207E5, + 0x1B80, 0x000207E7, + 0x1B80, 0x630F07F5, + 0x1B80, 0x630F07F7, + 0x1B80, 0xE13F0805, + 0x1B80, 0xE13F0807, + 0x1B80, 0x63070815, + 0x1B80, 0x63070817, + 0x1B80, 0xE13F0825, + 0x1B80, 0xE13F0827, + 0x1B80, 0x07140835, + 0x1B80, 0x07140837, + 0x1B80, 0x56000845, + 0x1B80, 0x56000847, + 0x1B80, 0x5CF20855, + 0x1B80, 0x5CF20857, + 0x1B80, 0x0AF10865, + 0x1B80, 0x0AF10867, + 0x1B80, 0x07140875, + 0x1B80, 0x07140877, + 0x1B80, 0x07140885, + 0x1B80, 0x07140887, + 0x1B80, 0x630F0895, + 0x1B80, 0x630F0897, + 0x1B80, 0xE13F08A5, + 0x1B80, 0xE13F08A7, + 0x1B80, 0x631708B5, + 0x1B80, 0x631708B7, + 0x1B80, 0xE13F08C5, + 0x1B80, 0xE13F08C7, + 0x1B80, 0x002508D5, + 0x1B80, 0x002508D7, + 0x1B80, 0xE16C08E5, + 0x1B80, 0xE16C08E7, + 0x1B80, 0x000208F5, + 0x1B80, 0x000208F7, + 0x1B80, 0x30C30905, + 0x1B80, 0x30C30907, + 0x1B80, 0xE1A90915, + 0x1B80, 0xE1A90917, + 0x1B80, 0x62060925, + 0x1B80, 0x62060927, + 0x1B80, 0x60060935, + 0x1B80, 0x60060937, + 0x1B80, 0xE1160945, + 0x1B80, 0xE1160947, + 0x1B80, 0x54BE0955, + 0x1B80, 0x54BE0957, + 0x1B80, 0x56010965, + 0x1B80, 0x56010967, + 0x1B80, 0x5CE20975, + 0x1B80, 0x5CE20977, + 0x1B80, 0x0AE10985, + 0x1B80, 0x0AE10987, + 0x1B80, 0x633A0995, + 0x1B80, 0x633A0997, + 0x1B80, 0xE13F09A5, + 0x1B80, 0xE13F09A7, + 0x1B80, 0x633709B5, + 0x1B80, 0x633709B7, + 0x1B80, 0xE13F09C5, + 0x1B80, 0xE13F09C7, + 0x1B80, 0x632F09D5, + 0x1B80, 0x632F09D7, + 0x1B80, 0xE13F09E5, + 0x1B80, 0xE13F09E7, + 0x1B80, 0x632709F5, + 0x1B80, 0x632709F7, + 0x1B80, 0xE13F0A05, + 0x1B80, 0xE13F0A07, + 0x1B80, 0x631F0A15, + 0x1B80, 0x631F0A17, + 0x1B80, 0xE13F0A25, + 0x1B80, 0xE13F0A27, + 0x1B80, 0x63170A35, + 0x1B80, 0x63170A37, + 0x1B80, 0xE13F0A45, + 0x1B80, 0xE13F0A47, + 0x1B80, 0x630F0A55, + 0x1B80, 0x630F0A57, + 0x1B80, 0xE13F0A65, + 0x1B80, 0xE13F0A67, + 0x1B80, 0x63070A75, + 0x1B80, 0x63070A77, + 0x1B80, 0xE13F0A85, + 0x1B80, 0xE13F0A87, + 0x1B80, 0xE16C0A95, + 0x1B80, 0xE16C0A97, + 0x1B80, 0x56000AA5, + 0x1B80, 0x56000AA7, + 0x1B80, 0x5CF20AB5, + 0x1B80, 0x5CF20AB7, + 0x1B80, 0x0AF10AC5, + 0x1B80, 0x0AF10AC7, + 0x1B80, 0xF5040AD5, + 0x1B80, 0xF5040AD7, + 0x1B80, 0xE13F0AE5, + 0x1B80, 0xE13F0AE7, + 0x1B80, 0xE16C0AF5, + 0x1B80, 0xE16C0AF7, + 0x1B80, 0x30B30B05, + 0x1B80, 0x30B30B07, + 0x1B80, 0x07140B15, + 0x1B80, 0x07140B17, + 0x1B80, 0x07140B25, + 0x1B80, 0x07140B27, + 0x1B80, 0x630F0B35, + 0x1B80, 0x630F0B37, + 0x1B80, 0xE13F0B45, + 0x1B80, 0xE13F0B47, + 0x1B80, 0x63170B55, + 0x1B80, 0x63170B57, + 0x1B80, 0xE13F0B65, + 0x1B80, 0xE13F0B67, + 0x1B80, 0x631F0B75, + 0x1B80, 0x631F0B77, + 0x1B80, 0xE13F0B85, + 0x1B80, 0xE13F0B87, + 0x1B80, 0x63270B95, + 0x1B80, 0x63270B97, + 0x1B80, 0xE13F0BA5, + 0x1B80, 0xE13F0BA7, + 0x1B80, 0x632F0BB5, + 0x1B80, 0x632F0BB7, + 0x1B80, 0xE13F0BC5, + 0x1B80, 0xE13F0BC7, + 0x1B80, 0x63370BD5, + 0x1B80, 0x63370BD7, + 0x1B80, 0xE13F0BE5, + 0x1B80, 0xE13F0BE7, + 0x1B80, 0x633A0BF5, + 0x1B80, 0x633A0BF7, + 0x1B80, 0xE13F0C05, + 0x1B80, 0xE13F0C07, + 0x1B80, 0xF60B0C15, + 0x1B80, 0xF60B0C17, + 0x1B80, 0xF7170C25, + 0x1B80, 0xF7170C27, + 0x1B80, 0x4D300C35, + 0x1B80, 0x4D300C37, + 0x1B80, 0x57040C45, + 0x1B80, 0x57040C47, + 0x1B80, 0x57000C55, + 0x1B80, 0x57000C57, + 0x1B80, 0x96000C65, + 0x1B80, 0x96000C67, + 0x1B80, 0x57080C75, + 0x1B80, 0x57080C77, + 0x1B80, 0x57000C85, + 0x1B80, 0x57000C87, + 0x1B80, 0x95000C95, + 0x1B80, 0x95000C97, + 0x1B80, 0x4D000CA5, + 0x1B80, 0x4D000CA7, + 0x1B80, 0x6C070CB5, + 0x1B80, 0x6C070CB7, + 0x1B80, 0x00010CC5, + 0x1B80, 0x00010CC7, + 0x1B80, 0x00220CD5, + 0x1B80, 0x00220CD7, + 0x1B80, 0x06140CE5, + 0x1B80, 0x06140CE7, + 0x1B80, 0xE16C0CF5, + 0x1B80, 0xE16C0CF7, + 0x1B80, 0x00020D05, + 0x1B80, 0x00020D07, + 0x1B80, 0x00250D15, + 0x1B80, 0x00250D17, + 0x1B80, 0x06140D25, + 0x1B80, 0x06140D27, + 0x1B80, 0xE16C0D35, + 0x1B80, 0xE16C0D37, + 0x1B80, 0x00020D45, + 0x1B80, 0x00020D47, + 0x1B80, 0x00010D55, + 0x1B80, 0x00010D57, + 0x1B80, 0x00320D65, + 0x1B80, 0x00320D67, + 0x1B80, 0xE16C0D75, + 0x1B80, 0xE16C0D77, + 0x1B80, 0x00020D85, + 0x1B80, 0x00020D87, + 0x1B80, 0xE1860D95, + 0x1B80, 0xE1860D97, + 0x1B80, 0xE1B60DA5, + 0x1B80, 0xE1B60DA7, + 0x1B80, 0x5CD10DB5, + 0x1B80, 0x5CD10DB7, + 0x1B80, 0x673A0DC5, + 0x1B80, 0x673A0DC7, + 0x1B80, 0xE1230DD5, + 0x1B80, 0xE1230DD7, + 0x1B80, 0xF80B0DE5, + 0x1B80, 0xF80B0DE7, + 0x1B80, 0xF9110DF5, + 0x1B80, 0xF9110DF7, + 0x1B80, 0xE1580E05, + 0x1B80, 0xE1580E07, + 0x1B80, 0x67370E15, + 0x1B80, 0x67370E17, + 0x1B80, 0xE1580E25, + 0x1B80, 0xE1580E27, + 0x1B80, 0x672F0E35, + 0x1B80, 0x672F0E37, + 0x1B80, 0xE1580E45, + 0x1B80, 0xE1580E47, + 0x1B80, 0x67270E55, + 0x1B80, 0x67270E57, + 0x1B80, 0xE1580E65, + 0x1B80, 0xE1580E67, + 0x1B80, 0x671F0E75, + 0x1B80, 0x671F0E77, + 0x1B80, 0xE1580E85, + 0x1B80, 0xE1580E87, + 0x1B80, 0x67170E95, + 0x1B80, 0x67170E97, + 0x1B80, 0xE1580EA5, + 0x1B80, 0xE1580EA7, + 0x1B80, 0xF8020EB5, + 0x1B80, 0xF8020EB7, + 0x1B80, 0x30EE0EC5, + 0x1B80, 0x30EE0EC7, + 0x1B80, 0xE0D10ED5, + 0x1B80, 0xE0D10ED7, + 0x1B80, 0x670F0EE5, + 0x1B80, 0x670F0EE7, + 0x1B80, 0xE1580EF5, + 0x1B80, 0xE1580EF7, + 0x1B80, 0x67070F05, + 0x1B80, 0x67070F07, + 0x1B80, 0xE1580F15, + 0x1B80, 0xE1580F17, + 0x1B80, 0xF9020F25, + 0x1B80, 0xF9020F27, + 0x1B80, 0x30F50F35, + 0x1B80, 0x30F50F37, + 0x1B80, 0xE0CD0F45, + 0x1B80, 0xE0CD0F47, + 0x1B80, 0x06140F55, + 0x1B80, 0x06140F57, + 0x1B80, 0xE16C0F65, + 0x1B80, 0xE16C0F67, + 0x1B80, 0x5CF10F75, + 0x1B80, 0x5CF10F77, + 0x1B80, 0xE1580F85, + 0x1B80, 0xE1580F87, + 0x1B80, 0x06140F95, + 0x1B80, 0x06140F97, + 0x1B80, 0xE16C0FA5, + 0x1B80, 0xE16C0FA7, + 0x1B80, 0xF9020FB5, + 0x1B80, 0xF9020FB7, + 0x1B80, 0x30FF0FC5, + 0x1B80, 0x30FF0FC7, + 0x1B80, 0xE0CD0FD5, + 0x1B80, 0xE0CD0FD7, + 0x1B80, 0x31130FE5, + 0x1B80, 0x31130FE7, + 0x1B80, 0x670F0FF5, + 0x1B80, 0x670F0FF7, + 0x1B80, 0xE1581005, + 0x1B80, 0xE1581007, + 0x1B80, 0x67171015, + 0x1B80, 0x67171017, + 0x1B80, 0xE1581025, + 0x1B80, 0xE1581027, + 0x1B80, 0xF8021035, + 0x1B80, 0xF8021037, + 0x1B80, 0x31071045, + 0x1B80, 0x31071047, + 0x1B80, 0xE0D11055, + 0x1B80, 0xE0D11057, + 0x1B80, 0x31131065, + 0x1B80, 0x31131067, + 0x1B80, 0x670F1075, + 0x1B80, 0x670F1077, + 0x1B80, 0xE1581085, + 0x1B80, 0xE1581087, + 0x1B80, 0x671F1095, + 0x1B80, 0x671F1097, + 0x1B80, 0xE15810A5, + 0x1B80, 0xE15810A7, + 0x1B80, 0x672710B5, + 0x1B80, 0x672710B7, + 0x1B80, 0xE15810C5, + 0x1B80, 0xE15810C7, + 0x1B80, 0x672F10D5, + 0x1B80, 0x672F10D7, + 0x1B80, 0xE15810E5, + 0x1B80, 0xE15810E7, + 0x1B80, 0x673710F5, + 0x1B80, 0x673710F7, + 0x1B80, 0xE1581105, + 0x1B80, 0xE1581107, + 0x1B80, 0x673A1115, + 0x1B80, 0x673A1117, + 0x1B80, 0xE1581125, + 0x1B80, 0xE1581127, + 0x1B80, 0x4D101135, + 0x1B80, 0x4D101137, + 0x1B80, 0x30C41145, + 0x1B80, 0x30C41147, + 0x1B80, 0x00011155, + 0x1B80, 0x00011157, + 0x1B80, 0x6F241165, + 0x1B80, 0x6F241167, + 0x1B80, 0x6E401175, + 0x1B80, 0x6E401177, + 0x1B80, 0x6D001185, + 0x1B80, 0x6D001187, + 0x1B80, 0x55031195, + 0x1B80, 0x55031197, + 0x1B80, 0x312311A5, + 0x1B80, 0x312311A7, + 0x1B80, 0x6F1C11B5, + 0x1B80, 0x6F1C11B7, + 0x1B80, 0x6E4011C5, + 0x1B80, 0x6E4011C7, + 0x1B80, 0x550B11D5, + 0x1B80, 0x550B11D7, + 0x1B80, 0x312311E5, + 0x1B80, 0x312311E7, + 0x1B80, 0x061C11F5, + 0x1B80, 0x061C11F7, + 0x1B80, 0x54DE1205, + 0x1B80, 0x54DE1207, + 0x1B80, 0x06DC1215, + 0x1B80, 0x06DC1217, + 0x1B80, 0x55131225, + 0x1B80, 0x55131227, + 0x1B80, 0x74011235, + 0x1B80, 0x74011237, + 0x1B80, 0x74001245, + 0x1B80, 0x74001247, + 0x1B80, 0x8E001255, + 0x1B80, 0x8E001257, + 0x1B80, 0x00011265, + 0x1B80, 0x00011267, + 0x1B80, 0x57021275, + 0x1B80, 0x57021277, + 0x1B80, 0x57001285, + 0x1B80, 0x57001287, + 0x1B80, 0x97001295, + 0x1B80, 0x97001297, + 0x1B80, 0x000112A5, + 0x1B80, 0x000112A7, + 0x1B80, 0x54BF12B5, + 0x1B80, 0x54BF12B7, + 0x1B80, 0x54C112C5, + 0x1B80, 0x54C112C7, + 0x1B80, 0x54A212D5, + 0x1B80, 0x54A212D7, + 0x1B80, 0x54C012E5, + 0x1B80, 0x54C012E7, + 0x1B80, 0x54A112F5, + 0x1B80, 0x54A112F7, + 0x1B80, 0x54DF1305, + 0x1B80, 0x54DF1307, + 0x1B80, 0x00011315, + 0x1B80, 0x00011317, + 0x1B80, 0x55001325, + 0x1B80, 0x55001327, + 0x1B80, 0xE1231335, + 0x1B80, 0xE1231337, + 0x1B80, 0x54811345, + 0x1B80, 0x54811347, + 0x1B80, 0xE1231355, + 0x1B80, 0xE1231357, + 0x1B80, 0x54801365, + 0x1B80, 0x54801367, + 0x1B80, 0x002A1375, + 0x1B80, 0x002A1377, + 0x1B80, 0xE12B1385, + 0x1B80, 0xE12B1387, + 0x1B80, 0xE1231395, + 0x1B80, 0xE1231397, + 0x1B80, 0x548013A5, + 0x1B80, 0x548013A7, + 0x1B80, 0xE17213B5, + 0x1B80, 0xE17213B7, + 0x1B80, 0xBF3013C5, + 0x1B80, 0xBF3013C7, + 0x1B80, 0x000213D5, + 0x1B80, 0x000213D7, + 0x1B80, 0x302813E5, + 0x1B80, 0x302813E7, + 0x1B80, 0x4F7813F5, + 0x1B80, 0x4F7813F7, + 0x1B80, 0x4E001405, + 0x1B80, 0x4E001407, + 0x1B80, 0x53871415, + 0x1B80, 0x53871417, + 0x1B80, 0x52F11425, + 0x1B80, 0x52F11427, + 0x1B80, 0xE1161435, + 0x1B80, 0xE1161437, + 0x1B80, 0xE11B1445, + 0x1B80, 0xE11B1447, + 0x1B80, 0xE11F1455, + 0x1B80, 0xE11F1457, + 0x1B80, 0xE1271465, + 0x1B80, 0xE1271467, + 0x1B80, 0x54811475, + 0x1B80, 0x54811477, + 0x1B80, 0xE1161485, + 0x1B80, 0xE1161487, + 0x1B80, 0xE11B1495, + 0x1B80, 0xE11B1497, + 0x1B80, 0xE11F14A5, + 0x1B80, 0xE11F14A7, + 0x1B80, 0xE12714B5, + 0x1B80, 0xE12714B7, + 0x1B80, 0x548014C5, + 0x1B80, 0x548014C7, + 0x1B80, 0x002A14D5, + 0x1B80, 0x002A14D7, + 0x1B80, 0xE12B14E5, + 0x1B80, 0xE12B14E7, + 0x1B80, 0xE11614F5, + 0x1B80, 0xE11614F7, + 0x1B80, 0xE11B1505, + 0x1B80, 0xE11B1507, + 0x1B80, 0xE11F1515, + 0x1B80, 0xE11F1517, + 0x1B80, 0xE1271525, + 0x1B80, 0xE1271527, + 0x1B80, 0x54801535, + 0x1B80, 0x54801537, + 0x1B80, 0xE1721545, + 0x1B80, 0xE1721547, + 0x1B80, 0xBF171555, + 0x1B80, 0xBF171557, + 0x1B80, 0x00021565, + 0x1B80, 0x00021567, + 0x1B80, 0x30281575, + 0x1B80, 0x30281577, + 0x1B80, 0x06141585, + 0x1B80, 0x06141587, + 0x1B80, 0x73201595, + 0x1B80, 0x73201597, + 0x1B80, 0x720015A5, + 0x1B80, 0x720015A7, + 0x1B80, 0x710015B5, + 0x1B80, 0x710015B7, + 0x1B80, 0x550115C5, + 0x1B80, 0x550115C7, + 0x1B80, 0xE12315D5, + 0x1B80, 0xE12315D7, + 0x1B80, 0xE12715E5, + 0x1B80, 0xE12715E7, + 0x1B80, 0x548115F5, + 0x1B80, 0x548115F7, + 0x1B80, 0xE1231605, + 0x1B80, 0xE1231607, + 0x1B80, 0xE1271615, + 0x1B80, 0xE1271617, + 0x1B80, 0x54801625, + 0x1B80, 0x54801627, + 0x1B80, 0x002A1635, + 0x1B80, 0x002A1637, + 0x1B80, 0xE12B1645, + 0x1B80, 0xE12B1647, + 0x1B80, 0xE1231655, + 0x1B80, 0xE1231657, + 0x1B80, 0xE1271665, + 0x1B80, 0xE1271667, + 0x1B80, 0x54801675, + 0x1B80, 0x54801677, + 0x1B80, 0xE1721685, + 0x1B80, 0xE1721687, + 0x1B80, 0xBF031695, + 0x1B80, 0xBF031697, + 0x1B80, 0x000216A5, + 0x1B80, 0x000216A7, + 0x1B80, 0x302816B5, + 0x1B80, 0x302816B7, + 0x1B80, 0x54BF16C5, + 0x1B80, 0x54BF16C7, + 0x1B80, 0x54C516D5, + 0x1B80, 0x54C516D7, + 0x1B80, 0x050A16E5, + 0x1B80, 0x050A16E7, + 0x1B80, 0x071416F5, + 0x1B80, 0x071416F7, + 0x1B80, 0x54DF1705, + 0x1B80, 0x54DF1707, + 0x1B80, 0x00011715, + 0x1B80, 0x00011717, + 0x1B80, 0x54BF1725, + 0x1B80, 0x54BF1727, + 0x1B80, 0x54C01735, + 0x1B80, 0x54C01737, + 0x1B80, 0x54A31745, + 0x1B80, 0x54A31747, + 0x1B80, 0x54C11755, + 0x1B80, 0x54C11757, + 0x1B80, 0x54A41765, + 0x1B80, 0x54A41767, + 0x1B80, 0x4C831775, + 0x1B80, 0x4C831777, + 0x1B80, 0x4C031785, + 0x1B80, 0x4C031787, + 0x1B80, 0xBF0B1795, + 0x1B80, 0xBF0B1797, + 0x1B80, 0x54C217A5, + 0x1B80, 0x54C217A7, + 0x1B80, 0x54A417B5, + 0x1B80, 0x54A417B7, + 0x1B80, 0x4C8517C5, + 0x1B80, 0x4C8517C7, + 0x1B80, 0x4C0517D5, + 0x1B80, 0x4C0517D7, + 0x1B80, 0xBF0617E5, + 0x1B80, 0xBF0617E7, + 0x1B80, 0x54C117F5, + 0x1B80, 0x54C117F7, + 0x1B80, 0x54A31805, + 0x1B80, 0x54A31807, + 0x1B80, 0x4C861815, + 0x1B80, 0x4C861817, + 0x1B80, 0x4C061825, + 0x1B80, 0x4C061827, + 0x1B80, 0xBF011835, + 0x1B80, 0xBF011837, + 0x1B80, 0x54DF1845, + 0x1B80, 0x54DF1847, + 0x1B80, 0x00011855, + 0x1B80, 0x00011857, + 0x1B80, 0x00071865, + 0x1B80, 0x00071867, + 0x1B80, 0x54011875, + 0x1B80, 0x54011877, + 0x1B80, 0x00041885, + 0x1B80, 0x00041887, + 0x1B80, 0x56001895, + 0x1B80, 0x56001897, + 0x1B80, 0x5CF218A5, + 0x1B80, 0x5CF218A7, + 0x1B80, 0x630718B5, + 0x1B80, 0x630718B7, + 0x1B80, 0x620418C5, + 0x1B80, 0x620418C7, + 0x1B80, 0x610018D5, + 0x1B80, 0x610018D7, + 0x1B80, 0x670718E5, + 0x1B80, 0x670718E7, + 0x1B80, 0x660618F5, + 0x1B80, 0x660618F7, + 0x1B80, 0x6F201905, + 0x1B80, 0x6F201907, + 0x1B80, 0x6E001915, + 0x1B80, 0x6E001917, + 0x1B80, 0x6D001925, + 0x1B80, 0x6D001927, + 0x1B80, 0x6C031935, + 0x1B80, 0x6C031937, + 0x1B80, 0x73201945, + 0x1B80, 0x73201947, + 0x1B80, 0x72001955, + 0x1B80, 0x72001957, + 0x1B80, 0x71001965, + 0x1B80, 0x71001967, + 0x1B80, 0x7B201975, + 0x1B80, 0x7B201977, + 0x1B80, 0x7A001985, + 0x1B80, 0x7A001987, + 0x1B80, 0x79001995, + 0x1B80, 0x79001997, + 0x1B80, 0x7F2019A5, + 0x1B80, 0x7F2019A7, + 0x1B80, 0x7E0019B5, + 0x1B80, 0x7E0019B7, + 0x1B80, 0x7D0019C5, + 0x1B80, 0x7D0019C7, + 0x1B80, 0x090119D5, + 0x1B80, 0x090119D7, + 0x1B80, 0x0AC619E5, + 0x1B80, 0x0AC619E7, + 0x1B80, 0x0BA619F5, + 0x1B80, 0x0BA619F7, + 0x1B80, 0x0C011A05, + 0x1B80, 0x0C011A07, + 0x1B80, 0x0D021A15, + 0x1B80, 0x0D021A17, + 0x1B80, 0x0E041A25, + 0x1B80, 0x0E041A27, + 0x1B80, 0x0FFF1A35, + 0x1B80, 0x0FFF1A37, + 0x1B80, 0x4D041A45, + 0x1B80, 0x4D041A47, + 0x1B80, 0x28F81A55, + 0x1B80, 0x28F81A57, + 0x1B80, 0xE0001A65, + 0x1B80, 0xE0001A67, + 0x1B80, 0x4D001A75, + 0x1B80, 0x4D001A77, + 0x1B80, 0x00011A85, + 0x1B80, 0x00011A87, + 0x1B80, 0x4D041A95, + 0x1B80, 0x4D041A97, + 0x1B80, 0x2EF81AA5, + 0x1B80, 0x2EF81AA7, + 0x1B80, 0x00021AB5, + 0x1B80, 0x00021AB7, + 0x1B80, 0x23031AC5, + 0x1B80, 0x23031AC7, + 0x1B80, 0x00001AD5, + 0x1B80, 0x00001AD7, + 0x1B80, 0x23131AE5, + 0x1B80, 0x23131AE7, + 0x1B80, 0xE77F1AF5, + 0x1B80, 0xE77F1AF7, + 0x1B80, 0x232F1B05, + 0x1B80, 0x232F1B07, + 0x1B80, 0xEFBF1B15, + 0x1B80, 0xEFBF1B17, + 0x1B80, 0x2EF01B25, + 0x1B80, 0x2EF01B27, + 0x1B80, 0x00021B35, + 0x1B80, 0x00021B37, + 0x1B80, 0x4D001B45, + 0x1B80, 0x4D001B47, + 0x1B80, 0x00011B55, + 0x1B80, 0x00011B57, + 0x1B80, 0x4D041B65, + 0x1B80, 0x4D041B67, + 0x1B80, 0x2EF81B75, + 0x1B80, 0x2EF81B77, + 0x1B80, 0x00021B85, + 0x1B80, 0x00021B87, + 0x1B80, 0x23031B95, + 0x1B80, 0x23031B97, + 0x1B80, 0x00001BA5, + 0x1B80, 0x00001BA7, + 0x1B80, 0x23131BB5, + 0x1B80, 0x23131BB7, + 0x1B80, 0xE77F1BC5, + 0x1B80, 0xE77F1BC7, + 0x1B80, 0x232F1BD5, + 0x1B80, 0x232F1BD7, + 0x1B80, 0xE79F1BE5, + 0x1B80, 0xE79F1BE7, + 0x1B80, 0x2EF01BF5, + 0x1B80, 0x2EF01BF7, + 0x1B80, 0x00021C05, + 0x1B80, 0x00021C07, + 0x1B80, 0x28F81C15, + 0x1B80, 0x28F81C17, + 0x1B80, 0x80001C25, + 0x1B80, 0x80001C27, + 0x1B80, 0x4D001C35, + 0x1B80, 0x4D001C37, + 0x1B80, 0x00011C45, + 0x1B80, 0x00011C47, + 0x1B80, 0x00041C55, + 0x1B80, 0x00041C57, + 0x1B80, 0x6BC01C65, + 0x1B80, 0x6BC01C67, + 0x1B80, 0x4D041C75, + 0x1B80, 0x4D041C77, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241C85, + 0x1B80, 0x68241C87, + 0xA0000000, 0x00000000, + 0x1B80, 0x68481C85, + 0x1B80, 0x68481C87, + 0xB0000000, 0x00000000, + 0x1B80, 0x66061C95, + 0x1B80, 0x66061C97, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x650C1CA5, + 0x1B80, 0x650C1CA7, + 0xA0000000, 0x00000000, + 0x1B80, 0x65041CA5, + 0x1B80, 0x65041CA7, + 0xB0000000, 0x00000000, + 0x1B80, 0x64471CB5, + 0x1B80, 0x64471CB7, + 0x1B80, 0x23411CC5, + 0x1B80, 0x23411CC7, + 0x1B80, 0x100E1CD5, + 0x1B80, 0x100E1CD7, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60101CE5, + 0x1B80, 0x60101CE7, + 0xA0000000, 0x00000000, + 0x1B80, 0x60011CE5, + 0x1B80, 0x60011CE7, + 0xB0000000, 0x00000000, + 0x1B80, 0x23411CF5, + 0x1B80, 0x23411CF7, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60811D05, + 0x1B80, 0x60811D07, + 0xA0000000, 0x00000000, + 0x1B80, 0x60611D05, + 0x1B80, 0x60611D07, + 0xB0000000, 0x00000000, + 0x1B80, 0x23411D15, + 0x1B80, 0x23411D17, + 0x1B80, 0x70E11D25, + 0x1B80, 0x70E11D27, + 0x1B80, 0x4D001D35, + 0x1B80, 0x4D001D37, + 0x1B80, 0x00011D45, + 0x1B80, 0x00011D47, + 0x1B80, 0x00041D55, + 0x1B80, 0x00041D57, + 0x1B80, 0x6B401D65, + 0x1B80, 0x6B401D67, + 0x1B80, 0x4D041D75, + 0x1B80, 0x4D041D77, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x68241D85, + 0x1B80, 0x68241D87, + 0xA0000000, 0x00000000, + 0x1B80, 0x68481D85, + 0x1B80, 0x68481D87, + 0xB0000000, 0x00000000, + 0x1B80, 0x66061D95, + 0x1B80, 0x66061D97, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65081DA5, + 0x1B80, 0x65081DA7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65081DA5, + 0x1B80, 0x65081DA7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x65181DA5, + 0x1B80, 0x65181DA7, + 0xA0000000, 0x00000000, + 0x1B80, 0x65081DA5, + 0x1B80, 0x65081DA7, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x64481DB5, + 0x1B80, 0x64481DB7, + 0xA0000000, 0x00000000, + 0x1B80, 0x64471DB5, + 0x1B80, 0x64471DB7, + 0xB0000000, 0x00000000, + 0x1B80, 0x23411DC5, + 0x1B80, 0x23411DC7, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E41DD5, + 0x1B80, 0x11E41DD7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E41DD5, + 0x1B80, 0x11E41DD7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x11E81DD5, + 0x1B80, 0x11E81DD7, + 0xA0000000, 0x00000000, + 0x1B80, 0x11E41DD5, + 0x1B80, 0x11E41DD7, + 0xB0000000, 0x00000000, + 0x1B80, 0x60011DE5, + 0x1B80, 0x60011DE7, + 0x1B80, 0x23411DF5, + 0x1B80, 0x23411DF7, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60E11E05, + 0x1B80, 0x60E11E07, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x60E11E05, + 0x1B80, 0x60E11E07, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x1B80, 0x61E11E05, + 0x1B80, 0x61E11E07, + 0xA0000000, 0x00000000, + 0x1B80, 0x60E11E05, + 0x1B80, 0x60E11E07, + 0xB0000000, 0x00000000, + 0x1B80, 0x23411E15, + 0x1B80, 0x23411E17, + 0x1B80, 0x70611E25, + 0x1B80, 0x70611E27, + 0x1B80, 0x4D001E35, + 0x1B80, 0x4D001E37, + 0x1B80, 0x00011E45, + 0x1B80, 0x00011E47, + 0x1B80, 0x00001E55, + 0x1B80, 0x00001E57, + 0x1B80, 0x00001E65, + 0x1B80, 0x00001E67, + 0x1B80, 0x00001E75, + 0x1B80, 0x00001E77, + 0x1B80, 0x00001E85, + 0x1B80, 0x00001E87, + 0x1B80, 0x00001E95, + 0x1B80, 0x00001E97, + 0x1B80, 0x00001EA5, + 0x1B80, 0x00001EA7, + 0x1B80, 0x00001EB5, + 0x1B80, 0x00001EB7, + 0x1B80, 0x00001EC5, + 0x1B80, 0x00001EC7, + 0x1B80, 0x00001ED5, + 0x1B80, 0x00001ED7, + 0x1B80, 0x00001EE5, + 0x1B80, 0x00001EE7, + 0x1B80, 0x00001EF5, + 0x1B80, 0x00001EF7, + 0x1B80, 0x00001F05, + 0x1B80, 0x00001F07, + 0x1B80, 0x00001F15, + 0x1B80, 0x00001F17, + 0x1B80, 0x00001F25, + 0x1B80, 0x00001F27, + 0x1B80, 0x00001F35, + 0x1B80, 0x00001F37, + 0x1B80, 0x00001F45, + 0x1B80, 0x00001F47, + 0x1B80, 0x00001F55, + 0x1B80, 0x00001F57, + 0x1B80, 0x00001F65, + 0x1B80, 0x00001F67, + 0x1B80, 0x00001F75, + 0x1B80, 0x00001F77, + 0x1B80, 0x00001F85, + 0x1B80, 0x00001F87, + 0x1B80, 0x00001F95, + 0x1B80, 0x00001F97, + 0x1B80, 0x00001FA5, + 0x1B80, 0x00001FA7, + 0x1B80, 0x00001FB5, + 0x1B80, 0x00001FB7, + 0x1B80, 0x00001FC5, + 0x1B80, 0x00001FC7, + 0x1B80, 0x00001FD5, + 0x1B80, 0x00001FD7, + 0x1B80, 0x00001FE5, + 0x1B80, 0x00001FE7, + 0x1B80, 0x00001FF5, + 0x1B80, 0x00001FF7, + 0x1B80, 0x00000006, + 0x1B80, 0x00000002, +}; + +RTW_DECL_TABLE_PHY_COND(rtw8814a_bb, rtw_phy_cfg_bb); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x32323232, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x26283032, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x32323232, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x26283032, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type0[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x32323232, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x32323232, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x28303232, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x32323232, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x26283032, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x30303030, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x28282828, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x22242628, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x32323232, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x26283032, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x30302224, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x28303030, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x20222426, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x28282828, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x22242628, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x18202020, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x32323232, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x32323232, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x28303232, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x32323232, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x26283032, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x30303030, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x28282828, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x22242628, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x32323232, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x26283032, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x30302224, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x28303030, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x20222426, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x28282828, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x22242628, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x18202020, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x32323232, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x32323232, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x28303232, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x32323232, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x26283032, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x30303030, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x28282828, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x22242628, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x32323232, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x26283032, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x30302224, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x28303030, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x20222426, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x28282828, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x22242628, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x18202020, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x32323232, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x32323232, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x28303232, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x32323232, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x26283032, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x30303030, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x28282828, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x22242628, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x32323232, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x26283032, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x30302224, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x28303030, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x20222426, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x28282828, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x22242628, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x18202020, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x32323232, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x28303232, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x32323232, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x26283032, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x30303030, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x24262830, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x28282828, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x22242628, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x32323232, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x26283032, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x30302224, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x28303030, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x20222426, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x28282828, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x22242628, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x18202020, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x32323232, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x28303232, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x32323232, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x26283032, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x30303030, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x24262830, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x28282828, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x22242628, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x32323232, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x26283032, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x30302224, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x28303030, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x20222426, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x28282828, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x22242628, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x18202020, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x32323232, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x28303232, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x32323232, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x26283032, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x30303030, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x24262830, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x28282828, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x22242628, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x32323232, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x26283032, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x30302224, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x28303030, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x20222426, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x28282828, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x22242628, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x18202020, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x32323232, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x28303232, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x32323232, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x26283032, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x30303030, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x24262830, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x28282828, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x22242628, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x32323232, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x26283032, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x30302224, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x28303030, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x20222426, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x28282828, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x22242628, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x18202020, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type0); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type2[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x32323232, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x26283032, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x32323232, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x26283032, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x30303030, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x24262830, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x32323232, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x26283032, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x30303030, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x24262830, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x32323232, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x26283032, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x30303030, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x24262830, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x32323232, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x26283032, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x30303030, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x24262830, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type2); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type3[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x48484848, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x42444646, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x42444646, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x46463840, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x46464646, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x38404244, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x42444646, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x38383840, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x48484848, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x42444646, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x42444646, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x46463840, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x46464646, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x38404244, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x42444646, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x38383840, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x48484848, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x44464646, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x42444646, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x46464646, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x42444646, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x42444646, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x42444646, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x46463840, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x46464646, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x38404244, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x42444646, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x38383840, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x48484848, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x42444646, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x42444646, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x46463840, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x46464646, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x38404244, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x42444646, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x38383840, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x42444646, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x42444646, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x46463840, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x46464646, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x38404244, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x42444646, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x38383840, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x42444646, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x42444646, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x46463840, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x46464646, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x38404244, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x42444646, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x38383840, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x44464646, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x42444646, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x46464646, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x42444646, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x42444646, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x42444646, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x46463840, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x46464646, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x38404244, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x42444646, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x38383840, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x42444646, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x42444646, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x46463840, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x46464646, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x38404244, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x42444646, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x38383840, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type3); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type4[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x42424242, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x42424242, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x36384042, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x42424242, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x34363840, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x42424242, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x34363840, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x42424242, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x34363840, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x42423032, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x38404242, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x30323436, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x34363840, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x30303032, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x42424242, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x42424242, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x36384042, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x42424242, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x34363840, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x42424242, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x34363840, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x42424242, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x34363840, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x42423032, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x38404242, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x30323436, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x34363840, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x30303032, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x42424242, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x42424242, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x36384042, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x42424242, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x34363840, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x42424242, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x34363840, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x42424242, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x34363840, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x42423032, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x38404242, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x30323436, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x34363840, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x30303032, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x42424242, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x42424242, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x36384042, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x42424242, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x34363840, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x42424242, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x34363840, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x42424242, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x34363840, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x42423032, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x38404242, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x30323436, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x34363840, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x30303032, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x42424242, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x36384042, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x42424242, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x34363840, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x42424242, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x34363840, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x42424242, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x34363840, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x42423032, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x38404242, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x30323436, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x34363840, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x30303032, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x42424242, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x36384042, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x42424242, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x34363840, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x42424242, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x34363840, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x42424242, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x34363840, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x42423032, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x38404242, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x30323436, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x34363840, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x30303032, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x42424242, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x36384042, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x42424242, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x34363840, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x42424242, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x34363840, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x42424242, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x34363840, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x42423032, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x38404242, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x30323436, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x34363840, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x30303032, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x42424242, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x36384042, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x42424242, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x34363840, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x42424242, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x34363840, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x42424242, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x34363840, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x42423032, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x38404242, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x30323436, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x34363840, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x30303032, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type4); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type5[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x48484848, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x44444444, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x40424444, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x38404242, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x44444040, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x44444444, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x38384042, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x38404242, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20203636, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x48484848, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x44444444, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x40424444, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x38404242, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x44444040, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x44444444, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x38384042, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x38404242, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20203636, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x48484848, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x44464646, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x42444646, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x44444444, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x40424444, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x38404242, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x42444646, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x44444040, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x44444444, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x38384042, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x38404242, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20203636, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x48484848, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x44444444, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x40424444, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x38404242, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x44444040, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x44444444, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x38384042, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x38404242, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20203636, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x44464646, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x42444646, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x44444444, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x40424444, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x42424242, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x38404242, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x42444646, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x44443840, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x44444444, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x36384042, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x42424242, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x38404242, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x20203436, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x44464646, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x42444646, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x44444444, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x40424444, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x42424242, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x38404242, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x42444646, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x44443840, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x44444444, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x36384042, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x42424242, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x38404242, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x20203436, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x44464646, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x42444646, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x44444444, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x40424444, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x42424242, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x38404242, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x42444646, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x44443840, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x44444444, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x36384042, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x42424242, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x38404242, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x20203436, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x44464646, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x42444646, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x44444444, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x40424444, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x42424242, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x38404242, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x42444646, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x44443840, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x44444444, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x36384042, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x42424242, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x38404242, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x20203436, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type5); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type7[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x34343434, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x28303234, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x34343434, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x34342426, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x32343434, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x34343434, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x28303234, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x24263434, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x34343434, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x28303234, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x34343434, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x34342426, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x32343434, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x34343434, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x28303234, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x24263434, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x34343434, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x28303234, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x34343434, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x34342426, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x32343434, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x34343434, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x28303234, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x24263434, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x34343434, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x28303234, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x34343434, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x34342426, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x32343434, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x34343434, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x28303234, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x24263434, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x30323434, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x28303234, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x34343434, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x28303234, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x34343434, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x28303234, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x34342426, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x32343434, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x24262830, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x34343434, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x28303234, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x24263434, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x30323434, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x28303234, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x34343434, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x28303234, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x34343434, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x28303234, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x34342426, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x32343434, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x24262830, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x34343434, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x28303234, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x24263434, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x30323434, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x28303234, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x34343434, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x28303234, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x34343434, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x28303234, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x34342426, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x32343434, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x24262830, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x34343434, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x28303234, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x24263434, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x30323434, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x28303234, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x34343434, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x28303234, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x34343434, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x28303234, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x34342426, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x32343434, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x24262830, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x34343434, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x28303234, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x24263434, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type7); + +static const struct rtw_phy_pg_cfg_pair rtw8814a_bb_pg_type8[] = { + { 0, 0, 0, 0x00000c20, 0xffffffff, 0x43434343, }, + { 0, 0, 0, 0x00000c24, 0xffffffff, 0x43434343, }, + { 0, 0, 0, 0x00000c28, 0xffffffff, 0x35373941, }, + { 0, 0, 0, 0x00000c2c, 0xffffffff, 0x43434343, }, + { 0, 0, 0, 0x00000c30, 0xffffffff, 0x33353739, }, + { 0, 0, 1, 0x00000c34, 0xffffffff, 0x43434343, }, + { 0, 0, 1, 0x00000c38, 0xffffffff, 0x31333537, }, + { 0, 0, 2, 0x00000cd8, 0xffffffff, 0x43434343, }, + { 0, 0, 2, 0x00000cdc, 0xffffffff, 0x29313335, }, + { 0, 0, 0, 0x00000c3c, 0xffffffff, 0x34343434, }, + { 0, 0, 0, 0x00000c40, 0xffffffff, 0x28303234, }, + { 0, 0, 0, 0x00000c44, 0xffffffff, 0x32322426, }, + { 0, 0, 1, 0x00000c48, 0xffffffff, 0x30323232, }, + { 0, 0, 1, 0x00000c4c, 0xffffffff, 0x22242628, }, + { 0, 0, 2, 0x00000ce0, 0xffffffff, 0x30303030, }, + { 0, 0, 2, 0x00000ce4, 0xffffffff, 0x24262830, }, + { 0, 0, 2, 0x00000ce8, 0x0000ffff, 0x20222222, }, + { 0, 1, 0, 0x00000e20, 0xffffffff, 0x43434343, }, + { 0, 1, 0, 0x00000e24, 0xffffffff, 0x43434343, }, + { 0, 1, 0, 0x00000e28, 0xffffffff, 0x35373941, }, + { 0, 1, 0, 0x00000e2c, 0xffffffff, 0x41434343, }, + { 0, 1, 0, 0x00000e30, 0xffffffff, 0x33353739, }, + { 0, 1, 1, 0x00000e34, 0xffffffff, 0x39414141, }, + { 0, 1, 1, 0x00000e38, 0xffffffff, 0x31333537, }, + { 0, 1, 2, 0x00000ed8, 0xffffffff, 0x37393939, }, + { 0, 1, 2, 0x00000edc, 0xffffffff, 0x29313335, }, + { 0, 1, 0, 0x00000e3c, 0xffffffff, 0x34343434, }, + { 0, 1, 0, 0x00000e40, 0xffffffff, 0x28303234, }, + { 0, 1, 0, 0x00000e44, 0xffffffff, 0x32322426, }, + { 0, 1, 1, 0x00000e48, 0xffffffff, 0x30323232, }, + { 0, 1, 1, 0x00000e4c, 0xffffffff, 0x22242628, }, + { 0, 1, 2, 0x00000ee0, 0xffffffff, 0x30303030, }, + { 0, 1, 2, 0x00000ee4, 0xffffffff, 0x24262830, }, + { 0, 1, 2, 0x00000ee8, 0x0000ffff, 0x20222222, }, + { 0, 2, 0, 0x00001820, 0xffffffff, 0x43434343, }, + { 0, 2, 0, 0x00001824, 0xffffffff, 0x43434343, }, + { 0, 2, 0, 0x00001828, 0xffffffff, 0x35373941, }, + { 0, 2, 0, 0x0000182c, 0xffffffff, 0x41434343, }, + { 0, 2, 0, 0x00001830, 0xffffffff, 0x33353739, }, + { 0, 2, 1, 0x00001834, 0xffffffff, 0x39414141, }, + { 0, 2, 1, 0x00001838, 0xffffffff, 0x31333537, }, + { 0, 2, 2, 0x000018d8, 0xffffffff, 0x37393939, }, + { 0, 2, 2, 0x000018dc, 0xffffffff, 0x29313335, }, + { 0, 2, 0, 0x0000183c, 0xffffffff, 0x34343434, }, + { 0, 2, 0, 0x00001840, 0xffffffff, 0x28303234, }, + { 0, 2, 0, 0x00001844, 0xffffffff, 0x32322426, }, + { 0, 2, 1, 0x00001848, 0xffffffff, 0x30323232, }, + { 0, 2, 1, 0x0000184c, 0xffffffff, 0x22242628, }, + { 0, 2, 2, 0x000018e0, 0xffffffff, 0x30303030, }, + { 0, 2, 2, 0x000018e4, 0xffffffff, 0x24262830, }, + { 0, 2, 2, 0x000018e8, 0x0000ffff, 0x20222222, }, + { 0, 3, 0, 0x00001a20, 0xffffffff, 0x43434343, }, + { 0, 3, 0, 0x00001a24, 0xffffffff, 0x43434343, }, + { 0, 3, 0, 0x00001a28, 0xffffffff, 0x35373941, }, + { 0, 3, 0, 0x00001a2c, 0xffffffff, 0x41434343, }, + { 0, 3, 0, 0x00001a30, 0xffffffff, 0x33353739, }, + { 0, 3, 1, 0x00001a34, 0xffffffff, 0x39414141, }, + { 0, 3, 1, 0x00001a38, 0xffffffff, 0x31333537, }, + { 0, 3, 2, 0x00001ad8, 0xffffffff, 0x37393939, }, + { 0, 3, 2, 0x00001adc, 0xffffffff, 0x29313335, }, + { 0, 3, 0, 0x00001a3c, 0xffffffff, 0x34343434, }, + { 0, 3, 0, 0x00001a40, 0xffffffff, 0x28303234, }, + { 0, 3, 0, 0x00001a44, 0xffffffff, 0x32322426, }, + { 0, 3, 1, 0x00001a48, 0xffffffff, 0x30323232, }, + { 0, 3, 1, 0x00001a4c, 0xffffffff, 0x22242628, }, + { 0, 3, 2, 0x00001ae0, 0xffffffff, 0x30303030, }, + { 0, 3, 2, 0x00001ae4, 0xffffffff, 0x24262830, }, + { 0, 3, 2, 0x00001ae8, 0x0000ffff, 0x20222222, }, + { 1, 0, 0, 0x00000c24, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c28, 0xffffffff, 0x39414345, }, + { 1, 0, 0, 0x00000c2c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c30, 0xffffffff, 0x38404244, }, + { 1, 0, 1, 0x00000c34, 0xffffffff, 0x46464646, }, + { 1, 0, 1, 0x00000c38, 0xffffffff, 0x36384042, }, + { 1, 0, 2, 0x00000cd8, 0xffffffff, 0x46464646, }, + { 1, 0, 2, 0x00000cdc, 0xffffffff, 0x34363840, }, + { 1, 0, 0, 0x00000c3c, 0xffffffff, 0x46464646, }, + { 1, 0, 0, 0x00000c40, 0xffffffff, 0x38404244, }, + { 1, 0, 0, 0x00000c44, 0xffffffff, 0x46463738, }, + { 1, 0, 1, 0x00000c48, 0xffffffff, 0x42444646, }, + { 1, 0, 1, 0x00000c4c, 0xffffffff, 0x35373840, }, + { 1, 0, 2, 0x00000ce0, 0xffffffff, 0x46464646, }, + { 1, 0, 2, 0x00000ce4, 0xffffffff, 0x37394143, }, + { 1, 0, 2, 0x00000ce8, 0x0000ffff, 0x33333335, }, + { 1, 1, 0, 0x00000e24, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e28, 0xffffffff, 0x39414345, }, + { 1, 1, 0, 0x00000e2c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e30, 0xffffffff, 0x38404244, }, + { 1, 1, 1, 0x00000e34, 0xffffffff, 0x46464646, }, + { 1, 1, 1, 0x00000e38, 0xffffffff, 0x36384042, }, + { 1, 1, 2, 0x00000ed8, 0xffffffff, 0x46464646, }, + { 1, 1, 2, 0x00000edc, 0xffffffff, 0x34363840, }, + { 1, 1, 0, 0x00000e3c, 0xffffffff, 0x46464646, }, + { 1, 1, 0, 0x00000e40, 0xffffffff, 0x38404244, }, + { 1, 1, 0, 0x00000e44, 0xffffffff, 0x46463738, }, + { 1, 1, 1, 0x00000e48, 0xffffffff, 0x42444646, }, + { 1, 1, 1, 0x00000e4c, 0xffffffff, 0x35373840, }, + { 1, 1, 2, 0x00000ee0, 0xffffffff, 0x46464646, }, + { 1, 1, 2, 0x00000ee4, 0xffffffff, 0x37394143, }, + { 1, 1, 2, 0x00000ee8, 0x0000ffff, 0x33333335, }, + { 1, 2, 0, 0x00001824, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001828, 0xffffffff, 0x39414345, }, + { 1, 2, 0, 0x0000182c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001830, 0xffffffff, 0x38404244, }, + { 1, 2, 1, 0x00001834, 0xffffffff, 0x46464646, }, + { 1, 2, 1, 0x00001838, 0xffffffff, 0x36384042, }, + { 1, 2, 2, 0x000018d8, 0xffffffff, 0x46464646, }, + { 1, 2, 2, 0x000018dc, 0xffffffff, 0x34363840, }, + { 1, 2, 0, 0x0000183c, 0xffffffff, 0x46464646, }, + { 1, 2, 0, 0x00001840, 0xffffffff, 0x38404244, }, + { 1, 2, 0, 0x00001844, 0xffffffff, 0x46463738, }, + { 1, 2, 1, 0x00001848, 0xffffffff, 0x42444646, }, + { 1, 2, 1, 0x0000184c, 0xffffffff, 0x35373840, }, + { 1, 2, 2, 0x000018e0, 0xffffffff, 0x46464646, }, + { 1, 2, 2, 0x000018e4, 0xffffffff, 0x37394143, }, + { 1, 2, 2, 0x000018e8, 0x0000ffff, 0x33333335, }, + { 1, 3, 0, 0x00001a24, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a28, 0xffffffff, 0x39414345, }, + { 1, 3, 0, 0x00001a2c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a30, 0xffffffff, 0x38404244, }, + { 1, 3, 1, 0x00001a34, 0xffffffff, 0x46464646, }, + { 1, 3, 1, 0x00001a38, 0xffffffff, 0x36384042, }, + { 1, 3, 2, 0x00001ad8, 0xffffffff, 0x46464646, }, + { 1, 3, 2, 0x00001adc, 0xffffffff, 0x34363840, }, + { 1, 3, 0, 0x00001a3c, 0xffffffff, 0x46464646, }, + { 1, 3, 0, 0x00001a40, 0xffffffff, 0x38404244, }, + { 1, 3, 0, 0x00001a44, 0xffffffff, 0x46463738, }, + { 1, 3, 1, 0x00001a48, 0xffffffff, 0x42444646, }, + { 1, 3, 1, 0x00001a4c, 0xffffffff, 0x35373840, }, + { 1, 3, 2, 0x00001ae0, 0xffffffff, 0x46464646, }, + { 1, 3, 2, 0x00001ae4, 0xffffffff, 0x37394143, }, + { 1, 3, 2, 0x00001ae8, 0x0000ffff, 0x33333335, }, +}; + +RTW_DECL_TABLE_BB_PG(rtw8814a_bb_pg_type8); + +static const u32 rtw8814a_rf_a[] = { + 0x018, 0x00013124, + 0x040, 0x00000C00, + 0x058, 0x00000F98, + 0x07F, 0x00068004, + 0x0B0, 0x000FFFFE, + 0x0B1, 0x0003FF48, + 0x0B2, 0x0006AA3F, + 0x0B3, 0x000FFC9A, + 0x0B4, 0x0000A78F, + 0x0B5, 0x00000A3F, + 0x0B6, 0x0000C09C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0B7, 0x00030008, + 0xA0000000, 0x00000000, + 0x0B7, 0x0003000C, + 0xB0000000, 0x00000000, + 0x0B8, 0x0007400E, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0xA0000000, 0x00000000, + 0x0B9, 0x000FBF50, + 0xB0000000, 0x00000000, + 0x0BA, 0x00050780, + 0x0BB, 0x00000000, + 0x0BC, 0x00040009, + 0x0BD, 0x00000000, + 0x0BE, 0x00000000, + 0x0BF, 0x00000000, + 0x0EF, 0x00020000, + 0x03E, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0xA0000000, 0x00000000, + 0x03F, 0x00030000, + 0xB0000000, 0x00000000, + 0x03E, 0x00020000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00040000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00040000, + 0xA0000000, 0x00000000, + 0x03F, 0x00040000, + 0xB0000000, 0x00000000, + 0x03E, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0xA0000000, 0x00000000, + 0x03F, 0x00030000, + 0xB0000000, 0x00000000, + 0x03E, 0x00060000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03F, 0x00030000, + 0xA0000000, 0x00000000, + 0x03F, 0x00030000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00010000, + 0x03E, 0x00000000, + 0x03F, 0x00006800, + 0x03E, 0x00000080, + 0x03F, 0x00006000, + 0x03E, 0x00000100, + 0x03F, 0x00004800, + 0x03E, 0x00000180, + 0x03F, 0x00004000, + 0x03E, 0x00000200, + 0x03F, 0x00004000, + 0x03E, 0x00000280, + 0x03F, 0x00002800, + 0x03E, 0x00000300, + 0x03F, 0x00002800, + 0x03E, 0x00000380, + 0x03F, 0x00002000, + 0x0EF, 0x00000000, + 0x0EF, 0x00040000, + 0x03E, 0x00000000, + 0x03F, 0x000000BC, + 0x03E, 0x00000040, + 0x03F, 0x00000053, + 0x03E, 0x00000050, + 0x03F, 0x00000050, + 0x03E, 0x00000060, + 0x03F, 0x00000050, + 0x0EF, 0x00000000, + 0x0EF, 0x00000400, + 0x03E, 0x00000006, + 0x041, 0x000EE080, + 0x03E, 0x00000008, + 0x041, 0x000EE0C0, + 0x03E, 0x0000000A, + 0x041, 0x000EE100, + 0x03E, 0x0000000C, + 0x041, 0x000EE100, + 0x0EF, 0x00000000, + 0x018, 0x00000006, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0xA0000000, 0x00000000, + 0x086, 0x000E4B58, + 0x087, 0x00049F80, + 0xB0000000, 0x00000000, + 0x0DF, 0x00000008, + 0x0EF, 0x00002000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x000179C3, + 0xA0000000, 0x00000000, + 0x03B, 0x0003F258, + 0x03B, 0x00030A58, + 0x03B, 0x0002FA58, + 0x03B, 0x00022590, + 0x03B, 0x0001FA50, + 0x03B, 0x00010248, + 0x03B, 0x00008240, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0000ADF6, + 0x034, 0x00009DF3, + 0x034, 0x00008DF0, + 0x034, 0x00007DED, + 0x034, 0x00006DEA, + 0x034, 0x00005CED, + 0x034, 0x00004CEA, + 0x034, 0x000034EA, + 0x034, 0x000024E7, + 0x034, 0x0000146A, + 0x034, 0x0000006B, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0008ADF6, + 0x034, 0x00089DF3, + 0x034, 0x00088DF0, + 0x034, 0x00087DED, + 0x034, 0x00086DEA, + 0x034, 0x00085CED, + 0x034, 0x00084CEA, + 0x034, 0x000834EA, + 0x034, 0x000824E7, + 0x034, 0x0008146A, + 0x034, 0x0008006B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x000020A2, + 0x0DF, 0x00000080, + 0x035, 0x00000192, + 0x035, 0x00008192, + 0x035, 0x00010192, + 0x036, 0x00000024, + 0x036, 0x00008024, + 0x036, 0x00010024, + 0x036, 0x00018024, + 0x0EF, 0x00000000, + 0x051, 0x00000C21, + 0x052, 0x000006D9, + 0x053, 0x000FC649, + 0x054, 0x0000017E, + 0x018, 0x0001012A, + 0x081, 0x0007FC00, + 0x089, 0x00050110, + 0x08A, 0x00043E50, + 0x08B, 0x0002E180, + 0x08C, 0x00093C3C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0xA0000000, 0x00000000, + 0x085, 0x000F8000, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xA0000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xB0000000, 0x00000000, + 0x0EF, 0x00001000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x00028000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00030023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00028623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00021633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0001C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00010293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00009593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0000118B, + 0xA0000000, 0x00000000, + 0x03B, 0x0000078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x000AC000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00040000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x0004C000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00070023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00068623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00061633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0005C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00050293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00049593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0004138B, + 0xA0000000, 0x00000000, + 0x03B, 0x0004078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0008C000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0xA0000000, 0x00000000, + 0x03C, 0x00004000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B0023, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A8623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A1633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0009C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00090293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00089593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0008118B, + 0xA0000000, 0x00000000, + 0x03B, 0x0008078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000800, + 0x03B, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000801, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0xA0000000, 0x00000000, + 0x03A, 0x00000803, + 0xB0000000, 0x00000000, + 0x03B, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001801, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000003, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000003, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001001, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0xA0000000, 0x00000000, + 0x03A, 0x00001000, + 0xB0000000, 0x00000000, + 0x03B, 0x00080000, + 0x80000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000800, + 0xA0000000, 0x00000000, + 0x03A, 0x00001002, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x00013124, + 0x0EF, 0x00000100, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A1AD, + 0x034, 0x000491AA, + 0x034, 0x000481A7, + 0x034, 0x000470AA, + 0x034, 0x000460A7, + 0x034, 0x00045049, + 0x034, 0x00044046, + 0x034, 0x00043026, + 0x034, 0x00042009, + 0x034, 0x00041006, + 0x034, 0x00040003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3F5, + 0x034, 0x000493F2, + 0x034, 0x000483B0, + 0x034, 0x00047370, + 0x034, 0x0004636D, + 0x034, 0x0004536A, + 0x034, 0x00044349, + 0x034, 0x0004316A, + 0x034, 0x00042167, + 0x034, 0x00041129, + 0x034, 0x00040049, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AF, + 0x034, 0x000483AB, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004406A, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0xA0000000, 0x00000000, + 0x034, 0x0004AFF1, + 0x034, 0x00049FEE, + 0x034, 0x00048FEB, + 0x034, 0x00047FE8, + 0x034, 0x00046DEA, + 0x034, 0x00045DE7, + 0x034, 0x00044CEA, + 0x034, 0x00043CE7, + 0x034, 0x00042C69, + 0x034, 0x00041C66, + 0x034, 0x00040C28, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A1AD, + 0x034, 0x000291AA, + 0x034, 0x000281A7, + 0x034, 0x000270AA, + 0x034, 0x000260A7, + 0x034, 0x00025049, + 0x034, 0x00024046, + 0x034, 0x00023026, + 0x034, 0x00022009, + 0x034, 0x00021006, + 0x034, 0x00020003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3F5, + 0x034, 0x000293F2, + 0x034, 0x000282F1, + 0x034, 0x000272B0, + 0x034, 0x000262AD, + 0x034, 0x000252AA, + 0x034, 0x000242A7, + 0x034, 0x000230EC, + 0x034, 0x000220E9, + 0x034, 0x0002106A, + 0x034, 0x00020067, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0xA0000000, 0x00000000, + 0x034, 0x0002AFF1, + 0x034, 0x00029FEE, + 0x034, 0x00028FEB, + 0x034, 0x00027FE8, + 0x034, 0x00026DEA, + 0x034, 0x00025DE7, + 0x034, 0x00024CEA, + 0x034, 0x00023CE7, + 0x034, 0x00022C69, + 0x034, 0x00021C66, + 0x034, 0x00020C28, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x0000938C, + 0x034, 0x000081AD, + 0x034, 0x000071AA, + 0x034, 0x000061A7, + 0x034, 0x000050AA, + 0x034, 0x000040A7, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x0000100C, + 0x034, 0x00000009, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3F4, + 0x034, 0x000093F1, + 0x034, 0x000082B1, + 0x034, 0x000071D1, + 0x034, 0x000061CE, + 0x034, 0x000051CB, + 0x034, 0x000041C8, + 0x034, 0x000030CB, + 0x034, 0x000020C8, + 0x034, 0x00001087, + 0x034, 0x00000084, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0xA0000000, 0x00000000, + 0x034, 0x0000AFF1, + 0x034, 0x00009FEE, + 0x034, 0x00008FEB, + 0x034, 0x00007FE8, + 0x034, 0x00006DEA, + 0x034, 0x00005DE7, + 0x034, 0x00004CEA, + 0x034, 0x00003CE7, + 0x034, 0x00002C69, + 0x034, 0x00001C66, + 0x034, 0x00000C28, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA1AD, + 0x034, 0x000C91AA, + 0x034, 0x000C81A7, + 0x034, 0x000C70AA, + 0x034, 0x000C60A7, + 0x034, 0x000C5049, + 0x034, 0x000C4046, + 0x034, 0x000C3026, + 0x034, 0x000C2009, + 0x034, 0x000C1006, + 0x034, 0x000C0003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3F5, + 0x034, 0x000C93F2, + 0x034, 0x000C83B0, + 0x034, 0x000C7370, + 0x034, 0x000C636D, + 0x034, 0x000C536A, + 0x034, 0x000C4349, + 0x034, 0x000C316A, + 0x034, 0x000C2167, + 0x034, 0x000C1129, + 0x034, 0x000C0049, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AF, + 0x034, 0x000C83AB, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C406A, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0xA0000000, 0x00000000, + 0x034, 0x000CA794, + 0x034, 0x000C9791, + 0x034, 0x000C878E, + 0x034, 0x000C778B, + 0x034, 0x000C658D, + 0x034, 0x000C558A, + 0x034, 0x000C448D, + 0x034, 0x000C348A, + 0x034, 0x000C244C, + 0x034, 0x000C1449, + 0x034, 0x000C042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA1AD, + 0x034, 0x000A91AA, + 0x034, 0x000A81A7, + 0x034, 0x000A70AA, + 0x034, 0x000A60A7, + 0x034, 0x000A5049, + 0x034, 0x000A4046, + 0x034, 0x000A3026, + 0x034, 0x000A2009, + 0x034, 0x000A1006, + 0x034, 0x000A0003, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3F5, + 0x034, 0x000A93F2, + 0x034, 0x000A82F1, + 0x034, 0x000A72B0, + 0x034, 0x000A62AD, + 0x034, 0x000A52AA, + 0x034, 0x000A42A7, + 0x034, 0x000A30EC, + 0x034, 0x000A20E9, + 0x034, 0x000A106A, + 0x034, 0x000A0067, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0xA0000000, 0x00000000, + 0x034, 0x000AA794, + 0x034, 0x000A9791, + 0x034, 0x000A878E, + 0x034, 0x000A778B, + 0x034, 0x000A658D, + 0x034, 0x000A558A, + 0x034, 0x000A448D, + 0x034, 0x000A348A, + 0x034, 0x000A244C, + 0x034, 0x000A1449, + 0x034, 0x000A042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x0008938C, + 0x034, 0x000881AD, + 0x034, 0x000871AA, + 0x034, 0x000861A7, + 0x034, 0x000850AA, + 0x034, 0x000840A7, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x0008100C, + 0x034, 0x00080009, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3F4, + 0x034, 0x000893F1, + 0x034, 0x000882B1, + 0x034, 0x000871D1, + 0x034, 0x000861CE, + 0x034, 0x000851CB, + 0x034, 0x000841C8, + 0x034, 0x000830CB, + 0x034, 0x000820C8, + 0x034, 0x00081087, + 0x034, 0x00080084, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0xA0000000, 0x00000000, + 0x034, 0x0008A794, + 0x034, 0x00089791, + 0x034, 0x0008878E, + 0x034, 0x0008778B, + 0x034, 0x0008658D, + 0x034, 0x0008558A, + 0x034, 0x0008448D, + 0x034, 0x0008348A, + 0x034, 0x0008244C, + 0x034, 0x00081449, + 0x034, 0x0008042B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0DF, 0x00000001, + 0x018, 0x0001712A, + 0x0EF, 0x00000040, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0xA0000000, 0x00000000, + 0x035, 0x00000747, + 0x035, 0x00008747, + 0x035, 0x00010747, + 0x035, 0x00020747, + 0x035, 0x00028747, + 0x035, 0x00030747, + 0x035, 0x00040747, + 0x035, 0x00048747, + 0x035, 0x00050747, + 0x035, 0x000805FB, + 0x035, 0x000885FB, + 0x035, 0x000905FB, + 0x035, 0x000A05FB, + 0x035, 0x000A85FB, + 0x035, 0x000B05FB, + 0x035, 0x000C05FB, + 0x035, 0x000C85FB, + 0x035, 0x000D05FB, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0DF, 0x00000001, + 0x018, 0x0001712A, + 0x0EF, 0x00000010, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0xA0000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x0EF, 0x00000004, + 0x037, 0x00000000, + 0x038, 0x00005146, + 0x037, 0x00004000, + 0x038, 0x00005146, + 0x037, 0x00008000, + 0x038, 0x00005146, + 0x037, 0x00010000, + 0x038, 0x00005146, + 0x037, 0x00014000, + 0x038, 0x00005146, + 0x037, 0x00018000, + 0x038, 0x00004D4E, + 0x037, 0x0001C000, + 0x038, 0x00004D4E, + 0x037, 0x00020000, + 0x038, 0x00004D4E, + 0x037, 0x00024000, + 0x038, 0x000071C6, + 0x037, 0x00028000, + 0x038, 0x000071C6, + 0x037, 0x0002C000, + 0x038, 0x000071C6, + 0x037, 0x00030000, + 0x038, 0x000071CE, + 0x037, 0x00034000, + 0x038, 0x000071CE, + 0x037, 0x00038000, + 0x038, 0x00005126, + 0x037, 0x0003C000, + 0x038, 0x00005126, + 0x037, 0x00040000, + 0x038, 0x00005126, + 0x037, 0x00044000, + 0x038, 0x00005126, + 0x037, 0x00048000, + 0x038, 0x00005126, + 0x037, 0x00080000, + 0x038, 0x00005ECE, + 0x037, 0x00084000, + 0x038, 0x00005ECE, + 0x037, 0x00088000, + 0x038, 0x00005ECE, + 0x037, 0x00090000, + 0x038, 0x00005ECE, + 0x037, 0x00094000, + 0x038, 0x00005ECE, + 0x037, 0x00098000, + 0x038, 0x00005ECE, + 0x037, 0x0009C000, + 0x038, 0x00005ECE, + 0x037, 0x000A0000, + 0x038, 0x00005ECE, + 0x037, 0x000A4000, + 0x038, 0x00005ECE, + 0x037, 0x000A8000, + 0x038, 0x00005ECE, + 0x037, 0x000AC000, + 0x038, 0x00005ECE, + 0x037, 0x000B0000, + 0x038, 0x00005ECE, + 0x037, 0x000B4000, + 0x038, 0x00005ECE, + 0x037, 0x000B8000, + 0x038, 0x00005ECE, + 0x037, 0x000BC000, + 0x038, 0x00005ECE, + 0x037, 0x000C0000, + 0x038, 0x00005ECE, + 0x037, 0x000C4000, + 0x038, 0x00005ECE, + 0x037, 0x000C8000, + 0x038, 0x00005ECE, + 0x0EF, 0x00000000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000008, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x03C, 0x0000007D, + 0x03C, 0x0000047D, + 0x03C, 0x0000087D, + 0x03C, 0x0000107D, + 0x03C, 0x0000147D, + 0x03C, 0x0000187D, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x0000054A, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x0000154A, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x0000254A, + 0x03C, 0x00002821, + 0xA0000000, 0x00000000, + 0x03C, 0x0000037E, + 0x03C, 0x00000575, + 0x03C, 0x00000971, + 0x03C, 0x0000127E, + 0x03C, 0x00001575, + 0x03C, 0x00001871, + 0x03C, 0x0000217E, + 0x03C, 0x00002575, + 0x03C, 0x00002871, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x061, 0x000C0D47, + 0x062, 0x0000133C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0xA0000000, 0x00000000, + 0x063, 0x0007D0E7, + 0xB0000000, 0x00000000, + 0x064, 0x00014FEC, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0xA0000000, 0x00000000, + 0x065, 0x000933FF, + 0xB0000000, 0x00000000, + 0x066, 0x00000040, + 0x057, 0x00050000, + 0x056, 0x00051DF0, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x055, 0x00082061, + 0xA0000000, 0x00000000, + 0x055, 0x00082060, + 0xB0000000, 0x00000000, + 0x01C, 0x000739D2, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x01F, 0x0002255C, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x01F, 0x0002255C, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x01F, 0x0002255C, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x01F, 0x0002255C, + 0xA0000000, 0x00000000, + 0x01F, 0x0002255C, + 0xB0000000, 0x00000000, + 0x0B1, 0x0007FF48, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0C4, 0x00081700, + 0xA0000000, 0x00000000, + 0x0C4, 0x00083F00, + 0xB0000000, 0x00000000, + 0x018, 0x0001B126, + 0xFFE, 0x00000000, + 0xFFE, 0x00000000, + 0xFFE, 0x00000000, + 0x018, 0x00013126, + 0x018, 0x00013124, +}; + +RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_a, A); + +static const u32 rtw8814a_rf_b[] = { + 0x018, 0x00013124, + 0x040, 0x00000C00, + 0x058, 0x00000F98, + 0x07F, 0x00068004, + 0x018, 0x00000006, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0xA0000000, 0x00000000, + 0x086, 0x000E4B58, + 0x087, 0x00049F80, + 0xB0000000, 0x00000000, + 0x0DF, 0x00000008, + 0x0EF, 0x00002000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F39B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F39B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017BC3, + 0xA0000000, 0x00000000, + 0x03B, 0x0003F258, + 0x03B, 0x00030A58, + 0x03B, 0x0002FA58, + 0x03B, 0x00022590, + 0x03B, 0x0001FA50, + 0x03B, 0x00010248, + 0x03B, 0x00008240, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0000ADF6, + 0x034, 0x00009DF3, + 0x034, 0x00008DF0, + 0x034, 0x00007DED, + 0x034, 0x00006DEA, + 0x034, 0x00005CED, + 0x034, 0x00004CEA, + 0x034, 0x000034EA, + 0x034, 0x000024E7, + 0x034, 0x0000146A, + 0x034, 0x0000006B, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0008ADF6, + 0x034, 0x00089DF3, + 0x034, 0x00088DF0, + 0x034, 0x00087DED, + 0x034, 0x00086DEA, + 0x034, 0x00085CED, + 0x034, 0x00084CEA, + 0x034, 0x000834EA, + 0x034, 0x000824E7, + 0x034, 0x0008146A, + 0x034, 0x0008006B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x000020A2, + 0x0DF, 0x00000080, + 0x035, 0x00000192, + 0x035, 0x00008192, + 0x035, 0x00010192, + 0x036, 0x00000024, + 0x036, 0x00008024, + 0x036, 0x00010024, + 0x036, 0x00018024, + 0x0EF, 0x00000000, + 0x051, 0x00000C21, + 0x052, 0x000006D9, + 0x053, 0x000FC649, + 0x054, 0x0000017E, + 0x018, 0x0001012A, + 0x081, 0x0007FC00, + 0x089, 0x00050110, + 0x08A, 0x00043E50, + 0x08B, 0x0002E180, + 0x08C, 0x00093C3C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0xA0000000, 0x00000000, + 0x085, 0x000F8000, + 0xB0000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x0EF, 0x00001000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x00040000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00030023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00028623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00021633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0001C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00010293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00009593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x00000F8B, + 0xA0000000, 0x00000000, + 0x03B, 0x0000078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00070023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00068623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00061633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0005C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00050293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00049593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x03B, 0x0004078B, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0004C000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0004C000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00004000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B0023, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A8623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A1633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0009C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00090293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00089593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0008138B, + 0xA0000000, 0x00000000, + 0x03B, 0x0008078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000800, + 0x03B, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0xA0000000, 0x00000000, + 0x03A, 0x00000803, + 0xB0000000, 0x00000000, + 0x03B, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000800, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000800, + 0xA0000000, 0x00000000, + 0x03A, 0x00001000, + 0xB0000000, 0x00000000, + 0x03B, 0x00080000, + 0x80000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0xA0000000, 0x00000000, + 0x03A, 0x00001002, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x00013124, + 0x0EF, 0x00000100, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x000491AD, + 0x034, 0x000481AA, + 0x034, 0x000471A7, + 0x034, 0x000460AA, + 0x034, 0x000450A7, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x0004200C, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x00049389, + 0x034, 0x0004816D, + 0x034, 0x0004716A, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38B, + 0x034, 0x00049388, + 0x034, 0x0004818B, + 0x034, 0x00047188, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x00049389, + 0x034, 0x0004816D, + 0x034, 0x0004716A, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38B, + 0x034, 0x00049388, + 0x034, 0x0004818B, + 0x034, 0x00047188, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3F5, + 0x034, 0x000493F3, + 0x034, 0x000483B2, + 0x034, 0x00047390, + 0x034, 0x0004638D, + 0x034, 0x0004538A, + 0x034, 0x00044387, + 0x034, 0x0004324A, + 0x034, 0x00042247, + 0x034, 0x0004104D, + 0x034, 0x0004004A, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004AFF7, + 0x034, 0x00049FF6, + 0x034, 0x00048FF3, + 0x034, 0x00047FF0, + 0x034, 0x00046FED, + 0x034, 0x00045FEA, + 0x034, 0x00044FE7, + 0x034, 0x00043DEA, + 0x034, 0x00042DE7, + 0x034, 0x00041DE4, + 0x034, 0x00040CE7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x00049389, + 0x034, 0x0004816D, + 0x034, 0x0004716A, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x00049389, + 0x034, 0x0004816D, + 0x034, 0x0004716A, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0xA0000000, 0x00000000, + 0x034, 0x0004AFF4, + 0x034, 0x00049FF1, + 0x034, 0x00048FEE, + 0x034, 0x00047FEB, + 0x034, 0x00046FE8, + 0x034, 0x00045DEA, + 0x034, 0x00044CED, + 0x034, 0x00043CEA, + 0x034, 0x00042C6C, + 0x034, 0x00041C69, + 0x034, 0x00040C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A38C, + 0x034, 0x000291AD, + 0x034, 0x000281AA, + 0x034, 0x000271A7, + 0x034, 0x000260AA, + 0x034, 0x000250A7, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x0002200C, + 0x034, 0x00021009, + 0x034, 0x00020006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293AC, + 0x034, 0x00028389, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AD, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293AC, + 0x034, 0x00028389, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EF, + 0x034, 0x000293AD, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3F5, + 0x034, 0x000293F3, + 0x034, 0x000283D0, + 0x034, 0x00027371, + 0x034, 0x0002636E, + 0x034, 0x0002536B, + 0x034, 0x00024368, + 0x034, 0x0002332A, + 0x034, 0x00022327, + 0x034, 0x0002104C, + 0x034, 0x00020049, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002AFF7, + 0x034, 0x00029FF6, + 0x034, 0x00028FF3, + 0x034, 0x00027FF0, + 0x034, 0x00026FED, + 0x034, 0x00025FEA, + 0x034, 0x00024FE7, + 0x034, 0x00023DEA, + 0x034, 0x00022DE7, + 0x034, 0x00021DE4, + 0x034, 0x00020F25, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293AC, + 0x034, 0x00028389, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293AC, + 0x034, 0x00028389, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0xA0000000, 0x00000000, + 0x034, 0x0002AFF4, + 0x034, 0x00029FF1, + 0x034, 0x00028FEE, + 0x034, 0x00027FEB, + 0x034, 0x00026FE8, + 0x034, 0x00025DEA, + 0x034, 0x00024CED, + 0x034, 0x00023CEA, + 0x034, 0x00022C6C, + 0x034, 0x00021C69, + 0x034, 0x00020C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A38C, + 0x034, 0x000091AD, + 0x034, 0x000081AA, + 0x034, 0x000071A7, + 0x034, 0x000060AA, + 0x034, 0x000050A7, + 0x034, 0x0000402C, + 0x034, 0x00003029, + 0x034, 0x00002026, + 0x034, 0x00001009, + 0x034, 0x00000006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x000093AC, + 0x034, 0x000081EC, + 0x034, 0x0000716D, + 0x034, 0x0000616A, + 0x034, 0x0000506D, + 0x034, 0x0000404C, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EF, + 0x034, 0x000093AD, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x000093AC, + 0x034, 0x000081EC, + 0x034, 0x0000716D, + 0x034, 0x0000616A, + 0x034, 0x0000506D, + 0x034, 0x0000404C, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EF, + 0x034, 0x000093AD, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3F4, + 0x034, 0x000093F0, + 0x034, 0x000083AE, + 0x034, 0x00007350, + 0x034, 0x0000634D, + 0x034, 0x0000534A, + 0x034, 0x00004347, + 0x034, 0x0000312D, + 0x034, 0x0000212A, + 0x034, 0x00001127, + 0x034, 0x0000002A, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000AFF7, + 0x034, 0x00009FF4, + 0x034, 0x00008FF1, + 0x034, 0x00007FEE, + 0x034, 0x00006FEB, + 0x034, 0x00005FE8, + 0x034, 0x00004DEB, + 0x034, 0x00003DE8, + 0x034, 0x00002DE5, + 0x034, 0x00001C8B, + 0x034, 0x00000C88, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x000093AC, + 0x034, 0x000081EC, + 0x034, 0x0000716D, + 0x034, 0x0000616A, + 0x034, 0x0000506D, + 0x034, 0x0000404C, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EC, + 0x034, 0x000093AC, + 0x034, 0x000081EC, + 0x034, 0x0000716D, + 0x034, 0x0000616A, + 0x034, 0x0000506D, + 0x034, 0x0000404C, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0xA0000000, 0x00000000, + 0x034, 0x0000AFF4, + 0x034, 0x00009FF1, + 0x034, 0x00008FEE, + 0x034, 0x00007FEB, + 0x034, 0x00006FE8, + 0x034, 0x00005DEA, + 0x034, 0x00004CED, + 0x034, 0x00003CEA, + 0x034, 0x00002C6C, + 0x034, 0x00001C69, + 0x034, 0x00000C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C91AD, + 0x034, 0x000C81AA, + 0x034, 0x000C71A7, + 0x034, 0x000C60AA, + 0x034, 0x000C50A7, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C200C, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C9389, + 0x034, 0x000C816D, + 0x034, 0x000C716A, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38B, + 0x034, 0x000C9388, + 0x034, 0x000C818B, + 0x034, 0x000C7188, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C9389, + 0x034, 0x000C816D, + 0x034, 0x000C716A, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38B, + 0x034, 0x000C9388, + 0x034, 0x000C818B, + 0x034, 0x000C7188, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3F5, + 0x034, 0x000C93F3, + 0x034, 0x000C83B2, + 0x034, 0x000C7390, + 0x034, 0x000C638D, + 0x034, 0x000C538A, + 0x034, 0x000C4387, + 0x034, 0x000C324A, + 0x034, 0x000C2247, + 0x034, 0x000C104D, + 0x034, 0x000C004A, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CAFF7, + 0x034, 0x000C9FF6, + 0x034, 0x000C8FF3, + 0x034, 0x000C7FF0, + 0x034, 0x000C6FED, + 0x034, 0x000C5FEA, + 0x034, 0x000C4FE7, + 0x034, 0x000C3DEA, + 0x034, 0x000C2DE7, + 0x034, 0x000C1DE4, + 0x034, 0x000C0CE7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C9389, + 0x034, 0x000C816D, + 0x034, 0x000C716A, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C9389, + 0x034, 0x000C816D, + 0x034, 0x000C716A, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0xA0000000, 0x00000000, + 0x034, 0x000CA794, + 0x034, 0x000C9791, + 0x034, 0x000C878E, + 0x034, 0x000C778B, + 0x034, 0x000C658D, + 0x034, 0x000C558A, + 0x034, 0x000C448D, + 0x034, 0x000C348A, + 0x034, 0x000C244C, + 0x034, 0x000C1449, + 0x034, 0x000C042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA38C, + 0x034, 0x000A91AD, + 0x034, 0x000A81AA, + 0x034, 0x000A71A7, + 0x034, 0x000A60AA, + 0x034, 0x000A50A7, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A200C, + 0x034, 0x000A1009, + 0x034, 0x000A0006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93AC, + 0x034, 0x000A8389, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AD, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93AC, + 0x034, 0x000A8389, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EF, + 0x034, 0x000A93AD, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3F5, + 0x034, 0x000A93F3, + 0x034, 0x000A83D0, + 0x034, 0x000A7371, + 0x034, 0x000A636E, + 0x034, 0x000A536B, + 0x034, 0x000A4368, + 0x034, 0x000A332A, + 0x034, 0x000A2327, + 0x034, 0x000A104C, + 0x034, 0x000A0049, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AAFF7, + 0x034, 0x000A9FF6, + 0x034, 0x000A8FF3, + 0x034, 0x000A7FF0, + 0x034, 0x000A6FED, + 0x034, 0x000A5FEA, + 0x034, 0x000A4FE7, + 0x034, 0x000A3DEA, + 0x034, 0x000A2DE7, + 0x034, 0x000A1DE4, + 0x034, 0x000A0F25, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93AC, + 0x034, 0x000A8389, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93AC, + 0x034, 0x000A8389, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0xA0000000, 0x00000000, + 0x034, 0x000AA794, + 0x034, 0x000A9791, + 0x034, 0x000A878E, + 0x034, 0x000A778B, + 0x034, 0x000A658D, + 0x034, 0x000A558A, + 0x034, 0x000A448D, + 0x034, 0x000A348A, + 0x034, 0x000A244C, + 0x034, 0x000A1449, + 0x034, 0x000A042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A38C, + 0x034, 0x000891AD, + 0x034, 0x000881AA, + 0x034, 0x000871A7, + 0x034, 0x000860AA, + 0x034, 0x000850A7, + 0x034, 0x0008402C, + 0x034, 0x00083029, + 0x034, 0x00082026, + 0x034, 0x00081009, + 0x034, 0x00080006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x000893AC, + 0x034, 0x000881EC, + 0x034, 0x0008716D, + 0x034, 0x0008616A, + 0x034, 0x0008506D, + 0x034, 0x0008404C, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EF, + 0x034, 0x000893AD, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x000893AC, + 0x034, 0x000881EC, + 0x034, 0x0008716D, + 0x034, 0x0008616A, + 0x034, 0x0008506D, + 0x034, 0x0008404C, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EF, + 0x034, 0x000893AD, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3F4, + 0x034, 0x000893F0, + 0x034, 0x000883AE, + 0x034, 0x00087350, + 0x034, 0x0008634D, + 0x034, 0x0008534A, + 0x034, 0x00084347, + 0x034, 0x0008312D, + 0x034, 0x0008212A, + 0x034, 0x00081127, + 0x034, 0x0008002A, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008AFF7, + 0x034, 0x00089FF4, + 0x034, 0x00088FF1, + 0x034, 0x00087FEE, + 0x034, 0x00086FEB, + 0x034, 0x00085FE8, + 0x034, 0x00084DEB, + 0x034, 0x00083DE8, + 0x034, 0x00082DE5, + 0x034, 0x00081C8B, + 0x034, 0x00080C88, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x000893AC, + 0x034, 0x000881EC, + 0x034, 0x0008716D, + 0x034, 0x0008616A, + 0x034, 0x0008506D, + 0x034, 0x0008404C, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EC, + 0x034, 0x000893AC, + 0x034, 0x000881EC, + 0x034, 0x0008716D, + 0x034, 0x0008616A, + 0x034, 0x0008506D, + 0x034, 0x0008404C, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0xA0000000, 0x00000000, + 0x034, 0x0008A794, + 0x034, 0x00089791, + 0x034, 0x0008878E, + 0x034, 0x0008778B, + 0x034, 0x0008658D, + 0x034, 0x0008558A, + 0x034, 0x0008448D, + 0x034, 0x0008348A, + 0x034, 0x0008244C, + 0x034, 0x00081449, + 0x034, 0x0008042B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000040, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0xA0000000, 0x00000000, + 0x035, 0x00000484, + 0x035, 0x00008484, + 0x035, 0x00010484, + 0x035, 0x00020584, + 0x035, 0x00028584, + 0x035, 0x00030584, + 0x035, 0x00040584, + 0x035, 0x00048584, + 0x035, 0x00050584, + 0x035, 0x000805FB, + 0x035, 0x000885FB, + 0x035, 0x000905FB, + 0x035, 0x000A05FB, + 0x035, 0x000A85FB, + 0x035, 0x000B05FB, + 0x035, 0x000C05FB, + 0x035, 0x000C85FB, + 0x035, 0x000D05FB, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000010, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0xA0000000, 0x00000000, + 0x036, 0x00000474, + 0x036, 0x00008474, + 0x036, 0x00010474, + 0x036, 0x00020474, + 0x036, 0x00028474, + 0x036, 0x00030474, + 0x036, 0x00040474, + 0x036, 0x00048474, + 0x036, 0x00050474, + 0x036, 0x00080474, + 0x036, 0x00088474, + 0x036, 0x00090474, + 0x036, 0x000A0474, + 0x036, 0x000A8474, + 0x036, 0x000B0474, + 0x036, 0x000C0474, + 0x036, 0x000C8474, + 0x036, 0x000D0474, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x0EF, 0x00000004, + 0x037, 0x00000000, + 0x038, 0x0000514E, + 0x037, 0x00004000, + 0x038, 0x0000514E, + 0x037, 0x00008000, + 0x038, 0x0000514E, + 0x037, 0x00010000, + 0x038, 0x0000514E, + 0x037, 0x00014000, + 0x038, 0x0000514E, + 0x037, 0x00018000, + 0x038, 0x0000514E, + 0x037, 0x0001C000, + 0x038, 0x0000514E, + 0x037, 0x00020000, + 0x038, 0x0000514E, + 0x037, 0x00024000, + 0x038, 0x0000514E, + 0x037, 0x00028000, + 0x038, 0x0000514E, + 0x037, 0x0002C000, + 0x038, 0x0000714E, + 0x037, 0x00030000, + 0x038, 0x0000514E, + 0x037, 0x00034000, + 0x038, 0x0000514E, + 0x037, 0x00038000, + 0x038, 0x0000514E, + 0x037, 0x0003C000, + 0x038, 0x0000514E, + 0x037, 0x00040000, + 0x038, 0x0000514E, + 0x037, 0x00044000, + 0x038, 0x0000514E, + 0x037, 0x00048000, + 0x038, 0x0000514E, + 0x037, 0x00080000, + 0x038, 0x00005ECE, + 0x037, 0x00084000, + 0x038, 0x00005ECE, + 0x037, 0x00088000, + 0x038, 0x00005ECE, + 0x037, 0x00090000, + 0x038, 0x00005ECE, + 0x037, 0x00094000, + 0x038, 0x00005ECE, + 0x037, 0x00098000, + 0x038, 0x00005ECE, + 0x037, 0x0009C000, + 0x038, 0x00005ECE, + 0x037, 0x000A0000, + 0x038, 0x00005ECE, + 0x037, 0x000A4000, + 0x038, 0x00005ECE, + 0x037, 0x000A8000, + 0x038, 0x00005ECE, + 0x037, 0x000AC000, + 0x038, 0x00005ECE, + 0x037, 0x000B0000, + 0x038, 0x00005ECE, + 0x037, 0x000B4000, + 0x038, 0x00005ECE, + 0x037, 0x000B8000, + 0x038, 0x00005ECE, + 0x037, 0x000BC000, + 0x038, 0x00005ECE, + 0x037, 0x000C0000, + 0x038, 0x00005ECE, + 0x037, 0x000C4000, + 0x038, 0x00005ECE, + 0x037, 0x000C8000, + 0x038, 0x00005ECE, + 0x0EF, 0x00000000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000008, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x03C, 0x0000007D, + 0x03C, 0x0000047D, + 0x03C, 0x0000087D, + 0x03C, 0x0000107D, + 0x03C, 0x0000147D, + 0x03C, 0x0000187D, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027E, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127E, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227E, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0xA0000000, 0x00000000, + 0x03C, 0x0000037E, + 0x03C, 0x00000575, + 0x03C, 0x00000971, + 0x03C, 0x0000127E, + 0x03C, 0x00001575, + 0x03C, 0x00001871, + 0x03C, 0x0000217E, + 0x03C, 0x00002575, + 0x03C, 0x00002871, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x061, 0x000C0D47, + 0x062, 0x0000133C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0xA0000000, 0x00000000, + 0x063, 0x0007D0E7, + 0xB0000000, 0x00000000, + 0x064, 0x00014FEC, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0xA0000000, 0x00000000, + 0x065, 0x000923FF, + 0xB0000000, 0x00000000, + 0x066, 0x00000040, + 0x057, 0x00050000, + 0x056, 0x00051DF0, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x055, 0x00082060, + 0xB0000000, 0x00000000, +}; + +RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_b, B); + +static const u32 rtw8814a_rf_c[] = { + 0x018, 0x00013124, + 0x040, 0x00000C00, + 0x058, 0x00000F98, + 0x07F, 0x00068004, + 0x018, 0x00000006, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0xA0000000, 0x00000000, + 0x086, 0x000E4B58, + 0x087, 0x00049F80, + 0xB0000000, 0x00000000, + 0x0DF, 0x00000008, + 0x0EF, 0x00002000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017823, + 0xA0000000, 0x00000000, + 0x03B, 0x0003F258, + 0x03B, 0x00030A58, + 0x03B, 0x0002FA58, + 0x03B, 0x00022590, + 0x03B, 0x0001FA50, + 0x03B, 0x00010248, + 0x03B, 0x00008240, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0000ADF6, + 0x034, 0x00009DF3, + 0x034, 0x00008DF0, + 0x034, 0x00007DED, + 0x034, 0x00006DEA, + 0x034, 0x00005CED, + 0x034, 0x00004CEA, + 0x034, 0x000034EA, + 0x034, 0x000024E7, + 0x034, 0x0000146A, + 0x034, 0x0000006B, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0008ADF6, + 0x034, 0x00089DF3, + 0x034, 0x00088DF0, + 0x034, 0x00087DED, + 0x034, 0x00086DEA, + 0x034, 0x00085CED, + 0x034, 0x00084CEA, + 0x034, 0x000834EA, + 0x034, 0x000824E7, + 0x034, 0x0008146A, + 0x034, 0x0008006B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x000020A2, + 0x0DF, 0x00000080, + 0x035, 0x00000192, + 0x035, 0x00008192, + 0x035, 0x00010192, + 0x036, 0x00000024, + 0x036, 0x00008024, + 0x036, 0x00010024, + 0x036, 0x00018024, + 0x0EF, 0x00000000, + 0x051, 0x00000C21, + 0x052, 0x000006D9, + 0x053, 0x000FC649, + 0x054, 0x0000017E, + 0x018, 0x0001012A, + 0x081, 0x0007FC00, + 0x089, 0x00050110, + 0x08A, 0x00043E50, + 0x08B, 0x0002E180, + 0x08C, 0x00093C3C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0xA0000000, 0x00000000, + 0x085, 0x000F8000, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xA0000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xB0000000, 0x00000000, + 0x0EF, 0x00001000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0006C000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x000D4000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0006C000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0008C000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00004000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x000A0000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00030023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00028623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00021633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0001C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00010293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00009593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0000118B, + 0xA0000000, 0x00000000, + 0x03B, 0x0000078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0004C000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00084000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0004C000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x000D0000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0xA0000000, 0x00000000, + 0x03C, 0x00028000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00070023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00068623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00061633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0005C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00050293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00049593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x03B, 0x0004078B, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00080000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B0023, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A8623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A1633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0009C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00090293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00089593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0008128B, + 0xA0000000, 0x00000000, + 0x03B, 0x0008078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000800, + 0x03B, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001803, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0xA0000000, 0x00000000, + 0x03A, 0x00000803, + 0xB0000000, 0x00000000, + 0x03B, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000800, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0xA0000000, 0x00000000, + 0x03A, 0x00001000, + 0xB0000000, 0x00000000, + 0x03B, 0x00080000, + 0x80000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0xA0000000, 0x00000000, + 0x03A, 0x00001002, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x018, 0x00013124, + 0xA0000000, 0x00000000, + 0x018, 0x00013124, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A38C, + 0x034, 0x000491AD, + 0x034, 0x000481AA, + 0x034, 0x000471A7, + 0x034, 0x000460AA, + 0x034, 0x000450A7, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x0004200C, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3F5, + 0x034, 0x000493F3, + 0x034, 0x00048393, + 0x034, 0x00047390, + 0x034, 0x0004638D, + 0x034, 0x0004538A, + 0x034, 0x00044387, + 0x034, 0x000430ED, + 0x034, 0x000420EA, + 0x034, 0x000410E7, + 0x034, 0x0004002D, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004AFF7, + 0x034, 0x00049FF6, + 0x034, 0x00048FF3, + 0x034, 0x00047FF0, + 0x034, 0x00046FED, + 0x034, 0x00045FEA, + 0x034, 0x00044FE7, + 0x034, 0x00043CD0, + 0x034, 0x00042CCD, + 0x034, 0x00041CCA, + 0x034, 0x00040CC7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EF, + 0x034, 0x000493AD, + 0x034, 0x0004838A, + 0x034, 0x0004718C, + 0x034, 0x00046189, + 0x034, 0x0004506D, + 0x034, 0x0004404C, + 0x034, 0x0004302C, + 0x034, 0x00042029, + 0x034, 0x00041026, + 0x034, 0x00040023, + 0xA0000000, 0x00000000, + 0x034, 0x0004AFF4, + 0x034, 0x00049FF1, + 0x034, 0x00048FEE, + 0x034, 0x00047FEB, + 0x034, 0x00046FE8, + 0x034, 0x00045DEA, + 0x034, 0x00044CED, + 0x034, 0x00043CEA, + 0x034, 0x00042C6C, + 0x034, 0x00041C69, + 0x034, 0x00040C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002938C, + 0x034, 0x000281AD, + 0x034, 0x000271AA, + 0x034, 0x000261A7, + 0x034, 0x000250AA, + 0x034, 0x000240A7, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x0002100C, + 0x034, 0x00020009, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002936D, + 0x034, 0x0002836A, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002936D, + 0x034, 0x0002836A, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x000293AC, + 0x034, 0x0002838A, + 0x034, 0x0002718C, + 0x034, 0x00026189, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3F5, + 0x034, 0x000293F3, + 0x034, 0x000282F2, + 0x034, 0x000272D0, + 0x034, 0x000262CD, + 0x034, 0x000252CA, + 0x034, 0x000242C7, + 0x034, 0x000230CD, + 0x034, 0x000220CA, + 0x034, 0x000210C7, + 0x034, 0x00020086, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002AFF7, + 0x034, 0x00029FF6, + 0x034, 0x00028FF3, + 0x034, 0x00027FF0, + 0x034, 0x00026FED, + 0x034, 0x00025FEA, + 0x034, 0x00024FE7, + 0x034, 0x00023DEA, + 0x034, 0x00022DE7, + 0x034, 0x00021DE4, + 0x034, 0x00020E44, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002936D, + 0x034, 0x0002836A, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EC, + 0x034, 0x0002936D, + 0x034, 0x0002836A, + 0x034, 0x0002716D, + 0x034, 0x0002616A, + 0x034, 0x0002506D, + 0x034, 0x0002406A, + 0x034, 0x0002302C, + 0x034, 0x00022029, + 0x034, 0x00021026, + 0x034, 0x00020023, + 0xA0000000, 0x00000000, + 0x034, 0x0002AFF4, + 0x034, 0x00029FF1, + 0x034, 0x00028FEE, + 0x034, 0x00027FEB, + 0x034, 0x00026FE8, + 0x034, 0x00025DEA, + 0x034, 0x00024CED, + 0x034, 0x00023CEA, + 0x034, 0x00022C6C, + 0x034, 0x00021C69, + 0x034, 0x00020C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A38C, + 0x034, 0x000091AD, + 0x034, 0x000081AA, + 0x034, 0x000071A7, + 0x034, 0x000060AA, + 0x034, 0x000050A7, + 0x034, 0x0000402C, + 0x034, 0x00003029, + 0x034, 0x0000200C, + 0x034, 0x00001009, + 0x034, 0x00000006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3F5, + 0x034, 0x000093F1, + 0x034, 0x000083B0, + 0x034, 0x00007370, + 0x034, 0x0000636D, + 0x034, 0x0000536A, + 0x034, 0x00004367, + 0x034, 0x0000308E, + 0x034, 0x0000208B, + 0x034, 0x00001088, + 0x034, 0x00000085, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000AFF7, + 0x034, 0x00009FF5, + 0x034, 0x00008FF2, + 0x034, 0x00007FEF, + 0x034, 0x00006FEC, + 0x034, 0x00005FE9, + 0x034, 0x00004EAA, + 0x034, 0x00003EA7, + 0x034, 0x00002C70, + 0x034, 0x00001C6D, + 0x034, 0x00000C6A, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AB, + 0x034, 0x00008389, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0xA0000000, 0x00000000, + 0x034, 0x0000AFF4, + 0x034, 0x00009FF1, + 0x034, 0x00008FEE, + 0x034, 0x00007FEB, + 0x034, 0x00006FE8, + 0x034, 0x00005DEA, + 0x034, 0x00004CED, + 0x034, 0x00003CEA, + 0x034, 0x00002C6C, + 0x034, 0x00001C69, + 0x034, 0x00000C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA38C, + 0x034, 0x000C91AD, + 0x034, 0x000C81AA, + 0x034, 0x000C71A7, + 0x034, 0x000C60AA, + 0x034, 0x000C50A7, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C200C, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3F5, + 0x034, 0x000C93F3, + 0x034, 0x000C8393, + 0x034, 0x000C7390, + 0x034, 0x000C638D, + 0x034, 0x000C538A, + 0x034, 0x000C4387, + 0x034, 0x000C30ED, + 0x034, 0x000C20EA, + 0x034, 0x000C10E7, + 0x034, 0x000C002D, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CAFF7, + 0x034, 0x000C9FF6, + 0x034, 0x000C8FF3, + 0x034, 0x000C7FF0, + 0x034, 0x000C6FED, + 0x034, 0x000C5FEA, + 0x034, 0x000C4FE7, + 0x034, 0x000C3CD0, + 0x034, 0x000C2CCD, + 0x034, 0x000C1CCA, + 0x034, 0x000C0CC7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EF, + 0x034, 0x000C93AD, + 0x034, 0x000C838A, + 0x034, 0x000C718C, + 0x034, 0x000C6189, + 0x034, 0x000C506D, + 0x034, 0x000C404C, + 0x034, 0x000C302C, + 0x034, 0x000C2029, + 0x034, 0x000C1026, + 0x034, 0x000C0023, + 0xA0000000, 0x00000000, + 0x034, 0x000CA794, + 0x034, 0x000C9791, + 0x034, 0x000C878E, + 0x034, 0x000C778B, + 0x034, 0x000C658D, + 0x034, 0x000C558A, + 0x034, 0x000C448D, + 0x034, 0x000C348A, + 0x034, 0x000C244C, + 0x034, 0x000C1449, + 0x034, 0x000C042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A938C, + 0x034, 0x000A81AD, + 0x034, 0x000A71AA, + 0x034, 0x000A61A7, + 0x034, 0x000A50AA, + 0x034, 0x000A40A7, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A100C, + 0x034, 0x000A0009, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A936D, + 0x034, 0x000A836A, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A936D, + 0x034, 0x000A836A, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A93AC, + 0x034, 0x000A838A, + 0x034, 0x000A718C, + 0x034, 0x000A6189, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3F5, + 0x034, 0x000A93F3, + 0x034, 0x000A82F2, + 0x034, 0x000A72D0, + 0x034, 0x000A62CD, + 0x034, 0x000A52CA, + 0x034, 0x000A42C7, + 0x034, 0x000A30CD, + 0x034, 0x000A20CA, + 0x034, 0x000A10C7, + 0x034, 0x000A0086, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AAFF7, + 0x034, 0x000A9FF6, + 0x034, 0x000A8FF3, + 0x034, 0x000A7FF0, + 0x034, 0x000A6FED, + 0x034, 0x000A5FEA, + 0x034, 0x000A4FE7, + 0x034, 0x000A3DEA, + 0x034, 0x000A2DE7, + 0x034, 0x000A1DE4, + 0x034, 0x000A0E44, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A936D, + 0x034, 0x000A836A, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EC, + 0x034, 0x000A936D, + 0x034, 0x000A836A, + 0x034, 0x000A716D, + 0x034, 0x000A616A, + 0x034, 0x000A506D, + 0x034, 0x000A406A, + 0x034, 0x000A302C, + 0x034, 0x000A2029, + 0x034, 0x000A1026, + 0x034, 0x000A0023, + 0xA0000000, 0x00000000, + 0x034, 0x000AA794, + 0x034, 0x000A9791, + 0x034, 0x000A878E, + 0x034, 0x000A778B, + 0x034, 0x000A658D, + 0x034, 0x000A558A, + 0x034, 0x000A448D, + 0x034, 0x000A348A, + 0x034, 0x000A244C, + 0x034, 0x000A1449, + 0x034, 0x000A042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A38C, + 0x034, 0x000891AD, + 0x034, 0x000881AA, + 0x034, 0x000871A7, + 0x034, 0x000860AA, + 0x034, 0x000850A7, + 0x034, 0x0008402C, + 0x034, 0x00083029, + 0x034, 0x0008200C, + 0x034, 0x00081009, + 0x034, 0x00000006, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3F5, + 0x034, 0x000893F1, + 0x034, 0x000883B0, + 0x034, 0x00087370, + 0x034, 0x0008636D, + 0x034, 0x0008536A, + 0x034, 0x00084367, + 0x034, 0x0008308E, + 0x034, 0x0008208B, + 0x034, 0x00081088, + 0x034, 0x00080085, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008AFF7, + 0x034, 0x00089FF5, + 0x034, 0x00088FF2, + 0x034, 0x00087FEF, + 0x034, 0x00086FEC, + 0x034, 0x00085FE9, + 0x034, 0x00084EAA, + 0x034, 0x00083EA7, + 0x034, 0x00082C70, + 0x034, 0x00081C6D, + 0x034, 0x00080C6A, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AB, + 0x034, 0x00088389, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0xA0000000, 0x00000000, + 0x034, 0x0008A794, + 0x034, 0x00089791, + 0x034, 0x0008878E, + 0x034, 0x0008778B, + 0x034, 0x0008658D, + 0x034, 0x0008558A, + 0x034, 0x0008448D, + 0x034, 0x0008348A, + 0x034, 0x0008244C, + 0x034, 0x00081449, + 0x034, 0x0008042B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000040, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0xA0000000, 0x00000000, + 0x035, 0x00000484, + 0x035, 0x00008484, + 0x035, 0x00010484, + 0x035, 0x00020584, + 0x035, 0x00028584, + 0x035, 0x00030584, + 0x035, 0x00040584, + 0x035, 0x00048584, + 0x035, 0x00050584, + 0x035, 0x000805FB, + 0x035, 0x000885FB, + 0x035, 0x000905FB, + 0x035, 0x000A05FB, + 0x035, 0x000A85FB, + 0x035, 0x000B05FB, + 0x035, 0x000C05FB, + 0x035, 0x000C85FB, + 0x035, 0x000D05FB, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000010, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0xA0000000, 0x00000000, + 0x036, 0x00000474, + 0x036, 0x00008474, + 0x036, 0x00010474, + 0x036, 0x00020474, + 0x036, 0x00028474, + 0x036, 0x00030474, + 0x036, 0x00040474, + 0x036, 0x00048474, + 0x036, 0x00050474, + 0x036, 0x00080474, + 0x036, 0x00088474, + 0x036, 0x00090474, + 0x036, 0x000A0474, + 0x036, 0x000A8474, + 0x036, 0x000B0474, + 0x036, 0x000C0474, + 0x036, 0x000C8474, + 0x036, 0x000D0474, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x0EF, 0x00000004, + 0x037, 0x00000000, + 0x038, 0x0000514E, + 0x037, 0x00004000, + 0x038, 0x0000514E, + 0x037, 0x00008000, + 0x038, 0x0000514E, + 0x037, 0x00010000, + 0x038, 0x0000514E, + 0x037, 0x00014000, + 0x038, 0x0000514E, + 0x037, 0x00018000, + 0x038, 0x0000514E, + 0x037, 0x0001C000, + 0x038, 0x0000514E, + 0x037, 0x00020000, + 0x038, 0x0000514E, + 0x037, 0x00024000, + 0x038, 0x0000514E, + 0x037, 0x00028000, + 0x038, 0x0000514E, + 0x037, 0x0002C000, + 0x038, 0x0000714E, + 0x037, 0x00030000, + 0x038, 0x0000514E, + 0x037, 0x00034000, + 0x038, 0x0000514E, + 0x037, 0x00038000, + 0x038, 0x0000514E, + 0x037, 0x0003C000, + 0x038, 0x0000514E, + 0x037, 0x00040000, + 0x038, 0x0000514E, + 0x037, 0x00044000, + 0x038, 0x0000514E, + 0x037, 0x00048000, + 0x038, 0x0000514E, + 0x037, 0x00080000, + 0x038, 0x00005ECE, + 0x037, 0x00084000, + 0x038, 0x00005ECE, + 0x037, 0x00088000, + 0x038, 0x00005ECE, + 0x037, 0x00090000, + 0x038, 0x00005ECE, + 0x037, 0x00094000, + 0x038, 0x00005ECE, + 0x037, 0x00098000, + 0x038, 0x00005ECE, + 0x037, 0x0009C000, + 0x038, 0x00005ECE, + 0x037, 0x000A0000, + 0x038, 0x00005ECE, + 0x037, 0x000A4000, + 0x038, 0x00005ECE, + 0x037, 0x000A8000, + 0x038, 0x00005ECE, + 0x037, 0x000AC000, + 0x038, 0x00005ECE, + 0x037, 0x000B0000, + 0x038, 0x00005ECE, + 0x037, 0x000B4000, + 0x038, 0x00005ECE, + 0x037, 0x000B8000, + 0x038, 0x00005ECE, + 0x037, 0x000BC000, + 0x038, 0x00005ECE, + 0x037, 0x000C0000, + 0x038, 0x00005ECE, + 0x037, 0x000C4000, + 0x038, 0x00005ECE, + 0x037, 0x000C8000, + 0x038, 0x00005ECE, + 0x0EF, 0x00000000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000008, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x03C, 0x0000007D, + 0x03C, 0x0000047D, + 0x03C, 0x0000087D, + 0x03C, 0x0000107D, + 0x03C, 0x0000147D, + 0x03C, 0x0000187D, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000541, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001541, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002541, + 0x03C, 0x00002821, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027D, + 0x03C, 0x00000546, + 0x03C, 0x00000821, + 0x03C, 0x0000127D, + 0x03C, 0x00001546, + 0x03C, 0x00001821, + 0x03C, 0x0000227D, + 0x03C, 0x00002546, + 0x03C, 0x00002821, + 0xA0000000, 0x00000000, + 0x03C, 0x0000037E, + 0x03C, 0x00000575, + 0x03C, 0x00000971, + 0x03C, 0x0000127E, + 0x03C, 0x00001575, + 0x03C, 0x00001871, + 0x03C, 0x0000217E, + 0x03C, 0x00002575, + 0x03C, 0x00002871, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x061, 0x000C0D47, + 0x062, 0x0000133C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0xA0000000, 0x00000000, + 0x063, 0x0007D0E7, + 0xB0000000, 0x00000000, + 0x064, 0x00014FEC, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0xA0000000, 0x00000000, + 0x065, 0x000923FF, + 0xB0000000, 0x00000000, + 0x066, 0x00000040, + 0x057, 0x00050000, + 0x056, 0x00051DF0, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x055, 0x00082060, + 0xB0000000, 0x00000000, +}; + +RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_c, C); + +static const u32 rtw8814a_rf_d[] = { + 0x018, 0x00013124, + 0x040, 0x00000C00, + 0x058, 0x00000F98, + 0x07F, 0x00068004, + 0x018, 0x00000006, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x086, 0x000E335A, + 0x087, 0x00079F80, + 0xA0000000, 0x00000000, + 0x086, 0x000E4B58, + 0x087, 0x00049F80, + 0xB0000000, 0x00000000, + 0x0DF, 0x00000008, + 0x0EF, 0x00002000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F09B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F09B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0003F19B, + 0x03B, 0x00037A5B, + 0x03B, 0x0002A433, + 0x03B, 0x00027BD3, + 0x03B, 0x0001F80B, + 0x03B, 0x00017803, + 0xA0000000, 0x00000000, + 0x03B, 0x0003F258, + 0x03B, 0x00030A58, + 0x03B, 0x0002FA58, + 0x03B, 0x00022590, + 0x03B, 0x0001FA50, + 0x03B, 0x00010248, + 0x03B, 0x00008240, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000100, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0000ADF6, + 0x034, 0x00009DF3, + 0x034, 0x00008DF0, + 0x034, 0x00007DED, + 0x034, 0x00006DEA, + 0x034, 0x00005CED, + 0x034, 0x00004CEA, + 0x034, 0x000034EA, + 0x034, 0x000024E7, + 0x034, 0x0000146A, + 0x034, 0x0000006B, + 0xB0000000, 0x00000000, + 0x80000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A0D0, + 0x034, 0x000090CD, + 0x034, 0x000080CA, + 0x034, 0x0000704D, + 0x034, 0x0000604A, + 0x034, 0x00005047, + 0x034, 0x0000400A, + 0x034, 0x00003007, + 0x034, 0x00002004, + 0x034, 0x00001001, + 0x034, 0x00000001, + 0xA0000000, 0x00000000, + 0x034, 0x0008ADF6, + 0x034, 0x00089DF3, + 0x034, 0x00088DF0, + 0x034, 0x00087DED, + 0x034, 0x00086DEA, + 0x034, 0x00085CED, + 0x034, 0x00084CEA, + 0x034, 0x000834EA, + 0x034, 0x000824E7, + 0x034, 0x0008146A, + 0x034, 0x0008006B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x000020A2, + 0x0DF, 0x00000080, + 0x035, 0x00000192, + 0x035, 0x00008192, + 0x035, 0x00010192, + 0x036, 0x00000024, + 0x036, 0x00008024, + 0x036, 0x00010024, + 0x036, 0x00018024, + 0x0EF, 0x00000000, + 0x051, 0x00000C21, + 0x052, 0x000006D9, + 0x053, 0x000FC649, + 0x054, 0x0000017E, + 0x018, 0x0001012A, + 0x081, 0x0007FC00, + 0x089, 0x00050110, + 0x08A, 0x00043E50, + 0x08B, 0x0002E180, + 0x08C, 0x00093C3C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x085, 0x000F8000, + 0xA0000000, 0x00000000, + 0x085, 0x000F8000, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xA0000000, 0x00000000, + 0x08D, 0x000FFFF0, + 0xB0000000, 0x00000000, + 0x0EF, 0x00001000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00038023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00048000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00040000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00088000, + 0xA0000000, 0x00000000, + 0x03C, 0x00048000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00030023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00028623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00021633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0001C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00010293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00009593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x00000F8B, + 0xA0000000, 0x00000000, + 0x03B, 0x0000078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00078023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00044000, + 0xA0000000, 0x00000000, + 0x03C, 0x00024000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00070023, + 0x03C, 0x00048000, + 0x03A, 0x0000013C, + 0x03B, 0x00068623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00061633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0005C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00050293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00049593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x00040F8B, + 0xA0000000, 0x00000000, + 0x03B, 0x0004078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xA0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B8023, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00004000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00004000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00060000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00024000, + 0xA0000000, 0x00000000, + 0x03C, 0x00004000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000B0023, + 0x80000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00020000, + 0xA0000000, 0x00000000, + 0x03C, 0x00020000, + 0xB0000000, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A8623, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x000A1633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x0009C633, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00090293, + 0x03C, 0x00000000, + 0x03A, 0x0000013C, + 0x03B, 0x00089593, + 0x03C, 0x00000000, + 0x03A, 0x00000148, + 0x80000008, 0x00000000, 0x40000000, 0x00000000, + 0x03B, 0x0008138B, + 0xA0000000, 0x00000000, + 0x03B, 0x0008078B, + 0xB0000000, 0x00000000, + 0x03C, 0x00000000, + 0x0EF, 0x00000000, + 0x0EF, 0x00000800, + 0x03B, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001003, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001803, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000803, + 0xA0000000, 0x00000000, + 0x03A, 0x00000803, + 0xB0000000, 0x00000000, + 0x03B, 0x00040000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001002, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000001, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000802, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001803, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0xA0000000, 0x00000000, + 0x03A, 0x00001000, + 0xB0000000, 0x00000000, + 0x03B, 0x00080000, + 0x80000007, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001802, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00001000, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x03A, 0x00000802, + 0xA0000000, 0x00000000, + 0x03A, 0x00001002, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000006, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x00013124, + 0x0EF, 0x00000100, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3EB, + 0x034, 0x0004938B, + 0x034, 0x000481AC, + 0x034, 0x000471A9, + 0x034, 0x000460AC, + 0x034, 0x000450A9, + 0x034, 0x0004402E, + 0x034, 0x0004302B, + 0x034, 0x00042028, + 0x034, 0x0004100B, + 0x034, 0x00040008, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3F4, + 0x034, 0x000493D2, + 0x034, 0x000482D1, + 0x034, 0x000471F1, + 0x034, 0x000461EE, + 0x034, 0x000451EB, + 0x034, 0x000441E8, + 0x034, 0x0004314B, + 0x034, 0x00042148, + 0x034, 0x0004104B, + 0x034, 0x00040048, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004AFF7, + 0x034, 0x00049FF6, + 0x034, 0x00048FF3, + 0x034, 0x00047FF0, + 0x034, 0x00046FED, + 0x034, 0x00045FEA, + 0x034, 0x00044FE7, + 0x034, 0x00043CB1, + 0x034, 0x00042CAE, + 0x034, 0x00041CAB, + 0x034, 0x00040CA8, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0004A3AD, + 0x034, 0x0004938A, + 0x034, 0x0004818C, + 0x034, 0x00047189, + 0x034, 0x0004606D, + 0x034, 0x0004506A, + 0x034, 0x0004402C, + 0x034, 0x00043029, + 0x034, 0x00042026, + 0x034, 0x00041009, + 0x034, 0x00040006, + 0xA0000000, 0x00000000, + 0x034, 0x0004AFF4, + 0x034, 0x00049FF1, + 0x034, 0x00048FEE, + 0x034, 0x00047FEB, + 0x034, 0x00046FE8, + 0x034, 0x00045DEA, + 0x034, 0x00044CED, + 0x034, 0x00043CEA, + 0x034, 0x00042C6C, + 0x034, 0x00041C69, + 0x034, 0x00040C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3EE, + 0x034, 0x000293EB, + 0x034, 0x0002838B, + 0x034, 0x000271AC, + 0x034, 0x000261A9, + 0x034, 0x000250AC, + 0x034, 0x000240A9, + 0x034, 0x000230A6, + 0x034, 0x0002202C, + 0x034, 0x00021029, + 0x034, 0x00020026, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3F5, + 0x034, 0x000293D2, + 0x034, 0x000283CE, + 0x034, 0x00027290, + 0x034, 0x0002628D, + 0x034, 0x0002528A, + 0x034, 0x00024287, + 0x034, 0x0002308D, + 0x034, 0x0002208A, + 0x034, 0x00021087, + 0x034, 0x00020048, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002AFF7, + 0x034, 0x00029FF6, + 0x034, 0x00028FF3, + 0x034, 0x00027FF0, + 0x034, 0x00026FED, + 0x034, 0x00025FEA, + 0x034, 0x00024FE7, + 0x034, 0x00023DEA, + 0x034, 0x00022DE7, + 0x034, 0x00021DE4, + 0x034, 0x00020D48, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0002A3AD, + 0x034, 0x0002938A, + 0x034, 0x0002818C, + 0x034, 0x00027189, + 0x034, 0x0002606D, + 0x034, 0x0002504C, + 0x034, 0x0002402C, + 0x034, 0x00023029, + 0x034, 0x00022026, + 0x034, 0x00021023, + 0x034, 0x00020006, + 0xA0000000, 0x00000000, + 0x034, 0x0002AFF4, + 0x034, 0x00029FF1, + 0x034, 0x00028FEE, + 0x034, 0x00027FEB, + 0x034, 0x00026FE8, + 0x034, 0x00025DEA, + 0x034, 0x00024CED, + 0x034, 0x00023CEA, + 0x034, 0x00022C6C, + 0x034, 0x00021C69, + 0x034, 0x00020C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EF, + 0x034, 0x000093EC, + 0x034, 0x0000838C, + 0x034, 0x000071AD, + 0x034, 0x000061AA, + 0x034, 0x000050AD, + 0x034, 0x000040AA, + 0x034, 0x0000306A, + 0x034, 0x0000202D, + 0x034, 0x0000102A, + 0x034, 0x00000027, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3F1, + 0x034, 0x000092B1, + 0x034, 0x000081CF, + 0x034, 0x00007170, + 0x034, 0x0000616D, + 0x034, 0x0000516A, + 0x034, 0x00004167, + 0x034, 0x0000302F, + 0x034, 0x0000202C, + 0x034, 0x00001029, + 0x034, 0x00000026, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000AFF7, + 0x034, 0x00009FF6, + 0x034, 0x00008FF3, + 0x034, 0x00007FF0, + 0x034, 0x00006FED, + 0x034, 0x00005FEA, + 0x034, 0x00004FE7, + 0x034, 0x00003EC7, + 0x034, 0x00002EC4, + 0x034, 0x00001D4B, + 0x034, 0x00000D48, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0000A3EE, + 0x034, 0x000093AC, + 0x034, 0x0000838A, + 0x034, 0x0000718C, + 0x034, 0x00006189, + 0x034, 0x0000506D, + 0x034, 0x0000406A, + 0x034, 0x0000302C, + 0x034, 0x00002029, + 0x034, 0x00001026, + 0x034, 0x00000023, + 0xA0000000, 0x00000000, + 0x034, 0x0000AFF4, + 0x034, 0x00009FF1, + 0x034, 0x00008FEE, + 0x034, 0x00007FEB, + 0x034, 0x00006FE8, + 0x034, 0x00005DEA, + 0x034, 0x00004CED, + 0x034, 0x00003CEA, + 0x034, 0x00002C6C, + 0x034, 0x00001C69, + 0x034, 0x00000C2B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3EB, + 0x034, 0x000C938B, + 0x034, 0x000C81AC, + 0x034, 0x000C71A9, + 0x034, 0x000C60AC, + 0x034, 0x000C50A9, + 0x034, 0x000C402E, + 0x034, 0x000C302B, + 0x034, 0x000C2028, + 0x034, 0x000C100B, + 0x034, 0x000C0008, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3F4, + 0x034, 0x000C93D2, + 0x034, 0x000C82D1, + 0x034, 0x000C71F1, + 0x034, 0x000C61EE, + 0x034, 0x000C51EB, + 0x034, 0x000C41E8, + 0x034, 0x000C314B, + 0x034, 0x000C2148, + 0x034, 0x000C104B, + 0x034, 0x000C0048, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CAFF7, + 0x034, 0x000C9FF6, + 0x034, 0x000C8FF3, + 0x034, 0x000C7FF0, + 0x034, 0x000C6FED, + 0x034, 0x000C5FEA, + 0x034, 0x000C4FE7, + 0x034, 0x000C3CB1, + 0x034, 0x000C2CAE, + 0x034, 0x000C1CAB, + 0x034, 0x000C0CA8, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000CA3AD, + 0x034, 0x000C938A, + 0x034, 0x000C818C, + 0x034, 0x000C7189, + 0x034, 0x000C606D, + 0x034, 0x000C506A, + 0x034, 0x000C402C, + 0x034, 0x000C3029, + 0x034, 0x000C2026, + 0x034, 0x000C1009, + 0x034, 0x000C0006, + 0xA0000000, 0x00000000, + 0x034, 0x000CA794, + 0x034, 0x000C9791, + 0x034, 0x000C878E, + 0x034, 0x000C778B, + 0x034, 0x000C658D, + 0x034, 0x000C558A, + 0x034, 0x000C448D, + 0x034, 0x000C348A, + 0x034, 0x000C244C, + 0x034, 0x000C1449, + 0x034, 0x000C042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3EE, + 0x034, 0x000A93EB, + 0x034, 0x000A838B, + 0x034, 0x000A71AC, + 0x034, 0x000A61A9, + 0x034, 0x000A50AC, + 0x034, 0x000A40A9, + 0x034, 0x000A30A6, + 0x034, 0x000A202C, + 0x034, 0x000A1029, + 0x034, 0x000A0026, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3F5, + 0x034, 0x000A93D2, + 0x034, 0x000A83CE, + 0x034, 0x000A7290, + 0x034, 0x000A628D, + 0x034, 0x000A528A, + 0x034, 0x000A4287, + 0x034, 0x000A308D, + 0x034, 0x000A208A, + 0x034, 0x000A1087, + 0x034, 0x000A0048, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AAFF7, + 0x034, 0x000A9FF6, + 0x034, 0x000A8FF3, + 0x034, 0x000A7FF0, + 0x034, 0x000A6FED, + 0x034, 0x000A5FEA, + 0x034, 0x000A4FE7, + 0x034, 0x000A3DEA, + 0x034, 0x000A2DE7, + 0x034, 0x000A1DE4, + 0x034, 0x000A0D48, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x000AA3AD, + 0x034, 0x000A938A, + 0x034, 0x000A818C, + 0x034, 0x000A7189, + 0x034, 0x000A606D, + 0x034, 0x000A504C, + 0x034, 0x000A402C, + 0x034, 0x000A3029, + 0x034, 0x000A2026, + 0x034, 0x000A1023, + 0x034, 0x000A0006, + 0xA0000000, 0x00000000, + 0x034, 0x000AA794, + 0x034, 0x000A9791, + 0x034, 0x000A878E, + 0x034, 0x000A778B, + 0x034, 0x000A658D, + 0x034, 0x000A558A, + 0x034, 0x000A448D, + 0x034, 0x000A348A, + 0x034, 0x000A244C, + 0x034, 0x000A1449, + 0x034, 0x000A042B, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EF, + 0x034, 0x000893EC, + 0x034, 0x0008838C, + 0x034, 0x000871AD, + 0x034, 0x000861AA, + 0x034, 0x000850AD, + 0x034, 0x000840AA, + 0x034, 0x0008306A, + 0x034, 0x0008202D, + 0x034, 0x0008102A, + 0x034, 0x00080027, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3F1, + 0x034, 0x000892B1, + 0x034, 0x000881CF, + 0x034, 0x00087170, + 0x034, 0x0008616D, + 0x034, 0x0008516A, + 0x034, 0x00084167, + 0x034, 0x0008302F, + 0x034, 0x0008202C, + 0x034, 0x00081029, + 0x034, 0x00080026, + 0x90000009, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008AFF7, + 0x034, 0x00089FF6, + 0x034, 0x00088FF3, + 0x034, 0x00087FF0, + 0x034, 0x00086FED, + 0x034, 0x00085FEA, + 0x034, 0x00084FE7, + 0x034, 0x00083EC7, + 0x034, 0x00082EC4, + 0x034, 0x00081D4B, + 0x034, 0x00080D48, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x034, 0x0008A3EE, + 0x034, 0x000893AC, + 0x034, 0x0008838A, + 0x034, 0x0008718C, + 0x034, 0x00086189, + 0x034, 0x0008506D, + 0x034, 0x0008406A, + 0x034, 0x0008302C, + 0x034, 0x00082029, + 0x034, 0x00081026, + 0x034, 0x00080023, + 0xA0000000, 0x00000000, + 0x034, 0x0008A794, + 0x034, 0x00089791, + 0x034, 0x0008878E, + 0x034, 0x0008778B, + 0x034, 0x0008658D, + 0x034, 0x0008558A, + 0x034, 0x0008448D, + 0x034, 0x0008348A, + 0x034, 0x0008244C, + 0x034, 0x00081449, + 0x034, 0x0008042B, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000040, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x035, 0x000006CC, + 0x035, 0x000086CC, + 0x035, 0x000106CC, + 0x035, 0x000206CC, + 0x035, 0x000286CC, + 0x035, 0x000306CC, + 0x035, 0x000406CC, + 0x035, 0x000486CC, + 0x035, 0x000506CC, + 0x035, 0x000806CC, + 0x035, 0x000886CC, + 0x035, 0x000906CC, + 0x035, 0x000A06CC, + 0x035, 0x000A86CC, + 0x035, 0x000B06CC, + 0x035, 0x000C06CC, + 0x035, 0x000C86CC, + 0x035, 0x000D06CC, + 0xA0000000, 0x00000000, + 0x035, 0x00000484, + 0x035, 0x00008484, + 0x035, 0x00010484, + 0x035, 0x00020584, + 0x035, 0x00028584, + 0x035, 0x00030584, + 0x035, 0x00040584, + 0x035, 0x00048584, + 0x035, 0x00050584, + 0x035, 0x000805FB, + 0x035, 0x000885FB, + 0x035, 0x000905FB, + 0x035, 0x000A05FB, + 0x035, 0x000A85FB, + 0x035, 0x000B05FB, + 0x035, 0x000C05FB, + 0x035, 0x000C85FB, + 0x035, 0x000D05FB, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x0DF, 0x00000001, + 0xA0000000, 0x00000000, + 0x0DF, 0x00000000, + 0xB0000000, 0x00000000, + 0x018, 0x0001712A, + 0x0EF, 0x00000010, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000473, + 0x036, 0x00008473, + 0x036, 0x00010473, + 0x036, 0x00020473, + 0x036, 0x00028473, + 0x036, 0x00030473, + 0x036, 0x00040473, + 0x036, 0x00048473, + 0x036, 0x00050473, + 0x036, 0x00080473, + 0x036, 0x00088473, + 0x036, 0x00090473, + 0x036, 0x000A0473, + 0x036, 0x000A8473, + 0x036, 0x000B0473, + 0x036, 0x000C0473, + 0x036, 0x000C8473, + 0x036, 0x000D0473, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x036, 0x00000475, + 0x036, 0x00008475, + 0x036, 0x00010475, + 0x036, 0x00020475, + 0x036, 0x00028475, + 0x036, 0x00030475, + 0x036, 0x00040475, + 0x036, 0x00048475, + 0x036, 0x00050475, + 0x036, 0x00080475, + 0x036, 0x00088475, + 0x036, 0x00090475, + 0x036, 0x000A0475, + 0x036, 0x000A8475, + 0x036, 0x000B0475, + 0x036, 0x000C0475, + 0x036, 0x000C8475, + 0x036, 0x000D0475, + 0xA0000000, 0x00000000, + 0x036, 0x00000474, + 0x036, 0x00008474, + 0x036, 0x00010474, + 0x036, 0x00020474, + 0x036, 0x00028474, + 0x036, 0x00030474, + 0x036, 0x00040474, + 0x036, 0x00048474, + 0x036, 0x00050474, + 0x036, 0x00080474, + 0x036, 0x00088474, + 0x036, 0x00090474, + 0x036, 0x000A0474, + 0x036, 0x000A8474, + 0x036, 0x000B0474, + 0x036, 0x000C0474, + 0x036, 0x000C8474, + 0x036, 0x000D0474, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x0EF, 0x00000004, + 0x037, 0x00000000, + 0x038, 0x0000514E, + 0x037, 0x00004000, + 0x038, 0x0000514E, + 0x037, 0x00008000, + 0x038, 0x0000514E, + 0x037, 0x00010000, + 0x038, 0x0000514E, + 0x037, 0x00014000, + 0x038, 0x0000514E, + 0x037, 0x00018000, + 0x038, 0x0000514E, + 0x037, 0x0001C000, + 0x038, 0x0000514E, + 0x037, 0x00020000, + 0x038, 0x0000514E, + 0x037, 0x00024000, + 0x038, 0x0000514E, + 0x037, 0x00028000, + 0x038, 0x0000514E, + 0x037, 0x0002C000, + 0x038, 0x0000714E, + 0x037, 0x00030000, + 0x038, 0x0000514E, + 0x037, 0x00034000, + 0x038, 0x0000514E, + 0x037, 0x00038000, + 0x038, 0x0000514E, + 0x037, 0x0003C000, + 0x038, 0x0000514E, + 0x037, 0x00040000, + 0x038, 0x0000514E, + 0x037, 0x00044000, + 0x038, 0x0000514E, + 0x037, 0x00048000, + 0x038, 0x0000514E, + 0x037, 0x00080000, + 0x038, 0x00005ECE, + 0x037, 0x00084000, + 0x038, 0x00005ECE, + 0x037, 0x00088000, + 0x038, 0x00005ECE, + 0x037, 0x00090000, + 0x038, 0x00005ECE, + 0x037, 0x00094000, + 0x038, 0x00005ECE, + 0x037, 0x00098000, + 0x038, 0x00005ECE, + 0x037, 0x0009C000, + 0x038, 0x00005ECE, + 0x037, 0x000A0000, + 0x038, 0x00005ECE, + 0x037, 0x000A4000, + 0x038, 0x00005ECE, + 0x037, 0x000A8000, + 0x038, 0x00005ECE, + 0x037, 0x000AC000, + 0x038, 0x00005ECE, + 0x037, 0x000B0000, + 0x038, 0x00005ECE, + 0x037, 0x000B4000, + 0x038, 0x00005ECE, + 0x037, 0x000B8000, + 0x038, 0x00005ECE, + 0x037, 0x000BC000, + 0x038, 0x00005ECE, + 0x037, 0x000C0000, + 0x038, 0x00005ECE, + 0x037, 0x000C4000, + 0x038, 0x00005ECE, + 0x037, 0x000C8000, + 0x038, 0x00005ECE, + 0x0EF, 0x00000000, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000008, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x03C, 0x0000007D, + 0x03C, 0x0000047D, + 0x03C, 0x0000087D, + 0x03C, 0x0000107D, + 0x03C, 0x0000147D, + 0x03C, 0x0000187D, + 0xB0000000, 0x00000000, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x00000275, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x00001275, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x00002275, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x03C, 0x0000027F, + 0x03C, 0x00000542, + 0x03C, 0x00000821, + 0x03C, 0x0000127F, + 0x03C, 0x00001542, + 0x03C, 0x00001821, + 0x03C, 0x0000227F, + 0x03C, 0x00002542, + 0x03C, 0x00002821, + 0xA0000000, 0x00000000, + 0x03C, 0x0000037E, + 0x03C, 0x00000575, + 0x03C, 0x00000971, + 0x03C, 0x0000127E, + 0x03C, 0x00001575, + 0x03C, 0x00001871, + 0x03C, 0x0000217E, + 0x03C, 0x00002575, + 0x03C, 0x00002871, + 0xB0000000, 0x00000000, + 0x0EF, 0x00000000, + 0x061, 0x000C0D47, + 0x062, 0x0000133C, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x063, 0x000750E7, + 0xA0000000, 0x00000000, + 0x063, 0x0007D0E7, + 0xB0000000, 0x00000000, + 0x064, 0x00014FEC, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0x065, 0x000920D0, + 0xA0000000, 0x00000000, + 0x065, 0x000923FF, + 0xB0000000, 0x00000000, + 0x066, 0x00000040, + 0x057, 0x00050000, + 0x056, 0x00051DF0, + 0x80000001, 0x00000000, 0x40000000, 0x00000000, + 0x90000002, 0x00000000, 0x40000000, 0x00000000, + 0x90000003, 0x00000000, 0x40000000, 0x00000000, + 0x90000004, 0x00000000, 0x40000000, 0x00000000, + 0x90000005, 0x00000000, 0x40000000, 0x00000000, + 0x90000008, 0x00000000, 0x40000000, 0x00000000, + 0x9000000a, 0x00000000, 0x40000000, 0x00000000, + 0x9000000b, 0x00000000, 0x40000000, 0x00000000, + 0xA0000000, 0x00000000, + 0x055, 0x00082060, + 0xB0000000, 0x00000000, +}; + +RTW_DECL_TABLE_RF_RADIO(rtw8814a_rf_d, D); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt[] = { + { 0, 0, 0, 0, 1, 36, }, + { 2, 0, 0, 0, 1, 32, }, + { 1, 0, 0, 0, 1, 32, }, + { 0, 0, 0, 0, 2, 36, }, + { 2, 0, 0, 0, 2, 32, }, + { 1, 0, 0, 0, 2, 32, }, + { 0, 0, 0, 0, 3, 36, }, + { 2, 0, 0, 0, 3, 32, }, + { 1, 0, 0, 0, 3, 32, }, + { 0, 0, 0, 0, 4, 36, }, + { 2, 0, 0, 0, 4, 32, }, + { 1, 0, 0, 0, 4, 32, }, + { 0, 0, 0, 0, 5, 36, }, + { 2, 0, 0, 0, 5, 32, }, + { 1, 0, 0, 0, 5, 32, }, + { 0, 0, 0, 0, 6, 36, }, + { 2, 0, 0, 0, 6, 32, }, + { 1, 0, 0, 0, 6, 32, }, + { 0, 0, 0, 0, 7, 36, }, + { 2, 0, 0, 0, 7, 32, }, + { 1, 0, 0, 0, 7, 32, }, + { 0, 0, 0, 0, 8, 36, }, + { 2, 0, 0, 0, 8, 32, }, + { 1, 0, 0, 0, 8, 32, }, + { 0, 0, 0, 0, 9, 36, }, + { 2, 0, 0, 0, 9, 32, }, + { 1, 0, 0, 0, 9, 32, }, + { 0, 0, 0, 0, 10, 36, }, + { 2, 0, 0, 0, 10, 32, }, + { 1, 0, 0, 0, 10, 32, }, + { 0, 0, 0, 0, 11, 36, }, + { 2, 0, 0, 0, 11, 32, }, + { 1, 0, 0, 0, 11, 32, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 32, }, + { 1, 0, 0, 0, 12, 32, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 32, }, + { 1, 0, 0, 0, 13, 32, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 32, }, + { 0, 0, 0, 1, 1, 34, }, + { 2, 0, 0, 1, 1, 32, }, + { 1, 0, 0, 1, 1, 32, }, + { 0, 0, 0, 1, 2, 36, }, + { 2, 0, 0, 1, 2, 32, }, + { 1, 0, 0, 1, 2, 32, }, + { 0, 0, 0, 1, 3, 36, }, + { 2, 0, 0, 1, 3, 32, }, + { 1, 0, 0, 1, 3, 32, }, + { 0, 0, 0, 1, 4, 36, }, + { 2, 0, 0, 1, 4, 32, }, + { 1, 0, 0, 1, 4, 32, }, + { 0, 0, 0, 1, 5, 36, }, + { 2, 0, 0, 1, 5, 32, }, + { 1, 0, 0, 1, 5, 32, }, + { 0, 0, 0, 1, 6, 36, }, + { 2, 0, 0, 1, 6, 32, }, + { 1, 0, 0, 1, 6, 32, }, + { 0, 0, 0, 1, 7, 36, }, + { 2, 0, 0, 1, 7, 32, }, + { 1, 0, 0, 1, 7, 32, }, + { 0, 0, 0, 1, 8, 36, }, + { 2, 0, 0, 1, 8, 32, }, + { 1, 0, 0, 1, 8, 32, }, + { 0, 0, 0, 1, 9, 36, }, + { 2, 0, 0, 1, 9, 32, }, + { 1, 0, 0, 1, 9, 32, }, + { 0, 0, 0, 1, 10, 36, }, + { 2, 0, 0, 1, 10, 32, }, + { 1, 0, 0, 1, 10, 32, }, + { 0, 0, 0, 1, 11, 32, }, + { 2, 0, 0, 1, 11, 32, }, + { 1, 0, 0, 1, 11, 32, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 32, }, + { 1, 0, 0, 1, 12, 32, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 32, }, + { 1, 0, 0, 1, 13, 32, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 34, }, + { 2, 0, 0, 2, 1, 32, }, + { 1, 0, 0, 2, 1, 32, }, + { 0, 0, 0, 2, 2, 36, }, + { 2, 0, 0, 2, 2, 32, }, + { 1, 0, 0, 2, 2, 32, }, + { 0, 0, 0, 2, 3, 36, }, + { 2, 0, 0, 2, 3, 32, }, + { 1, 0, 0, 2, 3, 32, }, + { 0, 0, 0, 2, 4, 36, }, + { 2, 0, 0, 2, 4, 32, }, + { 1, 0, 0, 2, 4, 32, }, + { 0, 0, 0, 2, 5, 36, }, + { 2, 0, 0, 2, 5, 32, }, + { 1, 0, 0, 2, 5, 32, }, + { 0, 0, 0, 2, 6, 36, }, + { 2, 0, 0, 2, 6, 32, }, + { 1, 0, 0, 2, 6, 32, }, + { 0, 0, 0, 2, 7, 36, }, + { 2, 0, 0, 2, 7, 32, }, + { 1, 0, 0, 2, 7, 32, }, + { 0, 0, 0, 2, 8, 36, }, + { 2, 0, 0, 2, 8, 32, }, + { 1, 0, 0, 2, 8, 32, }, + { 0, 0, 0, 2, 9, 36, }, + { 2, 0, 0, 2, 9, 32, }, + { 1, 0, 0, 2, 9, 32, }, + { 0, 0, 0, 2, 10, 36, }, + { 2, 0, 0, 2, 10, 32, }, + { 1, 0, 0, 2, 10, 32, }, + { 0, 0, 0, 2, 11, 32, }, + { 2, 0, 0, 2, 11, 32, }, + { 1, 0, 0, 2, 11, 32, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 32, }, + { 1, 0, 0, 2, 12, 32, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 32, }, + { 1, 0, 0, 2, 13, 32, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 32, }, + { 2, 0, 0, 3, 1, 30, }, + { 1, 0, 0, 3, 1, 30, }, + { 0, 0, 0, 3, 2, 34, }, + { 2, 0, 0, 3, 2, 30, }, + { 1, 0, 0, 3, 2, 30, }, + { 0, 0, 0, 3, 3, 34, }, + { 2, 0, 0, 3, 3, 30, }, + { 1, 0, 0, 3, 3, 30, }, + { 0, 0, 0, 3, 4, 34, }, + { 2, 0, 0, 3, 4, 30, }, + { 1, 0, 0, 3, 4, 30, }, + { 0, 0, 0, 3, 5, 34, }, + { 2, 0, 0, 3, 5, 30, }, + { 1, 0, 0, 3, 5, 30, }, + { 0, 0, 0, 3, 6, 34, }, + { 2, 0, 0, 3, 6, 30, }, + { 1, 0, 0, 3, 6, 30, }, + { 0, 0, 0, 3, 7, 34, }, + { 2, 0, 0, 3, 7, 30, }, + { 1, 0, 0, 3, 7, 30, }, + { 0, 0, 0, 3, 8, 34, }, + { 2, 0, 0, 3, 8, 30, }, + { 1, 0, 0, 3, 8, 30, }, + { 0, 0, 0, 3, 9, 34, }, + { 2, 0, 0, 3, 9, 30, }, + { 1, 0, 0, 3, 9, 30, }, + { 0, 0, 0, 3, 10, 34, }, + { 2, 0, 0, 3, 10, 30, }, + { 1, 0, 0, 3, 10, 30, }, + { 0, 0, 0, 3, 11, 30, }, + { 2, 0, 0, 3, 11, 30, }, + { 1, 0, 0, 3, 11, 30, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 30, }, + { 1, 0, 0, 3, 12, 30, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 30, }, + { 1, 0, 0, 3, 13, 30, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 30, }, + { 2, 0, 0, 6, 1, 28, }, + { 1, 0, 0, 6, 1, 28, }, + { 0, 0, 0, 6, 2, 32, }, + { 2, 0, 0, 6, 2, 28, }, + { 1, 0, 0, 6, 2, 28, }, + { 0, 0, 0, 6, 3, 32, }, + { 2, 0, 0, 6, 3, 28, }, + { 1, 0, 0, 6, 3, 28, }, + { 0, 0, 0, 6, 4, 32, }, + { 2, 0, 0, 6, 4, 28, }, + { 1, 0, 0, 6, 4, 28, }, + { 0, 0, 0, 6, 5, 32, }, + { 2, 0, 0, 6, 5, 28, }, + { 1, 0, 0, 6, 5, 28, }, + { 0, 0, 0, 6, 6, 32, }, + { 2, 0, 0, 6, 6, 28, }, + { 1, 0, 0, 6, 6, 28, }, + { 0, 0, 0, 6, 7, 32, }, + { 2, 0, 0, 6, 7, 28, }, + { 1, 0, 0, 6, 7, 28, }, + { 0, 0, 0, 6, 8, 32, }, + { 2, 0, 0, 6, 8, 28, }, + { 1, 0, 0, 6, 8, 28, }, + { 0, 0, 0, 6, 9, 32, }, + { 2, 0, 0, 6, 9, 28, }, + { 1, 0, 0, 6, 9, 28, }, + { 0, 0, 0, 6, 10, 32, }, + { 2, 0, 0, 6, 10, 28, }, + { 1, 0, 0, 6, 10, 28, }, + { 0, 0, 0, 6, 11, 28, }, + { 2, 0, 0, 6, 11, 28, }, + { 1, 0, 0, 6, 11, 28, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 28, }, + { 1, 0, 0, 6, 12, 28, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 28, }, + { 1, 0, 0, 6, 13, 28, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 28, }, + { 2, 0, 0, 7, 1, 26, }, + { 1, 0, 0, 7, 1, 26, }, + { 0, 0, 0, 7, 2, 30, }, + { 2, 0, 0, 7, 2, 26, }, + { 1, 0, 0, 7, 2, 26, }, + { 0, 0, 0, 7, 3, 30, }, + { 2, 0, 0, 7, 3, 26, }, + { 1, 0, 0, 7, 3, 26, }, + { 0, 0, 0, 7, 4, 30, }, + { 2, 0, 0, 7, 4, 26, }, + { 1, 0, 0, 7, 4, 26, }, + { 0, 0, 0, 7, 5, 30, }, + { 2, 0, 0, 7, 5, 26, }, + { 1, 0, 0, 7, 5, 26, }, + { 0, 0, 0, 7, 6, 30, }, + { 2, 0, 0, 7, 6, 26, }, + { 1, 0, 0, 7, 6, 26, }, + { 0, 0, 0, 7, 7, 30, }, + { 2, 0, 0, 7, 7, 26, }, + { 1, 0, 0, 7, 7, 26, }, + { 0, 0, 0, 7, 8, 30, }, + { 2, 0, 0, 7, 8, 26, }, + { 1, 0, 0, 7, 8, 26, }, + { 0, 0, 0, 7, 9, 30, }, + { 2, 0, 0, 7, 9, 26, }, + { 1, 0, 0, 7, 9, 26, }, + { 0, 0, 0, 7, 10, 30, }, + { 2, 0, 0, 7, 10, 26, }, + { 1, 0, 0, 7, 10, 26, }, + { 0, 0, 0, 7, 11, 26, }, + { 2, 0, 0, 7, 11, 26, }, + { 1, 0, 0, 7, 11, 26, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 26, }, + { 1, 0, 0, 7, 12, 26, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 26, }, + { 1, 0, 0, 7, 13, 26, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 32, }, + { 2, 0, 1, 2, 3, 32, }, + { 1, 0, 1, 2, 3, 32, }, + { 0, 0, 1, 2, 4, 36, }, + { 2, 0, 1, 2, 4, 32, }, + { 1, 0, 1, 2, 4, 32, }, + { 0, 0, 1, 2, 5, 36, }, + { 2, 0, 1, 2, 5, 32, }, + { 1, 0, 1, 2, 5, 32, }, + { 0, 0, 1, 2, 6, 36, }, + { 2, 0, 1, 2, 6, 32, }, + { 1, 0, 1, 2, 6, 32, }, + { 0, 0, 1, 2, 7, 36, }, + { 2, 0, 1, 2, 7, 32, }, + { 1, 0, 1, 2, 7, 32, }, + { 0, 0, 1, 2, 8, 36, }, + { 2, 0, 1, 2, 8, 32, }, + { 1, 0, 1, 2, 8, 32, }, + { 0, 0, 1, 2, 9, 36, }, + { 2, 0, 1, 2, 9, 32, }, + { 1, 0, 1, 2, 9, 32, }, + { 0, 0, 1, 2, 10, 36, }, + { 2, 0, 1, 2, 10, 32, }, + { 1, 0, 1, 2, 10, 32, }, + { 0, 0, 1, 2, 11, 32, }, + { 2, 0, 1, 2, 11, 32, }, + { 1, 0, 1, 2, 11, 32, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 32, }, + { 1, 0, 1, 2, 12, 32, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 32, }, + { 1, 0, 1, 2, 13, 32, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 30, }, + { 2, 0, 1, 3, 3, 30, }, + { 1, 0, 1, 3, 3, 30, }, + { 0, 0, 1, 3, 4, 34, }, + { 2, 0, 1, 3, 4, 30, }, + { 1, 0, 1, 3, 4, 30, }, + { 0, 0, 1, 3, 5, 34, }, + { 2, 0, 1, 3, 5, 30, }, + { 1, 0, 1, 3, 5, 30, }, + { 0, 0, 1, 3, 6, 34, }, + { 2, 0, 1, 3, 6, 30, }, + { 1, 0, 1, 3, 6, 30, }, + { 0, 0, 1, 3, 7, 34, }, + { 2, 0, 1, 3, 7, 30, }, + { 1, 0, 1, 3, 7, 30, }, + { 0, 0, 1, 3, 8, 34, }, + { 2, 0, 1, 3, 8, 30, }, + { 1, 0, 1, 3, 8, 30, }, + { 0, 0, 1, 3, 9, 34, }, + { 2, 0, 1, 3, 9, 30, }, + { 1, 0, 1, 3, 9, 30, }, + { 0, 0, 1, 3, 10, 34, }, + { 2, 0, 1, 3, 10, 30, }, + { 1, 0, 1, 3, 10, 30, }, + { 0, 0, 1, 3, 11, 30, }, + { 2, 0, 1, 3, 11, 30, }, + { 1, 0, 1, 3, 11, 30, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 30, }, + { 1, 0, 1, 3, 12, 30, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 30, }, + { 1, 0, 1, 3, 13, 30, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 28, }, + { 2, 0, 1, 6, 3, 28, }, + { 1, 0, 1, 6, 3, 28, }, + { 0, 0, 1, 6, 4, 32, }, + { 2, 0, 1, 6, 4, 28, }, + { 1, 0, 1, 6, 4, 28, }, + { 0, 0, 1, 6, 5, 32, }, + { 2, 0, 1, 6, 5, 28, }, + { 1, 0, 1, 6, 5, 28, }, + { 0, 0, 1, 6, 6, 32, }, + { 2, 0, 1, 6, 6, 28, }, + { 1, 0, 1, 6, 6, 28, }, + { 0, 0, 1, 6, 7, 32, }, + { 2, 0, 1, 6, 7, 28, }, + { 1, 0, 1, 6, 7, 28, }, + { 0, 0, 1, 6, 8, 32, }, + { 2, 0, 1, 6, 8, 28, }, + { 1, 0, 1, 6, 8, 28, }, + { 0, 0, 1, 6, 9, 32, }, + { 2, 0, 1, 6, 9, 28, }, + { 1, 0, 1, 6, 9, 28, }, + { 0, 0, 1, 6, 10, 32, }, + { 2, 0, 1, 6, 10, 28, }, + { 1, 0, 1, 6, 10, 28, }, + { 0, 0, 1, 6, 11, 28, }, + { 2, 0, 1, 6, 11, 28, }, + { 1, 0, 1, 6, 11, 28, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 28, }, + { 1, 0, 1, 6, 12, 28, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 28, }, + { 1, 0, 1, 6, 13, 28, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 26, }, + { 2, 0, 1, 7, 3, 26, }, + { 1, 0, 1, 7, 3, 26, }, + { 0, 0, 1, 7, 4, 30, }, + { 2, 0, 1, 7, 4, 26, }, + { 1, 0, 1, 7, 4, 26, }, + { 0, 0, 1, 7, 5, 30, }, + { 2, 0, 1, 7, 5, 26, }, + { 1, 0, 1, 7, 5, 26, }, + { 0, 0, 1, 7, 6, 30, }, + { 2, 0, 1, 7, 6, 26, }, + { 1, 0, 1, 7, 6, 26, }, + { 0, 0, 1, 7, 7, 30, }, + { 2, 0, 1, 7, 7, 26, }, + { 1, 0, 1, 7, 7, 26, }, + { 0, 0, 1, 7, 8, 30, }, + { 2, 0, 1, 7, 8, 26, }, + { 1, 0, 1, 7, 8, 26, }, + { 0, 0, 1, 7, 9, 30, }, + { 2, 0, 1, 7, 9, 26, }, + { 1, 0, 1, 7, 9, 26, }, + { 0, 0, 1, 7, 10, 30, }, + { 2, 0, 1, 7, 10, 26, }, + { 1, 0, 1, 7, 10, 26, }, + { 0, 0, 1, 7, 11, 26, }, + { 2, 0, 1, 7, 11, 26, }, + { 1, 0, 1, 7, 11, 26, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 26, }, + { 1, 0, 1, 7, 12, 26, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 26, }, + { 1, 0, 1, 7, 13, 26, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 30, }, + { 2, 1, 0, 1, 36, 32, }, + { 1, 1, 0, 1, 36, 32, }, + { 0, 1, 0, 1, 40, 30, }, + { 2, 1, 0, 1, 40, 32, }, + { 1, 1, 0, 1, 40, 32, }, + { 0, 1, 0, 1, 44, 30, }, + { 2, 1, 0, 1, 44, 32, }, + { 1, 1, 0, 1, 44, 32, }, + { 0, 1, 0, 1, 48, 30, }, + { 2, 1, 0, 1, 48, 32, }, + { 1, 1, 0, 1, 48, 32, }, + { 0, 1, 0, 1, 52, 36, }, + { 2, 1, 0, 1, 52, 32, }, + { 1, 1, 0, 1, 52, 32, }, + { 0, 1, 0, 1, 56, 34, }, + { 2, 1, 0, 1, 56, 32, }, + { 1, 1, 0, 1, 56, 32, }, + { 0, 1, 0, 1, 60, 32, }, + { 2, 1, 0, 1, 60, 32, }, + { 1, 1, 0, 1, 60, 32, }, + { 0, 1, 0, 1, 64, 28, }, + { 2, 1, 0, 1, 64, 32, }, + { 1, 1, 0, 1, 64, 32, }, + { 0, 1, 0, 1, 100, 30, }, + { 2, 1, 0, 1, 100, 32, }, + { 1, 1, 0, 1, 100, 32, }, + { 0, 1, 0, 1, 104, 30, }, + { 2, 1, 0, 1, 104, 32, }, + { 1, 1, 0, 1, 104, 32, }, + { 0, 1, 0, 1, 108, 32, }, + { 2, 1, 0, 1, 108, 32, }, + { 1, 1, 0, 1, 108, 32, }, + { 0, 1, 0, 1, 112, 34, }, + { 2, 1, 0, 1, 112, 32, }, + { 1, 1, 0, 1, 112, 32, }, + { 0, 1, 0, 1, 116, 34, }, + { 2, 1, 0, 1, 116, 32, }, + { 1, 1, 0, 1, 116, 32, }, + { 0, 1, 0, 1, 120, 36, }, + { 2, 1, 0, 1, 120, 32, }, + { 1, 1, 0, 1, 120, 32, }, + { 0, 1, 0, 1, 124, 34, }, + { 2, 1, 0, 1, 124, 32, }, + { 1, 1, 0, 1, 124, 32, }, + { 0, 1, 0, 1, 128, 32, }, + { 2, 1, 0, 1, 128, 32, }, + { 1, 1, 0, 1, 128, 32, }, + { 0, 1, 0, 1, 132, 30, }, + { 2, 1, 0, 1, 132, 32, }, + { 1, 1, 0, 1, 132, 32, }, + { 0, 1, 0, 1, 136, 30, }, + { 2, 1, 0, 1, 136, 32, }, + { 1, 1, 0, 1, 136, 32, }, + { 0, 1, 0, 1, 140, 28, }, + { 2, 1, 0, 1, 140, 32, }, + { 1, 1, 0, 1, 140, 32, }, + { 0, 1, 0, 1, 149, 36, }, + { 2, 1, 0, 1, 149, 32, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 36, }, + { 2, 1, 0, 1, 153, 32, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 36, }, + { 2, 1, 0, 1, 157, 32, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 36, }, + { 2, 1, 0, 1, 161, 32, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 36, }, + { 2, 1, 0, 1, 165, 32, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 30, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 30, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 30, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 30, }, + { 2, 1, 0, 2, 48, 32, }, + { 1, 1, 0, 2, 48, 32, }, + { 0, 1, 0, 2, 52, 36, }, + { 2, 1, 0, 2, 52, 32, }, + { 1, 1, 0, 2, 52, 32, }, + { 0, 1, 0, 2, 56, 34, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 32, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 28, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 30, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 30, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 32, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 34, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 34, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 36, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 34, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 32, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 30, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 30, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 28, }, + { 2, 1, 0, 2, 140, 32, }, + { 1, 1, 0, 2, 140, 32, }, + { 0, 1, 0, 2, 149, 36, }, + { 2, 1, 0, 2, 149, 32, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 36, }, + { 2, 1, 0, 2, 153, 32, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 36, }, + { 2, 1, 0, 2, 157, 32, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 36, }, + { 2, 1, 0, 2, 161, 32, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 36, }, + { 2, 1, 0, 2, 165, 32, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 28, }, + { 2, 1, 0, 3, 36, 30, }, + { 1, 1, 0, 3, 36, 30, }, + { 0, 1, 0, 3, 40, 28, }, + { 2, 1, 0, 3, 40, 30, }, + { 1, 1, 0, 3, 40, 30, }, + { 0, 1, 0, 3, 44, 28, }, + { 2, 1, 0, 3, 44, 30, }, + { 1, 1, 0, 3, 44, 30, }, + { 0, 1, 0, 3, 48, 28, }, + { 2, 1, 0, 3, 48, 30, }, + { 1, 1, 0, 3, 48, 30, }, + { 0, 1, 0, 3, 52, 34, }, + { 2, 1, 0, 3, 52, 30, }, + { 1, 1, 0, 3, 52, 30, }, + { 0, 1, 0, 3, 56, 32, }, + { 2, 1, 0, 3, 56, 30, }, + { 1, 1, 0, 3, 56, 30, }, + { 0, 1, 0, 3, 60, 30, }, + { 2, 1, 0, 3, 60, 30, }, + { 1, 1, 0, 3, 60, 30, }, + { 0, 1, 0, 3, 64, 26, }, + { 2, 1, 0, 3, 64, 30, }, + { 1, 1, 0, 3, 64, 30, }, + { 0, 1, 0, 3, 100, 28, }, + { 2, 1, 0, 3, 100, 30, }, + { 1, 1, 0, 3, 100, 30, }, + { 0, 1, 0, 3, 104, 28, }, + { 2, 1, 0, 3, 104, 30, }, + { 1, 1, 0, 3, 104, 30, }, + { 0, 1, 0, 3, 108, 30, }, + { 2, 1, 0, 3, 108, 30, }, + { 1, 1, 0, 3, 108, 30, }, + { 0, 1, 0, 3, 112, 32, }, + { 2, 1, 0, 3, 112, 30, }, + { 1, 1, 0, 3, 112, 30, }, + { 0, 1, 0, 3, 116, 32, }, + { 2, 1, 0, 3, 116, 30, }, + { 1, 1, 0, 3, 116, 30, }, + { 0, 1, 0, 3, 120, 34, }, + { 2, 1, 0, 3, 120, 30, }, + { 1, 1, 0, 3, 120, 30, }, + { 0, 1, 0, 3, 124, 32, }, + { 2, 1, 0, 3, 124, 30, }, + { 1, 1, 0, 3, 124, 30, }, + { 0, 1, 0, 3, 128, 30, }, + { 2, 1, 0, 3, 128, 30, }, + { 1, 1, 0, 3, 128, 30, }, + { 0, 1, 0, 3, 132, 28, }, + { 2, 1, 0, 3, 132, 30, }, + { 1, 1, 0, 3, 132, 30, }, + { 0, 1, 0, 3, 136, 28, }, + { 2, 1, 0, 3, 136, 30, }, + { 1, 1, 0, 3, 136, 30, }, + { 0, 1, 0, 3, 140, 26, }, + { 2, 1, 0, 3, 140, 30, }, + { 1, 1, 0, 3, 140, 30, }, + { 0, 1, 0, 3, 149, 34, }, + { 2, 1, 0, 3, 149, 30, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 34, }, + { 2, 1, 0, 3, 153, 30, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 34, }, + { 2, 1, 0, 3, 157, 30, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 34, }, + { 2, 1, 0, 3, 161, 30, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 34, }, + { 2, 1, 0, 3, 165, 30, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 26, }, + { 2, 1, 0, 6, 36, 28, }, + { 1, 1, 0, 6, 36, 28, }, + { 0, 1, 0, 6, 40, 26, }, + { 2, 1, 0, 6, 40, 28, }, + { 1, 1, 0, 6, 40, 28, }, + { 0, 1, 0, 6, 44, 26, }, + { 2, 1, 0, 6, 44, 28, }, + { 1, 1, 0, 6, 44, 28, }, + { 0, 1, 0, 6, 48, 26, }, + { 2, 1, 0, 6, 48, 28, }, + { 1, 1, 0, 6, 48, 28, }, + { 0, 1, 0, 6, 52, 32, }, + { 2, 1, 0, 6, 52, 28, }, + { 1, 1, 0, 6, 52, 28, }, + { 0, 1, 0, 6, 56, 30, }, + { 2, 1, 0, 6, 56, 28, }, + { 1, 1, 0, 6, 56, 28, }, + { 0, 1, 0, 6, 60, 28, }, + { 2, 1, 0, 6, 60, 28, }, + { 1, 1, 0, 6, 60, 28, }, + { 0, 1, 0, 6, 64, 24, }, + { 2, 1, 0, 6, 64, 28, }, + { 1, 1, 0, 6, 64, 28, }, + { 0, 1, 0, 6, 100, 26, }, + { 2, 1, 0, 6, 100, 28, }, + { 1, 1, 0, 6, 100, 28, }, + { 0, 1, 0, 6, 104, 26, }, + { 2, 1, 0, 6, 104, 28, }, + { 1, 1, 0, 6, 104, 28, }, + { 0, 1, 0, 6, 108, 28, }, + { 2, 1, 0, 6, 108, 28, }, + { 1, 1, 0, 6, 108, 28, }, + { 0, 1, 0, 6, 112, 30, }, + { 2, 1, 0, 6, 112, 28, }, + { 1, 1, 0, 6, 112, 28, }, + { 0, 1, 0, 6, 116, 30, }, + { 2, 1, 0, 6, 116, 28, }, + { 1, 1, 0, 6, 116, 28, }, + { 0, 1, 0, 6, 120, 32, }, + { 2, 1, 0, 6, 120, 28, }, + { 1, 1, 0, 6, 120, 28, }, + { 0, 1, 0, 6, 124, 30, }, + { 2, 1, 0, 6, 124, 28, }, + { 1, 1, 0, 6, 124, 28, }, + { 0, 1, 0, 6, 128, 28, }, + { 2, 1, 0, 6, 128, 28, }, + { 1, 1, 0, 6, 128, 28, }, + { 0, 1, 0, 6, 132, 26, }, + { 2, 1, 0, 6, 132, 28, }, + { 1, 1, 0, 6, 132, 28, }, + { 0, 1, 0, 6, 136, 26, }, + { 2, 1, 0, 6, 136, 28, }, + { 1, 1, 0, 6, 136, 28, }, + { 0, 1, 0, 6, 140, 24, }, + { 2, 1, 0, 6, 140, 28, }, + { 1, 1, 0, 6, 140, 28, }, + { 0, 1, 0, 6, 149, 32, }, + { 2, 1, 0, 6, 149, 28, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 32, }, + { 2, 1, 0, 6, 153, 28, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 32, }, + { 2, 1, 0, 6, 157, 28, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 32, }, + { 2, 1, 0, 6, 161, 28, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 32, }, + { 2, 1, 0, 6, 165, 28, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 24, }, + { 2, 1, 0, 7, 36, 26, }, + { 1, 1, 0, 7, 36, 26, }, + { 0, 1, 0, 7, 40, 24, }, + { 2, 1, 0, 7, 40, 26, }, + { 1, 1, 0, 7, 40, 26, }, + { 0, 1, 0, 7, 44, 24, }, + { 2, 1, 0, 7, 44, 26, }, + { 1, 1, 0, 7, 44, 26, }, + { 0, 1, 0, 7, 48, 24, }, + { 2, 1, 0, 7, 48, 26, }, + { 1, 1, 0, 7, 48, 26, }, + { 0, 1, 0, 7, 52, 30, }, + { 2, 1, 0, 7, 52, 26, }, + { 1, 1, 0, 7, 52, 26, }, + { 0, 1, 0, 7, 56, 28, }, + { 2, 1, 0, 7, 56, 26, }, + { 1, 1, 0, 7, 56, 26, }, + { 0, 1, 0, 7, 60, 26, }, + { 2, 1, 0, 7, 60, 26, }, + { 1, 1, 0, 7, 60, 26, }, + { 0, 1, 0, 7, 64, 22, }, + { 2, 1, 0, 7, 64, 26, }, + { 1, 1, 0, 7, 64, 26, }, + { 0, 1, 0, 7, 100, 24, }, + { 2, 1, 0, 7, 100, 26, }, + { 1, 1, 0, 7, 100, 26, }, + { 0, 1, 0, 7, 104, 24, }, + { 2, 1, 0, 7, 104, 26, }, + { 1, 1, 0, 7, 104, 26, }, + { 0, 1, 0, 7, 108, 26, }, + { 2, 1, 0, 7, 108, 26, }, + { 1, 1, 0, 7, 108, 26, }, + { 0, 1, 0, 7, 112, 28, }, + { 2, 1, 0, 7, 112, 26, }, + { 1, 1, 0, 7, 112, 26, }, + { 0, 1, 0, 7, 116, 28, }, + { 2, 1, 0, 7, 116, 26, }, + { 1, 1, 0, 7, 116, 26, }, + { 0, 1, 0, 7, 120, 30, }, + { 2, 1, 0, 7, 120, 26, }, + { 1, 1, 0, 7, 120, 26, }, + { 0, 1, 0, 7, 124, 28, }, + { 2, 1, 0, 7, 124, 26, }, + { 1, 1, 0, 7, 124, 26, }, + { 0, 1, 0, 7, 128, 26, }, + { 2, 1, 0, 7, 128, 26, }, + { 1, 1, 0, 7, 128, 26, }, + { 0, 1, 0, 7, 132, 24, }, + { 2, 1, 0, 7, 132, 26, }, + { 1, 1, 0, 7, 132, 26, }, + { 0, 1, 0, 7, 136, 24, }, + { 2, 1, 0, 7, 136, 26, }, + { 1, 1, 0, 7, 136, 26, }, + { 0, 1, 0, 7, 140, 22, }, + { 2, 1, 0, 7, 140, 26, }, + { 1, 1, 0, 7, 140, 26, }, + { 0, 1, 0, 7, 149, 30, }, + { 2, 1, 0, 7, 149, 26, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 30, }, + { 2, 1, 0, 7, 153, 26, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 30, }, + { 2, 1, 0, 7, 157, 26, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 30, }, + { 2, 1, 0, 7, 161, 26, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 30, }, + { 2, 1, 0, 7, 165, 26, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 30, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 30, }, + { 2, 1, 1, 2, 46, 32, }, + { 1, 1, 1, 2, 46, 32, }, + { 0, 1, 1, 2, 54, 32, }, + { 2, 1, 1, 2, 54, 32, }, + { 1, 1, 1, 2, 54, 32, }, + { 0, 1, 1, 2, 62, 32, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 28, }, + { 2, 1, 1, 2, 102, 32, }, + { 1, 1, 1, 2, 102, 32, }, + { 0, 1, 1, 2, 110, 32, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 36, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 34, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 32, }, + { 2, 1, 1, 2, 134, 32, }, + { 1, 1, 1, 2, 134, 32, }, + { 0, 1, 1, 2, 151, 36, }, + { 2, 1, 1, 2, 151, 32, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 36, }, + { 2, 1, 1, 2, 159, 32, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 28, }, + { 2, 1, 1, 3, 38, 30, }, + { 1, 1, 1, 3, 38, 30, }, + { 0, 1, 1, 3, 46, 28, }, + { 2, 1, 1, 3, 46, 30, }, + { 1, 1, 1, 3, 46, 30, }, + { 0, 1, 1, 3, 54, 30, }, + { 2, 1, 1, 3, 54, 30, }, + { 1, 1, 1, 3, 54, 30, }, + { 0, 1, 1, 3, 62, 30, }, + { 2, 1, 1, 3, 62, 30, }, + { 1, 1, 1, 3, 62, 30, }, + { 0, 1, 1, 3, 102, 26, }, + { 2, 1, 1, 3, 102, 30, }, + { 1, 1, 1, 3, 102, 30, }, + { 0, 1, 1, 3, 110, 30, }, + { 2, 1, 1, 3, 110, 30, }, + { 1, 1, 1, 3, 110, 30, }, + { 0, 1, 1, 3, 118, 34, }, + { 2, 1, 1, 3, 118, 30, }, + { 1, 1, 1, 3, 118, 30, }, + { 0, 1, 1, 3, 126, 32, }, + { 2, 1, 1, 3, 126, 30, }, + { 1, 1, 1, 3, 126, 30, }, + { 0, 1, 1, 3, 134, 30, }, + { 2, 1, 1, 3, 134, 30, }, + { 1, 1, 1, 3, 134, 30, }, + { 0, 1, 1, 3, 151, 34, }, + { 2, 1, 1, 3, 151, 30, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 34, }, + { 2, 1, 1, 3, 159, 30, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 26, }, + { 2, 1, 1, 6, 38, 28, }, + { 1, 1, 1, 6, 38, 28, }, + { 0, 1, 1, 6, 46, 26, }, + { 2, 1, 1, 6, 46, 28, }, + { 1, 1, 1, 6, 46, 28, }, + { 0, 1, 1, 6, 54, 28, }, + { 2, 1, 1, 6, 54, 28, }, + { 1, 1, 1, 6, 54, 28, }, + { 0, 1, 1, 6, 62, 28, }, + { 2, 1, 1, 6, 62, 28, }, + { 1, 1, 1, 6, 62, 28, }, + { 0, 1, 1, 6, 102, 24, }, + { 2, 1, 1, 6, 102, 28, }, + { 1, 1, 1, 6, 102, 28, }, + { 0, 1, 1, 6, 110, 28, }, + { 2, 1, 1, 6, 110, 28, }, + { 1, 1, 1, 6, 110, 28, }, + { 0, 1, 1, 6, 118, 32, }, + { 2, 1, 1, 6, 118, 28, }, + { 1, 1, 1, 6, 118, 28, }, + { 0, 1, 1, 6, 126, 30, }, + { 2, 1, 1, 6, 126, 28, }, + { 1, 1, 1, 6, 126, 28, }, + { 0, 1, 1, 6, 134, 28, }, + { 2, 1, 1, 6, 134, 28, }, + { 1, 1, 1, 6, 134, 28, }, + { 0, 1, 1, 6, 151, 32, }, + { 2, 1, 1, 6, 151, 28, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 32, }, + { 2, 1, 1, 6, 159, 28, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 24, }, + { 2, 1, 1, 7, 38, 26, }, + { 1, 1, 1, 7, 38, 26, }, + { 0, 1, 1, 7, 46, 24, }, + { 2, 1, 1, 7, 46, 26, }, + { 1, 1, 1, 7, 46, 26, }, + { 0, 1, 1, 7, 54, 26, }, + { 2, 1, 1, 7, 54, 26, }, + { 1, 1, 1, 7, 54, 26, }, + { 0, 1, 1, 7, 62, 26, }, + { 2, 1, 1, 7, 62, 26, }, + { 1, 1, 1, 7, 62, 26, }, + { 0, 1, 1, 7, 102, 22, }, + { 2, 1, 1, 7, 102, 26, }, + { 1, 1, 1, 7, 102, 26, }, + { 0, 1, 1, 7, 110, 26, }, + { 2, 1, 1, 7, 110, 26, }, + { 1, 1, 1, 7, 110, 26, }, + { 0, 1, 1, 7, 118, 30, }, + { 2, 1, 1, 7, 118, 26, }, + { 1, 1, 1, 7, 118, 26, }, + { 0, 1, 1, 7, 126, 28, }, + { 2, 1, 1, 7, 126, 26, }, + { 1, 1, 1, 7, 126, 26, }, + { 0, 1, 1, 7, 134, 26, }, + { 2, 1, 1, 7, 134, 26, }, + { 1, 1, 1, 7, 134, 26, }, + { 0, 1, 1, 7, 151, 30, }, + { 2, 1, 1, 7, 151, 26, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 30, }, + { 2, 1, 1, 7, 159, 26, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 30, }, + { 2, 1, 2, 4, 42, 32, }, + { 1, 1, 2, 4, 42, 32, }, + { 0, 1, 2, 4, 58, 28, }, + { 2, 1, 2, 4, 58, 32, }, + { 1, 1, 2, 4, 58, 32, }, + { 0, 1, 2, 4, 106, 30, }, + { 2, 1, 2, 4, 106, 32, }, + { 1, 1, 2, 4, 106, 32, }, + { 0, 1, 2, 4, 122, 34, }, + { 2, 1, 2, 4, 122, 32, }, + { 1, 1, 2, 4, 122, 32, }, + { 0, 1, 2, 4, 155, 36, }, + { 2, 1, 2, 4, 155, 32, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 28, }, + { 2, 1, 2, 5, 42, 30, }, + { 1, 1, 2, 5, 42, 30, }, + { 0, 1, 2, 5, 58, 26, }, + { 2, 1, 2, 5, 58, 30, }, + { 1, 1, 2, 5, 58, 30, }, + { 0, 1, 2, 5, 106, 28, }, + { 2, 1, 2, 5, 106, 30, }, + { 1, 1, 2, 5, 106, 30, }, + { 0, 1, 2, 5, 122, 32, }, + { 2, 1, 2, 5, 122, 30, }, + { 1, 1, 2, 5, 122, 30, }, + { 0, 1, 2, 5, 155, 34, }, + { 2, 1, 2, 5, 155, 30, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 26, }, + { 2, 1, 2, 8, 42, 28, }, + { 1, 1, 2, 8, 42, 28, }, + { 0, 1, 2, 8, 58, 24, }, + { 2, 1, 2, 8, 58, 28, }, + { 1, 1, 2, 8, 58, 28, }, + { 0, 1, 2, 8, 106, 26, }, + { 2, 1, 2, 8, 106, 28, }, + { 1, 1, 2, 8, 106, 28, }, + { 0, 1, 2, 8, 122, 30, }, + { 2, 1, 2, 8, 122, 28, }, + { 1, 1, 2, 8, 122, 28, }, + { 0, 1, 2, 8, 155, 32, }, + { 2, 1, 2, 8, 155, 28, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 24, }, + { 2, 1, 2, 9, 42, 26, }, + { 1, 1, 2, 9, 42, 26, }, + { 0, 1, 2, 9, 58, 22, }, + { 2, 1, 2, 9, 58, 26, }, + { 1, 1, 2, 9, 58, 26, }, + { 0, 1, 2, 9, 106, 24, }, + { 2, 1, 2, 9, 106, 26, }, + { 1, 1, 2, 9, 106, 26, }, + { 0, 1, 2, 9, 122, 28, }, + { 2, 1, 2, 9, 122, 26, }, + { 1, 1, 2, 9, 122, 26, }, + { 0, 1, 2, 9, 155, 30, }, + { 2, 1, 2, 9, 155, 26, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type0[] = { + { 0, 0, 0, 0, 1, 32, }, + { 2, 0, 0, 0, 1, 32, }, + { 1, 0, 0, 0, 1, 32, }, + { 0, 0, 0, 0, 2, 32, }, + { 2, 0, 0, 0, 2, 32, }, + { 1, 0, 0, 0, 2, 32, }, + { 0, 0, 0, 0, 3, 32, }, + { 2, 0, 0, 0, 3, 32, }, + { 1, 0, 0, 0, 3, 32, }, + { 0, 0, 0, 0, 4, 32, }, + { 2, 0, 0, 0, 4, 32, }, + { 1, 0, 0, 0, 4, 32, }, + { 0, 0, 0, 0, 5, 32, }, + { 2, 0, 0, 0, 5, 32, }, + { 1, 0, 0, 0, 5, 32, }, + { 0, 0, 0, 0, 6, 32, }, + { 2, 0, 0, 0, 6, 32, }, + { 1, 0, 0, 0, 6, 32, }, + { 0, 0, 0, 0, 7, 32, }, + { 2, 0, 0, 0, 7, 32, }, + { 1, 0, 0, 0, 7, 32, }, + { 0, 0, 0, 0, 8, 32, }, + { 2, 0, 0, 0, 8, 32, }, + { 1, 0, 0, 0, 8, 32, }, + { 0, 0, 0, 0, 9, 32, }, + { 2, 0, 0, 0, 9, 32, }, + { 1, 0, 0, 0, 9, 32, }, + { 0, 0, 0, 0, 10, 32, }, + { 2, 0, 0, 0, 10, 32, }, + { 1, 0, 0, 0, 10, 32, }, + { 0, 0, 0, 0, 11, 32, }, + { 2, 0, 0, 0, 11, 32, }, + { 1, 0, 0, 0, 11, 32, }, + { 0, 0, 0, 0, 12, 24, }, + { 2, 0, 0, 0, 12, 32, }, + { 1, 0, 0, 0, 12, 32, }, + { 0, 0, 0, 0, 13, 16, }, + { 2, 0, 0, 0, 13, 32, }, + { 1, 0, 0, 0, 13, 32, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 32, }, + { 0, 0, 0, 1, 1, 28, }, + { 2, 0, 0, 1, 1, 32, }, + { 1, 0, 0, 1, 1, 32, }, + { 0, 0, 0, 1, 2, 32, }, + { 2, 0, 0, 1, 2, 32, }, + { 1, 0, 0, 1, 2, 32, }, + { 0, 0, 0, 1, 3, 32, }, + { 2, 0, 0, 1, 3, 32, }, + { 1, 0, 0, 1, 3, 32, }, + { 0, 0, 0, 1, 4, 32, }, + { 2, 0, 0, 1, 4, 32, }, + { 1, 0, 0, 1, 4, 32, }, + { 0, 0, 0, 1, 5, 32, }, + { 2, 0, 0, 1, 5, 32, }, + { 1, 0, 0, 1, 5, 32, }, + { 0, 0, 0, 1, 6, 32, }, + { 2, 0, 0, 1, 6, 32, }, + { 1, 0, 0, 1, 6, 32, }, + { 0, 0, 0, 1, 7, 32, }, + { 2, 0, 0, 1, 7, 32, }, + { 1, 0, 0, 1, 7, 32, }, + { 0, 0, 0, 1, 8, 32, }, + { 2, 0, 0, 1, 8, 32, }, + { 1, 0, 0, 1, 8, 32, }, + { 0, 0, 0, 1, 9, 32, }, + { 2, 0, 0, 1, 9, 32, }, + { 1, 0, 0, 1, 9, 32, }, + { 0, 0, 0, 1, 10, 32, }, + { 2, 0, 0, 1, 10, 32, }, + { 1, 0, 0, 1, 10, 32, }, + { 0, 0, 0, 1, 11, 28, }, + { 2, 0, 0, 1, 11, 32, }, + { 1, 0, 0, 1, 11, 32, }, + { 0, 0, 0, 1, 12, 18, }, + { 2, 0, 0, 1, 12, 32, }, + { 1, 0, 0, 1, 12, 32, }, + { 0, 0, 0, 1, 13, 8, }, + { 2, 0, 0, 1, 13, 32, }, + { 1, 0, 0, 1, 13, 32, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 26, }, + { 2, 0, 0, 2, 1, 32, }, + { 1, 0, 0, 2, 1, 32, }, + { 0, 0, 0, 2, 2, 32, }, + { 2, 0, 0, 2, 2, 32, }, + { 1, 0, 0, 2, 2, 32, }, + { 0, 0, 0, 2, 3, 32, }, + { 2, 0, 0, 2, 3, 32, }, + { 1, 0, 0, 2, 3, 32, }, + { 0, 0, 0, 2, 4, 32, }, + { 2, 0, 0, 2, 4, 32, }, + { 1, 0, 0, 2, 4, 32, }, + { 0, 0, 0, 2, 5, 32, }, + { 2, 0, 0, 2, 5, 32, }, + { 1, 0, 0, 2, 5, 32, }, + { 0, 0, 0, 2, 6, 32, }, + { 2, 0, 0, 2, 6, 32, }, + { 1, 0, 0, 2, 6, 32, }, + { 0, 0, 0, 2, 7, 32, }, + { 2, 0, 0, 2, 7, 32, }, + { 1, 0, 0, 2, 7, 32, }, + { 0, 0, 0, 2, 8, 32, }, + { 2, 0, 0, 2, 8, 32, }, + { 1, 0, 0, 2, 8, 32, }, + { 0, 0, 0, 2, 9, 32, }, + { 2, 0, 0, 2, 9, 32, }, + { 1, 0, 0, 2, 9, 32, }, + { 0, 0, 0, 2, 10, 32, }, + { 2, 0, 0, 2, 10, 32, }, + { 1, 0, 0, 2, 10, 32, }, + { 0, 0, 0, 2, 11, 26, }, + { 2, 0, 0, 2, 11, 32, }, + { 1, 0, 0, 2, 11, 32, }, + { 0, 0, 0, 2, 12, 16, }, + { 2, 0, 0, 2, 12, 32, }, + { 1, 0, 0, 2, 12, 32, }, + { 0, 0, 0, 2, 13, 6, }, + { 2, 0, 0, 2, 13, 32, }, + { 1, 0, 0, 2, 13, 32, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 24, }, + { 2, 0, 0, 3, 1, 30, }, + { 1, 0, 0, 3, 1, 30, }, + { 0, 0, 0, 3, 2, 30, }, + { 2, 0, 0, 3, 2, 30, }, + { 1, 0, 0, 3, 2, 30, }, + { 0, 0, 0, 3, 3, 30, }, + { 2, 0, 0, 3, 3, 30, }, + { 1, 0, 0, 3, 3, 30, }, + { 0, 0, 0, 3, 4, 30, }, + { 2, 0, 0, 3, 4, 30, }, + { 1, 0, 0, 3, 4, 30, }, + { 0, 0, 0, 3, 5, 30, }, + { 2, 0, 0, 3, 5, 30, }, + { 1, 0, 0, 3, 5, 30, }, + { 0, 0, 0, 3, 6, 30, }, + { 2, 0, 0, 3, 6, 30, }, + { 1, 0, 0, 3, 6, 30, }, + { 0, 0, 0, 3, 7, 30, }, + { 2, 0, 0, 3, 7, 30, }, + { 1, 0, 0, 3, 7, 30, }, + { 0, 0, 0, 3, 8, 30, }, + { 2, 0, 0, 3, 8, 30, }, + { 1, 0, 0, 3, 8, 30, }, + { 0, 0, 0, 3, 9, 30, }, + { 2, 0, 0, 3, 9, 30, }, + { 1, 0, 0, 3, 9, 30, }, + { 0, 0, 0, 3, 10, 30, }, + { 2, 0, 0, 3, 10, 30, }, + { 1, 0, 0, 3, 10, 30, }, + { 0, 0, 0, 3, 11, 24, }, + { 2, 0, 0, 3, 11, 30, }, + { 1, 0, 0, 3, 11, 30, }, + { 0, 0, 0, 3, 12, 14, }, + { 2, 0, 0, 3, 12, 30, }, + { 1, 0, 0, 3, 12, 30, }, + { 0, 0, 0, 3, 13, 4, }, + { 2, 0, 0, 3, 13, 30, }, + { 1, 0, 0, 3, 13, 30, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 22, }, + { 2, 0, 0, 6, 1, 28, }, + { 1, 0, 0, 6, 1, 28, }, + { 0, 0, 0, 6, 2, 28, }, + { 2, 0, 0, 6, 2, 28, }, + { 1, 0, 0, 6, 2, 28, }, + { 0, 0, 0, 6, 3, 28, }, + { 2, 0, 0, 6, 3, 28, }, + { 1, 0, 0, 6, 3, 28, }, + { 0, 0, 0, 6, 4, 28, }, + { 2, 0, 0, 6, 4, 28, }, + { 1, 0, 0, 6, 4, 28, }, + { 0, 0, 0, 6, 5, 28, }, + { 2, 0, 0, 6, 5, 28, }, + { 1, 0, 0, 6, 5, 28, }, + { 0, 0, 0, 6, 6, 28, }, + { 2, 0, 0, 6, 6, 28, }, + { 1, 0, 0, 6, 6, 28, }, + { 0, 0, 0, 6, 7, 28, }, + { 2, 0, 0, 6, 7, 28, }, + { 1, 0, 0, 6, 7, 28, }, + { 0, 0, 0, 6, 8, 28, }, + { 2, 0, 0, 6, 8, 28, }, + { 1, 0, 0, 6, 8, 28, }, + { 0, 0, 0, 6, 9, 28, }, + { 2, 0, 0, 6, 9, 28, }, + { 1, 0, 0, 6, 9, 28, }, + { 0, 0, 0, 6, 10, 28, }, + { 2, 0, 0, 6, 10, 28, }, + { 1, 0, 0, 6, 10, 28, }, + { 0, 0, 0, 6, 11, 22, }, + { 2, 0, 0, 6, 11, 28, }, + { 1, 0, 0, 6, 11, 28, }, + { 0, 0, 0, 6, 12, 14, }, + { 2, 0, 0, 6, 12, 28, }, + { 1, 0, 0, 6, 12, 28, }, + { 0, 0, 0, 6, 13, 4, }, + { 2, 0, 0, 6, 13, 28, }, + { 1, 0, 0, 6, 13, 28, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 20, }, + { 2, 0, 0, 7, 1, 26, }, + { 1, 0, 0, 7, 1, 26, }, + { 0, 0, 0, 7, 2, 26, }, + { 2, 0, 0, 7, 2, 26, }, + { 1, 0, 0, 7, 2, 26, }, + { 0, 0, 0, 7, 3, 26, }, + { 2, 0, 0, 7, 3, 26, }, + { 1, 0, 0, 7, 3, 26, }, + { 0, 0, 0, 7, 4, 26, }, + { 2, 0, 0, 7, 4, 26, }, + { 1, 0, 0, 7, 4, 26, }, + { 0, 0, 0, 7, 5, 26, }, + { 2, 0, 0, 7, 5, 26, }, + { 1, 0, 0, 7, 5, 26, }, + { 0, 0, 0, 7, 6, 26, }, + { 2, 0, 0, 7, 6, 26, }, + { 1, 0, 0, 7, 6, 26, }, + { 0, 0, 0, 7, 7, 26, }, + { 2, 0, 0, 7, 7, 26, }, + { 1, 0, 0, 7, 7, 26, }, + { 0, 0, 0, 7, 8, 26, }, + { 2, 0, 0, 7, 8, 26, }, + { 1, 0, 0, 7, 8, 26, }, + { 0, 0, 0, 7, 9, 26, }, + { 2, 0, 0, 7, 9, 26, }, + { 1, 0, 0, 7, 9, 26, }, + { 0, 0, 0, 7, 10, 26, }, + { 2, 0, 0, 7, 10, 26, }, + { 1, 0, 0, 7, 10, 26, }, + { 0, 0, 0, 7, 11, 20, }, + { 2, 0, 0, 7, 11, 26, }, + { 1, 0, 0, 7, 11, 26, }, + { 0, 0, 0, 7, 12, 14, }, + { 2, 0, 0, 7, 12, 26, }, + { 1, 0, 0, 7, 12, 26, }, + { 0, 0, 0, 7, 13, 4, }, + { 2, 0, 0, 7, 13, 26, }, + { 1, 0, 0, 7, 13, 26, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 26, }, + { 2, 0, 1, 2, 3, 32, }, + { 1, 0, 1, 2, 3, 32, }, + { 0, 0, 1, 2, 4, 32, }, + { 2, 0, 1, 2, 4, 32, }, + { 1, 0, 1, 2, 4, 32, }, + { 0, 0, 1, 2, 5, 32, }, + { 2, 0, 1, 2, 5, 32, }, + { 1, 0, 1, 2, 5, 32, }, + { 0, 0, 1, 2, 6, 32, }, + { 2, 0, 1, 2, 6, 32, }, + { 1, 0, 1, 2, 6, 32, }, + { 0, 0, 1, 2, 7, 32, }, + { 2, 0, 1, 2, 7, 32, }, + { 1, 0, 1, 2, 7, 32, }, + { 0, 0, 1, 2, 8, 32, }, + { 2, 0, 1, 2, 8, 32, }, + { 1, 0, 1, 2, 8, 32, }, + { 0, 0, 1, 2, 9, 32, }, + { 2, 0, 1, 2, 9, 32, }, + { 1, 0, 1, 2, 9, 32, }, + { 0, 0, 1, 2, 10, 32, }, + { 2, 0, 1, 2, 10, 32, }, + { 1, 0, 1, 2, 10, 32, }, + { 0, 0, 1, 2, 11, 26, }, + { 2, 0, 1, 2, 11, 32, }, + { 1, 0, 1, 2, 11, 32, }, + { 0, 0, 1, 2, 12, 16, }, + { 2, 0, 1, 2, 12, 32, }, + { 1, 0, 1, 2, 12, 32, }, + { 0, 0, 1, 2, 13, 10, }, + { 2, 0, 1, 2, 13, 32, }, + { 1, 0, 1, 2, 13, 32, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 24, }, + { 2, 0, 1, 3, 3, 30, }, + { 1, 0, 1, 3, 3, 30, }, + { 0, 0, 1, 3, 4, 30, }, + { 2, 0, 1, 3, 4, 30, }, + { 1, 0, 1, 3, 4, 30, }, + { 0, 0, 1, 3, 5, 30, }, + { 2, 0, 1, 3, 5, 30, }, + { 1, 0, 1, 3, 5, 30, }, + { 0, 0, 1, 3, 6, 30, }, + { 2, 0, 1, 3, 6, 30, }, + { 1, 0, 1, 3, 6, 30, }, + { 0, 0, 1, 3, 7, 30, }, + { 2, 0, 1, 3, 7, 30, }, + { 1, 0, 1, 3, 7, 30, }, + { 0, 0, 1, 3, 8, 30, }, + { 2, 0, 1, 3, 8, 30, }, + { 1, 0, 1, 3, 8, 30, }, + { 0, 0, 1, 3, 9, 30, }, + { 2, 0, 1, 3, 9, 30, }, + { 1, 0, 1, 3, 9, 30, }, + { 0, 0, 1, 3, 10, 30, }, + { 2, 0, 1, 3, 10, 30, }, + { 1, 0, 1, 3, 10, 30, }, + { 0, 0, 1, 3, 11, 24, }, + { 2, 0, 1, 3, 11, 30, }, + { 1, 0, 1, 3, 11, 30, }, + { 0, 0, 1, 3, 12, 14, }, + { 2, 0, 1, 3, 12, 30, }, + { 1, 0, 1, 3, 12, 30, }, + { 0, 0, 1, 3, 13, 8, }, + { 2, 0, 1, 3, 13, 30, }, + { 1, 0, 1, 3, 13, 30, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 22, }, + { 2, 0, 1, 6, 3, 28, }, + { 1, 0, 1, 6, 3, 28, }, + { 0, 0, 1, 6, 4, 28, }, + { 2, 0, 1, 6, 4, 28, }, + { 1, 0, 1, 6, 4, 28, }, + { 0, 0, 1, 6, 5, 28, }, + { 2, 0, 1, 6, 5, 28, }, + { 1, 0, 1, 6, 5, 28, }, + { 0, 0, 1, 6, 6, 28, }, + { 2, 0, 1, 6, 6, 28, }, + { 1, 0, 1, 6, 6, 28, }, + { 0, 0, 1, 6, 7, 28, }, + { 2, 0, 1, 6, 7, 28, }, + { 1, 0, 1, 6, 7, 28, }, + { 0, 0, 1, 6, 8, 28, }, + { 2, 0, 1, 6, 8, 28, }, + { 1, 0, 1, 6, 8, 28, }, + { 0, 0, 1, 6, 9, 28, }, + { 2, 0, 1, 6, 9, 28, }, + { 1, 0, 1, 6, 9, 28, }, + { 0, 0, 1, 6, 10, 28, }, + { 2, 0, 1, 6, 10, 28, }, + { 1, 0, 1, 6, 10, 28, }, + { 0, 0, 1, 6, 11, 22, }, + { 2, 0, 1, 6, 11, 28, }, + { 1, 0, 1, 6, 11, 28, }, + { 0, 0, 1, 6, 12, 14, }, + { 2, 0, 1, 6, 12, 28, }, + { 1, 0, 1, 6, 12, 28, }, + { 0, 0, 1, 6, 13, 8, }, + { 2, 0, 1, 6, 13, 28, }, + { 1, 0, 1, 6, 13, 28, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 20, }, + { 2, 0, 1, 7, 3, 26, }, + { 1, 0, 1, 7, 3, 26, }, + { 0, 0, 1, 7, 4, 26, }, + { 2, 0, 1, 7, 4, 26, }, + { 1, 0, 1, 7, 4, 26, }, + { 0, 0, 1, 7, 5, 26, }, + { 2, 0, 1, 7, 5, 26, }, + { 1, 0, 1, 7, 5, 26, }, + { 0, 0, 1, 7, 6, 26, }, + { 2, 0, 1, 7, 6, 26, }, + { 1, 0, 1, 7, 6, 26, }, + { 0, 0, 1, 7, 7, 26, }, + { 2, 0, 1, 7, 7, 26, }, + { 1, 0, 1, 7, 7, 26, }, + { 0, 0, 1, 7, 8, 26, }, + { 2, 0, 1, 7, 8, 26, }, + { 1, 0, 1, 7, 8, 26, }, + { 0, 0, 1, 7, 9, 26, }, + { 2, 0, 1, 7, 9, 26, }, + { 1, 0, 1, 7, 9, 26, }, + { 0, 0, 1, 7, 10, 26, }, + { 2, 0, 1, 7, 10, 26, }, + { 1, 0, 1, 7, 10, 26, }, + { 0, 0, 1, 7, 11, 20, }, + { 2, 0, 1, 7, 11, 26, }, + { 1, 0, 1, 7, 11, 26, }, + { 0, 0, 1, 7, 12, 14, }, + { 2, 0, 1, 7, 12, 26, }, + { 1, 0, 1, 7, 12, 26, }, + { 0, 0, 1, 7, 13, 8, }, + { 2, 0, 1, 7, 13, 26, }, + { 1, 0, 1, 7, 13, 26, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 28, }, + { 2, 1, 0, 1, 36, 32, }, + { 1, 1, 0, 1, 36, 32, }, + { 0, 1, 0, 1, 40, 32, }, + { 2, 1, 0, 1, 40, 32, }, + { 1, 1, 0, 1, 40, 32, }, + { 0, 1, 0, 1, 44, 32, }, + { 2, 1, 0, 1, 44, 32, }, + { 1, 1, 0, 1, 44, 32, }, + { 0, 1, 0, 1, 48, 32, }, + { 2, 1, 0, 1, 48, 32, }, + { 1, 1, 0, 1, 48, 32, }, + { 0, 1, 0, 1, 52, 32, }, + { 2, 1, 0, 1, 52, 32, }, + { 1, 1, 0, 1, 52, 32, }, + { 0, 1, 0, 1, 56, 32, }, + { 2, 1, 0, 1, 56, 32, }, + { 1, 1, 0, 1, 56, 32, }, + { 0, 1, 0, 1, 60, 32, }, + { 2, 1, 0, 1, 60, 32, }, + { 1, 1, 0, 1, 60, 32, }, + { 0, 1, 0, 1, 64, 28, }, + { 2, 1, 0, 1, 64, 32, }, + { 1, 1, 0, 1, 64, 32, }, + { 0, 1, 0, 1, 100, 28, }, + { 2, 1, 0, 1, 100, 32, }, + { 1, 1, 0, 1, 100, 32, }, + { 0, 1, 0, 1, 104, 32, }, + { 2, 1, 0, 1, 104, 32, }, + { 1, 1, 0, 1, 104, 32, }, + { 0, 1, 0, 1, 108, 32, }, + { 2, 1, 0, 1, 108, 32, }, + { 1, 1, 0, 1, 108, 32, }, + { 0, 1, 0, 1, 112, 32, }, + { 2, 1, 0, 1, 112, 32, }, + { 1, 1, 0, 1, 112, 32, }, + { 0, 1, 0, 1, 116, 32, }, + { 2, 1, 0, 1, 116, 32, }, + { 1, 1, 0, 1, 116, 32, }, + { 0, 1, 0, 1, 120, 32, }, + { 2, 1, 0, 1, 120, 32, }, + { 1, 1, 0, 1, 120, 32, }, + { 0, 1, 0, 1, 124, 32, }, + { 2, 1, 0, 1, 124, 32, }, + { 1, 1, 0, 1, 124, 32, }, + { 0, 1, 0, 1, 128, 32, }, + { 2, 1, 0, 1, 128, 32, }, + { 1, 1, 0, 1, 128, 32, }, + { 0, 1, 0, 1, 132, 32, }, + { 2, 1, 0, 1, 132, 32, }, + { 1, 1, 0, 1, 132, 32, }, + { 0, 1, 0, 1, 136, 32, }, + { 2, 1, 0, 1, 136, 32, }, + { 1, 1, 0, 1, 136, 32, }, + { 0, 1, 0, 1, 140, 28, }, + { 2, 1, 0, 1, 140, 32, }, + { 1, 1, 0, 1, 140, 32, }, + { 0, 1, 0, 1, 149, 28, }, + { 2, 1, 0, 1, 149, 32, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 32, }, + { 2, 1, 0, 1, 153, 32, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 32, }, + { 2, 1, 0, 1, 157, 32, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 32, }, + { 2, 1, 0, 1, 161, 32, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 32, }, + { 2, 1, 0, 1, 165, 32, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 26, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 32, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 32, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 32, }, + { 2, 1, 0, 2, 48, 32, }, + { 1, 1, 0, 2, 48, 32, }, + { 0, 1, 0, 2, 52, 32, }, + { 2, 1, 0, 2, 52, 32, }, + { 1, 1, 0, 2, 52, 32, }, + { 0, 1, 0, 2, 56, 32, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 32, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 26, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 26, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 32, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 32, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 32, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 32, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 32, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 32, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 32, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 32, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 32, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 26, }, + { 2, 1, 0, 2, 140, 32, }, + { 1, 1, 0, 2, 140, 32, }, + { 0, 1, 0, 2, 149, 26, }, + { 2, 1, 0, 2, 149, 32, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 32, }, + { 2, 1, 0, 2, 153, 32, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 32, }, + { 2, 1, 0, 2, 157, 32, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 32, }, + { 2, 1, 0, 2, 161, 32, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 32, }, + { 2, 1, 0, 2, 165, 32, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 24, }, + { 2, 1, 0, 3, 36, 28, }, + { 1, 1, 0, 3, 36, 28, }, + { 0, 1, 0, 3, 40, 28, }, + { 2, 1, 0, 3, 40, 28, }, + { 1, 1, 0, 3, 40, 28, }, + { 0, 1, 0, 3, 44, 28, }, + { 2, 1, 0, 3, 44, 28, }, + { 1, 1, 0, 3, 44, 28, }, + { 0, 1, 0, 3, 48, 28, }, + { 2, 1, 0, 3, 48, 28, }, + { 1, 1, 0, 3, 48, 28, }, + { 0, 1, 0, 3, 52, 28, }, + { 2, 1, 0, 3, 52, 28, }, + { 1, 1, 0, 3, 52, 28, }, + { 0, 1, 0, 3, 56, 28, }, + { 2, 1, 0, 3, 56, 28, }, + { 1, 1, 0, 3, 56, 28, }, + { 0, 1, 0, 3, 60, 28, }, + { 2, 1, 0, 3, 60, 28, }, + { 1, 1, 0, 3, 60, 28, }, + { 0, 1, 0, 3, 64, 24, }, + { 2, 1, 0, 3, 64, 28, }, + { 1, 1, 0, 3, 64, 28, }, + { 0, 1, 0, 3, 100, 24, }, + { 2, 1, 0, 3, 100, 28, }, + { 1, 1, 0, 3, 100, 28, }, + { 0, 1, 0, 3, 104, 28, }, + { 2, 1, 0, 3, 104, 28, }, + { 1, 1, 0, 3, 104, 28, }, + { 0, 1, 0, 3, 108, 28, }, + { 2, 1, 0, 3, 108, 28, }, + { 1, 1, 0, 3, 108, 28, }, + { 0, 1, 0, 3, 112, 28, }, + { 2, 1, 0, 3, 112, 28, }, + { 1, 1, 0, 3, 112, 28, }, + { 0, 1, 0, 3, 116, 28, }, + { 2, 1, 0, 3, 116, 28, }, + { 1, 1, 0, 3, 116, 28, }, + { 0, 1, 0, 3, 120, 28, }, + { 2, 1, 0, 3, 120, 28, }, + { 1, 1, 0, 3, 120, 28, }, + { 0, 1, 0, 3, 124, 28, }, + { 2, 1, 0, 3, 124, 28, }, + { 1, 1, 0, 3, 124, 28, }, + { 0, 1, 0, 3, 128, 28, }, + { 2, 1, 0, 3, 128, 28, }, + { 1, 1, 0, 3, 128, 28, }, + { 0, 1, 0, 3, 132, 28, }, + { 2, 1, 0, 3, 132, 28, }, + { 1, 1, 0, 3, 132, 28, }, + { 0, 1, 0, 3, 136, 28, }, + { 2, 1, 0, 3, 136, 28, }, + { 1, 1, 0, 3, 136, 28, }, + { 0, 1, 0, 3, 140, 24, }, + { 2, 1, 0, 3, 140, 28, }, + { 1, 1, 0, 3, 140, 28, }, + { 0, 1, 0, 3, 149, 24, }, + { 2, 1, 0, 3, 149, 28, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 28, }, + { 2, 1, 0, 3, 153, 28, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 28, }, + { 2, 1, 0, 3, 157, 28, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 28, }, + { 2, 1, 0, 3, 161, 28, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 28, }, + { 2, 1, 0, 3, 165, 28, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 22, }, + { 2, 1, 0, 6, 36, 26, }, + { 1, 1, 0, 6, 36, 26, }, + { 0, 1, 0, 6, 40, 26, }, + { 2, 1, 0, 6, 40, 26, }, + { 1, 1, 0, 6, 40, 26, }, + { 0, 1, 0, 6, 44, 26, }, + { 2, 1, 0, 6, 44, 26, }, + { 1, 1, 0, 6, 44, 26, }, + { 0, 1, 0, 6, 48, 26, }, + { 2, 1, 0, 6, 48, 26, }, + { 1, 1, 0, 6, 48, 26, }, + { 0, 1, 0, 6, 52, 26, }, + { 2, 1, 0, 6, 52, 26, }, + { 1, 1, 0, 6, 52, 26, }, + { 0, 1, 0, 6, 56, 26, }, + { 2, 1, 0, 6, 56, 26, }, + { 1, 1, 0, 6, 56, 26, }, + { 0, 1, 0, 6, 60, 26, }, + { 2, 1, 0, 6, 60, 26, }, + { 1, 1, 0, 6, 60, 26, }, + { 0, 1, 0, 6, 64, 22, }, + { 2, 1, 0, 6, 64, 26, }, + { 1, 1, 0, 6, 64, 26, }, + { 0, 1, 0, 6, 100, 22, }, + { 2, 1, 0, 6, 100, 26, }, + { 1, 1, 0, 6, 100, 26, }, + { 0, 1, 0, 6, 104, 26, }, + { 2, 1, 0, 6, 104, 26, }, + { 1, 1, 0, 6, 104, 26, }, + { 0, 1, 0, 6, 108, 26, }, + { 2, 1, 0, 6, 108, 26, }, + { 1, 1, 0, 6, 108, 26, }, + { 0, 1, 0, 6, 112, 26, }, + { 2, 1, 0, 6, 112, 26, }, + { 1, 1, 0, 6, 112, 26, }, + { 0, 1, 0, 6, 116, 26, }, + { 2, 1, 0, 6, 116, 26, }, + { 1, 1, 0, 6, 116, 26, }, + { 0, 1, 0, 6, 120, 26, }, + { 2, 1, 0, 6, 120, 26, }, + { 1, 1, 0, 6, 120, 26, }, + { 0, 1, 0, 6, 124, 26, }, + { 2, 1, 0, 6, 124, 26, }, + { 1, 1, 0, 6, 124, 26, }, + { 0, 1, 0, 6, 128, 26, }, + { 2, 1, 0, 6, 128, 26, }, + { 1, 1, 0, 6, 128, 26, }, + { 0, 1, 0, 6, 132, 26, }, + { 2, 1, 0, 6, 132, 26, }, + { 1, 1, 0, 6, 132, 26, }, + { 0, 1, 0, 6, 136, 26, }, + { 2, 1, 0, 6, 136, 26, }, + { 1, 1, 0, 6, 136, 26, }, + { 0, 1, 0, 6, 140, 22, }, + { 2, 1, 0, 6, 140, 26, }, + { 1, 1, 0, 6, 140, 26, }, + { 0, 1, 0, 6, 149, 22, }, + { 2, 1, 0, 6, 149, 26, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 26, }, + { 2, 1, 0, 6, 153, 26, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 26, }, + { 2, 1, 0, 6, 157, 26, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 26, }, + { 2, 1, 0, 6, 161, 26, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 26, }, + { 2, 1, 0, 6, 165, 26, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 20, }, + { 2, 1, 0, 7, 36, 24, }, + { 1, 1, 0, 7, 36, 24, }, + { 0, 1, 0, 7, 40, 24, }, + { 2, 1, 0, 7, 40, 24, }, + { 1, 1, 0, 7, 40, 24, }, + { 0, 1, 0, 7, 44, 24, }, + { 2, 1, 0, 7, 44, 24, }, + { 1, 1, 0, 7, 44, 24, }, + { 0, 1, 0, 7, 48, 24, }, + { 2, 1, 0, 7, 48, 24, }, + { 1, 1, 0, 7, 48, 24, }, + { 0, 1, 0, 7, 52, 24, }, + { 2, 1, 0, 7, 52, 24, }, + { 1, 1, 0, 7, 52, 24, }, + { 0, 1, 0, 7, 56, 24, }, + { 2, 1, 0, 7, 56, 24, }, + { 1, 1, 0, 7, 56, 24, }, + { 0, 1, 0, 7, 60, 24, }, + { 2, 1, 0, 7, 60, 24, }, + { 1, 1, 0, 7, 60, 24, }, + { 0, 1, 0, 7, 64, 20, }, + { 2, 1, 0, 7, 64, 24, }, + { 1, 1, 0, 7, 64, 24, }, + { 0, 1, 0, 7, 100, 20, }, + { 2, 1, 0, 7, 100, 24, }, + { 1, 1, 0, 7, 100, 24, }, + { 0, 1, 0, 7, 104, 24, }, + { 2, 1, 0, 7, 104, 24, }, + { 1, 1, 0, 7, 104, 24, }, + { 0, 1, 0, 7, 108, 24, }, + { 2, 1, 0, 7, 108, 24, }, + { 1, 1, 0, 7, 108, 24, }, + { 0, 1, 0, 7, 112, 24, }, + { 2, 1, 0, 7, 112, 24, }, + { 1, 1, 0, 7, 112, 24, }, + { 0, 1, 0, 7, 116, 24, }, + { 2, 1, 0, 7, 116, 24, }, + { 1, 1, 0, 7, 116, 24, }, + { 0, 1, 0, 7, 120, 24, }, + { 2, 1, 0, 7, 120, 24, }, + { 1, 1, 0, 7, 120, 24, }, + { 0, 1, 0, 7, 124, 24, }, + { 2, 1, 0, 7, 124, 24, }, + { 1, 1, 0, 7, 124, 24, }, + { 0, 1, 0, 7, 128, 24, }, + { 2, 1, 0, 7, 128, 24, }, + { 1, 1, 0, 7, 128, 24, }, + { 0, 1, 0, 7, 132, 24, }, + { 2, 1, 0, 7, 132, 24, }, + { 1, 1, 0, 7, 132, 24, }, + { 0, 1, 0, 7, 136, 24, }, + { 2, 1, 0, 7, 136, 24, }, + { 1, 1, 0, 7, 136, 24, }, + { 0, 1, 0, 7, 140, 20, }, + { 2, 1, 0, 7, 140, 24, }, + { 1, 1, 0, 7, 140, 24, }, + { 0, 1, 0, 7, 149, 20, }, + { 2, 1, 0, 7, 149, 24, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 24, }, + { 2, 1, 0, 7, 153, 24, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 24, }, + { 2, 1, 0, 7, 157, 24, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 24, }, + { 2, 1, 0, 7, 161, 24, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 24, }, + { 2, 1, 0, 7, 165, 24, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 26, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 32, }, + { 2, 1, 1, 2, 46, 32, }, + { 1, 1, 1, 2, 46, 32, }, + { 0, 1, 1, 2, 54, 32, }, + { 2, 1, 1, 2, 54, 32, }, + { 1, 1, 1, 2, 54, 32, }, + { 0, 1, 1, 2, 62, 26, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 26, }, + { 2, 1, 1, 2, 102, 32, }, + { 1, 1, 1, 2, 102, 32, }, + { 0, 1, 1, 2, 110, 32, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 32, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 32, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 32, }, + { 2, 1, 1, 2, 134, 32, }, + { 1, 1, 1, 2, 134, 32, }, + { 0, 1, 1, 2, 151, 26, }, + { 2, 1, 1, 2, 151, 32, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 32, }, + { 2, 1, 1, 2, 159, 32, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 24, }, + { 2, 1, 1, 3, 38, 28, }, + { 1, 1, 1, 3, 38, 28, }, + { 0, 1, 1, 3, 46, 28, }, + { 2, 1, 1, 3, 46, 28, }, + { 1, 1, 1, 3, 46, 28, }, + { 0, 1, 1, 3, 54, 28, }, + { 2, 1, 1, 3, 54, 28, }, + { 1, 1, 1, 3, 54, 28, }, + { 0, 1, 1, 3, 62, 24, }, + { 2, 1, 1, 3, 62, 28, }, + { 1, 1, 1, 3, 62, 28, }, + { 0, 1, 1, 3, 102, 24, }, + { 2, 1, 1, 3, 102, 28, }, + { 1, 1, 1, 3, 102, 28, }, + { 0, 1, 1, 3, 110, 28, }, + { 2, 1, 1, 3, 110, 28, }, + { 1, 1, 1, 3, 110, 28, }, + { 0, 1, 1, 3, 118, 28, }, + { 2, 1, 1, 3, 118, 28, }, + { 1, 1, 1, 3, 118, 28, }, + { 0, 1, 1, 3, 126, 28, }, + { 2, 1, 1, 3, 126, 28, }, + { 1, 1, 1, 3, 126, 28, }, + { 0, 1, 1, 3, 134, 28, }, + { 2, 1, 1, 3, 134, 28, }, + { 1, 1, 1, 3, 134, 28, }, + { 0, 1, 1, 3, 151, 24, }, + { 2, 1, 1, 3, 151, 28, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 28, }, + { 2, 1, 1, 3, 159, 28, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 20, }, + { 2, 1, 1, 6, 38, 26, }, + { 1, 1, 1, 6, 38, 26, }, + { 0, 1, 1, 6, 46, 26, }, + { 2, 1, 1, 6, 46, 26, }, + { 1, 1, 1, 6, 46, 26, }, + { 0, 1, 1, 6, 54, 26, }, + { 2, 1, 1, 6, 54, 26, }, + { 1, 1, 1, 6, 54, 26, }, + { 0, 1, 1, 6, 62, 20, }, + { 2, 1, 1, 6, 62, 26, }, + { 1, 1, 1, 6, 62, 26, }, + { 0, 1, 1, 6, 102, 20, }, + { 2, 1, 1, 6, 102, 26, }, + { 1, 1, 1, 6, 102, 26, }, + { 0, 1, 1, 6, 110, 26, }, + { 2, 1, 1, 6, 110, 26, }, + { 1, 1, 1, 6, 110, 26, }, + { 0, 1, 1, 6, 118, 26, }, + { 2, 1, 1, 6, 118, 26, }, + { 1, 1, 1, 6, 118, 26, }, + { 0, 1, 1, 6, 126, 26, }, + { 2, 1, 1, 6, 126, 26, }, + { 1, 1, 1, 6, 126, 26, }, + { 0, 1, 1, 6, 134, 26, }, + { 2, 1, 1, 6, 134, 26, }, + { 1, 1, 1, 6, 134, 26, }, + { 0, 1, 1, 6, 151, 20, }, + { 2, 1, 1, 6, 151, 26, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 26, }, + { 2, 1, 1, 6, 159, 26, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 18, }, + { 2, 1, 1, 7, 38, 24, }, + { 1, 1, 1, 7, 38, 24, }, + { 0, 1, 1, 7, 46, 24, }, + { 2, 1, 1, 7, 46, 24, }, + { 1, 1, 1, 7, 46, 24, }, + { 0, 1, 1, 7, 54, 24, }, + { 2, 1, 1, 7, 54, 24, }, + { 1, 1, 1, 7, 54, 24, }, + { 0, 1, 1, 7, 62, 18, }, + { 2, 1, 1, 7, 62, 24, }, + { 1, 1, 1, 7, 62, 24, }, + { 0, 1, 1, 7, 102, 18, }, + { 2, 1, 1, 7, 102, 24, }, + { 1, 1, 1, 7, 102, 24, }, + { 0, 1, 1, 7, 110, 24, }, + { 2, 1, 1, 7, 110, 24, }, + { 1, 1, 1, 7, 110, 24, }, + { 0, 1, 1, 7, 118, 24, }, + { 2, 1, 1, 7, 118, 24, }, + { 1, 1, 1, 7, 118, 24, }, + { 0, 1, 1, 7, 126, 24, }, + { 2, 1, 1, 7, 126, 24, }, + { 1, 1, 1, 7, 126, 24, }, + { 0, 1, 1, 7, 134, 24, }, + { 2, 1, 1, 7, 134, 24, }, + { 1, 1, 1, 7, 134, 24, }, + { 0, 1, 1, 7, 151, 18, }, + { 2, 1, 1, 7, 151, 24, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 24, }, + { 2, 1, 1, 7, 159, 24, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 22, }, + { 2, 1, 2, 4, 42, 30, }, + { 1, 1, 2, 4, 42, 30, }, + { 0, 1, 2, 4, 58, 22, }, + { 2, 1, 2, 4, 58, 30, }, + { 1, 1, 2, 4, 58, 30, }, + { 0, 1, 2, 4, 106, 22, }, + { 2, 1, 2, 4, 106, 30, }, + { 1, 1, 2, 4, 106, 30, }, + { 0, 1, 2, 4, 122, 30, }, + { 2, 1, 2, 4, 122, 30, }, + { 1, 1, 2, 4, 122, 30, }, + { 0, 1, 2, 4, 155, 22, }, + { 2, 1, 2, 4, 155, 30, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 20, }, + { 2, 1, 2, 5, 42, 28, }, + { 1, 1, 2, 5, 42, 28, }, + { 0, 1, 2, 5, 58, 20, }, + { 2, 1, 2, 5, 58, 28, }, + { 1, 1, 2, 5, 58, 28, }, + { 0, 1, 2, 5, 106, 20, }, + { 2, 1, 2, 5, 106, 28, }, + { 1, 1, 2, 5, 106, 28, }, + { 0, 1, 2, 5, 122, 28, }, + { 2, 1, 2, 5, 122, 28, }, + { 1, 1, 2, 5, 122, 28, }, + { 0, 1, 2, 5, 155, 20, }, + { 2, 1, 2, 5, 155, 28, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 18, }, + { 2, 1, 2, 8, 42, 26, }, + { 1, 1, 2, 8, 42, 26, }, + { 0, 1, 2, 8, 58, 18, }, + { 2, 1, 2, 8, 58, 26, }, + { 1, 1, 2, 8, 58, 26, }, + { 0, 1, 2, 8, 106, 18, }, + { 2, 1, 2, 8, 106, 26, }, + { 1, 1, 2, 8, 106, 26, }, + { 0, 1, 2, 8, 122, 26, }, + { 2, 1, 2, 8, 122, 26, }, + { 1, 1, 2, 8, 122, 26, }, + { 0, 1, 2, 8, 155, 18, }, + { 2, 1, 2, 8, 155, 26, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 16, }, + { 2, 1, 2, 9, 42, 24, }, + { 1, 1, 2, 9, 42, 24, }, + { 0, 1, 2, 9, 58, 16, }, + { 2, 1, 2, 9, 58, 24, }, + { 1, 1, 2, 9, 58, 24, }, + { 0, 1, 2, 9, 106, 16, }, + { 2, 1, 2, 9, 106, 24, }, + { 1, 1, 2, 9, 106, 24, }, + { 0, 1, 2, 9, 122, 24, }, + { 2, 1, 2, 9, 122, 24, }, + { 1, 1, 2, 9, 122, 24, }, + { 0, 1, 2, 9, 155, 16, }, + { 2, 1, 2, 9, 155, 24, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type0); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type1[] = { + { 0, 0, 0, 0, 1, 34, }, + { 2, 0, 0, 0, 1, 32, }, + { 1, 0, 0, 0, 1, 32, }, + { 0, 0, 0, 0, 2, 34, }, + { 2, 0, 0, 0, 2, 32, }, + { 1, 0, 0, 0, 2, 32, }, + { 0, 0, 0, 0, 3, 34, }, + { 2, 0, 0, 0, 3, 32, }, + { 1, 0, 0, 0, 3, 32, }, + { 0, 0, 0, 0, 4, 34, }, + { 2, 0, 0, 0, 4, 32, }, + { 1, 0, 0, 0, 4, 32, }, + { 0, 0, 0, 0, 5, 34, }, + { 2, 0, 0, 0, 5, 32, }, + { 1, 0, 0, 0, 5, 32, }, + { 0, 0, 0, 0, 6, 34, }, + { 2, 0, 0, 0, 6, 32, }, + { 1, 0, 0, 0, 6, 32, }, + { 0, 0, 0, 0, 7, 34, }, + { 2, 0, 0, 0, 7, 32, }, + { 1, 0, 0, 0, 7, 32, }, + { 0, 0, 0, 0, 8, 34, }, + { 2, 0, 0, 0, 8, 32, }, + { 1, 0, 0, 0, 8, 32, }, + { 0, 0, 0, 0, 9, 34, }, + { 2, 0, 0, 0, 9, 32, }, + { 1, 0, 0, 0, 9, 32, }, + { 0, 0, 0, 0, 10, 34, }, + { 2, 0, 0, 0, 10, 32, }, + { 1, 0, 0, 0, 10, 32, }, + { 0, 0, 0, 0, 11, 34, }, + { 2, 0, 0, 0, 11, 32, }, + { 1, 0, 0, 0, 11, 32, }, + { 0, 0, 0, 0, 12, 24, }, + { 2, 0, 0, 0, 12, 32, }, + { 1, 0, 0, 0, 12, 32, }, + { 0, 0, 0, 0, 13, 16, }, + { 2, 0, 0, 0, 13, 32, }, + { 1, 0, 0, 0, 13, 32, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 32, }, + { 0, 0, 0, 1, 1, 30, }, + { 2, 0, 0, 1, 1, 32, }, + { 1, 0, 0, 1, 1, 32, }, + { 0, 0, 0, 1, 2, 32, }, + { 2, 0, 0, 1, 2, 32, }, + { 1, 0, 0, 1, 2, 32, }, + { 0, 0, 0, 1, 3, 32, }, + { 2, 0, 0, 1, 3, 32, }, + { 1, 0, 0, 1, 3, 32, }, + { 0, 0, 0, 1, 4, 32, }, + { 2, 0, 0, 1, 4, 32, }, + { 1, 0, 0, 1, 4, 32, }, + { 0, 0, 0, 1, 5, 32, }, + { 2, 0, 0, 1, 5, 32, }, + { 1, 0, 0, 1, 5, 32, }, + { 0, 0, 0, 1, 6, 32, }, + { 2, 0, 0, 1, 6, 32, }, + { 1, 0, 0, 1, 6, 32, }, + { 0, 0, 0, 1, 7, 32, }, + { 2, 0, 0, 1, 7, 32, }, + { 1, 0, 0, 1, 7, 32, }, + { 0, 0, 0, 1, 8, 32, }, + { 2, 0, 0, 1, 8, 32, }, + { 1, 0, 0, 1, 8, 32, }, + { 0, 0, 0, 1, 9, 32, }, + { 2, 0, 0, 1, 9, 32, }, + { 1, 0, 0, 1, 9, 32, }, + { 0, 0, 0, 1, 10, 32, }, + { 2, 0, 0, 1, 10, 32, }, + { 1, 0, 0, 1, 10, 32, }, + { 0, 0, 0, 1, 11, 30, }, + { 2, 0, 0, 1, 11, 32, }, + { 1, 0, 0, 1, 11, 32, }, + { 0, 0, 0, 1, 12, 18, }, + { 2, 0, 0, 1, 12, 32, }, + { 1, 0, 0, 1, 12, 32, }, + { 0, 0, 0, 1, 13, 8, }, + { 2, 0, 0, 1, 13, 32, }, + { 1, 0, 0, 1, 13, 32, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 28, }, + { 2, 0, 0, 2, 1, 32, }, + { 1, 0, 0, 2, 1, 32, }, + { 0, 0, 0, 2, 2, 32, }, + { 2, 0, 0, 2, 2, 32, }, + { 1, 0, 0, 2, 2, 32, }, + { 0, 0, 0, 2, 3, 32, }, + { 2, 0, 0, 2, 3, 32, }, + { 1, 0, 0, 2, 3, 32, }, + { 0, 0, 0, 2, 4, 32, }, + { 2, 0, 0, 2, 4, 32, }, + { 1, 0, 0, 2, 4, 32, }, + { 0, 0, 0, 2, 5, 32, }, + { 2, 0, 0, 2, 5, 32, }, + { 1, 0, 0, 2, 5, 32, }, + { 0, 0, 0, 2, 6, 32, }, + { 2, 0, 0, 2, 6, 32, }, + { 1, 0, 0, 2, 6, 32, }, + { 0, 0, 0, 2, 7, 32, }, + { 2, 0, 0, 2, 7, 32, }, + { 1, 0, 0, 2, 7, 32, }, + { 0, 0, 0, 2, 8, 32, }, + { 2, 0, 0, 2, 8, 32, }, + { 1, 0, 0, 2, 8, 32, }, + { 0, 0, 0, 2, 9, 32, }, + { 2, 0, 0, 2, 9, 32, }, + { 1, 0, 0, 2, 9, 32, }, + { 0, 0, 0, 2, 10, 32, }, + { 2, 0, 0, 2, 10, 32, }, + { 1, 0, 0, 2, 10, 32, }, + { 0, 0, 0, 2, 11, 28, }, + { 2, 0, 0, 2, 11, 32, }, + { 1, 0, 0, 2, 11, 32, }, + { 0, 0, 0, 2, 12, 16, }, + { 2, 0, 0, 2, 12, 32, }, + { 1, 0, 0, 2, 12, 32, }, + { 0, 0, 0, 2, 13, 6, }, + { 2, 0, 0, 2, 13, 32, }, + { 1, 0, 0, 2, 13, 32, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 26, }, + { 2, 0, 0, 3, 1, 30, }, + { 1, 0, 0, 3, 1, 30, }, + { 0, 0, 0, 3, 2, 30, }, + { 2, 0, 0, 3, 2, 30, }, + { 1, 0, 0, 3, 2, 30, }, + { 0, 0, 0, 3, 3, 30, }, + { 2, 0, 0, 3, 3, 30, }, + { 1, 0, 0, 3, 3, 30, }, + { 0, 0, 0, 3, 4, 30, }, + { 2, 0, 0, 3, 4, 30, }, + { 1, 0, 0, 3, 4, 30, }, + { 0, 0, 0, 3, 5, 30, }, + { 2, 0, 0, 3, 5, 30, }, + { 1, 0, 0, 3, 5, 30, }, + { 0, 0, 0, 3, 6, 30, }, + { 2, 0, 0, 3, 6, 30, }, + { 1, 0, 0, 3, 6, 30, }, + { 0, 0, 0, 3, 7, 30, }, + { 2, 0, 0, 3, 7, 30, }, + { 1, 0, 0, 3, 7, 30, }, + { 0, 0, 0, 3, 8, 30, }, + { 2, 0, 0, 3, 8, 30, }, + { 1, 0, 0, 3, 8, 30, }, + { 0, 0, 0, 3, 9, 30, }, + { 2, 0, 0, 3, 9, 30, }, + { 1, 0, 0, 3, 9, 30, }, + { 0, 0, 0, 3, 10, 30, }, + { 2, 0, 0, 3, 10, 30, }, + { 1, 0, 0, 3, 10, 30, }, + { 0, 0, 0, 3, 11, 26, }, + { 2, 0, 0, 3, 11, 30, }, + { 1, 0, 0, 3, 11, 30, }, + { 0, 0, 0, 3, 12, 14, }, + { 2, 0, 0, 3, 12, 30, }, + { 1, 0, 0, 3, 12, 30, }, + { 0, 0, 0, 3, 13, 4, }, + { 2, 0, 0, 3, 13, 30, }, + { 1, 0, 0, 3, 13, 30, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 24, }, + { 2, 0, 0, 6, 1, 28, }, + { 1, 0, 0, 6, 1, 28, }, + { 0, 0, 0, 6, 2, 28, }, + { 2, 0, 0, 6, 2, 28, }, + { 1, 0, 0, 6, 2, 28, }, + { 0, 0, 0, 6, 3, 28, }, + { 2, 0, 0, 6, 3, 28, }, + { 1, 0, 0, 6, 3, 28, }, + { 0, 0, 0, 6, 4, 28, }, + { 2, 0, 0, 6, 4, 28, }, + { 1, 0, 0, 6, 4, 28, }, + { 0, 0, 0, 6, 5, 28, }, + { 2, 0, 0, 6, 5, 28, }, + { 1, 0, 0, 6, 5, 28, }, + { 0, 0, 0, 6, 6, 28, }, + { 2, 0, 0, 6, 6, 28, }, + { 1, 0, 0, 6, 6, 28, }, + { 0, 0, 0, 6, 7, 28, }, + { 2, 0, 0, 6, 7, 28, }, + { 1, 0, 0, 6, 7, 28, }, + { 0, 0, 0, 6, 8, 28, }, + { 2, 0, 0, 6, 8, 28, }, + { 1, 0, 0, 6, 8, 28, }, + { 0, 0, 0, 6, 9, 28, }, + { 2, 0, 0, 6, 9, 28, }, + { 1, 0, 0, 6, 9, 28, }, + { 0, 0, 0, 6, 10, 28, }, + { 2, 0, 0, 6, 10, 28, }, + { 1, 0, 0, 6, 10, 28, }, + { 0, 0, 0, 6, 11, 24, }, + { 2, 0, 0, 6, 11, 28, }, + { 1, 0, 0, 6, 11, 28, }, + { 0, 0, 0, 6, 12, 14, }, + { 2, 0, 0, 6, 12, 28, }, + { 1, 0, 0, 6, 12, 28, }, + { 0, 0, 0, 6, 13, 4, }, + { 2, 0, 0, 6, 13, 28, }, + { 1, 0, 0, 6, 13, 28, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 22, }, + { 2, 0, 0, 7, 1, 26, }, + { 1, 0, 0, 7, 1, 26, }, + { 0, 0, 0, 7, 2, 26, }, + { 2, 0, 0, 7, 2, 26, }, + { 1, 0, 0, 7, 2, 26, }, + { 0, 0, 0, 7, 3, 26, }, + { 2, 0, 0, 7, 3, 26, }, + { 1, 0, 0, 7, 3, 26, }, + { 0, 0, 0, 7, 4, 26, }, + { 2, 0, 0, 7, 4, 26, }, + { 1, 0, 0, 7, 4, 26, }, + { 0, 0, 0, 7, 5, 26, }, + { 2, 0, 0, 7, 5, 26, }, + { 1, 0, 0, 7, 5, 26, }, + { 0, 0, 0, 7, 6, 26, }, + { 2, 0, 0, 7, 6, 26, }, + { 1, 0, 0, 7, 6, 26, }, + { 0, 0, 0, 7, 7, 26, }, + { 2, 0, 0, 7, 7, 26, }, + { 1, 0, 0, 7, 7, 26, }, + { 0, 0, 0, 7, 8, 26, }, + { 2, 0, 0, 7, 8, 26, }, + { 1, 0, 0, 7, 8, 26, }, + { 0, 0, 0, 7, 9, 26, }, + { 2, 0, 0, 7, 9, 26, }, + { 1, 0, 0, 7, 9, 26, }, + { 0, 0, 0, 7, 10, 26, }, + { 2, 0, 0, 7, 10, 26, }, + { 1, 0, 0, 7, 10, 26, }, + { 0, 0, 0, 7, 11, 22, }, + { 2, 0, 0, 7, 11, 26, }, + { 1, 0, 0, 7, 11, 26, }, + { 0, 0, 0, 7, 12, 14, }, + { 2, 0, 0, 7, 12, 26, }, + { 1, 0, 0, 7, 12, 26, }, + { 0, 0, 0, 7, 13, 4, }, + { 2, 0, 0, 7, 13, 26, }, + { 1, 0, 0, 7, 13, 26, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 28, }, + { 2, 0, 1, 2, 3, 32, }, + { 1, 0, 1, 2, 3, 32, }, + { 0, 0, 1, 2, 4, 32, }, + { 2, 0, 1, 2, 4, 32, }, + { 1, 0, 1, 2, 4, 32, }, + { 0, 0, 1, 2, 5, 32, }, + { 2, 0, 1, 2, 5, 32, }, + { 1, 0, 1, 2, 5, 32, }, + { 0, 0, 1, 2, 6, 32, }, + { 2, 0, 1, 2, 6, 32, }, + { 1, 0, 1, 2, 6, 32, }, + { 0, 0, 1, 2, 7, 32, }, + { 2, 0, 1, 2, 7, 32, }, + { 1, 0, 1, 2, 7, 32, }, + { 0, 0, 1, 2, 8, 32, }, + { 2, 0, 1, 2, 8, 32, }, + { 1, 0, 1, 2, 8, 32, }, + { 0, 0, 1, 2, 9, 32, }, + { 2, 0, 1, 2, 9, 32, }, + { 1, 0, 1, 2, 9, 32, }, + { 0, 0, 1, 2, 10, 32, }, + { 2, 0, 1, 2, 10, 32, }, + { 1, 0, 1, 2, 10, 32, }, + { 0, 0, 1, 2, 11, 28, }, + { 2, 0, 1, 2, 11, 32, }, + { 1, 0, 1, 2, 11, 32, }, + { 0, 0, 1, 2, 12, 16, }, + { 2, 0, 1, 2, 12, 32, }, + { 1, 0, 1, 2, 12, 32, }, + { 0, 0, 1, 2, 13, 10, }, + { 2, 0, 1, 2, 13, 32, }, + { 1, 0, 1, 2, 13, 32, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 26, }, + { 2, 0, 1, 3, 3, 30, }, + { 1, 0, 1, 3, 3, 30, }, + { 0, 0, 1, 3, 4, 30, }, + { 2, 0, 1, 3, 4, 30, }, + { 1, 0, 1, 3, 4, 30, }, + { 0, 0, 1, 3, 5, 30, }, + { 2, 0, 1, 3, 5, 30, }, + { 1, 0, 1, 3, 5, 30, }, + { 0, 0, 1, 3, 6, 30, }, + { 2, 0, 1, 3, 6, 30, }, + { 1, 0, 1, 3, 6, 30, }, + { 0, 0, 1, 3, 7, 30, }, + { 2, 0, 1, 3, 7, 30, }, + { 1, 0, 1, 3, 7, 30, }, + { 0, 0, 1, 3, 8, 30, }, + { 2, 0, 1, 3, 8, 30, }, + { 1, 0, 1, 3, 8, 30, }, + { 0, 0, 1, 3, 9, 30, }, + { 2, 0, 1, 3, 9, 30, }, + { 1, 0, 1, 3, 9, 30, }, + { 0, 0, 1, 3, 10, 30, }, + { 2, 0, 1, 3, 10, 30, }, + { 1, 0, 1, 3, 10, 30, }, + { 0, 0, 1, 3, 11, 26, }, + { 2, 0, 1, 3, 11, 30, }, + { 1, 0, 1, 3, 11, 30, }, + { 0, 0, 1, 3, 12, 14, }, + { 2, 0, 1, 3, 12, 30, }, + { 1, 0, 1, 3, 12, 30, }, + { 0, 0, 1, 3, 13, 8, }, + { 2, 0, 1, 3, 13, 30, }, + { 1, 0, 1, 3, 13, 30, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 24, }, + { 2, 0, 1, 6, 3, 28, }, + { 1, 0, 1, 6, 3, 28, }, + { 0, 0, 1, 6, 4, 28, }, + { 2, 0, 1, 6, 4, 28, }, + { 1, 0, 1, 6, 4, 28, }, + { 0, 0, 1, 6, 5, 28, }, + { 2, 0, 1, 6, 5, 28, }, + { 1, 0, 1, 6, 5, 28, }, + { 0, 0, 1, 6, 6, 28, }, + { 2, 0, 1, 6, 6, 28, }, + { 1, 0, 1, 6, 6, 28, }, + { 0, 0, 1, 6, 7, 28, }, + { 2, 0, 1, 6, 7, 28, }, + { 1, 0, 1, 6, 7, 28, }, + { 0, 0, 1, 6, 8, 28, }, + { 2, 0, 1, 6, 8, 28, }, + { 1, 0, 1, 6, 8, 28, }, + { 0, 0, 1, 6, 9, 28, }, + { 2, 0, 1, 6, 9, 28, }, + { 1, 0, 1, 6, 9, 28, }, + { 0, 0, 1, 6, 10, 28, }, + { 2, 0, 1, 6, 10, 28, }, + { 1, 0, 1, 6, 10, 28, }, + { 0, 0, 1, 6, 11, 24, }, + { 2, 0, 1, 6, 11, 28, }, + { 1, 0, 1, 6, 11, 28, }, + { 0, 0, 1, 6, 12, 14, }, + { 2, 0, 1, 6, 12, 28, }, + { 1, 0, 1, 6, 12, 28, }, + { 0, 0, 1, 6, 13, 8, }, + { 2, 0, 1, 6, 13, 28, }, + { 1, 0, 1, 6, 13, 28, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 22, }, + { 2, 0, 1, 7, 3, 26, }, + { 1, 0, 1, 7, 3, 26, }, + { 0, 0, 1, 7, 4, 26, }, + { 2, 0, 1, 7, 4, 26, }, + { 1, 0, 1, 7, 4, 26, }, + { 0, 0, 1, 7, 5, 26, }, + { 2, 0, 1, 7, 5, 26, }, + { 1, 0, 1, 7, 5, 26, }, + { 0, 0, 1, 7, 6, 26, }, + { 2, 0, 1, 7, 6, 26, }, + { 1, 0, 1, 7, 6, 26, }, + { 0, 0, 1, 7, 7, 26, }, + { 2, 0, 1, 7, 7, 26, }, + { 1, 0, 1, 7, 7, 26, }, + { 0, 0, 1, 7, 8, 26, }, + { 2, 0, 1, 7, 8, 26, }, + { 1, 0, 1, 7, 8, 26, }, + { 0, 0, 1, 7, 9, 26, }, + { 2, 0, 1, 7, 9, 26, }, + { 1, 0, 1, 7, 9, 26, }, + { 0, 0, 1, 7, 10, 26, }, + { 2, 0, 1, 7, 10, 26, }, + { 1, 0, 1, 7, 10, 26, }, + { 0, 0, 1, 7, 11, 22, }, + { 2, 0, 1, 7, 11, 26, }, + { 1, 0, 1, 7, 11, 26, }, + { 0, 0, 1, 7, 12, 14, }, + { 2, 0, 1, 7, 12, 26, }, + { 1, 0, 1, 7, 12, 26, }, + { 0, 0, 1, 7, 13, 8, }, + { 2, 0, 1, 7, 13, 26, }, + { 1, 0, 1, 7, 13, 26, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 28, }, + { 2, 1, 0, 1, 36, 32, }, + { 1, 1, 0, 1, 36, 32, }, + { 0, 1, 0, 1, 40, 32, }, + { 2, 1, 0, 1, 40, 32, }, + { 1, 1, 0, 1, 40, 32, }, + { 0, 1, 0, 1, 44, 32, }, + { 2, 1, 0, 1, 44, 32, }, + { 1, 1, 0, 1, 44, 32, }, + { 0, 1, 0, 1, 48, 32, }, + { 2, 1, 0, 1, 48, 32, }, + { 1, 1, 0, 1, 48, 32, }, + { 0, 1, 0, 1, 52, 32, }, + { 2, 1, 0, 1, 52, 32, }, + { 1, 1, 0, 1, 52, 32, }, + { 0, 1, 0, 1, 56, 32, }, + { 2, 1, 0, 1, 56, 32, }, + { 1, 1, 0, 1, 56, 32, }, + { 0, 1, 0, 1, 60, 32, }, + { 2, 1, 0, 1, 60, 32, }, + { 1, 1, 0, 1, 60, 32, }, + { 0, 1, 0, 1, 64, 30, }, + { 2, 1, 0, 1, 64, 32, }, + { 1, 1, 0, 1, 64, 32, }, + { 0, 1, 0, 1, 100, 28, }, + { 2, 1, 0, 1, 100, 32, }, + { 1, 1, 0, 1, 100, 32, }, + { 0, 1, 0, 1, 104, 32, }, + { 2, 1, 0, 1, 104, 32, }, + { 1, 1, 0, 1, 104, 32, }, + { 0, 1, 0, 1, 108, 32, }, + { 2, 1, 0, 1, 108, 32, }, + { 1, 1, 0, 1, 108, 32, }, + { 0, 1, 0, 1, 112, 32, }, + { 2, 1, 0, 1, 112, 32, }, + { 1, 1, 0, 1, 112, 32, }, + { 0, 1, 0, 1, 116, 32, }, + { 2, 1, 0, 1, 116, 32, }, + { 1, 1, 0, 1, 116, 32, }, + { 0, 1, 0, 1, 120, 32, }, + { 2, 1, 0, 1, 120, 32, }, + { 1, 1, 0, 1, 120, 32, }, + { 0, 1, 0, 1, 124, 32, }, + { 2, 1, 0, 1, 124, 32, }, + { 1, 1, 0, 1, 124, 32, }, + { 0, 1, 0, 1, 128, 32, }, + { 2, 1, 0, 1, 128, 32, }, + { 1, 1, 0, 1, 128, 32, }, + { 0, 1, 0, 1, 132, 32, }, + { 2, 1, 0, 1, 132, 32, }, + { 1, 1, 0, 1, 132, 32, }, + { 0, 1, 0, 1, 136, 32, }, + { 2, 1, 0, 1, 136, 32, }, + { 1, 1, 0, 1, 136, 32, }, + { 0, 1, 0, 1, 140, 28, }, + { 2, 1, 0, 1, 140, 32, }, + { 1, 1, 0, 1, 140, 32, }, + { 0, 1, 0, 1, 149, 28, }, + { 2, 1, 0, 1, 149, 32, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 32, }, + { 2, 1, 0, 1, 153, 32, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 32, }, + { 2, 1, 0, 1, 157, 32, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 32, }, + { 2, 1, 0, 1, 161, 32, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 32, }, + { 2, 1, 0, 1, 165, 32, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 28, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 32, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 32, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 32, }, + { 2, 1, 0, 2, 48, 32, }, + { 1, 1, 0, 2, 48, 32, }, + { 0, 1, 0, 2, 52, 32, }, + { 2, 1, 0, 2, 52, 32, }, + { 1, 1, 0, 2, 52, 32, }, + { 0, 1, 0, 2, 56, 32, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 32, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 30, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 28, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 32, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 32, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 32, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 32, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 32, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 32, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 32, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 32, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 32, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 28, }, + { 2, 1, 0, 2, 140, 32, }, + { 1, 1, 0, 2, 140, 32, }, + { 0, 1, 0, 2, 149, 28, }, + { 2, 1, 0, 2, 149, 32, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 32, }, + { 2, 1, 0, 2, 153, 32, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 32, }, + { 2, 1, 0, 2, 157, 32, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 32, }, + { 2, 1, 0, 2, 161, 32, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 32, }, + { 2, 1, 0, 2, 165, 32, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 26, }, + { 2, 1, 0, 3, 36, 30, }, + { 1, 1, 0, 3, 36, 30, }, + { 0, 1, 0, 3, 40, 30, }, + { 2, 1, 0, 3, 40, 30, }, + { 1, 1, 0, 3, 40, 30, }, + { 0, 1, 0, 3, 44, 30, }, + { 2, 1, 0, 3, 44, 30, }, + { 1, 1, 0, 3, 44, 30, }, + { 0, 1, 0, 3, 48, 30, }, + { 2, 1, 0, 3, 48, 30, }, + { 1, 1, 0, 3, 48, 30, }, + { 0, 1, 0, 3, 52, 30, }, + { 2, 1, 0, 3, 52, 30, }, + { 1, 1, 0, 3, 52, 30, }, + { 0, 1, 0, 3, 56, 30, }, + { 2, 1, 0, 3, 56, 30, }, + { 1, 1, 0, 3, 56, 30, }, + { 0, 1, 0, 3, 60, 30, }, + { 2, 1, 0, 3, 60, 30, }, + { 1, 1, 0, 3, 60, 30, }, + { 0, 1, 0, 3, 64, 28, }, + { 2, 1, 0, 3, 64, 30, }, + { 1, 1, 0, 3, 64, 30, }, + { 0, 1, 0, 3, 100, 28, }, + { 2, 1, 0, 3, 100, 30, }, + { 1, 1, 0, 3, 100, 30, }, + { 0, 1, 0, 3, 104, 30, }, + { 2, 1, 0, 3, 104, 30, }, + { 1, 1, 0, 3, 104, 30, }, + { 0, 1, 0, 3, 108, 30, }, + { 2, 1, 0, 3, 108, 30, }, + { 1, 1, 0, 3, 108, 30, }, + { 0, 1, 0, 3, 112, 30, }, + { 2, 1, 0, 3, 112, 30, }, + { 1, 1, 0, 3, 112, 30, }, + { 0, 1, 0, 3, 116, 30, }, + { 2, 1, 0, 3, 116, 30, }, + { 1, 1, 0, 3, 116, 30, }, + { 0, 1, 0, 3, 120, 30, }, + { 2, 1, 0, 3, 120, 30, }, + { 1, 1, 0, 3, 120, 30, }, + { 0, 1, 0, 3, 124, 30, }, + { 2, 1, 0, 3, 124, 30, }, + { 1, 1, 0, 3, 124, 30, }, + { 0, 1, 0, 3, 128, 30, }, + { 2, 1, 0, 3, 128, 30, }, + { 1, 1, 0, 3, 128, 30, }, + { 0, 1, 0, 3, 132, 30, }, + { 2, 1, 0, 3, 132, 30, }, + { 1, 1, 0, 3, 132, 30, }, + { 0, 1, 0, 3, 136, 30, }, + { 2, 1, 0, 3, 136, 30, }, + { 1, 1, 0, 3, 136, 30, }, + { 0, 1, 0, 3, 140, 26, }, + { 2, 1, 0, 3, 140, 30, }, + { 1, 1, 0, 3, 140, 30, }, + { 0, 1, 0, 3, 149, 26, }, + { 2, 1, 0, 3, 149, 30, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 30, }, + { 2, 1, 0, 3, 153, 30, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 30, }, + { 2, 1, 0, 3, 157, 30, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 30, }, + { 2, 1, 0, 3, 161, 30, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 30, }, + { 2, 1, 0, 3, 165, 30, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 24, }, + { 2, 1, 0, 6, 36, 28, }, + { 1, 1, 0, 6, 36, 28, }, + { 0, 1, 0, 6, 40, 28, }, + { 2, 1, 0, 6, 40, 28, }, + { 1, 1, 0, 6, 40, 28, }, + { 0, 1, 0, 6, 44, 28, }, + { 2, 1, 0, 6, 44, 28, }, + { 1, 1, 0, 6, 44, 28, }, + { 0, 1, 0, 6, 48, 28, }, + { 2, 1, 0, 6, 48, 28, }, + { 1, 1, 0, 6, 48, 28, }, + { 0, 1, 0, 6, 52, 28, }, + { 2, 1, 0, 6, 52, 28, }, + { 1, 1, 0, 6, 52, 28, }, + { 0, 1, 0, 6, 56, 28, }, + { 2, 1, 0, 6, 56, 28, }, + { 1, 1, 0, 6, 56, 28, }, + { 0, 1, 0, 6, 60, 28, }, + { 2, 1, 0, 6, 60, 28, }, + { 1, 1, 0, 6, 60, 28, }, + { 0, 1, 0, 6, 64, 26, }, + { 2, 1, 0, 6, 64, 28, }, + { 1, 1, 0, 6, 64, 28, }, + { 0, 1, 0, 6, 100, 24, }, + { 2, 1, 0, 6, 100, 28, }, + { 1, 1, 0, 6, 100, 28, }, + { 0, 1, 0, 6, 104, 28, }, + { 2, 1, 0, 6, 104, 28, }, + { 1, 1, 0, 6, 104, 28, }, + { 0, 1, 0, 6, 108, 28, }, + { 2, 1, 0, 6, 108, 28, }, + { 1, 1, 0, 6, 108, 28, }, + { 0, 1, 0, 6, 112, 28, }, + { 2, 1, 0, 6, 112, 28, }, + { 1, 1, 0, 6, 112, 28, }, + { 0, 1, 0, 6, 116, 28, }, + { 2, 1, 0, 6, 116, 28, }, + { 1, 1, 0, 6, 116, 28, }, + { 0, 1, 0, 6, 120, 28, }, + { 2, 1, 0, 6, 120, 28, }, + { 1, 1, 0, 6, 120, 28, }, + { 0, 1, 0, 6, 124, 28, }, + { 2, 1, 0, 6, 124, 28, }, + { 1, 1, 0, 6, 124, 28, }, + { 0, 1, 0, 6, 128, 28, }, + { 2, 1, 0, 6, 128, 28, }, + { 1, 1, 0, 6, 128, 28, }, + { 0, 1, 0, 6, 132, 28, }, + { 2, 1, 0, 6, 132, 28, }, + { 1, 1, 0, 6, 132, 28, }, + { 0, 1, 0, 6, 136, 28, }, + { 2, 1, 0, 6, 136, 28, }, + { 1, 1, 0, 6, 136, 28, }, + { 0, 1, 0, 6, 140, 24, }, + { 2, 1, 0, 6, 140, 28, }, + { 1, 1, 0, 6, 140, 28, }, + { 0, 1, 0, 6, 149, 24, }, + { 2, 1, 0, 6, 149, 28, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 28, }, + { 2, 1, 0, 6, 153, 28, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 28, }, + { 2, 1, 0, 6, 157, 28, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 28, }, + { 2, 1, 0, 6, 161, 28, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 28, }, + { 2, 1, 0, 6, 165, 28, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 22, }, + { 2, 1, 0, 7, 36, 26, }, + { 1, 1, 0, 7, 36, 26, }, + { 0, 1, 0, 7, 40, 26, }, + { 2, 1, 0, 7, 40, 26, }, + { 1, 1, 0, 7, 40, 26, }, + { 0, 1, 0, 7, 44, 26, }, + { 2, 1, 0, 7, 44, 26, }, + { 1, 1, 0, 7, 44, 26, }, + { 0, 1, 0, 7, 48, 26, }, + { 2, 1, 0, 7, 48, 26, }, + { 1, 1, 0, 7, 48, 26, }, + { 0, 1, 0, 7, 52, 26, }, + { 2, 1, 0, 7, 52, 26, }, + { 1, 1, 0, 7, 52, 26, }, + { 0, 1, 0, 7, 56, 26, }, + { 2, 1, 0, 7, 56, 26, }, + { 1, 1, 0, 7, 56, 26, }, + { 0, 1, 0, 7, 60, 26, }, + { 2, 1, 0, 7, 60, 26, }, + { 1, 1, 0, 7, 60, 26, }, + { 0, 1, 0, 7, 64, 24, }, + { 2, 1, 0, 7, 64, 26, }, + { 1, 1, 0, 7, 64, 26, }, + { 0, 1, 0, 7, 100, 22, }, + { 2, 1, 0, 7, 100, 26, }, + { 1, 1, 0, 7, 100, 26, }, + { 0, 1, 0, 7, 104, 26, }, + { 2, 1, 0, 7, 104, 26, }, + { 1, 1, 0, 7, 104, 26, }, + { 0, 1, 0, 7, 108, 26, }, + { 2, 1, 0, 7, 108, 26, }, + { 1, 1, 0, 7, 108, 26, }, + { 0, 1, 0, 7, 112, 26, }, + { 2, 1, 0, 7, 112, 26, }, + { 1, 1, 0, 7, 112, 26, }, + { 0, 1, 0, 7, 116, 26, }, + { 2, 1, 0, 7, 116, 26, }, + { 1, 1, 0, 7, 116, 26, }, + { 0, 1, 0, 7, 120, 26, }, + { 2, 1, 0, 7, 120, 26, }, + { 1, 1, 0, 7, 120, 26, }, + { 0, 1, 0, 7, 124, 26, }, + { 2, 1, 0, 7, 124, 26, }, + { 1, 1, 0, 7, 124, 26, }, + { 0, 1, 0, 7, 128, 26, }, + { 2, 1, 0, 7, 128, 26, }, + { 1, 1, 0, 7, 128, 26, }, + { 0, 1, 0, 7, 132, 26, }, + { 2, 1, 0, 7, 132, 26, }, + { 1, 1, 0, 7, 132, 26, }, + { 0, 1, 0, 7, 136, 26, }, + { 2, 1, 0, 7, 136, 26, }, + { 1, 1, 0, 7, 136, 26, }, + { 0, 1, 0, 7, 140, 22, }, + { 2, 1, 0, 7, 140, 26, }, + { 1, 1, 0, 7, 140, 26, }, + { 0, 1, 0, 7, 149, 22, }, + { 2, 1, 0, 7, 149, 26, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 26, }, + { 2, 1, 0, 7, 153, 26, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 26, }, + { 2, 1, 0, 7, 157, 26, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 26, }, + { 2, 1, 0, 7, 161, 26, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 26, }, + { 2, 1, 0, 7, 165, 26, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 28, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 32, }, + { 2, 1, 1, 2, 46, 32, }, + { 1, 1, 1, 2, 46, 32, }, + { 0, 1, 1, 2, 54, 32, }, + { 2, 1, 1, 2, 54, 32, }, + { 1, 1, 1, 2, 54, 32, }, + { 0, 1, 1, 2, 62, 28, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 28, }, + { 2, 1, 1, 2, 102, 32, }, + { 1, 1, 1, 2, 102, 32, }, + { 0, 1, 1, 2, 110, 32, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 32, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 32, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 30, }, + { 2, 1, 1, 2, 134, 32, }, + { 1, 1, 1, 2, 134, 32, }, + { 0, 1, 1, 2, 151, 28, }, + { 2, 1, 1, 2, 151, 32, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 32, }, + { 2, 1, 1, 2, 159, 32, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 26, }, + { 2, 1, 1, 3, 38, 30, }, + { 1, 1, 1, 3, 38, 30, }, + { 0, 1, 1, 3, 46, 30, }, + { 2, 1, 1, 3, 46, 30, }, + { 1, 1, 1, 3, 46, 30, }, + { 0, 1, 1, 3, 54, 30, }, + { 2, 1, 1, 3, 54, 30, }, + { 1, 1, 1, 3, 54, 30, }, + { 0, 1, 1, 3, 62, 26, }, + { 2, 1, 1, 3, 62, 30, }, + { 1, 1, 1, 3, 62, 30, }, + { 0, 1, 1, 3, 102, 26, }, + { 2, 1, 1, 3, 102, 30, }, + { 1, 1, 1, 3, 102, 30, }, + { 0, 1, 1, 3, 110, 30, }, + { 2, 1, 1, 3, 110, 30, }, + { 1, 1, 1, 3, 110, 30, }, + { 0, 1, 1, 3, 118, 30, }, + { 2, 1, 1, 3, 118, 30, }, + { 1, 1, 1, 3, 118, 30, }, + { 0, 1, 1, 3, 126, 30, }, + { 2, 1, 1, 3, 126, 30, }, + { 1, 1, 1, 3, 126, 30, }, + { 0, 1, 1, 3, 134, 28, }, + { 2, 1, 1, 3, 134, 30, }, + { 1, 1, 1, 3, 134, 30, }, + { 0, 1, 1, 3, 151, 26, }, + { 2, 1, 1, 3, 151, 30, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 30, }, + { 2, 1, 1, 3, 159, 30, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 20, }, + { 2, 1, 1, 6, 38, 28, }, + { 1, 1, 1, 6, 38, 28, }, + { 0, 1, 1, 6, 46, 28, }, + { 2, 1, 1, 6, 46, 28, }, + { 1, 1, 1, 6, 46, 28, }, + { 0, 1, 1, 6, 54, 28, }, + { 2, 1, 1, 6, 54, 28, }, + { 1, 1, 1, 6, 54, 28, }, + { 0, 1, 1, 6, 62, 20, }, + { 2, 1, 1, 6, 62, 28, }, + { 1, 1, 1, 6, 62, 28, }, + { 0, 1, 1, 6, 102, 22, }, + { 2, 1, 1, 6, 102, 28, }, + { 1, 1, 1, 6, 102, 28, }, + { 0, 1, 1, 6, 110, 28, }, + { 2, 1, 1, 6, 110, 28, }, + { 1, 1, 1, 6, 110, 28, }, + { 0, 1, 1, 6, 118, 28, }, + { 2, 1, 1, 6, 118, 28, }, + { 1, 1, 1, 6, 118, 28, }, + { 0, 1, 1, 6, 126, 28, }, + { 2, 1, 1, 6, 126, 28, }, + { 1, 1, 1, 6, 126, 28, }, + { 0, 1, 1, 6, 134, 26, }, + { 2, 1, 1, 6, 134, 28, }, + { 1, 1, 1, 6, 134, 28, }, + { 0, 1, 1, 6, 151, 22, }, + { 2, 1, 1, 6, 151, 28, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 28, }, + { 2, 1, 1, 6, 159, 28, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 18, }, + { 2, 1, 1, 7, 38, 26, }, + { 1, 1, 1, 7, 38, 26, }, + { 0, 1, 1, 7, 46, 26, }, + { 2, 1, 1, 7, 46, 26, }, + { 1, 1, 1, 7, 46, 26, }, + { 0, 1, 1, 7, 54, 26, }, + { 2, 1, 1, 7, 54, 26, }, + { 1, 1, 1, 7, 54, 26, }, + { 0, 1, 1, 7, 62, 18, }, + { 2, 1, 1, 7, 62, 26, }, + { 1, 1, 1, 7, 62, 26, }, + { 0, 1, 1, 7, 102, 20, }, + { 2, 1, 1, 7, 102, 26, }, + { 1, 1, 1, 7, 102, 26, }, + { 0, 1, 1, 7, 110, 26, }, + { 2, 1, 1, 7, 110, 26, }, + { 1, 1, 1, 7, 110, 26, }, + { 0, 1, 1, 7, 118, 26, }, + { 2, 1, 1, 7, 118, 26, }, + { 1, 1, 1, 7, 118, 26, }, + { 0, 1, 1, 7, 126, 26, }, + { 2, 1, 1, 7, 126, 26, }, + { 1, 1, 1, 7, 126, 26, }, + { 0, 1, 1, 7, 134, 24, }, + { 2, 1, 1, 7, 134, 26, }, + { 1, 1, 1, 7, 134, 26, }, + { 0, 1, 1, 7, 151, 20, }, + { 2, 1, 1, 7, 151, 26, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 26, }, + { 2, 1, 1, 7, 159, 26, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 24, }, + { 2, 1, 2, 4, 42, 32, }, + { 1, 1, 2, 4, 42, 32, }, + { 0, 1, 2, 4, 58, 24, }, + { 2, 1, 2, 4, 58, 32, }, + { 1, 1, 2, 4, 58, 32, }, + { 0, 1, 2, 4, 106, 24, }, + { 2, 1, 2, 4, 106, 32, }, + { 1, 1, 2, 4, 106, 32, }, + { 0, 1, 2, 4, 122, 32, }, + { 2, 1, 2, 4, 122, 32, }, + { 1, 1, 2, 4, 122, 32, }, + { 0, 1, 2, 4, 155, 26, }, + { 2, 1, 2, 4, 155, 32, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 22, }, + { 2, 1, 2, 5, 42, 30, }, + { 1, 1, 2, 5, 42, 30, }, + { 0, 1, 2, 5, 58, 22, }, + { 2, 1, 2, 5, 58, 30, }, + { 1, 1, 2, 5, 58, 30, }, + { 0, 1, 2, 5, 106, 22, }, + { 2, 1, 2, 5, 106, 30, }, + { 1, 1, 2, 5, 106, 30, }, + { 0, 1, 2, 5, 122, 30, }, + { 2, 1, 2, 5, 122, 30, }, + { 1, 1, 2, 5, 122, 30, }, + { 0, 1, 2, 5, 155, 24, }, + { 2, 1, 2, 5, 155, 30, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 20, }, + { 2, 1, 2, 8, 42, 28, }, + { 1, 1, 2, 8, 42, 28, }, + { 0, 1, 2, 8, 58, 20, }, + { 2, 1, 2, 8, 58, 28, }, + { 1, 1, 2, 8, 58, 28, }, + { 0, 1, 2, 8, 106, 20, }, + { 2, 1, 2, 8, 106, 28, }, + { 1, 1, 2, 8, 106, 28, }, + { 0, 1, 2, 8, 122, 28, }, + { 2, 1, 2, 8, 122, 28, }, + { 1, 1, 2, 8, 122, 28, }, + { 0, 1, 2, 8, 155, 20, }, + { 2, 1, 2, 8, 155, 28, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 18, }, + { 2, 1, 2, 9, 42, 26, }, + { 1, 1, 2, 9, 42, 26, }, + { 0, 1, 2, 9, 58, 18, }, + { 2, 1, 2, 9, 58, 26, }, + { 1, 1, 2, 9, 58, 26, }, + { 0, 1, 2, 9, 106, 18, }, + { 2, 1, 2, 9, 106, 26, }, + { 1, 1, 2, 9, 106, 26, }, + { 0, 1, 2, 9, 122, 26, }, + { 2, 1, 2, 9, 122, 26, }, + { 1, 1, 2, 9, 122, 26, }, + { 0, 1, 2, 9, 155, 18, }, + { 2, 1, 2, 9, 155, 26, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type1); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type2[] = { + { 0, 0, 0, 0, 1, 42, }, + { 2, 0, 0, 0, 1, 42, }, + { 1, 0, 0, 0, 1, 42, }, + { 0, 0, 0, 0, 2, 50, }, + { 2, 0, 0, 0, 2, 42, }, + { 1, 0, 0, 0, 2, 42, }, + { 0, 0, 0, 0, 3, 50, }, + { 2, 0, 0, 0, 3, 42, }, + { 1, 0, 0, 0, 3, 42, }, + { 0, 0, 0, 0, 4, 50, }, + { 2, 0, 0, 0, 4, 42, }, + { 1, 0, 0, 0, 4, 42, }, + { 0, 0, 0, 0, 5, 50, }, + { 2, 0, 0, 0, 5, 42, }, + { 1, 0, 0, 0, 5, 42, }, + { 0, 0, 0, 0, 6, 50, }, + { 2, 0, 0, 0, 6, 42, }, + { 1, 0, 0, 0, 6, 42, }, + { 0, 0, 0, 0, 7, 50, }, + { 2, 0, 0, 0, 7, 42, }, + { 1, 0, 0, 0, 7, 42, }, + { 0, 0, 0, 0, 8, 50, }, + { 2, 0, 0, 0, 8, 42, }, + { 1, 0, 0, 0, 8, 42, }, + { 0, 0, 0, 0, 9, 50, }, + { 2, 0, 0, 0, 9, 42, }, + { 1, 0, 0, 0, 9, 42, }, + { 0, 0, 0, 0, 10, 50, }, + { 2, 0, 0, 0, 10, 42, }, + { 1, 0, 0, 0, 10, 42, }, + { 0, 0, 0, 0, 11, 44, }, + { 2, 0, 0, 0, 11, 42, }, + { 1, 0, 0, 0, 11, 42, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 42, }, + { 1, 0, 0, 0, 12, 42, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 42, }, + { 1, 0, 0, 0, 13, 42, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 42, }, + { 0, 0, 0, 1, 1, 32, }, + { 2, 0, 0, 1, 1, 42, }, + { 1, 0, 0, 1, 1, 42, }, + { 0, 0, 0, 1, 2, 42, }, + { 2, 0, 0, 1, 2, 42, }, + { 1, 0, 0, 1, 2, 42, }, + { 0, 0, 0, 1, 3, 42, }, + { 2, 0, 0, 1, 3, 42, }, + { 1, 0, 0, 1, 3, 42, }, + { 0, 0, 0, 1, 4, 42, }, + { 2, 0, 0, 1, 4, 42, }, + { 1, 0, 0, 1, 4, 42, }, + { 0, 0, 0, 1, 5, 42, }, + { 2, 0, 0, 1, 5, 42, }, + { 1, 0, 0, 1, 5, 42, }, + { 0, 0, 0, 1, 6, 42, }, + { 2, 0, 0, 1, 6, 42, }, + { 1, 0, 0, 1, 6, 42, }, + { 0, 0, 0, 1, 7, 42, }, + { 2, 0, 0, 1, 7, 42, }, + { 1, 0, 0, 1, 7, 42, }, + { 0, 0, 0, 1, 8, 42, }, + { 2, 0, 0, 1, 8, 42, }, + { 1, 0, 0, 1, 8, 42, }, + { 0, 0, 0, 1, 9, 42, }, + { 2, 0, 0, 1, 9, 42, }, + { 1, 0, 0, 1, 9, 42, }, + { 0, 0, 0, 1, 10, 42, }, + { 2, 0, 0, 1, 10, 42, }, + { 1, 0, 0, 1, 10, 42, }, + { 0, 0, 0, 1, 11, 32, }, + { 2, 0, 0, 1, 11, 42, }, + { 1, 0, 0, 1, 11, 42, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 42, }, + { 1, 0, 0, 1, 12, 42, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 42, }, + { 1, 0, 0, 1, 13, 42, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 32, }, + { 2, 0, 0, 2, 1, 42, }, + { 1, 0, 0, 2, 1, 42, }, + { 0, 0, 0, 2, 2, 40, }, + { 2, 0, 0, 2, 2, 42, }, + { 1, 0, 0, 2, 2, 42, }, + { 0, 0, 0, 2, 3, 40, }, + { 2, 0, 0, 2, 3, 42, }, + { 1, 0, 0, 2, 3, 42, }, + { 0, 0, 0, 2, 4, 40, }, + { 2, 0, 0, 2, 4, 42, }, + { 1, 0, 0, 2, 4, 42, }, + { 0, 0, 0, 2, 5, 40, }, + { 2, 0, 0, 2, 5, 42, }, + { 1, 0, 0, 2, 5, 42, }, + { 0, 0, 0, 2, 6, 40, }, + { 2, 0, 0, 2, 6, 42, }, + { 1, 0, 0, 2, 6, 42, }, + { 0, 0, 0, 2, 7, 40, }, + { 2, 0, 0, 2, 7, 42, }, + { 1, 0, 0, 2, 7, 42, }, + { 0, 0, 0, 2, 8, 40, }, + { 2, 0, 0, 2, 8, 42, }, + { 1, 0, 0, 2, 8, 42, }, + { 0, 0, 0, 2, 9, 40, }, + { 2, 0, 0, 2, 9, 42, }, + { 1, 0, 0, 2, 9, 42, }, + { 0, 0, 0, 2, 10, 40, }, + { 2, 0, 0, 2, 10, 42, }, + { 1, 0, 0, 2, 10, 42, }, + { 0, 0, 0, 2, 11, 28, }, + { 2, 0, 0, 2, 11, 42, }, + { 1, 0, 0, 2, 11, 42, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 42, }, + { 1, 0, 0, 2, 12, 42, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 42, }, + { 1, 0, 0, 2, 13, 42, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 32, }, + { 2, 0, 0, 3, 1, 40, }, + { 1, 0, 0, 3, 1, 40, }, + { 0, 0, 0, 3, 2, 40, }, + { 2, 0, 0, 3, 2, 40, }, + { 1, 0, 0, 3, 2, 40, }, + { 0, 0, 0, 3, 3, 40, }, + { 2, 0, 0, 3, 3, 40, }, + { 1, 0, 0, 3, 3, 40, }, + { 0, 0, 0, 3, 4, 40, }, + { 2, 0, 0, 3, 4, 40, }, + { 1, 0, 0, 3, 4, 40, }, + { 0, 0, 0, 3, 5, 40, }, + { 2, 0, 0, 3, 5, 40, }, + { 1, 0, 0, 3, 5, 40, }, + { 0, 0, 0, 3, 6, 40, }, + { 2, 0, 0, 3, 6, 40, }, + { 1, 0, 0, 3, 6, 40, }, + { 0, 0, 0, 3, 7, 40, }, + { 2, 0, 0, 3, 7, 40, }, + { 1, 0, 0, 3, 7, 40, }, + { 0, 0, 0, 3, 8, 40, }, + { 2, 0, 0, 3, 8, 40, }, + { 1, 0, 0, 3, 8, 40, }, + { 0, 0, 0, 3, 9, 40, }, + { 2, 0, 0, 3, 9, 40, }, + { 1, 0, 0, 3, 9, 40, }, + { 0, 0, 0, 3, 10, 40, }, + { 2, 0, 0, 3, 10, 40, }, + { 1, 0, 0, 3, 10, 40, }, + { 0, 0, 0, 3, 11, 28, }, + { 2, 0, 0, 3, 11, 40, }, + { 1, 0, 0, 3, 11, 40, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 40, }, + { 1, 0, 0, 3, 12, 40, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 40, }, + { 1, 0, 0, 3, 13, 40, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 32, }, + { 2, 0, 0, 6, 1, 40, }, + { 1, 0, 0, 6, 1, 40, }, + { 0, 0, 0, 6, 2, 40, }, + { 2, 0, 0, 6, 2, 40, }, + { 1, 0, 0, 6, 2, 40, }, + { 0, 0, 0, 6, 3, 40, }, + { 2, 0, 0, 6, 3, 40, }, + { 1, 0, 0, 6, 3, 40, }, + { 0, 0, 0, 6, 4, 40, }, + { 2, 0, 0, 6, 4, 40, }, + { 1, 0, 0, 6, 4, 40, }, + { 0, 0, 0, 6, 5, 40, }, + { 2, 0, 0, 6, 5, 40, }, + { 1, 0, 0, 6, 5, 40, }, + { 0, 0, 0, 6, 6, 40, }, + { 2, 0, 0, 6, 6, 40, }, + { 1, 0, 0, 6, 6, 40, }, + { 0, 0, 0, 6, 7, 40, }, + { 2, 0, 0, 6, 7, 40, }, + { 1, 0, 0, 6, 7, 40, }, + { 0, 0, 0, 6, 8, 40, }, + { 2, 0, 0, 6, 8, 40, }, + { 1, 0, 0, 6, 8, 40, }, + { 0, 0, 0, 6, 9, 40, }, + { 2, 0, 0, 6, 9, 40, }, + { 1, 0, 0, 6, 9, 40, }, + { 0, 0, 0, 6, 10, 40, }, + { 2, 0, 0, 6, 10, 40, }, + { 1, 0, 0, 6, 10, 40, }, + { 0, 0, 0, 6, 11, 28, }, + { 2, 0, 0, 6, 11, 40, }, + { 1, 0, 0, 6, 11, 40, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 40, }, + { 1, 0, 0, 6, 12, 40, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 40, }, + { 1, 0, 0, 6, 13, 40, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 32, }, + { 2, 0, 0, 7, 1, 40, }, + { 1, 0, 0, 7, 1, 40, }, + { 0, 0, 0, 7, 2, 40, }, + { 2, 0, 0, 7, 2, 40, }, + { 1, 0, 0, 7, 2, 40, }, + { 0, 0, 0, 7, 3, 40, }, + { 2, 0, 0, 7, 3, 40, }, + { 1, 0, 0, 7, 3, 40, }, + { 0, 0, 0, 7, 4, 40, }, + { 2, 0, 0, 7, 4, 40, }, + { 1, 0, 0, 7, 4, 40, }, + { 0, 0, 0, 7, 5, 40, }, + { 2, 0, 0, 7, 5, 40, }, + { 1, 0, 0, 7, 5, 40, }, + { 0, 0, 0, 7, 6, 40, }, + { 2, 0, 0, 7, 6, 40, }, + { 1, 0, 0, 7, 6, 40, }, + { 0, 0, 0, 7, 7, 40, }, + { 2, 0, 0, 7, 7, 40, }, + { 1, 0, 0, 7, 7, 40, }, + { 0, 0, 0, 7, 8, 40, }, + { 2, 0, 0, 7, 8, 40, }, + { 1, 0, 0, 7, 8, 40, }, + { 0, 0, 0, 7, 9, 40, }, + { 2, 0, 0, 7, 9, 40, }, + { 1, 0, 0, 7, 9, 40, }, + { 0, 0, 0, 7, 10, 40, }, + { 2, 0, 0, 7, 10, 40, }, + { 1, 0, 0, 7, 10, 40, }, + { 0, 0, 0, 7, 11, 28, }, + { 2, 0, 0, 7, 11, 40, }, + { 1, 0, 0, 7, 11, 40, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 40, }, + { 1, 0, 0, 7, 12, 40, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 40, }, + { 1, 0, 0, 7, 13, 40, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 30, }, + { 2, 0, 1, 2, 3, 34, }, + { 1, 0, 1, 2, 3, 34, }, + { 0, 0, 1, 2, 4, 34, }, + { 2, 0, 1, 2, 4, 34, }, + { 1, 0, 1, 2, 4, 34, }, + { 0, 0, 1, 2, 5, 34, }, + { 2, 0, 1, 2, 5, 34, }, + { 1, 0, 1, 2, 5, 34, }, + { 0, 0, 1, 2, 6, 34, }, + { 2, 0, 1, 2, 6, 34, }, + { 1, 0, 1, 2, 6, 34, }, + { 0, 0, 1, 2, 7, 34, }, + { 2, 0, 1, 2, 7, 34, }, + { 1, 0, 1, 2, 7, 34, }, + { 0, 0, 1, 2, 8, 34, }, + { 2, 0, 1, 2, 8, 34, }, + { 1, 0, 1, 2, 8, 34, }, + { 0, 0, 1, 2, 9, 34, }, + { 2, 0, 1, 2, 9, 34, }, + { 1, 0, 1, 2, 9, 34, }, + { 0, 0, 1, 2, 10, 34, }, + { 2, 0, 1, 2, 10, 34, }, + { 1, 0, 1, 2, 10, 34, }, + { 0, 0, 1, 2, 11, 28, }, + { 2, 0, 1, 2, 11, 34, }, + { 1, 0, 1, 2, 11, 34, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 34, }, + { 1, 0, 1, 2, 12, 34, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 34, }, + { 1, 0, 1, 2, 13, 34, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 30, }, + { 2, 0, 1, 3, 3, 34, }, + { 1, 0, 1, 3, 3, 34, }, + { 0, 0, 1, 3, 4, 34, }, + { 2, 0, 1, 3, 4, 34, }, + { 1, 0, 1, 3, 4, 34, }, + { 0, 0, 1, 3, 5, 34, }, + { 2, 0, 1, 3, 5, 34, }, + { 1, 0, 1, 3, 5, 34, }, + { 0, 0, 1, 3, 6, 34, }, + { 2, 0, 1, 3, 6, 34, }, + { 1, 0, 1, 3, 6, 34, }, + { 0, 0, 1, 3, 7, 34, }, + { 2, 0, 1, 3, 7, 34, }, + { 1, 0, 1, 3, 7, 34, }, + { 0, 0, 1, 3, 8, 34, }, + { 2, 0, 1, 3, 8, 34, }, + { 1, 0, 1, 3, 8, 34, }, + { 0, 0, 1, 3, 9, 34, }, + { 2, 0, 1, 3, 9, 34, }, + { 1, 0, 1, 3, 9, 34, }, + { 0, 0, 1, 3, 10, 34, }, + { 2, 0, 1, 3, 10, 34, }, + { 1, 0, 1, 3, 10, 34, }, + { 0, 0, 1, 3, 11, 28, }, + { 2, 0, 1, 3, 11, 34, }, + { 1, 0, 1, 3, 11, 34, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 34, }, + { 1, 0, 1, 3, 12, 34, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 34, }, + { 1, 0, 1, 3, 13, 34, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 30, }, + { 2, 0, 1, 6, 3, 34, }, + { 1, 0, 1, 6, 3, 34, }, + { 0, 0, 1, 6, 4, 34, }, + { 2, 0, 1, 6, 4, 34, }, + { 1, 0, 1, 6, 4, 34, }, + { 0, 0, 1, 6, 5, 34, }, + { 2, 0, 1, 6, 5, 34, }, + { 1, 0, 1, 6, 5, 34, }, + { 0, 0, 1, 6, 6, 34, }, + { 2, 0, 1, 6, 6, 34, }, + { 1, 0, 1, 6, 6, 34, }, + { 0, 0, 1, 6, 7, 34, }, + { 2, 0, 1, 6, 7, 34, }, + { 1, 0, 1, 6, 7, 34, }, + { 0, 0, 1, 6, 8, 34, }, + { 2, 0, 1, 6, 8, 34, }, + { 1, 0, 1, 6, 8, 34, }, + { 0, 0, 1, 6, 9, 34, }, + { 2, 0, 1, 6, 9, 34, }, + { 1, 0, 1, 6, 9, 34, }, + { 0, 0, 1, 6, 10, 34, }, + { 2, 0, 1, 6, 10, 34, }, + { 1, 0, 1, 6, 10, 34, }, + { 0, 0, 1, 6, 11, 28, }, + { 2, 0, 1, 6, 11, 34, }, + { 1, 0, 1, 6, 11, 34, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 34, }, + { 1, 0, 1, 6, 12, 34, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 34, }, + { 1, 0, 1, 6, 13, 34, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 30, }, + { 2, 0, 1, 7, 3, 34, }, + { 1, 0, 1, 7, 3, 34, }, + { 0, 0, 1, 7, 4, 34, }, + { 2, 0, 1, 7, 4, 34, }, + { 1, 0, 1, 7, 4, 34, }, + { 0, 0, 1, 7, 5, 34, }, + { 2, 0, 1, 7, 5, 34, }, + { 1, 0, 1, 7, 5, 34, }, + { 0, 0, 1, 7, 6, 34, }, + { 2, 0, 1, 7, 6, 34, }, + { 1, 0, 1, 7, 6, 34, }, + { 0, 0, 1, 7, 7, 34, }, + { 2, 0, 1, 7, 7, 34, }, + { 1, 0, 1, 7, 7, 34, }, + { 0, 0, 1, 7, 8, 34, }, + { 2, 0, 1, 7, 8, 34, }, + { 1, 0, 1, 7, 8, 34, }, + { 0, 0, 1, 7, 9, 34, }, + { 2, 0, 1, 7, 9, 34, }, + { 1, 0, 1, 7, 9, 34, }, + { 0, 0, 1, 7, 10, 34, }, + { 2, 0, 1, 7, 10, 34, }, + { 1, 0, 1, 7, 10, 34, }, + { 0, 0, 1, 7, 11, 28, }, + { 2, 0, 1, 7, 11, 34, }, + { 1, 0, 1, 7, 11, 34, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 34, }, + { 1, 0, 1, 7, 12, 34, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 34, }, + { 1, 0, 1, 7, 13, 34, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 42, }, + { 2, 1, 0, 1, 36, 42, }, + { 1, 1, 0, 1, 36, 42, }, + { 0, 1, 0, 1, 40, 42, }, + { 2, 1, 0, 1, 40, 42, }, + { 1, 1, 0, 1, 40, 42, }, + { 0, 1, 0, 1, 44, 42, }, + { 2, 1, 0, 1, 44, 42, }, + { 1, 1, 0, 1, 44, 42, }, + { 0, 1, 0, 1, 48, 42, }, + { 2, 1, 0, 1, 48, 42, }, + { 1, 1, 0, 1, 48, 42, }, + { 0, 1, 0, 1, 52, 42, }, + { 2, 1, 0, 1, 52, 42, }, + { 1, 1, 0, 1, 52, 42, }, + { 0, 1, 0, 1, 56, 42, }, + { 2, 1, 0, 1, 56, 42, }, + { 1, 1, 0, 1, 56, 42, }, + { 0, 1, 0, 1, 60, 42, }, + { 2, 1, 0, 1, 60, 42, }, + { 1, 1, 0, 1, 60, 42, }, + { 0, 1, 0, 1, 64, 42, }, + { 2, 1, 0, 1, 64, 42, }, + { 1, 1, 0, 1, 64, 42, }, + { 0, 1, 0, 1, 100, 42, }, + { 2, 1, 0, 1, 100, 42, }, + { 1, 1, 0, 1, 100, 42, }, + { 0, 1, 0, 1, 104, 42, }, + { 2, 1, 0, 1, 104, 42, }, + { 1, 1, 0, 1, 104, 42, }, + { 0, 1, 0, 1, 108, 42, }, + { 2, 1, 0, 1, 108, 42, }, + { 1, 1, 0, 1, 108, 42, }, + { 0, 1, 0, 1, 112, 42, }, + { 2, 1, 0, 1, 112, 42, }, + { 1, 1, 0, 1, 112, 42, }, + { 0, 1, 0, 1, 116, 42, }, + { 2, 1, 0, 1, 116, 42, }, + { 1, 1, 0, 1, 116, 42, }, + { 0, 1, 0, 1, 120, 42, }, + { 2, 1, 0, 1, 120, 42, }, + { 1, 1, 0, 1, 120, 42, }, + { 0, 1, 0, 1, 124, 42, }, + { 2, 1, 0, 1, 124, 42, }, + { 1, 1, 0, 1, 124, 42, }, + { 0, 1, 0, 1, 128, 42, }, + { 2, 1, 0, 1, 128, 42, }, + { 1, 1, 0, 1, 128, 42, }, + { 0, 1, 0, 1, 132, 42, }, + { 2, 1, 0, 1, 132, 42, }, + { 1, 1, 0, 1, 132, 42, }, + { 0, 1, 0, 1, 136, 42, }, + { 2, 1, 0, 1, 136, 42, }, + { 1, 1, 0, 1, 136, 42, }, + { 0, 1, 0, 1, 140, 40, }, + { 2, 1, 0, 1, 140, 40, }, + { 1, 1, 0, 1, 140, 40, }, + { 0, 1, 0, 1, 149, 44, }, + { 2, 1, 0, 1, 149, 44, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 44, }, + { 2, 1, 0, 1, 153, 44, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 44, }, + { 2, 1, 0, 1, 157, 44, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 44, }, + { 2, 1, 0, 1, 161, 44, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 44, }, + { 2, 1, 0, 1, 165, 44, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 32, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 32, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 32, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 36, }, + { 2, 1, 0, 2, 48, 36, }, + { 1, 1, 0, 2, 48, 36, }, + { 0, 1, 0, 2, 52, 36, }, + { 2, 1, 0, 2, 52, 36, }, + { 1, 1, 0, 2, 52, 36, }, + { 0, 1, 0, 2, 56, 32, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 32, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 32, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 32, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 32, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 32, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 32, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 32, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 32, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 32, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 32, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 32, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 32, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 30, }, + { 2, 1, 0, 2, 140, 30, }, + { 1, 1, 0, 2, 140, 30, }, + { 0, 1, 0, 2, 149, 40, }, + { 2, 1, 0, 2, 149, 40, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 40, }, + { 2, 1, 0, 2, 153, 40, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 40, }, + { 2, 1, 0, 2, 157, 40, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 40, }, + { 2, 1, 0, 2, 161, 40, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 42, }, + { 2, 1, 0, 2, 165, 42, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 32, }, + { 2, 1, 0, 3, 36, 32, }, + { 1, 1, 0, 3, 36, 32, }, + { 0, 1, 0, 3, 40, 32, }, + { 2, 1, 0, 3, 40, 32, }, + { 1, 1, 0, 3, 40, 32, }, + { 0, 1, 0, 3, 44, 32, }, + { 2, 1, 0, 3, 44, 32, }, + { 1, 1, 0, 3, 44, 32, }, + { 0, 1, 0, 3, 48, 36, }, + { 2, 1, 0, 3, 48, 36, }, + { 1, 1, 0, 3, 48, 36, }, + { 0, 1, 0, 3, 52, 36, }, + { 2, 1, 0, 3, 52, 36, }, + { 1, 1, 0, 3, 52, 36, }, + { 0, 1, 0, 3, 56, 32, }, + { 2, 1, 0, 3, 56, 32, }, + { 1, 1, 0, 3, 56, 32, }, + { 0, 1, 0, 3, 60, 32, }, + { 2, 1, 0, 3, 60, 32, }, + { 1, 1, 0, 3, 60, 32, }, + { 0, 1, 0, 3, 64, 32, }, + { 2, 1, 0, 3, 64, 32, }, + { 1, 1, 0, 3, 64, 32, }, + { 0, 1, 0, 3, 100, 32, }, + { 2, 1, 0, 3, 100, 32, }, + { 1, 1, 0, 3, 100, 32, }, + { 0, 1, 0, 3, 104, 32, }, + { 2, 1, 0, 3, 104, 32, }, + { 1, 1, 0, 3, 104, 32, }, + { 0, 1, 0, 3, 108, 32, }, + { 2, 1, 0, 3, 108, 32, }, + { 1, 1, 0, 3, 108, 32, }, + { 0, 1, 0, 3, 112, 32, }, + { 2, 1, 0, 3, 112, 32, }, + { 1, 1, 0, 3, 112, 32, }, + { 0, 1, 0, 3, 116, 32, }, + { 2, 1, 0, 3, 116, 32, }, + { 1, 1, 0, 3, 116, 32, }, + { 0, 1, 0, 3, 120, 32, }, + { 2, 1, 0, 3, 120, 32, }, + { 1, 1, 0, 3, 120, 32, }, + { 0, 1, 0, 3, 124, 32, }, + { 2, 1, 0, 3, 124, 32, }, + { 1, 1, 0, 3, 124, 32, }, + { 0, 1, 0, 3, 128, 32, }, + { 2, 1, 0, 3, 128, 32, }, + { 1, 1, 0, 3, 128, 32, }, + { 0, 1, 0, 3, 132, 32, }, + { 2, 1, 0, 3, 132, 32, }, + { 1, 1, 0, 3, 132, 32, }, + { 0, 1, 0, 3, 136, 32, }, + { 2, 1, 0, 3, 136, 32, }, + { 1, 1, 0, 3, 136, 32, }, + { 0, 1, 0, 3, 140, 30, }, + { 2, 1, 0, 3, 140, 30, }, + { 1, 1, 0, 3, 140, 30, }, + { 0, 1, 0, 3, 149, 40, }, + { 2, 1, 0, 3, 149, 40, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 40, }, + { 2, 1, 0, 3, 153, 40, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 40, }, + { 2, 1, 0, 3, 157, 40, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 40, }, + { 2, 1, 0, 3, 161, 40, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 42, }, + { 2, 1, 0, 3, 165, 42, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 32, }, + { 2, 1, 0, 6, 36, 32, }, + { 1, 1, 0, 6, 36, 32, }, + { 0, 1, 0, 6, 40, 32, }, + { 2, 1, 0, 6, 40, 32, }, + { 1, 1, 0, 6, 40, 32, }, + { 0, 1, 0, 6, 44, 32, }, + { 2, 1, 0, 6, 44, 32, }, + { 1, 1, 0, 6, 44, 32, }, + { 0, 1, 0, 6, 48, 36, }, + { 2, 1, 0, 6, 48, 36, }, + { 1, 1, 0, 6, 48, 36, }, + { 0, 1, 0, 6, 52, 36, }, + { 2, 1, 0, 6, 52, 36, }, + { 1, 1, 0, 6, 52, 36, }, + { 0, 1, 0, 6, 56, 32, }, + { 2, 1, 0, 6, 56, 32, }, + { 1, 1, 0, 6, 56, 32, }, + { 0, 1, 0, 6, 60, 32, }, + { 2, 1, 0, 6, 60, 32, }, + { 1, 1, 0, 6, 60, 32, }, + { 0, 1, 0, 6, 64, 32, }, + { 2, 1, 0, 6, 64, 32, }, + { 1, 1, 0, 6, 64, 32, }, + { 0, 1, 0, 6, 100, 32, }, + { 2, 1, 0, 6, 100, 32, }, + { 1, 1, 0, 6, 100, 32, }, + { 0, 1, 0, 6, 104, 32, }, + { 2, 1, 0, 6, 104, 32, }, + { 1, 1, 0, 6, 104, 32, }, + { 0, 1, 0, 6, 108, 32, }, + { 2, 1, 0, 6, 108, 32, }, + { 1, 1, 0, 6, 108, 32, }, + { 0, 1, 0, 6, 112, 32, }, + { 2, 1, 0, 6, 112, 32, }, + { 1, 1, 0, 6, 112, 32, }, + { 0, 1, 0, 6, 116, 32, }, + { 2, 1, 0, 6, 116, 32, }, + { 1, 1, 0, 6, 116, 32, }, + { 0, 1, 0, 6, 120, 32, }, + { 2, 1, 0, 6, 120, 32, }, + { 1, 1, 0, 6, 120, 32, }, + { 0, 1, 0, 6, 124, 32, }, + { 2, 1, 0, 6, 124, 32, }, + { 1, 1, 0, 6, 124, 32, }, + { 0, 1, 0, 6, 128, 32, }, + { 2, 1, 0, 6, 128, 32, }, + { 1, 1, 0, 6, 128, 32, }, + { 0, 1, 0, 6, 132, 32, }, + { 2, 1, 0, 6, 132, 32, }, + { 1, 1, 0, 6, 132, 32, }, + { 0, 1, 0, 6, 136, 32, }, + { 2, 1, 0, 6, 136, 32, }, + { 1, 1, 0, 6, 136, 32, }, + { 0, 1, 0, 6, 140, 30, }, + { 2, 1, 0, 6, 140, 30, }, + { 1, 1, 0, 6, 140, 30, }, + { 0, 1, 0, 6, 149, 40, }, + { 2, 1, 0, 6, 149, 40, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 40, }, + { 2, 1, 0, 6, 153, 40, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 40, }, + { 2, 1, 0, 6, 157, 40, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 40, }, + { 2, 1, 0, 6, 161, 40, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 42, }, + { 2, 1, 0, 6, 165, 42, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 32, }, + { 2, 1, 0, 7, 36, 32, }, + { 1, 1, 0, 7, 36, 32, }, + { 0, 1, 0, 7, 40, 32, }, + { 2, 1, 0, 7, 40, 32, }, + { 1, 1, 0, 7, 40, 32, }, + { 0, 1, 0, 7, 44, 32, }, + { 2, 1, 0, 7, 44, 32, }, + { 1, 1, 0, 7, 44, 32, }, + { 0, 1, 0, 7, 48, 36, }, + { 2, 1, 0, 7, 48, 36, }, + { 1, 1, 0, 7, 48, 36, }, + { 0, 1, 0, 7, 52, 36, }, + { 2, 1, 0, 7, 52, 36, }, + { 1, 1, 0, 7, 52, 36, }, + { 0, 1, 0, 7, 56, 32, }, + { 2, 1, 0, 7, 56, 32, }, + { 1, 1, 0, 7, 56, 32, }, + { 0, 1, 0, 7, 60, 32, }, + { 2, 1, 0, 7, 60, 32, }, + { 1, 1, 0, 7, 60, 32, }, + { 0, 1, 0, 7, 64, 32, }, + { 2, 1, 0, 7, 64, 32, }, + { 1, 1, 0, 7, 64, 32, }, + { 0, 1, 0, 7, 100, 32, }, + { 2, 1, 0, 7, 100, 32, }, + { 1, 1, 0, 7, 100, 32, }, + { 0, 1, 0, 7, 104, 32, }, + { 2, 1, 0, 7, 104, 32, }, + { 1, 1, 0, 7, 104, 32, }, + { 0, 1, 0, 7, 108, 32, }, + { 2, 1, 0, 7, 108, 32, }, + { 1, 1, 0, 7, 108, 32, }, + { 0, 1, 0, 7, 112, 32, }, + { 2, 1, 0, 7, 112, 32, }, + { 1, 1, 0, 7, 112, 32, }, + { 0, 1, 0, 7, 116, 32, }, + { 2, 1, 0, 7, 116, 32, }, + { 1, 1, 0, 7, 116, 32, }, + { 0, 1, 0, 7, 120, 32, }, + { 2, 1, 0, 7, 120, 32, }, + { 1, 1, 0, 7, 120, 32, }, + { 0, 1, 0, 7, 124, 32, }, + { 2, 1, 0, 7, 124, 32, }, + { 1, 1, 0, 7, 124, 32, }, + { 0, 1, 0, 7, 128, 32, }, + { 2, 1, 0, 7, 128, 32, }, + { 1, 1, 0, 7, 128, 32, }, + { 0, 1, 0, 7, 132, 32, }, + { 2, 1, 0, 7, 132, 32, }, + { 1, 1, 0, 7, 132, 32, }, + { 0, 1, 0, 7, 136, 32, }, + { 2, 1, 0, 7, 136, 32, }, + { 1, 1, 0, 7, 136, 32, }, + { 0, 1, 0, 7, 140, 30, }, + { 2, 1, 0, 7, 140, 30, }, + { 1, 1, 0, 7, 140, 30, }, + { 0, 1, 0, 7, 149, 40, }, + { 2, 1, 0, 7, 149, 40, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 40, }, + { 2, 1, 0, 7, 153, 40, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 40, }, + { 2, 1, 0, 7, 157, 40, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 40, }, + { 2, 1, 0, 7, 161, 40, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 42, }, + { 2, 1, 0, 7, 165, 42, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 32, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 36, }, + { 2, 1, 1, 2, 46, 36, }, + { 1, 1, 1, 2, 46, 36, }, + { 0, 1, 1, 2, 54, 36, }, + { 2, 1, 1, 2, 54, 36, }, + { 1, 1, 1, 2, 54, 36, }, + { 0, 1, 1, 2, 62, 32, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 30, }, + { 2, 1, 1, 2, 102, 30, }, + { 1, 1, 1, 2, 102, 30, }, + { 0, 1, 1, 2, 110, 32, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 32, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 32, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 36, }, + { 2, 1, 1, 2, 134, 36, }, + { 1, 1, 1, 2, 134, 36, }, + { 0, 1, 1, 2, 151, 36, }, + { 2, 1, 1, 2, 151, 36, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 40, }, + { 2, 1, 1, 2, 159, 40, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 32, }, + { 2, 1, 1, 3, 38, 32, }, + { 1, 1, 1, 3, 38, 32, }, + { 0, 1, 1, 3, 46, 36, }, + { 2, 1, 1, 3, 46, 36, }, + { 1, 1, 1, 3, 46, 36, }, + { 0, 1, 1, 3, 54, 36, }, + { 2, 1, 1, 3, 54, 36, }, + { 1, 1, 1, 3, 54, 36, }, + { 0, 1, 1, 3, 62, 32, }, + { 2, 1, 1, 3, 62, 32, }, + { 1, 1, 1, 3, 62, 32, }, + { 0, 1, 1, 3, 102, 30, }, + { 2, 1, 1, 3, 102, 30, }, + { 1, 1, 1, 3, 102, 30, }, + { 0, 1, 1, 3, 110, 32, }, + { 2, 1, 1, 3, 110, 32, }, + { 1, 1, 1, 3, 110, 32, }, + { 0, 1, 1, 3, 118, 32, }, + { 2, 1, 1, 3, 118, 32, }, + { 1, 1, 1, 3, 118, 32, }, + { 0, 1, 1, 3, 126, 32, }, + { 2, 1, 1, 3, 126, 32, }, + { 1, 1, 1, 3, 126, 32, }, + { 0, 1, 1, 3, 134, 36, }, + { 2, 1, 1, 3, 134, 36, }, + { 1, 1, 1, 3, 134, 36, }, + { 0, 1, 1, 3, 151, 36, }, + { 2, 1, 1, 3, 151, 36, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 40, }, + { 2, 1, 1, 3, 159, 40, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 32, }, + { 2, 1, 1, 6, 38, 32, }, + { 1, 1, 1, 6, 38, 32, }, + { 0, 1, 1, 6, 46, 36, }, + { 2, 1, 1, 6, 46, 36, }, + { 1, 1, 1, 6, 46, 36, }, + { 0, 1, 1, 6, 54, 36, }, + { 2, 1, 1, 6, 54, 36, }, + { 1, 1, 1, 6, 54, 36, }, + { 0, 1, 1, 6, 62, 32, }, + { 2, 1, 1, 6, 62, 32, }, + { 1, 1, 1, 6, 62, 32, }, + { 0, 1, 1, 6, 102, 30, }, + { 2, 1, 1, 6, 102, 30, }, + { 1, 1, 1, 6, 102, 30, }, + { 0, 1, 1, 6, 110, 32, }, + { 2, 1, 1, 6, 110, 32, }, + { 1, 1, 1, 6, 110, 32, }, + { 0, 1, 1, 6, 118, 32, }, + { 2, 1, 1, 6, 118, 32, }, + { 1, 1, 1, 6, 118, 32, }, + { 0, 1, 1, 6, 126, 32, }, + { 2, 1, 1, 6, 126, 32, }, + { 1, 1, 1, 6, 126, 32, }, + { 0, 1, 1, 6, 134, 36, }, + { 2, 1, 1, 6, 134, 36, }, + { 1, 1, 1, 6, 134, 36, }, + { 0, 1, 1, 6, 151, 36, }, + { 2, 1, 1, 6, 151, 36, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 40, }, + { 2, 1, 1, 6, 159, 40, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 32, }, + { 2, 1, 1, 7, 38, 32, }, + { 1, 1, 1, 7, 38, 32, }, + { 0, 1, 1, 7, 46, 36, }, + { 2, 1, 1, 7, 46, 36, }, + { 1, 1, 1, 7, 46, 36, }, + { 0, 1, 1, 7, 54, 36, }, + { 2, 1, 1, 7, 54, 36, }, + { 1, 1, 1, 7, 54, 36, }, + { 0, 1, 1, 7, 62, 32, }, + { 2, 1, 1, 7, 62, 32, }, + { 1, 1, 1, 7, 62, 32, }, + { 0, 1, 1, 7, 102, 30, }, + { 2, 1, 1, 7, 102, 30, }, + { 1, 1, 1, 7, 102, 30, }, + { 0, 1, 1, 7, 110, 32, }, + { 2, 1, 1, 7, 110, 32, }, + { 1, 1, 1, 7, 110, 32, }, + { 0, 1, 1, 7, 118, 32, }, + { 2, 1, 1, 7, 118, 32, }, + { 1, 1, 1, 7, 118, 32, }, + { 0, 1, 1, 7, 126, 32, }, + { 2, 1, 1, 7, 126, 32, }, + { 1, 1, 1, 7, 126, 32, }, + { 0, 1, 1, 7, 134, 36, }, + { 2, 1, 1, 7, 134, 36, }, + { 1, 1, 1, 7, 134, 36, }, + { 0, 1, 1, 7, 151, 36, }, + { 2, 1, 1, 7, 151, 36, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 40, }, + { 2, 1, 1, 7, 159, 40, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 34, }, + { 2, 1, 2, 4, 42, 34, }, + { 1, 1, 2, 4, 42, 34, }, + { 0, 1, 2, 4, 58, 34, }, + { 2, 1, 2, 4, 58, 34, }, + { 1, 1, 2, 4, 58, 34, }, + { 0, 1, 2, 4, 106, 32, }, + { 2, 1, 2, 4, 106, 32, }, + { 1, 1, 2, 4, 106, 32, }, + { 0, 1, 2, 4, 122, 34, }, + { 2, 1, 2, 4, 122, 34, }, + { 1, 1, 2, 4, 122, 34, }, + { 0, 1, 2, 4, 155, 34, }, + { 2, 1, 2, 4, 155, 34, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 34, }, + { 2, 1, 2, 5, 42, 34, }, + { 1, 1, 2, 5, 42, 34, }, + { 0, 1, 2, 5, 58, 34, }, + { 2, 1, 2, 5, 58, 34, }, + { 1, 1, 2, 5, 58, 34, }, + { 0, 1, 2, 5, 106, 32, }, + { 2, 1, 2, 5, 106, 32, }, + { 1, 1, 2, 5, 106, 32, }, + { 0, 1, 2, 5, 122, 34, }, + { 2, 1, 2, 5, 122, 34, }, + { 1, 1, 2, 5, 122, 34, }, + { 0, 1, 2, 5, 155, 34, }, + { 2, 1, 2, 5, 155, 34, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 34, }, + { 2, 1, 2, 8, 42, 34, }, + { 1, 1, 2, 8, 42, 34, }, + { 0, 1, 2, 8, 58, 34, }, + { 2, 1, 2, 8, 58, 34, }, + { 1, 1, 2, 8, 58, 34, }, + { 0, 1, 2, 8, 106, 32, }, + { 2, 1, 2, 8, 106, 32, }, + { 1, 1, 2, 8, 106, 32, }, + { 0, 1, 2, 8, 122, 34, }, + { 2, 1, 2, 8, 122, 34, }, + { 1, 1, 2, 8, 122, 34, }, + { 0, 1, 2, 8, 155, 34, }, + { 2, 1, 2, 8, 155, 34, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 34, }, + { 2, 1, 2, 9, 42, 34, }, + { 1, 1, 2, 9, 42, 34, }, + { 0, 1, 2, 9, 58, 34, }, + { 2, 1, 2, 9, 58, 34, }, + { 1, 1, 2, 9, 58, 34, }, + { 0, 1, 2, 9, 106, 32, }, + { 2, 1, 2, 9, 106, 32, }, + { 1, 1, 2, 9, 106, 32, }, + { 0, 1, 2, 9, 122, 34, }, + { 2, 1, 2, 9, 122, 34, }, + { 1, 1, 2, 9, 122, 34, }, + { 0, 1, 2, 9, 155, 34, }, + { 2, 1, 2, 9, 155, 34, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type2); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type3[] = { + { 0, 0, 0, 0, 1, 46, }, + { 2, 0, 0, 0, 1, 40, }, + { 1, 0, 0, 0, 1, 40, }, + { 0, 0, 0, 0, 2, 46, }, + { 2, 0, 0, 0, 2, 40, }, + { 1, 0, 0, 0, 2, 40, }, + { 0, 0, 0, 0, 3, 46, }, + { 2, 0, 0, 0, 3, 40, }, + { 1, 0, 0, 0, 3, 40, }, + { 0, 0, 0, 0, 4, 46, }, + { 2, 0, 0, 0, 4, 40, }, + { 1, 0, 0, 0, 4, 40, }, + { 0, 0, 0, 0, 5, 46, }, + { 2, 0, 0, 0, 5, 40, }, + { 1, 0, 0, 0, 5, 40, }, + { 0, 0, 0, 0, 6, 46, }, + { 2, 0, 0, 0, 6, 40, }, + { 1, 0, 0, 0, 6, 40, }, + { 0, 0, 0, 0, 7, 46, }, + { 2, 0, 0, 0, 7, 40, }, + { 1, 0, 0, 0, 7, 40, }, + { 0, 0, 0, 0, 8, 46, }, + { 2, 0, 0, 0, 8, 40, }, + { 1, 0, 0, 0, 8, 40, }, + { 0, 0, 0, 0, 9, 46, }, + { 2, 0, 0, 0, 9, 40, }, + { 1, 0, 0, 0, 9, 40, }, + { 0, 0, 0, 0, 10, 46, }, + { 2, 0, 0, 0, 10, 40, }, + { 1, 0, 0, 0, 10, 40, }, + { 0, 0, 0, 0, 11, 46, }, + { 2, 0, 0, 0, 11, 40, }, + { 1, 0, 0, 0, 11, 40, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 40, }, + { 1, 0, 0, 0, 12, 40, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 40, }, + { 1, 0, 0, 0, 13, 40, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 40, }, + { 0, 0, 0, 1, 1, 46, }, + { 2, 0, 0, 1, 1, 40, }, + { 1, 0, 0, 1, 1, 40, }, + { 0, 0, 0, 1, 2, 46, }, + { 2, 0, 0, 1, 2, 40, }, + { 1, 0, 0, 1, 2, 40, }, + { 0, 0, 0, 1, 3, 46, }, + { 2, 0, 0, 1, 3, 40, }, + { 1, 0, 0, 1, 3, 40, }, + { 0, 0, 0, 1, 4, 46, }, + { 2, 0, 0, 1, 4, 40, }, + { 1, 0, 0, 1, 4, 40, }, + { 0, 0, 0, 1, 5, 46, }, + { 2, 0, 0, 1, 5, 40, }, + { 1, 0, 0, 1, 5, 40, }, + { 0, 0, 0, 1, 6, 46, }, + { 2, 0, 0, 1, 6, 40, }, + { 1, 0, 0, 1, 6, 40, }, + { 0, 0, 0, 1, 7, 46, }, + { 2, 0, 0, 1, 7, 40, }, + { 1, 0, 0, 1, 7, 40, }, + { 0, 0, 0, 1, 8, 46, }, + { 2, 0, 0, 1, 8, 40, }, + { 1, 0, 0, 1, 8, 40, }, + { 0, 0, 0, 1, 9, 46, }, + { 2, 0, 0, 1, 9, 40, }, + { 1, 0, 0, 1, 9, 40, }, + { 0, 0, 0, 1, 10, 46, }, + { 2, 0, 0, 1, 10, 40, }, + { 1, 0, 0, 1, 10, 40, }, + { 0, 0, 0, 1, 11, 46, }, + { 2, 0, 0, 1, 11, 40, }, + { 1, 0, 0, 1, 11, 40, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 40, }, + { 1, 0, 0, 1, 12, 40, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 40, }, + { 1, 0, 0, 1, 13, 40, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 46, }, + { 2, 0, 0, 2, 1, 40, }, + { 1, 0, 0, 2, 1, 40, }, + { 0, 0, 0, 2, 2, 46, }, + { 2, 0, 0, 2, 2, 40, }, + { 1, 0, 0, 2, 2, 40, }, + { 0, 0, 0, 2, 3, 46, }, + { 2, 0, 0, 2, 3, 40, }, + { 1, 0, 0, 2, 3, 40, }, + { 0, 0, 0, 2, 4, 46, }, + { 2, 0, 0, 2, 4, 40, }, + { 1, 0, 0, 2, 4, 40, }, + { 0, 0, 0, 2, 5, 46, }, + { 2, 0, 0, 2, 5, 40, }, + { 1, 0, 0, 2, 5, 40, }, + { 0, 0, 0, 2, 6, 46, }, + { 2, 0, 0, 2, 6, 40, }, + { 1, 0, 0, 2, 6, 40, }, + { 0, 0, 0, 2, 7, 46, }, + { 2, 0, 0, 2, 7, 40, }, + { 1, 0, 0, 2, 7, 40, }, + { 0, 0, 0, 2, 8, 46, }, + { 2, 0, 0, 2, 8, 40, }, + { 1, 0, 0, 2, 8, 40, }, + { 0, 0, 0, 2, 9, 46, }, + { 2, 0, 0, 2, 9, 40, }, + { 1, 0, 0, 2, 9, 40, }, + { 0, 0, 0, 2, 10, 46, }, + { 2, 0, 0, 2, 10, 40, }, + { 1, 0, 0, 2, 10, 40, }, + { 0, 0, 0, 2, 11, 46, }, + { 2, 0, 0, 2, 11, 40, }, + { 1, 0, 0, 2, 11, 40, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 40, }, + { 1, 0, 0, 2, 12, 40, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 40, }, + { 1, 0, 0, 2, 13, 40, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 46, }, + { 2, 0, 0, 3, 1, 40, }, + { 1, 0, 0, 3, 1, 40, }, + { 0, 0, 0, 3, 2, 46, }, + { 2, 0, 0, 3, 2, 40, }, + { 1, 0, 0, 3, 2, 40, }, + { 0, 0, 0, 3, 3, 46, }, + { 2, 0, 0, 3, 3, 40, }, + { 1, 0, 0, 3, 3, 40, }, + { 0, 0, 0, 3, 4, 46, }, + { 2, 0, 0, 3, 4, 40, }, + { 1, 0, 0, 3, 4, 40, }, + { 0, 0, 0, 3, 5, 46, }, + { 2, 0, 0, 3, 5, 40, }, + { 1, 0, 0, 3, 5, 40, }, + { 0, 0, 0, 3, 6, 46, }, + { 2, 0, 0, 3, 6, 40, }, + { 1, 0, 0, 3, 6, 40, }, + { 0, 0, 0, 3, 7, 46, }, + { 2, 0, 0, 3, 7, 40, }, + { 1, 0, 0, 3, 7, 40, }, + { 0, 0, 0, 3, 8, 46, }, + { 2, 0, 0, 3, 8, 40, }, + { 1, 0, 0, 3, 8, 40, }, + { 0, 0, 0, 3, 9, 46, }, + { 2, 0, 0, 3, 9, 40, }, + { 1, 0, 0, 3, 9, 40, }, + { 0, 0, 0, 3, 10, 46, }, + { 2, 0, 0, 3, 10, 40, }, + { 1, 0, 0, 3, 10, 40, }, + { 0, 0, 0, 3, 11, 46, }, + { 2, 0, 0, 3, 11, 40, }, + { 1, 0, 0, 3, 11, 40, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 40, }, + { 1, 0, 0, 3, 12, 40, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 40, }, + { 1, 0, 0, 3, 13, 40, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 46, }, + { 2, 0, 0, 6, 1, 40, }, + { 1, 0, 0, 6, 1, 40, }, + { 0, 0, 0, 6, 2, 46, }, + { 2, 0, 0, 6, 2, 40, }, + { 1, 0, 0, 6, 2, 40, }, + { 0, 0, 0, 6, 3, 46, }, + { 2, 0, 0, 6, 3, 40, }, + { 1, 0, 0, 6, 3, 40, }, + { 0, 0, 0, 6, 4, 46, }, + { 2, 0, 0, 6, 4, 40, }, + { 1, 0, 0, 6, 4, 40, }, + { 0, 0, 0, 6, 5, 46, }, + { 2, 0, 0, 6, 5, 40, }, + { 1, 0, 0, 6, 5, 40, }, + { 0, 0, 0, 6, 6, 46, }, + { 2, 0, 0, 6, 6, 40, }, + { 1, 0, 0, 6, 6, 40, }, + { 0, 0, 0, 6, 7, 46, }, + { 2, 0, 0, 6, 7, 40, }, + { 1, 0, 0, 6, 7, 40, }, + { 0, 0, 0, 6, 8, 46, }, + { 2, 0, 0, 6, 8, 40, }, + { 1, 0, 0, 6, 8, 40, }, + { 0, 0, 0, 6, 9, 46, }, + { 2, 0, 0, 6, 9, 40, }, + { 1, 0, 0, 6, 9, 40, }, + { 0, 0, 0, 6, 10, 46, }, + { 2, 0, 0, 6, 10, 40, }, + { 1, 0, 0, 6, 10, 40, }, + { 0, 0, 0, 6, 11, 46, }, + { 2, 0, 0, 6, 11, 40, }, + { 1, 0, 0, 6, 11, 40, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 40, }, + { 1, 0, 0, 6, 12, 40, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 40, }, + { 1, 0, 0, 6, 13, 40, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 46, }, + { 2, 0, 0, 7, 1, 40, }, + { 1, 0, 0, 7, 1, 40, }, + { 0, 0, 0, 7, 2, 46, }, + { 2, 0, 0, 7, 2, 40, }, + { 1, 0, 0, 7, 2, 40, }, + { 0, 0, 0, 7, 3, 46, }, + { 2, 0, 0, 7, 3, 40, }, + { 1, 0, 0, 7, 3, 40, }, + { 0, 0, 0, 7, 4, 46, }, + { 2, 0, 0, 7, 4, 40, }, + { 1, 0, 0, 7, 4, 40, }, + { 0, 0, 0, 7, 5, 46, }, + { 2, 0, 0, 7, 5, 40, }, + { 1, 0, 0, 7, 5, 40, }, + { 0, 0, 0, 7, 6, 46, }, + { 2, 0, 0, 7, 6, 40, }, + { 1, 0, 0, 7, 6, 40, }, + { 0, 0, 0, 7, 7, 46, }, + { 2, 0, 0, 7, 7, 40, }, + { 1, 0, 0, 7, 7, 40, }, + { 0, 0, 0, 7, 8, 46, }, + { 2, 0, 0, 7, 8, 40, }, + { 1, 0, 0, 7, 8, 40, }, + { 0, 0, 0, 7, 9, 46, }, + { 2, 0, 0, 7, 9, 40, }, + { 1, 0, 0, 7, 9, 40, }, + { 0, 0, 0, 7, 10, 46, }, + { 2, 0, 0, 7, 10, 40, }, + { 1, 0, 0, 7, 10, 40, }, + { 0, 0, 0, 7, 11, 46, }, + { 2, 0, 0, 7, 11, 40, }, + { 1, 0, 0, 7, 11, 40, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 40, }, + { 1, 0, 0, 7, 12, 40, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 40, }, + { 1, 0, 0, 7, 13, 40, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 46, }, + { 2, 0, 1, 2, 3, 40, }, + { 1, 0, 1, 2, 3, 40, }, + { 0, 0, 1, 2, 4, 46, }, + { 2, 0, 1, 2, 4, 40, }, + { 1, 0, 1, 2, 4, 40, }, + { 0, 0, 1, 2, 5, 46, }, + { 2, 0, 1, 2, 5, 40, }, + { 1, 0, 1, 2, 5, 40, }, + { 0, 0, 1, 2, 6, 46, }, + { 2, 0, 1, 2, 6, 40, }, + { 1, 0, 1, 2, 6, 40, }, + { 0, 0, 1, 2, 7, 46, }, + { 2, 0, 1, 2, 7, 40, }, + { 1, 0, 1, 2, 7, 40, }, + { 0, 0, 1, 2, 8, 46, }, + { 2, 0, 1, 2, 8, 40, }, + { 1, 0, 1, 2, 8, 40, }, + { 0, 0, 1, 2, 9, 46, }, + { 2, 0, 1, 2, 9, 40, }, + { 1, 0, 1, 2, 9, 40, }, + { 0, 0, 1, 2, 10, 46, }, + { 2, 0, 1, 2, 10, 40, }, + { 1, 0, 1, 2, 10, 40, }, + { 0, 0, 1, 2, 11, 46, }, + { 2, 0, 1, 2, 11, 40, }, + { 1, 0, 1, 2, 11, 40, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 40, }, + { 1, 0, 1, 2, 12, 40, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 40, }, + { 1, 0, 1, 2, 13, 40, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 46, }, + { 2, 0, 1, 3, 3, 40, }, + { 1, 0, 1, 3, 3, 40, }, + { 0, 0, 1, 3, 4, 46, }, + { 2, 0, 1, 3, 4, 40, }, + { 1, 0, 1, 3, 4, 40, }, + { 0, 0, 1, 3, 5, 46, }, + { 2, 0, 1, 3, 5, 40, }, + { 1, 0, 1, 3, 5, 40, }, + { 0, 0, 1, 3, 6, 46, }, + { 2, 0, 1, 3, 6, 40, }, + { 1, 0, 1, 3, 6, 40, }, + { 0, 0, 1, 3, 7, 46, }, + { 2, 0, 1, 3, 7, 40, }, + { 1, 0, 1, 3, 7, 40, }, + { 0, 0, 1, 3, 8, 46, }, + { 2, 0, 1, 3, 8, 40, }, + { 1, 0, 1, 3, 8, 40, }, + { 0, 0, 1, 3, 9, 46, }, + { 2, 0, 1, 3, 9, 40, }, + { 1, 0, 1, 3, 9, 40, }, + { 0, 0, 1, 3, 10, 46, }, + { 2, 0, 1, 3, 10, 40, }, + { 1, 0, 1, 3, 10, 40, }, + { 0, 0, 1, 3, 11, 46, }, + { 2, 0, 1, 3, 11, 40, }, + { 1, 0, 1, 3, 11, 40, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 40, }, + { 1, 0, 1, 3, 12, 40, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 40, }, + { 1, 0, 1, 3, 13, 40, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 46, }, + { 2, 0, 1, 6, 3, 40, }, + { 1, 0, 1, 6, 3, 40, }, + { 0, 0, 1, 6, 4, 46, }, + { 2, 0, 1, 6, 4, 40, }, + { 1, 0, 1, 6, 4, 40, }, + { 0, 0, 1, 6, 5, 46, }, + { 2, 0, 1, 6, 5, 40, }, + { 1, 0, 1, 6, 5, 40, }, + { 0, 0, 1, 6, 6, 46, }, + { 2, 0, 1, 6, 6, 40, }, + { 1, 0, 1, 6, 6, 40, }, + { 0, 0, 1, 6, 7, 46, }, + { 2, 0, 1, 6, 7, 40, }, + { 1, 0, 1, 6, 7, 40, }, + { 0, 0, 1, 6, 8, 46, }, + { 2, 0, 1, 6, 8, 40, }, + { 1, 0, 1, 6, 8, 40, }, + { 0, 0, 1, 6, 9, 46, }, + { 2, 0, 1, 6, 9, 40, }, + { 1, 0, 1, 6, 9, 40, }, + { 0, 0, 1, 6, 10, 46, }, + { 2, 0, 1, 6, 10, 40, }, + { 1, 0, 1, 6, 10, 40, }, + { 0, 0, 1, 6, 11, 46, }, + { 2, 0, 1, 6, 11, 40, }, + { 1, 0, 1, 6, 11, 40, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 40, }, + { 1, 0, 1, 6, 12, 40, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 40, }, + { 1, 0, 1, 6, 13, 40, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 46, }, + { 2, 0, 1, 7, 3, 40, }, + { 1, 0, 1, 7, 3, 40, }, + { 0, 0, 1, 7, 4, 46, }, + { 2, 0, 1, 7, 4, 40, }, + { 1, 0, 1, 7, 4, 40, }, + { 0, 0, 1, 7, 5, 46, }, + { 2, 0, 1, 7, 5, 40, }, + { 1, 0, 1, 7, 5, 40, }, + { 0, 0, 1, 7, 6, 46, }, + { 2, 0, 1, 7, 6, 40, }, + { 1, 0, 1, 7, 6, 40, }, + { 0, 0, 1, 7, 7, 46, }, + { 2, 0, 1, 7, 7, 40, }, + { 1, 0, 1, 7, 7, 40, }, + { 0, 0, 1, 7, 8, 46, }, + { 2, 0, 1, 7, 8, 40, }, + { 1, 0, 1, 7, 8, 40, }, + { 0, 0, 1, 7, 9, 46, }, + { 2, 0, 1, 7, 9, 40, }, + { 1, 0, 1, 7, 9, 40, }, + { 0, 0, 1, 7, 10, 46, }, + { 2, 0, 1, 7, 10, 40, }, + { 1, 0, 1, 7, 10, 40, }, + { 0, 0, 1, 7, 11, 46, }, + { 2, 0, 1, 7, 11, 40, }, + { 1, 0, 1, 7, 11, 40, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 40, }, + { 1, 0, 1, 7, 12, 40, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 40, }, + { 1, 0, 1, 7, 13, 40, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 46, }, + { 2, 1, 0, 1, 36, 40, }, + { 1, 1, 0, 1, 36, 40, }, + { 0, 1, 0, 1, 40, 46, }, + { 2, 1, 0, 1, 40, 40, }, + { 1, 1, 0, 1, 40, 40, }, + { 0, 1, 0, 1, 44, 46, }, + { 2, 1, 0, 1, 44, 40, }, + { 1, 1, 0, 1, 44, 40, }, + { 0, 1, 0, 1, 48, 46, }, + { 2, 1, 0, 1, 48, 40, }, + { 1, 1, 0, 1, 48, 40, }, + { 0, 1, 0, 1, 52, 46, }, + { 2, 1, 0, 1, 52, 40, }, + { 1, 1, 0, 1, 52, 40, }, + { 0, 1, 0, 1, 56, 46, }, + { 2, 1, 0, 1, 56, 40, }, + { 1, 1, 0, 1, 56, 40, }, + { 0, 1, 0, 1, 60, 46, }, + { 2, 1, 0, 1, 60, 40, }, + { 1, 1, 0, 1, 60, 40, }, + { 0, 1, 0, 1, 64, 46, }, + { 2, 1, 0, 1, 64, 40, }, + { 1, 1, 0, 1, 64, 40, }, + { 0, 1, 0, 1, 100, 46, }, + { 2, 1, 0, 1, 100, 40, }, + { 1, 1, 0, 1, 100, 40, }, + { 0, 1, 0, 1, 104, 46, }, + { 2, 1, 0, 1, 104, 40, }, + { 1, 1, 0, 1, 104, 40, }, + { 0, 1, 0, 1, 108, 46, }, + { 2, 1, 0, 1, 108, 40, }, + { 1, 1, 0, 1, 108, 40, }, + { 0, 1, 0, 1, 112, 46, }, + { 2, 1, 0, 1, 112, 40, }, + { 1, 1, 0, 1, 112, 40, }, + { 0, 1, 0, 1, 116, 46, }, + { 2, 1, 0, 1, 116, 40, }, + { 1, 1, 0, 1, 116, 40, }, + { 0, 1, 0, 1, 120, 46, }, + { 2, 1, 0, 1, 120, 40, }, + { 1, 1, 0, 1, 120, 40, }, + { 0, 1, 0, 1, 124, 46, }, + { 2, 1, 0, 1, 124, 40, }, + { 1, 1, 0, 1, 124, 40, }, + { 0, 1, 0, 1, 128, 46, }, + { 2, 1, 0, 1, 128, 40, }, + { 1, 1, 0, 1, 128, 40, }, + { 0, 1, 0, 1, 132, 46, }, + { 2, 1, 0, 1, 132, 40, }, + { 1, 1, 0, 1, 132, 40, }, + { 0, 1, 0, 1, 136, 46, }, + { 2, 1, 0, 1, 136, 40, }, + { 1, 1, 0, 1, 136, 40, }, + { 0, 1, 0, 1, 140, 46, }, + { 2, 1, 0, 1, 140, 40, }, + { 1, 1, 0, 1, 140, 40, }, + { 0, 1, 0, 1, 149, 46, }, + { 2, 1, 0, 1, 149, 40, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 46, }, + { 2, 1, 0, 1, 153, 40, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 46, }, + { 2, 1, 0, 1, 157, 40, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 46, }, + { 2, 1, 0, 1, 161, 40, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 46, }, + { 2, 1, 0, 1, 165, 40, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 46, }, + { 2, 1, 0, 2, 36, 40, }, + { 1, 1, 0, 2, 36, 40, }, + { 0, 1, 0, 2, 40, 46, }, + { 2, 1, 0, 2, 40, 40, }, + { 1, 1, 0, 2, 40, 40, }, + { 0, 1, 0, 2, 44, 46, }, + { 2, 1, 0, 2, 44, 40, }, + { 1, 1, 0, 2, 44, 40, }, + { 0, 1, 0, 2, 48, 46, }, + { 2, 1, 0, 2, 48, 40, }, + { 1, 1, 0, 2, 48, 40, }, + { 0, 1, 0, 2, 52, 46, }, + { 2, 1, 0, 2, 52, 40, }, + { 1, 1, 0, 2, 52, 40, }, + { 0, 1, 0, 2, 56, 46, }, + { 2, 1, 0, 2, 56, 40, }, + { 1, 1, 0, 2, 56, 40, }, + { 0, 1, 0, 2, 60, 46, }, + { 2, 1, 0, 2, 60, 40, }, + { 1, 1, 0, 2, 60, 40, }, + { 0, 1, 0, 2, 64, 46, }, + { 2, 1, 0, 2, 64, 40, }, + { 1, 1, 0, 2, 64, 40, }, + { 0, 1, 0, 2, 100, 46, }, + { 2, 1, 0, 2, 100, 40, }, + { 1, 1, 0, 2, 100, 40, }, + { 0, 1, 0, 2, 104, 46, }, + { 2, 1, 0, 2, 104, 40, }, + { 1, 1, 0, 2, 104, 40, }, + { 0, 1, 0, 2, 108, 46, }, + { 2, 1, 0, 2, 108, 40, }, + { 1, 1, 0, 2, 108, 40, }, + { 0, 1, 0, 2, 112, 46, }, + { 2, 1, 0, 2, 112, 40, }, + { 1, 1, 0, 2, 112, 40, }, + { 0, 1, 0, 2, 116, 46, }, + { 2, 1, 0, 2, 116, 40, }, + { 1, 1, 0, 2, 116, 40, }, + { 0, 1, 0, 2, 120, 46, }, + { 2, 1, 0, 2, 120, 40, }, + { 1, 1, 0, 2, 120, 40, }, + { 0, 1, 0, 2, 124, 46, }, + { 2, 1, 0, 2, 124, 40, }, + { 1, 1, 0, 2, 124, 40, }, + { 0, 1, 0, 2, 128, 46, }, + { 2, 1, 0, 2, 128, 40, }, + { 1, 1, 0, 2, 128, 40, }, + { 0, 1, 0, 2, 132, 46, }, + { 2, 1, 0, 2, 132, 40, }, + { 1, 1, 0, 2, 132, 40, }, + { 0, 1, 0, 2, 136, 46, }, + { 2, 1, 0, 2, 136, 40, }, + { 1, 1, 0, 2, 136, 40, }, + { 0, 1, 0, 2, 140, 46, }, + { 2, 1, 0, 2, 140, 40, }, + { 1, 1, 0, 2, 140, 40, }, + { 0, 1, 0, 2, 149, 46, }, + { 2, 1, 0, 2, 149, 40, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 46, }, + { 2, 1, 0, 2, 153, 40, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 46, }, + { 2, 1, 0, 2, 157, 40, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 46, }, + { 2, 1, 0, 2, 161, 40, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 46, }, + { 2, 1, 0, 2, 165, 40, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 46, }, + { 2, 1, 0, 3, 36, 40, }, + { 1, 1, 0, 3, 36, 40, }, + { 0, 1, 0, 3, 40, 46, }, + { 2, 1, 0, 3, 40, 40, }, + { 1, 1, 0, 3, 40, 40, }, + { 0, 1, 0, 3, 44, 46, }, + { 2, 1, 0, 3, 44, 40, }, + { 1, 1, 0, 3, 44, 40, }, + { 0, 1, 0, 3, 48, 46, }, + { 2, 1, 0, 3, 48, 40, }, + { 1, 1, 0, 3, 48, 40, }, + { 0, 1, 0, 3, 52, 46, }, + { 2, 1, 0, 3, 52, 40, }, + { 1, 1, 0, 3, 52, 40, }, + { 0, 1, 0, 3, 56, 46, }, + { 2, 1, 0, 3, 56, 40, }, + { 1, 1, 0, 3, 56, 40, }, + { 0, 1, 0, 3, 60, 46, }, + { 2, 1, 0, 3, 60, 40, }, + { 1, 1, 0, 3, 60, 40, }, + { 0, 1, 0, 3, 64, 46, }, + { 2, 1, 0, 3, 64, 40, }, + { 1, 1, 0, 3, 64, 40, }, + { 0, 1, 0, 3, 100, 46, }, + { 2, 1, 0, 3, 100, 40, }, + { 1, 1, 0, 3, 100, 40, }, + { 0, 1, 0, 3, 104, 46, }, + { 2, 1, 0, 3, 104, 40, }, + { 1, 1, 0, 3, 104, 40, }, + { 0, 1, 0, 3, 108, 46, }, + { 2, 1, 0, 3, 108, 40, }, + { 1, 1, 0, 3, 108, 40, }, + { 0, 1, 0, 3, 112, 46, }, + { 2, 1, 0, 3, 112, 40, }, + { 1, 1, 0, 3, 112, 40, }, + { 0, 1, 0, 3, 116, 46, }, + { 2, 1, 0, 3, 116, 40, }, + { 1, 1, 0, 3, 116, 40, }, + { 0, 1, 0, 3, 120, 46, }, + { 2, 1, 0, 3, 120, 40, }, + { 1, 1, 0, 3, 120, 40, }, + { 0, 1, 0, 3, 124, 46, }, + { 2, 1, 0, 3, 124, 40, }, + { 1, 1, 0, 3, 124, 40, }, + { 0, 1, 0, 3, 128, 46, }, + { 2, 1, 0, 3, 128, 40, }, + { 1, 1, 0, 3, 128, 40, }, + { 0, 1, 0, 3, 132, 46, }, + { 2, 1, 0, 3, 132, 40, }, + { 1, 1, 0, 3, 132, 40, }, + { 0, 1, 0, 3, 136, 46, }, + { 2, 1, 0, 3, 136, 40, }, + { 1, 1, 0, 3, 136, 40, }, + { 0, 1, 0, 3, 140, 46, }, + { 2, 1, 0, 3, 140, 40, }, + { 1, 1, 0, 3, 140, 40, }, + { 0, 1, 0, 3, 149, 46, }, + { 2, 1, 0, 3, 149, 40, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 46, }, + { 2, 1, 0, 3, 153, 40, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 46, }, + { 2, 1, 0, 3, 157, 40, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 46, }, + { 2, 1, 0, 3, 161, 40, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 46, }, + { 2, 1, 0, 3, 165, 40, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 46, }, + { 2, 1, 0, 6, 36, 40, }, + { 1, 1, 0, 6, 36, 40, }, + { 0, 1, 0, 6, 40, 46, }, + { 2, 1, 0, 6, 40, 40, }, + { 1, 1, 0, 6, 40, 40, }, + { 0, 1, 0, 6, 44, 46, }, + { 2, 1, 0, 6, 44, 40, }, + { 1, 1, 0, 6, 44, 40, }, + { 0, 1, 0, 6, 48, 46, }, + { 2, 1, 0, 6, 48, 40, }, + { 1, 1, 0, 6, 48, 40, }, + { 0, 1, 0, 6, 52, 46, }, + { 2, 1, 0, 6, 52, 40, }, + { 1, 1, 0, 6, 52, 40, }, + { 0, 1, 0, 6, 56, 46, }, + { 2, 1, 0, 6, 56, 40, }, + { 1, 1, 0, 6, 56, 40, }, + { 0, 1, 0, 6, 60, 46, }, + { 2, 1, 0, 6, 60, 40, }, + { 1, 1, 0, 6, 60, 40, }, + { 0, 1, 0, 6, 64, 46, }, + { 2, 1, 0, 6, 64, 40, }, + { 1, 1, 0, 6, 64, 40, }, + { 0, 1, 0, 6, 100, 46, }, + { 2, 1, 0, 6, 100, 40, }, + { 1, 1, 0, 6, 100, 40, }, + { 0, 1, 0, 6, 104, 46, }, + { 2, 1, 0, 6, 104, 40, }, + { 1, 1, 0, 6, 104, 40, }, + { 0, 1, 0, 6, 108, 46, }, + { 2, 1, 0, 6, 108, 40, }, + { 1, 1, 0, 6, 108, 40, }, + { 0, 1, 0, 6, 112, 46, }, + { 2, 1, 0, 6, 112, 40, }, + { 1, 1, 0, 6, 112, 40, }, + { 0, 1, 0, 6, 116, 46, }, + { 2, 1, 0, 6, 116, 40, }, + { 1, 1, 0, 6, 116, 40, }, + { 0, 1, 0, 6, 120, 46, }, + { 2, 1, 0, 6, 120, 40, }, + { 1, 1, 0, 6, 120, 40, }, + { 0, 1, 0, 6, 124, 46, }, + { 2, 1, 0, 6, 124, 40, }, + { 1, 1, 0, 6, 124, 40, }, + { 0, 1, 0, 6, 128, 46, }, + { 2, 1, 0, 6, 128, 40, }, + { 1, 1, 0, 6, 128, 40, }, + { 0, 1, 0, 6, 132, 46, }, + { 2, 1, 0, 6, 132, 40, }, + { 1, 1, 0, 6, 132, 40, }, + { 0, 1, 0, 6, 136, 46, }, + { 2, 1, 0, 6, 136, 40, }, + { 1, 1, 0, 6, 136, 40, }, + { 0, 1, 0, 6, 140, 46, }, + { 2, 1, 0, 6, 140, 40, }, + { 1, 1, 0, 6, 140, 40, }, + { 0, 1, 0, 6, 149, 46, }, + { 2, 1, 0, 6, 149, 40, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 46, }, + { 2, 1, 0, 6, 153, 40, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 46, }, + { 2, 1, 0, 6, 157, 40, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 46, }, + { 2, 1, 0, 6, 161, 40, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 46, }, + { 2, 1, 0, 6, 165, 40, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 46, }, + { 2, 1, 0, 7, 36, 40, }, + { 1, 1, 0, 7, 36, 40, }, + { 0, 1, 0, 7, 40, 46, }, + { 2, 1, 0, 7, 40, 40, }, + { 1, 1, 0, 7, 40, 40, }, + { 0, 1, 0, 7, 44, 46, }, + { 2, 1, 0, 7, 44, 40, }, + { 1, 1, 0, 7, 44, 40, }, + { 0, 1, 0, 7, 48, 46, }, + { 2, 1, 0, 7, 48, 40, }, + { 1, 1, 0, 7, 48, 40, }, + { 0, 1, 0, 7, 52, 46, }, + { 2, 1, 0, 7, 52, 40, }, + { 1, 1, 0, 7, 52, 40, }, + { 0, 1, 0, 7, 56, 46, }, + { 2, 1, 0, 7, 56, 40, }, + { 1, 1, 0, 7, 56, 40, }, + { 0, 1, 0, 7, 60, 46, }, + { 2, 1, 0, 7, 60, 40, }, + { 1, 1, 0, 7, 60, 40, }, + { 0, 1, 0, 7, 64, 46, }, + { 2, 1, 0, 7, 64, 40, }, + { 1, 1, 0, 7, 64, 40, }, + { 0, 1, 0, 7, 100, 46, }, + { 2, 1, 0, 7, 100, 40, }, + { 1, 1, 0, 7, 100, 40, }, + { 0, 1, 0, 7, 104, 46, }, + { 2, 1, 0, 7, 104, 40, }, + { 1, 1, 0, 7, 104, 40, }, + { 0, 1, 0, 7, 108, 46, }, + { 2, 1, 0, 7, 108, 40, }, + { 1, 1, 0, 7, 108, 40, }, + { 0, 1, 0, 7, 112, 46, }, + { 2, 1, 0, 7, 112, 40, }, + { 1, 1, 0, 7, 112, 40, }, + { 0, 1, 0, 7, 116, 46, }, + { 2, 1, 0, 7, 116, 40, }, + { 1, 1, 0, 7, 116, 40, }, + { 0, 1, 0, 7, 120, 46, }, + { 2, 1, 0, 7, 120, 40, }, + { 1, 1, 0, 7, 120, 40, }, + { 0, 1, 0, 7, 124, 46, }, + { 2, 1, 0, 7, 124, 40, }, + { 1, 1, 0, 7, 124, 40, }, + { 0, 1, 0, 7, 128, 46, }, + { 2, 1, 0, 7, 128, 40, }, + { 1, 1, 0, 7, 128, 40, }, + { 0, 1, 0, 7, 132, 46, }, + { 2, 1, 0, 7, 132, 40, }, + { 1, 1, 0, 7, 132, 40, }, + { 0, 1, 0, 7, 136, 46, }, + { 2, 1, 0, 7, 136, 40, }, + { 1, 1, 0, 7, 136, 40, }, + { 0, 1, 0, 7, 140, 46, }, + { 2, 1, 0, 7, 140, 40, }, + { 1, 1, 0, 7, 140, 40, }, + { 0, 1, 0, 7, 149, 46, }, + { 2, 1, 0, 7, 149, 40, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 46, }, + { 2, 1, 0, 7, 153, 40, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 46, }, + { 2, 1, 0, 7, 157, 40, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 46, }, + { 2, 1, 0, 7, 161, 40, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 46, }, + { 2, 1, 0, 7, 165, 40, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 46, }, + { 2, 1, 1, 2, 38, 40, }, + { 1, 1, 1, 2, 38, 40, }, + { 0, 1, 1, 2, 46, 46, }, + { 2, 1, 1, 2, 46, 40, }, + { 1, 1, 1, 2, 46, 40, }, + { 0, 1, 1, 2, 54, 46, }, + { 2, 1, 1, 2, 54, 40, }, + { 1, 1, 1, 2, 54, 40, }, + { 0, 1, 1, 2, 62, 46, }, + { 2, 1, 1, 2, 62, 40, }, + { 1, 1, 1, 2, 62, 40, }, + { 0, 1, 1, 2, 102, 46, }, + { 2, 1, 1, 2, 102, 40, }, + { 1, 1, 1, 2, 102, 40, }, + { 0, 1, 1, 2, 110, 46, }, + { 2, 1, 1, 2, 110, 40, }, + { 1, 1, 1, 2, 110, 40, }, + { 0, 1, 1, 2, 118, 46, }, + { 2, 1, 1, 2, 118, 40, }, + { 1, 1, 1, 2, 118, 40, }, + { 0, 1, 1, 2, 126, 46, }, + { 2, 1, 1, 2, 126, 40, }, + { 1, 1, 1, 2, 126, 40, }, + { 0, 1, 1, 2, 134, 46, }, + { 2, 1, 1, 2, 134, 40, }, + { 1, 1, 1, 2, 134, 40, }, + { 0, 1, 1, 2, 151, 46, }, + { 2, 1, 1, 2, 151, 40, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 46, }, + { 2, 1, 1, 2, 159, 40, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 46, }, + { 2, 1, 1, 3, 38, 40, }, + { 1, 1, 1, 3, 38, 40, }, + { 0, 1, 1, 3, 46, 46, }, + { 2, 1, 1, 3, 46, 40, }, + { 1, 1, 1, 3, 46, 40, }, + { 0, 1, 1, 3, 54, 46, }, + { 2, 1, 1, 3, 54, 40, }, + { 1, 1, 1, 3, 54, 40, }, + { 0, 1, 1, 3, 62, 46, }, + { 2, 1, 1, 3, 62, 40, }, + { 1, 1, 1, 3, 62, 40, }, + { 0, 1, 1, 3, 102, 46, }, + { 2, 1, 1, 3, 102, 40, }, + { 1, 1, 1, 3, 102, 40, }, + { 0, 1, 1, 3, 110, 46, }, + { 2, 1, 1, 3, 110, 40, }, + { 1, 1, 1, 3, 110, 40, }, + { 0, 1, 1, 3, 118, 46, }, + { 2, 1, 1, 3, 118, 40, }, + { 1, 1, 1, 3, 118, 40, }, + { 0, 1, 1, 3, 126, 46, }, + { 2, 1, 1, 3, 126, 40, }, + { 1, 1, 1, 3, 126, 40, }, + { 0, 1, 1, 3, 134, 46, }, + { 2, 1, 1, 3, 134, 40, }, + { 1, 1, 1, 3, 134, 40, }, + { 0, 1, 1, 3, 151, 46, }, + { 2, 1, 1, 3, 151, 40, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 46, }, + { 2, 1, 1, 3, 159, 40, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 46, }, + { 2, 1, 1, 6, 38, 40, }, + { 1, 1, 1, 6, 38, 40, }, + { 0, 1, 1, 6, 46, 46, }, + { 2, 1, 1, 6, 46, 40, }, + { 1, 1, 1, 6, 46, 40, }, + { 0, 1, 1, 6, 54, 46, }, + { 2, 1, 1, 6, 54, 40, }, + { 1, 1, 1, 6, 54, 40, }, + { 0, 1, 1, 6, 62, 46, }, + { 2, 1, 1, 6, 62, 40, }, + { 1, 1, 1, 6, 62, 40, }, + { 0, 1, 1, 6, 102, 46, }, + { 2, 1, 1, 6, 102, 40, }, + { 1, 1, 1, 6, 102, 40, }, + { 0, 1, 1, 6, 110, 46, }, + { 2, 1, 1, 6, 110, 40, }, + { 1, 1, 1, 6, 110, 40, }, + { 0, 1, 1, 6, 118, 46, }, + { 2, 1, 1, 6, 118, 40, }, + { 1, 1, 1, 6, 118, 40, }, + { 0, 1, 1, 6, 126, 46, }, + { 2, 1, 1, 6, 126, 40, }, + { 1, 1, 1, 6, 126, 40, }, + { 0, 1, 1, 6, 134, 46, }, + { 2, 1, 1, 6, 134, 40, }, + { 1, 1, 1, 6, 134, 40, }, + { 0, 1, 1, 6, 151, 46, }, + { 2, 1, 1, 6, 151, 40, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 46, }, + { 2, 1, 1, 6, 159, 40, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 46, }, + { 2, 1, 1, 7, 38, 40, }, + { 1, 1, 1, 7, 38, 40, }, + { 0, 1, 1, 7, 46, 46, }, + { 2, 1, 1, 7, 46, 40, }, + { 1, 1, 1, 7, 46, 40, }, + { 0, 1, 1, 7, 54, 46, }, + { 2, 1, 1, 7, 54, 40, }, + { 1, 1, 1, 7, 54, 40, }, + { 0, 1, 1, 7, 62, 46, }, + { 2, 1, 1, 7, 62, 40, }, + { 1, 1, 1, 7, 62, 40, }, + { 0, 1, 1, 7, 102, 46, }, + { 2, 1, 1, 7, 102, 40, }, + { 1, 1, 1, 7, 102, 40, }, + { 0, 1, 1, 7, 110, 46, }, + { 2, 1, 1, 7, 110, 40, }, + { 1, 1, 1, 7, 110, 40, }, + { 0, 1, 1, 7, 118, 46, }, + { 2, 1, 1, 7, 118, 40, }, + { 1, 1, 1, 7, 118, 40, }, + { 0, 1, 1, 7, 126, 46, }, + { 2, 1, 1, 7, 126, 40, }, + { 1, 1, 1, 7, 126, 40, }, + { 0, 1, 1, 7, 134, 46, }, + { 2, 1, 1, 7, 134, 40, }, + { 1, 1, 1, 7, 134, 40, }, + { 0, 1, 1, 7, 151, 46, }, + { 2, 1, 1, 7, 151, 40, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 46, }, + { 2, 1, 1, 7, 159, 40, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 46, }, + { 2, 1, 2, 4, 42, 40, }, + { 1, 1, 2, 4, 42, 40, }, + { 0, 1, 2, 4, 58, 46, }, + { 2, 1, 2, 4, 58, 40, }, + { 1, 1, 2, 4, 58, 40, }, + { 0, 1, 2, 4, 106, 46, }, + { 2, 1, 2, 4, 106, 40, }, + { 1, 1, 2, 4, 106, 40, }, + { 0, 1, 2, 4, 122, 46, }, + { 2, 1, 2, 4, 122, 40, }, + { 1, 1, 2, 4, 122, 40, }, + { 0, 1, 2, 4, 155, 46, }, + { 2, 1, 2, 4, 155, 40, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 46, }, + { 2, 1, 2, 5, 42, 40, }, + { 1, 1, 2, 5, 42, 40, }, + { 0, 1, 2, 5, 58, 46, }, + { 2, 1, 2, 5, 58, 40, }, + { 1, 1, 2, 5, 58, 40, }, + { 0, 1, 2, 5, 106, 46, }, + { 2, 1, 2, 5, 106, 40, }, + { 1, 1, 2, 5, 106, 40, }, + { 0, 1, 2, 5, 122, 46, }, + { 2, 1, 2, 5, 122, 40, }, + { 1, 1, 2, 5, 122, 40, }, + { 0, 1, 2, 5, 155, 46, }, + { 2, 1, 2, 5, 155, 40, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 46, }, + { 2, 1, 2, 8, 42, 40, }, + { 1, 1, 2, 8, 42, 40, }, + { 0, 1, 2, 8, 58, 46, }, + { 2, 1, 2, 8, 58, 40, }, + { 1, 1, 2, 8, 58, 40, }, + { 0, 1, 2, 8, 106, 46, }, + { 2, 1, 2, 8, 106, 40, }, + { 1, 1, 2, 8, 106, 40, }, + { 0, 1, 2, 8, 122, 46, }, + { 2, 1, 2, 8, 122, 40, }, + { 1, 1, 2, 8, 122, 40, }, + { 0, 1, 2, 8, 155, 46, }, + { 2, 1, 2, 8, 155, 40, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 46, }, + { 2, 1, 2, 9, 42, 40, }, + { 1, 1, 2, 9, 42, 40, }, + { 0, 1, 2, 9, 58, 46, }, + { 2, 1, 2, 9, 58, 40, }, + { 1, 1, 2, 9, 58, 40, }, + { 0, 1, 2, 9, 106, 46, }, + { 2, 1, 2, 9, 106, 40, }, + { 1, 1, 2, 9, 106, 40, }, + { 0, 1, 2, 9, 122, 46, }, + { 2, 1, 2, 9, 122, 40, }, + { 1, 1, 2, 9, 122, 40, }, + { 0, 1, 2, 9, 155, 46, }, + { 2, 1, 2, 9, 155, 40, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type3); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type5[] = { + { 0, 0, 0, 0, 1, 46, }, + { 2, 0, 0, 0, 1, 40, }, + { 1, 0, 0, 0, 1, 40, }, + { 0, 0, 0, 0, 2, 46, }, + { 2, 0, 0, 0, 2, 40, }, + { 1, 0, 0, 0, 2, 40, }, + { 0, 0, 0, 0, 3, 46, }, + { 2, 0, 0, 0, 3, 40, }, + { 1, 0, 0, 0, 3, 40, }, + { 0, 0, 0, 0, 4, 46, }, + { 2, 0, 0, 0, 4, 40, }, + { 1, 0, 0, 0, 4, 40, }, + { 0, 0, 0, 0, 5, 46, }, + { 2, 0, 0, 0, 5, 40, }, + { 1, 0, 0, 0, 5, 40, }, + { 0, 0, 0, 0, 6, 46, }, + { 2, 0, 0, 0, 6, 40, }, + { 1, 0, 0, 0, 6, 40, }, + { 0, 0, 0, 0, 7, 46, }, + { 2, 0, 0, 0, 7, 40, }, + { 1, 0, 0, 0, 7, 40, }, + { 0, 0, 0, 0, 8, 46, }, + { 2, 0, 0, 0, 8, 40, }, + { 1, 0, 0, 0, 8, 40, }, + { 0, 0, 0, 0, 9, 46, }, + { 2, 0, 0, 0, 9, 40, }, + { 1, 0, 0, 0, 9, 40, }, + { 0, 0, 0, 0, 10, 46, }, + { 2, 0, 0, 0, 10, 40, }, + { 1, 0, 0, 0, 10, 40, }, + { 0, 0, 0, 0, 11, 46, }, + { 2, 0, 0, 0, 11, 40, }, + { 1, 0, 0, 0, 11, 40, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 40, }, + { 1, 0, 0, 0, 12, 40, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 40, }, + { 1, 0, 0, 0, 13, 40, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 40, }, + { 0, 0, 0, 1, 1, 46, }, + { 2, 0, 0, 1, 1, 40, }, + { 1, 0, 0, 1, 1, 40, }, + { 0, 0, 0, 1, 2, 46, }, + { 2, 0, 0, 1, 2, 40, }, + { 1, 0, 0, 1, 2, 40, }, + { 0, 0, 0, 1, 3, 46, }, + { 2, 0, 0, 1, 3, 40, }, + { 1, 0, 0, 1, 3, 40, }, + { 0, 0, 0, 1, 4, 46, }, + { 2, 0, 0, 1, 4, 40, }, + { 1, 0, 0, 1, 4, 40, }, + { 0, 0, 0, 1, 5, 46, }, + { 2, 0, 0, 1, 5, 40, }, + { 1, 0, 0, 1, 5, 40, }, + { 0, 0, 0, 1, 6, 46, }, + { 2, 0, 0, 1, 6, 40, }, + { 1, 0, 0, 1, 6, 40, }, + { 0, 0, 0, 1, 7, 46, }, + { 2, 0, 0, 1, 7, 40, }, + { 1, 0, 0, 1, 7, 40, }, + { 0, 0, 0, 1, 8, 46, }, + { 2, 0, 0, 1, 8, 40, }, + { 1, 0, 0, 1, 8, 40, }, + { 0, 0, 0, 1, 9, 46, }, + { 2, 0, 0, 1, 9, 40, }, + { 1, 0, 0, 1, 9, 40, }, + { 0, 0, 0, 1, 10, 46, }, + { 2, 0, 0, 1, 10, 40, }, + { 1, 0, 0, 1, 10, 40, }, + { 0, 0, 0, 1, 11, 46, }, + { 2, 0, 0, 1, 11, 40, }, + { 1, 0, 0, 1, 11, 40, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 40, }, + { 1, 0, 0, 1, 12, 40, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 40, }, + { 1, 0, 0, 1, 13, 40, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 46, }, + { 2, 0, 0, 2, 1, 40, }, + { 1, 0, 0, 2, 1, 40, }, + { 0, 0, 0, 2, 2, 46, }, + { 2, 0, 0, 2, 2, 40, }, + { 1, 0, 0, 2, 2, 40, }, + { 0, 0, 0, 2, 3, 46, }, + { 2, 0, 0, 2, 3, 40, }, + { 1, 0, 0, 2, 3, 40, }, + { 0, 0, 0, 2, 4, 46, }, + { 2, 0, 0, 2, 4, 40, }, + { 1, 0, 0, 2, 4, 40, }, + { 0, 0, 0, 2, 5, 46, }, + { 2, 0, 0, 2, 5, 40, }, + { 1, 0, 0, 2, 5, 40, }, + { 0, 0, 0, 2, 6, 46, }, + { 2, 0, 0, 2, 6, 40, }, + { 1, 0, 0, 2, 6, 40, }, + { 0, 0, 0, 2, 7, 46, }, + { 2, 0, 0, 2, 7, 40, }, + { 1, 0, 0, 2, 7, 40, }, + { 0, 0, 0, 2, 8, 46, }, + { 2, 0, 0, 2, 8, 40, }, + { 1, 0, 0, 2, 8, 40, }, + { 0, 0, 0, 2, 9, 46, }, + { 2, 0, 0, 2, 9, 40, }, + { 1, 0, 0, 2, 9, 40, }, + { 0, 0, 0, 2, 10, 46, }, + { 2, 0, 0, 2, 10, 40, }, + { 1, 0, 0, 2, 10, 40, }, + { 0, 0, 0, 2, 11, 46, }, + { 2, 0, 0, 2, 11, 40, }, + { 1, 0, 0, 2, 11, 40, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 40, }, + { 1, 0, 0, 2, 12, 40, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 40, }, + { 1, 0, 0, 2, 13, 40, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 46, }, + { 2, 0, 0, 3, 1, 40, }, + { 1, 0, 0, 3, 1, 40, }, + { 0, 0, 0, 3, 2, 46, }, + { 2, 0, 0, 3, 2, 40, }, + { 1, 0, 0, 3, 2, 40, }, + { 0, 0, 0, 3, 3, 46, }, + { 2, 0, 0, 3, 3, 40, }, + { 1, 0, 0, 3, 3, 40, }, + { 0, 0, 0, 3, 4, 46, }, + { 2, 0, 0, 3, 4, 40, }, + { 1, 0, 0, 3, 4, 40, }, + { 0, 0, 0, 3, 5, 46, }, + { 2, 0, 0, 3, 5, 40, }, + { 1, 0, 0, 3, 5, 40, }, + { 0, 0, 0, 3, 6, 46, }, + { 2, 0, 0, 3, 6, 40, }, + { 1, 0, 0, 3, 6, 40, }, + { 0, 0, 0, 3, 7, 46, }, + { 2, 0, 0, 3, 7, 40, }, + { 1, 0, 0, 3, 7, 40, }, + { 0, 0, 0, 3, 8, 46, }, + { 2, 0, 0, 3, 8, 40, }, + { 1, 0, 0, 3, 8, 40, }, + { 0, 0, 0, 3, 9, 46, }, + { 2, 0, 0, 3, 9, 40, }, + { 1, 0, 0, 3, 9, 40, }, + { 0, 0, 0, 3, 10, 46, }, + { 2, 0, 0, 3, 10, 40, }, + { 1, 0, 0, 3, 10, 40, }, + { 0, 0, 0, 3, 11, 46, }, + { 2, 0, 0, 3, 11, 40, }, + { 1, 0, 0, 3, 11, 40, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 40, }, + { 1, 0, 0, 3, 12, 40, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 40, }, + { 1, 0, 0, 3, 13, 40, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 46, }, + { 2, 0, 0, 6, 1, 40, }, + { 1, 0, 0, 6, 1, 40, }, + { 0, 0, 0, 6, 2, 46, }, + { 2, 0, 0, 6, 2, 40, }, + { 1, 0, 0, 6, 2, 40, }, + { 0, 0, 0, 6, 3, 46, }, + { 2, 0, 0, 6, 3, 40, }, + { 1, 0, 0, 6, 3, 40, }, + { 0, 0, 0, 6, 4, 46, }, + { 2, 0, 0, 6, 4, 40, }, + { 1, 0, 0, 6, 4, 40, }, + { 0, 0, 0, 6, 5, 46, }, + { 2, 0, 0, 6, 5, 40, }, + { 1, 0, 0, 6, 5, 40, }, + { 0, 0, 0, 6, 6, 46, }, + { 2, 0, 0, 6, 6, 40, }, + { 1, 0, 0, 6, 6, 40, }, + { 0, 0, 0, 6, 7, 46, }, + { 2, 0, 0, 6, 7, 40, }, + { 1, 0, 0, 6, 7, 40, }, + { 0, 0, 0, 6, 8, 46, }, + { 2, 0, 0, 6, 8, 40, }, + { 1, 0, 0, 6, 8, 40, }, + { 0, 0, 0, 6, 9, 46, }, + { 2, 0, 0, 6, 9, 40, }, + { 1, 0, 0, 6, 9, 40, }, + { 0, 0, 0, 6, 10, 46, }, + { 2, 0, 0, 6, 10, 40, }, + { 1, 0, 0, 6, 10, 40, }, + { 0, 0, 0, 6, 11, 46, }, + { 2, 0, 0, 6, 11, 40, }, + { 1, 0, 0, 6, 11, 40, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 40, }, + { 1, 0, 0, 6, 12, 40, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 40, }, + { 1, 0, 0, 6, 13, 40, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 46, }, + { 2, 0, 0, 7, 1, 40, }, + { 1, 0, 0, 7, 1, 40, }, + { 0, 0, 0, 7, 2, 46, }, + { 2, 0, 0, 7, 2, 40, }, + { 1, 0, 0, 7, 2, 40, }, + { 0, 0, 0, 7, 3, 46, }, + { 2, 0, 0, 7, 3, 40, }, + { 1, 0, 0, 7, 3, 40, }, + { 0, 0, 0, 7, 4, 46, }, + { 2, 0, 0, 7, 4, 40, }, + { 1, 0, 0, 7, 4, 40, }, + { 0, 0, 0, 7, 5, 46, }, + { 2, 0, 0, 7, 5, 40, }, + { 1, 0, 0, 7, 5, 40, }, + { 0, 0, 0, 7, 6, 46, }, + { 2, 0, 0, 7, 6, 40, }, + { 1, 0, 0, 7, 6, 40, }, + { 0, 0, 0, 7, 7, 46, }, + { 2, 0, 0, 7, 7, 40, }, + { 1, 0, 0, 7, 7, 40, }, + { 0, 0, 0, 7, 8, 46, }, + { 2, 0, 0, 7, 8, 40, }, + { 1, 0, 0, 7, 8, 40, }, + { 0, 0, 0, 7, 9, 46, }, + { 2, 0, 0, 7, 9, 40, }, + { 1, 0, 0, 7, 9, 40, }, + { 0, 0, 0, 7, 10, 46, }, + { 2, 0, 0, 7, 10, 40, }, + { 1, 0, 0, 7, 10, 40, }, + { 0, 0, 0, 7, 11, 46, }, + { 2, 0, 0, 7, 11, 40, }, + { 1, 0, 0, 7, 11, 40, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 40, }, + { 1, 0, 0, 7, 12, 40, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 40, }, + { 1, 0, 0, 7, 13, 40, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 46, }, + { 2, 0, 1, 2, 3, 40, }, + { 1, 0, 1, 2, 3, 40, }, + { 0, 0, 1, 2, 4, 46, }, + { 2, 0, 1, 2, 4, 40, }, + { 1, 0, 1, 2, 4, 40, }, + { 0, 0, 1, 2, 5, 46, }, + { 2, 0, 1, 2, 5, 40, }, + { 1, 0, 1, 2, 5, 40, }, + { 0, 0, 1, 2, 6, 46, }, + { 2, 0, 1, 2, 6, 40, }, + { 1, 0, 1, 2, 6, 40, }, + { 0, 0, 1, 2, 7, 46, }, + { 2, 0, 1, 2, 7, 40, }, + { 1, 0, 1, 2, 7, 40, }, + { 0, 0, 1, 2, 8, 46, }, + { 2, 0, 1, 2, 8, 40, }, + { 1, 0, 1, 2, 8, 40, }, + { 0, 0, 1, 2, 9, 46, }, + { 2, 0, 1, 2, 9, 40, }, + { 1, 0, 1, 2, 9, 40, }, + { 0, 0, 1, 2, 10, 46, }, + { 2, 0, 1, 2, 10, 40, }, + { 1, 0, 1, 2, 10, 40, }, + { 0, 0, 1, 2, 11, 46, }, + { 2, 0, 1, 2, 11, 40, }, + { 1, 0, 1, 2, 11, 40, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 40, }, + { 1, 0, 1, 2, 12, 40, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 40, }, + { 1, 0, 1, 2, 13, 40, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 46, }, + { 2, 0, 1, 3, 3, 40, }, + { 1, 0, 1, 3, 3, 40, }, + { 0, 0, 1, 3, 4, 46, }, + { 2, 0, 1, 3, 4, 40, }, + { 1, 0, 1, 3, 4, 40, }, + { 0, 0, 1, 3, 5, 46, }, + { 2, 0, 1, 3, 5, 40, }, + { 1, 0, 1, 3, 5, 40, }, + { 0, 0, 1, 3, 6, 46, }, + { 2, 0, 1, 3, 6, 40, }, + { 1, 0, 1, 3, 6, 40, }, + { 0, 0, 1, 3, 7, 46, }, + { 2, 0, 1, 3, 7, 40, }, + { 1, 0, 1, 3, 7, 40, }, + { 0, 0, 1, 3, 8, 46, }, + { 2, 0, 1, 3, 8, 40, }, + { 1, 0, 1, 3, 8, 40, }, + { 0, 0, 1, 3, 9, 46, }, + { 2, 0, 1, 3, 9, 40, }, + { 1, 0, 1, 3, 9, 40, }, + { 0, 0, 1, 3, 10, 46, }, + { 2, 0, 1, 3, 10, 40, }, + { 1, 0, 1, 3, 10, 40, }, + { 0, 0, 1, 3, 11, 46, }, + { 2, 0, 1, 3, 11, 40, }, + { 1, 0, 1, 3, 11, 40, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 40, }, + { 1, 0, 1, 3, 12, 40, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 40, }, + { 1, 0, 1, 3, 13, 40, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 46, }, + { 2, 0, 1, 6, 3, 40, }, + { 1, 0, 1, 6, 3, 40, }, + { 0, 0, 1, 6, 4, 46, }, + { 2, 0, 1, 6, 4, 40, }, + { 1, 0, 1, 6, 4, 40, }, + { 0, 0, 1, 6, 5, 46, }, + { 2, 0, 1, 6, 5, 40, }, + { 1, 0, 1, 6, 5, 40, }, + { 0, 0, 1, 6, 6, 46, }, + { 2, 0, 1, 6, 6, 40, }, + { 1, 0, 1, 6, 6, 40, }, + { 0, 0, 1, 6, 7, 46, }, + { 2, 0, 1, 6, 7, 40, }, + { 1, 0, 1, 6, 7, 40, }, + { 0, 0, 1, 6, 8, 46, }, + { 2, 0, 1, 6, 8, 40, }, + { 1, 0, 1, 6, 8, 40, }, + { 0, 0, 1, 6, 9, 46, }, + { 2, 0, 1, 6, 9, 40, }, + { 1, 0, 1, 6, 9, 40, }, + { 0, 0, 1, 6, 10, 46, }, + { 2, 0, 1, 6, 10, 40, }, + { 1, 0, 1, 6, 10, 40, }, + { 0, 0, 1, 6, 11, 46, }, + { 2, 0, 1, 6, 11, 40, }, + { 1, 0, 1, 6, 11, 40, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 40, }, + { 1, 0, 1, 6, 12, 40, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 40, }, + { 1, 0, 1, 6, 13, 40, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 46, }, + { 2, 0, 1, 7, 3, 40, }, + { 1, 0, 1, 7, 3, 40, }, + { 0, 0, 1, 7, 4, 46, }, + { 2, 0, 1, 7, 4, 40, }, + { 1, 0, 1, 7, 4, 40, }, + { 0, 0, 1, 7, 5, 46, }, + { 2, 0, 1, 7, 5, 40, }, + { 1, 0, 1, 7, 5, 40, }, + { 0, 0, 1, 7, 6, 46, }, + { 2, 0, 1, 7, 6, 40, }, + { 1, 0, 1, 7, 6, 40, }, + { 0, 0, 1, 7, 7, 46, }, + { 2, 0, 1, 7, 7, 40, }, + { 1, 0, 1, 7, 7, 40, }, + { 0, 0, 1, 7, 8, 46, }, + { 2, 0, 1, 7, 8, 40, }, + { 1, 0, 1, 7, 8, 40, }, + { 0, 0, 1, 7, 9, 46, }, + { 2, 0, 1, 7, 9, 40, }, + { 1, 0, 1, 7, 9, 40, }, + { 0, 0, 1, 7, 10, 46, }, + { 2, 0, 1, 7, 10, 40, }, + { 1, 0, 1, 7, 10, 40, }, + { 0, 0, 1, 7, 11, 46, }, + { 2, 0, 1, 7, 11, 40, }, + { 1, 0, 1, 7, 11, 40, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 40, }, + { 1, 0, 1, 7, 12, 40, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 40, }, + { 1, 0, 1, 7, 13, 40, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 46, }, + { 2, 1, 0, 1, 36, 40, }, + { 1, 1, 0, 1, 36, 40, }, + { 0, 1, 0, 1, 40, 46, }, + { 2, 1, 0, 1, 40, 40, }, + { 1, 1, 0, 1, 40, 40, }, + { 0, 1, 0, 1, 44, 46, }, + { 2, 1, 0, 1, 44, 40, }, + { 1, 1, 0, 1, 44, 40, }, + { 0, 1, 0, 1, 48, 46, }, + { 2, 1, 0, 1, 48, 40, }, + { 1, 1, 0, 1, 48, 40, }, + { 0, 1, 0, 1, 52, 46, }, + { 2, 1, 0, 1, 52, 40, }, + { 1, 1, 0, 1, 52, 40, }, + { 0, 1, 0, 1, 56, 46, }, + { 2, 1, 0, 1, 56, 40, }, + { 1, 1, 0, 1, 56, 40, }, + { 0, 1, 0, 1, 60, 46, }, + { 2, 1, 0, 1, 60, 40, }, + { 1, 1, 0, 1, 60, 40, }, + { 0, 1, 0, 1, 64, 46, }, + { 2, 1, 0, 1, 64, 40, }, + { 1, 1, 0, 1, 64, 40, }, + { 0, 1, 0, 1, 100, 46, }, + { 2, 1, 0, 1, 100, 40, }, + { 1, 1, 0, 1, 100, 40, }, + { 0, 1, 0, 1, 104, 46, }, + { 2, 1, 0, 1, 104, 40, }, + { 1, 1, 0, 1, 104, 40, }, + { 0, 1, 0, 1, 108, 46, }, + { 2, 1, 0, 1, 108, 40, }, + { 1, 1, 0, 1, 108, 40, }, + { 0, 1, 0, 1, 112, 46, }, + { 2, 1, 0, 1, 112, 40, }, + { 1, 1, 0, 1, 112, 40, }, + { 0, 1, 0, 1, 116, 46, }, + { 2, 1, 0, 1, 116, 40, }, + { 1, 1, 0, 1, 116, 40, }, + { 0, 1, 0, 1, 120, 46, }, + { 2, 1, 0, 1, 120, 40, }, + { 1, 1, 0, 1, 120, 40, }, + { 0, 1, 0, 1, 124, 46, }, + { 2, 1, 0, 1, 124, 40, }, + { 1, 1, 0, 1, 124, 40, }, + { 0, 1, 0, 1, 128, 46, }, + { 2, 1, 0, 1, 128, 40, }, + { 1, 1, 0, 1, 128, 40, }, + { 0, 1, 0, 1, 132, 46, }, + { 2, 1, 0, 1, 132, 40, }, + { 1, 1, 0, 1, 132, 40, }, + { 0, 1, 0, 1, 136, 46, }, + { 2, 1, 0, 1, 136, 40, }, + { 1, 1, 0, 1, 136, 40, }, + { 0, 1, 0, 1, 140, 46, }, + { 2, 1, 0, 1, 140, 40, }, + { 1, 1, 0, 1, 140, 40, }, + { 0, 1, 0, 1, 149, 46, }, + { 2, 1, 0, 1, 149, 40, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 46, }, + { 2, 1, 0, 1, 153, 40, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 46, }, + { 2, 1, 0, 1, 157, 40, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 46, }, + { 2, 1, 0, 1, 161, 40, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 46, }, + { 2, 1, 0, 1, 165, 40, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 46, }, + { 2, 1, 0, 2, 36, 40, }, + { 1, 1, 0, 2, 36, 40, }, + { 0, 1, 0, 2, 40, 46, }, + { 2, 1, 0, 2, 40, 40, }, + { 1, 1, 0, 2, 40, 40, }, + { 0, 1, 0, 2, 44, 46, }, + { 2, 1, 0, 2, 44, 40, }, + { 1, 1, 0, 2, 44, 40, }, + { 0, 1, 0, 2, 48, 46, }, + { 2, 1, 0, 2, 48, 40, }, + { 1, 1, 0, 2, 48, 40, }, + { 0, 1, 0, 2, 52, 46, }, + { 2, 1, 0, 2, 52, 40, }, + { 1, 1, 0, 2, 52, 40, }, + { 0, 1, 0, 2, 56, 46, }, + { 2, 1, 0, 2, 56, 40, }, + { 1, 1, 0, 2, 56, 40, }, + { 0, 1, 0, 2, 60, 46, }, + { 2, 1, 0, 2, 60, 40, }, + { 1, 1, 0, 2, 60, 40, }, + { 0, 1, 0, 2, 64, 46, }, + { 2, 1, 0, 2, 64, 40, }, + { 1, 1, 0, 2, 64, 40, }, + { 0, 1, 0, 2, 100, 46, }, + { 2, 1, 0, 2, 100, 40, }, + { 1, 1, 0, 2, 100, 40, }, + { 0, 1, 0, 2, 104, 46, }, + { 2, 1, 0, 2, 104, 40, }, + { 1, 1, 0, 2, 104, 40, }, + { 0, 1, 0, 2, 108, 46, }, + { 2, 1, 0, 2, 108, 40, }, + { 1, 1, 0, 2, 108, 40, }, + { 0, 1, 0, 2, 112, 46, }, + { 2, 1, 0, 2, 112, 40, }, + { 1, 1, 0, 2, 112, 40, }, + { 0, 1, 0, 2, 116, 46, }, + { 2, 1, 0, 2, 116, 40, }, + { 1, 1, 0, 2, 116, 40, }, + { 0, 1, 0, 2, 120, 46, }, + { 2, 1, 0, 2, 120, 40, }, + { 1, 1, 0, 2, 120, 40, }, + { 0, 1, 0, 2, 124, 46, }, + { 2, 1, 0, 2, 124, 40, }, + { 1, 1, 0, 2, 124, 40, }, + { 0, 1, 0, 2, 128, 46, }, + { 2, 1, 0, 2, 128, 40, }, + { 1, 1, 0, 2, 128, 40, }, + { 0, 1, 0, 2, 132, 46, }, + { 2, 1, 0, 2, 132, 40, }, + { 1, 1, 0, 2, 132, 40, }, + { 0, 1, 0, 2, 136, 46, }, + { 2, 1, 0, 2, 136, 40, }, + { 1, 1, 0, 2, 136, 40, }, + { 0, 1, 0, 2, 140, 46, }, + { 2, 1, 0, 2, 140, 40, }, + { 1, 1, 0, 2, 140, 40, }, + { 0, 1, 0, 2, 149, 46, }, + { 2, 1, 0, 2, 149, 40, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 46, }, + { 2, 1, 0, 2, 153, 40, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 46, }, + { 2, 1, 0, 2, 157, 40, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 46, }, + { 2, 1, 0, 2, 161, 40, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 46, }, + { 2, 1, 0, 2, 165, 40, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 46, }, + { 2, 1, 0, 3, 36, 40, }, + { 1, 1, 0, 3, 36, 40, }, + { 0, 1, 0, 3, 40, 46, }, + { 2, 1, 0, 3, 40, 40, }, + { 1, 1, 0, 3, 40, 40, }, + { 0, 1, 0, 3, 44, 46, }, + { 2, 1, 0, 3, 44, 40, }, + { 1, 1, 0, 3, 44, 40, }, + { 0, 1, 0, 3, 48, 46, }, + { 2, 1, 0, 3, 48, 40, }, + { 1, 1, 0, 3, 48, 40, }, + { 0, 1, 0, 3, 52, 46, }, + { 2, 1, 0, 3, 52, 40, }, + { 1, 1, 0, 3, 52, 40, }, + { 0, 1, 0, 3, 56, 46, }, + { 2, 1, 0, 3, 56, 40, }, + { 1, 1, 0, 3, 56, 40, }, + { 0, 1, 0, 3, 60, 46, }, + { 2, 1, 0, 3, 60, 40, }, + { 1, 1, 0, 3, 60, 40, }, + { 0, 1, 0, 3, 64, 46, }, + { 2, 1, 0, 3, 64, 40, }, + { 1, 1, 0, 3, 64, 40, }, + { 0, 1, 0, 3, 100, 46, }, + { 2, 1, 0, 3, 100, 40, }, + { 1, 1, 0, 3, 100, 40, }, + { 0, 1, 0, 3, 104, 46, }, + { 2, 1, 0, 3, 104, 40, }, + { 1, 1, 0, 3, 104, 40, }, + { 0, 1, 0, 3, 108, 46, }, + { 2, 1, 0, 3, 108, 40, }, + { 1, 1, 0, 3, 108, 40, }, + { 0, 1, 0, 3, 112, 46, }, + { 2, 1, 0, 3, 112, 40, }, + { 1, 1, 0, 3, 112, 40, }, + { 0, 1, 0, 3, 116, 46, }, + { 2, 1, 0, 3, 116, 40, }, + { 1, 1, 0, 3, 116, 40, }, + { 0, 1, 0, 3, 120, 46, }, + { 2, 1, 0, 3, 120, 40, }, + { 1, 1, 0, 3, 120, 40, }, + { 0, 1, 0, 3, 124, 46, }, + { 2, 1, 0, 3, 124, 40, }, + { 1, 1, 0, 3, 124, 40, }, + { 0, 1, 0, 3, 128, 46, }, + { 2, 1, 0, 3, 128, 40, }, + { 1, 1, 0, 3, 128, 40, }, + { 0, 1, 0, 3, 132, 46, }, + { 2, 1, 0, 3, 132, 40, }, + { 1, 1, 0, 3, 132, 40, }, + { 0, 1, 0, 3, 136, 46, }, + { 2, 1, 0, 3, 136, 40, }, + { 1, 1, 0, 3, 136, 40, }, + { 0, 1, 0, 3, 140, 46, }, + { 2, 1, 0, 3, 140, 40, }, + { 1, 1, 0, 3, 140, 40, }, + { 0, 1, 0, 3, 149, 46, }, + { 2, 1, 0, 3, 149, 40, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 46, }, + { 2, 1, 0, 3, 153, 40, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 46, }, + { 2, 1, 0, 3, 157, 40, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 46, }, + { 2, 1, 0, 3, 161, 40, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 46, }, + { 2, 1, 0, 3, 165, 40, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 46, }, + { 2, 1, 0, 6, 36, 40, }, + { 1, 1, 0, 6, 36, 40, }, + { 0, 1, 0, 6, 40, 46, }, + { 2, 1, 0, 6, 40, 40, }, + { 1, 1, 0, 6, 40, 40, }, + { 0, 1, 0, 6, 44, 46, }, + { 2, 1, 0, 6, 44, 40, }, + { 1, 1, 0, 6, 44, 40, }, + { 0, 1, 0, 6, 48, 46, }, + { 2, 1, 0, 6, 48, 40, }, + { 1, 1, 0, 6, 48, 40, }, + { 0, 1, 0, 6, 52, 46, }, + { 2, 1, 0, 6, 52, 40, }, + { 1, 1, 0, 6, 52, 40, }, + { 0, 1, 0, 6, 56, 46, }, + { 2, 1, 0, 6, 56, 40, }, + { 1, 1, 0, 6, 56, 40, }, + { 0, 1, 0, 6, 60, 46, }, + { 2, 1, 0, 6, 60, 40, }, + { 1, 1, 0, 6, 60, 40, }, + { 0, 1, 0, 6, 64, 46, }, + { 2, 1, 0, 6, 64, 40, }, + { 1, 1, 0, 6, 64, 40, }, + { 0, 1, 0, 6, 100, 46, }, + { 2, 1, 0, 6, 100, 40, }, + { 1, 1, 0, 6, 100, 40, }, + { 0, 1, 0, 6, 104, 46, }, + { 2, 1, 0, 6, 104, 40, }, + { 1, 1, 0, 6, 104, 40, }, + { 0, 1, 0, 6, 108, 46, }, + { 2, 1, 0, 6, 108, 40, }, + { 1, 1, 0, 6, 108, 40, }, + { 0, 1, 0, 6, 112, 46, }, + { 2, 1, 0, 6, 112, 40, }, + { 1, 1, 0, 6, 112, 40, }, + { 0, 1, 0, 6, 116, 46, }, + { 2, 1, 0, 6, 116, 40, }, + { 1, 1, 0, 6, 116, 40, }, + { 0, 1, 0, 6, 120, 46, }, + { 2, 1, 0, 6, 120, 40, }, + { 1, 1, 0, 6, 120, 40, }, + { 0, 1, 0, 6, 124, 46, }, + { 2, 1, 0, 6, 124, 40, }, + { 1, 1, 0, 6, 124, 40, }, + { 0, 1, 0, 6, 128, 46, }, + { 2, 1, 0, 6, 128, 40, }, + { 1, 1, 0, 6, 128, 40, }, + { 0, 1, 0, 6, 132, 46, }, + { 2, 1, 0, 6, 132, 40, }, + { 1, 1, 0, 6, 132, 40, }, + { 0, 1, 0, 6, 136, 46, }, + { 2, 1, 0, 6, 136, 40, }, + { 1, 1, 0, 6, 136, 40, }, + { 0, 1, 0, 6, 140, 46, }, + { 2, 1, 0, 6, 140, 40, }, + { 1, 1, 0, 6, 140, 40, }, + { 0, 1, 0, 6, 149, 46, }, + { 2, 1, 0, 6, 149, 40, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 46, }, + { 2, 1, 0, 6, 153, 40, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 46, }, + { 2, 1, 0, 6, 157, 40, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 46, }, + { 2, 1, 0, 6, 161, 40, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 46, }, + { 2, 1, 0, 6, 165, 40, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 46, }, + { 2, 1, 0, 7, 36, 40, }, + { 1, 1, 0, 7, 36, 40, }, + { 0, 1, 0, 7, 40, 46, }, + { 2, 1, 0, 7, 40, 40, }, + { 1, 1, 0, 7, 40, 40, }, + { 0, 1, 0, 7, 44, 46, }, + { 2, 1, 0, 7, 44, 40, }, + { 1, 1, 0, 7, 44, 40, }, + { 0, 1, 0, 7, 48, 46, }, + { 2, 1, 0, 7, 48, 40, }, + { 1, 1, 0, 7, 48, 40, }, + { 0, 1, 0, 7, 52, 46, }, + { 2, 1, 0, 7, 52, 40, }, + { 1, 1, 0, 7, 52, 40, }, + { 0, 1, 0, 7, 56, 46, }, + { 2, 1, 0, 7, 56, 40, }, + { 1, 1, 0, 7, 56, 40, }, + { 0, 1, 0, 7, 60, 46, }, + { 2, 1, 0, 7, 60, 40, }, + { 1, 1, 0, 7, 60, 40, }, + { 0, 1, 0, 7, 64, 46, }, + { 2, 1, 0, 7, 64, 40, }, + { 1, 1, 0, 7, 64, 40, }, + { 0, 1, 0, 7, 100, 46, }, + { 2, 1, 0, 7, 100, 40, }, + { 1, 1, 0, 7, 100, 40, }, + { 0, 1, 0, 7, 104, 46, }, + { 2, 1, 0, 7, 104, 40, }, + { 1, 1, 0, 7, 104, 40, }, + { 0, 1, 0, 7, 108, 46, }, + { 2, 1, 0, 7, 108, 40, }, + { 1, 1, 0, 7, 108, 40, }, + { 0, 1, 0, 7, 112, 46, }, + { 2, 1, 0, 7, 112, 40, }, + { 1, 1, 0, 7, 112, 40, }, + { 0, 1, 0, 7, 116, 46, }, + { 2, 1, 0, 7, 116, 40, }, + { 1, 1, 0, 7, 116, 40, }, + { 0, 1, 0, 7, 120, 46, }, + { 2, 1, 0, 7, 120, 40, }, + { 1, 1, 0, 7, 120, 40, }, + { 0, 1, 0, 7, 124, 46, }, + { 2, 1, 0, 7, 124, 40, }, + { 1, 1, 0, 7, 124, 40, }, + { 0, 1, 0, 7, 128, 46, }, + { 2, 1, 0, 7, 128, 40, }, + { 1, 1, 0, 7, 128, 40, }, + { 0, 1, 0, 7, 132, 46, }, + { 2, 1, 0, 7, 132, 40, }, + { 1, 1, 0, 7, 132, 40, }, + { 0, 1, 0, 7, 136, 46, }, + { 2, 1, 0, 7, 136, 40, }, + { 1, 1, 0, 7, 136, 40, }, + { 0, 1, 0, 7, 140, 46, }, + { 2, 1, 0, 7, 140, 40, }, + { 1, 1, 0, 7, 140, 40, }, + { 0, 1, 0, 7, 149, 46, }, + { 2, 1, 0, 7, 149, 40, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 46, }, + { 2, 1, 0, 7, 153, 40, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 46, }, + { 2, 1, 0, 7, 157, 40, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 46, }, + { 2, 1, 0, 7, 161, 40, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 46, }, + { 2, 1, 0, 7, 165, 40, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 46, }, + { 2, 1, 1, 2, 38, 40, }, + { 1, 1, 1, 2, 38, 40, }, + { 0, 1, 1, 2, 46, 46, }, + { 2, 1, 1, 2, 46, 40, }, + { 1, 1, 1, 2, 46, 40, }, + { 0, 1, 1, 2, 54, 46, }, + { 2, 1, 1, 2, 54, 40, }, + { 1, 1, 1, 2, 54, 40, }, + { 0, 1, 1, 2, 62, 46, }, + { 2, 1, 1, 2, 62, 40, }, + { 1, 1, 1, 2, 62, 40, }, + { 0, 1, 1, 2, 102, 46, }, + { 2, 1, 1, 2, 102, 40, }, + { 1, 1, 1, 2, 102, 40, }, + { 0, 1, 1, 2, 110, 46, }, + { 2, 1, 1, 2, 110, 40, }, + { 1, 1, 1, 2, 110, 40, }, + { 0, 1, 1, 2, 118, 46, }, + { 2, 1, 1, 2, 118, 40, }, + { 1, 1, 1, 2, 118, 40, }, + { 0, 1, 1, 2, 126, 46, }, + { 2, 1, 1, 2, 126, 40, }, + { 1, 1, 1, 2, 126, 40, }, + { 0, 1, 1, 2, 134, 46, }, + { 2, 1, 1, 2, 134, 40, }, + { 1, 1, 1, 2, 134, 40, }, + { 0, 1, 1, 2, 151, 46, }, + { 2, 1, 1, 2, 151, 40, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 46, }, + { 2, 1, 1, 2, 159, 40, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 46, }, + { 2, 1, 1, 3, 38, 40, }, + { 1, 1, 1, 3, 38, 40, }, + { 0, 1, 1, 3, 46, 46, }, + { 2, 1, 1, 3, 46, 40, }, + { 1, 1, 1, 3, 46, 40, }, + { 0, 1, 1, 3, 54, 46, }, + { 2, 1, 1, 3, 54, 40, }, + { 1, 1, 1, 3, 54, 40, }, + { 0, 1, 1, 3, 62, 46, }, + { 2, 1, 1, 3, 62, 40, }, + { 1, 1, 1, 3, 62, 40, }, + { 0, 1, 1, 3, 102, 46, }, + { 2, 1, 1, 3, 102, 40, }, + { 1, 1, 1, 3, 102, 40, }, + { 0, 1, 1, 3, 110, 46, }, + { 2, 1, 1, 3, 110, 40, }, + { 1, 1, 1, 3, 110, 40, }, + { 0, 1, 1, 3, 118, 46, }, + { 2, 1, 1, 3, 118, 40, }, + { 1, 1, 1, 3, 118, 40, }, + { 0, 1, 1, 3, 126, 46, }, + { 2, 1, 1, 3, 126, 40, }, + { 1, 1, 1, 3, 126, 40, }, + { 0, 1, 1, 3, 134, 46, }, + { 2, 1, 1, 3, 134, 40, }, + { 1, 1, 1, 3, 134, 40, }, + { 0, 1, 1, 3, 151, 46, }, + { 2, 1, 1, 3, 151, 40, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 46, }, + { 2, 1, 1, 3, 159, 40, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 46, }, + { 2, 1, 1, 6, 38, 40, }, + { 1, 1, 1, 6, 38, 40, }, + { 0, 1, 1, 6, 46, 46, }, + { 2, 1, 1, 6, 46, 40, }, + { 1, 1, 1, 6, 46, 40, }, + { 0, 1, 1, 6, 54, 46, }, + { 2, 1, 1, 6, 54, 40, }, + { 1, 1, 1, 6, 54, 40, }, + { 0, 1, 1, 6, 62, 46, }, + { 2, 1, 1, 6, 62, 40, }, + { 1, 1, 1, 6, 62, 40, }, + { 0, 1, 1, 6, 102, 46, }, + { 2, 1, 1, 6, 102, 40, }, + { 1, 1, 1, 6, 102, 40, }, + { 0, 1, 1, 6, 110, 46, }, + { 2, 1, 1, 6, 110, 40, }, + { 1, 1, 1, 6, 110, 40, }, + { 0, 1, 1, 6, 118, 46, }, + { 2, 1, 1, 6, 118, 40, }, + { 1, 1, 1, 6, 118, 40, }, + { 0, 1, 1, 6, 126, 46, }, + { 2, 1, 1, 6, 126, 40, }, + { 1, 1, 1, 6, 126, 40, }, + { 0, 1, 1, 6, 134, 46, }, + { 2, 1, 1, 6, 134, 40, }, + { 1, 1, 1, 6, 134, 40, }, + { 0, 1, 1, 6, 151, 46, }, + { 2, 1, 1, 6, 151, 40, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 46, }, + { 2, 1, 1, 6, 159, 40, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 46, }, + { 2, 1, 1, 7, 38, 40, }, + { 1, 1, 1, 7, 38, 40, }, + { 0, 1, 1, 7, 46, 46, }, + { 2, 1, 1, 7, 46, 40, }, + { 1, 1, 1, 7, 46, 40, }, + { 0, 1, 1, 7, 54, 46, }, + { 2, 1, 1, 7, 54, 40, }, + { 1, 1, 1, 7, 54, 40, }, + { 0, 1, 1, 7, 62, 46, }, + { 2, 1, 1, 7, 62, 40, }, + { 1, 1, 1, 7, 62, 40, }, + { 0, 1, 1, 7, 102, 46, }, + { 2, 1, 1, 7, 102, 40, }, + { 1, 1, 1, 7, 102, 40, }, + { 0, 1, 1, 7, 110, 46, }, + { 2, 1, 1, 7, 110, 40, }, + { 1, 1, 1, 7, 110, 40, }, + { 0, 1, 1, 7, 118, 46, }, + { 2, 1, 1, 7, 118, 40, }, + { 1, 1, 1, 7, 118, 40, }, + { 0, 1, 1, 7, 126, 46, }, + { 2, 1, 1, 7, 126, 40, }, + { 1, 1, 1, 7, 126, 40, }, + { 0, 1, 1, 7, 134, 46, }, + { 2, 1, 1, 7, 134, 40, }, + { 1, 1, 1, 7, 134, 40, }, + { 0, 1, 1, 7, 151, 46, }, + { 2, 1, 1, 7, 151, 40, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 46, }, + { 2, 1, 1, 7, 159, 40, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 46, }, + { 2, 1, 2, 4, 42, 40, }, + { 1, 1, 2, 4, 42, 40, }, + { 0, 1, 2, 4, 58, 46, }, + { 2, 1, 2, 4, 58, 40, }, + { 1, 1, 2, 4, 58, 40, }, + { 0, 1, 2, 4, 106, 46, }, + { 2, 1, 2, 4, 106, 40, }, + { 1, 1, 2, 4, 106, 40, }, + { 0, 1, 2, 4, 122, 46, }, + { 2, 1, 2, 4, 122, 40, }, + { 1, 1, 2, 4, 122, 40, }, + { 0, 1, 2, 4, 155, 46, }, + { 2, 1, 2, 4, 155, 40, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 46, }, + { 2, 1, 2, 5, 42, 40, }, + { 1, 1, 2, 5, 42, 40, }, + { 0, 1, 2, 5, 58, 46, }, + { 2, 1, 2, 5, 58, 40, }, + { 1, 1, 2, 5, 58, 40, }, + { 0, 1, 2, 5, 106, 46, }, + { 2, 1, 2, 5, 106, 40, }, + { 1, 1, 2, 5, 106, 40, }, + { 0, 1, 2, 5, 122, 46, }, + { 2, 1, 2, 5, 122, 40, }, + { 1, 1, 2, 5, 122, 40, }, + { 0, 1, 2, 5, 155, 46, }, + { 2, 1, 2, 5, 155, 40, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 46, }, + { 2, 1, 2, 8, 42, 40, }, + { 1, 1, 2, 8, 42, 40, }, + { 0, 1, 2, 8, 58, 46, }, + { 2, 1, 2, 8, 58, 40, }, + { 1, 1, 2, 8, 58, 40, }, + { 0, 1, 2, 8, 106, 46, }, + { 2, 1, 2, 8, 106, 40, }, + { 1, 1, 2, 8, 106, 40, }, + { 0, 1, 2, 8, 122, 46, }, + { 2, 1, 2, 8, 122, 40, }, + { 1, 1, 2, 8, 122, 40, }, + { 0, 1, 2, 8, 155, 46, }, + { 2, 1, 2, 8, 155, 40, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 46, }, + { 2, 1, 2, 9, 42, 40, }, + { 1, 1, 2, 9, 42, 40, }, + { 0, 1, 2, 9, 58, 46, }, + { 2, 1, 2, 9, 58, 40, }, + { 1, 1, 2, 9, 58, 40, }, + { 0, 1, 2, 9, 106, 46, }, + { 2, 1, 2, 9, 106, 40, }, + { 1, 1, 2, 9, 106, 40, }, + { 0, 1, 2, 9, 122, 46, }, + { 2, 1, 2, 9, 122, 40, }, + { 1, 1, 2, 9, 122, 40, }, + { 0, 1, 2, 9, 155, 46, }, + { 2, 1, 2, 9, 155, 40, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type5); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type7[] = { + { 0, 0, 0, 0, 1, 44, }, + { 2, 0, 0, 0, 1, 32, }, + { 1, 0, 0, 0, 1, 32, }, + { 0, 0, 0, 0, 2, 52, }, + { 2, 0, 0, 0, 2, 32, }, + { 1, 0, 0, 0, 2, 32, }, + { 0, 0, 0, 0, 3, 52, }, + { 2, 0, 0, 0, 3, 32, }, + { 1, 0, 0, 0, 3, 32, }, + { 0, 0, 0, 0, 4, 52, }, + { 2, 0, 0, 0, 4, 32, }, + { 1, 0, 0, 0, 4, 32, }, + { 0, 0, 0, 0, 5, 52, }, + { 2, 0, 0, 0, 5, 32, }, + { 1, 0, 0, 0, 5, 32, }, + { 0, 0, 0, 0, 6, 52, }, + { 2, 0, 0, 0, 6, 32, }, + { 1, 0, 0, 0, 6, 32, }, + { 0, 0, 0, 0, 7, 52, }, + { 2, 0, 0, 0, 7, 32, }, + { 1, 0, 0, 0, 7, 32, }, + { 0, 0, 0, 0, 8, 52, }, + { 2, 0, 0, 0, 8, 32, }, + { 1, 0, 0, 0, 8, 32, }, + { 0, 0, 0, 0, 9, 52, }, + { 2, 0, 0, 0, 9, 32, }, + { 1, 0, 0, 0, 9, 32, }, + { 0, 0, 0, 0, 10, 52, }, + { 2, 0, 0, 0, 10, 32, }, + { 1, 0, 0, 0, 10, 32, }, + { 0, 0, 0, 0, 11, 44, }, + { 2, 0, 0, 0, 11, 32, }, + { 1, 0, 0, 0, 11, 32, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 32, }, + { 1, 0, 0, 0, 12, 32, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 32, }, + { 1, 0, 0, 0, 13, 32, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 32, }, + { 0, 0, 0, 1, 1, 38, }, + { 2, 0, 0, 1, 1, 32, }, + { 1, 0, 0, 1, 1, 32, }, + { 0, 0, 0, 1, 2, 46, }, + { 2, 0, 0, 1, 2, 32, }, + { 1, 0, 0, 1, 2, 32, }, + { 0, 0, 0, 1, 3, 46, }, + { 2, 0, 0, 1, 3, 32, }, + { 1, 0, 0, 1, 3, 32, }, + { 0, 0, 0, 1, 4, 46, }, + { 2, 0, 0, 1, 4, 32, }, + { 1, 0, 0, 1, 4, 32, }, + { 0, 0, 0, 1, 5, 46, }, + { 2, 0, 0, 1, 5, 32, }, + { 1, 0, 0, 1, 5, 32, }, + { 0, 0, 0, 1, 6, 46, }, + { 2, 0, 0, 1, 6, 32, }, + { 1, 0, 0, 1, 6, 32, }, + { 0, 0, 0, 1, 7, 46, }, + { 2, 0, 0, 1, 7, 32, }, + { 1, 0, 0, 1, 7, 32, }, + { 0, 0, 0, 1, 8, 46, }, + { 2, 0, 0, 1, 8, 32, }, + { 1, 0, 0, 1, 8, 32, }, + { 0, 0, 0, 1, 9, 46, }, + { 2, 0, 0, 1, 9, 32, }, + { 1, 0, 0, 1, 9, 32, }, + { 0, 0, 0, 1, 10, 46, }, + { 2, 0, 0, 1, 10, 32, }, + { 1, 0, 0, 1, 10, 32, }, + { 0, 0, 0, 1, 11, 38, }, + { 2, 0, 0, 1, 11, 32, }, + { 1, 0, 0, 1, 11, 32, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 32, }, + { 1, 0, 0, 1, 12, 32, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 32, }, + { 1, 0, 0, 1, 13, 32, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 63, }, + { 0, 0, 0, 2, 1, 34, }, + { 2, 0, 0, 2, 1, 32, }, + { 1, 0, 0, 2, 1, 32, }, + { 0, 0, 0, 2, 2, 46, }, + { 2, 0, 0, 2, 2, 32, }, + { 1, 0, 0, 2, 2, 32, }, + { 0, 0, 0, 2, 3, 46, }, + { 2, 0, 0, 2, 3, 32, }, + { 1, 0, 0, 2, 3, 32, }, + { 0, 0, 0, 2, 4, 46, }, + { 2, 0, 0, 2, 4, 32, }, + { 1, 0, 0, 2, 4, 32, }, + { 0, 0, 0, 2, 5, 46, }, + { 2, 0, 0, 2, 5, 32, }, + { 1, 0, 0, 2, 5, 32, }, + { 0, 0, 0, 2, 6, 46, }, + { 2, 0, 0, 2, 6, 32, }, + { 1, 0, 0, 2, 6, 32, }, + { 0, 0, 0, 2, 7, 46, }, + { 2, 0, 0, 2, 7, 32, }, + { 1, 0, 0, 2, 7, 32, }, + { 0, 0, 0, 2, 8, 46, }, + { 2, 0, 0, 2, 8, 32, }, + { 1, 0, 0, 2, 8, 32, }, + { 0, 0, 0, 2, 9, 46, }, + { 2, 0, 0, 2, 9, 32, }, + { 1, 0, 0, 2, 9, 32, }, + { 0, 0, 0, 2, 10, 46, }, + { 2, 0, 0, 2, 10, 32, }, + { 1, 0, 0, 2, 10, 32, }, + { 0, 0, 0, 2, 11, 34, }, + { 2, 0, 0, 2, 11, 32, }, + { 1, 0, 0, 2, 11, 32, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 32, }, + { 1, 0, 0, 2, 12, 32, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 32, }, + { 1, 0, 0, 2, 13, 32, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 63, }, + { 0, 0, 0, 3, 1, 32, }, + { 2, 0, 0, 3, 1, 30, }, + { 1, 0, 0, 3, 1, 30, }, + { 0, 0, 0, 3, 2, 44, }, + { 2, 0, 0, 3, 2, 30, }, + { 1, 0, 0, 3, 2, 30, }, + { 0, 0, 0, 3, 3, 44, }, + { 2, 0, 0, 3, 3, 30, }, + { 1, 0, 0, 3, 3, 30, }, + { 0, 0, 0, 3, 4, 44, }, + { 2, 0, 0, 3, 4, 30, }, + { 1, 0, 0, 3, 4, 30, }, + { 0, 0, 0, 3, 5, 44, }, + { 2, 0, 0, 3, 5, 30, }, + { 1, 0, 0, 3, 5, 30, }, + { 0, 0, 0, 3, 6, 44, }, + { 2, 0, 0, 3, 6, 30, }, + { 1, 0, 0, 3, 6, 30, }, + { 0, 0, 0, 3, 7, 44, }, + { 2, 0, 0, 3, 7, 30, }, + { 1, 0, 0, 3, 7, 30, }, + { 0, 0, 0, 3, 8, 44, }, + { 2, 0, 0, 3, 8, 30, }, + { 1, 0, 0, 3, 8, 30, }, + { 0, 0, 0, 3, 9, 44, }, + { 2, 0, 0, 3, 9, 30, }, + { 1, 0, 0, 3, 9, 30, }, + { 0, 0, 0, 3, 10, 44, }, + { 2, 0, 0, 3, 10, 30, }, + { 1, 0, 0, 3, 10, 30, }, + { 0, 0, 0, 3, 11, 32, }, + { 2, 0, 0, 3, 11, 30, }, + { 1, 0, 0, 3, 11, 30, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 30, }, + { 1, 0, 0, 3, 12, 30, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 30, }, + { 1, 0, 0, 3, 13, 30, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 63, }, + { 0, 0, 0, 6, 1, 30, }, + { 2, 0, 0, 6, 1, 28, }, + { 1, 0, 0, 6, 1, 28, }, + { 0, 0, 0, 6, 2, 42, }, + { 2, 0, 0, 6, 2, 28, }, + { 1, 0, 0, 6, 2, 28, }, + { 0, 0, 0, 6, 3, 42, }, + { 2, 0, 0, 6, 3, 28, }, + { 1, 0, 0, 6, 3, 28, }, + { 0, 0, 0, 6, 4, 42, }, + { 2, 0, 0, 6, 4, 28, }, + { 1, 0, 0, 6, 4, 28, }, + { 0, 0, 0, 6, 5, 42, }, + { 2, 0, 0, 6, 5, 28, }, + { 1, 0, 0, 6, 5, 28, }, + { 0, 0, 0, 6, 6, 42, }, + { 2, 0, 0, 6, 6, 28, }, + { 1, 0, 0, 6, 6, 28, }, + { 0, 0, 0, 6, 7, 42, }, + { 2, 0, 0, 6, 7, 28, }, + { 1, 0, 0, 6, 7, 28, }, + { 0, 0, 0, 6, 8, 42, }, + { 2, 0, 0, 6, 8, 28, }, + { 1, 0, 0, 6, 8, 28, }, + { 0, 0, 0, 6, 9, 42, }, + { 2, 0, 0, 6, 9, 28, }, + { 1, 0, 0, 6, 9, 28, }, + { 0, 0, 0, 6, 10, 42, }, + { 2, 0, 0, 6, 10, 28, }, + { 1, 0, 0, 6, 10, 28, }, + { 0, 0, 0, 6, 11, 30, }, + { 2, 0, 0, 6, 11, 28, }, + { 1, 0, 0, 6, 11, 28, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 28, }, + { 1, 0, 0, 6, 12, 28, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 28, }, + { 1, 0, 0, 6, 13, 28, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 63, }, + { 0, 0, 0, 7, 1, 28, }, + { 2, 0, 0, 7, 1, 26, }, + { 1, 0, 0, 7, 1, 26, }, + { 0, 0, 0, 7, 2, 40, }, + { 2, 0, 0, 7, 2, 26, }, + { 1, 0, 0, 7, 2, 26, }, + { 0, 0, 0, 7, 3, 40, }, + { 2, 0, 0, 7, 3, 26, }, + { 1, 0, 0, 7, 3, 26, }, + { 0, 0, 0, 7, 4, 40, }, + { 2, 0, 0, 7, 4, 26, }, + { 1, 0, 0, 7, 4, 26, }, + { 0, 0, 0, 7, 5, 40, }, + { 2, 0, 0, 7, 5, 26, }, + { 1, 0, 0, 7, 5, 26, }, + { 0, 0, 0, 7, 6, 40, }, + { 2, 0, 0, 7, 6, 26, }, + { 1, 0, 0, 7, 6, 26, }, + { 0, 0, 0, 7, 7, 40, }, + { 2, 0, 0, 7, 7, 26, }, + { 1, 0, 0, 7, 7, 26, }, + { 0, 0, 0, 7, 8, 40, }, + { 2, 0, 0, 7, 8, 26, }, + { 1, 0, 0, 7, 8, 26, }, + { 0, 0, 0, 7, 9, 40, }, + { 2, 0, 0, 7, 9, 26, }, + { 1, 0, 0, 7, 9, 26, }, + { 0, 0, 0, 7, 10, 40, }, + { 2, 0, 0, 7, 10, 26, }, + { 1, 0, 0, 7, 10, 26, }, + { 0, 0, 0, 7, 11, 28, }, + { 2, 0, 0, 7, 11, 26, }, + { 1, 0, 0, 7, 11, 26, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 26, }, + { 1, 0, 0, 7, 12, 26, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 26, }, + { 1, 0, 0, 7, 13, 26, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 63, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 36, }, + { 2, 0, 1, 2, 3, 32, }, + { 1, 0, 1, 2, 3, 32, }, + { 0, 0, 1, 2, 4, 40, }, + { 2, 0, 1, 2, 4, 32, }, + { 1, 0, 1, 2, 4, 32, }, + { 0, 0, 1, 2, 5, 40, }, + { 2, 0, 1, 2, 5, 32, }, + { 1, 0, 1, 2, 5, 32, }, + { 0, 0, 1, 2, 6, 40, }, + { 2, 0, 1, 2, 6, 32, }, + { 1, 0, 1, 2, 6, 32, }, + { 0, 0, 1, 2, 7, 40, }, + { 2, 0, 1, 2, 7, 32, }, + { 1, 0, 1, 2, 7, 32, }, + { 0, 0, 1, 2, 8, 40, }, + { 2, 0, 1, 2, 8, 32, }, + { 1, 0, 1, 2, 8, 32, }, + { 0, 0, 1, 2, 9, 40, }, + { 2, 0, 1, 2, 9, 32, }, + { 1, 0, 1, 2, 9, 32, }, + { 0, 0, 1, 2, 10, 40, }, + { 2, 0, 1, 2, 10, 32, }, + { 1, 0, 1, 2, 10, 32, }, + { 0, 0, 1, 2, 11, 34, }, + { 2, 0, 1, 2, 11, 32, }, + { 1, 0, 1, 2, 11, 32, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 32, }, + { 1, 0, 1, 2, 12, 32, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 32, }, + { 1, 0, 1, 2, 13, 32, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 34, }, + { 2, 0, 1, 3, 3, 30, }, + { 1, 0, 1, 3, 3, 30, }, + { 0, 0, 1, 3, 4, 38, }, + { 2, 0, 1, 3, 4, 30, }, + { 1, 0, 1, 3, 4, 30, }, + { 0, 0, 1, 3, 5, 38, }, + { 2, 0, 1, 3, 5, 30, }, + { 1, 0, 1, 3, 5, 30, }, + { 0, 0, 1, 3, 6, 38, }, + { 2, 0, 1, 3, 6, 30, }, + { 1, 0, 1, 3, 6, 30, }, + { 0, 0, 1, 3, 7, 38, }, + { 2, 0, 1, 3, 7, 30, }, + { 1, 0, 1, 3, 7, 30, }, + { 0, 0, 1, 3, 8, 38, }, + { 2, 0, 1, 3, 8, 30, }, + { 1, 0, 1, 3, 8, 30, }, + { 0, 0, 1, 3, 9, 38, }, + { 2, 0, 1, 3, 9, 30, }, + { 1, 0, 1, 3, 9, 30, }, + { 0, 0, 1, 3, 10, 38, }, + { 2, 0, 1, 3, 10, 30, }, + { 1, 0, 1, 3, 10, 30, }, + { 0, 0, 1, 3, 11, 32, }, + { 2, 0, 1, 3, 11, 30, }, + { 1, 0, 1, 3, 11, 30, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 30, }, + { 1, 0, 1, 3, 12, 30, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 30, }, + { 1, 0, 1, 3, 13, 30, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 32, }, + { 2, 0, 1, 6, 3, 28, }, + { 1, 0, 1, 6, 3, 28, }, + { 0, 0, 1, 6, 4, 36, }, + { 2, 0, 1, 6, 4, 28, }, + { 1, 0, 1, 6, 4, 28, }, + { 0, 0, 1, 6, 5, 36, }, + { 2, 0, 1, 6, 5, 28, }, + { 1, 0, 1, 6, 5, 28, }, + { 0, 0, 1, 6, 6, 36, }, + { 2, 0, 1, 6, 6, 28, }, + { 1, 0, 1, 6, 6, 28, }, + { 0, 0, 1, 6, 7, 36, }, + { 2, 0, 1, 6, 7, 28, }, + { 1, 0, 1, 6, 7, 28, }, + { 0, 0, 1, 6, 8, 36, }, + { 2, 0, 1, 6, 8, 28, }, + { 1, 0, 1, 6, 8, 28, }, + { 0, 0, 1, 6, 9, 36, }, + { 2, 0, 1, 6, 9, 28, }, + { 1, 0, 1, 6, 9, 28, }, + { 0, 0, 1, 6, 10, 36, }, + { 2, 0, 1, 6, 10, 28, }, + { 1, 0, 1, 6, 10, 28, }, + { 0, 0, 1, 6, 11, 30, }, + { 2, 0, 1, 6, 11, 28, }, + { 1, 0, 1, 6, 11, 28, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 28, }, + { 1, 0, 1, 6, 12, 28, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 28, }, + { 1, 0, 1, 6, 13, 28, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 32, }, + { 2, 0, 1, 7, 3, 26, }, + { 1, 0, 1, 7, 3, 26, }, + { 0, 0, 1, 7, 4, 36, }, + { 2, 0, 1, 7, 4, 26, }, + { 1, 0, 1, 7, 4, 26, }, + { 0, 0, 1, 7, 5, 36, }, + { 2, 0, 1, 7, 5, 26, }, + { 1, 0, 1, 7, 5, 26, }, + { 0, 0, 1, 7, 6, 36, }, + { 2, 0, 1, 7, 6, 26, }, + { 1, 0, 1, 7, 6, 26, }, + { 0, 0, 1, 7, 7, 36, }, + { 2, 0, 1, 7, 7, 26, }, + { 1, 0, 1, 7, 7, 26, }, + { 0, 0, 1, 7, 8, 36, }, + { 2, 0, 1, 7, 8, 26, }, + { 1, 0, 1, 7, 8, 26, }, + { 0, 0, 1, 7, 9, 36, }, + { 2, 0, 1, 7, 9, 26, }, + { 1, 0, 1, 7, 9, 26, }, + { 0, 0, 1, 7, 10, 36, }, + { 2, 0, 1, 7, 10, 26, }, + { 1, 0, 1, 7, 10, 26, }, + { 0, 0, 1, 7, 11, 30, }, + { 2, 0, 1, 7, 11, 26, }, + { 1, 0, 1, 7, 11, 26, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 26, }, + { 1, 0, 1, 7, 12, 26, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 26, }, + { 1, 0, 1, 7, 13, 26, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 38, }, + { 2, 1, 0, 1, 36, 32, }, + { 1, 1, 0, 1, 36, 32, }, + { 0, 1, 0, 1, 40, 38, }, + { 2, 1, 0, 1, 40, 32, }, + { 1, 1, 0, 1, 40, 32, }, + { 0, 1, 0, 1, 44, 38, }, + { 2, 1, 0, 1, 44, 32, }, + { 1, 1, 0, 1, 44, 32, }, + { 0, 1, 0, 1, 48, 38, }, + { 2, 1, 0, 1, 48, 32, }, + { 1, 1, 0, 1, 48, 32, }, + { 0, 1, 0, 1, 52, 38, }, + { 2, 1, 0, 1, 52, 32, }, + { 1, 1, 0, 1, 52, 32, }, + { 0, 1, 0, 1, 56, 38, }, + { 2, 1, 0, 1, 56, 32, }, + { 1, 1, 0, 1, 56, 32, }, + { 0, 1, 0, 1, 60, 38, }, + { 2, 1, 0, 1, 60, 32, }, + { 1, 1, 0, 1, 60, 32, }, + { 0, 1, 0, 1, 64, 38, }, + { 2, 1, 0, 1, 64, 32, }, + { 1, 1, 0, 1, 64, 32, }, + { 0, 1, 0, 1, 100, 36, }, + { 2, 1, 0, 1, 100, 32, }, + { 1, 1, 0, 1, 100, 32, }, + { 0, 1, 0, 1, 104, 36, }, + { 2, 1, 0, 1, 104, 32, }, + { 1, 1, 0, 1, 104, 32, }, + { 0, 1, 0, 1, 108, 36, }, + { 2, 1, 0, 1, 108, 32, }, + { 1, 1, 0, 1, 108, 32, }, + { 0, 1, 0, 1, 112, 36, }, + { 2, 1, 0, 1, 112, 32, }, + { 1, 1, 0, 1, 112, 32, }, + { 0, 1, 0, 1, 116, 36, }, + { 2, 1, 0, 1, 116, 32, }, + { 1, 1, 0, 1, 116, 32, }, + { 0, 1, 0, 1, 120, 36, }, + { 2, 1, 0, 1, 120, 32, }, + { 1, 1, 0, 1, 120, 32, }, + { 0, 1, 0, 1, 124, 36, }, + { 2, 1, 0, 1, 124, 32, }, + { 1, 1, 0, 1, 124, 32, }, + { 0, 1, 0, 1, 128, 36, }, + { 2, 1, 0, 1, 128, 32, }, + { 1, 1, 0, 1, 128, 32, }, + { 0, 1, 0, 1, 132, 36, }, + { 2, 1, 0, 1, 132, 32, }, + { 1, 1, 0, 1, 132, 32, }, + { 0, 1, 0, 1, 136, 36, }, + { 2, 1, 0, 1, 136, 32, }, + { 1, 1, 0, 1, 136, 32, }, + { 0, 1, 0, 1, 140, 36, }, + { 2, 1, 0, 1, 140, 32, }, + { 1, 1, 0, 1, 140, 32, }, + { 0, 1, 0, 1, 149, 36, }, + { 2, 1, 0, 1, 149, 32, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 36, }, + { 2, 1, 0, 1, 153, 32, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 36, }, + { 2, 1, 0, 1, 157, 32, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 36, }, + { 2, 1, 0, 1, 161, 32, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 36, }, + { 2, 1, 0, 1, 165, 32, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 36, }, + { 2, 1, 0, 2, 36, 32, }, + { 1, 1, 0, 2, 36, 32, }, + { 0, 1, 0, 2, 40, 36, }, + { 2, 1, 0, 2, 40, 32, }, + { 1, 1, 0, 2, 40, 32, }, + { 0, 1, 0, 2, 44, 36, }, + { 2, 1, 0, 2, 44, 32, }, + { 1, 1, 0, 2, 44, 32, }, + { 0, 1, 0, 2, 48, 36, }, + { 2, 1, 0, 2, 48, 32, }, + { 1, 1, 0, 2, 48, 32, }, + { 0, 1, 0, 2, 52, 36, }, + { 2, 1, 0, 2, 52, 32, }, + { 1, 1, 0, 2, 52, 32, }, + { 0, 1, 0, 2, 56, 36, }, + { 2, 1, 0, 2, 56, 32, }, + { 1, 1, 0, 2, 56, 32, }, + { 0, 1, 0, 2, 60, 36, }, + { 2, 1, 0, 2, 60, 32, }, + { 1, 1, 0, 2, 60, 32, }, + { 0, 1, 0, 2, 64, 36, }, + { 2, 1, 0, 2, 64, 32, }, + { 1, 1, 0, 2, 64, 32, }, + { 0, 1, 0, 2, 100, 36, }, + { 2, 1, 0, 2, 100, 32, }, + { 1, 1, 0, 2, 100, 32, }, + { 0, 1, 0, 2, 104, 36, }, + { 2, 1, 0, 2, 104, 32, }, + { 1, 1, 0, 2, 104, 32, }, + { 0, 1, 0, 2, 108, 36, }, + { 2, 1, 0, 2, 108, 32, }, + { 1, 1, 0, 2, 108, 32, }, + { 0, 1, 0, 2, 112, 36, }, + { 2, 1, 0, 2, 112, 32, }, + { 1, 1, 0, 2, 112, 32, }, + { 0, 1, 0, 2, 116, 36, }, + { 2, 1, 0, 2, 116, 32, }, + { 1, 1, 0, 2, 116, 32, }, + { 0, 1, 0, 2, 120, 36, }, + { 2, 1, 0, 2, 120, 32, }, + { 1, 1, 0, 2, 120, 32, }, + { 0, 1, 0, 2, 124, 36, }, + { 2, 1, 0, 2, 124, 32, }, + { 1, 1, 0, 2, 124, 32, }, + { 0, 1, 0, 2, 128, 36, }, + { 2, 1, 0, 2, 128, 32, }, + { 1, 1, 0, 2, 128, 32, }, + { 0, 1, 0, 2, 132, 36, }, + { 2, 1, 0, 2, 132, 32, }, + { 1, 1, 0, 2, 132, 32, }, + { 0, 1, 0, 2, 136, 36, }, + { 2, 1, 0, 2, 136, 32, }, + { 1, 1, 0, 2, 136, 32, }, + { 0, 1, 0, 2, 140, 34, }, + { 2, 1, 0, 2, 140, 32, }, + { 1, 1, 0, 2, 140, 32, }, + { 0, 1, 0, 2, 149, 32, }, + { 2, 1, 0, 2, 149, 32, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 38, }, + { 2, 1, 0, 2, 153, 32, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 38, }, + { 2, 1, 0, 2, 157, 32, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 38, }, + { 2, 1, 0, 2, 161, 32, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 38, }, + { 2, 1, 0, 2, 165, 32, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 34, }, + { 2, 1, 0, 3, 36, 30, }, + { 1, 1, 0, 3, 36, 30, }, + { 0, 1, 0, 3, 40, 34, }, + { 2, 1, 0, 3, 40, 30, }, + { 1, 1, 0, 3, 40, 30, }, + { 0, 1, 0, 3, 44, 34, }, + { 2, 1, 0, 3, 44, 30, }, + { 1, 1, 0, 3, 44, 30, }, + { 0, 1, 0, 3, 48, 34, }, + { 2, 1, 0, 3, 48, 30, }, + { 1, 1, 0, 3, 48, 30, }, + { 0, 1, 0, 3, 52, 34, }, + { 2, 1, 0, 3, 52, 30, }, + { 1, 1, 0, 3, 52, 30, }, + { 0, 1, 0, 3, 56, 34, }, + { 2, 1, 0, 3, 56, 30, }, + { 1, 1, 0, 3, 56, 30, }, + { 0, 1, 0, 3, 60, 34, }, + { 2, 1, 0, 3, 60, 30, }, + { 1, 1, 0, 3, 60, 30, }, + { 0, 1, 0, 3, 64, 34, }, + { 2, 1, 0, 3, 64, 30, }, + { 1, 1, 0, 3, 64, 30, }, + { 0, 1, 0, 3, 100, 34, }, + { 2, 1, 0, 3, 100, 30, }, + { 1, 1, 0, 3, 100, 30, }, + { 0, 1, 0, 3, 104, 34, }, + { 2, 1, 0, 3, 104, 30, }, + { 1, 1, 0, 3, 104, 30, }, + { 0, 1, 0, 3, 108, 34, }, + { 2, 1, 0, 3, 108, 30, }, + { 1, 1, 0, 3, 108, 30, }, + { 0, 1, 0, 3, 112, 34, }, + { 2, 1, 0, 3, 112, 30, }, + { 1, 1, 0, 3, 112, 30, }, + { 0, 1, 0, 3, 116, 34, }, + { 2, 1, 0, 3, 116, 30, }, + { 1, 1, 0, 3, 116, 30, }, + { 0, 1, 0, 3, 120, 34, }, + { 2, 1, 0, 3, 120, 30, }, + { 1, 1, 0, 3, 120, 30, }, + { 0, 1, 0, 3, 124, 34, }, + { 2, 1, 0, 3, 124, 30, }, + { 1, 1, 0, 3, 124, 30, }, + { 0, 1, 0, 3, 128, 34, }, + { 2, 1, 0, 3, 128, 30, }, + { 1, 1, 0, 3, 128, 30, }, + { 0, 1, 0, 3, 132, 34, }, + { 2, 1, 0, 3, 132, 30, }, + { 1, 1, 0, 3, 132, 30, }, + { 0, 1, 0, 3, 136, 34, }, + { 2, 1, 0, 3, 136, 30, }, + { 1, 1, 0, 3, 136, 30, }, + { 0, 1, 0, 3, 140, 32, }, + { 2, 1, 0, 3, 140, 30, }, + { 1, 1, 0, 3, 140, 30, }, + { 0, 1, 0, 3, 149, 30, }, + { 2, 1, 0, 3, 149, 30, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 36, }, + { 2, 1, 0, 3, 153, 30, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 36, }, + { 2, 1, 0, 3, 157, 30, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 36, }, + { 2, 1, 0, 3, 161, 30, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 36, }, + { 2, 1, 0, 3, 165, 30, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 32, }, + { 2, 1, 0, 6, 36, 28, }, + { 1, 1, 0, 6, 36, 28, }, + { 0, 1, 0, 6, 40, 32, }, + { 2, 1, 0, 6, 40, 28, }, + { 1, 1, 0, 6, 40, 28, }, + { 0, 1, 0, 6, 44, 32, }, + { 2, 1, 0, 6, 44, 28, }, + { 1, 1, 0, 6, 44, 28, }, + { 0, 1, 0, 6, 48, 32, }, + { 2, 1, 0, 6, 48, 28, }, + { 1, 1, 0, 6, 48, 28, }, + { 0, 1, 0, 6, 52, 32, }, + { 2, 1, 0, 6, 52, 28, }, + { 1, 1, 0, 6, 52, 28, }, + { 0, 1, 0, 6, 56, 32, }, + { 2, 1, 0, 6, 56, 28, }, + { 1, 1, 0, 6, 56, 28, }, + { 0, 1, 0, 6, 60, 32, }, + { 2, 1, 0, 6, 60, 28, }, + { 1, 1, 0, 6, 60, 28, }, + { 0, 1, 0, 6, 64, 32, }, + { 2, 1, 0, 6, 64, 28, }, + { 1, 1, 0, 6, 64, 28, }, + { 0, 1, 0, 6, 100, 32, }, + { 2, 1, 0, 6, 100, 28, }, + { 1, 1, 0, 6, 100, 28, }, + { 0, 1, 0, 6, 104, 32, }, + { 2, 1, 0, 6, 104, 28, }, + { 1, 1, 0, 6, 104, 28, }, + { 0, 1, 0, 6, 108, 32, }, + { 2, 1, 0, 6, 108, 28, }, + { 1, 1, 0, 6, 108, 28, }, + { 0, 1, 0, 6, 112, 32, }, + { 2, 1, 0, 6, 112, 28, }, + { 1, 1, 0, 6, 112, 28, }, + { 0, 1, 0, 6, 116, 32, }, + { 2, 1, 0, 6, 116, 28, }, + { 1, 1, 0, 6, 116, 28, }, + { 0, 1, 0, 6, 120, 32, }, + { 2, 1, 0, 6, 120, 28, }, + { 1, 1, 0, 6, 120, 28, }, + { 0, 1, 0, 6, 124, 32, }, + { 2, 1, 0, 6, 124, 28, }, + { 1, 1, 0, 6, 124, 28, }, + { 0, 1, 0, 6, 128, 32, }, + { 2, 1, 0, 6, 128, 28, }, + { 1, 1, 0, 6, 128, 28, }, + { 0, 1, 0, 6, 132, 32, }, + { 2, 1, 0, 6, 132, 28, }, + { 1, 1, 0, 6, 132, 28, }, + { 0, 1, 0, 6, 136, 32, }, + { 2, 1, 0, 6, 136, 28, }, + { 1, 1, 0, 6, 136, 28, }, + { 0, 1, 0, 6, 140, 30, }, + { 2, 1, 0, 6, 140, 28, }, + { 1, 1, 0, 6, 140, 28, }, + { 0, 1, 0, 6, 149, 28, }, + { 2, 1, 0, 6, 149, 28, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 34, }, + { 2, 1, 0, 6, 153, 28, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 34, }, + { 2, 1, 0, 6, 157, 28, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 34, }, + { 2, 1, 0, 6, 161, 28, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 34, }, + { 2, 1, 0, 6, 165, 28, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 30, }, + { 2, 1, 0, 7, 36, 26, }, + { 1, 1, 0, 7, 36, 26, }, + { 0, 1, 0, 7, 40, 30, }, + { 2, 1, 0, 7, 40, 26, }, + { 1, 1, 0, 7, 40, 26, }, + { 0, 1, 0, 7, 44, 30, }, + { 2, 1, 0, 7, 44, 26, }, + { 1, 1, 0, 7, 44, 26, }, + { 0, 1, 0, 7, 48, 30, }, + { 2, 1, 0, 7, 48, 26, }, + { 1, 1, 0, 7, 48, 26, }, + { 0, 1, 0, 7, 52, 30, }, + { 2, 1, 0, 7, 52, 26, }, + { 1, 1, 0, 7, 52, 26, }, + { 0, 1, 0, 7, 56, 30, }, + { 2, 1, 0, 7, 56, 26, }, + { 1, 1, 0, 7, 56, 26, }, + { 0, 1, 0, 7, 60, 30, }, + { 2, 1, 0, 7, 60, 26, }, + { 1, 1, 0, 7, 60, 26, }, + { 0, 1, 0, 7, 64, 30, }, + { 2, 1, 0, 7, 64, 26, }, + { 1, 1, 0, 7, 64, 26, }, + { 0, 1, 0, 7, 100, 30, }, + { 2, 1, 0, 7, 100, 26, }, + { 1, 1, 0, 7, 100, 26, }, + { 0, 1, 0, 7, 104, 30, }, + { 2, 1, 0, 7, 104, 26, }, + { 1, 1, 0, 7, 104, 26, }, + { 0, 1, 0, 7, 108, 30, }, + { 2, 1, 0, 7, 108, 26, }, + { 1, 1, 0, 7, 108, 26, }, + { 0, 1, 0, 7, 112, 30, }, + { 2, 1, 0, 7, 112, 26, }, + { 1, 1, 0, 7, 112, 26, }, + { 0, 1, 0, 7, 116, 30, }, + { 2, 1, 0, 7, 116, 26, }, + { 1, 1, 0, 7, 116, 26, }, + { 0, 1, 0, 7, 120, 30, }, + { 2, 1, 0, 7, 120, 26, }, + { 1, 1, 0, 7, 120, 26, }, + { 0, 1, 0, 7, 124, 30, }, + { 2, 1, 0, 7, 124, 26, }, + { 1, 1, 0, 7, 124, 26, }, + { 0, 1, 0, 7, 128, 30, }, + { 2, 1, 0, 7, 128, 26, }, + { 1, 1, 0, 7, 128, 26, }, + { 0, 1, 0, 7, 132, 30, }, + { 2, 1, 0, 7, 132, 26, }, + { 1, 1, 0, 7, 132, 26, }, + { 0, 1, 0, 7, 136, 30, }, + { 2, 1, 0, 7, 136, 26, }, + { 1, 1, 0, 7, 136, 26, }, + { 0, 1, 0, 7, 140, 28, }, + { 2, 1, 0, 7, 140, 26, }, + { 1, 1, 0, 7, 140, 26, }, + { 0, 1, 0, 7, 149, 26, }, + { 2, 1, 0, 7, 149, 26, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 32, }, + { 2, 1, 0, 7, 153, 26, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 32, }, + { 2, 1, 0, 7, 157, 26, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 32, }, + { 2, 1, 0, 7, 161, 26, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 32, }, + { 2, 1, 0, 7, 165, 26, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 32, }, + { 2, 1, 1, 2, 38, 32, }, + { 1, 1, 1, 2, 38, 32, }, + { 0, 1, 1, 2, 46, 32, }, + { 2, 1, 1, 2, 46, 32, }, + { 1, 1, 1, 2, 46, 32, }, + { 0, 1, 1, 2, 54, 32, }, + { 2, 1, 1, 2, 54, 32, }, + { 1, 1, 1, 2, 54, 32, }, + { 0, 1, 1, 2, 62, 30, }, + { 2, 1, 1, 2, 62, 32, }, + { 1, 1, 1, 2, 62, 32, }, + { 0, 1, 1, 2, 102, 30, }, + { 2, 1, 1, 2, 102, 32, }, + { 1, 1, 1, 2, 102, 32, }, + { 0, 1, 1, 2, 110, 38, }, + { 2, 1, 1, 2, 110, 32, }, + { 1, 1, 1, 2, 110, 32, }, + { 0, 1, 1, 2, 118, 38, }, + { 2, 1, 1, 2, 118, 32, }, + { 1, 1, 1, 2, 118, 32, }, + { 0, 1, 1, 2, 126, 38, }, + { 2, 1, 1, 2, 126, 32, }, + { 1, 1, 1, 2, 126, 32, }, + { 0, 1, 1, 2, 134, 38, }, + { 2, 1, 1, 2, 134, 32, }, + { 1, 1, 1, 2, 134, 32, }, + { 0, 1, 1, 2, 151, 32, }, + { 2, 1, 1, 2, 151, 32, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 32, }, + { 2, 1, 1, 2, 159, 32, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 30, }, + { 2, 1, 1, 3, 38, 30, }, + { 1, 1, 1, 3, 38, 30, }, + { 0, 1, 1, 3, 46, 30, }, + { 2, 1, 1, 3, 46, 30, }, + { 1, 1, 1, 3, 46, 30, }, + { 0, 1, 1, 3, 54, 30, }, + { 2, 1, 1, 3, 54, 30, }, + { 1, 1, 1, 3, 54, 30, }, + { 0, 1, 1, 3, 62, 28, }, + { 2, 1, 1, 3, 62, 30, }, + { 1, 1, 1, 3, 62, 30, }, + { 0, 1, 1, 3, 102, 28, }, + { 2, 1, 1, 3, 102, 30, }, + { 1, 1, 1, 3, 102, 30, }, + { 0, 1, 1, 3, 110, 36, }, + { 2, 1, 1, 3, 110, 30, }, + { 1, 1, 1, 3, 110, 30, }, + { 0, 1, 1, 3, 118, 36, }, + { 2, 1, 1, 3, 118, 30, }, + { 1, 1, 1, 3, 118, 30, }, + { 0, 1, 1, 3, 126, 36, }, + { 2, 1, 1, 3, 126, 30, }, + { 1, 1, 1, 3, 126, 30, }, + { 0, 1, 1, 3, 134, 36, }, + { 2, 1, 1, 3, 134, 30, }, + { 1, 1, 1, 3, 134, 30, }, + { 0, 1, 1, 3, 151, 30, }, + { 2, 1, 1, 3, 151, 30, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 30, }, + { 2, 1, 1, 3, 159, 30, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 28, }, + { 2, 1, 1, 6, 38, 28, }, + { 1, 1, 1, 6, 38, 28, }, + { 0, 1, 1, 6, 46, 28, }, + { 2, 1, 1, 6, 46, 28, }, + { 1, 1, 1, 6, 46, 28, }, + { 0, 1, 1, 6, 54, 28, }, + { 2, 1, 1, 6, 54, 28, }, + { 1, 1, 1, 6, 54, 28, }, + { 0, 1, 1, 6, 62, 26, }, + { 2, 1, 1, 6, 62, 28, }, + { 1, 1, 1, 6, 62, 28, }, + { 0, 1, 1, 6, 102, 26, }, + { 2, 1, 1, 6, 102, 28, }, + { 1, 1, 1, 6, 102, 28, }, + { 0, 1, 1, 6, 110, 34, }, + { 2, 1, 1, 6, 110, 28, }, + { 1, 1, 1, 6, 110, 28, }, + { 0, 1, 1, 6, 118, 34, }, + { 2, 1, 1, 6, 118, 28, }, + { 1, 1, 1, 6, 118, 28, }, + { 0, 1, 1, 6, 126, 34, }, + { 2, 1, 1, 6, 126, 28, }, + { 1, 1, 1, 6, 126, 28, }, + { 0, 1, 1, 6, 134, 34, }, + { 2, 1, 1, 6, 134, 28, }, + { 1, 1, 1, 6, 134, 28, }, + { 0, 1, 1, 6, 151, 28, }, + { 2, 1, 1, 6, 151, 28, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 28, }, + { 2, 1, 1, 6, 159, 28, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 26, }, + { 2, 1, 1, 7, 38, 26, }, + { 1, 1, 1, 7, 38, 26, }, + { 0, 1, 1, 7, 46, 26, }, + { 2, 1, 1, 7, 46, 26, }, + { 1, 1, 1, 7, 46, 26, }, + { 0, 1, 1, 7, 54, 26, }, + { 2, 1, 1, 7, 54, 26, }, + { 1, 1, 1, 7, 54, 26, }, + { 0, 1, 1, 7, 62, 24, }, + { 2, 1, 1, 7, 62, 26, }, + { 1, 1, 1, 7, 62, 26, }, + { 0, 1, 1, 7, 102, 24, }, + { 2, 1, 1, 7, 102, 26, }, + { 1, 1, 1, 7, 102, 26, }, + { 0, 1, 1, 7, 110, 32, }, + { 2, 1, 1, 7, 110, 26, }, + { 1, 1, 1, 7, 110, 26, }, + { 0, 1, 1, 7, 118, 32, }, + { 2, 1, 1, 7, 118, 26, }, + { 1, 1, 1, 7, 118, 26, }, + { 0, 1, 1, 7, 126, 32, }, + { 2, 1, 1, 7, 126, 26, }, + { 1, 1, 1, 7, 126, 26, }, + { 0, 1, 1, 7, 134, 32, }, + { 2, 1, 1, 7, 134, 26, }, + { 1, 1, 1, 7, 134, 26, }, + { 0, 1, 1, 7, 151, 26, }, + { 2, 1, 1, 7, 151, 26, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 26, }, + { 2, 1, 1, 7, 159, 26, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 26, }, + { 2, 1, 2, 4, 42, 32, }, + { 1, 1, 2, 4, 42, 32, }, + { 0, 1, 2, 4, 58, 26, }, + { 2, 1, 2, 4, 58, 32, }, + { 1, 1, 2, 4, 58, 32, }, + { 0, 1, 2, 4, 106, 28, }, + { 2, 1, 2, 4, 106, 32, }, + { 1, 1, 2, 4, 106, 32, }, + { 0, 1, 2, 4, 122, 28, }, + { 2, 1, 2, 4, 122, 32, }, + { 1, 1, 2, 4, 122, 32, }, + { 0, 1, 2, 4, 155, 28, }, + { 2, 1, 2, 4, 155, 32, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 24, }, + { 2, 1, 2, 5, 42, 30, }, + { 1, 1, 2, 5, 42, 30, }, + { 0, 1, 2, 5, 58, 24, }, + { 2, 1, 2, 5, 58, 30, }, + { 1, 1, 2, 5, 58, 30, }, + { 0, 1, 2, 5, 106, 26, }, + { 2, 1, 2, 5, 106, 30, }, + { 1, 1, 2, 5, 106, 30, }, + { 0, 1, 2, 5, 122, 26, }, + { 2, 1, 2, 5, 122, 30, }, + { 1, 1, 2, 5, 122, 30, }, + { 0, 1, 2, 5, 155, 26, }, + { 2, 1, 2, 5, 155, 30, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 22, }, + { 2, 1, 2, 8, 42, 28, }, + { 1, 1, 2, 8, 42, 28, }, + { 0, 1, 2, 8, 58, 22, }, + { 2, 1, 2, 8, 58, 28, }, + { 1, 1, 2, 8, 58, 28, }, + { 0, 1, 2, 8, 106, 24, }, + { 2, 1, 2, 8, 106, 28, }, + { 1, 1, 2, 8, 106, 28, }, + { 0, 1, 2, 8, 122, 24, }, + { 2, 1, 2, 8, 122, 28, }, + { 1, 1, 2, 8, 122, 28, }, + { 0, 1, 2, 8, 155, 24, }, + { 2, 1, 2, 8, 155, 28, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 20, }, + { 2, 1, 2, 9, 42, 26, }, + { 1, 1, 2, 9, 42, 26, }, + { 0, 1, 2, 9, 58, 20, }, + { 2, 1, 2, 9, 58, 26, }, + { 1, 1, 2, 9, 58, 26, }, + { 0, 1, 2, 9, 106, 22, }, + { 2, 1, 2, 9, 106, 26, }, + { 1, 1, 2, 9, 106, 26, }, + { 0, 1, 2, 9, 122, 22, }, + { 2, 1, 2, 9, 122, 26, }, + { 1, 1, 2, 9, 122, 26, }, + { 0, 1, 2, 9, 155, 22, }, + { 2, 1, 2, 9, 155, 26, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type7); + +static const struct rtw_txpwr_lmt_cfg_pair rtw8814a_txpwr_lmt_type8[] = { + { 0, 0, 0, 0, 1, 46, }, + { 2, 0, 0, 0, 1, 46, }, + { 1, 0, 0, 0, 1, 46, }, + { 0, 0, 0, 0, 2, 46, }, + { 2, 0, 0, 0, 2, 46, }, + { 1, 0, 0, 0, 2, 46, }, + { 0, 0, 0, 0, 3, 46, }, + { 2, 0, 0, 0, 3, 46, }, + { 1, 0, 0, 0, 3, 46, }, + { 0, 0, 0, 0, 4, 46, }, + { 2, 0, 0, 0, 4, 46, }, + { 1, 0, 0, 0, 4, 46, }, + { 0, 0, 0, 0, 5, 46, }, + { 2, 0, 0, 0, 5, 46, }, + { 1, 0, 0, 0, 5, 46, }, + { 0, 0, 0, 0, 6, 46, }, + { 2, 0, 0, 0, 6, 46, }, + { 1, 0, 0, 0, 6, 46, }, + { 0, 0, 0, 0, 7, 46, }, + { 2, 0, 0, 0, 7, 46, }, + { 1, 0, 0, 0, 7, 46, }, + { 0, 0, 0, 0, 8, 46, }, + { 2, 0, 0, 0, 8, 46, }, + { 1, 0, 0, 0, 8, 46, }, + { 0, 0, 0, 0, 9, 46, }, + { 2, 0, 0, 0, 9, 46, }, + { 1, 0, 0, 0, 9, 46, }, + { 0, 0, 0, 0, 10, 46, }, + { 2, 0, 0, 0, 10, 46, }, + { 1, 0, 0, 0, 10, 46, }, + { 0, 0, 0, 0, 11, 46, }, + { 2, 0, 0, 0, 11, 46, }, + { 1, 0, 0, 0, 11, 46, }, + { 0, 0, 0, 0, 12, 63, }, + { 2, 0, 0, 0, 12, 46, }, + { 1, 0, 0, 0, 12, 46, }, + { 0, 0, 0, 0, 13, 63, }, + { 2, 0, 0, 0, 13, 46, }, + { 1, 0, 0, 0, 13, 46, }, + { 0, 0, 0, 0, 14, 63, }, + { 2, 0, 0, 0, 14, 63, }, + { 1, 0, 0, 0, 14, 46, }, + { 0, 0, 0, 1, 1, 46, }, + { 2, 0, 0, 1, 1, 46, }, + { 1, 0, 0, 1, 1, 46, }, + { 0, 0, 0, 1, 2, 46, }, + { 2, 0, 0, 1, 2, 46, }, + { 1, 0, 0, 1, 2, 46, }, + { 0, 0, 0, 1, 3, 46, }, + { 2, 0, 0, 1, 3, 46, }, + { 1, 0, 0, 1, 3, 46, }, + { 0, 0, 0, 1, 4, 46, }, + { 2, 0, 0, 1, 4, 46, }, + { 1, 0, 0, 1, 4, 46, }, + { 0, 0, 0, 1, 5, 46, }, + { 2, 0, 0, 1, 5, 46, }, + { 1, 0, 0, 1, 5, 46, }, + { 0, 0, 0, 1, 6, 46, }, + { 2, 0, 0, 1, 6, 46, }, + { 1, 0, 0, 1, 6, 46, }, + { 0, 0, 0, 1, 7, 46, }, + { 2, 0, 0, 1, 7, 46, }, + { 1, 0, 0, 1, 7, 46, }, + { 0, 0, 0, 1, 8, 46, }, + { 2, 0, 0, 1, 8, 46, }, + { 1, 0, 0, 1, 8, 46, }, + { 0, 0, 0, 1, 9, 46, }, + { 2, 0, 0, 1, 9, 46, }, + { 1, 0, 0, 1, 9, 46, }, + { 0, 0, 0, 1, 10, 46, }, + { 2, 0, 0, 1, 10, 46, }, + { 1, 0, 0, 1, 10, 46, }, + { 0, 0, 0, 1, 11, 46, }, + { 2, 0, 0, 1, 11, 46, }, + { 1, 0, 0, 1, 11, 46, }, + { 0, 0, 0, 1, 12, 63, }, + { 2, 0, 0, 1, 12, 46, }, + { 1, 0, 0, 1, 12, 46, }, + { 0, 0, 0, 1, 13, 63, }, + { 2, 0, 0, 1, 13, 46, }, + { 1, 0, 0, 1, 13, 46, }, + { 0, 0, 0, 1, 14, 63, }, + { 2, 0, 0, 1, 14, 63, }, + { 1, 0, 0, 1, 14, 46, }, + { 0, 0, 0, 2, 1, 46, }, + { 2, 0, 0, 2, 1, 46, }, + { 1, 0, 0, 2, 1, 46, }, + { 0, 0, 0, 2, 2, 46, }, + { 2, 0, 0, 2, 2, 46, }, + { 1, 0, 0, 2, 2, 46, }, + { 0, 0, 0, 2, 3, 46, }, + { 2, 0, 0, 2, 3, 46, }, + { 1, 0, 0, 2, 3, 46, }, + { 0, 0, 0, 2, 4, 46, }, + { 2, 0, 0, 2, 4, 46, }, + { 1, 0, 0, 2, 4, 46, }, + { 0, 0, 0, 2, 5, 46, }, + { 2, 0, 0, 2, 5, 46, }, + { 1, 0, 0, 2, 5, 46, }, + { 0, 0, 0, 2, 6, 46, }, + { 2, 0, 0, 2, 6, 46, }, + { 1, 0, 0, 2, 6, 46, }, + { 0, 0, 0, 2, 7, 46, }, + { 2, 0, 0, 2, 7, 46, }, + { 1, 0, 0, 2, 7, 46, }, + { 0, 0, 0, 2, 8, 46, }, + { 2, 0, 0, 2, 8, 46, }, + { 1, 0, 0, 2, 8, 46, }, + { 0, 0, 0, 2, 9, 46, }, + { 2, 0, 0, 2, 9, 46, }, + { 1, 0, 0, 2, 9, 46, }, + { 0, 0, 0, 2, 10, 46, }, + { 2, 0, 0, 2, 10, 46, }, + { 1, 0, 0, 2, 10, 46, }, + { 0, 0, 0, 2, 11, 46, }, + { 2, 0, 0, 2, 11, 46, }, + { 1, 0, 0, 2, 11, 46, }, + { 0, 0, 0, 2, 12, 63, }, + { 2, 0, 0, 2, 12, 46, }, + { 1, 0, 0, 2, 12, 46, }, + { 0, 0, 0, 2, 13, 63, }, + { 2, 0, 0, 2, 13, 46, }, + { 1, 0, 0, 2, 13, 46, }, + { 0, 0, 0, 2, 14, 63, }, + { 2, 0, 0, 2, 14, 63, }, + { 1, 0, 0, 2, 14, 46, }, + { 0, 0, 0, 3, 1, 46, }, + { 2, 0, 0, 3, 1, 46, }, + { 1, 0, 0, 3, 1, 46, }, + { 0, 0, 0, 3, 2, 46, }, + { 2, 0, 0, 3, 2, 46, }, + { 1, 0, 0, 3, 2, 46, }, + { 0, 0, 0, 3, 3, 46, }, + { 2, 0, 0, 3, 3, 46, }, + { 1, 0, 0, 3, 3, 46, }, + { 0, 0, 0, 3, 4, 46, }, + { 2, 0, 0, 3, 4, 46, }, + { 1, 0, 0, 3, 4, 46, }, + { 0, 0, 0, 3, 5, 46, }, + { 2, 0, 0, 3, 5, 46, }, + { 1, 0, 0, 3, 5, 46, }, + { 0, 0, 0, 3, 6, 46, }, + { 2, 0, 0, 3, 6, 46, }, + { 1, 0, 0, 3, 6, 46, }, + { 0, 0, 0, 3, 7, 46, }, + { 2, 0, 0, 3, 7, 46, }, + { 1, 0, 0, 3, 7, 46, }, + { 0, 0, 0, 3, 8, 46, }, + { 2, 0, 0, 3, 8, 46, }, + { 1, 0, 0, 3, 8, 46, }, + { 0, 0, 0, 3, 9, 46, }, + { 2, 0, 0, 3, 9, 46, }, + { 1, 0, 0, 3, 9, 46, }, + { 0, 0, 0, 3, 10, 46, }, + { 2, 0, 0, 3, 10, 46, }, + { 1, 0, 0, 3, 10, 46, }, + { 0, 0, 0, 3, 11, 46, }, + { 2, 0, 0, 3, 11, 46, }, + { 1, 0, 0, 3, 11, 46, }, + { 0, 0, 0, 3, 12, 63, }, + { 2, 0, 0, 3, 12, 46, }, + { 1, 0, 0, 3, 12, 46, }, + { 0, 0, 0, 3, 13, 63, }, + { 2, 0, 0, 3, 13, 46, }, + { 1, 0, 0, 3, 13, 46, }, + { 0, 0, 0, 3, 14, 63, }, + { 2, 0, 0, 3, 14, 63, }, + { 1, 0, 0, 3, 14, 46, }, + { 0, 0, 0, 6, 1, 46, }, + { 2, 0, 0, 6, 1, 46, }, + { 1, 0, 0, 6, 1, 46, }, + { 0, 0, 0, 6, 2, 46, }, + { 2, 0, 0, 6, 2, 46, }, + { 1, 0, 0, 6, 2, 46, }, + { 0, 0, 0, 6, 3, 46, }, + { 2, 0, 0, 6, 3, 46, }, + { 1, 0, 0, 6, 3, 46, }, + { 0, 0, 0, 6, 4, 46, }, + { 2, 0, 0, 6, 4, 46, }, + { 1, 0, 0, 6, 4, 46, }, + { 0, 0, 0, 6, 5, 46, }, + { 2, 0, 0, 6, 5, 46, }, + { 1, 0, 0, 6, 5, 46, }, + { 0, 0, 0, 6, 6, 46, }, + { 2, 0, 0, 6, 6, 46, }, + { 1, 0, 0, 6, 6, 46, }, + { 0, 0, 0, 6, 7, 46, }, + { 2, 0, 0, 6, 7, 46, }, + { 1, 0, 0, 6, 7, 46, }, + { 0, 0, 0, 6, 8, 46, }, + { 2, 0, 0, 6, 8, 46, }, + { 1, 0, 0, 6, 8, 46, }, + { 0, 0, 0, 6, 9, 46, }, + { 2, 0, 0, 6, 9, 46, }, + { 1, 0, 0, 6, 9, 46, }, + { 0, 0, 0, 6, 10, 46, }, + { 2, 0, 0, 6, 10, 46, }, + { 1, 0, 0, 6, 10, 46, }, + { 0, 0, 0, 6, 11, 46, }, + { 2, 0, 0, 6, 11, 46, }, + { 1, 0, 0, 6, 11, 46, }, + { 0, 0, 0, 6, 12, 63, }, + { 2, 0, 0, 6, 12, 46, }, + { 1, 0, 0, 6, 12, 46, }, + { 0, 0, 0, 6, 13, 63, }, + { 2, 0, 0, 6, 13, 46, }, + { 1, 0, 0, 6, 13, 46, }, + { 0, 0, 0, 6, 14, 63, }, + { 2, 0, 0, 6, 14, 63, }, + { 1, 0, 0, 6, 14, 46, }, + { 0, 0, 0, 7, 1, 46, }, + { 2, 0, 0, 7, 1, 46, }, + { 1, 0, 0, 7, 1, 46, }, + { 0, 0, 0, 7, 2, 46, }, + { 2, 0, 0, 7, 2, 46, }, + { 1, 0, 0, 7, 2, 46, }, + { 0, 0, 0, 7, 3, 46, }, + { 2, 0, 0, 7, 3, 46, }, + { 1, 0, 0, 7, 3, 46, }, + { 0, 0, 0, 7, 4, 46, }, + { 2, 0, 0, 7, 4, 46, }, + { 1, 0, 0, 7, 4, 46, }, + { 0, 0, 0, 7, 5, 46, }, + { 2, 0, 0, 7, 5, 46, }, + { 1, 0, 0, 7, 5, 46, }, + { 0, 0, 0, 7, 6, 46, }, + { 2, 0, 0, 7, 6, 46, }, + { 1, 0, 0, 7, 6, 46, }, + { 0, 0, 0, 7, 7, 46, }, + { 2, 0, 0, 7, 7, 46, }, + { 1, 0, 0, 7, 7, 46, }, + { 0, 0, 0, 7, 8, 46, }, + { 2, 0, 0, 7, 8, 46, }, + { 1, 0, 0, 7, 8, 46, }, + { 0, 0, 0, 7, 9, 46, }, + { 2, 0, 0, 7, 9, 46, }, + { 1, 0, 0, 7, 9, 46, }, + { 0, 0, 0, 7, 10, 46, }, + { 2, 0, 0, 7, 10, 46, }, + { 1, 0, 0, 7, 10, 46, }, + { 0, 0, 0, 7, 11, 46, }, + { 2, 0, 0, 7, 11, 46, }, + { 1, 0, 0, 7, 11, 46, }, + { 0, 0, 0, 7, 12, 63, }, + { 2, 0, 0, 7, 12, 46, }, + { 1, 0, 0, 7, 12, 46, }, + { 0, 0, 0, 7, 13, 63, }, + { 2, 0, 0, 7, 13, 46, }, + { 1, 0, 0, 7, 13, 46, }, + { 0, 0, 0, 7, 14, 63, }, + { 2, 0, 0, 7, 14, 63, }, + { 1, 0, 0, 7, 14, 46, }, + { 0, 0, 1, 2, 1, 63, }, + { 2, 0, 1, 2, 1, 63, }, + { 1, 0, 1, 2, 1, 63, }, + { 0, 0, 1, 2, 2, 63, }, + { 2, 0, 1, 2, 2, 63, }, + { 1, 0, 1, 2, 2, 63, }, + { 0, 0, 1, 2, 3, 30, }, + { 2, 0, 1, 2, 3, 34, }, + { 1, 0, 1, 2, 3, 34, }, + { 0, 0, 1, 2, 4, 34, }, + { 2, 0, 1, 2, 4, 34, }, + { 1, 0, 1, 2, 4, 34, }, + { 0, 0, 1, 2, 5, 34, }, + { 2, 0, 1, 2, 5, 34, }, + { 1, 0, 1, 2, 5, 34, }, + { 0, 0, 1, 2, 6, 34, }, + { 2, 0, 1, 2, 6, 34, }, + { 1, 0, 1, 2, 6, 34, }, + { 0, 0, 1, 2, 7, 34, }, + { 2, 0, 1, 2, 7, 34, }, + { 1, 0, 1, 2, 7, 34, }, + { 0, 0, 1, 2, 8, 34, }, + { 2, 0, 1, 2, 8, 34, }, + { 1, 0, 1, 2, 8, 34, }, + { 0, 0, 1, 2, 9, 34, }, + { 2, 0, 1, 2, 9, 34, }, + { 1, 0, 1, 2, 9, 34, }, + { 0, 0, 1, 2, 10, 34, }, + { 2, 0, 1, 2, 10, 34, }, + { 1, 0, 1, 2, 10, 34, }, + { 0, 0, 1, 2, 11, 28, }, + { 2, 0, 1, 2, 11, 34, }, + { 1, 0, 1, 2, 11, 34, }, + { 0, 0, 1, 2, 12, 63, }, + { 2, 0, 1, 2, 12, 34, }, + { 1, 0, 1, 2, 12, 34, }, + { 0, 0, 1, 2, 13, 63, }, + { 2, 0, 1, 2, 13, 34, }, + { 1, 0, 1, 2, 13, 34, }, + { 0, 0, 1, 2, 14, 63, }, + { 2, 0, 1, 2, 14, 63, }, + { 1, 0, 1, 2, 14, 63, }, + { 0, 0, 1, 3, 1, 63, }, + { 2, 0, 1, 3, 1, 63, }, + { 1, 0, 1, 3, 1, 63, }, + { 0, 0, 1, 3, 2, 63, }, + { 2, 0, 1, 3, 2, 63, }, + { 1, 0, 1, 3, 2, 63, }, + { 0, 0, 1, 3, 3, 30, }, + { 2, 0, 1, 3, 3, 34, }, + { 1, 0, 1, 3, 3, 34, }, + { 0, 0, 1, 3, 4, 34, }, + { 2, 0, 1, 3, 4, 34, }, + { 1, 0, 1, 3, 4, 34, }, + { 0, 0, 1, 3, 5, 34, }, + { 2, 0, 1, 3, 5, 34, }, + { 1, 0, 1, 3, 5, 34, }, + { 0, 0, 1, 3, 6, 34, }, + { 2, 0, 1, 3, 6, 34, }, + { 1, 0, 1, 3, 6, 34, }, + { 0, 0, 1, 3, 7, 34, }, + { 2, 0, 1, 3, 7, 34, }, + { 1, 0, 1, 3, 7, 34, }, + { 0, 0, 1, 3, 8, 34, }, + { 2, 0, 1, 3, 8, 34, }, + { 1, 0, 1, 3, 8, 34, }, + { 0, 0, 1, 3, 9, 34, }, + { 2, 0, 1, 3, 9, 34, }, + { 1, 0, 1, 3, 9, 34, }, + { 0, 0, 1, 3, 10, 34, }, + { 2, 0, 1, 3, 10, 34, }, + { 1, 0, 1, 3, 10, 34, }, + { 0, 0, 1, 3, 11, 28, }, + { 2, 0, 1, 3, 11, 34, }, + { 1, 0, 1, 3, 11, 34, }, + { 0, 0, 1, 3, 12, 63, }, + { 2, 0, 1, 3, 12, 34, }, + { 1, 0, 1, 3, 12, 34, }, + { 0, 0, 1, 3, 13, 63, }, + { 2, 0, 1, 3, 13, 34, }, + { 1, 0, 1, 3, 13, 34, }, + { 0, 0, 1, 3, 14, 63, }, + { 2, 0, 1, 3, 14, 63, }, + { 1, 0, 1, 3, 14, 63, }, + { 0, 0, 1, 6, 1, 63, }, + { 2, 0, 1, 6, 1, 63, }, + { 1, 0, 1, 6, 1, 63, }, + { 0, 0, 1, 6, 2, 63, }, + { 2, 0, 1, 6, 2, 63, }, + { 1, 0, 1, 6, 2, 63, }, + { 0, 0, 1, 6, 3, 30, }, + { 2, 0, 1, 6, 3, 34, }, + { 1, 0, 1, 6, 3, 34, }, + { 0, 0, 1, 6, 4, 34, }, + { 2, 0, 1, 6, 4, 34, }, + { 1, 0, 1, 6, 4, 34, }, + { 0, 0, 1, 6, 5, 34, }, + { 2, 0, 1, 6, 5, 34, }, + { 1, 0, 1, 6, 5, 34, }, + { 0, 0, 1, 6, 6, 34, }, + { 2, 0, 1, 6, 6, 34, }, + { 1, 0, 1, 6, 6, 34, }, + { 0, 0, 1, 6, 7, 34, }, + { 2, 0, 1, 6, 7, 34, }, + { 1, 0, 1, 6, 7, 34, }, + { 0, 0, 1, 6, 8, 34, }, + { 2, 0, 1, 6, 8, 34, }, + { 1, 0, 1, 6, 8, 34, }, + { 0, 0, 1, 6, 9, 34, }, + { 2, 0, 1, 6, 9, 34, }, + { 1, 0, 1, 6, 9, 34, }, + { 0, 0, 1, 6, 10, 34, }, + { 2, 0, 1, 6, 10, 34, }, + { 1, 0, 1, 6, 10, 34, }, + { 0, 0, 1, 6, 11, 28, }, + { 2, 0, 1, 6, 11, 34, }, + { 1, 0, 1, 6, 11, 34, }, + { 0, 0, 1, 6, 12, 63, }, + { 2, 0, 1, 6, 12, 34, }, + { 1, 0, 1, 6, 12, 34, }, + { 0, 0, 1, 6, 13, 63, }, + { 2, 0, 1, 6, 13, 34, }, + { 1, 0, 1, 6, 13, 34, }, + { 0, 0, 1, 6, 14, 63, }, + { 2, 0, 1, 6, 14, 63, }, + { 1, 0, 1, 6, 14, 63, }, + { 0, 0, 1, 7, 1, 63, }, + { 2, 0, 1, 7, 1, 63, }, + { 1, 0, 1, 7, 1, 63, }, + { 0, 0, 1, 7, 2, 63, }, + { 2, 0, 1, 7, 2, 63, }, + { 1, 0, 1, 7, 2, 63, }, + { 0, 0, 1, 7, 3, 30, }, + { 2, 0, 1, 7, 3, 34, }, + { 1, 0, 1, 7, 3, 34, }, + { 0, 0, 1, 7, 4, 34, }, + { 2, 0, 1, 7, 4, 34, }, + { 1, 0, 1, 7, 4, 34, }, + { 0, 0, 1, 7, 5, 34, }, + { 2, 0, 1, 7, 5, 34, }, + { 1, 0, 1, 7, 5, 34, }, + { 0, 0, 1, 7, 6, 34, }, + { 2, 0, 1, 7, 6, 34, }, + { 1, 0, 1, 7, 6, 34, }, + { 0, 0, 1, 7, 7, 34, }, + { 2, 0, 1, 7, 7, 34, }, + { 1, 0, 1, 7, 7, 34, }, + { 0, 0, 1, 7, 8, 34, }, + { 2, 0, 1, 7, 8, 34, }, + { 1, 0, 1, 7, 8, 34, }, + { 0, 0, 1, 7, 9, 34, }, + { 2, 0, 1, 7, 9, 34, }, + { 1, 0, 1, 7, 9, 34, }, + { 0, 0, 1, 7, 10, 34, }, + { 2, 0, 1, 7, 10, 34, }, + { 1, 0, 1, 7, 10, 34, }, + { 0, 0, 1, 7, 11, 28, }, + { 2, 0, 1, 7, 11, 34, }, + { 1, 0, 1, 7, 11, 34, }, + { 0, 0, 1, 7, 12, 63, }, + { 2, 0, 1, 7, 12, 34, }, + { 1, 0, 1, 7, 12, 34, }, + { 0, 0, 1, 7, 13, 63, }, + { 2, 0, 1, 7, 13, 34, }, + { 1, 0, 1, 7, 13, 34, }, + { 0, 0, 1, 7, 14, 63, }, + { 2, 0, 1, 7, 14, 63, }, + { 1, 0, 1, 7, 14, 63, }, + { 0, 1, 0, 1, 36, 46, }, + { 2, 1, 0, 1, 36, 46, }, + { 1, 1, 0, 1, 36, 46, }, + { 0, 1, 0, 1, 40, 46, }, + { 2, 1, 0, 1, 40, 46, }, + { 1, 1, 0, 1, 40, 46, }, + { 0, 1, 0, 1, 44, 46, }, + { 2, 1, 0, 1, 44, 46, }, + { 1, 1, 0, 1, 44, 46, }, + { 0, 1, 0, 1, 48, 46, }, + { 2, 1, 0, 1, 48, 46, }, + { 1, 1, 0, 1, 48, 46, }, + { 0, 1, 0, 1, 52, 46, }, + { 2, 1, 0, 1, 52, 46, }, + { 1, 1, 0, 1, 52, 46, }, + { 0, 1, 0, 1, 56, 46, }, + { 2, 1, 0, 1, 56, 46, }, + { 1, 1, 0, 1, 56, 46, }, + { 0, 1, 0, 1, 60, 46, }, + { 2, 1, 0, 1, 60, 46, }, + { 1, 1, 0, 1, 60, 46, }, + { 0, 1, 0, 1, 64, 46, }, + { 2, 1, 0, 1, 64, 46, }, + { 1, 1, 0, 1, 64, 46, }, + { 0, 1, 0, 1, 100, 46, }, + { 2, 1, 0, 1, 100, 46, }, + { 1, 1, 0, 1, 100, 46, }, + { 0, 1, 0, 1, 104, 46, }, + { 2, 1, 0, 1, 104, 46, }, + { 1, 1, 0, 1, 104, 46, }, + { 0, 1, 0, 1, 108, 46, }, + { 2, 1, 0, 1, 108, 46, }, + { 1, 1, 0, 1, 108, 46, }, + { 0, 1, 0, 1, 112, 46, }, + { 2, 1, 0, 1, 112, 46, }, + { 1, 1, 0, 1, 112, 46, }, + { 0, 1, 0, 1, 116, 46, }, + { 2, 1, 0, 1, 116, 46, }, + { 1, 1, 0, 1, 116, 46, }, + { 0, 1, 0, 1, 120, 46, }, + { 2, 1, 0, 1, 120, 46, }, + { 1, 1, 0, 1, 120, 46, }, + { 0, 1, 0, 1, 124, 46, }, + { 2, 1, 0, 1, 124, 46, }, + { 1, 1, 0, 1, 124, 46, }, + { 0, 1, 0, 1, 128, 46, }, + { 2, 1, 0, 1, 128, 46, }, + { 1, 1, 0, 1, 128, 46, }, + { 0, 1, 0, 1, 132, 46, }, + { 2, 1, 0, 1, 132, 46, }, + { 1, 1, 0, 1, 132, 46, }, + { 0, 1, 0, 1, 136, 46, }, + { 2, 1, 0, 1, 136, 46, }, + { 1, 1, 0, 1, 136, 46, }, + { 0, 1, 0, 1, 140, 46, }, + { 2, 1, 0, 1, 140, 46, }, + { 1, 1, 0, 1, 140, 46, }, + { 0, 1, 0, 1, 149, 46, }, + { 2, 1, 0, 1, 149, 46, }, + { 1, 1, 0, 1, 149, 63, }, + { 0, 1, 0, 1, 153, 46, }, + { 2, 1, 0, 1, 153, 46, }, + { 1, 1, 0, 1, 153, 63, }, + { 0, 1, 0, 1, 157, 46, }, + { 2, 1, 0, 1, 157, 46, }, + { 1, 1, 0, 1, 157, 63, }, + { 0, 1, 0, 1, 161, 46, }, + { 2, 1, 0, 1, 161, 46, }, + { 1, 1, 0, 1, 161, 63, }, + { 0, 1, 0, 1, 165, 46, }, + { 2, 1, 0, 1, 165, 46, }, + { 1, 1, 0, 1, 165, 63, }, + { 0, 1, 0, 2, 36, 46, }, + { 2, 1, 0, 2, 36, 46, }, + { 1, 1, 0, 2, 36, 46, }, + { 0, 1, 0, 2, 40, 46, }, + { 2, 1, 0, 2, 40, 46, }, + { 1, 1, 0, 2, 40, 46, }, + { 0, 1, 0, 2, 44, 46, }, + { 2, 1, 0, 2, 44, 46, }, + { 1, 1, 0, 2, 44, 46, }, + { 0, 1, 0, 2, 48, 46, }, + { 2, 1, 0, 2, 48, 46, }, + { 1, 1, 0, 2, 48, 46, }, + { 0, 1, 0, 2, 52, 46, }, + { 2, 1, 0, 2, 52, 46, }, + { 1, 1, 0, 2, 52, 46, }, + { 0, 1, 0, 2, 56, 46, }, + { 2, 1, 0, 2, 56, 46, }, + { 1, 1, 0, 2, 56, 46, }, + { 0, 1, 0, 2, 60, 46, }, + { 2, 1, 0, 2, 60, 46, }, + { 1, 1, 0, 2, 60, 46, }, + { 0, 1, 0, 2, 64, 46, }, + { 2, 1, 0, 2, 64, 46, }, + { 1, 1, 0, 2, 64, 46, }, + { 0, 1, 0, 2, 100, 46, }, + { 2, 1, 0, 2, 100, 46, }, + { 1, 1, 0, 2, 100, 46, }, + { 0, 1, 0, 2, 104, 46, }, + { 2, 1, 0, 2, 104, 46, }, + { 1, 1, 0, 2, 104, 46, }, + { 0, 1, 0, 2, 108, 46, }, + { 2, 1, 0, 2, 108, 46, }, + { 1, 1, 0, 2, 108, 46, }, + { 0, 1, 0, 2, 112, 46, }, + { 2, 1, 0, 2, 112, 46, }, + { 1, 1, 0, 2, 112, 46, }, + { 0, 1, 0, 2, 116, 46, }, + { 2, 1, 0, 2, 116, 46, }, + { 1, 1, 0, 2, 116, 46, }, + { 0, 1, 0, 2, 120, 46, }, + { 2, 1, 0, 2, 120, 46, }, + { 1, 1, 0, 2, 120, 46, }, + { 0, 1, 0, 2, 124, 46, }, + { 2, 1, 0, 2, 124, 46, }, + { 1, 1, 0, 2, 124, 46, }, + { 0, 1, 0, 2, 128, 46, }, + { 2, 1, 0, 2, 128, 46, }, + { 1, 1, 0, 2, 128, 46, }, + { 0, 1, 0, 2, 132, 46, }, + { 2, 1, 0, 2, 132, 46, }, + { 1, 1, 0, 2, 132, 46, }, + { 0, 1, 0, 2, 136, 46, }, + { 2, 1, 0, 2, 136, 46, }, + { 1, 1, 0, 2, 136, 46, }, + { 0, 1, 0, 2, 140, 46, }, + { 2, 1, 0, 2, 140, 46, }, + { 1, 1, 0, 2, 140, 46, }, + { 0, 1, 0, 2, 149, 46, }, + { 2, 1, 0, 2, 149, 46, }, + { 1, 1, 0, 2, 149, 63, }, + { 0, 1, 0, 2, 153, 46, }, + { 2, 1, 0, 2, 153, 46, }, + { 1, 1, 0, 2, 153, 63, }, + { 0, 1, 0, 2, 157, 46, }, + { 2, 1, 0, 2, 157, 46, }, + { 1, 1, 0, 2, 157, 63, }, + { 0, 1, 0, 2, 161, 46, }, + { 2, 1, 0, 2, 161, 46, }, + { 1, 1, 0, 2, 161, 63, }, + { 0, 1, 0, 2, 165, 46, }, + { 2, 1, 0, 2, 165, 46, }, + { 1, 1, 0, 2, 165, 63, }, + { 0, 1, 0, 3, 36, 46, }, + { 2, 1, 0, 3, 36, 46, }, + { 1, 1, 0, 3, 36, 46, }, + { 0, 1, 0, 3, 40, 46, }, + { 2, 1, 0, 3, 40, 46, }, + { 1, 1, 0, 3, 40, 46, }, + { 0, 1, 0, 3, 44, 46, }, + { 2, 1, 0, 3, 44, 46, }, + { 1, 1, 0, 3, 44, 46, }, + { 0, 1, 0, 3, 48, 46, }, + { 2, 1, 0, 3, 48, 46, }, + { 1, 1, 0, 3, 48, 46, }, + { 0, 1, 0, 3, 52, 46, }, + { 2, 1, 0, 3, 52, 46, }, + { 1, 1, 0, 3, 52, 46, }, + { 0, 1, 0, 3, 56, 46, }, + { 2, 1, 0, 3, 56, 46, }, + { 1, 1, 0, 3, 56, 46, }, + { 0, 1, 0, 3, 60, 46, }, + { 2, 1, 0, 3, 60, 46, }, + { 1, 1, 0, 3, 60, 46, }, + { 0, 1, 0, 3, 64, 46, }, + { 2, 1, 0, 3, 64, 46, }, + { 1, 1, 0, 3, 64, 46, }, + { 0, 1, 0, 3, 100, 46, }, + { 2, 1, 0, 3, 100, 46, }, + { 1, 1, 0, 3, 100, 46, }, + { 0, 1, 0, 3, 104, 46, }, + { 2, 1, 0, 3, 104, 46, }, + { 1, 1, 0, 3, 104, 46, }, + { 0, 1, 0, 3, 108, 46, }, + { 2, 1, 0, 3, 108, 46, }, + { 1, 1, 0, 3, 108, 46, }, + { 0, 1, 0, 3, 112, 46, }, + { 2, 1, 0, 3, 112, 46, }, + { 1, 1, 0, 3, 112, 46, }, + { 0, 1, 0, 3, 116, 46, }, + { 2, 1, 0, 3, 116, 46, }, + { 1, 1, 0, 3, 116, 46, }, + { 0, 1, 0, 3, 120, 46, }, + { 2, 1, 0, 3, 120, 46, }, + { 1, 1, 0, 3, 120, 46, }, + { 0, 1, 0, 3, 124, 46, }, + { 2, 1, 0, 3, 124, 46, }, + { 1, 1, 0, 3, 124, 46, }, + { 0, 1, 0, 3, 128, 46, }, + { 2, 1, 0, 3, 128, 46, }, + { 1, 1, 0, 3, 128, 46, }, + { 0, 1, 0, 3, 132, 46, }, + { 2, 1, 0, 3, 132, 46, }, + { 1, 1, 0, 3, 132, 46, }, + { 0, 1, 0, 3, 136, 46, }, + { 2, 1, 0, 3, 136, 46, }, + { 1, 1, 0, 3, 136, 46, }, + { 0, 1, 0, 3, 140, 46, }, + { 2, 1, 0, 3, 140, 46, }, + { 1, 1, 0, 3, 140, 46, }, + { 0, 1, 0, 3, 149, 46, }, + { 2, 1, 0, 3, 149, 46, }, + { 1, 1, 0, 3, 149, 63, }, + { 0, 1, 0, 3, 153, 46, }, + { 2, 1, 0, 3, 153, 46, }, + { 1, 1, 0, 3, 153, 63, }, + { 0, 1, 0, 3, 157, 46, }, + { 2, 1, 0, 3, 157, 46, }, + { 1, 1, 0, 3, 157, 63, }, + { 0, 1, 0, 3, 161, 46, }, + { 2, 1, 0, 3, 161, 46, }, + { 1, 1, 0, 3, 161, 63, }, + { 0, 1, 0, 3, 165, 46, }, + { 2, 1, 0, 3, 165, 46, }, + { 1, 1, 0, 3, 165, 63, }, + { 0, 1, 0, 6, 36, 46, }, + { 2, 1, 0, 6, 36, 46, }, + { 1, 1, 0, 6, 36, 46, }, + { 0, 1, 0, 6, 40, 46, }, + { 2, 1, 0, 6, 40, 46, }, + { 1, 1, 0, 6, 40, 46, }, + { 0, 1, 0, 6, 44, 46, }, + { 2, 1, 0, 6, 44, 46, }, + { 1, 1, 0, 6, 44, 46, }, + { 0, 1, 0, 6, 48, 46, }, + { 2, 1, 0, 6, 48, 46, }, + { 1, 1, 0, 6, 48, 46, }, + { 0, 1, 0, 6, 52, 46, }, + { 2, 1, 0, 6, 52, 46, }, + { 1, 1, 0, 6, 52, 46, }, + { 0, 1, 0, 6, 56, 46, }, + { 2, 1, 0, 6, 56, 46, }, + { 1, 1, 0, 6, 56, 46, }, + { 0, 1, 0, 6, 60, 46, }, + { 2, 1, 0, 6, 60, 46, }, + { 1, 1, 0, 6, 60, 46, }, + { 0, 1, 0, 6, 64, 46, }, + { 2, 1, 0, 6, 64, 46, }, + { 1, 1, 0, 6, 64, 46, }, + { 0, 1, 0, 6, 100, 46, }, + { 2, 1, 0, 6, 100, 46, }, + { 1, 1, 0, 6, 100, 46, }, + { 0, 1, 0, 6, 104, 46, }, + { 2, 1, 0, 6, 104, 46, }, + { 1, 1, 0, 6, 104, 46, }, + { 0, 1, 0, 6, 108, 46, }, + { 2, 1, 0, 6, 108, 46, }, + { 1, 1, 0, 6, 108, 46, }, + { 0, 1, 0, 6, 112, 46, }, + { 2, 1, 0, 6, 112, 46, }, + { 1, 1, 0, 6, 112, 46, }, + { 0, 1, 0, 6, 116, 46, }, + { 2, 1, 0, 6, 116, 46, }, + { 1, 1, 0, 6, 116, 46, }, + { 0, 1, 0, 6, 120, 46, }, + { 2, 1, 0, 6, 120, 46, }, + { 1, 1, 0, 6, 120, 46, }, + { 0, 1, 0, 6, 124, 46, }, + { 2, 1, 0, 6, 124, 46, }, + { 1, 1, 0, 6, 124, 46, }, + { 0, 1, 0, 6, 128, 46, }, + { 2, 1, 0, 6, 128, 46, }, + { 1, 1, 0, 6, 128, 46, }, + { 0, 1, 0, 6, 132, 46, }, + { 2, 1, 0, 6, 132, 46, }, + { 1, 1, 0, 6, 132, 46, }, + { 0, 1, 0, 6, 136, 46, }, + { 2, 1, 0, 6, 136, 46, }, + { 1, 1, 0, 6, 136, 46, }, + { 0, 1, 0, 6, 140, 46, }, + { 2, 1, 0, 6, 140, 46, }, + { 1, 1, 0, 6, 140, 46, }, + { 0, 1, 0, 6, 149, 46, }, + { 2, 1, 0, 6, 149, 46, }, + { 1, 1, 0, 6, 149, 63, }, + { 0, 1, 0, 6, 153, 46, }, + { 2, 1, 0, 6, 153, 46, }, + { 1, 1, 0, 6, 153, 63, }, + { 0, 1, 0, 6, 157, 46, }, + { 2, 1, 0, 6, 157, 46, }, + { 1, 1, 0, 6, 157, 63, }, + { 0, 1, 0, 6, 161, 46, }, + { 2, 1, 0, 6, 161, 46, }, + { 1, 1, 0, 6, 161, 63, }, + { 0, 1, 0, 6, 165, 46, }, + { 2, 1, 0, 6, 165, 46, }, + { 1, 1, 0, 6, 165, 63, }, + { 0, 1, 0, 7, 36, 46, }, + { 2, 1, 0, 7, 36, 46, }, + { 1, 1, 0, 7, 36, 46, }, + { 0, 1, 0, 7, 40, 46, }, + { 2, 1, 0, 7, 40, 46, }, + { 1, 1, 0, 7, 40, 46, }, + { 0, 1, 0, 7, 44, 46, }, + { 2, 1, 0, 7, 44, 46, }, + { 1, 1, 0, 7, 44, 46, }, + { 0, 1, 0, 7, 48, 46, }, + { 2, 1, 0, 7, 48, 46, }, + { 1, 1, 0, 7, 48, 46, }, + { 0, 1, 0, 7, 52, 46, }, + { 2, 1, 0, 7, 52, 46, }, + { 1, 1, 0, 7, 52, 46, }, + { 0, 1, 0, 7, 56, 46, }, + { 2, 1, 0, 7, 56, 46, }, + { 1, 1, 0, 7, 56, 46, }, + { 0, 1, 0, 7, 60, 46, }, + { 2, 1, 0, 7, 60, 46, }, + { 1, 1, 0, 7, 60, 46, }, + { 0, 1, 0, 7, 64, 46, }, + { 2, 1, 0, 7, 64, 46, }, + { 1, 1, 0, 7, 64, 46, }, + { 0, 1, 0, 7, 100, 46, }, + { 2, 1, 0, 7, 100, 46, }, + { 1, 1, 0, 7, 100, 46, }, + { 0, 1, 0, 7, 104, 46, }, + { 2, 1, 0, 7, 104, 46, }, + { 1, 1, 0, 7, 104, 46, }, + { 0, 1, 0, 7, 108, 46, }, + { 2, 1, 0, 7, 108, 46, }, + { 1, 1, 0, 7, 108, 46, }, + { 0, 1, 0, 7, 112, 46, }, + { 2, 1, 0, 7, 112, 46, }, + { 1, 1, 0, 7, 112, 46, }, + { 0, 1, 0, 7, 116, 46, }, + { 2, 1, 0, 7, 116, 46, }, + { 1, 1, 0, 7, 116, 46, }, + { 0, 1, 0, 7, 120, 46, }, + { 2, 1, 0, 7, 120, 46, }, + { 1, 1, 0, 7, 120, 46, }, + { 0, 1, 0, 7, 124, 46, }, + { 2, 1, 0, 7, 124, 46, }, + { 1, 1, 0, 7, 124, 46, }, + { 0, 1, 0, 7, 128, 46, }, + { 2, 1, 0, 7, 128, 46, }, + { 1, 1, 0, 7, 128, 46, }, + { 0, 1, 0, 7, 132, 46, }, + { 2, 1, 0, 7, 132, 46, }, + { 1, 1, 0, 7, 132, 46, }, + { 0, 1, 0, 7, 136, 46, }, + { 2, 1, 0, 7, 136, 46, }, + { 1, 1, 0, 7, 136, 46, }, + { 0, 1, 0, 7, 140, 46, }, + { 2, 1, 0, 7, 140, 46, }, + { 1, 1, 0, 7, 140, 46, }, + { 0, 1, 0, 7, 149, 46, }, + { 2, 1, 0, 7, 149, 46, }, + { 1, 1, 0, 7, 149, 63, }, + { 0, 1, 0, 7, 153, 46, }, + { 2, 1, 0, 7, 153, 46, }, + { 1, 1, 0, 7, 153, 63, }, + { 0, 1, 0, 7, 157, 46, }, + { 2, 1, 0, 7, 157, 46, }, + { 1, 1, 0, 7, 157, 63, }, + { 0, 1, 0, 7, 161, 46, }, + { 2, 1, 0, 7, 161, 46, }, + { 1, 1, 0, 7, 161, 63, }, + { 0, 1, 0, 7, 165, 46, }, + { 2, 1, 0, 7, 165, 46, }, + { 1, 1, 0, 7, 165, 63, }, + { 0, 1, 1, 2, 38, 46, }, + { 2, 1, 1, 2, 38, 46, }, + { 1, 1, 1, 2, 38, 46, }, + { 0, 1, 1, 2, 46, 46, }, + { 2, 1, 1, 2, 46, 46, }, + { 1, 1, 1, 2, 46, 46, }, + { 0, 1, 1, 2, 54, 46, }, + { 2, 1, 1, 2, 54, 46, }, + { 1, 1, 1, 2, 54, 46, }, + { 0, 1, 1, 2, 62, 46, }, + { 2, 1, 1, 2, 62, 46, }, + { 1, 1, 1, 2, 62, 46, }, + { 0, 1, 1, 2, 102, 46, }, + { 2, 1, 1, 2, 102, 46, }, + { 1, 1, 1, 2, 102, 46, }, + { 0, 1, 1, 2, 110, 46, }, + { 2, 1, 1, 2, 110, 46, }, + { 1, 1, 1, 2, 110, 46, }, + { 0, 1, 1, 2, 118, 46, }, + { 2, 1, 1, 2, 118, 46, }, + { 1, 1, 1, 2, 118, 46, }, + { 0, 1, 1, 2, 126, 46, }, + { 2, 1, 1, 2, 126, 46, }, + { 1, 1, 1, 2, 126, 46, }, + { 0, 1, 1, 2, 134, 46, }, + { 2, 1, 1, 2, 134, 46, }, + { 1, 1, 1, 2, 134, 46, }, + { 0, 1, 1, 2, 151, 46, }, + { 2, 1, 1, 2, 151, 46, }, + { 1, 1, 1, 2, 151, 63, }, + { 0, 1, 1, 2, 159, 46, }, + { 2, 1, 1, 2, 159, 46, }, + { 1, 1, 1, 2, 159, 63, }, + { 0, 1, 1, 3, 38, 46, }, + { 2, 1, 1, 3, 38, 46, }, + { 1, 1, 1, 3, 38, 46, }, + { 0, 1, 1, 3, 46, 46, }, + { 2, 1, 1, 3, 46, 46, }, + { 1, 1, 1, 3, 46, 46, }, + { 0, 1, 1, 3, 54, 46, }, + { 2, 1, 1, 3, 54, 46, }, + { 1, 1, 1, 3, 54, 46, }, + { 0, 1, 1, 3, 62, 46, }, + { 2, 1, 1, 3, 62, 46, }, + { 1, 1, 1, 3, 62, 46, }, + { 0, 1, 1, 3, 102, 46, }, + { 2, 1, 1, 3, 102, 46, }, + { 1, 1, 1, 3, 102, 46, }, + { 0, 1, 1, 3, 110, 46, }, + { 2, 1, 1, 3, 110, 46, }, + { 1, 1, 1, 3, 110, 46, }, + { 0, 1, 1, 3, 118, 46, }, + { 2, 1, 1, 3, 118, 46, }, + { 1, 1, 1, 3, 118, 46, }, + { 0, 1, 1, 3, 126, 46, }, + { 2, 1, 1, 3, 126, 46, }, + { 1, 1, 1, 3, 126, 46, }, + { 0, 1, 1, 3, 134, 46, }, + { 2, 1, 1, 3, 134, 46, }, + { 1, 1, 1, 3, 134, 46, }, + { 0, 1, 1, 3, 151, 46, }, + { 2, 1, 1, 3, 151, 46, }, + { 1, 1, 1, 3, 151, 63, }, + { 0, 1, 1, 3, 159, 46, }, + { 2, 1, 1, 3, 159, 46, }, + { 1, 1, 1, 3, 159, 63, }, + { 0, 1, 1, 6, 38, 46, }, + { 2, 1, 1, 6, 38, 46, }, + { 1, 1, 1, 6, 38, 46, }, + { 0, 1, 1, 6, 46, 46, }, + { 2, 1, 1, 6, 46, 46, }, + { 1, 1, 1, 6, 46, 46, }, + { 0, 1, 1, 6, 54, 46, }, + { 2, 1, 1, 6, 54, 46, }, + { 1, 1, 1, 6, 54, 46, }, + { 0, 1, 1, 6, 62, 46, }, + { 2, 1, 1, 6, 62, 46, }, + { 1, 1, 1, 6, 62, 46, }, + { 0, 1, 1, 6, 102, 46, }, + { 2, 1, 1, 6, 102, 46, }, + { 1, 1, 1, 6, 102, 46, }, + { 0, 1, 1, 6, 110, 46, }, + { 2, 1, 1, 6, 110, 46, }, + { 1, 1, 1, 6, 110, 46, }, + { 0, 1, 1, 6, 118, 46, }, + { 2, 1, 1, 6, 118, 46, }, + { 1, 1, 1, 6, 118, 46, }, + { 0, 1, 1, 6, 126, 46, }, + { 2, 1, 1, 6, 126, 46, }, + { 1, 1, 1, 6, 126, 46, }, + { 0, 1, 1, 6, 134, 46, }, + { 2, 1, 1, 6, 134, 46, }, + { 1, 1, 1, 6, 134, 46, }, + { 0, 1, 1, 6, 151, 46, }, + { 2, 1, 1, 6, 151, 46, }, + { 1, 1, 1, 6, 151, 63, }, + { 0, 1, 1, 6, 159, 46, }, + { 2, 1, 1, 6, 159, 46, }, + { 1, 1, 1, 6, 159, 63, }, + { 0, 1, 1, 7, 38, 46, }, + { 2, 1, 1, 7, 38, 46, }, + { 1, 1, 1, 7, 38, 46, }, + { 0, 1, 1, 7, 46, 46, }, + { 2, 1, 1, 7, 46, 46, }, + { 1, 1, 1, 7, 46, 46, }, + { 0, 1, 1, 7, 54, 46, }, + { 2, 1, 1, 7, 54, 46, }, + { 1, 1, 1, 7, 54, 46, }, + { 0, 1, 1, 7, 62, 46, }, + { 2, 1, 1, 7, 62, 46, }, + { 1, 1, 1, 7, 62, 46, }, + { 0, 1, 1, 7, 102, 46, }, + { 2, 1, 1, 7, 102, 46, }, + { 1, 1, 1, 7, 102, 46, }, + { 0, 1, 1, 7, 110, 46, }, + { 2, 1, 1, 7, 110, 46, }, + { 1, 1, 1, 7, 110, 46, }, + { 0, 1, 1, 7, 118, 46, }, + { 2, 1, 1, 7, 118, 46, }, + { 1, 1, 1, 7, 118, 46, }, + { 0, 1, 1, 7, 126, 46, }, + { 2, 1, 1, 7, 126, 46, }, + { 1, 1, 1, 7, 126, 46, }, + { 0, 1, 1, 7, 134, 46, }, + { 2, 1, 1, 7, 134, 46, }, + { 1, 1, 1, 7, 134, 46, }, + { 0, 1, 1, 7, 151, 46, }, + { 2, 1, 1, 7, 151, 46, }, + { 1, 1, 1, 7, 151, 63, }, + { 0, 1, 1, 7, 159, 46, }, + { 2, 1, 1, 7, 159, 46, }, + { 1, 1, 1, 7, 159, 63, }, + { 0, 1, 2, 4, 42, 46, }, + { 2, 1, 2, 4, 42, 46, }, + { 1, 1, 2, 4, 42, 46, }, + { 0, 1, 2, 4, 58, 46, }, + { 2, 1, 2, 4, 58, 46, }, + { 1, 1, 2, 4, 58, 46, }, + { 0, 1, 2, 4, 106, 46, }, + { 2, 1, 2, 4, 106, 46, }, + { 1, 1, 2, 4, 106, 46, }, + { 0, 1, 2, 4, 122, 46, }, + { 2, 1, 2, 4, 122, 46, }, + { 1, 1, 2, 4, 122, 46, }, + { 0, 1, 2, 4, 155, 46, }, + { 2, 1, 2, 4, 155, 46, }, + { 1, 1, 2, 4, 155, 63, }, + { 0, 1, 2, 5, 42, 46, }, + { 2, 1, 2, 5, 42, 46, }, + { 1, 1, 2, 5, 42, 46, }, + { 0, 1, 2, 5, 58, 46, }, + { 2, 1, 2, 5, 58, 46, }, + { 1, 1, 2, 5, 58, 46, }, + { 0, 1, 2, 5, 106, 46, }, + { 2, 1, 2, 5, 106, 46, }, + { 1, 1, 2, 5, 106, 46, }, + { 0, 1, 2, 5, 122, 46, }, + { 2, 1, 2, 5, 122, 46, }, + { 1, 1, 2, 5, 122, 46, }, + { 0, 1, 2, 5, 155, 46, }, + { 2, 1, 2, 5, 155, 46, }, + { 1, 1, 2, 5, 155, 63, }, + { 0, 1, 2, 8, 42, 46, }, + { 2, 1, 2, 8, 42, 46, }, + { 1, 1, 2, 8, 42, 46, }, + { 0, 1, 2, 8, 58, 46, }, + { 2, 1, 2, 8, 58, 46, }, + { 1, 1, 2, 8, 58, 46, }, + { 0, 1, 2, 8, 106, 46, }, + { 2, 1, 2, 8, 106, 46, }, + { 1, 1, 2, 8, 106, 46, }, + { 0, 1, 2, 8, 122, 46, }, + { 2, 1, 2, 8, 122, 46, }, + { 1, 1, 2, 8, 122, 46, }, + { 0, 1, 2, 8, 155, 46, }, + { 2, 1, 2, 8, 155, 46, }, + { 1, 1, 2, 8, 155, 63, }, + { 0, 1, 2, 9, 42, 46, }, + { 2, 1, 2, 9, 42, 46, }, + { 1, 1, 2, 9, 42, 46, }, + { 0, 1, 2, 9, 58, 46, }, + { 2, 1, 2, 9, 58, 46, }, + { 1, 1, 2, 9, 58, 46, }, + { 0, 1, 2, 9, 106, 46, }, + { 2, 1, 2, 9, 106, 46, }, + { 1, 1, 2, 9, 106, 46, }, + { 0, 1, 2, 9, 122, 46, }, + { 2, 1, 2, 9, 122, 46, }, + { 1, 1, 2, 9, 122, 46, }, + { 0, 1, 2, 9, 155, 46, }, + { 2, 1, 2, 9, 155, 46, }, + { 1, 1, 2, 9, 155, 63, }, +}; + +RTW_DECL_TABLE_TXPWR_LMT(rtw8814a_txpwr_lmt_type8); + +static const u8 +rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11, + 11, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19}, + {0, 1, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, 10, + 11, 12, 12, 13, 13, 14, 15, 15, 16, 17, 17, 18}, + {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19}, +}; + +static const u8 +rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25, 25}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25}, +}; + +static const u8 +rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, + 11, 12, 13, 14, 15, 15, 15, 15, 16, 16, 17, 18}, + {0, 1, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 11, + 12, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20}, + {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 11, + 12, 13, 13, 14, 14, 15, 16, 17, 18, 18, 19, 20, 20}, +}; + +static const u8 +rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 18, 19, 20, 21, 21, 22, 23, 24, 25, 25}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 17, 18, 19, 20, 21, 22, 23, 24, 24, 25, 25, 25}, + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 18, 19, 20, 21, 22, 23, 23, 24, 25, 25}, +}; + +static const u8 +rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 2, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 13, 13, 14, 14, 15, 15, 16, 17, 17}, + {0, 1, 1, 2, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 11, + 12, 12, 13, 14, 15, 15, 16, 17, 17, 18, 19, 19, 20}, + {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 11, + 12, 13, 13, 14, 14, 15, 16, 17, 18, 18, 19, 20, 20}, +}; + +static const u8 +rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, + 15, 15, 16, 17, 18, 18, 19, 20, 21, 22, 23, 23, 24}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 16, 17, 18, 19, 20, 20, 21, 22, 23, 24, 25, 25}, +}; + +static const u8 +rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 11, 11, 11, 11, 12, 12, 13, 13, 14}, + {0, 1, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 9, 9, 10, 11, 11, + 12, 13, 14, 14, 15, 16, 16, 17, 18, 19, 19, 20, 21}, + {0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 12, 12, 13, 14, 14, 15, 16, 16, 17, 17, 18, 19}, +}; + +static const u8 +rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 12, 13, + 14, 15, 16, 16, 17, 18, 19, 20, 21, 21, 22, 23, 24}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 23, 24, 25, 25, 25, 25}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 17, 18, 19, 20, 21, 22, 23, 24, 25, 25, 25, 25}, +}; + +static const u8 rtw8814a_pwrtrk_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, + 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 7, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 12, 13 +}; + +static const u8 rtw8814a_pwrtrk_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13 +}; + +static const u8 rtw8814a_pwrtrk_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, + 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 6, + 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11, 12 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 4, 5, 5, 6, 6, 7, 7, + 7, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12, 13 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, + 6, 6, 7, 7, 8, 8, 8, 9, 9, 10, 10, 10, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 6, 6, 6, 7, + 7, 8, 8, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 3, 4, 4, 5, 5, 5, 6, 6, 7, + 7, 8, 8, 8, 9, 9, 10, 10, 11, 11, 11, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 rtw8814a_pwrtrk_type0_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type0_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type0_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type0_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type0_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type0_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type0_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type0_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type0_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type0_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type0_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type0_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type0_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type0_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type0_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type0_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type0_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type0_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type0_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type0_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 12, 12, + 12, 13, 14, 15, 15, 16, 16, 16, 16, 16, 16, 16, 16}, + {0, 1, 2, 3, 4, 5, 5, 6, 6, 7, 8, 9, 9, 10, 10, 10, 10, + 11, 11, 12, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 11, 12, + 12, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 22, 22, 22, 22, 22}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 22, 22, 22, 22, 22}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, + 17, 18, 19, 20, 21, 22, 23, 23, 23, 23, 23, 23, 23}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 12, + 12, 13, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 12, + 13, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21}, + {0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 20, 20, 21, 21, 21, 21, 21}, + {0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 11, 12, 13, + 13, 13, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 11, 11, 12, + 13, 13, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21, 21}, + {0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 10, 11, 12, 13, + 14, 15, 16, 17, 18, 19, 20, 20, 20, 20, 20, 20, 20}, + {0, 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 21, 21, 21, 21, 21, 21}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 5, 6, 7, 7, 8, 9, 10, 10, 11, 11, 11, + 12, 13, 13, 13, 13, 14, 15, 15, 15, 15, 15, 15, 15}, + {0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 10, 11, 12, 12, + 12, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 11, 11, 12, + 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13}, +}; + +static const u8 +rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, + 15, 15, 16, 17, 18, 18, 19, 19, 20, 20, 20, 20, 20}, + {0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14, + 15, 16, 16, 17, 18, 19, 20, 20, 20, 20, 20, 20, 20}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, 13, 14, 15, + 15, 16, 17, 18, 19, 19, 20, 20, 20, 20, 20, 20, 20}, +}; + +static const u8 rtw8814a_pwrtrk_type2_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, + 6, 6, 7, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 10, + 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9, + 10, 11, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type2_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, + 11, 12, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 8, + 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 6, 6, 6, 7, 8, 9, 9, 10, + 10, 11, 11, 11, 12, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, + 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 9, 9, 9, + 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type2_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type2_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type2_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type2_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type2_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type2_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type2_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type2_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type2_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type2_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type2_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type2_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type2_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type2_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type2_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type2_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type2_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type2_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type2_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 3, 3, 3, 4, 6, 6, 7, 7, 8, 9, 10, 11, 12, 13, 13, 14, + 14, 14, 14, 14, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 4, 5, 6, 7, 7, 8, 7, 8, 10, 11, 12, 12, 13, 13, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 5, 5, 6, 7, 8, 9, 9, 10, 11, 12, 13, 13, 14, 15, 15, 16, + 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 9, 10, 10, 11, 12, + 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 0, 0, 1, 1, 1, 2, 3, 4, 5, 6, 7, 8, 8, 9, 11, 12, + 12, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 0, 1, 1, 2, 2, 3, 5, 7, 8, 9, 10, 11, 12, 13, 13, + 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 3, 3, 4, 6, 7, 7, 8, 9, 9, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 3, 7, 7, 8, 8, 9, 11, 12, 12, 13, 14, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 2, 3, 4, 5, 7, 8, 8, 10, 11, 12, 12, 13, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 0, 1, 1, 2, 2, 3, 4, 5, 6, 6, 7, 8, 9, 11, 11, + 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 13, 13}, + {0, 0, 1, 2, 3, 3, 5, 5, 6, 8, 8, 9, 10, 11, 13, 13, 13, + 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 4, 5, 7, 8, 9, 9, 10, 11, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 2, 2, 2, 3, 4, 5, 6, 7, 9, 10, 10, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 3, 7, 7, 8, 8, 9, 11, 12, 12, 13, 14, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 2, 3, 4, 5, 7, 8, 8, 10, 11, 12, 12, 13, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 1, 2, 2, 4, 5, 6, 6, 7, 8, 9, 10, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 0, 2, 3, 4, 5, 6, 8, 8, 9, 9, 11, 12, 13, 13, 13, + 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 0, 1, 2, 3, 3, 4, 6, 7, 8, 9, 10, 11, 12, 12, 12, + 12, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 0, 1, 2, 3, 3, 3, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12}, + {0, 2, 3, 4, 5, 7, 7, 8, 9, 10, 12, 13, 14, 15, 16, 17, 17, + 17, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18}, + {0, 1, 2, 3, 3, 4, 6, 7, 8, 8, 10, 11, 11, 12, 13, 13, 13, + 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 1, 3, 3, 3, 5, 5, 6, 6, 8, 8, 9, 10, 11, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 8, 9, 11, 12, 13, 14, 15, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 3, 3, 4, 5, 5, 6, 7, 7, 8, 10, 10, 11, 11, 11, + 11, 12, 12, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13}, +}; + +static const u8 rtw8814a_pwrtrk_type5_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 7, 8, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 8, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, 10, + 10, 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 8, 8, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 10, 10, 10, + 10, 10, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type5_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 8, 9, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 111, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type5_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 7, 8, 8, 9, 9, 9, + 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 8, 9, 9, 9, 9, + 10, 10, 10, 10, 10, 11, 11, 10, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8, + 9, 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 8, 8, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8, + 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 2, 3, 4, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 7, 7, 8, 8, 8, + 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 5, 5, 6, 7, 7, 8, 8, 9, 9, 9, 9, 10, + 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +static const u8 rtw8814a_pwrtrk_type5_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 2, 3, 4, 4, 5, 6, 7, 8, 8, 9, 9, 9, + 9, 9, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type5_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type5_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type5_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type5_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type5_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type5_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type5_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type5_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type5_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type5_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type5_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type5_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type5_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type5_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type5_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type5_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type5_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type5_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, + {0, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, + 8, 9, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10}, +}; + +static const u8 +rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 0, 1, 1, 2, 3, 3, 4, 5, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 11, 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 rtw8814a_pwrtrk_type7_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 5, 6, 7, 7, + 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +static const u8 rtw8814a_pwrtrk_type7_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 8, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type7_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type7_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type7_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type7_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type7_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type7_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type7_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type7_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type7_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type7_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type7_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type7_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type7_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type7_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type7_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type7_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type7_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type7_2g_cck_a_p, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, 12, + 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 6, 7, 7, 8, 9, 9, 10, 10, 11, 11, 12, + 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 7, 8, 8, 8, 9, 10, 10, + 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 4, 4, 5, 6, 7, 8, 9, 10, 11, 12, 14, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 2, 3, 5, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 5, 6, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 4, 4, 5, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, 12, + 12, 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, + 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 4, 4, 5, 6, 7, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 3, 4, 4, 5, 6, 7, 7, 8, 10, 11, 12, 13, 14, + 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15}, + {0, 0, 1, 2, 4, 5, 5, 6, 6, 7, 7, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 4, 5, 6, 7, 7, 8, 9, 9, 10, 10, 10, 11, 11, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 8, 9, 10, 10, 11, 11, + 11, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 10, 11, 11, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 4, 5, 6, 7, 8, 10, 10, 11, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 1, 2, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, 11, 11, + 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12}, + {0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, 10, 11, 11, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, + {0, 1, 2, 3, 4, 4, 5, 6, 7, 7, 8, 9, 10, 10, 10, 11, 12, + 12, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13, 13}, +}; + +static const u8 +rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_NUM][RTW_PWR_TRK_TBL_SZ] = { + {0, 0, 1, 2, 3, 3, 4, 5, 6, 7, 7, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 11, 12, 13, 13, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, + {0, 0, 1, 2, 3, 3, 4, 4, 6, 7, 7, 9, 10, 11, 12, 13, 14, + 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14, 14}, +}; + +static const u8 rtw8814a_pwrtrk_type8_2gd_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, + 6, 6, 7, 8, 9, 10, 11, 11, 11, 11, 11, 11, 11 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gd_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gc_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 6, 6, 6, 7, 7, 8, 8, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gc_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 5, 6, 7, 8, 8, 9, 10, 10, + 11, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gb_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 6, 7, 7, 8, 8, 8, + 9, 9, 10, 10, 11, 11, 12, 12, 12, 13, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2gb_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2ga_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 9, 9, 9, + 10, 11, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type8_2ga_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 4, 4, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, + 11, 12, 12, 13, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_d_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 3, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 7, 8, + 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_d_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 6, 6, 7, 8, 8, 9, 10, 10, + 10, 11, 12, 12, 13, 14, 14, 14, 14, 14, 14, 14 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_c_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 4, 5, 5, 6, 6, 6, 6, 7, 8, 9, 9, 10, + 10, 11, 11, 11, 12, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_c_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_b_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 2, 2, 2, 3, 3, 4, 5, 6, 6, 7, 7, 8, 8, 9, 10, + 10, 10, 11, 11, 12, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_b_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 7, 8, 8, 9, 9, 10, + 10, 11, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_a_n[RTW_PWR_TRK_TBL_SZ] = { + 0, 1, 2, 2, 3, 4, 4, 4, 5, 5, 6, 6, 7, 8, 9, 9, 9, 9, + 10, 10, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12 +}; + +static const u8 rtw8814a_pwrtrk_type8_2g_cck_a_p[RTW_PWR_TRK_TBL_SZ] = { + 0, 0, 1, 2, 2, 3, 3, 4, 5, 5, 6, 7, 8, 9, 9, 10, 10, + 11, 11, 12, 12, 12, 13, 13, 13, 13, 13, 13, 13, 13 +}; + +const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type8_tbl = { + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gd_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gd_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gd_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gc_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gc_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gc_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gb_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5gb_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5gb_p[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_n[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5ga_n[RTW_PWR_TRK_5G_3], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_1] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_1], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_2] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_2], + .pwrtrk_5ga_p[RTW_PWR_TRK_5G_3] = rtw8814a_pwrtrk_type8_5ga_p[RTW_PWR_TRK_5G_3], + .pwrtrk_2gd_n = rtw8814a_pwrtrk_type8_2gd_n, + .pwrtrk_2gd_p = rtw8814a_pwrtrk_type8_2gd_p, + .pwrtrk_2gc_n = rtw8814a_pwrtrk_type8_2gc_n, + .pwrtrk_2gc_p = rtw8814a_pwrtrk_type8_2gc_p, + .pwrtrk_2gb_n = rtw8814a_pwrtrk_type8_2gb_n, + .pwrtrk_2gb_p = rtw8814a_pwrtrk_type8_2gb_p, + .pwrtrk_2ga_n = rtw8814a_pwrtrk_type8_2ga_n, + .pwrtrk_2ga_p = rtw8814a_pwrtrk_type8_2ga_p, + .pwrtrk_2g_cckd_n = rtw8814a_pwrtrk_type8_2g_cck_d_n, + .pwrtrk_2g_cckd_p = rtw8814a_pwrtrk_type8_2g_cck_d_p, + .pwrtrk_2g_cckc_n = rtw8814a_pwrtrk_type8_2g_cck_c_n, + .pwrtrk_2g_cckc_p = rtw8814a_pwrtrk_type8_2g_cck_c_p, + .pwrtrk_2g_cckb_n = rtw8814a_pwrtrk_type8_2g_cck_b_n, + .pwrtrk_2g_cckb_p = rtw8814a_pwrtrk_type8_2g_cck_b_p, + .pwrtrk_2g_ccka_n = rtw8814a_pwrtrk_type8_2g_cck_a_n, + .pwrtrk_2g_ccka_p = rtw8814a_pwrtrk_type8_2g_cck_a_p, +}; + +static const struct rtw_pwr_seq_cmd init_power_on_8814a[] = { + {0x10c2, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +static const struct rtw_pwr_seq_cmd trans_carddis_to_cardemu_8814a[] = { + {0x0012, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(6), BIT(6)}, + {0x0015, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(5), 0}, + {0x0015, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(6), 0}, + {0x0023, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(4), 0}, + {0x0046, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x00}, + {0x0062, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x00}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_PCI_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(2), 0}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(3), 0}, + {0x0301, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_PCI_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0}, + {0x0071, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_PCI_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(2), 0}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +static const struct rtw_pwr_seq_cmd trans_cardemu_to_act_8814a[] = { + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(2), 0}, + {0x0006, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_POLLING, BIT(1), BIT(1)}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(3), 0}, + {0x00F0, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(7), 0}, + {0x0081, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0x30, 0x20}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(0), BIT(0)}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_POLLING, BIT(0), 0}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +static const struct rtw_pwr_seq_cmd trans_act_to_cardemu_8814a[] = { + {0x0c00, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x04}, + {0x0e00, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x04}, + {0x1002, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(0), 0}, + {0x0002, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_DELAY, 1, RTW_PWR_DELAY_US}, + {0x1002, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_PCI_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x001F, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0}, + {0x0007, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x28}, + {0x0008, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0x02, 0}, + {0x0066, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(7), 0}, + {0x0041, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(4), 0}, + {0x0042, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x004e, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, + {0x0041, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_USB_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(0), 0}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), BIT(1)}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_POLLING, BIT(1), 0}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +static const struct rtw_pwr_seq_cmd trans_cardemu_to_carddis_8814a[] = { + {0x0003, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(2), 0}, + {0x0080, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x01}, + {0x0081, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x30}, + {0x0045, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x00}, + {0x0046, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0xff}, + {0x0047, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0}, + {0x0015, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(6), BIT(6)}, + {0x0015, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(5), BIT(5)}, + {0x0012, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(6), 0}, + {0x0023, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(4), BIT(4)}, + {0x0008, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0007, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xFF, 0x20}, + {0x001f, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0020, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0021, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0076, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(1), 0}, + {0x0091, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, 0xA0, 0xA0}, + {0x0070, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(3), BIT(3)}, + {0x0005, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + RTW_PWR_ADDR_MAC, + RTW_PWR_CMD_WRITE, BIT(3), BIT(3)}, + {0xFFFF, + RTW_PWR_CUT_ALL_MSK, + RTW_PWR_INTF_ALL_MSK, + 0, + RTW_PWR_CMD_END, 0, 0}, +}; + +const struct rtw_pwr_seq_cmd * const card_enable_flow_8814a[] = { + init_power_on_8814a, + trans_carddis_to_cardemu_8814a, + trans_cardemu_to_act_8814a, + NULL +}; + +const struct rtw_pwr_seq_cmd * const card_disable_flow_8814a[] = { + trans_act_to_cardemu_8814a, + trans_cardemu_to_carddis_8814a, + NULL +}; diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h new file mode 100644 index 000000000000..69fb87e36c48 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814a_table.h @@ -0,0 +1,40 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ +/* Copyright(c) 2025 Realtek Corporation + */ + +#ifndef __RTW8814A_TABLE_H__ +#define __RTW8814A_TABLE_H__ + +extern const struct rtw_table rtw8814a_mac_tbl; +extern const struct rtw_table rtw8814a_agc_tbl; +extern const struct rtw_table rtw8814a_bb_tbl; +extern const struct rtw_table rtw8814a_bb_pg_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type0_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type2_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type3_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type4_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type5_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type7_tbl; +extern const struct rtw_table rtw8814a_bb_pg_type8_tbl; +extern const struct rtw_table rtw8814a_rf_a_tbl; +extern const struct rtw_table rtw8814a_rf_b_tbl; +extern const struct rtw_table rtw8814a_rf_c_tbl; +extern const struct rtw_table rtw8814a_rf_d_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type0_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type1_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type2_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type3_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type5_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type7_tbl; +extern const struct rtw_table rtw8814a_txpwr_lmt_type8_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type0_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type2_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type5_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type7_tbl; +extern const struct rtw_pwr_track_tbl rtw8814a_rtw_pwrtrk_type8_tbl; +extern const struct rtw_pwr_seq_cmd * const card_disable_flow_8814a[]; +extern const struct rtw_pwr_seq_cmd * const card_enable_flow_8814a[]; + +#endif diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814ae.c b/drivers/net/wireless/realtek/rtw88/rtw8814ae.c new file mode 100644 index 000000000000..c7436f1c8d40 --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814ae.c @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include <linux/module.h> +#include <linux/pci.h> +#include "pci.h" +#include "rtw8814a.h" + +static const struct pci_device_id rtw_8814ae_id_table[] = { + { + PCI_DEVICE(PCI_VENDOR_ID_REALTEK, 0x8813), + .driver_data = (kernel_ulong_t)&rtw8814a_hw_spec + }, + {} +}; +MODULE_DEVICE_TABLE(pci, rtw_8814ae_id_table); + +static struct pci_driver rtw_8814ae_driver = { + .name = KBUILD_MODNAME, + .id_table = rtw_8814ae_id_table, + .probe = rtw_pci_probe, + .remove = rtw_pci_remove, + .driver.pm = &rtw_pm_ops, + .shutdown = rtw_pci_shutdown, +}; +module_pci_driver(rtw_8814ae_driver); + +MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814ae driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8814au.c b/drivers/net/wireless/realtek/rtw88/rtw8814au.c new file mode 100644 index 000000000000..1a0886ec17dd --- /dev/null +++ b/drivers/net/wireless/realtek/rtw88/rtw8814au.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause +/* Copyright(c) 2025 Realtek Corporation + */ + +#include <linux/module.h> +#include <linux/usb.h> +#include "main.h" +#include "rtw8814a.h" +#include "usb.h" + +static const struct usb_device_id rtw_8814au_id_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(RTW_USB_VENDOR_ID_REALTEK, 0x8813, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x400b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x056e, 0x400d, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0846, 0x9054, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1817, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1852, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0b05, 0x1853, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x0e66, 0x0026, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x331a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x809a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x20f4, 0x809b, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x2357, 0x0106, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xa834, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x7392, 0xa833, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8814a_hw_spec) }, + {}, +}; +MODULE_DEVICE_TABLE(usb, rtw_8814au_id_table); + +static struct usb_driver rtw_8814au_driver = { + .name = KBUILD_MODNAME, + .id_table = rtw_8814au_id_table, + .probe = rtw_usb_probe, + .disconnect = rtw_usb_disconnect, +}; +module_usb_driver(rtw_8814au_driver); + +MODULE_AUTHOR("Bitterblue Smith <rtl8821cerfe2@gmail.com>"); +MODULE_DESCRIPTION("Realtek 802.11ac wireless 8814au driver"); +MODULE_LICENSE("Dual BSD/GPL"); diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821a.c b/drivers/net/wireless/realtek/rtw88/rtw8821a.c index f68239b07319..413aec694c33 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821a.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821a.c @@ -871,6 +871,7 @@ static const struct rtw_chip_ops rtw8821a_ops = { .set_tx_power_index = rtw88xxa_set_tx_power_index, .cfg_ldo25 = rtw8821a_cfg_ldo25, .efuse_grant = rtw88xxa_efuse_grant, + .set_ampdu_factor = NULL, .false_alarm_statistics = rtw88xxa_false_alarm_statistics, .phy_calibration = rtw8821a_phy_calibration, .cck_pd_set = rtw88xxa_phy_cck_pd_set, @@ -1175,6 +1176,7 @@ const struct rtw_chip_info rtw8821a_hw_spec = { .rfe_defs = rtw8821a_rfe_defs, .rfe_defs_size = ARRAY_SIZE(rtw8821a_rfe_defs), .rx_ldpc = false, + .amsdu_in_ampdu = true, .hw_feature_report = false, .c2h_ra_report_size = 4, .old_datarate_fb_limit = true, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821au.c b/drivers/net/wireless/realtek/rtw88/rtw8821au.c index a01744b64e8d..28c281b32978 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821au.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821au.c @@ -66,7 +66,7 @@ static const struct usb_device_id rtw_8821au_id_table[] = { MODULE_DEVICE_TABLE(usb, rtw_8821au_id_table); static struct usb_driver rtw_8821au_driver = { - .name = "rtw_8821au", + .name = KBUILD_MODNAME, .id_table = rtw_8821au_id_table, .probe = rtw_usb_probe, .disconnect = rtw_usb_disconnect, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821c.c b/drivers/net/wireless/realtek/rtw88/rtw8821c.c index eb7e34c545d0..413130a30ca9 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821c.c @@ -680,11 +680,11 @@ static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, } static void -rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) +rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, + u8 rs, u32 *phy_pwr_idx) { struct rtw_hal *hal = &rtwdev->hal; static const u32 offset_txagc[2] = {0x1d00, 0x1d80}; - static u32 phy_pwr_idx; u8 rate, rate_idx, pwr_index, shift; int j; @@ -692,12 +692,12 @@ rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) rate = rtw_rate_section[rs][j]; pwr_index = hal->tx_pwr_tbl[path][rate]; shift = rate & 0x3; - phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); + *phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); if (shift == 0x3 || rate == DESC_RATEVHT1SS_MCS9) { rate_idx = rate & 0xfc; rtw_write32(rtwdev, offset_txagc[path] + rate_idx, - phy_pwr_idx); - phy_pwr_idx = 0; + *phy_pwr_idx); + *phy_pwr_idx = 0; } } } @@ -705,14 +705,16 @@ rtw8821c_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) static void rtw8821c_set_tx_power_index(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; + u32 phy_pwr_idx = 0; int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) { + for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) { if (rs == RTW_RATE_SECTION_HT_2S || rs == RTW_RATE_SECTION_VHT_2S) continue; - rtw8821c_set_tx_power_index_by_rate(rtwdev, path, rs); + rtw8821c_set_tx_power_index_by_rate(rtwdev, path, rs, + &phy_pwr_idx); } } } @@ -1666,6 +1668,7 @@ static const struct rtw_chip_ops rtw8821c_ops = { .set_antenna = NULL, .set_tx_power_index = rtw8821c_set_tx_power_index, .cfg_ldo25 = rtw8821c_cfg_ldo25, + .set_ampdu_factor = NULL, .false_alarm_statistics = rtw8821c_false_alarm_statistics, .phy_calibration = rtw8821c_phy_calibration, .cck_pd_set = rtw8821c_phy_cck_pd_set, @@ -1988,6 +1991,7 @@ const struct rtw_chip_info rtw8821c_hw_spec = { .band = RTW_BAND_2G | RTW_BAND_5G, .page_size = TX_PAGE_SIZE, .dig_min = 0x1c, + .amsdu_in_ampdu = true, .usb_tx_agg_desc_num = 3, .hw_feature_report = true, .c2h_ra_report_size = 7, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821ce.c b/drivers/net/wireless/realtek/rtw88/rtw8821ce.c index f3d971feda04..40637c079d99 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821ce.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821ce.c @@ -21,7 +21,7 @@ static const struct pci_device_id rtw_8821ce_id_table[] = { MODULE_DEVICE_TABLE(pci, rtw_8821ce_id_table); static struct pci_driver rtw_8821ce_driver = { - .name = "rtw_8821ce", + .name = KBUILD_MODNAME, .id_table = rtw_8821ce_id_table, .probe = rtw_pci_probe, .remove = rtw_pci_remove, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cs.c b/drivers/net/wireless/realtek/rtw88/rtw8821cs.c index a359413369a4..6d94162213c6 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821cs.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821cs.c @@ -20,7 +20,7 @@ static const struct sdio_device_id rtw_8821cs_id_table[] = { MODULE_DEVICE_TABLE(sdio, rtw_8821cs_id_table); static struct sdio_driver rtw_8821cs_driver = { - .name = "rtw_8821cs", + .name = KBUILD_MODNAME, .probe = rtw_sdio_probe, .remove = rtw_sdio_remove, .id_table = rtw_8821cs_id_table, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8821cu.c b/drivers/net/wireless/realtek/rtw88/rtw8821cu.c index a019f4085e73..7a0fffc359e2 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8821cu.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8821cu.c @@ -48,7 +48,7 @@ static int rtw_8821cu_probe(struct usb_interface *intf, } static struct usb_driver rtw_8821cu_driver = { - .name = "rtw_8821cu", + .name = KBUILD_MODNAME, .id_table = rtw_8821cu_id_table, .probe = rtw_8821cu_probe, .disconnect = rtw_usb_disconnect, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822b.c b/drivers/net/wireless/realtek/rtw88/rtw8822b.c index 7f03903ddf4b..ab199eaea3c7 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822b.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822b.c @@ -935,11 +935,11 @@ static void query_phy_status(struct rtw_dev *rtwdev, u8 *phy_status, } static void -rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) +rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, + u8 rs, u32 *phy_pwr_idx) { struct rtw_hal *hal = &rtwdev->hal; static const u32 offset_txagc[2] = {0x1d00, 0x1d80}; - static u32 phy_pwr_idx; u8 rate, rate_idx, pwr_index, shift; int j; @@ -947,12 +947,12 @@ rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) rate = rtw_rate_section[rs][j]; pwr_index = hal->tx_pwr_tbl[path][rate]; shift = rate & 0x3; - phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); + *phy_pwr_idx |= ((u32)pwr_index << (shift * 8)); if (shift == 0x3) { rate_idx = rate & 0xfc; rtw_write32(rtwdev, offset_txagc[path] + rate_idx, - phy_pwr_idx); - phy_pwr_idx = 0; + *phy_pwr_idx); + *phy_pwr_idx = 0; } } } @@ -960,11 +960,13 @@ rtw8822b_set_tx_power_index_by_rate(struct rtw_dev *rtwdev, u8 path, u8 rs) static void rtw8822b_set_tx_power_index(struct rtw_dev *rtwdev) { struct rtw_hal *hal = &rtwdev->hal; + u32 phy_pwr_idx = 0; int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) - rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs); + for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) + rtw8822b_set_tx_power_index_by_rate(rtwdev, path, rs, + &phy_pwr_idx); } } @@ -2156,6 +2158,7 @@ static const struct rtw_chip_ops rtw8822b_ops = { .set_tx_power_index = rtw8822b_set_tx_power_index, .set_antenna = rtw8822b_set_antenna, .cfg_ldo25 = rtw8822b_cfg_ldo25, + .set_ampdu_factor = NULL, .false_alarm_statistics = rtw8822b_false_alarm_statistics, .phy_calibration = rtw8822b_phy_calibration, .pwr_track = rtw8822b_pwr_track, @@ -2529,6 +2532,7 @@ const struct rtw_chip_info rtw8822b_hw_spec = { .band = RTW_BAND_2G | RTW_BAND_5G, .page_size = TX_PAGE_SIZE, .dig_min = 0x1c, + .amsdu_in_ampdu = true, .usb_tx_agg_desc_num = 3, .hw_feature_report = true, .c2h_ra_report_size = 7, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822be.c b/drivers/net/wireless/realtek/rtw88/rtw8822be.c index 4994950776cd..0bb9f70e7920 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822be.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822be.c @@ -17,7 +17,7 @@ static const struct pci_device_id rtw_8822be_id_table[] = { MODULE_DEVICE_TABLE(pci, rtw_8822be_id_table); static struct pci_driver rtw_8822be_driver = { - .name = "rtw_8822be", + .name = KBUILD_MODNAME, .id_table = rtw_8822be_id_table, .probe = rtw_pci_probe, .remove = rtw_pci_remove, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bs.c b/drivers/net/wireless/realtek/rtw88/rtw8822bs.c index 31d8645f83bd..744781dcb419 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822bs.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822bs.c @@ -20,7 +20,7 @@ static const struct sdio_device_id rtw_8822bs_id_table[] = { MODULE_DEVICE_TABLE(sdio, rtw_8822bs_id_table); static struct sdio_driver rtw_8822bs_driver = { - .name = "rtw_8822bs", + .name = KBUILD_MODNAME, .probe = rtw_sdio_probe, .remove = rtw_sdio_remove, .id_table = rtw_8822bs_id_table, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c index 8883300fc6ad..44e28e583964 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822bu.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822bu.c @@ -73,6 +73,12 @@ static const struct usb_device_id rtw_8822bu_id_table[] = { .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* ELECOM WDB-867DU3S */ { USB_DEVICE_AND_INTERFACE_INFO(0x2c4e, 0x0107, 0xff, 0xff, 0xff), .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Mercusys MA30H */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2c4e, 0x010a, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* Mercusys MA30N */ + { USB_DEVICE_AND_INTERFACE_INFO(0x2001, 0x3322, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* D-Link DWA-T185 rev. A1 */ + { USB_DEVICE_AND_INTERFACE_INFO(0x0411, 0x03d1, 0xff, 0xff, 0xff), + .driver_info = (kernel_ulong_t)&(rtw8822b_hw_spec) }, /* BUFFALO WI-U2-866DM */ {}, }; MODULE_DEVICE_TABLE(usb, rtw_8822bu_id_table); @@ -84,7 +90,7 @@ static int rtw8822bu_probe(struct usb_interface *intf, } static struct usb_driver rtw_8822bu_driver = { - .name = "rtw_8822bu", + .name = KBUILD_MODNAME, .id_table = rtw_8822bu_id_table, .probe = rtw8822bu_probe, .disconnect = rtw_usb_disconnect, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822c.c b/drivers/net/wireless/realtek/rtw88/rtw8822c.c index ec362a817f5f..017d959de3ce 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822c.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822c.c @@ -2746,7 +2746,7 @@ static void rtw8822c_set_tx_power_index(struct rtw_dev *rtwdev) s8 diff_idx[4]; rtw8822c_set_write_tx_power_ref(rtwdev, pwr_ref_cck, pwr_ref_ofdm); - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) { + for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) { for (j = 0; j < rtw_rate_size[rs]; j++) { rate = rtw_rate_section[rs][j]; pwr_a = hal->tx_pwr_tbl[RF_PATH_A][rate]; @@ -3951,7 +3951,8 @@ static void rtw8822c_dpk_cal_coef1(struct rtw_dev *rtwdev) rtw_write32(rtwdev, REG_NCTL0, 0x00001148); rtw_write32(rtwdev, REG_NCTL0, 0x00001149); - check_hw_ready(rtwdev, 0x2d9c, MASKBYTE0, 0x55); + if (!check_hw_ready(rtwdev, 0x2d9c, MASKBYTE0, 0x55)) + rtw_warn(rtwdev, "DPK stuck, performance may be suboptimal"); rtw_write8(rtwdev, 0x1b10, 0x0); rtw_write32_mask(rtwdev, REG_NCTL0, BIT_SUBPAGE, 0x0000000c); @@ -4968,6 +4969,7 @@ static const struct rtw_chip_ops rtw8822c_ops = { .set_tx_power_index = rtw8822c_set_tx_power_index, .set_antenna = rtw8822c_set_antenna, .cfg_ldo25 = rtw8822c_cfg_ldo25, + .set_ampdu_factor = NULL, .false_alarm_statistics = rtw8822c_false_alarm_statistics, .dpk_track = rtw8822c_dpk_track, .phy_calibration = rtw8822c_phy_calibration, @@ -5349,6 +5351,7 @@ const struct rtw_chip_info rtw8822c_hw_spec = { .band = RTW_BAND_2G | RTW_BAND_5G, .page_size = TX_PAGE_SIZE, .dig_min = 0x20, + .amsdu_in_ampdu = true, .usb_tx_agg_desc_num = 3, .hw_feature_report = true, .c2h_ra_report_size = 7, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822ce.c b/drivers/net/wireless/realtek/rtw88/rtw8822ce.c index e26c6bc82936..9def732480af 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822ce.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822ce.c @@ -21,7 +21,7 @@ static const struct pci_device_id rtw_8822ce_id_table[] = { MODULE_DEVICE_TABLE(pci, rtw_8822ce_id_table); static struct pci_driver rtw_8822ce_driver = { - .name = "rtw_8822ce", + .name = KBUILD_MODNAME, .id_table = rtw_8822ce_id_table, .probe = rtw_pci_probe, .remove = rtw_pci_remove, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cs.c b/drivers/net/wireless/realtek/rtw88/rtw8822cs.c index 975e81c824f2..322281e07eb8 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822cs.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822cs.c @@ -20,7 +20,7 @@ static const struct sdio_device_id rtw_8822cs_id_table[] = { MODULE_DEVICE_TABLE(sdio, rtw_8822cs_id_table); static struct sdio_driver rtw_8822cs_driver = { - .name = "rtw_8822cs", + .name = KBUILD_MODNAME, .probe = rtw_sdio_probe, .remove = rtw_sdio_remove, .id_table = rtw_8822cs_id_table, diff --git a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c index 157d5102a4b1..324fd5c8bfd4 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw8822cu.c +++ b/drivers/net/wireless/realtek/rtw88/rtw8822cu.c @@ -32,7 +32,7 @@ static int rtw8822cu_probe(struct usb_interface *intf, } static struct usb_driver rtw_8822cu_driver = { - .name = "rtw_8822cu", + .name = KBUILD_MODNAME, .id_table = rtw_8822cu_id_table, .probe = rtw8822cu_probe, .disconnect = rtw_usb_disconnect, diff --git a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c index 71e61b9c0bec..0fa943271fb6 100644 --- a/drivers/net/wireless/realtek/rtw88/rtw88xxa.c +++ b/drivers/net/wireless/realtek/rtw88/rtw88xxa.c @@ -1637,7 +1637,7 @@ void rtw88xxa_set_tx_power_index(struct rtw_dev *rtwdev) int rs, path; for (path = 0; path < hal->rf_path_num; path++) { - for (rs = 0; rs < RTW_RATE_SECTION_MAX; rs++) { + for (rs = 0; rs <= __RTW_RATE_SECTION_2SS_MAX; rs++) { if (hal->rf_path_num == 1 && (rs == RTW_RATE_SECTION_HT_2S || rs == RTW_RATE_SECTION_VHT_2S)) diff --git a/drivers/net/wireless/realtek/rtw88/rx.c b/drivers/net/wireless/realtek/rtw88/rx.c index 90fc8a5fa89e..8b0afaaffaa0 100644 --- a/drivers/net/wireless/realtek/rtw88/rx.c +++ b/drivers/net/wireless/realtek/rtw88/rx.c @@ -73,6 +73,12 @@ static void rtw_rx_phy_stat(struct rtw_dev *rtwdev, rate_ss_evm = 2; evm_id = RTW_EVM_2SS_A; break; + case DESC_RATEMCS16...DESC_RATEMCS23: + case DESC_RATEVHT3SS_MCS0...DESC_RATEVHT3SS_MCS9: + rate_ss = 3; + rate_ss_evm = 3; + evm_id = RTW_EVM_3SS_A; + break; default: rtw_warn(rtwdev, "unknown pkt rate = %d\n", pkt_stat->rate); return; diff --git a/drivers/net/wireless/realtek/rtw88/sar.c b/drivers/net/wireless/realtek/rtw88/sar.c index c472f1502b82..50b9c2412bb1 100644 --- a/drivers/net/wireless/realtek/rtw88/sar.c +++ b/drivers/net/wireless/realtek/rtw88/sar.c @@ -97,7 +97,7 @@ int rtw_set_sar_specs(struct rtw_dev *rtwdev, power, BIT(RTW_COMMON_SAR_FCT)); for (j = 0; j < RTW_RF_PATH_MAX; j++) { - for (k = 0; k < RTW_RATE_SECTION_MAX; k++) { + for (k = 0; k < RTW_RATE_SECTION_NUM; k++) { arg = (struct rtw_sar_arg){ .sar_band = idx, .path = j, diff --git a/drivers/net/wireless/realtek/rtw88/sdio.c b/drivers/net/wireless/realtek/rtw88/sdio.c index e024061bdbf7..e733ed846123 100644 --- a/drivers/net/wireless/realtek/rtw88/sdio.c +++ b/drivers/net/wireless/realtek/rtw88/sdio.c @@ -10,6 +10,7 @@ #include <linux/mmc/host.h> #include <linux/mmc/sdio_func.h> #include "main.h" +#include "mac.h" #include "debug.h" #include "fw.h" #include "ps.h" @@ -677,12 +678,22 @@ static void rtw_sdio_enable_rx_aggregation(struct rtw_dev *rtwdev) { u8 size, timeout; - if (rtw_chip_wcpu_11n(rtwdev)) { + switch (rtwdev->chip->id) { + case RTW_CHIP_TYPE_8703B: + case RTW_CHIP_TYPE_8821A: + case RTW_CHIP_TYPE_8812A: size = 0x6; timeout = 0x6; - } else { + break; + case RTW_CHIP_TYPE_8723D: + size = 0xa; + timeout = 0x3; + rtw_write8_set(rtwdev, REG_RXDMA_AGG_PG_TH + 3, BIT(7)); + break; + default: size = 0xff; timeout = 0x1; + break; } /* Make the firmware honor the size limit configured below */ @@ -718,10 +729,7 @@ static u8 rtw_sdio_get_tx_qsel(struct rtw_dev *rtwdev, struct sk_buff *skb, case RTW_TX_QUEUE_H2C: return TX_DESC_QSEL_H2C; case RTW_TX_QUEUE_MGMT: - if (rtw_chip_wcpu_11n(rtwdev)) - return TX_DESC_QSEL_HIGH; - else - return TX_DESC_QSEL_MGMT; + return TX_DESC_QSEL_MGMT; case RTW_TX_QUEUE_HI0: return TX_DESC_QSEL_HIGH; default: @@ -1147,7 +1155,7 @@ static void rtw_sdio_declaim(struct rtw_dev *rtwdev, sdio_release_host(sdio_func); } -static struct rtw_hci_ops rtw_sdio_ops = { +static const struct rtw_hci_ops rtw_sdio_ops = { .tx_write = rtw_sdio_tx_write, .tx_kick_off = rtw_sdio_tx_kick_off, .setup = rtw_sdio_setup, @@ -1157,6 +1165,7 @@ static struct rtw_hci_ops rtw_sdio_ops = { .link_ps = rtw_sdio_link_ps, .interface_cfg = rtw_sdio_interface_cfg, .dynamic_rx_agg = NULL, + .write_firmware_page = rtw_write_firmware_page, .read8 = rtw_sdio_read8, .read16 = rtw_sdio_read16, @@ -1227,10 +1236,7 @@ static void rtw_sdio_process_tx_queue(struct rtw_dev *rtwdev, return; } - if (queue <= RTW_TX_QUEUE_VO) - rtw_sdio_indicate_tx_status(rtwdev, skb); - else - dev_kfree_skb_any(skb); + rtw_sdio_indicate_tx_status(rtwdev, skb); } static void rtw_sdio_tx_handler(struct work_struct *work) @@ -1298,7 +1304,6 @@ static void rtw_sdio_deinit_tx(struct rtw_dev *rtwdev) struct rtw_sdio *rtwsdio = (struct rtw_sdio *)rtwdev->priv; int i; - flush_workqueue(rtwsdio->txwq); destroy_workqueue(rtwsdio->txwq); kfree(rtwsdio->tx_handler_data); diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index c4908db4ff0e..3b5126ffc81a 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -139,7 +139,7 @@ static void rtw_usb_write(struct rtw_dev *rtwdev, u32 addr, u32 val, int len) ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE, - addr, 0, data, len, 30000); + addr, 0, data, len, 500); if (ret < 0 && ret != -ENODEV && count++ < 4) rtw_err(rtwdev, "write register 0x%x failed with %d\n", addr, ret); @@ -165,6 +165,60 @@ static void rtw_usb_write32(struct rtw_dev *rtwdev, u32 addr, u32 val) rtw_usb_write(rtwdev, addr, val, 4); } +static void rtw_usb_write_firmware_page(struct rtw_dev *rtwdev, u32 page, + const u8 *data, u32 size) +{ + struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); + struct usb_device *udev = rtwusb->udev; + u32 addr = FW_START_ADDR_LEGACY; + u8 *data_dup, *buf; + u32 n, block_size; + int ret; + + switch (rtwdev->chip->id) { + case RTW_CHIP_TYPE_8723D: + block_size = 254; + break; + default: + block_size = 196; + break; + } + + data_dup = kmemdup(data, size, GFP_KERNEL); + if (!data_dup) + return; + + buf = data_dup; + + rtw_write32_mask(rtwdev, REG_MCUFW_CTRL, BIT_ROM_PGE, page); + + while (size > 0) { + if (size >= block_size) + n = block_size; + else if (size >= 8) + n = 8; + else + n = 1; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + RTW_USB_CMD_REQ, RTW_USB_CMD_WRITE, + addr, 0, buf, n, 500); + if (ret != n) { + if (ret != -ENODEV) + rtw_err(rtwdev, + "write 0x%x len %d failed: %d\n", + addr, n, ret); + break; + } + + addr += n; + buf += n; + size -= n; + } + + kfree(data_dup); +} + static int dma_mapping_to_ep(enum rtw_dma_mapping dma_mapping) { switch (dma_mapping) { @@ -866,6 +920,7 @@ static void rtw_usb_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable) case RTW_CHIP_TYPE_8822C: case RTW_CHIP_TYPE_8822B: case RTW_CHIP_TYPE_8821C: + case RTW_CHIP_TYPE_8814A: rtw_usb_dynamic_rx_agg_v1(rtwdev, enable); break; case RTW_CHIP_TYPE_8821A: @@ -881,7 +936,7 @@ static void rtw_usb_dynamic_rx_agg(struct rtw_dev *rtwdev, bool enable) } } -static struct rtw_hci_ops rtw_usb_ops = { +static const struct rtw_hci_ops rtw_usb_ops = { .tx_write = rtw_usb_tx_write, .tx_kick_off = rtw_usb_tx_kick_off, .setup = rtw_usb_setup, @@ -891,6 +946,7 @@ static struct rtw_hci_ops rtw_usb_ops = { .link_ps = rtw_usb_link_ps, .interface_cfg = rtw_usb_interface_cfg, .dynamic_rx_agg = rtw_usb_dynamic_rx_agg, + .write_firmware_page = rtw_usb_write_firmware_page, .write8 = rtw_usb_write8, .write16 = rtw_usb_write16, @@ -948,7 +1004,6 @@ static void rtw_usb_deinit_rx(struct rtw_dev *rtwdev) skb_queue_purge(&rtwusb->rx_queue); - flush_workqueue(rtwusb->rxwq); destroy_workqueue(rtwusb->rxwq); skb_queue_purge(&rtwusb->rx_free_queue); @@ -977,7 +1032,6 @@ static void rtw_usb_deinit_tx(struct rtw_dev *rtwdev) { struct rtw_usb *rtwusb = rtw_get_usb_priv(rtwdev); - flush_workqueue(rtwusb->txwq); destroy_workqueue(rtwusb->txwq); rtw_usb_tx_queue_purge(rtwusb); } @@ -1094,7 +1148,8 @@ static int rtw_usb_switch_mode_new(struct rtw_dev *rtwdev) static bool rtw_usb3_chip_old(u8 chip_id) { - return chip_id == RTW_CHIP_TYPE_8812A; + return chip_id == RTW_CHIP_TYPE_8812A || + chip_id == RTW_CHIP_TYPE_8814A; } static bool rtw_usb3_chip_new(u8 chip_id) diff --git a/drivers/net/wireless/realtek/rtw88/util.c b/drivers/net/wireless/realtek/rtw88/util.c index e222d3c01a77..66819f694405 100644 --- a/drivers/net/wireless/realtek/rtw88/util.c +++ b/drivers/net/wireless/realtek/rtw88/util.c @@ -101,7 +101,8 @@ void rtw_desc_to_mcsrate(u16 rate, u8 *mcs, u8 *nss) *nss = 4; *mcs = rate - DESC_RATEVHT4SS_MCS0; } else if (rate >= DESC_RATEMCS0 && - rate <= DESC_RATEMCS15) { + rate <= DESC_RATEMCS31) { + *nss = 0; *mcs = rate - DESC_RATEMCS0; } } diff --git a/drivers/net/wireless/realtek/rtw89/Kconfig b/drivers/net/wireless/realtek/rtw89/Kconfig index b1c86cdd9c0e..205d7ecca7d7 100644 --- a/drivers/net/wireless/realtek/rtw89/Kconfig +++ b/drivers/net/wireless/realtek/rtw89/Kconfig @@ -123,7 +123,7 @@ config RTW89_DEBUGMSG config RTW89_DEBUGFS bool "Realtek rtw89 debugfs support" - depends on RTW89_CORE + depends on RTW89_CORE && CFG80211_DEBUGFS select RTW89_DEBUG help Enable debugfs support diff --git a/drivers/net/wireless/realtek/rtw89/acpi.c b/drivers/net/wireless/realtek/rtw89/acpi.c index f5dedb12c129..581d6d4154d3 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.c +++ b/drivers/net/wireless/realtek/rtw89/acpi.c @@ -12,6 +12,121 @@ static const guid_t rtw89_guid = GUID_INIT(0xD2A8C3E8, 0x4B69, 0x4F00, 0x82, 0xBD, 0xFE, 0x86, 0x07, 0x80, 0x3A, 0xA7); +static u32 rtw89_acpi_traversal_object(struct rtw89_dev *rtwdev, + const union acpi_object *obj, u8 *pos) +{ + const union acpi_object *elm; + unsigned int i; + u32 sub_len; + u32 len = 0; + u8 *tmp; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + if (pos) + pos[len] = obj->integer.value; + + len++; + break; + case ACPI_TYPE_BUFFER: + if (unlikely(obj->buffer.length == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: invalid buffer type\n", __func__); + goto err; + } + + if (pos) + memcpy(pos, obj->buffer.pointer, obj->buffer.length); + + len += obj->buffer.length; + break; + case ACPI_TYPE_PACKAGE: + if (unlikely(obj->package.count == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: invalid package type\n", __func__); + goto err; + } + + for (i = 0; i < obj->package.count; i++) { + elm = &obj->package.elements[i]; + tmp = pos ? pos + len : NULL; + + sub_len = rtw89_acpi_traversal_object(rtwdev, elm, tmp); + if (unlikely(sub_len == 0)) + goto err; + + len += sub_len; + } + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: unhandled type: %d\n", + __func__, obj->type); + goto err; + } + + return len; + +err: + return 0; +} + +static u32 rtw89_acpi_calculate_object_length(struct rtw89_dev *rtwdev, + const union acpi_object *obj) +{ + return rtw89_acpi_traversal_object(rtwdev, obj, NULL); +} + +static struct rtw89_acpi_data * +rtw89_acpi_evaluate_method(struct rtw89_dev *rtwdev, const char *method) +{ + struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; + struct rtw89_acpi_data *data = NULL; + acpi_handle root, handle; + union acpi_object *obj; + acpi_status status; + u32 len; + + root = ACPI_HANDLE(rtwdev->dev); + if (!root) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to get root\n", method); + return NULL; + } + + status = acpi_get_handle(root, (acpi_string)method, &handle); + if (ACPI_FAILURE(status)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to get handle\n", method); + return NULL; + } + + status = acpi_evaluate_object(handle, NULL, NULL, &buf); + if (ACPI_FAILURE(status)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to evaluate object\n", method); + return NULL; + } + + obj = buf.pointer; + len = rtw89_acpi_calculate_object_length(rtwdev, obj); + if (unlikely(len == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi (%s): failed to traversal obj len\n", method); + goto out; + } + + data = kzalloc(struct_size(data, buf, len), GFP_KERNEL); + if (!data) + goto out; + + data->len = len; + rtw89_acpi_traversal_object(rtwdev, obj, data->buf); + +out: + ACPI_FREE(obj); + return data; +} + static int rtw89_acpi_dsm_get_value(struct rtw89_dev *rtwdev, union acpi_object *obj, u8 *value) @@ -121,6 +236,49 @@ int rtw89_acpi_dsm_get_policy_6ghz_sp(struct rtw89_dev *rtwdev, return 0; } +static bool chk_acpi_policy_tas_sig(const struct rtw89_acpi_policy_tas *p) +{ + return p->signature[0] == 0x52 && + p->signature[1] == 0x54 && + p->signature[2] == 0x4B && + p->signature[3] == 0x05; +} + +static int rtw89_acpi_dsm_get_policy_tas(struct rtw89_dev *rtwdev, + union acpi_object *obj, + struct rtw89_acpi_policy_tas **policy) +{ + const struct rtw89_acpi_policy_tas *ptr; + u32 buf_len; + + if (obj->type != ACPI_TYPE_BUFFER) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "acpi: expect buffer but type: %d\n", obj->type); + return -EINVAL; + } + + buf_len = obj->buffer.length; + if (buf_len < sizeof(*ptr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", + __func__, buf_len); + return -EINVAL; + } + + ptr = (typeof(ptr))obj->buffer.pointer; + if (!chk_acpi_policy_tas_sig(ptr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: bad signature\n", __func__); + return -EINVAL; + } + + *policy = kmemdup(ptr, sizeof(*ptr), GFP_KERNEL); + if (!*policy) + return -ENOMEM; + + rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "policy_tas: ", *policy, + sizeof(*ptr)); + return 0; +} + int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, enum rtw89_acpi_dsm_func func, struct rtw89_acpi_dsm_result *res) @@ -142,6 +300,8 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, else if (func == RTW89_ACPI_DSM_FUNC_6GHZ_SP_SUP) ret = rtw89_acpi_dsm_get_policy_6ghz_sp(rtwdev, obj, &res->u.policy_6ghz_sp); + else if (func == RTW89_ACPI_DSM_FUNC_TAS_EN) + ret = rtw89_acpi_dsm_get_policy_tas(rtwdev, obj, &res->u.policy_tas); else ret = rtw89_acpi_dsm_get_value(rtwdev, obj, &res->u.value); @@ -152,46 +312,875 @@ int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev, struct rtw89_acpi_rtag_result *res) { - struct acpi_buffer buf = {ACPI_ALLOCATE_BUFFER, NULL}; - acpi_handle root, handle; - union acpi_object *obj; - acpi_status status; + const struct rtw89_acpi_data *data; u32 buf_len; int ret = 0; - root = ACPI_HANDLE(rtwdev->dev); - if (!root) - return -EOPNOTSUPP; - - status = acpi_get_handle(root, (acpi_string)"RTAG", &handle); - if (ACPI_FAILURE(status)) + data = rtw89_acpi_evaluate_method(rtwdev, "RTAG"); + if (!data) return -EIO; - status = acpi_evaluate_object(handle, NULL, NULL, &buf); - if (ACPI_FAILURE(status)) - return -EIO; + buf_len = data->len; + if (buf_len != sizeof(*res)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", + __func__, buf_len); + ret = -EINVAL; + goto out; + } - obj = buf.pointer; - if (obj->type != ACPI_TYPE_BUFFER) { + *res = *(struct rtw89_acpi_rtag_result *)data->buf; + + rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res)); + +out: + kfree(data); + return ret; +} + +enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, + u32 center_freq) +{ + switch (center_freq) { + default: rtw89_debug(rtwdev, RTW89_DBG_ACPI, - "acpi: expect buffer but type: %d\n", obj->type); - ret = -EINVAL; + "center freq %u to ACPI SAR subband is unhandled\n", + center_freq); + fallthrough; + case 2412 ... 2484: + return RTW89_ACPI_SAR_2GHZ_SUBBAND; + case 5180 ... 5240: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_1; + case 5250 ... 5320: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_2; + case 5500 ... 5720: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_2E; + case 5745 ... 5885: + return RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4; + case 5955 ... 6155: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L; + case 6175 ... 6415: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H; + case 6435 ... 6515: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_6; + case 6535 ... 6695: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L; + case 6715 ... 6855: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H; + + /* freq 6875 (ch 185, 20MHz) spans RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H + * and RTW89_ACPI_SAR_6GHZ_SUBBAND_8, so directly describe it with + * struct rtw89_6ghz_span. + */ + + case 6895 ... 7115: + return RTW89_ACPI_SAR_6GHZ_SUBBAND_8; + } +} + +enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, + enum rtw89_acpi_sar_subband subband) +{ + switch (subband) { + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "ACPI SAR subband %u to band is unhandled\n", subband); + fallthrough; + case RTW89_ACPI_SAR_2GHZ_SUBBAND: + return RTW89_BAND_2G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_1: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_2: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_2E: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4: + return RTW89_BAND_5G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_6: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H: + return RTW89_BAND_6G; + case RTW89_ACPI_SAR_6GHZ_SUBBAND_8: + return RTW89_BAND_6G; + } +} + +static u8 rtw89_acpi_sar_rfpath_to_hp_antidx(enum rtw89_rf_path rfpath) +{ + switch (rfpath) { + default: + case RF_PATH_B: + return 0; + case RF_PATH_A: + return 1; + } +} + +static u8 rtw89_acpi_sar_rfpath_to_rt_antidx(enum rtw89_rf_path rfpath) +{ + switch (rfpath) { + default: + case RF_PATH_A: + return 0; + case RF_PATH_B: + return 1; + } +} + +static s16 rtw89_acpi_sar_normalize_hp_val(u8 v) +{ + static const u8 bias = 10; + static const u8 fct = 1; + u16 res; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + res = (bias << TXPWR_FACTOR_OF_RTW89_ACPI_SAR) + + (v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct)); + + return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR); +} + +static s16 rtw89_acpi_sar_normalize_rt_val(u8 v) +{ + static const u8 fct = 3; + u16 res; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + res = v << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct); + + return min_t(s32, res, MAX_VAL_OF_RTW89_ACPI_SAR); +} + +static +void rtw89_acpi_sar_load_std_legacy(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_std_legacy *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY) + ent->v[subband][path] = + rec->normalize(ptr->v[antidx][subband]); + else + ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR; + } + } +} + +static +void rtw89_acpi_sar_load_std_has_6ghz(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_std_has_6ghz *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND); + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]); + } + } +} + +static +void rtw89_acpi_sar_load_sml_legacy(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_sml_legacy *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + if (subband < RTW89_ACPI_SAR_SUBBAND_NR_LEGACY) + ent->v[subband][path] = + rec->normalize(ptr->v[antidx][subband]); + else + ent->v[subband][path] = MAX_VAL_OF_RTW89_ACPI_SAR; + } + } +} + +static +void rtw89_acpi_sar_load_sml_has_6ghz(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_sar_sml_has_6ghz *ptr = content; + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + + BUILD_BUG_ON(RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ != NUM_OF_RTW89_ACPI_SAR_SUBBAND); + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = rec->rfpath_to_antidx(path); + + ent->v[subband][path] = rec->normalize(ptr->v[antidx][subband]); + } + } +} + +static s16 rtw89_acpi_geo_sar_normalize_delta(s8 delta) +{ + static const u8 fct = 1; + + BUILD_BUG_ON(fct > TXPWR_FACTOR_OF_RTW89_ACPI_SAR); + + return delta << (TXPWR_FACTOR_OF_RTW89_ACPI_SAR - fct); +} + +static enum rtw89_acpi_geo_sar_regd_hp +rtw89_acpi_geo_sar_regd_convert_hp_idx(enum rtw89_regulation_type regd) +{ + switch (regd) { + case RTW89_FCC: + case RTW89_IC: + case RTW89_NCC: + case RTW89_CHILE: + case RTW89_MEXICO: + return RTW89_ACPI_GEO_SAR_REGD_HP_FCC; + case RTW89_ETSI: + case RTW89_MKK: + case RTW89_ACMA: + return RTW89_ACPI_GEO_SAR_REGD_HP_ETSI; + default: + case RTW89_WW: + case RTW89_NA: + case RTW89_KCC: + return RTW89_ACPI_GEO_SAR_REGD_HP_WW; + } +} + +static enum rtw89_acpi_geo_sar_regd_rt +rtw89_acpi_geo_sar_regd_convert_rt_idx(enum rtw89_regulation_type regd) +{ + switch (regd) { + case RTW89_FCC: + case RTW89_NCC: + case RTW89_CHILE: + case RTW89_MEXICO: + return RTW89_ACPI_GEO_SAR_REGD_RT_FCC; + case RTW89_ETSI: + case RTW89_ACMA: + return RTW89_ACPI_GEO_SAR_REGD_RT_ETSI; + case RTW89_MKK: + return RTW89_ACPI_GEO_SAR_REGD_RT_MKK; + case RTW89_IC: + return RTW89_ACPI_GEO_SAR_REGD_RT_IC; + case RTW89_KCC: + return RTW89_ACPI_GEO_SAR_REGD_RT_KCC; + default: + case RTW89_WW: + case RTW89_NA: + return RTW89_ACPI_GEO_SAR_REGD_RT_WW; + } +} + +static +void rtw89_acpi_geo_sar_load_by_hp(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_hp_val *ptr, + enum rtw89_rf_path path, s16 *val) +{ + u8 antidx = rtw89_acpi_sar_rfpath_to_hp_antidx(path); + s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta[antidx]); + s16 max = rtw89_acpi_sar_normalize_hp_val(ptr->max); + + *val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max); +} + +static +void rtw89_acpi_geo_sar_load_by_rt(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_rt_val *ptr, + s16 *val) +{ + s16 delta = rtw89_acpi_geo_sar_normalize_delta(ptr->delta); + s16 max = rtw89_acpi_sar_normalize_rt_val(ptr->max); + + *val = clamp_t(s32, (*val) + delta, MIN_VAL_OF_RTW89_ACPI_SAR, max); +} + +static +void rtw89_acpi_geo_sar_load_hp_legacy(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_hp_legacy *ptr = content; + const struct rtw89_acpi_geo_sar_hp_legacy_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_hp geo_idx = + rtw89_acpi_geo_sar_regd_convert_hp_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + default: + case RTW89_BAND_6G: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_hp_has_6ghz(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_hp_has_6ghz *ptr = content; + const struct rtw89_acpi_geo_sar_hp_has_6ghz_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_hp_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_hp geo_idx = + rtw89_acpi_geo_sar_regd_convert_hp_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + case RTW89_BAND_6G: + ptr_ent_val = &ptr_ent->val_6ghz; + break; + default: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_hp(rtwdev, ptr_ent_val, path, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_rt_legacy(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_rt_legacy *ptr = content; + const struct rtw89_acpi_geo_sar_rt_legacy_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_rt geo_idx = + rtw89_acpi_geo_sar_regd_convert_rt_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + default: + case RTW89_BAND_6G: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val, + &ent->v[subband][path]); + } +} + +static +void rtw89_acpi_geo_sar_load_rt_has_6ghz(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent) +{ + const struct rtw89_acpi_geo_sar_rt_has_6ghz *ptr = content; + const struct rtw89_acpi_geo_sar_rt_has_6ghz_entry *ptr_ent; + const struct rtw89_acpi_geo_sar_rt_val *ptr_ent_val; + enum rtw89_acpi_geo_sar_regd_rt geo_idx = + rtw89_acpi_geo_sar_regd_convert_rt_idx(regd); + enum rtw89_acpi_sar_subband subband; + enum rtw89_rf_path path; + enum rtw89_band band; + + ptr_ent = &ptr->entries[geo_idx]; + + for (subband = 0; subband < NUM_OF_RTW89_ACPI_SAR_SUBBAND; subband++) { + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband); + switch (band) { + case RTW89_BAND_2G: + ptr_ent_val = &ptr_ent->val_2ghz; + break; + case RTW89_BAND_5G: + ptr_ent_val = &ptr_ent->val_5ghz; + break; + case RTW89_BAND_6G: + ptr_ent_val = &ptr_ent->val_6ghz; + break; + default: + ptr_ent_val = NULL; + break; + } + + if (!ptr_ent_val) + continue; + + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) + rtw89_acpi_geo_sar_load_by_rt(rtwdev, ptr_ent_val, + &ent->v[subband][path]); + } +} + +#define RTW89_ACPI_GEO_SAR_DECL_HANDLER(type) \ +static const struct rtw89_acpi_geo_sar_handler \ +rtw89_acpi_geo_sar_handler_ ## type = { \ + .data_size = RTW89_ACPI_GEO_SAR_SIZE_OF(type), \ + .load = rtw89_acpi_geo_sar_load_ ## type, \ +} + +RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_legacy); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(hp_has_6ghz); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_legacy); +RTW89_ACPI_GEO_SAR_DECL_HANDLER(rt_has_6ghz); + +static const struct rtw89_acpi_sar_recognition rtw89_acpi_sar_recs[] = { + { + .id = { + .cid = RTW89_ACPI_SAR_CID_HP, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), + }, + .geo = &rtw89_acpi_geo_sar_handler_hp_legacy, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, + .normalize = rtw89_acpi_sar_normalize_hp_val, + .load = rtw89_acpi_sar_load_std_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_HP, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), + }, + .geo = &rtw89_acpi_geo_sar_handler_hp_has_6ghz, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_hp_antidx, + .normalize = rtw89_acpi_sar_normalize_hp_val, + .load = rtw89_acpi_sar_load_std_has_6ghz, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(std_legacy), + }, + .geo = &rtw89_acpi_geo_sar_handler_rt_legacy, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_std_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(std_has_6ghz), + }, + .geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_std_has_6ghz, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_LEGACY, + .size = RTW89_ACPI_SAR_SIZE_OF(sml_legacy), + }, + .geo = &rtw89_acpi_geo_sar_handler_rt_legacy, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_sml_legacy, + }, + { + .id = { + .cid = RTW89_ACPI_SAR_CID_RT, + .rev = RTW89_ACPI_SAR_REV_HAS_6GHZ, + .size = RTW89_ACPI_SAR_SIZE_OF(sml_has_6ghz), + }, + .geo = &rtw89_acpi_geo_sar_handler_rt_has_6ghz, + + .rfpath_to_antidx = rtw89_acpi_sar_rfpath_to_rt_antidx, + .normalize = rtw89_acpi_sar_normalize_rt_val, + .load = rtw89_acpi_sar_load_sml_has_6ghz, + }, +}; + +struct rtw89_acpi_sar_rec_parm { + u32 pld_len; + u8 tbl_cnt; + u16 cid; + u8 rev; +}; + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_sar_recognize(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_rec_parm *parm) +{ + const u32 tbl_len = parm->pld_len / parm->tbl_cnt; + const struct rtw89_acpi_sar_recognition *rec; + struct rtw89_acpi_sar_identifier id = {}; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, + "%s: cid %u, rev %u, tbl len %u, tbl cnt %u\n", + __func__, parm->cid, parm->rev, tbl_len, parm->tbl_cnt); + + if (unlikely(parm->pld_len % parm->tbl_cnt)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid pld len %u\n", + parm->pld_len); + return NULL; + } + + if (unlikely(tbl_len > RTW89_ACPI_SAR_SIZE_MAX)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl len %u\n", + tbl_len); + return NULL; + } + + if (unlikely(parm->tbl_cnt > MAX_NUM_OF_RTW89_ACPI_SAR_TBL)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid tbl cnt %u\n", + parm->tbl_cnt); + return NULL; + } + + switch (parm->cid) { + case RTW89_ACPI_SAR_CID_HP: + case RTW89_ACPI_SAR_CID_RT: + id.cid = parm->cid; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid cid 0x%x\n", + parm->cid); + return NULL; + } + + switch (parm->rev) { + case RTW89_ACPI_SAR_REV_LEGACY: + case RTW89_ACPI_SAR_REV_HAS_6GHZ: + id.rev = parm->rev; + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid rev %u\n", + parm->rev); + return NULL; + } + + id.size = tbl_len; + for (unsigned int i = 0; i < ARRAY_SIZE(rtw89_acpi_sar_recs); i++) { + rec = &rtw89_acpi_sar_recs[i]; + if (memcmp(&rec->id, &id, sizeof(rec->id)) == 0) + return rec; + } + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "failed to recognize\n"); + return NULL; +} + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_evaluate_static_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_sar_recognition *rec = NULL; + const struct rtw89_acpi_static_sar_hdr *hdr; + struct rtw89_sar_entry_from_acpi tmp = {}; + struct rtw89_acpi_sar_rec_parm parm = {}; + struct rtw89_sar_table_from_acpi *tbl; + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_STATIC_SAR); + if (!data) + return NULL; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load static sar\n"); + + len = data->len; + if (len <= sizeof(*hdr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); goto out; } - buf_len = obj->buffer.length; - if (buf_len != sizeof(*res)) { - rtw89_debug(rtwdev, RTW89_DBG_ACPI, "%s: invalid buffer length: %u\n", - __func__, buf_len); + hdr = (typeof(hdr))data->buf; + + parm.cid = le16_to_cpu(hdr->cid); + parm.rev = hdr->rev; + parm.tbl_cnt = 1; + parm.pld_len = len - sizeof(*hdr); + + rec = rtw89_acpi_sar_recognize(rtwdev, &parm); + if (!rec) + goto out; + + rec->load(rtwdev, rec, hdr->content, &tmp); + + tbl = &cfg->tables[0]; + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + tbl->entries[regd] = tmp; + + cfg->valid_num = 1; + +out: + kfree(data); + return rec; +} + +static const struct rtw89_acpi_sar_recognition * +rtw89_acpi_evaluate_dynamic_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_sar_recognition *rec = NULL; + const struct rtw89_acpi_dynamic_sar_hdr *hdr; + struct rtw89_acpi_sar_rec_parm parm = {}; + struct rtw89_sar_table_from_acpi *tbl; + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR); + if (!data) + return NULL; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar\n"); + + len = data->len; + if (len <= sizeof(*hdr)) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); + goto out; + } + + hdr = (typeof(hdr))data->buf; + + parm.cid = le16_to_cpu(hdr->cid); + parm.rev = hdr->rev; + parm.tbl_cnt = hdr->cnt; + parm.pld_len = len - sizeof(*hdr); + + rec = rtw89_acpi_sar_recognize(rtwdev, &parm); + if (!rec) + goto out; + + for (unsigned int i = 0; i < hdr->cnt; i++) { + const u8 *content = hdr->content + rec->id.size * i; + struct rtw89_sar_entry_from_acpi tmp = {}; + + rec->load(rtwdev, rec, content, &tmp); + + tbl = &cfg->tables[i]; + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + tbl->entries[regd] = tmp; + } + + cfg->valid_num = hdr->cnt; + +out: + kfree(data); + return rec; +} + +int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg, + bool *poll_changed) +{ + struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; + struct rtw89_sar_indicator_from_acpi tmp = *ind; + const struct rtw89_acpi_data *data; + const u8 *tbl_base1_by_ant; + enum rtw89_rf_path path; + int ret = 0; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR); + if (!data) + return -EFAULT; + + if (!poll_changed) + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load dynamic sar indicator\n"); + + len = data->len; + if (len != ind->fields) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u\n", len); ret = -EINVAL; goto out; } - *res = *(struct rtw89_acpi_rtag_result *)obj->buffer.pointer; + tbl_base1_by_ant = data->buf; - rtw89_hex_dump(rtwdev, RTW89_DBG_ACPI, "antenna_gain: ", res, sizeof(*res)); + for (path = 0; path < NUM_OF_RTW89_ACPI_SAR_RF_PATH; path++) { + u8 antidx = ind->rfpath_to_antidx(path); + u8 sel; + + if (antidx >= ind->fields) + antidx = 0; + + /* convert the table index from 1-based to 0-based */ + sel = tbl_base1_by_ant[antidx] - 1; + if (sel >= cfg->valid_num) + sel = 0; + + tmp.tblsel[path] = sel; + } + + if (memcmp(ind, &tmp, sizeof(*ind)) == 0) { + if (poll_changed) + *poll_changed = false; + } else { + if (poll_changed) + *poll_changed = true; + + *ind = tmp; + } out: - ACPI_FREE(obj); + kfree(data); return ret; } + +static +void rtw89_acpi_evaluate_geo_sar(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_geo_sar_handler *hdl, + struct rtw89_sar_cfg_acpi *cfg) +{ + const struct rtw89_acpi_data *data; + u32 len; + + data = rtw89_acpi_evaluate_method(rtwdev, RTW89_ACPI_METHOD_GEO_SAR); + if (!data) + return; + + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "acpi load geo sar\n"); + + len = data->len; + if (len != hdl->data_size) { + rtw89_debug(rtwdev, RTW89_DBG_ACPI, "invalid buf len %u (expected %u)\n", + len, hdl->data_size); + goto out; + } + + for (unsigned int i = 0; i < cfg->valid_num; i++) + for (u8 regd = 0; regd < RTW89_REGD_NUM; regd++) + hdl->load(rtwdev, data->buf, regd, &cfg->tables[i].entries[regd]); + +out: + kfree(data); +} + +int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg) +{ + struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; + const struct rtw89_acpi_sar_recognition *rec; + bool fetch_indicator = false; + int ret; + + rec = rtw89_acpi_evaluate_static_sar(rtwdev, cfg); + if (rec) + goto recognized; + + rec = rtw89_acpi_evaluate_dynamic_sar(rtwdev, cfg); + if (!rec) + return -ENOENT; + + fetch_indicator = true; + +recognized: + rtw89_acpi_evaluate_geo_sar(rtwdev, rec->geo, cfg); + + switch (rec->id.cid) { + case RTW89_ACPI_SAR_CID_HP: + cfg->downgrade_2tx = 3 << TXPWR_FACTOR_OF_RTW89_ACPI_SAR; + ind->fields = RTW89_ACPI_SAR_ANT_NR_STD; + break; + case RTW89_ACPI_SAR_CID_RT: + cfg->downgrade_2tx = 0; + ind->fields = 1; + break; + default: + return -EFAULT; + } + + if (fetch_indicator) { + ind->rfpath_to_antidx = rec->rfpath_to_antidx; + ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, NULL); + if (ret) + fetch_indicator = false; + } + + if (!fetch_indicator) + memset(ind->tblsel, 0, sizeof(ind->tblsel)); + + ind->enable_sync = fetch_indicator; + return 0; +} diff --git a/drivers/net/wireless/realtek/rtw89/acpi.h b/drivers/net/wireless/realtek/rtw89/acpi.h index b43ab106e44d..8c918ee02d2e 100644 --- a/drivers/net/wireless/realtek/rtw89/acpi.h +++ b/drivers/net/wireless/realtek/rtw89/acpi.h @@ -7,6 +7,11 @@ #include "core.h" +struct rtw89_acpi_data { + u32 len; + u8 buf[] __counted_by(len); +}; + enum rtw89_acpi_dsm_func { RTW89_ACPI_DSM_FUNC_IDN_BAND_SUP = 2, RTW89_ACPI_DSM_FUNC_6G_DIS = 3, @@ -26,6 +31,13 @@ enum rtw89_acpi_policy_mode { RTW89_ACPI_POLICY_ALLOW = 1, }; +enum rtw89_acpi_conf_tas { + RTW89_ACPI_CONF_TAS_US = BIT(0), + RTW89_ACPI_CONF_TAS_CA = BIT(1), + RTW89_ACPI_CONF_TAS_KR = BIT(2), + RTW89_ACPI_CONF_TAS_OTHERS = BIT(7), +}; + struct rtw89_acpi_country_code { /* below are allowed: * * ISO alpha2 country code @@ -54,12 +66,21 @@ struct rtw89_acpi_policy_6ghz_sp { u8 rsvd; } __packed; +struct rtw89_acpi_policy_tas { + u8 signature[4]; + u8 revision; + u8 enable; + u8 enabled_countries; + u8 rsvd[3]; +} __packed; + struct rtw89_acpi_dsm_result { union { u8 value; /* caller needs to free it after using */ struct rtw89_acpi_policy_6ghz *policy_6ghz; struct rtw89_acpi_policy_6ghz_sp *policy_6ghz_sp; + struct rtw89_acpi_policy_tas *policy_tas; } u; }; @@ -70,10 +91,179 @@ struct rtw89_acpi_rtag_result { u8 ant_gain_table[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR]; } __packed; +enum rtw89_acpi_sar_cid { + RTW89_ACPI_SAR_CID_HP = 0x5048, + RTW89_ACPI_SAR_CID_RT = 0x5452, +}; + +enum rtw89_acpi_sar_rev { + RTW89_ACPI_SAR_REV_LEGACY = 1, + RTW89_ACPI_SAR_REV_HAS_6GHZ = 2, +}; + +#define RTW89_ACPI_SAR_ANT_NR_STD 4 +#define RTW89_ACPI_SAR_ANT_NR_SML 2 + +#define RTW89_ACPI_METHOD_STATIC_SAR "WRDS" +#define RTW89_ACPI_METHOD_DYNAMIC_SAR "RWRD" +#define RTW89_ACPI_METHOD_DYNAMIC_SAR_INDICATOR "RWSI" +#define RTW89_ACPI_METHOD_GEO_SAR "RWGS" + +struct rtw89_acpi_sar_std_legacy { + u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY]; +} __packed; + +struct rtw89_acpi_sar_std_has_6ghz { + u8 v[RTW89_ACPI_SAR_ANT_NR_STD][RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ]; +} __packed; + +struct rtw89_acpi_sar_sml_legacy { + u8 v[RTW89_ACPI_SAR_ANT_NR_SML][RTW89_ACPI_SAR_SUBBAND_NR_LEGACY]; +} __packed; + +struct rtw89_acpi_sar_sml_has_6ghz { + u8 v[RTW89_ACPI_SAR_ANT_NR_SML][RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ]; +} __packed; + +struct rtw89_acpi_static_sar_hdr { + __le16 cid; + u8 rev; + u8 content[]; +} __packed; + +struct rtw89_acpi_dynamic_sar_hdr { + __le16 cid; + u8 rev; + u8 cnt; + u8 content[]; +} __packed; + +struct rtw89_acpi_sar_identifier { + enum rtw89_acpi_sar_cid cid; + enum rtw89_acpi_sar_rev rev; + u8 size; +}; + +/* for rtw89_acpi_sar_identifier::size */ +#define RTW89_ACPI_SAR_SIZE_MAX U8_MAX +#define RTW89_ACPI_SAR_SIZE_OF(type) \ + (BUILD_BUG_ON_ZERO(sizeof(struct rtw89_acpi_sar_ ## type) > \ + RTW89_ACPI_SAR_SIZE_MAX) + \ + sizeof(struct rtw89_acpi_sar_ ## type)) + +struct rtw89_acpi_sar_recognition { + struct rtw89_acpi_sar_identifier id; + const struct rtw89_acpi_geo_sar_handler *geo; + + u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath); + s16 (*normalize)(u8 v); + void (*load)(struct rtw89_dev *rtwdev, + const struct rtw89_acpi_sar_recognition *rec, + const void *content, + struct rtw89_sar_entry_from_acpi *ent); +}; + +struct rtw89_acpi_geo_sar_hp_val { + u8 max; + s8 delta[RTW89_ACPI_SAR_ANT_NR_STD]; +} __packed; + +struct rtw89_acpi_geo_sar_hp_legacy_entry { + struct rtw89_acpi_geo_sar_hp_val val_2ghz; + struct rtw89_acpi_geo_sar_hp_val val_5ghz; +} __packed; + +struct rtw89_acpi_geo_sar_hp_has_6ghz_entry { + struct rtw89_acpi_geo_sar_hp_val val_2ghz; + struct rtw89_acpi_geo_sar_hp_val val_5ghz; + struct rtw89_acpi_geo_sar_hp_val val_6ghz; +} __packed; + +enum rtw89_acpi_geo_sar_regd_hp { + RTW89_ACPI_GEO_SAR_REGD_HP_FCC = 0, + RTW89_ACPI_GEO_SAR_REGD_HP_ETSI = 1, + RTW89_ACPI_GEO_SAR_REGD_HP_WW = 2, + + RTW89_ACPI_GEO_SAR_REGD_NR_HP, +}; + +struct rtw89_acpi_geo_sar_hp_legacy { + struct rtw89_acpi_geo_sar_hp_legacy_entry + entries[RTW89_ACPI_GEO_SAR_REGD_NR_HP]; +} __packed; + +struct rtw89_acpi_geo_sar_hp_has_6ghz { + struct rtw89_acpi_geo_sar_hp_has_6ghz_entry + entries[RTW89_ACPI_GEO_SAR_REGD_NR_HP]; +} __packed; + +struct rtw89_acpi_geo_sar_rt_val { + u8 max; + s8 delta; +} __packed; + +struct rtw89_acpi_geo_sar_rt_legacy_entry { + struct rtw89_acpi_geo_sar_rt_val val_2ghz; + struct rtw89_acpi_geo_sar_rt_val val_5ghz; +} __packed; + +struct rtw89_acpi_geo_sar_rt_has_6ghz_entry { + struct rtw89_acpi_geo_sar_rt_val val_2ghz; + struct rtw89_acpi_geo_sar_rt_val val_5ghz; + struct rtw89_acpi_geo_sar_rt_val val_6ghz; +} __packed; + +enum rtw89_acpi_geo_sar_regd_rt { + RTW89_ACPI_GEO_SAR_REGD_RT_FCC = 0, + RTW89_ACPI_GEO_SAR_REGD_RT_ETSI = 1, + RTW89_ACPI_GEO_SAR_REGD_RT_MKK = 2, + RTW89_ACPI_GEO_SAR_REGD_RT_IC = 3, + RTW89_ACPI_GEO_SAR_REGD_RT_KCC = 4, + RTW89_ACPI_GEO_SAR_REGD_RT_WW = 5, + + RTW89_ACPI_GEO_SAR_REGD_NR_RT, +}; + +struct rtw89_acpi_geo_sar_rt_legacy { + struct rtw89_acpi_geo_sar_rt_legacy_entry + entries[RTW89_ACPI_GEO_SAR_REGD_NR_RT]; +} __packed; + +struct rtw89_acpi_geo_sar_rt_has_6ghz { + struct rtw89_acpi_geo_sar_rt_has_6ghz_entry + entries[RTW89_ACPI_GEO_SAR_REGD_NR_RT]; +} __packed; + +struct rtw89_acpi_geo_sar_handler { + u8 data_size; + + void (*load)(struct rtw89_dev *rtwdev, + const void *content, + enum rtw89_regulation_type regd, + struct rtw89_sar_entry_from_acpi *ent); +}; + +/* for rtw89_acpi_geo_sar_handler::data_size */ +#define RTW89_ACPI_GEO_SAR_SIZE_MAX U8_MAX +#define RTW89_ACPI_GEO_SAR_SIZE_OF(type) \ + (BUILD_BUG_ON_ZERO(sizeof(struct rtw89_acpi_geo_sar_ ## type) > \ + RTW89_ACPI_GEO_SAR_SIZE_MAX) + \ + sizeof(struct rtw89_acpi_geo_sar_ ## type)) + +enum rtw89_acpi_sar_subband rtw89_acpi_sar_get_subband(struct rtw89_dev *rtwdev, + u32 center_freq); +enum rtw89_band rtw89_acpi_sar_subband_to_band(struct rtw89_dev *rtwdev, + enum rtw89_acpi_sar_subband subband); + int rtw89_acpi_evaluate_dsm(struct rtw89_dev *rtwdev, enum rtw89_acpi_dsm_func func, struct rtw89_acpi_dsm_result *res); int rtw89_acpi_evaluate_rtag(struct rtw89_dev *rtwdev, struct rtw89_acpi_rtag_result *res); +int rtw89_acpi_evaluate_sar(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg); +int rtw89_acpi_evaluate_dynamic_sar_indicator(struct rtw89_dev *rtwdev, + struct rtw89_sar_cfg_acpi *cfg, + bool *changed); #endif diff --git a/drivers/net/wireless/realtek/rtw89/cam.c b/drivers/net/wireless/realtek/rtw89/cam.c index 8fa1e6c1ce13..385a238fe5cc 100644 --- a/drivers/net/wireless/realtek/rtw89/cam.c +++ b/drivers/net/wireless/realtek/rtw89/cam.c @@ -6,6 +6,7 @@ #include "debug.h" #include "fw.h" #include "mac.h" +#include "ps.h" static struct sk_buff * rtw89_cam_get_sec_key_cmd(struct rtw89_dev *rtwdev, @@ -469,13 +470,25 @@ int rtw89_cam_sec_key_add(struct rtw89_dev *rtwdev, bool ext_key = false; int ret; + if (ieee80211_vif_is_mld(vif) && !chip->hw_mlo_bmc_crypto && + !(key->flags & IEEE80211_KEY_FLAG_PAIRWISE)) + return -EOPNOTSUPP; + switch (key->cipher) { case WLAN_CIPHER_SUITE_WEP40: + rtw89_leave_ips_by_hwflags(rtwdev); hw_key_type = RTW89_SEC_KEY_TYPE_WEP40; break; case WLAN_CIPHER_SUITE_WEP104: + rtw89_leave_ips_by_hwflags(rtwdev); hw_key_type = RTW89_SEC_KEY_TYPE_WEP104; break; + case WLAN_CIPHER_SUITE_TKIP: + if (!chip->hw_tkip_crypto) + return -EOPNOTSUPP; + hw_key_type = RTW89_SEC_KEY_TYPE_TKIP; + key->flags |= IEEE80211_KEY_FLAG_GENERATE_MMIC; + break; case WLAN_CIPHER_SUITE_CCMP: hw_key_type = RTW89_SEC_KEY_TYPE_CCMP128; if (!chip->hw_mgmt_tx_encrypt) diff --git a/drivers/net/wireless/realtek/rtw89/chan.c b/drivers/net/wireless/realtek/rtw89/chan.c index 4df4e04c3e67..806f42429a29 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.c +++ b/drivers/net/wireless/realtek/rtw89/chan.c @@ -8,6 +8,7 @@ #include "fw.h" #include "mac.h" #include "ps.h" +#include "sar.h" #include "util.h" static void rtw89_swap_chanctx(struct rtw89_dev *rtwdev, @@ -155,7 +156,7 @@ int rtw89_iterate_entity_chan(struct rtw89_dev *rtwdev, int ret; u8 idx; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); for_each_set_bit(idx, hal->entity_map, NUM_OF_RTW89_CHANCTX) { chan = rtw89_chan_get(rtwdev, idx); @@ -188,9 +189,10 @@ void rtw89_config_entity_chandef(struct rtw89_dev *rtwdev, } void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev, - enum rtw89_chanctx_idx idx, + struct rtw89_vif_link *rtwvif_link, const struct cfg80211_chan_def *chandef) { + enum rtw89_chanctx_idx idx = rtwvif_link->chanctx_idx; struct rtw89_hal *hal = &rtwdev->hal; enum rtw89_chanctx_idx cur; @@ -204,6 +206,7 @@ void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev, } hal->roc_chandef = *chandef; + hal->roc_link_index = rtw89_vif_link_inst_get_index(rtwvif_link); } else { cur = atomic_cmpxchg(&hal->roc_chanctx_idx, idx, RTW89_CHANCTX_IDLE); @@ -310,7 +313,7 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, enum rtw89_entity_mode mode; u8 role_index; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (unlikely(link_index >= __RTW89_MLD_MAX_LINK_NUM)) { WARN(1, "link index %u is invalid (max link inst num: %d)\n", @@ -338,11 +341,10 @@ const struct rtw89_chan *__rtw89_mgnt_chan_get(struct rtw89_dev *rtwdev, roc_idx = atomic_read(&hal->roc_chanctx_idx); if (roc_idx != RTW89_CHANCTX_IDLE) { - /* ROC is ongoing (given ROC runs on RTW89_ROC_BY_LINK_INDEX). - * If @link_index is the same as RTW89_ROC_BY_LINK_INDEX, get - * the ongoing ROC chanctx. + /* ROC is ongoing (given ROC runs on @hal->roc_link_index). + * If @link_index is the same, get the ongoing ROC chanctx. */ - if (link_index == RTW89_ROC_BY_LINK_INDEX) + if (link_index == hal->roc_link_index) chanctx_idx = roc_idx; } @@ -357,16 +359,45 @@ dflt: } EXPORT_SYMBOL(__rtw89_mgnt_chan_get); +static enum rtw89_mlo_dbcc_mode +rtw89_entity_sel_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) +{ + if (rtwdev->chip->chip_gen != RTW89_CHIP_BE) + return MLO_DBCC_NOT_SUPPORT; + + switch (active_hws) { + case BIT(0): + return MLO_2_PLUS_0_1RF; + case BIT(1): + return MLO_0_PLUS_2_1RF; + case BIT(0) | BIT(1): + default: + return MLO_1_PLUS_1_1RF; + } +} + +static +void rtw89_entity_recalc_mlo_dbcc_mode(struct rtw89_dev *rtwdev, u8 active_hws) +{ + enum rtw89_mlo_dbcc_mode mode; + + mode = rtw89_entity_sel_mlo_dbcc_mode(rtwdev, active_hws); + rtwdev->mlo_dbcc_mode = mode; + + rtw89_debug(rtwdev, RTW89_DBG_STATE, "recalc mlo dbcc mode to %d\n", mode); +} + static void rtw89_entity_recalc_mgnt_roles(struct rtw89_dev *rtwdev) { struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; struct rtw89_vif_link *link; struct rtw89_vif *role; + u8 active_hws = 0; u8 pos = 0; int i, j; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); for (i = 0; i < RTW89_MAX_INTERFACE_NUM; i++) mgnt->active_roles[i] = NULL; @@ -411,10 +442,13 @@ fill: continue; mgnt->chanctx_tbl[pos][i] = link->chanctx_idx; + active_hws |= BIT(i); } mgnt->active_roles[pos++] = role; } + + rtw89_entity_recalc_mlo_dbcc_mode(rtwdev, active_hws); } enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) @@ -427,7 +461,7 @@ enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev) struct rtw89_chan chan; u8 idx; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); bitmap_copy(recalc_map, hal->entity_map, NUM_OF_RTW89_CHANCTX); @@ -556,7 +590,9 @@ static u32 rtw89_mcc_get_tbtt_ofst(struct rtw89_dev *rtwdev, u64 sync_tsf = READ_ONCE(rtwvif_link->sync_bcn_tsf); u32 remainder; - if (tsf < sync_tsf) { + if (role->is_go) { + sync_tsf = 0; + } else if (tsf < sync_tsf) { rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC get tbtt ofst: tsf might not update yet\n"); sync_tsf = 0; @@ -690,19 +726,13 @@ static void rtw89_mcc_role_macid_sta_iter(void *data, struct ieee80211_sta *sta) struct rtw89_vif *target = mcc_role->rtwvif_link->rtwvif; struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); struct rtw89_vif *rtwvif = rtwsta->rtwvif; - struct rtw89_dev *rtwdev = rtwsta->rtwdev; - struct rtw89_sta_link *rtwsta_link; + u8 macid; if (rtwvif != target) return; - rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); - if (unlikely(!rtwsta_link)) { - rtw89_err(rtwdev, "mcc sta macid: find no link on HW-0\n"); - return; - } - - rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, rtwsta_link->mac_id); + macid = rtw89_sta_get_main_macid(rtwsta); + rtw89_mcc_role_fw_macid_bitmap_set_bit(mcc_role, macid); } static void rtw89_mcc_fill_role_macid_bitmap(struct rtw89_dev *rtwdev, @@ -746,9 +776,11 @@ static void rtw89_mcc_fill_role_limit(struct rtw89_dev *rtwdev, int ret; int i; - if (!mcc_role->is_go && !mcc_role->is_gc) + if (!mcc_role->is_gc) return; + rtw89_p2p_noa_once_recalc(rtwvif_link); + rcu_read_lock(); bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); @@ -784,6 +816,9 @@ fill: } tsf_lmt = (tsf & GENMASK_ULL(63, 32)) | start_time; + if (tsf_lmt < tsf) + tsf_lmt += roundup_u64(tsf - tsf_lmt, interval); + max_toa_us = rtw89_mcc_get_tbtt_ofst(rtwdev, mcc_role, tsf_lmt); max_dur_us = interval - duration; max_tob_us = max_dur_us - max_toa_us; @@ -928,6 +963,15 @@ static int rtw89_mcc_fill_all_roles(struct rtw89_dev *rtwdev) return 0; } +static bool rtw89_mcc_can_courtesy(const struct rtw89_mcc_role *provider, + const struct rtw89_mcc_role *receiver) +{ + if (provider->is_go || receiver->is_gc) + return false; + + return true; +} + static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev, const struct rtw89_mcc_pattern *new) { @@ -936,37 +980,44 @@ static void rtw89_mcc_assign_pattern(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *aux = &mcc->role_aux; struct rtw89_mcc_config *config = &mcc->config; struct rtw89_mcc_pattern *pattern = &config->pattern; + struct rtw89_mcc_courtesy_cfg *crtz; rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC assign pattern: ref {%d | %d}, aux {%d | %d}\n", new->tob_ref, new->toa_ref, new->tob_aux, new->toa_aux); + rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC pattern plan: %d\n", new->plan); + *pattern = *new; memset(&pattern->courtesy, 0, sizeof(pattern->courtesy)); - if (pattern->tob_aux <= 0 || pattern->toa_aux <= 0) { - pattern->courtesy.macid_tgt = aux->rtwvif_link->mac_id; - pattern->courtesy.macid_src = ref->rtwvif_link->mac_id; - pattern->courtesy.slot_num = RTW89_MCC_DFLT_COURTESY_SLOT; - pattern->courtesy.enable = true; - } else if (pattern->tob_ref <= 0 || pattern->toa_ref <= 0) { - pattern->courtesy.macid_tgt = ref->rtwvif_link->mac_id; - pattern->courtesy.macid_src = aux->rtwvif_link->mac_id; - pattern->courtesy.slot_num = RTW89_MCC_DFLT_COURTESY_SLOT; - pattern->courtesy.enable = true; + if (RTW89_MCC_REQ_COURTESY(pattern, aux) && rtw89_mcc_can_courtesy(ref, aux)) { + crtz = &pattern->courtesy.ref; + ref->crtz = crtz; + + crtz->macid_tgt = aux->rtwvif_link->mac_id; + crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT; + + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MCC courtesy ref: tgt %d, slot %d\n", + crtz->macid_tgt, crtz->slot_num); + } else { + ref->crtz = NULL; } - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "MCC pattern flags: plan %d, courtesy_en %d\n", - pattern->plan, pattern->courtesy.enable); + if (RTW89_MCC_REQ_COURTESY(pattern, ref) && rtw89_mcc_can_courtesy(aux, ref)) { + crtz = &pattern->courtesy.aux; + aux->crtz = crtz; - if (!pattern->courtesy.enable) - return; + crtz->macid_tgt = ref->rtwvif_link->mac_id; + crtz->slot_num = RTW89_MCC_DFLT_COURTESY_SLOT; - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "MCC pattern courtesy: tgt %d, src %d, slot %d\n", - pattern->courtesy.macid_tgt, pattern->courtesy.macid_src, - pattern->courtesy.slot_num); + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MCC courtesy aux: tgt %d, slot %d\n", + crtz->macid_tgt, crtz->slot_num); + } else { + aux->crtz = NULL; + } } /* The follow-up roughly shows the relationship between the parameters @@ -991,6 +1042,7 @@ static void __rtw89_mcc_calc_pattern_loose(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *ref = &mcc->role_ref; struct rtw89_mcc_role *aux = &mcc->role_aux; struct rtw89_mcc_config *config = &mcc->config; + u16 mcc_intvl = config->mcc_interval; u16 bcn_ofst = config->beacon_offset; u16 bt_dur_in_mid = 0; u16 max_bcn_ofst; @@ -1024,7 +1076,7 @@ calc: res = bcn_ofst - bt_dur_in_mid; upper = min_t(s16, ref->duration, res); - lower = 0; + lower = max_t(s16, 0, ref->duration - (mcc_intvl - bcn_ofst)); if (ref->limit.enable) { upper = min_t(s16, upper, ref->limit.max_toa); @@ -1135,6 +1187,107 @@ static int __rtw89_mcc_calc_pattern_strict(struct rtw89_dev *rtwdev, return 0; } +static void __rtw89_mcc_fill_ptrn_anchor_ref(struct rtw89_dev *rtwdev, + struct rtw89_mcc_pattern *ptrn, + bool small_bcn_ofst) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_mcc_config *config = &mcc->config; + u16 bcn_ofst = config->beacon_offset; + u16 ref_tob; + u16 ref_toa; + + if (ref->limit.enable) { + ref_tob = ref->limit.max_tob; + ref_toa = ref->limit.max_toa; + } else { + ref_tob = ref->duration / 2; + ref_toa = ref->duration / 2; + } + + if (small_bcn_ofst) { + ptrn->toa_ref = ref_toa; + ptrn->tob_ref = ref->duration - ptrn->toa_ref; + } else { + ptrn->tob_ref = ref_tob; + ptrn->toa_ref = ref->duration - ptrn->tob_ref; + } + + ptrn->tob_aux = bcn_ofst - ptrn->toa_ref; + ptrn->toa_aux = aux->duration - ptrn->tob_aux; +} + +static void __rtw89_mcc_fill_ptrn_anchor_aux(struct rtw89_dev *rtwdev, + struct rtw89_mcc_pattern *ptrn, + bool small_bcn_ofst) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_mcc_config *config = &mcc->config; + u16 bcn_ofst = config->beacon_offset; + u16 aux_tob; + u16 aux_toa; + + if (aux->limit.enable) { + aux_tob = aux->limit.max_tob; + aux_toa = aux->limit.max_toa; + } else { + aux_tob = aux->duration / 2; + aux_toa = aux->duration / 2; + } + + if (small_bcn_ofst) { + ptrn->tob_aux = aux_tob; + ptrn->toa_aux = aux->duration - ptrn->tob_aux; + } else { + ptrn->toa_aux = aux_toa; + ptrn->tob_aux = aux->duration - ptrn->toa_aux; + } + + ptrn->toa_ref = bcn_ofst - ptrn->tob_aux; + ptrn->tob_ref = ref->duration - ptrn->toa_ref; +} + +static int __rtw89_mcc_calc_pattern_anchor(struct rtw89_dev *rtwdev, + struct rtw89_mcc_pattern *ptrn, + bool hdl_bt) +{ + struct rtw89_mcc_info *mcc = &rtwdev->mcc; + struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; + struct rtw89_mcc_config *config = &mcc->config; + u16 mcc_intvl = config->mcc_interval; + u16 bcn_ofst = config->beacon_offset; + bool small_bcn_ofst; + + if (bcn_ofst < RTW89_MCC_MIN_RX_BCN_TIME) + small_bcn_ofst = true; + else if (mcc_intvl - bcn_ofst < RTW89_MCC_MIN_RX_BCN_TIME) + small_bcn_ofst = false; + else + return -EPERM; + + *ptrn = (typeof(*ptrn)){ + .plan = hdl_bt ? RTW89_MCC_PLAN_TAIL_BT : RTW89_MCC_PLAN_NO_BT, + }; + + rtw89_debug(rtwdev, RTW89_DBG_CHAN, + "MCC calc ptrn_ac: plan %d, bcn_ofst %d\n", + ptrn->plan, bcn_ofst); + + if (ref->is_go || ref->is_gc) + __rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst); + else if (aux->is_go || aux->is_gc) + __rtw89_mcc_fill_ptrn_anchor_aux(rtwdev, ptrn, small_bcn_ofst); + else + __rtw89_mcc_fill_ptrn_anchor_ref(rtwdev, ptrn, small_bcn_ofst); + + return 0; +} + static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; @@ -1188,6 +1341,10 @@ static int rtw89_mcc_calc_pattern(struct rtw89_dev *rtwdev, bool hdl_bt) goto done; } + ret = __rtw89_mcc_calc_pattern_anchor(rtwdev, &ptrn, hdl_bt); + if (!ret) + goto done; + __rtw89_mcc_calc_pattern_loose(rtwdev, &ptrn, hdl_bt); done: @@ -1438,88 +1595,41 @@ static bool rtw89_mcc_duration_decision_on_bt(struct rtw89_dev *rtwdev) return false; } -static void rtw89_mcc_sync_tbtt(struct rtw89_dev *rtwdev, - struct rtw89_mcc_role *tgt, - struct rtw89_mcc_role *src, - bool ref_is_src) -{ - struct rtw89_mcc_info *mcc = &rtwdev->mcc; - struct rtw89_mcc_config *config = &mcc->config; - u16 beacon_offset_us = ieee80211_tu_to_usec(config->beacon_offset); - u32 bcn_intvl_src_us = ieee80211_tu_to_usec(src->beacon_interval); - u32 cur_tbtt_ofst_src; - u32 tsf_ofst_tgt; - u32 remainder; - u64 tbtt_tgt; - u64 tsf_src; - int ret; - - ret = rtw89_mac_port_get_tsf(rtwdev, src->rtwvif_link, &tsf_src); - if (ret) { - rtw89_warn(rtwdev, "MCC failed to get port tsf: %d\n", ret); - return; - } - - cur_tbtt_ofst_src = rtw89_mcc_get_tbtt_ofst(rtwdev, src, tsf_src); - - if (ref_is_src) - tbtt_tgt = tsf_src - cur_tbtt_ofst_src + beacon_offset_us; - else - tbtt_tgt = tsf_src - cur_tbtt_ofst_src + - (bcn_intvl_src_us - beacon_offset_us); - - div_u64_rem(tbtt_tgt, bcn_intvl_src_us, &remainder); - tsf_ofst_tgt = bcn_intvl_src_us - remainder; - - config->sync.macid_tgt = tgt->rtwvif_link->mac_id; - config->sync.band_tgt = tgt->rtwvif_link->mac_idx; - config->sync.port_tgt = tgt->rtwvif_link->port; - config->sync.macid_src = src->rtwvif_link->mac_id; - config->sync.band_src = src->rtwvif_link->mac_idx; - config->sync.port_src = src->rtwvif_link->port; - config->sync.offset = tsf_ofst_tgt / 1024; - config->sync.enable = true; - - rtw89_debug(rtwdev, RTW89_DBG_CHAN, - "MCC sync tbtt: tgt %d, src %d, offset %d\n", - config->sync.macid_tgt, config->sync.macid_src, - config->sync.offset); - - rtw89_mac_port_tsf_sync(rtwdev, tgt->rtwvif_link, src->rtwvif_link, - config->sync.offset); -} - static int rtw89_mcc_fill_start_tsf(struct rtw89_dev *rtwdev) { struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_role *ref = &mcc->role_ref; + struct rtw89_mcc_role *aux = &mcc->role_aux; struct rtw89_mcc_config *config = &mcc->config; u32 bcn_intvl_ref_us = ieee80211_tu_to_usec(ref->beacon_interval); - u32 tob_ref_us = ieee80211_tu_to_usec(config->pattern.tob_ref); - struct rtw89_vif_link *rtwvif_link = ref->rtwvif_link; + s32 tob_ref_us = ieee80211_tu_to_usec(config->pattern.tob_ref); u64 tsf, start_tsf; u32 cur_tbtt_ofst; u64 min_time; + u64 tsf_aux; int ret; - ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf); - if (ret) { - rtw89_warn(rtwdev, "MCC failed to get port tsf: %d\n", ret); + if (rtw89_concurrent_via_mrc(rtwdev)) + ret = __mrc_fw_req_tsf(rtwdev, &tsf, &tsf_aux); + else + ret = __mcc_fw_req_tsf(rtwdev, &tsf, &tsf_aux); + + if (ret) return ret; - } min_time = tsf; - if (ref->is_go) + if (ref->is_go || aux->is_go) min_time += ieee80211_tu_to_usec(RTW89_MCC_SHORT_TRIGGER_TIME); else min_time += ieee80211_tu_to_usec(RTW89_MCC_LONG_TRIGGER_TIME); cur_tbtt_ofst = rtw89_mcc_get_tbtt_ofst(rtwdev, ref, tsf); start_tsf = tsf - cur_tbtt_ofst + bcn_intvl_ref_us - tob_ref_us; - while (start_tsf < min_time) - start_tsf += bcn_intvl_ref_us; + if (start_tsf < min_time) + start_tsf += roundup_u64(min_time - start_tsf, bcn_intvl_ref_us); config->start_tsf = start_tsf; + config->start_tsf_in_aux_domain = tsf_aux + start_tsf - tsf; return 0; } @@ -1536,13 +1646,11 @@ static int rtw89_mcc_fill_config(struct rtw89_dev *rtwdev) switch (mcc->mode) { case RTW89_MCC_MODE_GO_STA: - config->beacon_offset = RTW89_MCC_DFLT_BCN_OFST_TIME; + config->beacon_offset = rtw89_mcc_get_bcn_ofst(rtwdev); if (ref->is_go) { - rtw89_mcc_sync_tbtt(rtwdev, ref, aux, false); config->mcc_interval = ref->beacon_interval; rtw89_mcc_set_duration_go_sta(rtwdev, ref, aux); } else { - rtw89_mcc_sync_tbtt(rtwdev, aux, ref, true); config->mcc_interval = aux->beacon_interval; rtw89_mcc_set_duration_go_sta(rtwdev, aux, ref); } @@ -1572,10 +1680,8 @@ bottom: static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *role) { + const struct rtw89_mcc_courtesy_cfg *crtz = role->crtz; struct rtw89_mcc_info *mcc = &rtwdev->mcc; - struct rtw89_mcc_config *config = &mcc->config; - struct rtw89_mcc_pattern *pattern = &config->pattern; - struct rtw89_mcc_courtesy *courtesy = &pattern->courtesy; struct rtw89_mcc_policy *policy = &role->policy; struct rtw89_fw_mcc_add_req req = {}; const struct rtw89_chan *chan; @@ -1598,9 +1704,9 @@ static int __mcc_fw_add_role(struct rtw89_dev *rtwdev, struct rtw89_mcc_role *ro req.duration = role->duration; req.btc_in_2g = false; - if (courtesy->enable && courtesy->macid_src == req.macid) { - req.courtesy_target = courtesy->macid_tgt; - req.courtesy_num = courtesy->slot_num; + if (crtz) { + req.courtesy_target = crtz->macid_tgt; + req.courtesy_num = crtz->slot_num; req.courtesy_en = true; } @@ -1780,26 +1886,23 @@ static void __mrc_fw_add_courtesy(struct rtw89_dev *rtwdev, struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_role *ref = &mcc->role_ref; struct rtw89_mcc_role *aux = &mcc->role_aux; - struct rtw89_mcc_config *config = &mcc->config; - struct rtw89_mcc_pattern *pattern = &config->pattern; - struct rtw89_mcc_courtesy *courtesy = &pattern->courtesy; struct rtw89_fw_mrc_add_slot_arg *slot_arg_src; - u8 slot_idx_tgt; - if (!courtesy->enable) - return; - - if (courtesy->macid_src == ref->rtwvif_link->mac_id) { + if (ref->crtz) { slot_arg_src = &arg->slots[ref->slot_idx]; - slot_idx_tgt = aux->slot_idx; - } else { - slot_arg_src = &arg->slots[aux->slot_idx]; - slot_idx_tgt = ref->slot_idx; + + slot_arg_src->courtesy_target = aux->slot_idx; + slot_arg_src->courtesy_period = ref->crtz->slot_num; + slot_arg_src->courtesy_en = true; } - slot_arg_src->courtesy_target = slot_idx_tgt; - slot_arg_src->courtesy_period = courtesy->slot_num; - slot_arg_src->courtesy_en = true; + if (aux->crtz) { + slot_arg_src = &arg->slots[aux->slot_idx]; + + slot_arg_src->courtesy_target = ref->slot_idx; + slot_arg_src->courtesy_period = aux->crtz->slot_num; + slot_arg_src->courtesy_en = true; + } } static int __mrc_fw_start(struct rtw89_dev *rtwdev, bool replace) @@ -1999,30 +2102,24 @@ static void rtw89_mcc_handle_beacon_noa(struct rtw89_dev *rtwdev, bool enable) struct rtw89_mcc_role *ref = &mcc->role_ref; struct rtw89_mcc_role *aux = &mcc->role_aux; struct rtw89_mcc_config *config = &mcc->config; - struct rtw89_mcc_pattern *pattern = &config->pattern; - struct rtw89_mcc_sync *sync = &config->sync; struct ieee80211_p2p_noa_desc noa_desc = {}; - u64 start_time = config->start_tsf; u32 interval = config->mcc_interval; struct rtw89_vif_link *rtwvif_go; + u64 start_time; u32 duration; if (mcc->mode != RTW89_MCC_MODE_GO_STA) return; if (ref->is_go) { + start_time = config->start_tsf; rtwvif_go = ref->rtwvif_link; start_time += ieee80211_tu_to_usec(ref->duration); duration = config->mcc_interval - ref->duration; } else if (aux->is_go) { + start_time = config->start_tsf_in_aux_domain; rtwvif_go = aux->rtwvif_link; - start_time += ieee80211_tu_to_usec(pattern->tob_ref) + - ieee80211_tu_to_usec(config->beacon_offset) + - ieee80211_tu_to_usec(pattern->toa_aux); duration = config->mcc_interval - aux->duration; - - /* convert time domain from sta(ref) to GO(aux) */ - start_time += ieee80211_tu_to_usec(sync->offset); } else { rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC find no GO: skip updating beacon NoA\n"); @@ -2126,6 +2223,12 @@ static int rtw89_mcc_start(struct rtw89_dev *rtwdev) } struct rtw89_mcc_stop_sel { + struct { + const struct rtw89_vif_link *target; + } hint; + + /* selection content */ + bool filled; u8 mac_id; u8 slot_idx; }; @@ -2135,6 +2238,7 @@ static void rtw89_mcc_stop_sel_fill(struct rtw89_mcc_stop_sel *sel, { sel->mac_id = mcc_role->rtwvif_link->mac_id; sel->slot_idx = mcc_role->slot_idx; + sel->filled = true; } static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev, @@ -2144,23 +2248,41 @@ static int rtw89_mcc_stop_sel_iterator(struct rtw89_dev *rtwdev, { struct rtw89_mcc_stop_sel *sel = data; + if (mcc_role->rtwvif_link == sel->hint.target) { + rtw89_mcc_stop_sel_fill(sel, mcc_role); + return 1; /* break iteration */ + } + + if (sel->filled) + return 0; + if (!mcc_role->rtwvif_link->chanctx_assigned) return 0; rtw89_mcc_stop_sel_fill(sel, mcc_role); - return 1; /* break iteration */ + return 0; } -static void rtw89_mcc_stop(struct rtw89_dev *rtwdev) +static void rtw89_mcc_stop(struct rtw89_dev *rtwdev, + const struct rtw89_chanctx_pause_parm *pause) { + struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_role *ref = &mcc->role_ref; - struct rtw89_mcc_stop_sel sel; + struct rtw89_mcc_stop_sel sel = { + .hint.target = pause ? pause->trigger : NULL, + }; int ret; + if (!pause) { + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->chanctx_work); + bitmap_zero(hal->changes, NUM_OF_RTW89_CHANCTX_CHANGES); + } + /* by default, stop at ref */ - rtw89_mcc_stop_sel_fill(&sel, ref); rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_stop_sel_iterator, &sel); + if (!sel.filled) + rtw89_mcc_stop_sel_fill(&sel, ref); rtw89_debug(rtwdev, RTW89_DBG_CHAN, "MCC stop at <macid %d>\n", sel.mac_id); @@ -2192,6 +2314,7 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev) struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_config *config = &mcc->config; struct rtw89_mcc_config old_cfg = *config; + bool courtesy_changed; bool sync_changed; int ret; @@ -2204,8 +2327,15 @@ static int rtw89_mcc_update(struct rtw89_dev *rtwdev) if (ret) return ret; + if (memcmp(&old_cfg.pattern.courtesy, &config->pattern.courtesy, + sizeof(old_cfg.pattern.courtesy)) == 0) + courtesy_changed = false; + else + courtesy_changed = true; + if (old_cfg.pattern.plan != RTW89_MCC_PLAN_NO_BT || - config->pattern.plan != RTW89_MCC_PLAN_NO_BT) { + config->pattern.plan != RTW89_MCC_PLAN_NO_BT || + courtesy_changed) { if (rtw89_concurrent_via_mrc(rtwdev)) ret = __mrc_fw_start(rtwdev, true); else @@ -2237,7 +2367,7 @@ static void rtw89_mcc_track(struct rtw89_dev *rtwdev) struct rtw89_mcc_info *mcc = &rtwdev->mcc; struct rtw89_mcc_config *config = &mcc->config; struct rtw89_mcc_pattern *pattern = &config->pattern; - s16 tolerance; + u16 tolerance; u16 bcn_ofst; u16 diff; @@ -2245,18 +2375,25 @@ static void rtw89_mcc_track(struct rtw89_dev *rtwdev) return; bcn_ofst = rtw89_mcc_get_bcn_ofst(rtwdev); + if (bcn_ofst == config->beacon_offset) + return; + if (bcn_ofst > config->beacon_offset) { diff = bcn_ofst - config->beacon_offset; if (pattern->tob_aux < 0) tolerance = -pattern->tob_aux; - else + else if (pattern->toa_aux > 0) tolerance = pattern->toa_aux; + else + return; /* no chance to improve */ } else { diff = config->beacon_offset - bcn_ofst; if (pattern->toa_aux < 0) tolerance = -pattern->toa_aux; - else + else if (pattern->tob_aux > 0) tolerance = pattern->tob_aux; + else + return; /* no chance to improve */ } if (diff <= tolerance) @@ -2390,7 +2527,7 @@ static void rtw89_mcc_update_limit(struct rtw89_dev *rtwdev) rtw89_iterate_mcc_roles(rtwdev, rtw89_mcc_upd_lmt_iterator, NULL); } -void rtw89_chanctx_work(struct work_struct *work) +void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, chanctx_work.work); @@ -2401,12 +2538,10 @@ void rtw89_chanctx_work(struct work_struct *work) int ret; int i; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); - if (hal->entity_pause) { - mutex_unlock(&rtwdev->mutex); + if (hal->entity_pause) return; - } for (i = 0; i < NUM_OF_RTW89_CHANCTX_CHANGES; i++) { if (test_and_clear_bit(i, hal->changes)) @@ -2445,8 +2580,6 @@ void rtw89_chanctx_work(struct work_struct *work) default: break; } - - mutex_unlock(&rtwdev->mutex); } void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, @@ -2477,8 +2610,8 @@ void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, rtw89_debug(rtwdev, RTW89_DBG_CHAN, "queue chanctx work for mode %d with delay %d us\n", mode, delay); - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->chanctx_work, - usecs_to_jiffies(delay)); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->chanctx_work, + usecs_to_jiffies(delay)); } void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev) @@ -2491,7 +2624,7 @@ void rtw89_chanctx_track(struct rtw89_dev *rtwdev) struct rtw89_hal *hal = &rtwdev->hal; enum rtw89_entity_mode mode; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (hal->entity_pause) return; @@ -2507,22 +2640,22 @@ void rtw89_chanctx_track(struct rtw89_dev *rtwdev) } void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, - enum rtw89_chanctx_pause_reasons rsn) + const struct rtw89_chanctx_pause_parm *pause_parm) { struct rtw89_hal *hal = &rtwdev->hal; enum rtw89_entity_mode mode; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (hal->entity_pause) return; - rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", rsn); + rtw89_debug(rtwdev, RTW89_DBG_CHAN, "chanctx pause (rsn: %d)\n", pause_parm->rsn); mode = rtw89_get_entity_mode(rtwdev); switch (mode) { case RTW89_ENTITY_MODE_MCC: - rtw89_mcc_stop(rtwdev); + rtw89_mcc_stop(rtwdev, pause_parm); break; default: break; @@ -2555,7 +2688,7 @@ void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev, enum rtw89_entity_mode mode; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (unlikely(!hal->entity_pause)) { rtw89_chanctx_proceed_cb(rtwdev, cb_parm); @@ -2677,6 +2810,7 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, struct rtw89_hal *hal = &rtwdev->hal; struct rtw89_entity_mgnt *mgnt = &hal->entity_mgnt; struct rtw89_entity_weight w = {}; + int ret; rtwvif_link->chanctx_idx = cfg->idx; rtwvif_link->chanctx_assigned = true; @@ -2696,7 +2830,13 @@ int rtw89_chanctx_ops_assign_vif(struct rtw89_dev *rtwdev, rtw89_swap_chanctx(rtwdev, cfg->idx, RTW89_CHANCTX_0); out: - return rtw89_set_channel(rtwdev); + ret = rtw89_set_channel(rtwdev); + if (ret) + return ret; + + rtw89_tas_reset(rtwdev, true); + + return 0; } void rtw89_chanctx_ops_unassign_vif(struct rtw89_dev *rtwdev, @@ -2740,7 +2880,7 @@ out: cur = rtw89_get_entity_mode(rtwdev); switch (cur) { case RTW89_ENTITY_MODE_MCC: - rtw89_mcc_stop(rtwdev); + rtw89_mcc_stop(rtwdev, NULL); break; default: break; diff --git a/drivers/net/wireless/realtek/rtw89/chan.h b/drivers/net/wireless/realtek/rtw89/chan.h index 092a6f676894..2a25563593af 100644 --- a/drivers/net/wireless/realtek/rtw89/chan.h +++ b/drivers/net/wireless/realtek/rtw89/chan.h @@ -31,6 +31,14 @@ #define RTW89_MCC_DFLT_TX_NULL_EARLY 3 #define RTW89_MCC_DFLT_COURTESY_SLOT 3 +#define RTW89_MCC_REQ_COURTESY_TIME 5 +#define RTW89_MCC_REQ_COURTESY(pattern, role) \ +({ \ + const struct rtw89_mcc_pattern *p = pattern; \ + p->tob_ ## role <= RTW89_MCC_REQ_COURTESY_TIME || \ + p->toa_ ## role <= RTW89_MCC_REQ_COURTESY_TIME; \ +}) + #define NUM_OF_RTW89_MCC_ROLES 2 enum rtw89_chanctx_pause_reasons { @@ -38,6 +46,11 @@ enum rtw89_chanctx_pause_reasons { RTW89_CHANCTX_PAUSE_REASON_ROC, }; +struct rtw89_chanctx_pause_parm { + const struct rtw89_vif_link *trigger; + enum rtw89_chanctx_pause_reasons rsn; +}; + struct rtw89_chanctx_cb_parm { int (*cb)(struct rtw89_dev *rtwdev, void *data); void *data; @@ -95,17 +108,17 @@ void rtw89_config_entity_chandef(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx idx, const struct cfg80211_chan_def *chandef); void rtw89_config_roc_chandef(struct rtw89_dev *rtwdev, - enum rtw89_chanctx_idx idx, + struct rtw89_vif_link *rtwvif_link, const struct cfg80211_chan_def *chandef); void rtw89_entity_init(struct rtw89_dev *rtwdev); enum rtw89_entity_mode rtw89_entity_recalc(struct rtw89_dev *rtwdev); -void rtw89_chanctx_work(struct work_struct *work); +void rtw89_chanctx_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_queue_chanctx_work(struct rtw89_dev *rtwdev); void rtw89_queue_chanctx_change(struct rtw89_dev *rtwdev, enum rtw89_chanctx_changes change); void rtw89_chanctx_track(struct rtw89_dev *rtwdev); void rtw89_chanctx_pause(struct rtw89_dev *rtwdev, - enum rtw89_chanctx_pause_reasons rsn); + const struct rtw89_chanctx_pause_parm *parm); void rtw89_chanctx_proceed(struct rtw89_dev *rtwdev, const struct rtw89_chanctx_cb_parm *cb_parm); diff --git a/drivers/net/wireless/realtek/rtw89/coex.c b/drivers/net/wireless/realtek/rtw89/coex.c index 68316d44b204..5ccf0cbaed2f 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.c +++ b/drivers/net/wireless/realtek/rtw89/coex.c @@ -10,7 +10,7 @@ #include "ps.h" #include "reg.h" -#define RTW89_COEX_VERSION 0x07000113 +#define RTW89_COEX_VERSION 0x07000413 #define FCXDEF_STEP 50 /* MUST <= FCXMAX_STEP and match with wl fw*/ #define BTC_E2G_LIMIT_DEF 80 @@ -89,10 +89,10 @@ static const struct rtw89_btc_fbtc_slot s_def[] = { [CXST_B4] = __DEF_FBTC_SLOT(50, 0xe5555555, SLOT_MIX), [CXST_LK] = __DEF_FBTC_SLOT(20, 0xea5a5a5a, SLOT_ISO), [CXST_BLK] = __DEF_FBTC_SLOT(500, 0x55555555, SLOT_MIX), - [CXST_E2G] = __DEF_FBTC_SLOT(0, 0xea5a5a5a, SLOT_MIX), - [CXST_E5G] = __DEF_FBTC_SLOT(0, 0xffffffff, SLOT_ISO), + [CXST_E2G] = __DEF_FBTC_SLOT(5, 0xea5a5a5a, SLOT_MIX), + [CXST_E5G] = __DEF_FBTC_SLOT(5, 0xffffffff, SLOT_ISO), [CXST_EBT] = __DEF_FBTC_SLOT(5, 0xe5555555, SLOT_MIX), - [CXST_ENULL] = __DEF_FBTC_SLOT(0, 0xaaaaaaaa, SLOT_ISO), + [CXST_ENULL] = __DEF_FBTC_SLOT(5, 0xaaaaaaaa, SLOT_ISO), [CXST_WLK] = __DEF_FBTC_SLOT(250, 0xea5a5a5a, SLOT_MIX), [CXST_W1FDD] = __DEF_FBTC_SLOT(50, 0xffffffff, SLOT_ISO), [CXST_B1FDD] = __DEF_FBTC_SLOT(50, 0xffffdfff, SLOT_ISO), @@ -132,6 +132,14 @@ static const u32 cxtbl[] = { static const struct rtw89_btc_ver rtw89_btc_ver_defs[] = { /* firmware version must be in decreasing order for each chip */ + {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 122, 0), + .fcxbtcrpt = 8, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, + .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, + .fcxbtver = 7, .fcxbtscan = 7, .fcxbtafh = 7, .fcxbtdevinfo = 7, + .fwlrole = 7, .frptmap = 3, .fcxctrl = 7, .fcxinit = 7, + .fwevntrptl = 1, .fwc2hfunc = 2, .drvinfo_type = 1, .info_buf = 1800, + .max_role_num = 6, + }, {RTL8852BT, RTW89_FW_VER_CODE(0, 29, 90, 0), .fcxbtcrpt = 7, .fcxtdma = 7, .fcxslots = 7, .fcxcysta = 7, .fcxstep = 7, .fcxnullsta = 7, .fcxmreg = 7, .fcxgpiodbg = 7, @@ -1372,11 +1380,9 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, } else if (ver->fcxbtcrpt == 8) { pfinfo = &pfwinfo->rpt_ctrl.finfo.v8; pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v8); - break; } else if (ver->fcxbtcrpt == 7) { pfinfo = &pfwinfo->rpt_ctrl.finfo.v7; pcinfo->req_len = sizeof(pfwinfo->rpt_ctrl.finfo.v7); - break; } else { goto err; } @@ -1534,6 +1540,9 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, } else if (ver->fcxbtafh == 2) { pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v2; pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v2); + } else if (ver->fcxbtafh == 7) { + pfinfo = &pfwinfo->rpt_fbtc_btafh.finfo.v7; + pcinfo->req_len = sizeof(pfwinfo->rpt_fbtc_btafh.finfo.v7); } else { goto err; } @@ -1602,7 +1611,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v4.wl_fw_info.fw_ver); dm->wl_fw_cx_offload = !!le32_to_cpu(prpt->v4.wl_fw_info.cx_offload); - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v4.gnt_val[i], sizeof(dm->gnt.band[i])); @@ -1634,7 +1643,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v5.rpt_info.fw_ver); dm->wl_fw_cx_offload = 0; - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v5.gnt_val[i][0], sizeof(dm->gnt.band[i])); @@ -1661,7 +1670,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw = le32_to_cpu(prpt->v105.rpt_info.fw_ver); dm->wl_fw_cx_offload = 0; - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v105.gnt_val[i][0], sizeof(dm->gnt.band[i])); @@ -1687,7 +1696,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw_coex = le32_to_cpu(prpt->v7.rpt_info.cx_ver); wl->ver_info.fw = le32_to_cpu(prpt->v7.rpt_info.fw_ver); - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v7.gnt_val[i][0], sizeof(dm->gnt.band[i])); @@ -1719,7 +1728,7 @@ static u32 _chk_btc_report(struct rtw89_dev *rtwdev, wl->ver_info.fw_coex = le32_to_cpu(prpt->v8.rpt_info.cx_ver); wl->ver_info.fw = le32_to_cpu(prpt->v8.rpt_info.fw_ver); - for (i = RTW89_PHY_0; i < RTW89_PHY_MAX; i++) + for (i = RTW89_PHY_0; i < RTW89_PHY_NUM; i++) memcpy(&dm->gnt.band[i], &prpt->v8.gnt_val[i][0], sizeof(dm->gnt.band[i])); @@ -2706,7 +2715,7 @@ static void _set_gnt(struct rtw89_dev *rtwdev, u8 phy_map, u8 wl_state, u8 bt_st if (phy_map > BTC_PHY_ALL) return; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { if (!(phy_map & BIT(i))) continue; @@ -2755,7 +2764,7 @@ static void _set_gnt_v1(struct rtw89_dev *rtwdev, u8 phy_map, if (phy_map > BTC_PHY_ALL) return; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { if (!(phy_map & BIT(i))) continue; @@ -2955,7 +2964,7 @@ static void _set_rf_trx_para(struct rtw89_dev *rtwdev) if (ver->fwlrole == 0) { link_mode = wl->role_info.link_mode; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { if (wl->dbcc_info.real_band[i] == RTW89_BAND_2G) dbcc_2g_phy = i; } @@ -4240,7 +4249,7 @@ static void _set_ant_v0(struct rtw89_dev *rtwdev, bool force_exec, case BTC_ANT_W2G: rtw89_chip_cfg_ctrl_path(rtwdev, BTC_CTRL_BY_WL); if (rtwdev->dbcc_en) { - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { b2g = (wl_dinfo->real_band[i] == RTW89_BAND_2G); gnt_wl_ctrl = b2g ? BTC_GNT_HW : BTC_GNT_SW_HI; @@ -4583,23 +4592,16 @@ static void _action_bt_hid(struct rtw89_dev *rtwdev) static void _action_bt_a2dp(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; - struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; struct rtw89_btc_dm *dm = &btc->dm; _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); + dm->slot_dur[CXST_W1] = 20; + dm->slot_dur[CXST_B1] = BTC_B1_MAX; + switch (btc->cx.state_map) { case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP */ - if (a2dp.vendor_id == 0x4c || dm->leak_ap) { - dm->slot_dur[CXST_W1] = 40; - dm->slot_dur[CXST_B1] = 200; - _set_policy(rtwdev, - BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP); - } else { - _set_policy(rtwdev, - BTC_CXP_PAUTO_TD50B1, BTC_ACT_BT_A2DP); - } + _set_policy(rtwdev, BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP); break; case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP */ _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP); @@ -4609,15 +4611,10 @@ static void _action_bt_a2dp(struct rtw89_dev *rtwdev) break; case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP */ case BTC_WLINKING: /* wl-connecting + bt-A2DP */ - if (a2dp.vendor_id == 0x4c || dm->leak_ap) { - dm->slot_dur[CXST_W1] = 40; - dm->slot_dur[CXST_B1] = 200; - _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, - BTC_ACT_BT_A2DP); - } else { - _set_policy(rtwdev, BTC_CXP_AUTO_TD50B1, - BTC_ACT_BT_A2DP); - } + if (btc->cx.wl.rfk_info.con_rfk) + _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DP); + else + _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, BTC_ACT_BT_A2DP); break; case BTC_WIDLE: /* wl-idle + bt-A2DP */ _set_policy(rtwdev, BTC_CXP_AUTO_TD20B1, BTC_ACT_BT_A2DP); @@ -4645,7 +4642,10 @@ static void _action_bt_a2dpsink(struct rtw89_dev *rtwdev) _set_policy(rtwdev, BTC_CXP_FIX_TD2060, BTC_ACT_BT_A2DPSINK); break; case BTC_WLINKING: /* wl-connecting + bt-A2dp_Sink */ - _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK); + if (btc->cx.wl.rfk_info.con_rfk) + _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DPSINK); + else + _set_policy(rtwdev, BTC_CXP_FIX_TD3030, BTC_ACT_BT_A2DPSINK); break; case BTC_WIDLE: /* wl-idle + bt-A2dp_Sink */ _set_policy(rtwdev, BTC_CXP_FIX_TD2080, BTC_ACT_BT_A2DPSINK); @@ -4693,27 +4693,20 @@ static void _action_bt_pan(struct rtw89_dev *rtwdev) static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev) { struct rtw89_btc *btc = &rtwdev->btc; - struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; - struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; struct rtw89_btc_dm *dm = &btc->dm; _set_ant(rtwdev, NM_EXEC, BTC_PHY_ALL, BTC_ANT_W2G); + dm->slot_dur[CXST_W1] = 20; + dm->slot_dur[CXST_B1] = BTC_B1_MAX; + switch (btc->cx.state_map) { case BTC_WBUSY_BNOSCAN: /* wl-busy + bt-A2DP+HID */ case BTC_WIDLE: /* wl-idle + bt-A2DP */ - if (a2dp.vendor_id == 0x4c || dm->leak_ap) { - dm->slot_dur[CXST_W1] = 40; - dm->slot_dur[CXST_B1] = 200; - _set_policy(rtwdev, - BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID); - } else { - _set_policy(rtwdev, - BTC_CXP_PAUTO_TD50B1, BTC_ACT_BT_A2DP_HID); - } + _set_policy(rtwdev, BTC_CXP_PAUTO_TDW1B1, BTC_ACT_BT_A2DP_HID); break; case BTC_WBUSY_BSCAN: /* wl-busy + bt-inq + bt-A2DP+HID */ - _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3050, BTC_ACT_BT_A2DP_HID); + _set_policy(rtwdev, BTC_CXP_PAUTO2_TD3070, BTC_ACT_BT_A2DP_HID); break; case BTC_WSCAN_BSCAN: /* wl-scan + bt-inq + bt-A2DP+HID */ @@ -4721,15 +4714,10 @@ static void _action_bt_a2dp_hid(struct rtw89_dev *rtwdev) break; case BTC_WSCAN_BNOSCAN: /* wl-scan + bt-A2DP+HID */ case BTC_WLINKING: /* wl-connecting + bt-A2DP+HID */ - if (a2dp.vendor_id == 0x4c || dm->leak_ap) { - dm->slot_dur[CXST_W1] = 40; - dm->slot_dur[CXST_B1] = 200; - _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, - BTC_ACT_BT_A2DP_HID); - } else { - _set_policy(rtwdev, BTC_CXP_AUTO_TD50B1, - BTC_ACT_BT_A2DP_HID); - } + if (btc->cx.wl.rfk_info.con_rfk) + _set_policy(rtwdev, BTC_CXP_OFF_BT, BTC_ACT_BT_A2DP_HID); + else + _set_policy(rtwdev, BTC_CXP_AUTO_TDW1B1, BTC_ACT_BT_A2DP_HID); break; } } @@ -4905,9 +4893,9 @@ static void _set_btg_ctrl(struct rtw89_dev *rtwdev) if (rtwdev->dbcc_en) { if (ver->fwlrole == 0) { - wl_rinfo.dbcc_2g_phy = RTW89_PHY_MAX; + wl_rinfo.dbcc_2g_phy = RTW89_PHY_NUM; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { if (wl_dinfo->real_band[i] == RTW89_BAND_2G) wl_rinfo.dbcc_2g_phy = i; } @@ -5408,7 +5396,8 @@ static void _action_wl_scan(struct rtw89_dev *rtwdev) struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; - if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) { + if (btc->cx.state_map != BTC_WLINKING && + RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) { _action_wl_25g_mcc(rtwdev); rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], Scan offload!\n"); } else if (rtwdev->dbcc_en) { @@ -5798,7 +5787,7 @@ static void _update_wl_info(struct rtw89_dev *rtwdev) phy = wl_linfo[i].phy; /* check dbcc role */ - if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { + if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { wl_dinfo->role[phy] = wl_linfo[i].role; wl_dinfo->op_band[phy] = wl_linfo[i].band; _update_dbcc_band(rtwdev, phy); @@ -5948,7 +5937,7 @@ static void _update_wl_info_v1(struct rtw89_dev *rtwdev) phy = wl_linfo[i].phy; - if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { + if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { wl_dinfo->role[phy] = wl_linfo[i].role; wl_dinfo->op_band[phy] = wl_linfo[i].band; _update_dbcc_band(rtwdev, phy); @@ -6098,7 +6087,7 @@ static void _update_wl_info_v2(struct rtw89_dev *rtwdev) phy = wl_linfo[i].phy; - if (rtwdev->dbcc_en && phy < RTW89_PHY_MAX) { + if (rtwdev->dbcc_en && phy < RTW89_PHY_NUM) { wl_dinfo->role[phy] = wl_linfo[i].role; wl_dinfo->op_band[phy] = wl_linfo[i].band; _update_dbcc_band(rtwdev, phy); @@ -6389,7 +6378,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; struct rtw89_btc_wl_link_info *wl_linfo = wl->link_info; struct rtw89_btc_wl_active_role_v7 *act_role = NULL; - u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_MAX, phy_dbcc; + u8 i, mode, cnt = 0, cnt_2g = 0, cnt_5g = 0, phy_now = RTW89_PHY_NUM, phy_dbcc; bool b2g = false, b5g = false, client_joined = false, client_inc_2g = false; u8 client_cnt_last[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; u8 cid_role[RTW89_BE_BTC_WL_MAX_ROLE_NUMBER] = {}; @@ -6400,7 +6389,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) memset(wl_rinfo, 0, sizeof(*wl_rinfo)); for (i = 0; i < RTW89_PORT_NUM; i++) { - if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_MAX) + if (!wl_linfo[i].active || wl_linfo[i].phy >= RTW89_PHY_NUM) continue; act_role = &wl_rinfo->active_role[i]; @@ -6494,7 +6483,7 @@ static void _update_wl_info_v7(struct rtw89_dev *rtwdev, u8 rid) mode = _chk_dbcc(rtwdev, cid_ch, cid_phy, cid_role, &dbcc_2g_phy); /* correct 2G-located PHY band for gnt ctrl */ - if (dbcc_2g_phy < RTW89_PHY_MAX) + if (dbcc_2g_phy < RTW89_PHY_NUM) wl_dinfo->op_band[dbcc_2g_phy] = RTW89_BAND_2G; } else if (b2g && b5g && cnt == 2) { mode = BTC_WLINK_25G_MCC; @@ -6720,7 +6709,7 @@ static void _update_wl_info_v8(struct rtw89_dev *rtwdev, u8 role_id, u8 rlink_id _fw_set_drv_info(rtwdev, CXDRVINFO_ROLE); } -void rtw89_coex_act1_work(struct work_struct *work) +void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, coex_act1_work.work); @@ -6729,7 +6718,8 @@ void rtw89_coex_act1_work(struct work_struct *work) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &cx->wl; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; if (wl->status.map._4way) @@ -6738,10 +6728,9 @@ void rtw89_coex_act1_work(struct work_struct *work) wl->status.map.connecting = false; _run_coex(rtwdev, BTC_RSN_ACT1_WORK); - mutex_unlock(&rtwdev->mutex); } -void rtw89_coex_bt_devinfo_work(struct work_struct *work) +void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, coex_bt_devinfo_work.work); @@ -6749,15 +6738,15 @@ void rtw89_coex_bt_devinfo_work(struct work_struct *work) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; a2dp->play_latency = 0; _run_coex(rtwdev, BTC_RSN_BT_DEVINFO_WORK); - mutex_unlock(&rtwdev->mutex); } -void rtw89_coex_rfk_chk_work(struct work_struct *work) +void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, coex_rfk_chk_work.work); @@ -6766,7 +6755,8 @@ void rtw89_coex_rfk_chk_work(struct work_struct *work) struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_wl_info *wl = &cx->wl; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); + rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): enter\n", __func__); dm->cnt_notify[BTC_NCNT_TIMER]++; if (wl->rfk_info.state != BTC_WRFK_STOP) { @@ -6778,7 +6768,6 @@ void rtw89_coex_rfk_chk_work(struct work_struct *work) _write_scbd(rtwdev, BTC_WSCB_WLRFK, false); _run_coex(rtwdev, BTC_RSN_RFK_CHK_WORK); } - mutex_unlock(&rtwdev->mutex); } static void _update_bt_scbd(struct rtw89_dev *rtwdev, bool only_update) @@ -6882,7 +6871,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; u8 mode, igno_bt, always_freerun; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); dm->run_reason = reason; _update_dm_step(rtwdev, reason); @@ -7002,7 +6991,7 @@ void _run_coex(struct rtw89_dev *rtwdev, enum btc_reason_and_action reason) goto exit; } - if (wl->status.val & btc_scanning_map.val) { + if (wl->status.val & btc_scanning_map.val && !wl->rfk_info.con_rfk) { _action_wl_scan(rtwdev); bt->scan_rx_low_pri = true; goto exit; @@ -7187,7 +7176,7 @@ void rtw89_btc_ntfy_scan_start(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band) "[BTC], %s(): phy_idx=%d, band=%d\n", __func__, phy_idx, band); - if (phy_idx >= RTW89_PHY_MAX) + if (phy_idx >= RTW89_PHY_NUM) return; btc->dm.cnt_notify[BTC_NCNT_SCAN_START]++; @@ -7223,6 +7212,8 @@ void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx) _fw_set_drv_info(rtwdev, CXDRVINFO_DBCC); } + btc->dm.tdma_instant_excute = 1; + _run_coex(rtwdev, BTC_RSN_NTFY_SCAN_FINISH); } @@ -7235,7 +7226,7 @@ void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band) "[BTC], %s(): phy_idx=%d, band=%d\n", __func__, phy_idx, band); - if (phy_idx >= RTW89_PHY_MAX) + if (phy_idx >= RTW89_PHY_NUM) return; btc->dm.cnt_notify[BTC_NCNT_SWITCH_BAND]++; @@ -7284,7 +7275,7 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, "[BTC], %s(): EAPOL_End cnt=%d\n", __func__, cnt); wl->status.map._4way = false; - cancel_delayed_work(&rtwdev->coex_act1_work); + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); break; case PACKET_ARP: cnt = ++cx->cnt_wl[BTC_WCNT_ARP]; @@ -7303,56 +7294,56 @@ void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, } if (delay_work) { - cancel_delayed_work(&rtwdev->coex_act1_work); - ieee80211_queue_delayed_work(rtwdev->hw, - &rtwdev->coex_act1_work, delay); + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_act1_work); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_act1_work, delay); } btc->dm.cnt_notify[BTC_NCNT_SPECIAL_PACKET]++; _run_coex(rtwdev, BTC_RSN_NTFY_SPECIFIC_PACKET); } -void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.eapol_notify_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); + rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_EAPOL); - mutex_unlock(&rtwdev->mutex); } -void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.arp_notify_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); + rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ARP); - mutex_unlock(&rtwdev->mutex); } -void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.dhcp_notify_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); + rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_DHCP); - mutex_unlock(&rtwdev->mutex); } -void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work) +void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, btc.icmp_notify_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); + rtw89_leave_ps_mode(rtwdev); rtw89_btc_ntfy_specific_packet(rtwdev, PACKET_ICMP); - mutex_unlock(&rtwdev->mutex); } static u8 _update_bt_rssi_level(struct rtw89_dev *rtwdev, u8 rssi) @@ -7531,9 +7522,9 @@ static void _update_bt_info(struct rtw89_dev *rtwdev, u8 *buf, u32 len) a2dp->vendor_id = 0; a2dp->flush_time = 0; a2dp->play_latency = 1; - ieee80211_queue_delayed_work(rtwdev->hw, - &rtwdev->coex_bt_devinfo_work, - RTW89_COEX_BT_DEVINFO_WORK_PERIOD); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_bt_devinfo_work, + RTW89_COEX_BT_DEVINFO_WORK_PERIOD); } _run_coex(rtwdev, BTC_RSN_UPDATE_BT_INFO); @@ -7671,7 +7662,8 @@ void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, else wl->status.map.connecting = 0; - if (state == BTC_ROLE_MSTS_STA_DIS_CONN) + if (state == BTC_ROLE_MSTS_STA_DIS_CONN || + state == BTC_ROLE_MSTS_STA_CONN_END) wl->status.map._4way = false; _run_coex(rtwdev, BTC_RSN_NTFY_ROLE_INFO); @@ -7781,7 +7773,7 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path, wl->rfk_info.state = BTC_WRFK_STOP; _write_scbd(rtwdev, BTC_WSCB_WLRFK, false); - cancel_delayed_work(&rtwdev->coex_rfk_chk_work); + wiphy_delayed_work_cancel(rtwdev->hw->wiphy, &rtwdev->coex_rfk_chk_work); break; default: rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7795,9 +7787,9 @@ static bool _ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_path, _run_coex(rtwdev, BTC_RSN_NTFY_WL_RFK); if (wl->rfk_info.state == BTC_WRFK_START) - ieee80211_queue_delayed_work(rtwdev->hw, - &rtwdev->coex_rfk_chk_work, - RTW89_COEX_RFK_CHK_WORK_PERIOD); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, + &rtwdev->coex_rfk_chk_work, + RTW89_COEX_RFK_CHK_WORK_PERIOD); } rtw89_debug(rtwdev, RTW89_DBG_BTC, @@ -7815,6 +7807,8 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map, bool allow; int ret; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + band = FIELD_GET(BTC_RFK_BAND_MAP, phy_map); rtw89_debug(rtwdev, RTW89_DBG_RFK, @@ -8115,6 +8109,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, return; func = rtw89_btc_c2h_get_index_by_ver(rtwdev, func); + pfwinfo->cnt_c2h++; switch (func) { case BTF_EVNT_BUF_OVERFLOW: @@ -8156,7 +8151,7 @@ void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, #define BTC_CX_FW_OFFLOAD 0 -static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_cx_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { union rtw89_btc_module_info *md = &rtwdev->btc.mdinfo; const struct rtw89_chip_info *chip = rtwdev->chip; @@ -8168,40 +8163,43 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &btc->cx.wl; u32 ver_main = 0, ver_sub = 0, ver_hotfix = 0, id_branch = 0; u8 cv, rfe, iso, ant_num, ant_single_pos; + char *p = buf, *end = buf + bufsz; if (!(dm->coex_info_map & BTC_COEX_INFO_CX)) - return; + return 0; dm->cnt_notify[BTC_NCNT_SHOW_COEX_INFO]++; - seq_printf(m, "========== [BTC COEX INFO (%d)] ==========\n", - chip->chip_id); + p += scnprintf(p, end - p, + "========== [BTC COEX INFO (%d)] ==========\n", + chip->chip_id); ver_main = FIELD_GET(GENMASK(31, 24), RTW89_COEX_VERSION); ver_sub = FIELD_GET(GENMASK(23, 16), RTW89_COEX_VERSION); ver_hotfix = FIELD_GET(GENMASK(15, 8), RTW89_COEX_VERSION); id_branch = FIELD_GET(GENMASK(7, 0), RTW89_COEX_VERSION); - seq_printf(m, " %-15s : Coex:%d.%d.%d(branch:%d), ", - "[coex_version]", ver_main, ver_sub, ver_hotfix, id_branch); + p += scnprintf(p, end - p, " %-15s : Coex:%d.%d.%d(branch:%d), ", + "[coex_version]", ver_main, ver_sub, ver_hotfix, + id_branch); ver_main = FIELD_GET(GENMASK(31, 24), wl->ver_info.fw_coex); ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw_coex); ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw_coex); id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw_coex); - seq_printf(m, "WL_FW_coex:%d.%d.%d(branch:%d)", - ver_main, ver_sub, ver_hotfix, id_branch); + p += scnprintf(p, end - p, "WL_FW_coex:%d.%d.%d(branch:%d)", + ver_main, ver_sub, ver_hotfix, id_branch); ver_main = FIELD_GET(GENMASK(31, 24), chip->wlcx_desired); ver_sub = FIELD_GET(GENMASK(23, 16), chip->wlcx_desired); ver_hotfix = FIELD_GET(GENMASK(15, 8), chip->wlcx_desired); - seq_printf(m, "(%s, desired:%d.%d.%d), ", - (wl->ver_info.fw_coex >= chip->wlcx_desired ? - "Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix); + p += scnprintf(p, end - p, "(%s, desired:%d.%d.%d), ", + (wl->ver_info.fw_coex >= chip->wlcx_desired ? + "Match" : "Mismatch"), ver_main, ver_sub, ver_hotfix); - seq_printf(m, "BT_FW_coex:%d(%s, desired:%d)\n", - bt->ver_info.fw_coex, - (bt->ver_info.fw_coex >= chip->btcx_desired ? - "Match" : "Mismatch"), chip->btcx_desired); + p += scnprintf(p, end - p, "BT_FW_coex:%d(%s, desired:%d)\n", + bt->ver_info.fw_coex, + (bt->ver_info.fw_coex >= chip->btcx_desired ? + "Match" : "Mismatch"), chip->btcx_desired); if (bt->enable.now && bt->ver_info.fw == 0) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_VER_INFO, true); @@ -8212,10 +8210,11 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) ver_sub = FIELD_GET(GENMASK(23, 16), wl->ver_info.fw); ver_hotfix = FIELD_GET(GENMASK(15, 8), wl->ver_info.fw); id_branch = FIELD_GET(GENMASK(7, 0), wl->ver_info.fw); - seq_printf(m, " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n", - "[sub_module]", - ver_main, ver_sub, ver_hotfix, id_branch, - bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM"); + p += scnprintf(p, end - p, + " %-15s : WL_FW:%d.%d.%d.%d, BT_FW:0x%x(%s)\n", + "[sub_module]", + ver_main, ver_sub, ver_hotfix, id_branch, + bt->ver_info.fw, bt->run_patch_code ? "patch" : "ROM"); if (ver->fcxinit == 7) { cv = md->md_v7.kt_ver; @@ -8231,36 +8230,41 @@ static void _show_cx_info(struct rtw89_dev *rtwdev, struct seq_file *m) ant_single_pos = md->md.ant.single_pos; } - seq_printf(m, " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s", - "[hw_info]", cv, rfe, iso, ant_num, - ant_num > 1 ? "" : - ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, "); + p += scnprintf(p, end - p, + " %-15s : cv:%x, rfe_type:0x%x, ant_iso:%d, ant_pg:%d, %s", + "[hw_info]", cv, rfe, iso, ant_num, + ant_num > 1 ? "" : + ant_single_pos ? "1Ant_Pos:S1, " : "1Ant_Pos:S0, "); - seq_printf(m, "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n", - btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss, - hal->rx_nss); + p += scnprintf(p, end - p, + "3rd_coex:%d, dbcc:%d, tx_num:%d, rx_num:%d\n", + btc->cx.other.type, rtwdev->dbcc_en, hal->tx_nss, + hal->rx_nss); + + return p - buf; } -static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_wl_role_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_wl_link_info *plink = NULL; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_wl_dbcc_info *wl_dinfo = &wl->dbcc_info; struct rtw89_traffic_stats *t; + char *p = buf, *end = buf + bufsz; u8 i; if (rtwdev->dbcc_en) { - seq_printf(m, - " %-15s : PHY0_band(op:%d/scan:%d/real:%d), ", - "[dbcc_info]", wl_dinfo->op_band[RTW89_PHY_0], - wl_dinfo->scan_band[RTW89_PHY_0], - wl_dinfo->real_band[RTW89_PHY_0]); - seq_printf(m, - "PHY1_band(op:%d/scan:%d/real:%d)\n", - wl_dinfo->op_band[RTW89_PHY_1], - wl_dinfo->scan_band[RTW89_PHY_1], - wl_dinfo->real_band[RTW89_PHY_1]); + p += scnprintf(p, end - p, + " %-15s : PHY0_band(op:%d/scan:%d/real:%d), ", + "[dbcc_info]", wl_dinfo->op_band[RTW89_PHY_0], + wl_dinfo->scan_band[RTW89_PHY_0], + wl_dinfo->real_band[RTW89_PHY_0]); + p += scnprintf(p, end - p, + "PHY1_band(op:%d/scan:%d/real:%d)\n", + wl_dinfo->op_band[RTW89_PHY_1], + wl_dinfo->scan_band[RTW89_PHY_1], + wl_dinfo->real_band[RTW89_PHY_1]); } for (i = 0; i < RTW89_PORT_NUM; i++) { @@ -8272,38 +8276,41 @@ static void _show_wl_role_info(struct rtw89_dev *rtwdev, struct seq_file *m) if (!plink->active) continue; - seq_printf(m, - " [port_%d] : role=%d(phy-%d), connect=%d(client_cnt=%d), mode=%d, center_ch=%d, bw=%d", - plink->pid, (u32)plink->role, plink->phy, - (u32)plink->connected, plink->client_cnt - 1, - (u32)plink->mode, plink->ch, (u32)plink->bw); + p += scnprintf(p, end - p, + " [port_%d] : role=%d(phy-%d), connect=%d(client_cnt=%d), mode=%d, center_ch=%d, bw=%d", + plink->pid, (u32)plink->role, plink->phy, + (u32)plink->connected, plink->client_cnt - 1, + (u32)plink->mode, plink->ch, (u32)plink->bw); if (plink->connected == MLME_NO_LINK) continue; - seq_printf(m, - ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n", - plink->mac_id, plink->tx_time, plink->tx_retry); + p += scnprintf(p, end - p, + ", mac_id=%d, max_tx_time=%dus, max_tx_retry=%d\n", + plink->mac_id, plink->tx_time, plink->tx_retry); - seq_printf(m, - " [port_%d] : rssi=-%ddBm(%d), busy=%d, dir=%s, ", - plink->pid, 110 - plink->stat.rssi, - plink->stat.rssi, plink->busy, - plink->dir == RTW89_TFC_UL ? "UL" : "DL"); + p += scnprintf(p, end - p, + " [port_%d] : rssi=-%ddBm(%d), busy=%d, dir=%s, ", + plink->pid, 110 - plink->stat.rssi, + plink->stat.rssi, plink->busy, + plink->dir == RTW89_TFC_UL ? "UL" : "DL"); t = &plink->stat.traffic; - seq_printf(m, - "tx[rate:%d/busy_level:%d], ", - (u32)t->tx_rate, t->tx_tfc_lv); + p += scnprintf(p, end - p, + "tx[rate:%d/busy_level:%d], ", + (u32)t->tx_rate, t->tx_tfc_lv); - seq_printf(m, "rx[rate:%d/busy_level:%d/drop:%d]\n", - (u32)t->rx_rate, - t->rx_tfc_lv, plink->rx_rate_drop_cnt); + p += scnprintf(p, end - p, + "rx[rate:%d/busy_level:%d/drop:%d]\n", + (u32)t->rx_rate, + t->rx_tfc_lv, plink->rx_rate_drop_cnt); } + + return p - buf; } -static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_wl_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; @@ -8314,12 +8321,13 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_role_info_v2 *wl_rinfo_v2 = &wl->role_info_v2; struct rtw89_btc_wl_role_info_v7 *wl_rinfo_v7 = &wl->role_info_v7; struct rtw89_btc_wl_role_info_v8 *wl_rinfo_v8 = &wl->role_info_v8; + char *p = buf, *end = buf + bufsz; u8 mode; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_WL)) - return; + return 0; - seq_puts(m, "========== [WL Status] ==========\n"); + p += scnprintf(p, end - p, "========== [WL Status] ==========\n"); if (ver->fwlrole == 0) mode = wl_rinfo->link_mode; @@ -8332,24 +8340,28 @@ static void _show_wl_info(struct rtw89_dev *rtwdev, struct seq_file *m) else if (ver->fwlrole == 8) mode = wl_rinfo_v8->link_mode; else - return; + goto out; - seq_printf(m, " %-15s : link_mode:%d, ", "[status]", mode); + p += scnprintf(p, end - p, " %-15s : link_mode:%d, ", "[status]", + mode); - seq_printf(m, - "rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ", - wl->status.map.rf_off, wl->status.map.lps, - wl->status.map.scan ? "Y" : "N", - wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map); + p += scnprintf(p, end - p, + "rf_off:%d, power_save:%d, scan:%s(band:%d/phy_map:0x%x), ", + wl->status.map.rf_off, wl->status.map.lps, + wl->status.map.scan ? "Y" : "N", + wl->scan_info.band[RTW89_PHY_0], wl->scan_info.phy_map); - seq_printf(m, - "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n", - wl->status.map.connecting ? "Y" : "N", - wl->status.map.roaming ? "Y" : "N", - wl->status.map._4way ? "Y" : "N", - wl->status.map.init_ok ? "Y" : "N"); + p += scnprintf(p, end - p, + "connecting:%s, roam:%s, 4way:%s, init_ok:%s\n", + wl->status.map.connecting ? "Y" : "N", + wl->status.map.roaming ? "Y" : "N", + wl->status.map._4way ? "Y" : "N", + wl->status.map.init_ok ? "Y" : "N"); - _show_wl_role_info(rtwdev, m); + p += _show_wl_role_info(rtwdev, p, end - p); + +out: + return p - buf; } enum btc_bt_a2dp_type { @@ -8358,7 +8370,7 @@ enum btc_bt_a2dp_type { BTC_A2DP_TWS_RELAY = 2, }; -static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_bt_profile_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_link_info *bt_linfo = &btc->cx.bt.link_info; @@ -8366,50 +8378,55 @@ static void _show_bt_profile_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_bt_hid_desc hid = bt_linfo->hid_desc; struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; struct rtw89_btc_bt_pan_desc pan = bt_linfo->pan_desc; + char *p = buf, *end = buf + bufsz; if (hfp.exist) { - seq_printf(m, " %-15s : type:%s, sut_pwr:%d, golden-rx:%d", - "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"), - bt_linfo->sut_pwr_level[0], - bt_linfo->golden_rx_shift[0]); + p += scnprintf(p, end - p, + " %-15s : type:%s, sut_pwr:%d, golden-rx:%d", + "[HFP]", (hfp.type == 0 ? "SCO" : "eSCO"), + bt_linfo->sut_pwr_level[0], + bt_linfo->golden_rx_shift[0]); } if (hid.exist) { - seq_printf(m, - "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n", - "[HID]", - hid.type & BTC_HID_218 ? "2/18," : "", - hid.type & BTC_HID_418 ? "4/18," : "", - hid.type & BTC_HID_BLE ? "BLE," : "", - hid.type & BTC_HID_RCU ? "RCU," : "", - hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "", - hid.pair_cnt, bt_linfo->sut_pwr_level[1], - bt_linfo->golden_rx_shift[1]); + p += scnprintf(p, end - p, + "\n\r %-15s : type:%s%s%s%s%s pair-cnt:%d, sut_pwr:%d, golden-rx:%d\n", + "[HID]", + hid.type & BTC_HID_218 ? "2/18," : "", + hid.type & BTC_HID_418 ? "4/18," : "", + hid.type & BTC_HID_BLE ? "BLE," : "", + hid.type & BTC_HID_RCU ? "RCU," : "", + hid.type & BTC_HID_RCU_VOICE ? "RCU-Voice," : "", + hid.pair_cnt, bt_linfo->sut_pwr_level[1], + bt_linfo->golden_rx_shift[1]); } if (a2dp.exist) { - seq_printf(m, - " %-15s : type:%s, bit-pool:%d, flush-time:%d, ", - "[A2DP]", - a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS", - a2dp.bitpool, a2dp.flush_time); + p += scnprintf(p, end - p, + " %-15s : type:%s, bit-pool:%d, flush-time:%d, ", + "[A2DP]", + a2dp.type == BTC_A2DP_LEGACY ? "Legacy" : "TWS", + a2dp.bitpool, a2dp.flush_time); - seq_printf(m, - "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n", - a2dp.vendor_id, a2dp.device_name, - bt_linfo->sut_pwr_level[2], - bt_linfo->golden_rx_shift[2]); + p += scnprintf(p, end - p, + "vid:0x%x, Dev-name:0x%x, sut_pwr:%d, golden-rx:%d\n", + a2dp.vendor_id, a2dp.device_name, + bt_linfo->sut_pwr_level[2], + bt_linfo->golden_rx_shift[2]); } if (pan.exist) { - seq_printf(m, " %-15s : sut_pwr:%d, golden-rx:%d\n", - "[PAN]", - bt_linfo->sut_pwr_level[3], - bt_linfo->golden_rx_shift[3]); + p += scnprintf(p, end - p, + " %-15s : sut_pwr:%d, golden-rx:%d\n", + "[PAN]", + bt_linfo->sut_pwr_level[3], + bt_linfo->golden_rx_shift[3]); } + + return p - buf; } -static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_bt_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; @@ -8418,129 +8435,136 @@ static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_bt_link_info *bt_linfo = &bt->link_info; union rtw89_btc_module_info *md = &btc->mdinfo; + char *p = buf, *end = buf + bufsz; u8 *afh = bt_linfo->afh_map; u8 *afh_le = bt_linfo->afh_map_le; u8 bt_pos; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_BT)) - return; + return 0; if (ver->fcxinit == 7) bt_pos = md->md_v7.bt_pos; else bt_pos = md->md.bt_pos; - seq_puts(m, "========== [BT Status] ==========\n"); - - seq_printf(m, " %-15s : enable:%s, btg:%s%s, connect:%s, ", - "[status]", bt->enable.now ? "Y" : "N", - bt->btg_type ? "Y" : "N", - (bt->enable.now && (bt->btg_type != bt_pos) ? - "(efuse-mismatch!!)" : ""), - (bt_linfo->status.map.connect ? "Y" : "N")); - - seq_printf(m, "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n", - bt->igno_wl ? "Y" : "N", - bt->mbx_avl ? "Y" : "N", bt->rfk_info.val); - - seq_printf(m, " %-15s : profile:%s%s%s%s%s ", - "[profile]", - (bt_linfo->profile_cnt.now == 0) ? "None," : "", - bt_linfo->hfp_desc.exist ? "HFP," : "", - bt_linfo->hid_desc.exist ? "HID," : "", - bt_linfo->a2dp_desc.exist ? - (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "", - bt_linfo->pan_desc.exist ? "PAN," : ""); - - seq_printf(m, - "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n", - bt_linfo->multi_link.now ? "Y" : "N", - bt_linfo->slave_role ? "Slave" : "Master", - bt_linfo->status.map.ble_connect ? "Y" : "N", - bt_linfo->cqddr ? "Y" : "N", - bt_linfo->a2dp_desc.active ? "Y" : "N", - bt_linfo->pan_desc.active ? "Y" : "N"); - - seq_printf(m, - " %-15s : rssi:%ddBm(lvl:%d), tx_rate:%dM, %s%s%s", - "[link]", bt_linfo->rssi - 100, - bt->rssi_level, - bt_linfo->tx_3m ? 3 : 2, - bt_linfo->status.map.inq_pag ? " inq-page!!" : "", - bt_linfo->status.map.acl_busy ? " acl_busy!!" : "", - bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : ""); - - seq_printf(m, - "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ", - bt_linfo->relink.now ? " ReLink!!" : "", - afh[0], afh[1], afh[2], afh[3], afh[4], - afh[5], afh[6], afh[7], afh[8], afh[9]); + p += scnprintf(p, end - p, "========== [BT Status] ==========\n"); + + p += scnprintf(p, end - p, + " %-15s : enable:%s, btg:%s%s, connect:%s, ", + "[status]", bt->enable.now ? "Y" : "N", + bt->btg_type ? "Y" : "N", + (bt->enable.now && (bt->btg_type != bt_pos) ? + "(efuse-mismatch!!)" : ""), + (bt_linfo->status.map.connect ? "Y" : "N")); + + p += scnprintf(p, end - p, + "igno_wl:%s, mailbox_avl:%s, rfk_state:0x%x\n", + bt->igno_wl ? "Y" : "N", + bt->mbx_avl ? "Y" : "N", bt->rfk_info.val); + + p += scnprintf(p, end - p, " %-15s : profile:%s%s%s%s%s ", + "[profile]", + (bt_linfo->profile_cnt.now == 0) ? "None," : "", + bt_linfo->hfp_desc.exist ? "HFP," : "", + bt_linfo->hid_desc.exist ? "HID," : "", + bt_linfo->a2dp_desc.exist ? + (bt_linfo->a2dp_desc.sink ? "A2DP_sink," : "A2DP,") : "", + bt_linfo->pan_desc.exist ? "PAN," : ""); + + p += scnprintf(p, end - p, + "multi-link:%s, role:%s, ble-connect:%s, CQDDR:%s, A2DP_active:%s, PAN_active:%s\n", + bt_linfo->multi_link.now ? "Y" : "N", + bt_linfo->slave_role ? "Slave" : "Master", + bt_linfo->status.map.ble_connect ? "Y" : "N", + bt_linfo->cqddr ? "Y" : "N", + bt_linfo->a2dp_desc.active ? "Y" : "N", + bt_linfo->pan_desc.active ? "Y" : "N"); + + p += scnprintf(p, end - p, + " %-15s : rssi:%ddBm(lvl:%d), tx_rate:%dM, %s%s%s", + "[link]", bt_linfo->rssi - 100, + bt->rssi_level, + bt_linfo->tx_3m ? 3 : 2, + bt_linfo->status.map.inq_pag ? " inq-page!!" : "", + bt_linfo->status.map.acl_busy ? " acl_busy!!" : "", + bt_linfo->status.map.mesh_busy ? " mesh_busy!!" : ""); + + p += scnprintf(p, end - p, + "%s afh_map[%02x%02x_%02x%02x_%02x%02x_%02x%02x_%02x%02x], ", + bt_linfo->relink.now ? " ReLink!!" : "", + afh[0], afh[1], afh[2], afh[3], afh[4], + afh[5], afh[6], afh[7], afh[8], afh[9]); if (ver->fcxbtafh == 2 && bt_linfo->status.map.ble_connect) - seq_printf(m, - "LE[%02x%02x_%02x_%02x%02x]", - afh_le[0], afh_le[1], afh_le[2], - afh_le[3], afh_le[4]); - - seq_printf(m, "wl_ch_map[en:%d/ch:%d/bw:%d]\n", - wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw); - - seq_printf(m, - " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ", - "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY], - cx->cnt_bt[BTC_BCNT_RELINK], cx->cnt_bt[BTC_BCNT_RATECHG], - cx->cnt_bt[BTC_BCNT_REINIT], cx->cnt_bt[BTC_BCNT_REENABLE]); - - seq_printf(m, - "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n", - cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH], - cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ], - cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]); - - _show_bt_profile_info(rtwdev, m); - - seq_printf(m, - " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n", - "[bt_info]", bt->raw_info[2], bt->raw_info[3], - bt->raw_info[4], bt->raw_info[5], bt->raw_info[6], - bt->raw_info[7], - bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", - cx->cnt_bt[BTC_BCNT_INFOUPDATE], - cx->cnt_bt[BTC_BCNT_INFOSAME]); - - seq_printf(m, - " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)", - "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX], - cx->cnt_bt[BTC_BCNT_HIPRI_TX], cx->cnt_bt[BTC_BCNT_LOPRI_RX], - cx->cnt_bt[BTC_BCNT_LOPRI_TX], cx->cnt_bt[BTC_BCNT_POLUT]); + p += scnprintf(p, end - p, + "LE[%02x%02x_%02x_%02x%02x]", + afh_le[0], afh_le[1], afh_le[2], + afh_le[3], afh_le[4]); + + p += scnprintf(p, end - p, "wl_ch_map[en:%d/ch:%d/bw:%d]\n", + wl->afh_info.en, wl->afh_info.ch, wl->afh_info.bw); + + p += scnprintf(p, end - p, + " %-15s : retry:%d, relink:%d, rate_chg:%d, reinit:%d, reenable:%d, ", + "[stat_cnt]", cx->cnt_bt[BTC_BCNT_RETRY], + cx->cnt_bt[BTC_BCNT_RELINK], + cx->cnt_bt[BTC_BCNT_RATECHG], + cx->cnt_bt[BTC_BCNT_REINIT], + cx->cnt_bt[BTC_BCNT_REENABLE]); + + p += scnprintf(p, end - p, + "role-switch:%d, afh:%d, inq_page:%d(inq:%d/page:%d), igno_wl:%d\n", + cx->cnt_bt[BTC_BCNT_ROLESW], cx->cnt_bt[BTC_BCNT_AFH], + cx->cnt_bt[BTC_BCNT_INQPAG], cx->cnt_bt[BTC_BCNT_INQ], + cx->cnt_bt[BTC_BCNT_PAGE], cx->cnt_bt[BTC_BCNT_IGNOWL]); + + p += _show_bt_profile_info(rtwdev, p, end - p); + + p += scnprintf(p, end - p, + " %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)\n", + "[bt_info]", bt->raw_info[2], bt->raw_info[3], + bt->raw_info[4], bt->raw_info[5], bt->raw_info[6], + bt->raw_info[7], + bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", + cx->cnt_bt[BTC_BCNT_INFOUPDATE], + cx->cnt_bt[BTC_BCNT_INFOSAME]); + + p += scnprintf(p, end - p, + " %-15s : Hi-rx = %d, Hi-tx = %d, Lo-rx = %d, Lo-tx = %d (bt_polut_wl_tx = %d)", + "[trx_req_cnt]", cx->cnt_bt[BTC_BCNT_HIPRI_RX], + cx->cnt_bt[BTC_BCNT_HIPRI_TX], + cx->cnt_bt[BTC_BCNT_LOPRI_RX], + cx->cnt_bt[BTC_BCNT_LOPRI_TX], + cx->cnt_bt[BTC_BCNT_POLUT]); if (!bt->scan_info_update) { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, true); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } else { rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_SCAN_INFO, false); if (ver->fcxbtscan == 1) { - seq_printf(m, - "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), - le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl)); + p += scnprintf(p, end - p, + "(INQ:%d-%d/PAGE:%d-%d/LE:%d-%d/INIT:%d-%d)", + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INQ].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_PAGE].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_BLE].intvl), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].win), + le16_to_cpu(bt->scan_info_v1[BTC_SCAN_INIT].intvl)); } else if (ver->fcxbtscan == 2) { - seq_printf(m, - "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", - le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), - le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), - le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), - le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl)); + p += scnprintf(p, end - p, + "(BG:%d-%d/INIT:%d-%d/LE:%d-%d)", + le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_BG].intvl), + le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_INIT].intvl), + le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].win), + le16_to_cpu(bt->scan_info_v2[CXSCAN_LE].intvl)); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (bt_linfo->profile_cnt.now || bt_linfo->status.map.ble_connect) @@ -8560,6 +8584,8 @@ static void _show_bt_info(struct rtw89_dev *rtwdev, struct seq_file *m) rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, true); else rtw89_btc_fw_en_rpt(rtwdev, RPT_EN_BT_DEVICE_INFO, false); + + return p - buf; } #define CASE_BTC_RSN_STR(e) case BTC_RSN_ ## e: return #e @@ -8853,114 +8879,132 @@ static const char *id_to_ant(u32 id) } static -void seq_print_segment(struct seq_file *m, const char *prefix, u16 *data, - u8 len, u8 seg_len, u8 start_idx, u8 ring_len) +int scnprintf_segment(char *buf, size_t bufsz, const char *prefix, const u16 *data, + u8 len, u8 seg_len, u8 start_idx, u8 ring_len) { - u8 i; + char *p = buf, *end = buf + bufsz; u8 cur_index; + u8 i; for (i = 0; i < len ; i++) { if ((i % seg_len) == 0) - seq_printf(m, " %-15s : ", prefix); + p += scnprintf(p, end - p, " %-15s : ", prefix); cur_index = (start_idx + i) % ring_len; if (i % 3 == 0) - seq_printf(m, "-> %-20s", - steps_to_str(*(data + cur_index))); + p += scnprintf(p, end - p, "-> %-20s", + steps_to_str(*(data + cur_index))); else if (i % 3 == 1) - seq_printf(m, "-> %-15s", - steps_to_str(*(data + cur_index))); + p += scnprintf(p, end - p, "-> %-15s", + steps_to_str(*(data + cur_index))); else - seq_printf(m, "-> %-13s", - steps_to_str(*(data + cur_index))); + p += scnprintf(p, end - p, "-> %-13s", + steps_to_str(*(data + cur_index))); if (i == (len - 1) || (i % seg_len) == (seg_len - 1)) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + + return p - buf; } -static void _show_dm_step(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_dm_step(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; + char *p = buf, *end = buf + bufsz; u8 start_idx; u8 len; len = dm->dm_step.step_ov ? RTW89_BTC_DM_MAXSTEP : dm->dm_step.step_pos; start_idx = dm->dm_step.step_ov ? dm->dm_step.step_pos : 0; - seq_print_segment(m, "[dm_steps]", dm->dm_step.step, len, 6, start_idx, - ARRAY_SIZE(dm->dm_step.step)); + p += scnprintf_segment(p, end - p, "[dm_steps]", dm->dm_step.step, len, + 6, start_idx, ARRAY_SIZE(dm->dm_step.step)); + + return p - buf; } -static void _show_dm_info(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_dm_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &btc->cx.wl; struct rtw89_btc_bt_info *bt = &btc->cx.bt; + char *p = buf, *end = buf + bufsz; u8 igno_bt; if (!(dm->coex_info_map & BTC_COEX_INFO_DM)) - return; + return 0; - seq_printf(m, "========== [Mechanism Status %s] ==========\n", - (btc->manual_ctrl ? "(Manual)" : "(Auto)")); + p += scnprintf(p, end - p, + "========== [Mechanism Status %s] ==========\n", + (btc->manual_ctrl ? "(Manual)" : "(Auto)")); - seq_printf(m, - " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%s, init_mode:%s, run_cnt:%d\n", - "[status]", - btc->ant_type == BTC_ANT_SHARED ? "shared" : "dedicated", - steps_to_str(dm->run_reason), - steps_to_str(dm->run_action | BTC_ACT_EXT_BIT), - id_to_ant(FIELD_GET(GENMASK(7, 0), dm->set_ant_path)), - id_to_mode(wl->coex_mode), - dm->cnt_dm[BTC_DCNT_RUN]); + p += scnprintf(p, end - p, + " %-15s : type:%s, reason:%s(), action:%s(), ant_path:%s, init_mode:%s, run_cnt:%d\n", + "[status]", + btc->ant_type == BTC_ANT_SHARED ? "shared" : "dedicated", + steps_to_str(dm->run_reason), + steps_to_str(dm->run_action | BTC_ACT_EXT_BIT), + id_to_ant(FIELD_GET(GENMASK(7, 0), dm->set_ant_path)), + id_to_mode(wl->coex_mode), + dm->cnt_dm[BTC_DCNT_RUN]); - _show_dm_step(rtwdev, m); + p += _show_dm_step(rtwdev, p, end - p); if (ver->fcxctrl == 7) igno_bt = btc->ctrl.ctrl_v7.igno_bt; else igno_bt = btc->ctrl.ctrl.igno_bt; - seq_printf(m, " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ", - "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt, - dm->freerun, btc->lps, dm->wl_mimo_ps); + p += scnprintf(p, end - p, + " %-15s : wl_only:%d, bt_only:%d, igno_bt:%d, free_run:%d, wl_ps_ctrl:%d, wl_mimo_ps:%d, ", + "[dm_flag]", dm->wl_only, dm->bt_only, igno_bt, + dm->freerun, btc->lps, dm->wl_mimo_ps); - seq_printf(m, "leak_ap:%d, fw_offload:%s%s\n", dm->leak_ap, - (BTC_CX_FW_OFFLOAD ? "Y" : "N"), - (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ? - "" : "(Mismatch!!)")); + p += scnprintf(p, end - p, "leak_ap:%d, fw_offload:%s%s\n", + dm->leak_ap, + (BTC_CX_FW_OFFLOAD ? "Y" : "N"), + (dm->wl_fw_cx_offload == BTC_CX_FW_OFFLOAD ? + "" : "(Mismatch!!)")); if (dm->rf_trx_para.wl_tx_power == 0xff) - seq_printf(m, - " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ", - "[trx_ctrl]", wl->rssi_level, dm->trx_para_level); + p += scnprintf(p, end - p, + " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:orig, ", + "[trx_ctrl]", wl->rssi_level, + dm->trx_para_level); else - seq_printf(m, - " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ", - "[trx_ctrl]", wl->rssi_level, dm->trx_para_level, - dm->rf_trx_para.wl_tx_power); + p += scnprintf(p, end - p, + " %-15s : wl_rssi_lvl:%d, para_lvl:%d, wl_tx_pwr:%d, ", + "[trx_ctrl]", wl->rssi_level, + dm->trx_para_level, + dm->rf_trx_para.wl_tx_power); - seq_printf(m, - "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n", - dm->rf_trx_para.wl_rx_gain, dm->rf_trx_para.bt_tx_power, - dm->rf_trx_para.bt_rx_gain, - (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx); + p += scnprintf(p, end - p, + "wl_rx_lvl:%d, bt_tx_pwr_dec:%d, bt_rx_lna:%d(%s-tbl), wl_btg_rx:%d\n", + dm->rf_trx_para.wl_rx_gain, + dm->rf_trx_para.bt_tx_power, + dm->rf_trx_para.bt_rx_gain, + (bt->hi_lna_rx ? "Hi" : "Ori"), dm->wl_btg_rx); - seq_printf(m, - " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n", - "[dm_ctrl]", dm->wl_tx_limit.enable, dm->wl_tx_limit.tx_time, - dm->wl_tx_limit.tx_retry, btc->bt_req_len, bt->scan_rx_low_pri); + p += scnprintf(p, end - p, + " %-15s : wl_tx_limit[en:%d/max_t:%dus/max_retry:%d], bt_slot_reg:%d-TU, bt_scan_rx_low_pri:%d\n", + "[dm_ctrl]", dm->wl_tx_limit.enable, + dm->wl_tx_limit.tx_time, + dm->wl_tx_limit.tx_retry, btc->bt_req_len, + bt->scan_rx_low_pri); + + return p - buf; } -static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_error(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; union rtw89_btc_fbtc_cysta_info *pcysta; + char *p = buf, *end = buf + bufsz; u32 except_cnt, exception_map; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo; @@ -8985,81 +9029,87 @@ static void _show_error(struct rtw89_dev *rtwdev, struct seq_file *m) except_cnt = pcysta->v7.except_cnt; exception_map = le32_to_cpu(pcysta->v7.except_map); } else { - return; + return 0; } if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW] == 0 && except_cnt == 0 && !pfwinfo->len_mismch && !pfwinfo->fver_mismch) - return; + return 0; - seq_printf(m, " %-15s : ", "[error]"); + p += scnprintf(p, end - p, " %-15s : ", "[error]"); if (pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]) { - seq_printf(m, - "overflow-cnt: %d, ", - pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]); + p += scnprintf(p, end - p, + "overflow-cnt: %d, ", + pfwinfo->event[BTF_EVNT_BUF_OVERFLOW]); } if (pfwinfo->len_mismch) { - seq_printf(m, - "len-mismatch: 0x%x, ", - pfwinfo->len_mismch); + p += scnprintf(p, end - p, + "len-mismatch: 0x%x, ", + pfwinfo->len_mismch); } if (pfwinfo->fver_mismch) { - seq_printf(m, - "fver-mismatch: 0x%x, ", - pfwinfo->fver_mismch); + p += scnprintf(p, end - p, + "fver-mismatch: 0x%x, ", + pfwinfo->fver_mismch); } /* cycle statistics exceptions */ if (exception_map || except_cnt) { - seq_printf(m, - "exception-type: 0x%x, exception-cnt = %d", - exception_map, except_cnt); + p += scnprintf(p, end - p, + "exception-type: 0x%x, exception-cnt = %d", + exception_map, except_cnt); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void _show_fbtc_tdma(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_tdma(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_tdma *t = NULL; + char *p = buf, *end = buf + bufsz; pcinfo = &pfwinfo->rpt_fbtc_tdma.cinfo; if (!pcinfo->valid) - return; + return 0; if (ver->fcxtdma == 1) t = &pfwinfo->rpt_fbtc_tdma.finfo.v1; else t = &pfwinfo->rpt_fbtc_tdma.finfo.v3.tdma; - seq_printf(m, - " %-15s : ", "[tdma_policy]"); - seq_printf(m, - "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ", - (u32)t->type, - t->rxflctrl, t->txpause); + p += scnprintf(p, end - p, + " %-15s : ", "[tdma_policy]"); + p += scnprintf(p, end - p, + "type:%d, rx_flow_ctrl:%d, tx_pause:%d, ", + (u32)t->type, + t->rxflctrl, t->txpause); - seq_printf(m, - "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ", - t->wtgle_n, t->leak_n, t->ext_ctrl); + p += scnprintf(p, end - p, + "wl_toggle_n:%d, leak_n:%d, ext_ctrl:%d, ", + t->wtgle_n, t->leak_n, t->ext_ctrl); - seq_printf(m, - "policy_type:%d", - (u32)btc->policy_type); + p += scnprintf(p, end - p, + "policy_type:%d", + (u32)btc->policy_type); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_slots(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_dm *dm = &btc->dm; + char *p = buf, *end = buf + bufsz; u16 dur, cxtype; u32 tbl; u8 i = 0; @@ -9074,28 +9124,30 @@ static void _show_fbtc_slots(struct rtw89_dev *rtwdev, struct seq_file *m) tbl = le32_to_cpu(dm->slot_now.v7[i].cxtbl); cxtype = le16_to_cpu(dm->slot_now.v7[i].cxtype); } else { - return; + return 0; } if (i % 5 == 0) - seq_printf(m, - " %-15s : %5s[%03d/0x%x/%d]", - "[slot_list]", - id_to_slot((u32)i), - dur, tbl, cxtype); + p += scnprintf(p, end - p, + " %-15s : %5s[%03d/0x%x/%d]", + "[slot_list]", + id_to_slot((u32)i), + dur, tbl, cxtype); else - seq_printf(m, - ", %5s[%03d/0x%x/%d]", - id_to_slot((u32)i), - dur, tbl, cxtype); + p += scnprintf(p, end - p, + ", %5s[%03d/0x%x/%d]", + id_to_slot((u32)i), + dur, tbl, cxtype); if (i % 5 == 4) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -9104,63 +9156,64 @@ static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_cysta_v2 *pcysta_le32 = NULL; union rtw89_btc_fbtc_rxflct r; - u8 i, cnt = 0, slot_pair; u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; + u8 i, cnt = 0, slot_pair; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta_le32 = &pfwinfo->rpt_fbtc_cysta.finfo.v2; - seq_printf(m, - " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", - "[cycle_cnt]", - le16_to_cpu(pcysta_le32->cycles), - le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL]), - le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL_OK]), - le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_SLOT]), - le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta_le32->cycles), + le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL]), + le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_ALL_OK]), + le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_SLOT]), + le32_to_cpu(pcysta_le32->bcn_cnt[CXBCN_BT_OK])); for (i = 0; i < CXST_MAX; i++) { if (!le32_to_cpu(pcysta_le32->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", id_to_slot((u32)i), - le32_to_cpu(pcysta_le32->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", id_to_slot((u32)i), + le32_to_cpu(pcysta_le32->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) { - seq_printf(m, ", leak_rx:%d", - le32_to_cpu(pcysta_le32->leakrx_cnt)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta_le32->leakrx_cnt)); } if (le32_to_cpu(pcysta_le32->collision_cnt)) { - seq_printf(m, ", collision:%d", - le32_to_cpu(pcysta_le32->collision_cnt)); + p += scnprintf(p, end - p, ", collision:%d", + le32_to_cpu(pcysta_le32->collision_cnt)); } if (le32_to_cpu(pcysta_le32->skip_cnt)) { - seq_printf(m, ", skip:%d", - le32_to_cpu(pcysta_le32->skip_cnt)); - } - seq_puts(m, "\n"); - - seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_time]", - le16_to_cpu(pcysta_le32->tavg_cycle[CXT_WL]), - le16_to_cpu(pcysta_le32->tavg_cycle[CXT_BT]), - le16_to_cpu(pcysta_le32->tavg_lk) / 1000, - le16_to_cpu(pcysta_le32->tavg_lk) % 1000); - seq_printf(m, ", max_t[wl:%d/bt:%d/lk:%d.%03d]", - le16_to_cpu(pcysta_le32->tmax_cycle[CXT_WL]), - le16_to_cpu(pcysta_le32->tmax_cycle[CXT_BT]), - le16_to_cpu(pcysta_le32->tmax_lk) / 1000, - le16_to_cpu(pcysta_le32->tmax_lk) % 1000); - seq_printf(m, ", maxdiff_t[wl:%d/bt:%d]\n", - le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_WL]), - le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_BT])); + p += scnprintf(p, end - p, ", skip:%d", + le32_to_cpu(pcysta_le32->skip_cnt)); + } + p += scnprintf(p, end - p, "\n"); + + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta_le32->tavg_cycle[CXT_WL]), + le16_to_cpu(pcysta_le32->tavg_cycle[CXT_BT]), + le16_to_cpu(pcysta_le32->tavg_lk) / 1000, + le16_to_cpu(pcysta_le32->tavg_lk) % 1000); + p += scnprintf(p, end - p, ", max_t[wl:%d/bt:%d/lk:%d.%03d]", + le16_to_cpu(pcysta_le32->tmax_cycle[CXT_WL]), + le16_to_cpu(pcysta_le32->tmax_cycle[CXT_BT]), + le16_to_cpu(pcysta_le32->tmax_lk) / 1000, + le16_to_cpu(pcysta_le32->tmax_lk) % 1000); + p += scnprintf(p, end - p, ", maxdiff_t[wl:%d/bt:%d]\n", + le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_WL]), + le16_to_cpu(pcysta_le32->tmaxdiff_cycle[CXT_BT])); if (le16_to_cpu(pcysta_le32->cycles) <= 1) - return; + goto out; /* 1 cycle record 1 wl-slot and 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9177,53 +9230,57 @@ static void _show_fbtc_cysta_v2(struct rtw89_dev *rtwdev, struct seq_file *m) store_index = ((cycle - 1) % slot_pair) * 2; if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 1) - seq_printf(m, - " %-15s : ->b%02d->w%02d", "[cycle_step]", - le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), - le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); + p += scnprintf(p, end - p, + " %-15s : ->b%02d->w%02d", + "[cycle_step]", + le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), + le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); else - seq_printf(m, - "->b%02d->w%02d", - le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), - le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); + p += scnprintf(p, end - p, + "->b%02d->w%02d", + le16_to_cpu(pcysta_le32->tslot_cycle[store_index]), + le16_to_cpu(pcysta_le32->tslot_cycle[store_index + 1])); if (cnt % (BTC_CYCLE_SLOT_MAX / 4) == 0 || cnt == c_end) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (a2dp->exist) { - seq_printf(m, - " %-15s : a2dp_ept:%d, a2dp_late:%d", - "[a2dp_t_sta]", - le16_to_cpu(pcysta_le32->a2dpept), - le16_to_cpu(pcysta_le32->a2dpeptto)); - - seq_printf(m, - ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta_le32->tavg_a2dpept), - le16_to_cpu(pcysta_le32->tmax_a2dpept)); + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta_le32->a2dpept), + le16_to_cpu(pcysta_le32->a2dpeptto)); + + p += scnprintf(p, end - p, + ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta_le32->tavg_a2dpept), + le16_to_cpu(pcysta_le32->tmax_a2dpept)); r.val = dm->tdma_now.rxflctrl; if (r.type && r.tgln_n) { - seq_printf(m, - ", cycle[PSTDMA:%d/TDMA:%d], ", - le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_ON]), - le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_OFF])); - - seq_printf(m, - "avg_t[PSTDMA:%d/TDMA:%d], ", - le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_ON]), - le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_OFF])); - - seq_printf(m, - "max_t[PSTDMA:%d/TDMA:%d]", - le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_ON]), - le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_OFF])); + p += scnprintf(p, end - p, + ", cycle[PSTDMA:%d/TDMA:%d], ", + le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_ON]), + le16_to_cpu(pcysta_le32->cycles_a2dp[CXT_FLCTRL_OFF])); + + p += scnprintf(p, end - p, + "avg_t[PSTDMA:%d/TDMA:%d], ", + le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_ON]), + le16_to_cpu(pcysta_le32->tavg_a2dp[CXT_FLCTRL_OFF])); + + p += scnprintf(p, end - p, + "max_t[PSTDMA:%d/TDMA:%d]", + le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_ON]), + le16_to_cpu(pcysta_le32->tmax_a2dp[CXT_FLCTRL_OFF])); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9234,60 +9291,64 @@ static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_rpt_cmn_info *pcinfo; u8 i, cnt = 0, slot_pair, divide_cnt; u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v3; - seq_printf(m, - " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", - "[cycle_cnt]", - le16_to_cpu(pcysta->cycles), - le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), - le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), - le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), - le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta->cycles), + le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le32_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le32_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); for (i = 0; i < CXST_MAX; i++) { if (!le32_to_cpu(pcysta->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", id_to_slot(i), - le32_to_cpu(pcysta->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), + le32_to_cpu(pcysta->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) - seq_printf(m, ", leak_rx:%d", le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); if (le32_to_cpu(pcysta->collision_cnt)) - seq_printf(m, ", collision:%d", le32_to_cpu(pcysta->collision_cnt)); + p += scnprintf(p, end - p, ", collision:%d", + le32_to_cpu(pcysta->collision_cnt)); if (le32_to_cpu(pcysta->skip_cnt)) - seq_printf(m, ", skip:%d", le32_to_cpu(pcysta->skip_cnt)); - - seq_puts(m, "\n"); - - seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_time]", - le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tavg) / 1000, - le16_to_cpu(pcysta->leak_slot.tavg) % 1000); - seq_printf(m, - ", max_t[wl:%d/bt:%d/lk:%d.%03d]", - le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tmax) / 1000, - le16_to_cpu(pcysta->leak_slot.tmax) % 1000); - seq_printf(m, - ", maxdiff_t[wl:%d/bt:%d]\n", - le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); + p += scnprintf(p, end - p, ", skip:%d", + le32_to_cpu(pcysta->skip_cnt)); + + p += scnprintf(p, end - p, "\n"); + + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d/lk:%d.%03d]", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tmax) / 1000, + le16_to_cpu(pcysta->leak_slot.tmax) % 1000); + p += scnprintf(p, end - p, + ", maxdiff_t[wl:%d/bt:%d]\n", + le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); cycle = le16_to_cpu(pcysta->cycles); if (cycle <= 1) - return; + goto out; /* 1 cycle record 1 wl-slot and 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9309,51 +9370,56 @@ static void _show_fbtc_cysta_v3(struct rtw89_dev *rtwdev, struct seq_file *m) store_index = ((cycle - 1) % slot_pair) * 2; if (cnt % divide_cnt == 1) - seq_printf(m, " %-15s : ", "[cycle_step]"); + p += scnprintf(p, end - p, " %-15s : ", + "[cycle_step]"); - seq_printf(m, "->b%02d", - le16_to_cpu(pcysta->slot_step_time[store_index])); + p += scnprintf(p, end - p, "->b%02d", + le16_to_cpu(pcysta->slot_step_time[store_index])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } - seq_printf(m, "->w%02d", - le16_to_cpu(pcysta->slot_step_time[store_index + 1])); + p += scnprintf(p, end - p, "->w%02d", + le16_to_cpu(pcysta->slot_step_time[store_index + 1])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } if (cnt % divide_cnt == 0 || cnt == c_end) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (a2dp->exist) { - seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", - "[a2dp_t_sta]", - le16_to_cpu(pcysta->a2dp_ept.cnt), - le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - seq_printf(m, ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta->a2dp_ept.tavg), - le16_to_cpu(pcysta->a2dp_ept.tmax)); + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9364,62 +9430,64 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_rpt_cmn_info *pcinfo; u8 i, cnt = 0, slot_pair, divide_cnt; u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v4; - seq_printf(m, - " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", - "[cycle_cnt]", - le16_to_cpu(pcysta->cycles), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta->cycles), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); for (i = 0; i < CXST_MAX; i++) { if (!le16_to_cpu(pcysta->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", id_to_slot(i), - le16_to_cpu(pcysta->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), + le16_to_cpu(pcysta->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) - seq_printf(m, ", leak_rx:%d", - le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); if (pcysta->collision_cnt) - seq_printf(m, ", collision:%d", pcysta->collision_cnt); + p += scnprintf(p, end - p, ", collision:%d", + pcysta->collision_cnt); if (le16_to_cpu(pcysta->skip_cnt)) - seq_printf(m, ", skip:%d", - le16_to_cpu(pcysta->skip_cnt)); - - seq_puts(m, "\n"); - - seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_time]", - le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tavg) / 1000, - le16_to_cpu(pcysta->leak_slot.tavg) % 1000); - seq_printf(m, - ", max_t[wl:%d/bt:%d/lk:%d.%03d]", - le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tmax) / 1000, - le16_to_cpu(pcysta->leak_slot.tmax) % 1000); - seq_printf(m, - ", maxdiff_t[wl:%d/bt:%d]\n", - le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); + p += scnprintf(p, end - p, ", skip:%d", + le16_to_cpu(pcysta->skip_cnt)); + + p += scnprintf(p, end - p, "\n"); + + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d/lk:%d.%03d]", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tmax) / 1000, + le16_to_cpu(pcysta->leak_slot.tmax) % 1000); + p += scnprintf(p, end - p, + ", maxdiff_t[wl:%d/bt:%d]\n", + le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmaxdiff[CXT_BT])); cycle = le16_to_cpu(pcysta->cycles); if (cycle <= 1) - return; + goto out; /* 1 cycle record 1 wl-slot and 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9441,51 +9509,56 @@ static void _show_fbtc_cysta_v4(struct rtw89_dev *rtwdev, struct seq_file *m) store_index = ((cycle - 1) % slot_pair) * 2; if (cnt % divide_cnt == 1) - seq_printf(m, " %-15s : ", "[cycle_step]"); + p += scnprintf(p, end - p, " %-15s : ", + "[cycle_step]"); - seq_printf(m, "->b%02d", - le16_to_cpu(pcysta->slot_step_time[store_index])); + p += scnprintf(p, end - p, "->b%02d", + le16_to_cpu(pcysta->slot_step_time[store_index])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } - seq_printf(m, "->w%02d", - le16_to_cpu(pcysta->slot_step_time[store_index + 1])); + p += scnprintf(p, end - p, "->w%02d", + le16_to_cpu(pcysta->slot_step_time[store_index + 1])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } if (cnt % divide_cnt == 0 || cnt == c_end) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (a2dp->exist) { - seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", - "[a2dp_t_sta]", - le16_to_cpu(pcysta->a2dp_ept.cnt), - le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - seq_printf(m, ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta->a2dp_ept.tavg), - le16_to_cpu(pcysta->a2dp_ept.tmax)); + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_bt_a2dp_desc *a2dp = &btc->cx.bt.link_info.a2dp_desc; @@ -9496,58 +9569,60 @@ static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_rpt_cmn_info *pcinfo; u8 i, cnt = 0, slot_pair, divide_cnt; u16 cycle, c_begin, c_end, store_index; + char *p = buf, *end = buf + bufsz; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v5; - seq_printf(m, - " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", - "[cycle_cnt]", - le16_to_cpu(pcysta->cycles), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, + " %-15s : cycle:%d, bcn[all:%d/all_ok:%d/bt:%d/bt_ok:%d]", + "[cycle_cnt]", + le16_to_cpu(pcysta->cycles), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); for (i = 0; i < CXST_MAX; i++) { if (!le16_to_cpu(pcysta->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", id_to_slot(i), - le16_to_cpu(pcysta->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", id_to_slot(i), + le16_to_cpu(pcysta->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) - seq_printf(m, ", leak_rx:%d", - le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); if (pcysta->collision_cnt) - seq_printf(m, ", collision:%d", pcysta->collision_cnt); + p += scnprintf(p, end - p, ", collision:%d", + pcysta->collision_cnt); if (le16_to_cpu(pcysta->skip_cnt)) - seq_printf(m, ", skip:%d", - le16_to_cpu(pcysta->skip_cnt)); - - seq_puts(m, "\n"); - - seq_printf(m, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_time]", - le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tavg) / 1000, - le16_to_cpu(pcysta->leak_slot.tavg) % 1000); - seq_printf(m, - ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n", - le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tmax) / 1000, - le16_to_cpu(pcysta->leak_slot.tmax) % 1000); + p += scnprintf(p, end - p, ", skip:%d", + le16_to_cpu(pcysta->skip_cnt)); + + p += scnprintf(p, end - p, "\n"); + + p += scnprintf(p, end - p, " %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_time]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d/lk:%d.%03d]\n", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tmax) / 1000, + le16_to_cpu(pcysta->leak_slot.tmax) % 1000); cycle = le16_to_cpu(pcysta->cycles); if (cycle <= 1) - return; + goto out; /* 1 cycle record 1 wl-slot and 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9565,58 +9640,63 @@ static void _show_fbtc_cysta_v5(struct rtw89_dev *rtwdev, struct seq_file *m) divide_cnt = BTC_CYCLE_SLOT_MAX / 4; if (c_begin > c_end) - return; + goto out; for (cycle = c_begin; cycle <= c_end; cycle++) { cnt++; store_index = ((cycle - 1) % slot_pair) * 2; if (cnt % divide_cnt == 1) - seq_printf(m, " %-15s : ", "[cycle_step]"); + p += scnprintf(p, end - p, " %-15s : ", + "[cycle_step]"); - seq_printf(m, "->b%02d", - le16_to_cpu(pcysta->slot_step_time[store_index])); + p += scnprintf(p, end - p, "->b%02d", + le16_to_cpu(pcysta->slot_step_time[store_index])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } - seq_printf(m, "->w%02d", - le16_to_cpu(pcysta->slot_step_time[store_index + 1])); + p += scnprintf(p, end - p, "->w%02d", + le16_to_cpu(pcysta->slot_step_time[store_index + 1])); if (a2dp->exist) { a2dp_trx = &pcysta->a2dp_trx[store_index + 1]; - seq_printf(m, "(%d/%d/%dM/%d/%d/%d)", - a2dp_trx->empty_cnt, - a2dp_trx->retry_cnt, - a2dp_trx->tx_rate ? 3 : 2, - a2dp_trx->tx_cnt, - a2dp_trx->ack_cnt, - a2dp_trx->nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%dM/%d/%d/%d)", + a2dp_trx->empty_cnt, + a2dp_trx->retry_cnt, + a2dp_trx->tx_rate ? 3 : 2, + a2dp_trx->tx_cnt, + a2dp_trx->ack_cnt, + a2dp_trx->nack_cnt); } if (cnt % divide_cnt == 0 || cnt == c_end) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } if (a2dp->exist) { - seq_printf(m, " %-15s : a2dp_ept:%d, a2dp_late:%d", - "[a2dp_t_sta]", - le16_to_cpu(pcysta->a2dp_ept.cnt), - le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); + p += scnprintf(p, end - p, + " %-15s : a2dp_ept:%d, a2dp_late:%d", + "[a2dp_t_sta]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout)); - seq_printf(m, ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta->a2dp_ept.tavg), - le16_to_cpu(pcysta->a2dp_ept.tmax)); + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_bt_info *bt = &rtwdev->btc.cx.bt; struct rtw89_btc_bt_a2dp_desc *a2dp = &bt->link_info.a2dp_desc; @@ -9624,68 +9704,75 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_fbtc_cysta_v7 *pcysta = NULL; struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_rpt_cmn_info *pcinfo; + char *p = buf, *end = buf + bufsz; u16 cycle, c_begin, c_end, s_id; u8 i, cnt = 0, divide_cnt; u8 slot_pair; pcinfo = &pfwinfo->rpt_fbtc_cysta.cinfo; if (!pcinfo->valid) - return; + return 0; pcysta = &pfwinfo->rpt_fbtc_cysta.finfo.v7; - seq_printf(m, "\n\r %-15s : cycle:%d", "[slot_stat]", - le16_to_cpu(pcysta->cycles)); + p += scnprintf(p, end - p, "\n\r %-15s : cycle:%d", "[slot_stat]", + le16_to_cpu(pcysta->cycles)); for (i = 0; i < CXST_MAX; i++) { if (!le16_to_cpu(pcysta->slot_cnt[i])) continue; - seq_printf(m, ", %s:%d", - id_to_slot(i), le16_to_cpu(pcysta->slot_cnt[i])); + p += scnprintf(p, end - p, ", %s:%d", + id_to_slot(i), + le16_to_cpu(pcysta->slot_cnt[i])); } if (dm->tdma_now.rxflctrl) - seq_printf(m, ", leak_rx:%d", - le32_to_cpu(pcysta->leak_slot.cnt_rximr)); + p += scnprintf(p, end - p, ", leak_rx:%d", + le32_to_cpu(pcysta->leak_slot.cnt_rximr)); if (pcysta->collision_cnt) - seq_printf(m, ", collision:%d", pcysta->collision_cnt); + p += scnprintf(p, end - p, ", collision:%d", + pcysta->collision_cnt); if (pcysta->skip_cnt) - seq_printf(m, ", skip:%d", le16_to_cpu(pcysta->skip_cnt)); - - seq_printf(m, "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", - "[cycle_stat]", - le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), - le16_to_cpu(pcysta->leak_slot.tavg) / 1000, - le16_to_cpu(pcysta->leak_slot.tavg) % 1000); - seq_printf(m, ", max_t[wl:%d/bt:%d(>%dms:%d)/lk:%d.%03d]", - le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), - le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), - dm->bt_slot_flood, dm->cnt_dm[BTC_DCNT_BT_SLOT_FLOOD], - le16_to_cpu(pcysta->leak_slot.tamx) / 1000, - le16_to_cpu(pcysta->leak_slot.tamx) % 1000); - seq_printf(m, ", bcn[all:%d/ok:%d/in_bt:%d/in_bt_ok:%d]", - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), - le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); + p += scnprintf(p, end - p, ", skip:%d", + le16_to_cpu(pcysta->skip_cnt)); + + p += scnprintf(p, end - p, + "\n\r %-15s : avg_t[wl:%d/bt:%d/lk:%d.%03d]", + "[cycle_stat]", + le16_to_cpu(pcysta->cycle_time.tavg[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tavg[CXT_BT]), + le16_to_cpu(pcysta->leak_slot.tavg) / 1000, + le16_to_cpu(pcysta->leak_slot.tavg) % 1000); + p += scnprintf(p, end - p, + ", max_t[wl:%d/bt:%d(>%dms:%d)/lk:%d.%03d]", + le16_to_cpu(pcysta->cycle_time.tmax[CXT_WL]), + le16_to_cpu(pcysta->cycle_time.tmax[CXT_BT]), + dm->bt_slot_flood, dm->cnt_dm[BTC_DCNT_BT_SLOT_FLOOD], + le16_to_cpu(pcysta->leak_slot.tamx) / 1000, + le16_to_cpu(pcysta->leak_slot.tamx) % 1000); + p += scnprintf(p, end - p, ", bcn[all:%d/ok:%d/in_bt:%d/in_bt_ok:%d]", + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_ALL_OK]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_SLOT]), + le16_to_cpu(pcysta->bcn_cnt[CXBCN_BT_OK])); if (a2dp->exist) { - seq_printf(m, - "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)", - "[a2dp_stat]", - le16_to_cpu(pcysta->a2dp_ept.cnt), - le16_to_cpu(pcysta->a2dp_ept.cnt_timeout), - a2dp->no_empty_streak_2s, a2dp->no_empty_streak_max); + p += scnprintf(p, end - p, + "\n\r %-15s : a2dp_ept:%d, a2dp_late:%d(streak 2S:%d/max:%d)", + "[a2dp_stat]", + le16_to_cpu(pcysta->a2dp_ept.cnt), + le16_to_cpu(pcysta->a2dp_ept.cnt_timeout), + a2dp->no_empty_streak_2s, + a2dp->no_empty_streak_max); - seq_printf(m, ", avg_t:%d, max_t:%d", - le16_to_cpu(pcysta->a2dp_ept.tavg), - le16_to_cpu(pcysta->a2dp_ept.tmax)); + p += scnprintf(p, end - p, ", avg_t:%d, max_t:%d", + le16_to_cpu(pcysta->a2dp_ept.tavg), + le16_to_cpu(pcysta->a2dp_ept.tmax)); } if (le16_to_cpu(pcysta->cycles) <= 1) - return; + goto out; /* 1 cycle = 1 wl-slot + 1 bt-slot */ slot_pair = BTC_CYCLE_SLOT_MAX / 2; @@ -9703,7 +9790,7 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) divide_cnt = 6; if (c_begin > c_end) - return; + goto out; for (cycle = c_begin; cycle <= c_end; cycle++) { cnt++; @@ -9711,129 +9798,142 @@ static void _show_fbtc_cysta_v7(struct rtw89_dev *rtwdev, struct seq_file *m) if (cnt % divide_cnt == 1) { if (a2dp->exist) - seq_printf(m, "\n\r %-15s : ", "[slotT_wermtan]"); + p += scnprintf(p, end - p, "\n\r %-15s : ", + "[slotT_wermtan]"); else - seq_printf(m, "\n\r %-15s : ", "[slotT_rxerr]"); + p += scnprintf(p, end - p, "\n\r %-15s : ", + "[slotT_rxerr]"); } - seq_printf(m, "->b%d", le16_to_cpu(pcysta->slot_step_time[s_id])); + p += scnprintf(p, end - p, "->b%d", + le16_to_cpu(pcysta->slot_step_time[s_id])); if (a2dp->exist) - seq_printf(m, "(%d/%d/%d/%dM/%d/%d/%d)", - pcysta->wl_rx_err_ratio[s_id], - pcysta->a2dp_trx[s_id].empty_cnt, - pcysta->a2dp_trx[s_id].retry_cnt, - (pcysta->a2dp_trx[s_id].tx_rate ? 3 : 2), - pcysta->a2dp_trx[s_id].tx_cnt, - pcysta->a2dp_trx[s_id].ack_cnt, - pcysta->a2dp_trx[s_id].nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%d/%dM/%d/%d/%d)", + pcysta->wl_rx_err_ratio[s_id], + pcysta->a2dp_trx[s_id].empty_cnt, + pcysta->a2dp_trx[s_id].retry_cnt, + (pcysta->a2dp_trx[s_id].tx_rate ? 3 : 2), + pcysta->a2dp_trx[s_id].tx_cnt, + pcysta->a2dp_trx[s_id].ack_cnt, + pcysta->a2dp_trx[s_id].nack_cnt); else - seq_printf(m, "(%d)", pcysta->wl_rx_err_ratio[s_id]); + p += scnprintf(p, end - p, "(%d)", + pcysta->wl_rx_err_ratio[s_id]); - seq_printf(m, "->w%d", le16_to_cpu(pcysta->slot_step_time[s_id + 1])); + p += scnprintf(p, end - p, "->w%d", + le16_to_cpu(pcysta->slot_step_time[s_id + 1])); if (a2dp->exist) - seq_printf(m, "(%d/%d/%d/%dM/%d/%d/%d)", - pcysta->wl_rx_err_ratio[s_id + 1], - pcysta->a2dp_trx[s_id + 1].empty_cnt, - pcysta->a2dp_trx[s_id + 1].retry_cnt, - (pcysta->a2dp_trx[s_id + 1].tx_rate ? 3 : 2), - pcysta->a2dp_trx[s_id + 1].tx_cnt, - pcysta->a2dp_trx[s_id + 1].ack_cnt, - pcysta->a2dp_trx[s_id + 1].nack_cnt); + p += scnprintf(p, end - p, "(%d/%d/%d/%dM/%d/%d/%d)", + pcysta->wl_rx_err_ratio[s_id + 1], + pcysta->a2dp_trx[s_id + 1].empty_cnt, + pcysta->a2dp_trx[s_id + 1].retry_cnt, + (pcysta->a2dp_trx[s_id + 1].tx_rate ? 3 : 2), + pcysta->a2dp_trx[s_id + 1].tx_cnt, + pcysta->a2dp_trx[s_id + 1].ack_cnt, + pcysta->a2dp_trx[s_id + 1].nack_cnt); else - seq_printf(m, "(%d)", pcysta->wl_rx_err_ratio[s_id + 1]); + p += scnprintf(p, end - p, "(%d)", + pcysta->wl_rx_err_ratio[s_id + 1]); } + +out: + return p - buf; } -static void _show_fbtc_nullsta(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_nullsta(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo; union rtw89_btc_fbtc_cynullsta_info *ns; + char *p = buf, *end = buf + bufsz; u8 i = 0; if (!btc->dm.tdma_now.rxflctrl) - return; + return 0; pcinfo = &pfwinfo->rpt_fbtc_nullsta.cinfo; if (!pcinfo->valid) - return; + return 0; ns = &pfwinfo->rpt_fbtc_nullsta.finfo; if (ver->fcxnullsta == 1) { for (i = 0; i < 2; i++) { - seq_printf(m, " %-15s : ", "[NULL-STA]"); - seq_printf(m, "null-%d", i); - seq_printf(m, "[ok:%d/", - le32_to_cpu(ns->v1.result[i][1])); - seq_printf(m, "fail:%d/", - le32_to_cpu(ns->v1.result[i][0])); - seq_printf(m, "on_time:%d/", - le32_to_cpu(ns->v1.result[i][2])); - seq_printf(m, "retry:%d/", - le32_to_cpu(ns->v1.result[i][3])); - seq_printf(m, "avg_t:%d.%03d/", - le32_to_cpu(ns->v1.avg_t[i]) / 1000, - le32_to_cpu(ns->v1.avg_t[i]) % 1000); - seq_printf(m, "max_t:%d.%03d]\n", - le32_to_cpu(ns->v1.max_t[i]) / 1000, - le32_to_cpu(ns->v1.max_t[i]) % 1000); + p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]"); + p += scnprintf(p, end - p, "null-%d", i); + p += scnprintf(p, end - p, "[ok:%d/", + le32_to_cpu(ns->v1.result[i][1])); + p += scnprintf(p, end - p, "fail:%d/", + le32_to_cpu(ns->v1.result[i][0])); + p += scnprintf(p, end - p, "on_time:%d/", + le32_to_cpu(ns->v1.result[i][2])); + p += scnprintf(p, end - p, "retry:%d/", + le32_to_cpu(ns->v1.result[i][3])); + p += scnprintf(p, end - p, "avg_t:%d.%03d/", + le32_to_cpu(ns->v1.avg_t[i]) / 1000, + le32_to_cpu(ns->v1.avg_t[i]) % 1000); + p += scnprintf(p, end - p, "max_t:%d.%03d]\n", + le32_to_cpu(ns->v1.max_t[i]) / 1000, + le32_to_cpu(ns->v1.max_t[i]) % 1000); } } else if (ver->fcxnullsta == 7) { for (i = 0; i < 2; i++) { - seq_printf(m, " %-15s : ", "[NULL-STA]"); - seq_printf(m, "null-%d", i); - seq_printf(m, "[Tx:%d/", - le32_to_cpu(ns->v7.result[i][4])); - seq_printf(m, "[ok:%d/", - le32_to_cpu(ns->v7.result[i][1])); - seq_printf(m, "fail:%d/", - le32_to_cpu(ns->v7.result[i][0])); - seq_printf(m, "on_time:%d/", - le32_to_cpu(ns->v7.result[i][2])); - seq_printf(m, "retry:%d/", - le32_to_cpu(ns->v7.result[i][3])); - seq_printf(m, "avg_t:%d.%03d/", - le32_to_cpu(ns->v7.tavg[i]) / 1000, - le32_to_cpu(ns->v7.tavg[i]) % 1000); - seq_printf(m, "max_t:%d.%03d]\n", - le32_to_cpu(ns->v7.tmax[i]) / 1000, - le32_to_cpu(ns->v7.tmax[i]) % 1000); + p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]"); + p += scnprintf(p, end - p, "null-%d", i); + p += scnprintf(p, end - p, "[Tx:%d/", + le32_to_cpu(ns->v7.result[i][4])); + p += scnprintf(p, end - p, "[ok:%d/", + le32_to_cpu(ns->v7.result[i][1])); + p += scnprintf(p, end - p, "fail:%d/", + le32_to_cpu(ns->v7.result[i][0])); + p += scnprintf(p, end - p, "on_time:%d/", + le32_to_cpu(ns->v7.result[i][2])); + p += scnprintf(p, end - p, "retry:%d/", + le32_to_cpu(ns->v7.result[i][3])); + p += scnprintf(p, end - p, "avg_t:%d.%03d/", + le32_to_cpu(ns->v7.tavg[i]) / 1000, + le32_to_cpu(ns->v7.tavg[i]) % 1000); + p += scnprintf(p, end - p, "max_t:%d.%03d]\n", + le32_to_cpu(ns->v7.tmax[i]) / 1000, + le32_to_cpu(ns->v7.tmax[i]) % 1000); } } else { for (i = 0; i < 2; i++) { - seq_printf(m, " %-15s : ", "[NULL-STA]"); - seq_printf(m, "null-%d", i); - seq_printf(m, "[Tx:%d/", - le32_to_cpu(ns->v2.result[i][4])); - seq_printf(m, "[ok:%d/", - le32_to_cpu(ns->v2.result[i][1])); - seq_printf(m, "fail:%d/", - le32_to_cpu(ns->v2.result[i][0])); - seq_printf(m, "on_time:%d/", - le32_to_cpu(ns->v2.result[i][2])); - seq_printf(m, "retry:%d/", - le32_to_cpu(ns->v2.result[i][3])); - seq_printf(m, "avg_t:%d.%03d/", - le32_to_cpu(ns->v2.avg_t[i]) / 1000, - le32_to_cpu(ns->v2.avg_t[i]) % 1000); - seq_printf(m, "max_t:%d.%03d]\n", - le32_to_cpu(ns->v2.max_t[i]) / 1000, - le32_to_cpu(ns->v2.max_t[i]) % 1000); + p += scnprintf(p, end - p, " %-15s : ", "[NULL-STA]"); + p += scnprintf(p, end - p, "null-%d", i); + p += scnprintf(p, end - p, "[Tx:%d/", + le32_to_cpu(ns->v2.result[i][4])); + p += scnprintf(p, end - p, "[ok:%d/", + le32_to_cpu(ns->v2.result[i][1])); + p += scnprintf(p, end - p, "fail:%d/", + le32_to_cpu(ns->v2.result[i][0])); + p += scnprintf(p, end - p, "on_time:%d/", + le32_to_cpu(ns->v2.result[i][2])); + p += scnprintf(p, end - p, "retry:%d/", + le32_to_cpu(ns->v2.result[i][3])); + p += scnprintf(p, end - p, "avg_t:%d.%03d/", + le32_to_cpu(ns->v2.avg_t[i]) / 1000, + le32_to_cpu(ns->v2.avg_t[i]) % 1000); + p += scnprintf(p, end - p, "max_t:%d.%03d]\n", + le32_to_cpu(ns->v2.max_t[i]) / 1000, + le32_to_cpu(ns->v2.max_t[i]) % 1000); } } + + return p - buf; } -static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_step_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; struct rtw89_btc_fbtc_steps_v2 *pstep = NULL; const struct rtw89_btc_ver *ver = btc->ver; + char *p = buf, *end = buf + bufsz; u8 type, val, cnt = 0, state = 0; bool outloop = false; u16 i, diff_t, n_start = 0, n_stop = 0; @@ -9841,14 +9941,14 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) pcinfo = &pfwinfo->rpt_fbtc_step.cinfo; if (!pcinfo->valid) - return; + return 0; pstep = &pfwinfo->rpt_fbtc_step.finfo.v2; pos_old = le16_to_cpu(pstep->pos_old); pos_new = le16_to_cpu(pstep->pos_new); if (pcinfo->req_fver != pstep->fver) - return; + return 0; /* store step info by using ring instead of FIFO*/ do { @@ -9877,13 +9977,15 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) continue; if (cnt % 10 == 0) - seq_printf(m, " %-15s : ", "[steps]"); + p += scnprintf(p, end - p, + " %-15s : ", "[steps]"); - seq_printf(m, "-> %s(%02d)(%02d)", - (type == CXSTEP_SLOT ? "SLT" : - "EVT"), (u32)val, diff_t); + p += scnprintf(p, end - p, + "-> %s(%02d)(%02d)", + (type == CXSTEP_SLOT ? "SLT" : + "EVT"), (u32)val, diff_t); if (cnt % 10 == 9) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); cnt++; } @@ -9900,29 +10002,32 @@ static void _show_fbtc_step_v2(struct rtw89_dev *rtwdev, struct seq_file *m) break; } } while (!outloop); + + return p - buf; } -static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fbtc_step_v3(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo; struct rtw89_btc_fbtc_steps_v3 *pstep; u32 i, n_begin, n_end, array_idx, cnt = 0; + char *p = buf, *end = buf + bufsz; u8 type, val; u16 diff_t; if ((pfwinfo->rpt_en_map & rtw89_btc_fw_rpt_ver(rtwdev, RPT_EN_FW_STEP_INFO)) == 0) - return; + return 0; pcinfo = &pfwinfo->rpt_fbtc_step.cinfo; if (!pcinfo->valid) - return; + return 0; pstep = &pfwinfo->rpt_fbtc_step.finfo.v3; if (pcinfo->req_fver != pstep->fver) - return; + return 0; if (le32_to_cpu(pstep->cnt) <= FCXDEF_STEP) n_begin = 1; @@ -9932,7 +10037,7 @@ static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m) n_end = le32_to_cpu(pstep->cnt); if (n_begin > n_end) - return; + return 0; /* restore step info by using ring instead of FIFO */ for (i = n_begin; i <= n_end; i++) { @@ -9945,50 +10050,55 @@ static void _show_fbtc_step_v3(struct rtw89_dev *rtwdev, struct seq_file *m) continue; if (cnt % 10 == 0) - seq_printf(m, " %-15s : ", "[steps]"); + p += scnprintf(p, end - p, " %-15s : ", "[steps]"); - seq_printf(m, "-> %s(%02d)", - (type == CXSTEP_SLOT ? - id_to_slot((u32)val) : - id_to_evt((u32)val)), diff_t); + p += scnprintf(p, end - p, "-> %s(%02d)", + (type == CXSTEP_SLOT ? + id_to_slot((u32)val) : + id_to_evt((u32)val)), diff_t); if (cnt % 10 == 9) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); cnt++; } + + return p - buf; } -static void _show_fw_dm_msg(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_fw_dm_msg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; + char *p = buf, *end = buf + bufsz; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_DM)) - return; + goto out; - _show_error(rtwdev, m); - _show_fbtc_tdma(rtwdev, m); - _show_fbtc_slots(rtwdev, m); + p += _show_error(rtwdev, p, end - p); + p += _show_fbtc_tdma(rtwdev, p, end - p); + p += _show_fbtc_slots(rtwdev, p, end - p); if (ver->fcxcysta == 2) - _show_fbtc_cysta_v2(rtwdev, m); + p += _show_fbtc_cysta_v2(rtwdev, p, end - p); else if (ver->fcxcysta == 3) - _show_fbtc_cysta_v3(rtwdev, m); + p += _show_fbtc_cysta_v3(rtwdev, p, end - p); else if (ver->fcxcysta == 4) - _show_fbtc_cysta_v4(rtwdev, m); + p += _show_fbtc_cysta_v4(rtwdev, p, end - p); else if (ver->fcxcysta == 5) - _show_fbtc_cysta_v5(rtwdev, m); + p += _show_fbtc_cysta_v5(rtwdev, p, end - p); else if (ver->fcxcysta == 7) - _show_fbtc_cysta_v7(rtwdev, m); + p += _show_fbtc_cysta_v7(rtwdev, p, end - p); - _show_fbtc_nullsta(rtwdev, m); + p += _show_fbtc_nullsta(rtwdev, p, end - p); if (ver->fcxstep == 2) - _show_fbtc_step_v2(rtwdev, m); + p += _show_fbtc_step_v2(rtwdev, p, end - p); else if (ver->fcxstep == 3) - _show_fbtc_step_v3(rtwdev, m); + p += _show_fbtc_step_v3(rtwdev, p, end - p); +out: + return p - buf; } static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt_cfg) @@ -10033,12 +10143,13 @@ static void _get_gnt(struct rtw89_dev *rtwdev, struct rtw89_mac_ax_coex_gnt *gnt } } -static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_gpio_dbg(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; const struct rtw89_btc_ver *ver = rtwdev->btc.ver; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; union rtw89_btc_fbtc_gpio_dbg *gdbg = NULL; + char *p = buf, *end = buf + bufsz; u8 *gpio_map, i; u32 en_map; @@ -10048,8 +10159,8 @@ static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): stop due rpt_fbtc_gpio_dbg.cinfo\n", __func__); - seq_puts(m, "\n"); - return; + p += scnprintf(p, end - p, "\n"); + goto out; } if (ver->fcxgpiodbg == 7) { @@ -10061,20 +10172,24 @@ static void _show_gpio_dbg(struct rtw89_dev *rtwdev, struct seq_file *m) } if (!en_map) - return; + goto out; - seq_printf(m, " %-15s : enable_map:0x%08x", - "[gpio_dbg]", en_map); + p += scnprintf(p, end - p, " %-15s : enable_map:0x%08x", + "[gpio_dbg]", en_map); for (i = 0; i < BTC_DBG_MAX1; i++) { if (!(en_map & BIT(i))) continue; - seq_printf(m, ", %s->GPIO%d", id_to_gdbg(i), gpio_map[i]); + p += scnprintf(p, end - p, ", %s->GPIO%d", id_to_gdbg(i), + gpio_map[i]); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + +out: + return p - buf; } -static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; @@ -10086,45 +10201,47 @@ static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_bt_info *bt = &btc->cx.bt; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; struct rtw89_mac_ax_gnt gnt; + char *p = buf, *end = buf + bufsz; u8 i = 0, type = 0, cnt = 0; u32 val, offset; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG)) - return; + return 0; - seq_puts(m, "========== [HW Status] ==========\n"); + p += scnprintf(p, end - p, "========== [HW Status] ==========\n"); - seq_printf(m, - " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", - "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + p += scnprintf(p, end - p, + " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd, + cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], + cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); _get_gnt(rtwdev, &gnt_cfg); gnt = gnt_cfg.band[0]; - seq_printf(m, - " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ", - "[gnt_status]", - chip->chip_id == RTL8852C ? "HW" : - btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", - gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, - gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt); + p += scnprintf(p, end - p, + " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], ", + "[gnt_status]", + chip->chip_id == RTL8852C ? "HW" : + btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", + gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt); gnt = gnt_cfg.band[1]; - seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", - gnt.gnt_wl_sw_en ? "SW" : "HW", - gnt.gnt_wl, - gnt.gnt_bt_sw_en ? "SW" : "HW", - gnt.gnt_bt); + p += scnprintf(p, end - p, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", + gnt.gnt_wl_sw_en ? "SW" : "HW", + gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", + gnt.gnt_bt); pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; if (!pcinfo->valid) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n", __func__); - return; + goto out; } pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v1; @@ -10138,21 +10255,26 @@ static void _show_mreg_v1(struct rtw89_dev *rtwdev, struct seq_file *m) val = le32_to_cpu(pmreg->mreg_val[i]); if (cnt % 6 == 0) - seq_printf(m, " %-15s : %d_0x%04x=0x%08x", - "[reg]", (u32)type, offset, val); + p += scnprintf(p, end - p, + " %-15s : %d_0x%04x=0x%08x", + "[reg]", (u32)type, offset, val); else - seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type, - offset, val); + p += scnprintf(p, end - p, ", %d_0x%04x=0x%08x", + (u32)type, + offset, val); if (cnt % 6 == 5) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); cnt++; if (i >= pmreg->reg_num) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v2(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_btc *btc = &rtwdev->btc; @@ -10164,46 +10286,48 @@ static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_bt_info *bt = &btc->cx.bt; struct rtw89_mac_ax_coex_gnt gnt_cfg = {}; struct rtw89_mac_ax_gnt gnt; + char *p = buf, *end = buf + bufsz; u8 i = 0, type = 0, cnt = 0; u32 val, offset; if (!(btc->dm.coex_info_map & BTC_COEX_INFO_MREG)) - return; + return 0; - seq_puts(m, "========== [HW Status] ==========\n"); + p += scnprintf(p, end - p, "========== [HW Status] ==========\n"); - seq_printf(m, - " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", - "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + p += scnprintf(p, end - p, + " %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)\n", + "[scoreboard]", wl->scbd, + cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], + cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); btc->dm.pta_owner = rtw89_mac_get_ctrl_path(rtwdev); _get_gnt(rtwdev, &gnt_cfg); gnt = gnt_cfg.band[0]; - seq_printf(m, - " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], polut_type:%s", - "[gnt_status]", - chip->chip_id == RTL8852C ? "HW" : - btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", - gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, - gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt, - id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); + p += scnprintf(p, end - p, + " %-15s : pta_owner:%s, phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d], polut_type:%s", + "[gnt_status]", + chip->chip_id == RTL8852C ? "HW" : + btc->dm.pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", + gnt.gnt_wl_sw_en ? "SW" : "HW", gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", gnt.gnt_bt, + id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); gnt = gnt_cfg.band[1]; - seq_printf(m, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", - gnt.gnt_wl_sw_en ? "SW" : "HW", - gnt.gnt_wl, - gnt.gnt_bt_sw_en ? "SW" : "HW", - gnt.gnt_bt); + p += scnprintf(p, end - p, "phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]\n", + gnt.gnt_wl_sw_en ? "SW" : "HW", + gnt.gnt_wl, + gnt.gnt_bt_sw_en ? "SW" : "HW", + gnt.gnt_bt); pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; if (!pcinfo->valid) { rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC], %s(): stop due rpt_fbtc_mregval.cinfo\n", __func__); - return; + goto out; } pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v2; @@ -10217,21 +10341,26 @@ static void _show_mreg_v2(struct rtw89_dev *rtwdev, struct seq_file *m) val = le32_to_cpu(pmreg->mreg_val[i]); if (cnt % 6 == 0) - seq_printf(m, " %-15s : %d_0x%04x=0x%08x", - "[reg]", (u32)type, offset, val); + p += scnprintf(p, end - p, + " %-15s : %d_0x%04x=0x%08x", + "[reg]", (u32)type, offset, val); else - seq_printf(m, ", %d_0x%04x=0x%08x", (u32)type, - offset, val); + p += scnprintf(p, end - p, ", %d_0x%04x=0x%08x", + (u32)type, + offset, val); if (cnt % 6 == 5) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); cnt++; if (i >= pmreg->reg_num) - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } + +out: + return p - buf; } -static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_mreg_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10242,46 +10371,50 @@ static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_bt_info *bt = &cx->bt; struct rtw89_mac_ax_gnt *gnt = NULL; struct rtw89_btc_dm *dm = &btc->dm; + char *p = buf, *end = buf + bufsz; u8 i, type, cnt = 0; u32 val, offset; if (!(dm->coex_info_map & BTC_COEX_INFO_MREG)) - return; + return 0; - seq_puts(m, "\n\r========== [HW Status] =========="); + p += scnprintf(p, end - p, "\n\r========== [HW Status] =========="); - seq_printf(m, - "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", - "[scoreboard]", wl->scbd, cx->cnt_wl[BTC_WCNT_SCBDUPDATE], - bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], - cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); + p += scnprintf(p, end - p, + "\n\r %-15s : WL->BT:0x%08x(cnt:%d), BT->WL:0x%08x(total:%d, bt_update:%d)", + "[scoreboard]", wl->scbd, + cx->cnt_wl[BTC_WCNT_SCBDUPDATE], + bt->scbd, cx->cnt_bt[BTC_BCNT_SCBDREAD], + cx->cnt_bt[BTC_BCNT_SCBDUPDATE]); /* To avoid I/O if WL LPS or power-off */ dm->pta_owner = rtw89_mac_get_ctrl_path(rtwdev); - seq_printf(m, - "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s", - "[gnt_status]", - rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" : - dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", - wl->pta_req_mac, id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); + p += scnprintf(p, end - p, + "\n\r %-15s : pta_owner:%s, pta_req_mac:MAC%d, rf_gnt_source: polut_type:%s", + "[gnt_status]", + rtwdev->chip->para_ver & BTC_FEAT_PTA_ONOFF_CTRL ? "HW" : + dm->pta_owner == BTC_CTRL_BY_WL ? "WL" : "BT", + wl->pta_req_mac, + id_to_polut(wl->bt_polut_type[wl->pta_req_mac])); gnt = &dm->gnt.band[RTW89_PHY_0]; - seq_printf(m, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", - gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, - gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); + p += scnprintf(p, end - p, ", phy-0[gnt_wl:%s-%d/gnt_bt:%s-%d]", + gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, + gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); if (rtwdev->dbcc_en) { gnt = &dm->gnt.band[RTW89_PHY_1]; - seq_printf(m, ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", - gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, - gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); + p += scnprintf(p, end - p, + ", phy-1[gnt_wl:%s-%d/gnt_bt:%s-%d]", + gnt->gnt_wl_sw_en ? "SW" : "HW", gnt->gnt_wl, + gnt->gnt_bt_sw_en ? "SW" : "HW", gnt->gnt_bt); } pcinfo = &pfwinfo->rpt_fbtc_mregval.cinfo; if (!pcinfo->valid) - return; + goto out; pmreg = &pfwinfo->rpt_fbtc_mregval.finfo.v7; @@ -10291,17 +10424,21 @@ static void _show_mreg_v7(struct rtw89_dev *rtwdev, struct seq_file *m) val = le32_to_cpu(pmreg->mreg_val[i]); if (cnt % 6 == 0) - seq_printf(m, "\n\r %-15s : %s_0x%x=0x%x", "[reg]", - id_to_regtype(type), offset, val); + p += scnprintf(p, end - p, + "\n\r %-15s : %s_0x%x=0x%x", "[reg]", + id_to_regtype(type), offset, val); else - seq_printf(m, ", %s_0x%x=0x%x", - id_to_regtype(type), offset, val); + p += scnprintf(p, end - p, ", %s_0x%x=0x%x", + id_to_regtype(type), offset, val); cnt++; } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + +out: + return p - buf; } -static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v1(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10312,56 +10449,59 @@ static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_bt_info *bt = &cx->bt; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_puts(m, "========== [Statistics] ==========\n"); + p += scnprintf(p, end - p, "========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v1; - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt, - pfwinfo->cnt_c2h, prptctrl->c2h_cnt); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, prptctrl->h2c_cnt, + pfwinfo->cnt_c2h, prptctrl->c2h_cnt); - seq_printf(m, - "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", - pfwinfo->event[BTF_EVNT_RPT], prptctrl->rpt_cnt, - prptctrl->rpt_enable, dm->error.val); + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", + pfwinfo->event[BTF_EVNT_RPT], + prptctrl->rpt_cnt, + prptctrl->rpt_enable, dm->error.val); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : send_ok:%d, send_fail:%d, recv:%d", - "[mailbox]", prptctrl->mb_send_ok_cnt, - prptctrl->mb_send_fail_cnt, prptctrl->mb_recv_cnt); - - seq_printf(m, - "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n", - prptctrl->mb_a2dp_empty_cnt, - prptctrl->mb_a2dp_flct_cnt, - prptctrl->mb_a2dp_full_cnt); - - seq_printf(m, - " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", - "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - - seq_printf(m, - ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ], - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO], - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT], - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT], - prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : send_ok:%d, send_fail:%d, recv:%d", + "[mailbox]", prptctrl->mb_send_ok_cnt, + prptctrl->mb_send_fail_cnt, + prptctrl->mb_recv_cnt); + + p += scnprintf(p, end - p, + "(A2DP_empty:%d, A2DP_flowstop:%d, A2DP_full:%d)\n", + prptctrl->mb_a2dp_empty_cnt, + prptctrl->mb_a2dp_flct_cnt, + prptctrl->mb_a2dp_full_cnt); + + p += scnprintf(p, end - p, + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", + "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + + p += scnprintf(p, end - p, + ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REQ], + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_GO], + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_REJECT], + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT], + prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_FAIL]); if (prptctrl->bt_rfk_cnt[BTC_BCNT_RFK_TIMEOUT] > 0) bt->rfk_info.map.timeout = 1; @@ -10370,42 +10510,44 @@ static void _show_summary_v1(struct rtw89_dev *rtwdev, struct seq_file *m) dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout; } else { - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, - pfwinfo->event[BTF_EVNT_RPT], - btc->fwinfo.rpt_en_map); - seq_puts(m, " (WL FW report invalid!!)\n"); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, + pfwinfo->event[BTF_EVNT_RPT], + btc->fwinfo.rpt_en_map); + p += scnprintf(p, end - p, " (WL FW report invalid!!)\n"); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_printf(m, - " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", + "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], + cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, - " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", - "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], - cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], - cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + "timer=%d, control=%d, customerize=%d\n", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], + cnt[BTC_NCNT_CUSTOMERIZE]); - seq_printf(m, - "timer=%d, control=%d, customerize=%d\n", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], - cnt[BTC_NCNT_CUSTOMERIZE]); + return p - buf; } -static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v4(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10416,64 +10558,65 @@ static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_wl_info *wl = &cx->wl; struct rtw89_btc_bt_info *bt = &cx->bt; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_puts(m, "========== [Statistics] ==========\n"); + p += scnprintf(p, end - p, "========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v4; - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, - le32_to_cpu(prptctrl->rpt_info.cnt_h2c), - pfwinfo->cnt_c2h, - le32_to_cpu(prptctrl->rpt_info.cnt_c2h)); - - seq_printf(m, - "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le32_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en), - dm->error.val); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le32_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le32_to_cpu(prptctrl->rpt_info.cnt_c2h)); + + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x, dm_error_map:0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le32_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en), + dm->error.val); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - - seq_printf(m, - "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - - seq_printf(m, - " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", - "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - - seq_printf(m, - ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]), - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_GO]), - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REJECT]), - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]), - le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_FAIL])); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + + p += scnprintf(p, end - p, + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/timeout:%d]", + "[RFK]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + + p += scnprintf(p, end - p, + ", bt_rfk[req:%d/go:%d/reject:%d/timeout:%d/fail:%d]\n", + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ]), + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_GO]), + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REJECT]), + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]), + le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_FAIL])); if (le32_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_TIMEOUT]) > 0) bt->rfk_info.map.timeout = 1; @@ -10482,42 +10625,44 @@ static void _show_summary_v4(struct rtw89_dev *rtwdev, struct seq_file *m) dm->error.map.wl_rfk_timeout = bt->rfk_info.map.timeout; } else { - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, - pfwinfo->event[BTF_EVNT_RPT], - btc->fwinfo.rpt_en_map); - seq_puts(m, " (WL FW report invalid!!)\n"); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d, rpt_cnt=%d, rpt_map=0x%x", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h, + pfwinfo->event[BTF_EVNT_RPT], + btc->fwinfo.rpt_en_map); + p += scnprintf(p, end - p, " (WL FW report invalid!!)\n"); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_printf(m, - " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d\n", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", + "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], + cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, - " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", - "[notify_cnt]", cnt[BTC_NCNT_SCAN_START], - cnt[BTC_NCNT_SCAN_FINISH], cnt[BTC_NCNT_SWITCH_BAND], - cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + "timer=%d, control=%d, customerize=%d\n", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], + cnt[BTC_NCNT_CUSTOMERIZE]); - seq_printf(m, - "timer=%d, control=%d, customerize=%d\n", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], - cnt[BTC_NCNT_CUSTOMERIZE]); + return p - buf; } -static void _show_summary_v5(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v5(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10527,112 +10672,118 @@ static void _show_summary_v5(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &cx->wl; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_puts(m, "========== [Statistics] ==========\n"); + p += scnprintf(p, end - p, "========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v5; - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", - "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - le16_to_cpu(prptctrl->rpt_info.cnt_h2c), - pfwinfo->cnt_c2h, - le16_to_cpu(prptctrl->rpt_info.cnt_c2h), - le16_to_cpu(prptctrl->rpt_info.len_c2h)); - - seq_printf(m, - "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le16_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en)); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h)); + + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - - seq_printf(m, - "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - - seq_printf(m, - " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - - seq_printf(m, - ", bt_rfk[req:%d]", - le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - - seq_printf(m, - ", AOAC[RF_on:%d/RF_off:%d]", - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + + p += scnprintf(p, end - p, + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", + "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + + p += scnprintf(p, end - p, + ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + + p += scnprintf(p, end - p, + ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); } if (!pcinfo->valid || pfwinfo->len_mismch || pfwinfo->fver_mismch || pfwinfo->err[BTFRE_EXCEPTION]) { - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" - "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", - "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, - pfwinfo->fver_mismch, pfwinfo->err[BTFRE_EXCEPTION], - wl->status.map.lps, wl->status.map.rf_off); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" + "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", + "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, + pfwinfo->fver_mismch, + pfwinfo->err[BTFRE_EXCEPTION], + wl->status.map.lps, wl->status.map.rf_off); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", - cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", - "[notify_cnt]", - cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], - cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + "timer=%d, control=%d, customerize=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], + cnt[BTC_NCNT_CUSTOMERIZE]); - seq_printf(m, - "timer=%d, control=%d, customerize=%d", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], - cnt[BTC_NCNT_CUSTOMERIZE]); + return p - buf; } -static void _show_summary_v105(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v105(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc *btc = &rtwdev->btc; struct rtw89_btc_btf_fwinfo *pfwinfo = &btc->fwinfo; @@ -10642,112 +10793,118 @@ static void _show_summary_v105(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_dm *dm = &btc->dm; struct rtw89_btc_wl_info *wl = &cx->wl; u32 cnt_sum = 0, *cnt = btc->dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_puts(m, "========== [Statistics] ==========\n"); + p += scnprintf(p, end - p, "========== [Statistics] ==========\n"); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && !wl->status.map.lps && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v105; - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", - "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - le16_to_cpu(prptctrl->rpt_info.cnt_h2c), - pfwinfo->cnt_c2h, - le16_to_cpu(prptctrl->rpt_info.cnt_c2h), - le16_to_cpu(prptctrl->rpt_info.len_c2h)); - - seq_printf(m, - "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le16_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en)); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h)); + + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - - seq_printf(m, - "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - - seq_printf(m, - " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); - - seq_printf(m, - ", bt_rfk[req:%d]", - le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - - seq_printf(m, - ", AOAC[RF_on:%d/RF_off:%d]", - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d, tx:%d, ack:%d, nack:%d)\n", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + + p += scnprintf(p, end - p, + " %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d]", + "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT]); + + p += scnprintf(p, end - p, + ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + + p += scnprintf(p, end - p, + ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { - seq_printf(m, - " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", - "[summary]", pfwinfo->cnt_h2c, - pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); + p += scnprintf(p, end - p, + " %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, pfwinfo->cnt_c2h); } if (!pcinfo->valid || pfwinfo->len_mismch || pfwinfo->fver_mismch || pfwinfo->err[BTFRE_EXCEPTION]) { - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" - "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", - "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, - pfwinfo->fver_mismch, pfwinfo->err[BTFRE_EXCEPTION], - wl->status.map.lps, wl->status.map.rf_off); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : WL FW rpt error!![rpt_ctrl_valid:%d/len:" + "0x%x/ver:0x%x/ex:%d/lps=%d/rf_off=%d]", + "[ERROR]", pcinfo->valid, pfwinfo->len_mismch, + pfwinfo->fver_mismch, + pfwinfo->err[BTFRE_EXCEPTION], + wl->status.map.lps, wl->status.map.rf_off); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", - cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_puts(m, "\n"); - seq_printf(m, - " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", - "[notify_cnt]", - cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], - cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, "\n"); + p += scnprintf(p, end - p, + " %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, - "timer=%d, control=%d, customerize=%d", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], - cnt[BTC_NCNT_CUSTOMERIZE]); + p += scnprintf(p, end - p, + "timer=%d, control=%d, customerize=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CONTROL], + cnt[BTC_NCNT_CUSTOMERIZE]); + + return p - buf; } -static void _show_summary_v7(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v7(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; struct rtw89_btc_fbtc_rpt_ctrl_v7 *prptctrl = NULL; @@ -10756,100 +10913,111 @@ static void _show_summary_v7(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_wl_info *wl = &cx->wl; u32 *cnt = rtwdev->btc.dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u32 cnt_sum = 0; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_printf(m, "%s", "\n\r========== [Statistics] =========="); + p += scnprintf(p, end - p, "%s", + "\n\r========== [Statistics] =========="); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v7; - seq_printf(m, - "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d)," - "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ", - "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - le16_to_cpu(prptctrl->rpt_info.cnt_h2c), pfwinfo->cnt_c2h, - le16_to_cpu(prptctrl->rpt_info.cnt_c2h), - le16_to_cpu(prptctrl->rpt_info.len_c2h), - rtwdev->btc.ver->info_buf); - - seq_printf(m, "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le16_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en)); + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d)," + "c2h_cnt=%d(fw_send:%d, len:%d, max:%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h), + rtwdev->btc.ver->info_buf); + + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - - seq_printf(m, "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - - seq_printf(m, "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - - seq_printf(m, - "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], - wl->rfk_info.proc_time); - - seq_printf(m, ", bt_rfk[req:%d]", - le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - - seq_printf(m, ", AOAC[RF_on:%d/RF_off:%d]", - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + + p += scnprintf(p, end - p, + "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + + p += scnprintf(p, end - p, + "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], + wl->rfk_info.proc_time); + + p += scnprintf(p, end - p, ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { - seq_printf(m, - "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", - "[summary]", - pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - pfwinfo->cnt_c2h, - wl->status.map.lps, wl->status.map.rf_off); + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + "[summary]", + pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, + pfwinfo->cnt_c2h, + wl->status.map.lps, wl->status.map.rf_off); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_printf(m, - "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", - cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, + "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, - "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", - "[notify_cnt]", - cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], - cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], - cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], + rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], + cnt[BTC_NCNT_COUNTRYCODE]); - seq_printf(m, "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], - rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], - cnt[BTC_NCNT_COUNTRYCODE]); + return p - buf; } -static void _show_summary_v8(struct rtw89_dev *rtwdev, struct seq_file *m) +static int _show_summary_v8(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_btc_btf_fwinfo *pfwinfo = &rtwdev->btc.fwinfo; struct rtw89_btc_rpt_cmn_info *pcinfo = NULL; @@ -10858,153 +11026,173 @@ static void _show_summary_v8(struct rtw89_dev *rtwdev, struct seq_file *m) struct rtw89_btc_dm *dm = &rtwdev->btc.dm; struct rtw89_btc_wl_info *wl = &cx->wl; u32 *cnt = rtwdev->btc.dm.cnt_notify; + char *p = buf, *end = buf + bufsz; u32 cnt_sum = 0; u8 i; if (!(dm->coex_info_map & BTC_COEX_INFO_SUMMARY)) - return; + return 0; - seq_printf(m, "%s", "\n\r========== [Statistics] =========="); + p += scnprintf(p, end - p, "%s", + "\n\r========== [Statistics] =========="); pcinfo = &pfwinfo->rpt_ctrl.cinfo; if (pcinfo->valid && wl->status.map.lps != BTC_LPS_RF_OFF && !wl->status.map.rf_off) { prptctrl = &pfwinfo->rpt_ctrl.finfo.v8; - seq_printf(m, - "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", - "[summary]", pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - le16_to_cpu(prptctrl->rpt_info.cnt_h2c), pfwinfo->cnt_c2h, - le16_to_cpu(prptctrl->rpt_info.cnt_c2h), - le16_to_cpu(prptctrl->rpt_info.len_c2h), - (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l, - rtwdev->btc.ver->info_buf); - - seq_printf(m, "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", - pfwinfo->event[BTF_EVNT_RPT], - le16_to_cpu(prptctrl->rpt_info.cnt), - le32_to_cpu(prptctrl->rpt_info.en)); + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d, fw_recv:%d), c2h_cnt=%d(fw_send:%d, len:%d, max:fw-%d/drv-%d), ", + "[summary]", pfwinfo->cnt_h2c, + pfwinfo->cnt_h2c_fail, + le16_to_cpu(prptctrl->rpt_info.cnt_h2c), + pfwinfo->cnt_c2h, + le16_to_cpu(prptctrl->rpt_info.cnt_c2h), + le16_to_cpu(prptctrl->rpt_info.len_c2h), + (prptctrl->rpt_len_max_h << 8) + prptctrl->rpt_len_max_l, + rtwdev->btc.ver->info_buf); + + p += scnprintf(p, end - p, + "rpt_cnt=%d(fw_send:%d), rpt_map=0x%x", + pfwinfo->event[BTF_EVNT_RPT], + le16_to_cpu(prptctrl->rpt_info.cnt), + le32_to_cpu(prptctrl->rpt_info.en)); if (dm->error.map.wl_fw_hang) - seq_puts(m, " (WL FW Hang!!)"); - - seq_printf(m, "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", - "[mailbox]", le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), - le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); - - seq_printf(m, "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), - le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); - - seq_printf(m, - "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", - "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], - cx->cnt_wl[BTC_WCNT_RFK_GO], - cx->cnt_wl[BTC_WCNT_RFK_REJECT], - cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], - wl->rfk_info.proc_time); - - seq_printf(m, ", bt_rfk[req:%d]", - le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); - - seq_printf(m, ", AOAC[RF_on:%d/RF_off:%d]", - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), - le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); + p += scnprintf(p, end - p, " (WL FW Hang!!)"); + + p += scnprintf(p, end - p, + "\n\r %-15s : send_ok:%d, send_fail:%d, recv:%d, ", + "[mailbox]", + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_ok), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_send_fail), + le32_to_cpu(prptctrl->bt_mbx_info.cnt_recv)); + + p += scnprintf(p, end - p, + "A2DP_empty:%d(stop:%d/tx:%d/ack:%d/nack:%d)", + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_empty), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_flowctrl), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_tx), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_ack), + le32_to_cpu(prptctrl->bt_mbx_info.a2dp.cnt_nack)); + + p += scnprintf(p, end - p, + "\n\r %-15s : wl_rfk[req:%d/go:%d/reject:%d/tout:%d/time:%dms]", + "[RFK/LPS]", cx->cnt_wl[BTC_WCNT_RFK_REQ], + cx->cnt_wl[BTC_WCNT_RFK_GO], + cx->cnt_wl[BTC_WCNT_RFK_REJECT], + cx->cnt_wl[BTC_WCNT_RFK_TIMEOUT], + wl->rfk_info.proc_time); + + p += scnprintf(p, end - p, ", bt_rfk[req:%d]", + le16_to_cpu(prptctrl->bt_cnt[BTC_BCNT_RFK_REQ])); + + p += scnprintf(p, end - p, ", AOAC[RF_on:%d/RF_off:%d]", + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_on), + le16_to_cpu(prptctrl->rpt_info.cnt_aoac_rf_off)); } else { - seq_printf(m, - "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", - "[summary]", - pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, - pfwinfo->cnt_c2h, - wl->status.map.lps, wl->status.map.rf_off); + p += scnprintf(p, end - p, + "\n\r %-15s : h2c_cnt=%d(fail:%d), c2h_cnt=%d (lps=%d/rf_off=%d)", + "[summary]", + pfwinfo->cnt_h2c, pfwinfo->cnt_h2c_fail, + pfwinfo->cnt_c2h, + wl->status.map.lps, wl->status.map.rf_off); } for (i = 0; i < BTC_NCNT_NUM; i++) cnt_sum += dm->cnt_notify[i]; - seq_printf(m, - "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", - "[notify_cnt]", - cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], - cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); + p += scnprintf(p, end - p, + "\n\r %-15s : total=%d, show_coex_info=%d, power_on=%d, init_coex=%d, ", + "[notify_cnt]", + cnt_sum, cnt[BTC_NCNT_SHOW_COEX_INFO], + cnt[BTC_NCNT_POWER_ON], cnt[BTC_NCNT_INIT_COEX]); - seq_printf(m, - "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", - cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], - cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], - cnt[BTC_NCNT_WL_STA]); + p += scnprintf(p, end - p, + "power_off=%d, radio_state=%d, role_info=%d, wl_rfk=%d, wl_sta=%d", + cnt[BTC_NCNT_POWER_OFF], cnt[BTC_NCNT_RADIO_STATE], + cnt[BTC_NCNT_ROLE_INFO], cnt[BTC_NCNT_WL_RFK], + cnt[BTC_NCNT_WL_STA]); - seq_printf(m, - "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", - "[notify_cnt]", - cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], - cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], - cnt[BTC_NCNT_SPECIAL_PACKET]); + p += scnprintf(p, end - p, + "\n\r %-15s : scan_start=%d, scan_finish=%d, switch_band=%d, switch_chbw=%d, special_pkt=%d, ", + "[notify_cnt]", + cnt[BTC_NCNT_SCAN_START], cnt[BTC_NCNT_SCAN_FINISH], + cnt[BTC_NCNT_SWITCH_BAND], cnt[BTC_NCNT_SWITCH_CHBW], + cnt[BTC_NCNT_SPECIAL_PACKET]); - seq_printf(m, "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", - cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], - rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], - cnt[BTC_NCNT_COUNTRYCODE]); + p += scnprintf(p, end - p, + "timer=%d, customerize=%d, hub_msg=%d, chg_fw=%d, send_cc=%d", + cnt[BTC_NCNT_TIMER], cnt[BTC_NCNT_CUSTOMERIZE], + rtwdev->btc.hubmsg_cnt, cnt[BTC_NCNT_RESUME_DL_FW], + cnt[BTC_NCNT_COUNTRYCODE]); + + return p - buf; } -void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m) +ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_fw_suit *fw_suit = &rtwdev->fw.normal; struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; struct rtw89_btc_cx *cx = &btc->cx; struct rtw89_btc_bt_info *bt = &cx->bt; - - seq_puts(m, "=========================================\n"); - seq_printf(m, "WL FW / BT FW %d.%d.%d.%d / NA\n", - fw_suit->major_ver, fw_suit->minor_ver, - fw_suit->sub_ver, fw_suit->sub_idex); - seq_printf(m, "manual %d\n", btc->manual_ctrl); - - seq_puts(m, "=========================================\n"); - - seq_printf(m, "\n\r %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)", - "[bt_info]", - bt->raw_info[2], bt->raw_info[3], - bt->raw_info[4], bt->raw_info[5], - bt->raw_info[6], bt->raw_info[7], - bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", - cx->cnt_bt[BTC_BCNT_INFOUPDATE], - cx->cnt_bt[BTC_BCNT_INFOSAME]); - - seq_puts(m, "\n=========================================\n"); - - _show_cx_info(rtwdev, m); - _show_wl_info(rtwdev, m); - _show_bt_info(rtwdev, m); - _show_dm_info(rtwdev, m); - _show_fw_dm_msg(rtwdev, m); + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, + "=========================================\n"); + p += scnprintf(p, end - p, + "WL FW / BT FW %d.%d.%d.%d / NA\n", + fw_suit->major_ver, fw_suit->minor_ver, + fw_suit->sub_ver, fw_suit->sub_idex); + p += scnprintf(p, end - p, "manual %d\n", + btc->manual_ctrl); + + p += scnprintf(p, end - p, + "=========================================\n"); + + p += scnprintf(p, end - p, + "\n\r %-15s : raw_data[%02x %02x %02x %02x %02x %02x] (type:%s/cnt:%d/same:%d)", + "[bt_info]", + bt->raw_info[2], bt->raw_info[3], + bt->raw_info[4], bt->raw_info[5], + bt->raw_info[6], bt->raw_info[7], + bt->raw_info[0] == BTC_BTINFO_AUTO ? "auto" : "reply", + cx->cnt_bt[BTC_BCNT_INFOUPDATE], + cx->cnt_bt[BTC_BCNT_INFOSAME]); + + p += scnprintf(p, end - p, + "\n=========================================\n"); + + p += _show_cx_info(rtwdev, p, end - p); + p += _show_wl_info(rtwdev, p, end - p); + p += _show_bt_info(rtwdev, p, end - p); + p += _show_dm_info(rtwdev, p, end - p); + p += _show_fw_dm_msg(rtwdev, p, end - p); if (ver->fcxmreg == 1) - _show_mreg_v1(rtwdev, m); + p += _show_mreg_v1(rtwdev, p, end - p); else if (ver->fcxmreg == 2) - _show_mreg_v2(rtwdev, m); + p += _show_mreg_v2(rtwdev, p, end - p); else if (ver->fcxmreg == 7) - _show_mreg_v7(rtwdev, m); + p += _show_mreg_v7(rtwdev, p, end - p); - _show_gpio_dbg(rtwdev, m); + p += _show_gpio_dbg(rtwdev, p, end - p); if (ver->fcxbtcrpt == 1) - _show_summary_v1(rtwdev, m); + p += _show_summary_v1(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 4) - _show_summary_v4(rtwdev, m); + p += _show_summary_v4(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 5) - _show_summary_v5(rtwdev, m); + p += _show_summary_v5(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 105) - _show_summary_v105(rtwdev, m); + p += _show_summary_v105(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 7) - _show_summary_v7(rtwdev, m); + p += _show_summary_v7(rtwdev, p, end - p); else if (ver->fcxbtcrpt == 8) - _show_summary_v8(rtwdev, m); + p += _show_summary_v8(rtwdev, p, end - p); + + return p - buf; } void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev) @@ -11037,3 +11225,24 @@ out: rtw89_debug(rtwdev, RTW89_DBG_BTC, "[BTC] use version def[%d] = 0x%08x\n", (int)(btc->ver - rtw89_btc_ver_defs), btc->ver->fw_ver_code); } + +void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms) +{ + struct rtw89_btc_bt_link_info *bt_linfo = &rtwdev->btc.cx.bt.link_info; + struct rtw89_btc_bt_a2dp_desc a2dp = bt_linfo->a2dp_desc; + + if (test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) + return; + + if (!a2dp.exist) + return; + + fsleep(ms * 1000); +} +EXPORT_SYMBOL(rtw89_btc_ntfy_preserve_bt_time); + +void rtw89_btc_ntfy_conn_rfk(struct rtw89_dev *rtwdev, bool state) +{ + rtwdev->btc.cx.wl.rfk_info.con_rfk = state; +} +EXPORT_SYMBOL(rtw89_btc_ntfy_conn_rfk); diff --git a/drivers/net/wireless/realtek/rtw89/coex.h b/drivers/net/wireless/realtek/rtw89/coex.h index dbdb56e063ef..e3a1fcd79620 100644 --- a/drivers/net/wireless/realtek/rtw89/coex.h +++ b/drivers/net/wireless/realtek/rtw89/coex.h @@ -267,10 +267,10 @@ void rtw89_btc_ntfy_scan_finish(struct rtw89_dev *rtwdev, u8 phy_idx); void rtw89_btc_ntfy_switch_band(struct rtw89_dev *rtwdev, u8 phy_idx, u8 band); void rtw89_btc_ntfy_specific_packet(struct rtw89_dev *rtwdev, enum btc_pkt_type pkt_type); -void rtw89_btc_ntfy_eapol_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_arp_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_dhcp_packet_work(struct work_struct *work); -void rtw89_btc_ntfy_icmp_packet_work(struct work_struct *work); +void rtw89_btc_ntfy_eapol_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_arp_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_dhcp_packet_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_btc_ntfy_icmp_packet_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_btc_ntfy_role_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, @@ -282,14 +282,16 @@ void rtw89_btc_ntfy_wl_rfk(struct rtw89_dev *rtwdev, u8 phy_map, void rtw89_btc_ntfy_wl_sta(struct rtw89_dev *rtwdev); void rtw89_btc_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 len, u8 class, u8 func); -void rtw89_btc_dump_info(struct rtw89_dev *rtwdev, struct seq_file *m); -void rtw89_coex_act1_work(struct work_struct *work); -void rtw89_coex_bt_devinfo_work(struct work_struct *work); -void rtw89_coex_rfk_chk_work(struct work_struct *work); +ssize_t rtw89_btc_dump_info(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); +void rtw89_coex_act1_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_coex_bt_devinfo_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_coex_rfk_chk_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_coex_power_on(struct rtw89_dev *rtwdev); void rtw89_btc_set_policy(struct rtw89_dev *rtwdev, u16 policy_type); void rtw89_btc_set_policy_v1(struct rtw89_dev *rtwdev, u16 policy_type); void rtw89_coex_recognize_ver(struct rtw89_dev *rtwdev); +void rtw89_btc_ntfy_preserve_bt_time(struct rtw89_dev *rtwdev, u32 ms); +void rtw89_btc_ntfy_conn_rfk(struct rtw89_dev *rtwdev, bool state); static inline u8 rtw89_btc_phymap(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, diff --git a/drivers/net/wireless/realtek/rtw89/core.c b/drivers/net/wireless/realtek/rtw89/core.c index 85f739f1173d..49447668cbf3 100644 --- a/drivers/net/wireless/realtek/rtw89/core.c +++ b/drivers/net/wireless/realtek/rtw89/core.c @@ -203,6 +203,23 @@ static const struct ieee80211_iface_combination rtw89_iface_combs[] = { }, }; +static const u8 rtw89_ext_capa_sta[] = { + [2] = WLAN_EXT_CAPA3_MULTI_BSSID_SUPPORT, + [7] = WLAN_EXT_CAPA8_OPMODE_NOTIF, +}; + +static const struct wiphy_iftype_ext_capab rtw89_iftypes_ext_capa[] = { + { + .iftype = NL80211_IFTYPE_STATION, + .extended_capabilities = rtw89_ext_capa_sta, + .extended_capabilities_mask = rtw89_ext_capa_sta, + .extended_capabilities_len = sizeof(rtw89_ext_capa_sta), + /* relevant only if EHT is supported */ + .eml_capabilities = 0, + .mld_capa_and_ops = 0, + }, +}; + #define RTW89_6GHZ_SPAN_HEAD 6145 #define RTW89_6GHZ_SPAN_IDX(center_freq) \ ((((int)(center_freq) - RTW89_6GHZ_SPAN_HEAD) / 5) / 2) @@ -211,6 +228,8 @@ static const struct ieee80211_iface_combination rtw89_iface_combs[] = { [RTW89_6GHZ_SPAN_IDX(center_freq)] = { \ .sar_subband_low = RTW89_SAR_6GHZ_ ## subband_l, \ .sar_subband_high = RTW89_SAR_6GHZ_ ## subband_h, \ + .acpi_sar_subband_low = RTW89_ACPI_SAR_6GHZ_ ## subband_l, \ + .acpi_sar_subband_high = RTW89_ACPI_SAR_6GHZ_ ## subband_h, \ .ant_gain_subband_low = RTW89_ANT_GAIN_6GHZ_ ## subband_l, \ .ant_gain_subband_high = RTW89_ANT_GAIN_6GHZ_ ## subband_h, \ } @@ -639,9 +658,17 @@ out: static u8 rtw89_core_tx_get_mac_id(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) { + struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; struct rtw89_vif_link *rtwvif_link = tx_req->rtwvif_link; struct rtw89_sta_link *rtwsta_link = tx_req->rtwsta_link; + if (desc_info->mlo && !desc_info->sw_mld) { + if (rtwsta_link) + return rtw89_sta_get_main_macid(rtwsta_link->rtwsta); + else + return rtw89_vif_get_main_macid(rtwvif_link->rtwvif); + } + if (!rtwsta_link) return rtwvif_link->mac_id; @@ -671,7 +698,7 @@ rtw89_core_tx_update_mgmt_info(struct rtw89_dev *rtwdev, struct sk_buff *skb = tx_req->skb; u8 qsel, ch_dma; - qsel = desc_info->hiq ? RTW89_TX_QSEL_B0_HI : RTW89_TX_QSEL_B0_MGMT; + qsel = rtw89_core_get_qsel_mgmt(rtwdev, tx_req); ch_dma = rtw89_core_get_ch_dma(rtwdev, qsel); desc_info->qsel = qsel; @@ -913,16 +940,17 @@ static enum btc_pkt_type rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) { + struct wiphy *wiphy = rtwdev->hw->wiphy; struct sk_buff *skb = tx_req->skb; struct udphdr *udphdr; if (IEEE80211_SKB_CB(skb)->control.flags & IEEE80211_TX_CTRL_PORT_CTRL_PROTO) { - ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.eapol_notify_work); + wiphy_work_queue(wiphy, &rtwdev->btc.eapol_notify_work); return PACKET_EAPOL; } if (skb->protocol == htons(ETH_P_ARP)) { - ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.arp_notify_work); + wiphy_work_queue(wiphy, &rtwdev->btc.arp_notify_work); return PACKET_ARP; } @@ -932,14 +960,14 @@ rtw89_core_tx_btc_spec_pkt_notify(struct rtw89_dev *rtwdev, if (((udphdr->source == htons(67) && udphdr->dest == htons(68)) || (udphdr->source == htons(68) && udphdr->dest == htons(67))) && skb->len > 282) { - ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.dhcp_notify_work); + wiphy_work_queue(wiphy, &rtwdev->btc.dhcp_notify_work); return PACKET_DHCP; } } if (skb->protocol == htons(ETH_P_IP) && ip_hdr(skb)->protocol == IPPROTO_ICMP) { - ieee80211_queue_work(rtwdev->hw, &rtwdev->btc.icmp_notify_work); + wiphy_work_queue(wiphy, &rtwdev->btc.icmp_notify_work); return PACKET_ICMP; } @@ -1103,39 +1131,23 @@ int rtw89_h2c_tx(struct rtw89_dev *rtwdev, return 0; } -int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, - struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel) +static int rtw89_core_tx_write_link(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, + struct rtw89_sta_link *rtwsta_link, + struct sk_buff *skb, int *qsel, bool sw_mld) { - struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta); - struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); - struct rtw89_core_tx_request tx_req = {0}; - struct rtw89_sta_link *rtwsta_link = NULL; - struct rtw89_vif_link *rtwvif_link; + struct ieee80211_sta *sta = rtwsta_link_to_sta_safe(rtwsta_link); + struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); + struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; + struct rtw89_core_tx_request tx_req = {}; int ret; - /* By default, driver writes tx via the link on HW-0. And then, - * according to links' status, HW can change tx to another link. - */ - - if (rtwsta) { - rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); - if (unlikely(!rtwsta_link)) { - rtw89_err(rtwdev, "tx: find no sta link on HW-0\n"); - return -ENOLINK; - } - } - - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); - if (unlikely(!rtwvif_link)) { - rtw89_err(rtwdev, "tx: find no vif link on HW-0\n"); - return -ENOLINK; - } - tx_req.skb = skb; tx_req.vif = vif; tx_req.sta = sta; tx_req.rtwvif_link = rtwvif_link; tx_req.rtwsta_link = rtwsta_link; + tx_req.desc_info.sw_mld = sw_mld; rtw89_traffic_stats_accu(rtwdev, &rtwdev->stats, skb, true); rtw89_traffic_stats_accu(rtwdev, &rtwvif->stats, skb, true); @@ -1154,6 +1166,33 @@ int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, return 0; } +int rtw89_core_tx_write(struct rtw89_dev *rtwdev, struct ieee80211_vif *vif, + struct ieee80211_sta *sta, struct sk_buff *skb, int *qsel) +{ + struct rtw89_sta *rtwsta = sta_to_rtwsta_safe(sta); + struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); + struct rtw89_sta_link *rtwsta_link = NULL; + struct rtw89_vif_link *rtwvif_link; + + if (rtwsta) { + rtwsta_link = rtw89_get_designated_link(rtwsta); + if (unlikely(!rtwsta_link)) { + rtw89_err(rtwdev, "tx: find no sta designated link\n"); + return -ENOLINK; + } + + rtwvif_link = rtwsta_link->rtwvif_link; + } else { + rtwvif_link = rtw89_get_designated_link(rtwvif); + if (unlikely(!rtwvif_link)) { + rtw89_err(rtwdev, "tx: find no vif designated link\n"); + return -ENOLINK; + } + } + + return rtw89_core_tx_write_link(rtwdev, rtwvif_link, rtwsta_link, skb, qsel, false); +} + static __le32 rtw89_build_txwd_body0(struct rtw89_tx_desc_info *desc_info) { u32 dword = FIELD_PREP(RTW89_TXWD_BODY0_WP_OFFSET, desc_info->wp_offset) | @@ -1381,7 +1420,9 @@ static __le32 rtw89_build_txwd_body2_v2(struct rtw89_tx_desc_info *desc_info) static __le32 rtw89_build_txwd_body3_v2(struct rtw89_tx_desc_info *desc_info) { - u32 dword = FIELD_PREP(BE_TXD_BODY3_WIFI_SEQ, desc_info->seq); + u32 dword = FIELD_PREP(BE_TXD_BODY3_WIFI_SEQ, desc_info->seq) | + FIELD_PREP(BE_TXD_BODY3_MLO_FLAG, desc_info->mlo) | + FIELD_PREP(BE_TXD_BODY3_IS_MLD_SW_EN, desc_info->sw_mld); return cpu_to_le32(dword); } @@ -1634,10 +1675,7 @@ static void rtw89_core_rx_process_phy_ppdu_iter(void *data, u8 evm_pos = 0; int i; - /* FIXME: For single link, taking link on HW-0 here is okay. But, when - * enabling multiple active links, we should determine the right link. - */ - rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); + rtwsta_link = rtw89_sta_get_link_inst(rtwsta, phy_ppdu->phy_idx); if (unlikely(!rtwsta_link)) return; @@ -2057,10 +2095,21 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev, break; if (aid == vif->cfg.aid) { - enum nl80211_he_ru_alloc rua = rtw89_he_rua_to_ru_alloc(tf_rua >> 1); + enum nl80211_he_ru_alloc rua; rtwvif->stats.rx_tf_acc++; rtwdev->stats.rx_tf_acc++; + + /* The following only required for HE trigger frame, but we + * cannot use UL HE-SIG-A2 reserved subfield to identify it + * since some 11ax APs will fill it with all 0s, which will + * be misunderstood as EHT trigger frame. + */ + if (bss_conf->eht_support) + break; + + rua = rtw89_he_rua_to_ru_alloc(tf_rua >> 1); + if (tf_bw == IEEE80211_TRIGGER_ULBW_160_80P80MHZ && rua <= NL80211_RATE_INFO_HE_RU_ALLOC_106) rtwvif_link->pwr_diff_en = true; @@ -2071,17 +2120,17 @@ static void rtw89_stats_trigger_frame(struct rtw89_dev *rtwdev, } } -static void rtw89_cancel_6ghz_probe_work(struct work_struct *work) +static void rtw89_cancel_6ghz_probe_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, cancel_6ghz_probe_work); struct list_head *pkt_list = rtwdev->scan_info.pkt_list; struct rtw89_pktofld_info *info; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); if (!rtwdev->scanning) - goto out; + return; list_for_each_entry(info, &pkt_list[NL80211_BAND_6GHZ], list) { if (!info->cancel || !test_bit(info->id, rtwdev->pkt_offload)) @@ -2094,9 +2143,6 @@ static void rtw89_cancel_6ghz_probe_work(struct work_struct *work) * since if during scanning, pkt_list is accessed in bottom half. */ } - -out: - mutex_unlock(&rtwdev->mutex); } static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, @@ -2131,7 +2177,7 @@ static void rtw89_core_cancel_6ghz_probe_tx(struct rtw89_dev *rtwdev, } if (queue_work) - ieee80211_queue_work(rtwdev->hw, &rtwdev->cancel_6ghz_probe_work); + wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->cancel_6ghz_probe_work); } static void rtw89_vif_sync_bcn_tsf(struct rtw89_vif_link *rtwvif_link, @@ -2154,8 +2200,10 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; struct rtw89_rx_desc_info *desc_info = iter_data->desc_info; struct sk_buff *skb = iter_data->skb; + struct ieee80211_rx_status *rx_status = IEEE80211_SKB_RXCB(skb); struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)skb->data; struct rtw89_rx_phy_ppdu *phy_ppdu = iter_data->phy_ppdu; + bool is_mld = ieee80211_vif_is_mld(vif); struct ieee80211_bss_conf *bss_conf; struct rtw89_vif_link *rtwvif_link; const u8 *bssid = iter_data->bssid; @@ -2167,10 +2215,7 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, rcu_read_lock(); - /* FIXME: For single link, taking link on HW-0 here is okay. But, when - * enabling multiple active links, we should determine the right link. - */ - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); + rtwvif_link = rtw89_vif_get_link_inst(rtwvif, desc_info->bb_sel); if (unlikely(!rtwvif_link)) goto out; @@ -2186,6 +2231,11 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, if (!ether_addr_equal(bss_conf->bssid, bssid)) goto out; + if (is_mld) { + rx_status->link_valid = true; + rx_status->link_id = rtwvif_link->link_id; + } + if (ieee80211_is_beacon(hdr->frame_control)) { if (vif->type == NL80211_IFTYPE_STATION && !test_bit(RTW89_FLAG_WOWLAN, rtwdev->flags)) { @@ -2194,8 +2244,11 @@ static void rtw89_vif_rx_stats_iter(void *data, u8 *mac, } pkt_stat->beacon_nr++; - if (phy_ppdu) + if (phy_ppdu) { ewma_rssi_add(&rtwdev->phystat.bcn_rssi, phy_ppdu->rssi_avg); + if (!test_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags)) + rtwvif_link->bcn_bw_idx = phy_ppdu->bw_idx; + } pkt_stat->beacon_rate = desc_info->data_rate; } @@ -2381,6 +2434,49 @@ static void rtw89_core_validate_rx_signal(struct ieee80211_rx_status *rx_status) rx_status->flag |= RX_FLAG_NO_SIGNAL_VAL; } +static void rtw89_core_update_rx_freq_from_ie(struct rtw89_dev *rtwdev, + struct sk_buff *skb, + struct ieee80211_rx_status *rx_status) +{ + struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *)skb->data; + size_t hdr_len, ielen; + u8 *variable; + int chan; + + if (!rtwdev->chip->rx_freq_frome_ie) + return; + + if (!rtwdev->scanning) + return; + + if (ieee80211_is_beacon(mgmt->frame_control)) { + variable = mgmt->u.beacon.variable; + hdr_len = offsetof(struct ieee80211_mgmt, + u.beacon.variable); + } else if (ieee80211_is_probe_resp(mgmt->frame_control)) { + variable = mgmt->u.probe_resp.variable; + hdr_len = offsetof(struct ieee80211_mgmt, + u.probe_resp.variable); + } else { + return; + } + + if (skb->len > hdr_len) + ielen = skb->len - hdr_len; + else + return; + + /* The parsing code for both 2GHz and 5GHz bands is the same in this + * function. + */ + chan = cfg80211_get_ies_channel_number(variable, ielen, NL80211_BAND_2GHZ); + if (chan == -1) + return; + + rx_status->band = chan > 14 ? RTW89_BAND_5G : RTW89_BAND_2G; + rx_status->freq = ieee80211_channel_to_frequency(chan, rx_status->band); +} + static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu, struct rtw89_rx_desc_info *desc_info, @@ -2398,6 +2494,7 @@ static void rtw89_core_rx_to_mac80211(struct rtw89_dev *rtwdev, rtw89_core_update_rx_status_by_ppdu(rtwdev, rx_status, phy_ppdu); rtw89_core_update_radiotap(rtwdev, skb_ppdu, rx_status); rtw89_core_validate_rx_signal(rx_status); + rtw89_core_update_rx_freq_from_ie(rtwdev, skb_ppdu, rx_status); /* In low power mode, it does RX in thread context. */ local_bh_disable(); @@ -2437,7 +2534,8 @@ static void rtw89_core_rx_process_ppdu_sts(struct rtw89_dev *rtwdev, .len = skb->len, .to_self = desc_info->addr1_match, .rate = desc_info->data_rate, - .mac_id = desc_info->mac_id}; + .mac_id = desc_info->mac_id, + .phy_idx = desc_info->bb_sel}; int ret; if (desc_info->mac_info_valid) { @@ -2548,6 +2646,7 @@ void rtw89_core_query_rxdesc_v2(struct rtw89_dev *rtwdev, desc_info->shift = le32_get_bits(rxd_s->dword0, BE_RXD_SHIFT_MASK); desc_info->long_rxdesc = le32_get_bits(rxd_s->dword0, BE_RXD_LONG_RXD); desc_info->pkt_type = le32_get_bits(rxd_s->dword0, BE_RXD_RPKT_TYPE_MASK); + desc_info->bb_sel = le32_get_bits(rxd_s->dword0, BE_RXD_BB_SEL); if (desc_info->pkt_type == RTW89_CORE_RX_TYPE_PPDU_STAT) desc_info->mac_info_valid = true; @@ -2620,10 +2719,7 @@ void rtw89_core_stats_sta_rx_status_iter(void *data, struct ieee80211_sta *sta) struct rtw89_sta_link *rtwsta_link; u8 mac_id = iter_data->mac_id; - /* FIXME: For single link, taking link on HW-0 here is okay. But, when - * enabling multiple active links, we should determine the right link. - */ - rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); + rtwsta_link = rtw89_sta_get_link_inst(rtwsta, desc_info->bb_sel); if (unlikely(!rtwsta_link)) return; @@ -3072,9 +3168,9 @@ static bool rtw89_core_txq_agg_wait(struct rtw89_dev *rtwdev, if (!rtwsta) return false; - rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); + rtwsta_link = rtw89_get_designated_link(rtwsta); if (unlikely(!rtwsta_link)) { - rtw89_err(rtwdev, "agg wait: find no link on HW-0\n"); + rtw89_err(rtwdev, "agg wait: find no designated link\n"); return false; } @@ -3143,13 +3239,14 @@ static void rtw89_core_txq_schedule(struct rtw89_dev *rtwdev, u8 ac, bool *reinv ieee80211_txq_schedule_end(hw, ac); } -static void rtw89_ips_work(struct work_struct *work) +static void rtw89_ips_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, ips_work); - mutex_lock(&rtwdev->mutex); + + lockdep_assert_wiphy(wiphy); + rtw89_enter_ips_by_hwflags(rtwdev); - mutex_unlock(&rtwdev->mutex); } static void rtw89_core_txq_work(struct work_struct *w) @@ -3238,8 +3335,10 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, { struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); int link_id = ieee80211_vif_is_mld(vif) ? rtwvif_link->link_id : -1; + struct rtw89_sta_link *rtwsta_link; struct ieee80211_sta *sta; struct ieee80211_hdr *hdr; + struct rtw89_sta *rtwsta; struct sk_buff *skb; int ret, qsel; @@ -3252,6 +3351,7 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, ret = -EINVAL; goto out; } + rtwsta = sta_to_rtwsta(sta); skb = ieee80211_nullfunc_get(rtwdev->hw, vif, link_id, qos); if (!skb) { @@ -3263,7 +3363,13 @@ static int rtw89_core_send_nullfunc(struct rtw89_dev *rtwdev, if (ps) hdr->frame_control |= cpu_to_le16(IEEE80211_FCTL_PM); - ret = rtw89_core_tx_write(rtwdev, vif, sta, skb, &qsel); + rtwsta_link = rtwsta->links[rtwvif_link->link_id]; + if (unlikely(!rtwsta_link)) { + ret = -ENOLINK; + goto out; + } + + ret = rtw89_core_tx_write_link(rtwdev, rtwvif_link, rtwsta_link, skb, &qsel, true); if (ret) { rtw89_warn(rtwdev, "nullfunc transmit failed: %d\n", ret); dev_kfree_skb_any(skb); @@ -3283,6 +3389,9 @@ out: void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + struct rtw89_chanctx_pause_parm pause_parm = { + .rsn = RTW89_CHANCTX_PAUSE_REASON_ROC, + }; struct ieee80211_hw *hw = rtwdev->hw; struct rtw89_roc *roc = &rtwvif->roc; struct rtw89_vif_link *rtwvif_link; @@ -3291,19 +3400,21 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) u32 reg; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_leave_ips_by_hwflags(rtwdev); rtw89_leave_lps(rtwdev); - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, RTW89_ROC_BY_LINK_INDEX); + rtwvif_link = rtw89_get_designated_link(rtwvif); if (unlikely(!rtwvif_link)) { - rtw89_err(rtwdev, "roc start: find no link on HW-%u\n", - RTW89_ROC_BY_LINK_INDEX); + rtw89_err(rtwdev, "roc start: find no designated link\n"); return; } - rtw89_chanctx_pause(rtwdev, RTW89_CHANCTX_PAUSE_REASON_ROC); + roc->link_id = rtwvif_link->link_id; + + pause_parm.trigger = rtwvif_link; + rtw89_chanctx_pause(rtwdev, &pause_parm); ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, true); if (ret) @@ -3323,16 +3434,16 @@ void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) } cfg80211_chandef_create(&roc_chan, &roc->chan, NL80211_CHAN_NO_HT); - rtw89_config_roc_chandef(rtwdev, rtwvif_link->chanctx_idx, &roc_chan); + rtw89_config_roc_chandef(rtwdev, rtwvif_link, &roc_chan); rtw89_set_channel(rtwdev); reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); rtw89_write32_clr(rtwdev, reg, B_AX_A_UC_CAM_MATCH | B_AX_A_BC_CAM_MATCH); ieee80211_ready_on_channel(hw); - cancel_delayed_work(&rtwvif->roc.roc_work); - ieee80211_queue_delayed_work(hw, &rtwvif->roc.roc_work, - msecs_to_jiffies(rtwvif->roc.duration)); + wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); + wiphy_delayed_work_queue(hw->wiphy, &rtwvif->roc.roc_work, + msecs_to_jiffies(rtwvif->roc.duration)); } void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) @@ -3345,17 +3456,17 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) u32 reg; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); ieee80211_remain_on_channel_expired(hw); rtw89_leave_ips_by_hwflags(rtwdev); rtw89_leave_lps(rtwdev); - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, RTW89_ROC_BY_LINK_INDEX); + rtwvif_link = rtwvif->links[roc->link_id]; if (unlikely(!rtwvif_link)) { - rtw89_err(rtwdev, "roc end: find no link on HW-%u\n", - RTW89_ROC_BY_LINK_INDEX); + rtw89_err(rtwdev, "roc end: find no link (link id %u)\n", + roc->link_id); return; } @@ -3363,7 +3474,7 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr); roc->state = RTW89_ROC_IDLE; - rtw89_config_roc_chandef(rtwdev, rtwvif_link->chanctx_idx, NULL); + rtw89_config_roc_chandef(rtwdev, rtwvif_link, NULL); rtw89_chanctx_proceed(rtwdev, NULL); ret = rtw89_core_send_nullfunc(rtwdev, rtwvif_link, true, false); if (ret) @@ -3377,18 +3488,18 @@ void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) queue_work(rtwdev->txq_wq, &rtwdev->txq_work); if (hw->conf.flags & IEEE80211_CONF_IDLE) - ieee80211_queue_delayed_work(hw, &roc->roc_work, - msecs_to_jiffies(RTW89_ROC_IDLE_TIMEOUT)); + wiphy_delayed_work_queue(hw->wiphy, &roc->roc_work, + msecs_to_jiffies(RTW89_ROC_IDLE_TIMEOUT)); } -void rtw89_roc_work(struct work_struct *work) +void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_vif *rtwvif = container_of(work, struct rtw89_vif, roc.roc_work.work); struct rtw89_dev *rtwdev = rtwvif->rtwdev; struct rtw89_roc *roc = &rtwvif->roc; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); switch (roc->state) { case RTW89_ROC_IDLE: @@ -3401,8 +3512,6 @@ void rtw89_roc_work(struct work_struct *work) default: break; } - - mutex_unlock(&rtwdev->mutex); } static enum rtw89_tfc_lv rtw89_get_traffic_level(struct rtw89_dev *rtwdev, @@ -3533,26 +3642,118 @@ void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, ewma_tp_init(&stats->rx_ewma_tp); } -static void rtw89_track_work(struct work_struct *work) +#define RTW89_MLSR_GOTO_2GHZ_THRESHOLD -53 +#define RTW89_MLSR_EXIT_2GHZ_THRESHOLD -38 +static void rtw89_core_mlsr_link_decision(struct rtw89_dev *rtwdev, + struct rtw89_vif *rtwvif) +{ + unsigned int sel_link_id = IEEE80211_MLD_MAX_NUM_LINKS; + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + struct rtw89_vif_link *rtwvif_link; + const struct rtw89_chan *chan; + unsigned long usable_links; + unsigned int link_id; + u8 decided_bands; + u8 rssi; + + rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); + if (unlikely(!rssi)) + return; + + if (RTW89_RSSI_RAW_TO_DBM(rssi) >= RTW89_MLSR_EXIT_2GHZ_THRESHOLD) + decided_bands = BIT(RTW89_BAND_5G) | BIT(RTW89_BAND_6G); + else if (RTW89_RSSI_RAW_TO_DBM(rssi) <= RTW89_MLSR_GOTO_2GHZ_THRESHOLD) + decided_bands = BIT(RTW89_BAND_2G); + else + return; + + usable_links = ieee80211_vif_usable_links(vif); + + rtwvif_link = rtw89_get_designated_link(rtwvif); + if (unlikely(!rtwvif_link)) + goto select; + + chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); + if (decided_bands & BIT(chan->band_type)) + return; + + usable_links &= ~BIT(rtwvif_link->link_id); + +select: + rcu_read_lock(); + + for_each_set_bit(link_id, &usable_links, IEEE80211_MLD_MAX_NUM_LINKS) { + struct ieee80211_bss_conf *link_conf; + struct ieee80211_channel *channel; + enum rtw89_band band; + + link_conf = rcu_dereference(vif->link_conf[link_id]); + if (unlikely(!link_conf)) + continue; + + channel = link_conf->chanreq.oper.chan; + if (unlikely(!channel)) + continue; + + band = rtw89_nl80211_to_hw_band(channel->band); + if (decided_bands & BIT(band)) { + sel_link_id = link_id; + break; + } + } + + rcu_read_unlock(); + + if (sel_link_id == IEEE80211_MLD_MAX_NUM_LINKS) + return; + + rtw89_core_mlsr_switch(rtwdev, rtwvif, sel_link_id); +} + +static void rtw89_core_mlo_track(struct rtw89_dev *rtwdev) +{ + struct rtw89_hal *hal = &rtwdev->hal; + struct ieee80211_vif *vif; + struct rtw89_vif *rtwvif; + + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_MLO)) + return; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + vif = rtwvif_to_vif(rtwvif); + if (!vif->cfg.assoc || !ieee80211_vif_is_mld(vif)) + continue; + + switch (rtwvif->mlo_mode) { + case RTW89_MLO_MODE_MLSR: + rtw89_core_mlsr_link_decision(rtwdev, rtwvif); + break; + default: + break; + } + } +} + +static void rtw89_track_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, track_work.work); bool tfc_changed; + lockdep_assert_wiphy(wiphy); + if (test_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags)) return; - mutex_lock(&rtwdev->mutex); - if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) - goto out; + return; - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, - RTW89_TRACK_WORK_PERIOD); + wiphy_delayed_work_queue(wiphy, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); tfc_changed = rtw89_traffic_stats_track(rtwdev); if (rtwdev->scanning) - goto out; + return; rtw89_leave_lps(rtwdev); @@ -3571,15 +3772,13 @@ static void rtw89_track_work(struct work_struct *work) rtw89_phy_antdiv_track(rtwdev); rtw89_phy_ul_tb_ctrl_track(rtwdev); rtw89_phy_edcca_track(rtwdev); - rtw89_tas_track(rtwdev); + rtw89_sar_track(rtwdev); rtw89_chanctx_track(rtwdev); rtw89_core_rfkill_poll(rtwdev, false); + rtw89_core_mlo_track(rtwdev); if (rtwdev->lps_enabled && !rtwdev->btc.lps) rtw89_enter_lps_track(rtwdev); - -out: - mutex_unlock(&rtwdev->mutex); } u8 rtw89_core_acquire_bit_map(unsigned long *addr, unsigned long size) @@ -3613,7 +3812,7 @@ int rtw89_core_acquire_sta_ba_entry(struct rtw89_dev *rtwdev, u8 idx; int i; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); idx = rtw89_core_acquire_bit_map(cam_info->ba_cam_map, chip->bacam_num); if (idx == chip->bacam_num) { @@ -3657,7 +3856,7 @@ int rtw89_core_release_sta_ba_entry(struct rtw89_dev *rtwdev, struct rtw89_ba_cam_entry *entry = NULL, *tmp; u8 idx; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); list_for_each_entry_safe(entry, tmp, &rtwsta_link->ba_cam_list, list) { if (entry->tid != tid) @@ -3805,6 +4004,9 @@ int rtw89_core_sta_link_disassoc(struct rtw89_dev *rtwdev, if (vif->type == NL80211_IFTYPE_STATION) rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, false); + if (rtwvif_link->wifi_role == RTW89_WIFI_ROLE_P2P_CLIENT) + rtw89_p2p_noa_once_deinit(rtwvif_link); + return 0; } @@ -4320,17 +4522,18 @@ static void rtw89_init_eht_cap(struct rtw89_dev *rtwdev, #define RTW89_SBAND_IFTYPES_NR 2 -static void rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev, - enum nl80211_band band, - struct ieee80211_supported_band *sband) +static int rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev, + enum nl80211_band band, + struct ieee80211_supported_band *sband) { struct ieee80211_sband_iftype_data *iftype_data; enum nl80211_iftype iftype; int idx = 0; - iftype_data = kcalloc(RTW89_SBAND_IFTYPES_NR, sizeof(*iftype_data), GFP_KERNEL); + iftype_data = devm_kcalloc(rtwdev->dev, RTW89_SBAND_IFTYPES_NR, + sizeof(*iftype_data), GFP_KERNEL); if (!iftype_data) - return; + return -ENOMEM; for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { switch (iftype) { @@ -4355,103 +4558,101 @@ static void rtw89_init_he_eht_cap(struct rtw89_dev *rtwdev, } _ieee80211_set_sband_iftype_data(sband, iftype_data, idx); + return 0; +} + +static struct ieee80211_supported_band * +rtw89_core_sband_dup(struct rtw89_dev *rtwdev, + const struct ieee80211_supported_band *sband) +{ + struct ieee80211_supported_band *dup; + + dup = devm_kmemdup(rtwdev->dev, sband, sizeof(*sband), GFP_KERNEL); + if (!dup) + return NULL; + + dup->channels = devm_kmemdup(rtwdev->dev, sband->channels, + sizeof(*sband->channels) * sband->n_channels, + GFP_KERNEL); + if (!dup->channels) + return NULL; + + dup->bitrates = devm_kmemdup(rtwdev->dev, sband->bitrates, + sizeof(*sband->bitrates) * sband->n_bitrates, + GFP_KERNEL); + if (!dup->bitrates) + return NULL; + + return dup; } static int rtw89_core_set_supported_band(struct rtw89_dev *rtwdev) { struct ieee80211_hw *hw = rtwdev->hw; - struct ieee80211_supported_band *sband_2ghz = NULL, *sband_5ghz = NULL; - struct ieee80211_supported_band *sband_6ghz = NULL; - u32 size = sizeof(struct ieee80211_supported_band); + struct ieee80211_supported_band *sband; u8 support_bands = rtwdev->chip->support_bands; + int ret; if (support_bands & BIT(NL80211_BAND_2GHZ)) { - sband_2ghz = kmemdup(&rtw89_sband_2ghz, size, GFP_KERNEL); - if (!sband_2ghz) - goto err; - rtw89_init_ht_cap(rtwdev, &sband_2ghz->ht_cap); - rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_2GHZ, sband_2ghz); - hw->wiphy->bands[NL80211_BAND_2GHZ] = sband_2ghz; + sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_2ghz); + if (!sband) + return -ENOMEM; + rtw89_init_ht_cap(rtwdev, &sband->ht_cap); + ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_2GHZ, sband); + if (ret) + return ret; + hw->wiphy->bands[NL80211_BAND_2GHZ] = sband; } if (support_bands & BIT(NL80211_BAND_5GHZ)) { - sband_5ghz = kmemdup(&rtw89_sband_5ghz, size, GFP_KERNEL); - if (!sband_5ghz) - goto err; - rtw89_init_ht_cap(rtwdev, &sband_5ghz->ht_cap); - rtw89_init_vht_cap(rtwdev, &sband_5ghz->vht_cap); - rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_5GHZ, sband_5ghz); - hw->wiphy->bands[NL80211_BAND_5GHZ] = sband_5ghz; + sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_5ghz); + if (!sband) + return -ENOMEM; + rtw89_init_ht_cap(rtwdev, &sband->ht_cap); + rtw89_init_vht_cap(rtwdev, &sband->vht_cap); + ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_5GHZ, sband); + if (ret) + return ret; + hw->wiphy->bands[NL80211_BAND_5GHZ] = sband; } if (support_bands & BIT(NL80211_BAND_6GHZ)) { - sband_6ghz = kmemdup(&rtw89_sband_6ghz, size, GFP_KERNEL); - if (!sband_6ghz) - goto err; - rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_6GHZ, sband_6ghz); - hw->wiphy->bands[NL80211_BAND_6GHZ] = sband_6ghz; + sband = rtw89_core_sband_dup(rtwdev, &rtw89_sband_6ghz); + if (!sband) + return -ENOMEM; + ret = rtw89_init_he_eht_cap(rtwdev, NL80211_BAND_6GHZ, sband); + if (ret) + return ret; + hw->wiphy->bands[NL80211_BAND_6GHZ] = sband; } return 0; - -err: - hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL; - hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL; - hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL; - if (sband_2ghz) - kfree((__force void *)sband_2ghz->iftype_data); - if (sband_5ghz) - kfree((__force void *)sband_5ghz->iftype_data); - if (sband_6ghz) - kfree((__force void *)sband_6ghz->iftype_data); - kfree(sband_2ghz); - kfree(sband_5ghz); - kfree(sband_6ghz); - return -ENOMEM; -} - -static void rtw89_core_clr_supported_band(struct rtw89_dev *rtwdev) -{ - struct ieee80211_hw *hw = rtwdev->hw; - - if (hw->wiphy->bands[NL80211_BAND_2GHZ]) - kfree((__force void *)hw->wiphy->bands[NL80211_BAND_2GHZ]->iftype_data); - if (hw->wiphy->bands[NL80211_BAND_5GHZ]) - kfree((__force void *)hw->wiphy->bands[NL80211_BAND_5GHZ]->iftype_data); - if (hw->wiphy->bands[NL80211_BAND_6GHZ]) - kfree((__force void *)hw->wiphy->bands[NL80211_BAND_6GHZ]->iftype_data); - kfree(hw->wiphy->bands[NL80211_BAND_2GHZ]); - kfree(hw->wiphy->bands[NL80211_BAND_5GHZ]); - kfree(hw->wiphy->bands[NL80211_BAND_6GHZ]); - hw->wiphy->bands[NL80211_BAND_2GHZ] = NULL; - hw->wiphy->bands[NL80211_BAND_5GHZ] = NULL; - hw->wiphy->bands[NL80211_BAND_6GHZ] = NULL; } static void rtw89_core_ppdu_sts_init(struct rtw89_dev *rtwdev) { int i; - for (i = 0; i < RTW89_PHY_MAX; i++) + for (i = 0; i < RTW89_PHY_NUM; i++) skb_queue_head_init(&rtwdev->ppdu_sts.rx_queue[i]); - for (i = 0; i < RTW89_PHY_MAX; i++) + for (i = 0; i < RTW89_PHY_NUM; i++) rtwdev->ppdu_sts.curr_rx_ppdu_cnt[i] = U8_MAX; } -void rtw89_core_update_beacon_work(struct work_struct *work) +void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev; struct rtw89_vif_link *rtwvif_link = container_of(work, struct rtw89_vif_link, update_beacon_work); + lockdep_assert_wiphy(wiphy); + if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE) return; rtwdev = rtwvif_link->rtwvif->rtwdev; - mutex_lock(&rtwdev->mutex); rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link); - mutex_unlock(&rtwdev->mutex); } int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond) @@ -4560,16 +4761,14 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) rtw89_mac_cfg_phy_rpt_bands(rtwdev, true); rtw89_mac_update_rts_threshold(rtwdev); - rtw89_tas_reset(rtwdev); - ret = rtw89_hci_start(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to start hci\n"); return ret; } - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, - RTW89_TRACK_WORK_PERIOD); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); set_bit(RTW89_FLAG_RUNNING, rtwdev->flags); @@ -4583,8 +4782,11 @@ int rtw89_core_start(struct rtw89_dev *rtwdev) void rtw89_core_stop(struct rtw89_dev *rtwdev) { + struct wiphy *wiphy = rtwdev->hw->wiphy; struct rtw89_btc *btc = &rtwdev->btc; + lockdep_assert_wiphy(wiphy); + /* Prvent to stop twice; enter_ips and ops_stop */ if (!test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) return; @@ -4593,25 +4795,21 @@ void rtw89_core_stop(struct rtw89_dev *rtwdev) clear_bit(RTW89_FLAG_RUNNING, rtwdev->flags); - mutex_unlock(&rtwdev->mutex); - - cancel_work_sync(&rtwdev->c2h_work); - cancel_work_sync(&rtwdev->cancel_6ghz_probe_work); - cancel_work_sync(&btc->eapol_notify_work); - cancel_work_sync(&btc->arp_notify_work); - cancel_work_sync(&btc->dhcp_notify_work); - cancel_work_sync(&btc->icmp_notify_work); + wiphy_work_cancel(wiphy, &rtwdev->c2h_work); + wiphy_work_cancel(wiphy, &rtwdev->cancel_6ghz_probe_work); + wiphy_work_cancel(wiphy, &btc->eapol_notify_work); + wiphy_work_cancel(wiphy, &btc->arp_notify_work); + wiphy_work_cancel(wiphy, &btc->dhcp_notify_work); + wiphy_work_cancel(wiphy, &btc->icmp_notify_work); cancel_delayed_work_sync(&rtwdev->txq_reinvoke_work); - cancel_delayed_work_sync(&rtwdev->track_work); - cancel_delayed_work_sync(&rtwdev->chanctx_work); - cancel_delayed_work_sync(&rtwdev->coex_act1_work); - cancel_delayed_work_sync(&rtwdev->coex_bt_devinfo_work); - cancel_delayed_work_sync(&rtwdev->coex_rfk_chk_work); - cancel_delayed_work_sync(&rtwdev->cfo_track_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->chanctx_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_act1_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_bt_devinfo_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->coex_rfk_chk_work); + wiphy_delayed_work_cancel(wiphy, &rtwdev->cfo_track_work); cancel_delayed_work_sync(&rtwdev->forbid_ba_work); - cancel_delayed_work_sync(&rtwdev->antdiv_work); - - mutex_lock(&rtwdev->mutex); + wiphy_delayed_work_cancel(wiphy, &rtwdev->antdiv_work); rtw89_btc_ntfy_poweroff(rtwdev); rtw89_hci_flush_queues(rtwdev, BIT(rtwdev->hw->queues) - 1, true); @@ -4736,6 +4934,7 @@ struct rtw89_vif_link *rtw89_vif_set_link(struct rtw89_vif *rtwvif, set_bit(index, rtwvif->links_inst_map); rtwvif->links[link_id] = rtwvif_link; + list_add_tail(&rtwvif_link->dlink_schd, &rtwvif->dlink_pool); return rtwvif_link; err: @@ -4756,6 +4955,7 @@ void rtw89_vif_unset_link(struct rtw89_vif *rtwvif, unsigned int link_id) index = rtw89_vif_link_inst_get_index(link); clear_bit(index, rtwvif->links_inst_map); *container = NULL; + list_del(&link->dlink_schd); } struct rtw89_sta_link *rtw89_sta_set_link(struct rtw89_sta *rtwsta, @@ -4786,6 +4986,7 @@ struct rtw89_sta_link *rtw89_sta_set_link(struct rtw89_sta *rtwsta, set_bit(index, rtwsta->links_inst_map); rtwsta->links[link_id] = rtwsta_link; + list_add_tail(&rtwsta_link->dlink_schd, &rtwsta->dlink_pool); return rtwsta_link; err: @@ -4806,6 +5007,7 @@ void rtw89_sta_unset_link(struct rtw89_sta *rtwsta, unsigned int link_id) index = rtw89_sta_link_inst_get_index(link); clear_bit(index, rtwsta->links_inst_map); *container = NULL; + list_del(&link->dlink_schd); } int rtw89_core_init(struct rtw89_dev *rtwdev) @@ -4822,35 +5024,36 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) continue; INIT_LIST_HEAD(&rtwdev->scan_info.pkt_list[band]); } + INIT_LIST_HEAD(&rtwdev->scan_info.chan_list); INIT_WORK(&rtwdev->ba_work, rtw89_core_ba_work); INIT_WORK(&rtwdev->txq_work, rtw89_core_txq_work); INIT_DELAYED_WORK(&rtwdev->txq_reinvoke_work, rtw89_core_txq_reinvoke_work); - INIT_DELAYED_WORK(&rtwdev->track_work, rtw89_track_work); - INIT_DELAYED_WORK(&rtwdev->chanctx_work, rtw89_chanctx_work); - INIT_DELAYED_WORK(&rtwdev->coex_act1_work, rtw89_coex_act1_work); - INIT_DELAYED_WORK(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); - INIT_DELAYED_WORK(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); - INIT_DELAYED_WORK(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); + wiphy_delayed_work_init(&rtwdev->track_work, rtw89_track_work); + wiphy_delayed_work_init(&rtwdev->chanctx_work, rtw89_chanctx_work); + wiphy_delayed_work_init(&rtwdev->coex_act1_work, rtw89_coex_act1_work); + wiphy_delayed_work_init(&rtwdev->coex_bt_devinfo_work, rtw89_coex_bt_devinfo_work); + wiphy_delayed_work_init(&rtwdev->coex_rfk_chk_work, rtw89_coex_rfk_chk_work); + wiphy_delayed_work_init(&rtwdev->cfo_track_work, rtw89_phy_cfo_track_work); INIT_DELAYED_WORK(&rtwdev->forbid_ba_work, rtw89_forbid_ba_work); - INIT_DELAYED_WORK(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); + wiphy_delayed_work_init(&rtwdev->antdiv_work, rtw89_phy_antdiv_work); rtwdev->txq_wq = alloc_workqueue("rtw89_tx_wq", WQ_UNBOUND | WQ_HIGHPRI, 0); if (!rtwdev->txq_wq) return -ENOMEM; spin_lock_init(&rtwdev->ba_lock); spin_lock_init(&rtwdev->rpwm_lock); - mutex_init(&rtwdev->mutex); mutex_init(&rtwdev->rf_mutex); rtwdev->total_sta_assoc = 0; rtw89_init_wait(&rtwdev->mcc.wait); + rtw89_init_wait(&rtwdev->mlo.wait); rtw89_init_wait(&rtwdev->mac.fw_ofld_wait); rtw89_init_wait(&rtwdev->wow.wait); rtw89_init_wait(&rtwdev->mac.ps_wait); - INIT_WORK(&rtwdev->c2h_work, rtw89_fw_c2h_work); - INIT_WORK(&rtwdev->ips_work, rtw89_ips_work); + wiphy_work_init(&rtwdev->c2h_work, rtw89_fw_c2h_work); + wiphy_work_init(&rtwdev->ips_work, rtw89_ips_work); + wiphy_work_init(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work); INIT_WORK(&rtwdev->load_firmware_work, rtw89_load_firmware_work); - INIT_WORK(&rtwdev->cancel_6ghz_probe_work, rtw89_cancel_6ghz_probe_work); skb_queue_head_init(&rtwdev->c2h_queue); rtw89_core_ppdu_sts_init(rtwdev); @@ -4864,13 +5067,16 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) { rtwdev->dbcc_en = true; rtwdev->mac.qta_mode = RTW89_QTA_DBCC; - rtwdev->mlo_dbcc_mode = MLO_2_PLUS_0_1RF; + rtwdev->mlo_dbcc_mode = MLO_1_PLUS_1_1RF; } - INIT_WORK(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); - INIT_WORK(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); - INIT_WORK(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work); - INIT_WORK(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work); + rtwdev->bbs[RTW89_PHY_0].phy_idx = RTW89_PHY_0; + rtwdev->bbs[RTW89_PHY_1].phy_idx = RTW89_PHY_1; + + wiphy_work_init(&btc->eapol_notify_work, rtw89_btc_ntfy_eapol_packet_work); + wiphy_work_init(&btc->arp_notify_work, rtw89_btc_ntfy_arp_packet_work); + wiphy_work_init(&btc->dhcp_notify_work, rtw89_btc_ntfy_dhcp_packet_work); + wiphy_work_init(&btc->icmp_notify_work, rtw89_btc_ntfy_icmp_packet_work); init_completion(&rtwdev->fw.req.completion); init_completion(&rtwdev->rfk_wait.completion); @@ -4879,7 +5085,7 @@ int rtw89_core_init(struct rtw89_dev *rtwdev) rtw89_ser_init(rtwdev); rtw89_entity_init(rtwdev); - rtw89_tas_init(rtwdev); + rtw89_sar_init(rtwdev); rtw89_phy_ant_gain_init(rtwdev); return 0; @@ -4890,11 +5096,10 @@ void rtw89_core_deinit(struct rtw89_dev *rtwdev) { rtw89_ser_deinit(rtwdev); rtw89_unload_firmware(rtwdev); - rtw89_fw_free_all_early_h2c(rtwdev); + __rtw89_fw_free_all_early_h2c(rtwdev); destroy_workqueue(rtwdev->txq_wq); mutex_destroy(&rtwdev->rf_mutex); - mutex_destroy(&rtwdev->mutex); } EXPORT_SYMBOL(rtw89_core_deinit); @@ -4903,17 +5108,16 @@ void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv { const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); rtwdev->scanning = true; - rtw89_leave_lps(rtwdev); - if (hw_scan) - rtw89_leave_ips_by_hwflags(rtwdev); ether_addr_copy(rtwvif_link->mac_addr, mac_addr); rtw89_btc_ntfy_scan_start(rtwdev, rtwvif_link->phy_idx, chan->band_type); rtw89_chip_rfk_scan(rtwdev, rtwvif_link, true); rtw89_hci_recalc_int_mit(rtwdev); - rtw89_phy_config_edcca(rtwdev, true); + rtw89_phy_config_edcca(rtwdev, bb, true); + rtw89_tas_scan(rtwdev, true); rtw89_fw_h2c_cam(rtwdev, rtwvif_link, NULL, mac_addr); } @@ -4922,6 +5126,7 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool hw_scan) { struct ieee80211_bss_conf *bss_conf; + struct rtw89_bb_ctx *bb; if (!rtwvif_link) return; @@ -4937,12 +5142,15 @@ void rtw89_core_scan_complete(struct rtw89_dev *rtwdev, rtw89_chip_rfk_scan(rtwdev, rtwvif_link, false); rtw89_btc_ntfy_scan_finish(rtwdev, rtwvif_link->phy_idx); - rtw89_phy_config_edcca(rtwdev, false); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + rtw89_phy_config_edcca(rtwdev, bb, false); + rtw89_tas_scan(rtwdev, false); rtwdev->scanning = false; - rtwdev->dig.bypass_dig = true; + rtw89_for_each_active_bb(rtwdev, bb) + bb->dig.bypass_dig = true; if (hw_scan && (rtwdev->hw->conf.flags & IEEE80211_CONF_IDLE)) - ieee80211_queue_work(rtwdev->hw, &rtwdev->ips_work); + wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->ips_work); } static void rtw89_read_chip_ver(struct rtw89_dev *rtwdev) @@ -5017,6 +5225,76 @@ out: rtw89_load_txpwr_table(rtwdev, rtwdev->rfe_parms->byr_tbl); } +int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + unsigned int link_id) +{ + struct ieee80211_vif *vif = rtwvif_to_vif(rtwvif); + u16 usable_links = ieee80211_vif_usable_links(vif); + u16 active_links = vif->active_links; + struct rtw89_vif_link *target, *cur; + int ret; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (unlikely(!ieee80211_vif_is_mld(vif))) + return -EOPNOTSUPP; + + if (unlikely(!(usable_links & BIT(link_id)))) { + rtw89_warn(rtwdev, "%s: link id %u is not usable\n", __func__, + link_id); + return -ENOLINK; + } + + if (active_links == BIT(link_id)) + return 0; + + rtw89_debug(rtwdev, RTW89_DBG_STATE, "%s: switch to link id %u MLSR\n", + __func__, link_id); + + rtw89_leave_lps(rtwdev); + + ieee80211_stop_queues(rtwdev->hw); + flush_work(&rtwdev->txq_work); + + cur = rtw89_get_designated_link(rtwvif); + + ret = ieee80211_set_active_links(vif, active_links | BIT(link_id)); + if (ret) { + rtw89_err(rtwdev, "%s: failed to activate link id %u\n", + __func__, link_id); + goto wake_queue; + } + + target = rtwvif->links[link_id]; + if (unlikely(!target)) { + rtw89_err(rtwdev, "%s: failed to confirm link id %u\n", + __func__, link_id); + + ieee80211_set_active_links(vif, active_links); + ret = -EFAULT; + goto wake_queue; + } + + if (likely(cur)) + rtw89_fw_h2c_mlo_link_cfg(rtwdev, cur, false); + + rtw89_fw_h2c_mlo_link_cfg(rtwdev, target, true); + + ret = ieee80211_set_active_links(vif, BIT(link_id)); + if (ret) + rtw89_err(rtwdev, "%s: failed to inactivate links 0x%x\n", + __func__, active_links); + + rtw89_chip_rfk_channel(rtwdev, target); + + rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; + +wake_queue: + ieee80211_wake_queues(rtwdev->hw); + + return ret; +} + static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; @@ -5042,8 +5320,6 @@ static int rtw89_chip_efuse_info_setup(struct rtw89_dev *rtwdev) rtw89_hci_mac_pre_deinit(rtwdev); - rtw89_mac_pwr_off(rtwdev); - return 0; } @@ -5124,36 +5400,45 @@ int rtw89_chip_info_setup(struct rtw89_dev *rtwdev) rtw89_read_chip_ver(rtwdev); + ret = rtw89_mac_pwr_on(rtwdev); + if (ret) { + rtw89_err(rtwdev, "failed to power on\n"); + return ret; + } + ret = rtw89_wait_firmware_completion(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to wait firmware completion\n"); - return ret; + goto out; } ret = rtw89_fw_recognize(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to recognize firmware\n"); - return ret; + goto out; } ret = rtw89_chip_efuse_info_setup(rtwdev); if (ret) - return ret; + goto out; ret = rtw89_fw_recognize_elements(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to recognize firmware elements\n"); - return ret; + goto out; } ret = rtw89_chip_board_info_setup(rtwdev); if (ret) - return ret; + goto out; rtw89_core_setup_rfe_parms(rtwdev); rtwdev->ps_mode = rtw89_update_ps_mode(rtwdev); - return 0; +out: + rtw89_mac_pwr_off(rtwdev); + + return ret; } EXPORT_SYMBOL(rtw89_chip_info_setup); @@ -5253,8 +5538,11 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) if (chip->chip_gen == RTW89_CHIP_BE) hw->wiphy->flags |= WIPHY_FLAG_DISABLE_WEXT; - if (rtwdev->support_mlo) + if (rtwdev->support_mlo) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; + hw->wiphy->iftype_ext_capab = rtw89_iftypes_ext_capa; + hw->wiphy->num_iftype_ext_capab = ARRAY_SIZE(rtw89_iftypes_ext_capa); + } hw->wiphy->features |= NL80211_FEATURE_SCAN_RANDOM_MAC_ADDR; @@ -5285,7 +5573,7 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) ret = rtw89_regd_setup(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to set up regd\n"); - goto err_free_supported_band; + return ret; } hw->wiphy->sar_capa = &rtw89_sar_capa; @@ -5293,10 +5581,10 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) ret = ieee80211_register_hw(hw); if (ret) { rtw89_err(rtwdev, "failed to register hw\n"); - goto err_free_supported_band; + return ret; } - ret = rtw89_regd_init(rtwdev, rtw89_regd_notifier); + ret = rtw89_regd_init_hint(rtwdev); if (ret) { rtw89_err(rtwdev, "failed to init regd\n"); goto err_unregister_hw; @@ -5308,8 +5596,6 @@ static int rtw89_core_register_hw(struct rtw89_dev *rtwdev) err_unregister_hw: ieee80211_unregister_hw(hw); -err_free_supported_band: - rtw89_core_clr_supported_band(rtwdev); return ret; } @@ -5320,7 +5606,6 @@ static void rtw89_core_unregister_hw(struct rtw89_dev *rtwdev) rtw89_rfkill_polling_deinit(rtwdev); ieee80211_unregister_hw(hw); - rtw89_core_clr_supported_band(rtwdev); } int rtw89_core_register(struct rtw89_dev *rtwdev) @@ -5388,13 +5673,13 @@ struct rtw89_dev *rtw89_alloc_ieee80211_hw(struct device *device, if (!hw) goto err; - /* TODO: When driver MLO arch. is done, determine whether to support MLO - * according to the following conditions. - * 1. run with chanctx_ops - * 2. chip->support_link_num != 0 - * 3. FW feature supports AP_LINK_PS + /* Currently, our AP_LINK_PS handling only works for non-MLD softap + * or MLD-single-link softap. If RTW89_MLD_NON_STA_LINK_NUM enlarges, + * please tweak entire AP_LINKS_PS handling before supporting MLO. */ - support_mlo = false; + support_mlo = !no_chanctx && chip->support_link_num && + RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &early_fw) && + RTW89_MLD_NON_STA_LINK_NUM == 1; hw->wiphy->iface_combinations = rtw89_iface_combs; diff --git a/drivers/net/wireless/realtek/rtw89/core.h b/drivers/net/wireless/realtek/rtw89/core.h index ff4894c7fa8a..1c8f3b9b7c4c 100644 --- a/drivers/net/wireless/realtek/rtw89/core.h +++ b/drivers/net/wireless/realtek/rtw89/core.h @@ -17,11 +17,13 @@ struct rtw89_dev; struct rtw89_pci_info; struct rtw89_mac_gen_def; struct rtw89_phy_gen_def; +struct rtw89_fw_blacklist; struct rtw89_efuse_block_cfg; struct rtw89_h2c_rf_tssi; struct rtw89_fw_txpwr_track_cfg; struct rtw89_phy_rfk_log_fmt; struct rtw89_debugfs; +struct rtw89_regd_data; extern const struct ieee80211_ops rtw89_ops; @@ -718,6 +720,7 @@ enum rtw89_ofdma_type { RTW89_OFDMA_NUM, }; +/* neither insert new in the middle, nor change any given definition */ enum rtw89_regulation_type { RTW89_WW = 0, RTW89_ETSI = 1, @@ -795,6 +798,7 @@ struct rtw89_rx_phy_ppdu { u8 rssi[RF_PATH_MAX]; u8 mac_id; u8 chan_idx; + u8 phy_idx; u8 ie; u16 rate; u8 rpl_avg; @@ -826,7 +830,7 @@ enum rtw89_mac_idx { enum rtw89_phy_idx { RTW89_PHY_0 = 0, RTW89_PHY_1 = 1, - RTW89_PHY_MAX + RTW89_PHY_NUM, }; #define __RTW89_MLD_MAX_LINK_NUM 2 @@ -1171,6 +1175,7 @@ struct rtw89_tx_desc_info { bool ldpc; bool upd_wlan_hdr; bool mlo; + bool sw_mld; }; struct rtw89_core_tx_request { @@ -1540,16 +1545,16 @@ struct rtw89_btc_u8_sta_chg { }; struct rtw89_btc_wl_scan_info { - u8 band[RTW89_PHY_MAX]; + u8 band[RTW89_PHY_NUM]; u8 phy_map; u8 rsvd; }; struct rtw89_btc_wl_dbcc_info { - u8 op_band[RTW89_PHY_MAX]; /* op band in each phy */ - u8 scan_band[RTW89_PHY_MAX]; /* scan band in each phy */ - u8 real_band[RTW89_PHY_MAX]; - u8 role[RTW89_PHY_MAX]; /* role in each phy */ + u8 op_band[RTW89_PHY_NUM]; /* op band in each phy */ + u8 scan_band[RTW89_PHY_NUM]; /* scan band in each phy */ + u8 real_band[RTW89_PHY_NUM]; + u8 role[RTW89_PHY_NUM]; /* role in each phy */ }; struct rtw89_btc_wl_active_role { @@ -1761,7 +1766,8 @@ struct rtw89_btc_wl_rfk_info { u32 phy_map: 2; u32 band: 2; u32 type: 8; - u32 rsvd: 14; + u32 con_rfk: 1; + u32 rsvd: 13; u32 start_time; u32 proc_time; @@ -1898,7 +1904,7 @@ struct rtw89_btc_wl_info { u8 cn_report; u8 coex_mode; u8 pta_req_mac; - u8 bt_polut_type[RTW89_PHY_MAX]; /* BT polluted WL-Tx type for phy0/1 */ + u8 bt_polut_type[RTW89_PHY_NUM]; /* BT polluted WL-Tx type for phy0/1 */ bool is_5g_hi_channel; bool pta_reg_mac_chg; @@ -2230,7 +2236,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v4 { struct rtw89_btc_fbtc_rpt_ctrl_wl_fw_info wl_fw_info; struct rtw89_btc_fbtc_rpt_ctrl_bt_mailbox bt_mbx_info; __le32 bt_cnt[BTC_BCNT_STA_MAX]; - struct rtw89_mac_ax_gnt gnt_val[RTW89_PHY_MAX]; + struct rtw89_mac_ax_gnt gnt_val[RTW89_PHY_NUM]; } __packed; struct rtw89_btc_fbtc_rpt_ctrl_v5 { @@ -2238,7 +2244,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v5 { u8 rsvd; __le16 rsvd1; - u8 gnt_val[RTW89_PHY_MAX][4]; + u8 gnt_val[RTW89_PHY_NUM][4]; __le16 bt_cnt[BTC_BCNT_STA_MAX]; struct rtw89_btc_fbtc_rpt_ctrl_info_v5 rpt_info; @@ -2250,7 +2256,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v105 { u8 rsvd; __le16 rsvd1; - u8 gnt_val[RTW89_PHY_MAX][4]; + u8 gnt_val[RTW89_PHY_NUM][4]; __le16 bt_cnt[BTC_BCNT_STA_MAX_V105]; struct rtw89_btc_fbtc_rpt_ctrl_info_v5 rpt_info; @@ -2263,7 +2269,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v7 { u8 rsvd1; u8 rsvd2; - u8 gnt_val[RTW89_PHY_MAX][4]; + u8 gnt_val[RTW89_PHY_NUM][4]; __le16 bt_cnt[BTC_BCNT_STA_MAX_V105]; struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info; @@ -2276,7 +2282,7 @@ struct rtw89_btc_fbtc_rpt_ctrl_v8 { u8 rpt_len_max_l; /* BTC_RPT_MAX bit0~7 */ u8 rpt_len_max_h; /* BTC_RPT_MAX bit8~15 */ - u8 gnt_val[RTW89_PHY_MAX][4]; + u8 gnt_val[RTW89_PHY_NUM][4]; __le16 bt_cnt[BTC_BCNT_STA_MAX_V105]; struct rtw89_btc_fbtc_rpt_ctrl_info_v8 rpt_info; @@ -3040,6 +3046,7 @@ struct rtw89_btc_rpt_cmn_info { union rtw89_btc_fbtc_btafh_info { struct rtw89_btc_fbtc_btafh v1; struct rtw89_btc_fbtc_btafh_v2 v2; + struct rtw89_btc_fbtc_btafh_v7 v7; }; struct rtw89_btc_report_ctrl_state { @@ -3174,10 +3181,10 @@ struct rtw89_btc { struct rtw89_btc_btf_fwinfo fwinfo; struct rtw89_btc_dbg dbg; - struct work_struct eapol_notify_work; - struct work_struct arp_notify_work; - struct work_struct dhcp_notify_work; - struct work_struct icmp_notify_work; + struct wiphy_work eapol_notify_work; + struct wiphy_work arp_notify_work; + struct wiphy_work dhcp_notify_work; + struct wiphy_work icmp_notify_work; u32 bt_req_len; @@ -3374,6 +3381,7 @@ struct rtw89_sec_cam_entry { struct rtw89_sta_link { struct rtw89_sta *rtwsta; + struct list_head dlink_schd; unsigned int link_id; u8 mac_id; @@ -3440,14 +3448,13 @@ enum rtw89_roc_state { RTW89_ROC_MGMT, }; -#define RTW89_ROC_BY_LINK_INDEX 0 - struct rtw89_roc { struct ieee80211_channel chan; - struct delayed_work roc_work; + struct wiphy_delayed_work roc_work; enum ieee80211_roc_type type; enum rtw89_roc_state state; int duration; + unsigned int link_id; }; #define RTW89_P2P_MAX_NOA_NUM 2 @@ -3478,8 +3485,17 @@ struct rtw89_p2p_noa_setter { u8 noa_index; }; +struct rtw89_ps_noa_once_handler { + bool in_duration; + u64 tsf_begin; + u64 tsf_end; + struct wiphy_delayed_work set_work; + struct wiphy_delayed_work clr_work; +}; + struct rtw89_vif_link { struct rtw89_vif *rtwvif; + struct list_head dlink_schd; unsigned int link_id; bool chanctx_assigned; /* only valid when running with chanctx_ops */ @@ -3498,9 +3514,11 @@ struct rtw89_vif_link { u8 self_role; u8 wmm; u8 bcn_hit_cond; + u8 bcn_bw_idx; u8 hit_rule; u8 last_noa_nr; u64 sync_bcn_tsf; + bool rand_tsf_done; bool trigger; bool lsig_txop; u8 tgt_ind; @@ -3514,13 +3532,14 @@ struct rtw89_vif_link { bool pre_pwr_diff_en; bool pwr_diff_en; u8 def_tri_idx; - struct work_struct update_beacon_work; + struct wiphy_work update_beacon_work; struct rtw89_addr_cam_entry addr_cam; struct rtw89_bssid_cam_entry bssid_cam; struct ieee80211_tx_queue_params tx_params[IEEE80211_NUM_ACS]; struct rtw89_phy_rate_pattern rate_pattern; struct list_head general_pkt_list; struct rtw89_p2p_noa_setter p2p_noa; + struct rtw89_ps_noa_once_handler noa_once; }; enum rtw89_lv1_rcvy_step { @@ -3672,6 +3691,8 @@ struct rtw89_chip_ops { int (*h2c_ampdu_cmac_tbl)(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link); + int (*h2c_txtime_cmac_tbl)(struct rtw89_dev *rtwdev, + struct rtw89_sta_link *rtwsta_link); int (*h2c_default_dmac_tbl)(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link); @@ -3978,7 +3999,11 @@ struct rtw89_rfe_parms { struct rtw89_txpwr_rule_2ghz rule_2ghz; struct rtw89_txpwr_rule_5ghz rule_5ghz; struct rtw89_txpwr_rule_6ghz rule_6ghz; + struct rtw89_txpwr_rule_2ghz rule_da_2ghz; + struct rtw89_txpwr_rule_5ghz rule_da_5ghz; + struct rtw89_txpwr_rule_6ghz rule_da_6ghz; struct rtw89_tx_shape tx_shape; + bool has_da; }; struct rtw89_rfe_parms_conf { @@ -4073,9 +4098,15 @@ struct rtw89_rfe_data { struct rtw89_txpwr_lmt_2ghz_data lmt_2ghz; struct rtw89_txpwr_lmt_5ghz_data lmt_5ghz; struct rtw89_txpwr_lmt_6ghz_data lmt_6ghz; + struct rtw89_txpwr_lmt_2ghz_data da_lmt_2ghz; + struct rtw89_txpwr_lmt_5ghz_data da_lmt_5ghz; + struct rtw89_txpwr_lmt_6ghz_data da_lmt_6ghz; struct rtw89_txpwr_lmt_ru_2ghz_data lmt_ru_2ghz; struct rtw89_txpwr_lmt_ru_5ghz_data lmt_ru_5ghz; struct rtw89_txpwr_lmt_ru_6ghz_data lmt_ru_6ghz; + struct rtw89_txpwr_lmt_ru_2ghz_data da_lmt_ru_2ghz; + struct rtw89_txpwr_lmt_ru_5ghz_data da_lmt_ru_5ghz; + struct rtw89_txpwr_lmt_ru_6ghz_data da_lmt_ru_6ghz; struct rtw89_tx_shape_lmt_data tx_shape_lmt; struct rtw89_tx_shape_lmt_ru_data tx_shape_lmt_ru; struct rtw89_rfe_parms rfe_parms; @@ -4189,10 +4220,12 @@ struct rtw89_edcca_regs { u32 edcca_p_mask; u32 ppdu_level; u32 ppdu_mask; - u32 rpt_a; - u32 rpt_b; - u32 rpt_sel; - u32 rpt_sel_mask; + struct rtw89_edcca_p_regs { + u32 rpt_a; + u32 rpt_b; + u32 rpt_sel; + u32 rpt_sel_mask; + } p[RTW89_PHY_NUM]; u32 rpt_sel_be; u32 rpt_sel_be_mask; u32 tx_collision_t2r_st; @@ -4231,6 +4264,7 @@ enum rtw89_chanctx_state { enum rtw89_chanctx_callbacks { RTW89_CHANCTX_CALLBACK_PLACEHOLDER, RTW89_CHANCTX_CALLBACK_RFK, + RTW89_CHANCTX_CALLBACK_TAS, NUM_OF_RTW89_CHANCTX_CALLBACKS, }; @@ -4251,6 +4285,7 @@ struct rtw89_chip_info { bool try_ce_fw; u8 bbmcu_nr; u32 needed_fw_elms; + const struct rtw89_fw_blacklist *fw_blacklist; u32 fifo_size; bool small_fifo_size; u32 dle_scc_rsvd_size; @@ -4271,10 +4306,15 @@ struct rtw89_chip_info { bool support_unii4; bool support_rnr; bool support_ant_gain; + bool support_tas; + bool support_sar_by_ant; bool ul_tb_waveform_ctrl; bool ul_tb_pwr_diff; + bool rx_freq_frome_ie; bool hw_sec_hdr; bool hw_mgmt_tx_encrypt; + bool hw_tkip_crypto; + bool hw_mlo_bmc_crypto; u8 rf_path_num; u8 tx_nss; u8 rx_nss; @@ -4478,6 +4518,8 @@ enum rtw89_fw_feature { RTW89_FW_FEATURE_CH_INFO_BE_V0, RTW89_FW_FEATURE_LPS_CH_INFO, RTW89_FW_FEATURE_NO_PHYCAP_P1, + RTW89_FW_FEATURE_NO_POWER_DIFFERENCE, + RTW89_FW_FEATURE_BEACON_LOSS_COUNT_V1, }; struct rtw89_fw_suit { @@ -4536,6 +4578,7 @@ struct rtw89_fw_elm_info { struct rtw89_phy_table *rf_nctl; struct rtw89_fw_txpwr_track_cfg *txpwr_trk; struct rtw89_phy_rfk_log_fmt *rfk_log_fmt; + const struct rtw89_regd_data *regd; }; enum rtw89_fw_mss_dev_type { @@ -4589,6 +4632,7 @@ struct rtw89_cam_info { enum rtw89_sar_sources { RTW89_SAR_SOURCE_NONE, RTW89_SAR_SOURCE_COMMON, + RTW89_SAR_SOURCE_ACPI, RTW89_SAR_SOURCE_NR, }; @@ -4613,8 +4657,62 @@ struct rtw89_sar_cfg_common { s32 cfg[RTW89_SAR_SUBBAND_NR]; }; +enum rtw89_acpi_sar_subband { + RTW89_ACPI_SAR_2GHZ_SUBBAND, + RTW89_ACPI_SAR_5GHZ_SUBBAND_1, /* U-NII-1 */ + RTW89_ACPI_SAR_5GHZ_SUBBAND_2, /* U-NII-2 */ + RTW89_ACPI_SAR_5GHZ_SUBBAND_2E, /* U-NII-2-Extended */ + RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4, /* U-NII-3 and U-NII-4 */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_5_L, /* U-NII-5 lower part */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_5_H, /* U-NII-5 higher part */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_6, /* U-NII-6 */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_7_L, /* U-NII-7 lower part */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_7_H, /* U-NII-7 higher part */ + RTW89_ACPI_SAR_6GHZ_SUBBAND_8, /* U-NII-8 */ + + NUM_OF_RTW89_ACPI_SAR_SUBBAND, + RTW89_ACPI_SAR_SUBBAND_NR_LEGACY = RTW89_ACPI_SAR_5GHZ_SUBBAND_3_4 + 1, + RTW89_ACPI_SAR_SUBBAND_NR_HAS_6GHZ = RTW89_ACPI_SAR_6GHZ_SUBBAND_8 + 1, +}; + +#define TXPWR_FACTOR_OF_RTW89_ACPI_SAR 3 /* unit: 0.125 dBm */ +#define MAX_VAL_OF_RTW89_ACPI_SAR S16_MAX +#define MIN_VAL_OF_RTW89_ACPI_SAR S16_MIN +#define MAX_NUM_OF_RTW89_ACPI_SAR_TBL 6 +#define NUM_OF_RTW89_ACPI_SAR_RF_PATH (RF_PATH_B + 1) + +struct rtw89_sar_entry_from_acpi { + s16 v[NUM_OF_RTW89_ACPI_SAR_SUBBAND][NUM_OF_RTW89_ACPI_SAR_RF_PATH]; +}; + +struct rtw89_sar_table_from_acpi { + /* If this table is active, must fill all fields according to either + * configuration in BIOS or some default values for SAR to work well. + */ + struct rtw89_sar_entry_from_acpi entries[RTW89_REGD_NUM]; +}; + +struct rtw89_sar_indicator_from_acpi { + bool enable_sync; + unsigned int fields; + u8 (*rfpath_to_antidx)(enum rtw89_rf_path rfpath); + + /* Select among @tables of container, rtw89_sar_cfg_acpi, by path. + * Not design with pointers since addresses will be invalid after + * sync content with local container instance. + */ + u8 tblsel[NUM_OF_RTW89_ACPI_SAR_RF_PATH]; +}; + +struct rtw89_sar_cfg_acpi { + u8 downgrade_2tx; + unsigned int valid_num; + struct rtw89_sar_table_from_acpi tables[MAX_NUM_OF_RTW89_ACPI_SAR_TBL]; + struct rtw89_sar_indicator_from_acpi indicator; +}; + struct rtw89_sar_info { - /* used to decide how to acces SAR cfg union */ + /* used to decide how to access SAR cfg union */ enum rtw89_sar_sources src; /* reserved for different knids of SAR cfg struct. @@ -4622,6 +4720,7 @@ struct rtw89_sar_info { */ union { struct rtw89_sar_cfg_common cfg_common; + struct rtw89_sar_cfg_acpi cfg_acpi; }; }; @@ -4651,33 +4750,49 @@ enum rtw89_ant_gain_domain_type { struct rtw89_ant_gain_info { s8 offset[RTW89_ANT_GAIN_CHAIN_NUM][RTW89_ANT_GAIN_SUBBAND_NR]; u32 regd_enabled; + bool block_country; }; struct rtw89_6ghz_span { enum rtw89_sar_subband sar_subband_low; enum rtw89_sar_subband sar_subband_high; + enum rtw89_acpi_sar_subband acpi_sar_subband_low; + enum rtw89_acpi_sar_subband acpi_sar_subband_high; enum rtw89_ant_gain_subband ant_gain_subband_low; enum rtw89_ant_gain_subband ant_gain_subband_high; }; #define RTW89_SAR_SPAN_VALID(span) ((span)->sar_subband_high) +#define RTW89_ACPI_SAR_SPAN_VALID(span) ((span)->acpi_sar_subband_high) #define RTW89_ANT_GAIN_SPAN_VALID(span) ((span)->ant_gain_subband_high) enum rtw89_tas_state { RTW89_TAS_STATE_DPR_OFF, RTW89_TAS_STATE_DPR_ON, - RTW89_TAS_STATE_DPR_FORBID, + RTW89_TAS_STATE_STATIC_SAR, }; -#define RTW89_TAS_MAX_WINDOW 50 +#define RTW89_TAS_TX_RATIO_WINDOW 6 +#define RTW89_TAS_TXPWR_WINDOW 180 struct rtw89_tas_info { - s16 txpwr_history[RTW89_TAS_MAX_WINDOW]; - s32 total_txpwr; - u8 cur_idx; - s8 dpr_gap; - s8 delta; + u16 tx_ratio_history[RTW89_TAS_TX_RATIO_WINDOW]; + u64 txpwr_history[RTW89_TAS_TXPWR_WINDOW]; + u8 enabled_countries; + u8 txpwr_head_idx; + u8 txpwr_tail_idx; + u8 tx_ratio_idx; + u16 total_tx_ratio; + u64 total_txpwr; + u64 instant_txpwr; + u32 window_size; + s8 dpr_on_threshold; + s8 dpr_off_threshold; + enum rtw89_tas_state backup_state; enum rtw89_tas_state state; + bool keep_history; + bool block_regd; bool enable; + bool pause; }; struct rtw89_chanctx_cfg { @@ -4735,6 +4850,8 @@ struct rtw89_edcca_bak { enum rtw89_dm_type { RTW89_DM_DYNAMIC_EDCCA, RTW89_DM_THERMAL_PROTECT, + RTW89_DM_TAS, + RTW89_DM_MLO, }; #define RTW89_THERMAL_PROT_LV_MAX 5 @@ -4756,18 +4873,18 @@ struct rtw89_hal { bool no_mcs_12_13; atomic_t roc_chanctx_idx; + u8 roc_link_index; DECLARE_BITMAP(changes, NUM_OF_RTW89_CHANCTX_CHANGES); DECLARE_BITMAP(entity_map, NUM_OF_RTW89_CHANCTX); struct rtw89_chanctx chanctx[NUM_OF_RTW89_CHANCTX]; struct cfg80211_chan_def roc_chandef; - bool entity_active[RTW89_PHY_MAX]; + bool entity_active[RTW89_PHY_NUM]; bool entity_pause; enum rtw89_entity_mode entity_mode; struct rtw89_entity_mgnt entity_mgnt; - struct rtw89_edcca_bak edcca_bak; u32 disabled_dm_bitmap; /* bitmap of enum rtw89_dm_type */ u8 thermal_prot_th; @@ -4973,7 +5090,7 @@ struct rtw89_dpk_bkup_para { struct rtw89_dpk_info { bool is_dpk_enable; bool is_dpk_reload_en; - u8 dpk_gs[RTW89_PHY_MAX]; + u8 dpk_gs[RTW89_PHY_NUM]; u16 dc_i[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; u16 dc_q[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; u8 corr_val[RTW89_DPK_RF_PATH][RTW89_DPK_BKUP_NUM]; @@ -5135,7 +5252,7 @@ struct rtw89_tssi_info { u32 alignment_backup_by_ch[RF_PATH_MAX][TSSI_MAX_CH_NUM][TSSI_ALIMK_VALUE_NUM]; u32 alignment_value[RF_PATH_MAX][TSSI_ALIMK_MAX][TSSI_ALIMK_VALUE_NUM]; bool alignment_done[RF_PATH_MAX][TSSI_ALIMK_MAX]; - u32 tssi_alimk_time; + u64 tssi_alimk_time; }; struct rtw89_power_trim_info { @@ -5146,9 +5263,27 @@ struct rtw89_power_trim_info { u8 pad_bias_trim[RF_PATH_MAX]; }; +enum rtw89_regd_func { + RTW89_REGD_FUNC_TAS = 0, /* TAS (Time Average SAR) */ + RTW89_REGD_FUNC_DAG = 1, /* DAG (Dynamic Antenna Gain) */ + + NUM_OF_RTW89_REGD_FUNC, +}; + struct rtw89_regd { char alpha2[3]; u8 txpwr_regd[RTW89_BAND_NUM]; + DECLARE_BITMAP(func_bitmap, NUM_OF_RTW89_REGD_FUNC); +}; + +struct rtw89_regd_data { + unsigned int nr; + struct rtw89_regd map[] __counted_by(nr); +}; + +struct rtw89_regd_ctrl { + unsigned int nr; + const struct rtw89_regd *map; }; #define RTW89_REGD_MAX_COUNTRY_NUM U8_MAX @@ -5156,9 +5291,12 @@ struct rtw89_regd { #define RTW89_5GHZ_UNII4_START_INDEX 25 struct rtw89_regulatory_info { + struct rtw89_regd_ctrl ctrl; const struct rtw89_regd *regd; enum rtw89_reg_6ghz_power reg_6ghz_power; struct rtw89_reg_6ghz_tpe reg_6ghz_tpe; + bool txpwr_uk_follow_etsi; + DECLARE_BITMAP(block_unii4, RTW89_REGD_MAX_COUNTRY_NUM); DECLARE_BITMAP(block_6ghz, RTW89_REGD_MAX_COUNTRY_NUM); DECLARE_BITMAP(block_6ghz_sp, RTW89_REGD_MAX_COUNTRY_NUM); @@ -5299,8 +5437,8 @@ struct rtw89_lps_parm { }; struct rtw89_ppdu_sts_info { - struct sk_buff_head rx_queue[RTW89_PHY_MAX]; - u8 curr_rx_ppdu_cnt[RTW89_PHY_MAX]; + struct sk_buff_head rx_queue[RTW89_PHY_NUM]; + u8 curr_rx_ppdu_cnt[RTW89_PHY_NUM]; }; struct rtw89_early_h2c { @@ -5312,9 +5450,10 @@ struct rtw89_early_h2c { struct rtw89_hw_scan_info { struct rtw89_vif_link *scanning_vif; struct list_head pkt_list[NUM_NL80211_BANDS]; + struct list_head chan_list; struct rtw89_chan op_chan; + bool connected; bool abort; - u32 last_chan_idx; }; enum rtw89_phy_bb_gain_band { @@ -5419,8 +5558,8 @@ struct rtw89_phy_efuse_gain { bool offset_valid; bool comp_valid; s8 offset[RF_PATH_MAX][RTW89_GAIN_OFFSET_NR]; /* S(8, 0) */ - s8 offset_base[RTW89_PHY_MAX]; /* S(8, 4) */ - s8 rssi_base[RTW89_PHY_MAX]; /* S(8, 4) */ + s8 offset_base[RTW89_PHY_NUM]; /* S(8, 4) */ + s8 rssi_base[RTW89_PHY_NUM]; /* S(8, 4) */ s8 comp[RF_PATH_MAX][RTW89_SUBBAND_NR]; /* S(8, 0) */ }; @@ -5526,6 +5665,8 @@ struct rtw89_mcc_role { struct rtw89_mcc_policy policy; struct rtw89_mcc_limit limit; + const struct rtw89_mcc_courtesy_cfg *crtz; + /* only valid when running with FW MRC mechanism */ u8 slot_idx; @@ -5543,13 +5684,16 @@ struct rtw89_mcc_bt_role { u16 duration; /* TU */ }; -struct rtw89_mcc_courtesy { - bool enable; +struct rtw89_mcc_courtesy_cfg { u8 slot_num; - u8 macid_src; u8 macid_tgt; }; +struct rtw89_mcc_courtesy { + struct rtw89_mcc_courtesy_cfg ref; + struct rtw89_mcc_courtesy_cfg aux; +}; + enum rtw89_mcc_plan { RTW89_MCC_PLAN_TAIL_BT, RTW89_MCC_PLAN_MID_BT, @@ -5583,6 +5727,7 @@ struct rtw89_mcc_config { struct rtw89_mcc_pattern pattern; struct rtw89_mcc_sync sync; u64 start_tsf; + u64 start_tsf_in_aux_domain; u16 mcc_interval; /* TU */ u16 beacon_offset; /* TU */ }; @@ -5603,6 +5748,16 @@ struct rtw89_mcc_info { struct rtw89_mcc_config config; }; +enum rtw89_mlo_mode { + RTW89_MLO_MODE_MLSR = 0, + + NUM_OF_RTW89_MLO_MODE, +}; + +struct rtw89_mlo_info { + struct rtw89_wait_info wait; +}; + struct rtw89_dev { struct ieee80211_hw *hw; struct device *dev; @@ -5618,6 +5773,7 @@ struct rtw89_dev { const struct rtw89_rfe_parms *rfe_parms; struct rtw89_hal hal; struct rtw89_mcc_info mcc; + struct rtw89_mlo_info mlo; struct rtw89_mac_info mac; struct rtw89_fw_info fw; struct rtw89_hci_info hci; @@ -5629,8 +5785,6 @@ struct rtw89_dev { struct rtw89_sta_link __rcu *assoc_link_on_macid[RTW89_MAX_MAC_ID_NUM]; refcount_t refcount_ap_info; - /* ensures exclusive access from mac80211 callbacks */ - struct mutex mutex; struct list_head rtwvifs_list; /* used to protect rf read write */ struct mutex rf_mutex; @@ -5650,10 +5804,10 @@ struct rtw89_dev { struct rtw89_cam_info cam_info; struct sk_buff_head c2h_queue; - struct work_struct c2h_work; - struct work_struct ips_work; + struct wiphy_work c2h_work; + struct wiphy_work ips_work; + struct wiphy_work cancel_6ghz_probe_work; struct work_struct load_firmware_work; - struct work_struct cancel_6ghz_probe_work; struct list_head early_h2c_list; @@ -5682,9 +5836,6 @@ struct rtw89_dev { struct rtw89_power_trim_info pwr_trim; struct rtw89_cfo_tracking_info cfo_tracking; - struct rtw89_env_monitor_info env_monitor; - struct rtw89_dig_info dig; - struct rtw89_phy_ch_info ch_info; union { struct rtw89_phy_bb_gain_info ax; struct rtw89_phy_bb_gain_info_be be; @@ -5693,15 +5844,22 @@ struct rtw89_dev { struct rtw89_phy_ul_tb_info ul_tb_info; struct rtw89_antdiv_info antdiv; - struct delayed_work track_work; - struct delayed_work chanctx_work; - struct delayed_work coex_act1_work; - struct delayed_work coex_bt_devinfo_work; - struct delayed_work coex_rfk_chk_work; - struct delayed_work cfo_track_work; + struct rtw89_bb_ctx { + enum rtw89_phy_idx phy_idx; + struct rtw89_env_monitor_info env_monitor; + struct rtw89_dig_info dig; + struct rtw89_phy_ch_info ch_info; + struct rtw89_edcca_bak edcca_bak; + } bbs[RTW89_PHY_NUM]; + + struct wiphy_delayed_work track_work; + struct wiphy_delayed_work chanctx_work; + struct wiphy_delayed_work coex_act1_work; + struct wiphy_delayed_work coex_bt_devinfo_work; + struct wiphy_delayed_work coex_rfk_chk_work; + struct wiphy_delayed_work cfo_track_work; struct delayed_work forbid_ba_work; - struct delayed_work roc_work; - struct delayed_work antdiv_work; + struct wiphy_delayed_work antdiv_work; struct rtw89_ppdu_sts_info ppdu_sts; u8 total_sta_assoc; bool scanning; @@ -5752,6 +5910,9 @@ struct rtw89_vif { struct rtw89_roc roc; bool offchan; + enum rtw89_mlo_mode mlo_mode; + + struct list_head dlink_pool; u8 links_inst_valid_num; DECLARE_BITMAP(links_inst_map, __RTW89_MLD_MAX_LINK_NUM); struct rtw89_vif_link *links[IEEE80211_MLD_MAX_NUM_LINKS]; @@ -5791,6 +5952,7 @@ struct rtw89_sta { DECLARE_BITMAP(pairwise_sec_cam_map, RTW89_MAX_SEC_CAM_NUM); + struct list_head dlink_pool; u8 links_inst_valid_num; DECLARE_BITMAP(links_inst_map, __RTW89_MLD_MAX_LINK_NUM); struct rtw89_sta_link *links[IEEE80211_MLD_MAX_NUM_LINKS]; @@ -5886,6 +6048,12 @@ rtw89_assoc_link_rcu_dereference(struct rtw89_dev *rtwdev, u8 macid) return rcu_dereference(rtwdev->assoc_link_on_macid[macid]); } +#define rtw89_get_designated_link(links_holder) \ +({ \ + typeof(links_holder) p = links_holder; \ + list_first_entry_or_null(&p->dlink_pool, typeof(*p->links_inst), dlink_schd); \ +}) + static inline int rtw89_hci_tx_write(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) { @@ -6775,9 +6943,14 @@ static inline void rtw89_load_txpwr_table(struct rtw89_dev *rtwdev, static inline u8 rtw89_regd_get(struct rtw89_dev *rtwdev, u8 band) { - const struct rtw89_regd *regd = rtwdev->regulatory.regd; + const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd *regd = regulatory->regd; + u8 txpwr_regd = regd->txpwr_regd[band]; + + if (regulatory->txpwr_uk_follow_etsi && txpwr_regd == RTW89_UK) + return RTW89_ETSI; - return regd->txpwr_regd[band]; + return txpwr_regd; } static inline void rtw89_ctrl_btg_bt_rx(struct rtw89_dev *rtwdev, bool en, @@ -6978,6 +7151,48 @@ static inline bool rtw89_is_mlo_1_1(struct rtw89_dev *rtwdev) } } +static inline u8 rtw89_get_active_phy_bitmap(struct rtw89_dev *rtwdev) +{ + if (!rtwdev->dbcc_en) + return BIT(RTW89_PHY_0); + + switch (rtwdev->mlo_dbcc_mode) { + case MLO_0_PLUS_2_1RF: + case MLO_0_PLUS_2_2RF: + return BIT(RTW89_PHY_1); + case MLO_1_PLUS_1_1RF: + case MLO_1_PLUS_1_2RF: + case MLO_2_PLUS_2_2RF: + case DBCC_LEGACY: + return BIT(RTW89_PHY_0) | BIT(RTW89_PHY_1); + case MLO_2_PLUS_0_1RF: + case MLO_2_PLUS_0_2RF: + default: + return BIT(RTW89_PHY_0); + } +} + +#define rtw89_for_each_active_bb(rtwdev, bb) \ + for (u8 __active_bb_bitmap = rtw89_get_active_phy_bitmap(rtwdev), \ + __phy_idx = 0; __phy_idx < RTW89_PHY_NUM; __phy_idx++) \ + if (__active_bb_bitmap & BIT(__phy_idx) && \ + (bb = &rtwdev->bbs[__phy_idx])) + +#define rtw89_for_each_capab_bb(rtwdev, bb) \ + for (u8 __phy_idx_max = rtwdev->dbcc_en ? RTW89_PHY_1 : RTW89_PHY_0, \ + __phy_idx = 0; __phy_idx <= __phy_idx_max; __phy_idx++) \ + if ((bb = &rtwdev->bbs[__phy_idx])) + +static inline +struct rtw89_bb_ctx *rtw89_get_bb_ctx(struct rtw89_dev *rtwdev, + enum rtw89_phy_idx phy_idx) +{ + if (phy_idx >= RTW89_PHY_NUM) + return &rtwdev->bbs[RTW89_PHY_0]; + + return &rtwdev->bbs[phy_idx]; +} + static inline bool rtw89_is_rtl885xb(struct rtw89_dev *rtwdev) { enum rtw89_core_chip_id chip_id = rtwdev->chip->chip_id; @@ -7092,9 +7307,8 @@ void rtw89_chip_cfg_txpwr_ul_tb_offset(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); bool rtw89_ra_report_to_bitrate(struct rtw89_dev *rtwdev, u8 rpt_rate, u16 *bitrate); int rtw89_regd_setup(struct rtw89_dev *rtwdev); -int rtw89_regd_init(struct rtw89_dev *rtwdev, - void (*reg_notifier)(struct wiphy *wiphy, struct regulatory_request *request)); -void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); +int rtw89_regd_init_hint(struct rtw89_dev *rtwdev); +const char *rtw89_regd_get_string(enum rtw89_regulation_type regd); void rtw89_traffic_stats_init(struct rtw89_dev *rtwdev, struct rtw89_traffic_stats *stats); int rtw89_wait_for_cond(struct rtw89_wait_info *wait, unsigned int cond); @@ -7102,8 +7316,8 @@ void rtw89_complete_cond(struct rtw89_wait_info *wait, unsigned int cond, const struct rtw89_completion_data *data); int rtw89_core_start(struct rtw89_dev *rtwdev); void rtw89_core_stop(struct rtw89_dev *rtwdev); -void rtw89_core_update_beacon_work(struct work_struct *work); -void rtw89_roc_work(struct work_struct *work); +void rtw89_core_update_beacon_work(struct wiphy *wiphy, struct wiphy_work *work); +void rtw89_roc_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_roc_start(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_roc_end(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif); void rtw89_core_scan_start(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, @@ -7116,5 +7330,7 @@ void rtw89_core_update_p2p_ps(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct ieee80211_bss_conf *bss_conf); void rtw89_core_ntfy_btc_event(struct rtw89_dev *rtwdev, enum rtw89_btc_hmsg event); +int rtw89_core_mlsr_switch(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, + unsigned int link_id); #endif diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 09fa977a6e6d..d6016fa107fb 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -13,6 +13,7 @@ #include "ps.h" #include "reg.h" #include "sar.h" +#include "util.h" #ifdef CONFIG_RTW89_DEBUGMSG unsigned int rtw89_debug_mask; @@ -22,11 +23,21 @@ MODULE_PARM_DESC(debug_mask, "Debugging mask"); #endif #ifdef CONFIG_RTW89_DEBUGFS +struct rtw89_debugfs_priv_opt { + bool rlock:1; + bool wlock:1; + size_t rsize; +}; + struct rtw89_debugfs_priv { struct rtw89_dev *rtwdev; - int (*cb_read)(struct seq_file *m, void *v); - ssize_t (*cb_write)(struct file *filp, const char __user *buffer, - size_t count, loff_t *loff); + ssize_t (*cb_read)(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz); + ssize_t (*cb_write)(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count); + struct rtw89_debugfs_priv_opt opt; union { u32 cb_data; struct { @@ -51,6 +62,8 @@ struct rtw89_debugfs_priv { u8 sel; } mac_mem; }; + ssize_t rused; + char *rbuf; }; struct rtw89_debugfs { @@ -72,8 +85,31 @@ struct rtw89_debugfs { struct rtw89_debugfs_priv phy_info; struct rtw89_debugfs_priv stations; struct rtw89_debugfs_priv disable_dm; + struct rtw89_debugfs_priv mlo_mode; +}; + +struct rtw89_debugfs_iter_data { + char *buf; + size_t bufsz; + int written_sz; }; +static void rtw89_debugfs_iter_data_setup(struct rtw89_debugfs_iter_data *iter_data, + char *buf, size_t bufsz) +{ + iter_data->buf = buf; + iter_data->bufsz = bufsz; + iter_data->written_sz = 0; +} + +static void rtw89_debugfs_iter_data_next(struct rtw89_debugfs_iter_data *iter_data, + char *buf, size_t bufsz, int written_sz) +{ + iter_data->buf = buf; + iter_data->bufsz = bufsz; + iter_data->written_sz += written_sz; +} + static const u16 rtw89_rate_info_bw_to_mhz_map[] = { [RATE_INFO_BW_20] = 20, [RATE_INFO_BW_40] = 40, @@ -90,84 +126,122 @@ static u16 rtw89_rate_info_bw_to_mhz(enum rate_info_bw bw) return 0; } -static int rtw89_debugfs_single_show(struct seq_file *m, void *v) +static ssize_t rtw89_debugfs_file_read_helper(struct wiphy *wiphy, struct file *file, + char *buf, size_t bufsz, void *data) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; + struct rtw89_debugfs_priv *debugfs_priv = data; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + ssize_t n; - return debugfs_priv->cb_read(m, v); + n = debugfs_priv->cb_read(rtwdev, debugfs_priv, buf, bufsz); + rtw89_might_trailing_ellipsis(buf, bufsz, n); + + return n; } -static ssize_t rtw89_debugfs_single_write(struct file *filp, - const char __user *buffer, - size_t count, loff_t *loff) +static ssize_t rtw89_debugfs_file_read(struct file *file, char __user *userbuf, + size_t count, loff_t *ppos) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; + struct rtw89_debugfs_priv *debugfs_priv = file->private_data; + struct rtw89_debugfs_priv_opt *opt = &debugfs_priv->opt; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + size_t bufsz = opt->rsize ? opt->rsize : PAGE_SIZE; + char *buf; + ssize_t n; - return debugfs_priv->cb_write(filp, buffer, count, loff); -} + if (!debugfs_priv->rbuf) + debugfs_priv->rbuf = devm_kzalloc(rtwdev->dev, bufsz, GFP_KERNEL); -static ssize_t rtw89_debugfs_seq_file_write(struct file *filp, - const char __user *buffer, - size_t count, loff_t *loff) -{ - struct seq_file *seqpriv = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = seqpriv->private; + buf = debugfs_priv->rbuf; + if (!buf) + return -ENOMEM; + + if (*ppos) { + n = debugfs_priv->rused; + goto out; + } - return debugfs_priv->cb_write(filp, buffer, count, loff); + if (opt->rlock) { + n = wiphy_locked_debugfs_read(rtwdev->hw->wiphy, file, buf, bufsz, + userbuf, count, ppos, + rtw89_debugfs_file_read_helper, + debugfs_priv); + debugfs_priv->rused = n; + + return n; + } + + n = rtw89_debugfs_file_read_helper(rtwdev->hw->wiphy, file, buf, bufsz, + debugfs_priv); + debugfs_priv->rused = n; + +out: + return simple_read_from_buffer(userbuf, count, ppos, buf, n); } -static int rtw89_debugfs_single_open(struct inode *inode, struct file *filp) +static ssize_t rtw89_debugfs_file_write_helper(struct wiphy *wiphy, struct file *file, + char *buf, size_t count, void *data) { - return single_open(filp, rtw89_debugfs_single_show, inode->i_private); + struct rtw89_debugfs_priv *debugfs_priv = data; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + + return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count); } -static int rtw89_debugfs_close(struct inode *inode, struct file *filp) +static ssize_t rtw89_debugfs_file_write(struct file *file, + const char __user *userbuf, + size_t count, loff_t *loff) { - return 0; + struct rtw89_debugfs_priv *debugfs_priv = file->private_data; + struct rtw89_debugfs_priv_opt *opt = &debugfs_priv->opt; + struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *buf __free(kfree) = kmalloc(count + 1, GFP_KERNEL); + ssize_t n; + + if (!buf) + return -ENOMEM; + + if (opt->wlock) { + n = wiphy_locked_debugfs_write(rtwdev->hw->wiphy, + file, buf, count + 1, + userbuf, count, + rtw89_debugfs_file_write_helper, + debugfs_priv); + return n; + } + + if (copy_from_user(buf, userbuf, count)) + return -EFAULT; + + buf[count] = '\0'; + + return debugfs_priv->cb_write(rtwdev, debugfs_priv, buf, count); } -static const struct file_operations file_ops_single_r = { - .owner = THIS_MODULE, - .open = rtw89_debugfs_single_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, +static const struct debugfs_short_fops file_ops_single_r = { + .read = rtw89_debugfs_file_read, + .llseek = generic_file_llseek, }; -static const struct file_operations file_ops_common_rw = { - .owner = THIS_MODULE, - .open = rtw89_debugfs_single_open, - .release = single_release, - .read = seq_read, - .llseek = seq_lseek, - .write = rtw89_debugfs_seq_file_write, +static const struct debugfs_short_fops file_ops_common_rw = { + .read = rtw89_debugfs_file_read, + .write = rtw89_debugfs_file_write, + .llseek = generic_file_llseek, }; -static const struct file_operations file_ops_single_w = { - .owner = THIS_MODULE, - .write = rtw89_debugfs_single_write, - .open = simple_open, - .release = rtw89_debugfs_close, +static const struct debugfs_short_fops file_ops_single_w = { + .write = rtw89_debugfs_file_write, + .llseek = generic_file_llseek, }; static ssize_t -rtw89_debug_priv_read_reg_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_read_reg_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 addr, len; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%x %x", &addr, &len); if (num != 2) { rtw89_info(rtwdev, "invalid format: <addr> <len>\n"); @@ -182,11 +256,13 @@ rtw89_debug_priv_read_reg_select(struct file *filp, return count; } -static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_read_reg_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - u32 addr, end, data, k; + char *p = buf, *end = buf + bufsz; + u32 addr, addr_end, data, k; u32 len; len = debugfs_priv->read_reg.len; @@ -210,41 +286,34 @@ static int rtw89_debug_priv_read_reg_get(struct seq_file *m, void *v) return -EINVAL; } - seq_printf(m, "get %d bytes at 0x%08x=0x%08x\n", len, addr, data); + p += scnprintf(p, end - p, "get %d bytes at 0x%08x=0x%08x\n", len, + addr, data); - return 0; + return p - buf; ndata: - end = addr + len; + addr_end = addr + len; - for (; addr < end; addr += 16) { - seq_printf(m, "%08xh : ", 0x18600000 + addr); + for (; addr < addr_end; addr += 16) { + p += scnprintf(p, end - p, "%08xh : ", 0x18600000 + addr); for (k = 0; k < 16; k += 4) { data = rtw89_read32(rtwdev, addr + k); - seq_printf(m, "%08x ", data); + p += scnprintf(p, end - p, "%08x ", data); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } - return 0; + return p - buf; } -static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static +ssize_t rtw89_debug_priv_write_reg_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 addr, val, len; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%x %x %x", &addr, &val, &len); if (num != 3) { rtw89_info(rtwdev, "invalid format: <addr> <val> <len>\n"); @@ -273,24 +342,14 @@ static ssize_t rtw89_debug_priv_write_reg_set(struct file *filp, } static ssize_t -rtw89_debug_priv_read_rf_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_read_rf_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 addr, mask; u8 path; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%hhd %x %x", &path, &addr, &mask); if (num != 3) { rtw89_info(rtwdev, "invalid format: <path> <addr> <mask>\n"); @@ -310,10 +369,12 @@ rtw89_debug_priv_read_rf_select(struct file *filp, return count; } -static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_read_rf_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *p = buf, *end = buf + bufsz; u32 addr, data, mask; u8 path; @@ -323,28 +384,21 @@ static int rtw89_debug_priv_read_rf_get(struct seq_file *m, void *v) data = rtw89_read_rf(rtwdev, path, addr, mask); - seq_printf(m, "path %d, rf register 0x%08x=0x%08x\n", path, addr, data); + p += scnprintf(p, end - p, "path %d, rf register 0x%08x=0x%08x\n", + path, addr, data); - return 0; + return p - buf; } -static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static +ssize_t rtw89_debug_priv_write_rf_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 addr, val, mask; u8 path; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%hhd %x %x %x", &path, &addr, &mask, &val); if (num != 4) { rtw89_info(rtwdev, "invalid format: <path> <addr> <mask> <val>\n"); @@ -363,29 +417,31 @@ static ssize_t rtw89_debug_priv_write_rf_set(struct file *filp, return count; } -static int rtw89_debug_priv_rf_reg_dump_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_rf_reg_dump_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; u32 addr, offset, data; u8 path; for (path = 0; path < chip->rf_path_num; path++) { - seq_printf(m, "RF path %d:\n\n", path); + p += scnprintf(p, end - p, "RF path %d:\n\n", path); for (addr = 0; addr < 0x100; addr += 4) { - seq_printf(m, "0x%08x: ", addr); + p += scnprintf(p, end - p, "0x%08x: ", addr); for (offset = 0; offset < 4; offset++) { data = rtw89_read_rf(rtwdev, path, addr + offset, RFREG_MASK); - seq_printf(m, "0x%05x ", data); + p += scnprintf(p, end - p, "0x%05x ", data); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } - return 0; + return p - buf; } struct txpwr_ent { @@ -704,56 +760,71 @@ static const struct txpwr_map __txpwr_map_lmt_ru_be = { }; static unsigned int -__print_txpwr_ent(struct seq_file *m, const struct txpwr_ent *ent, - const s8 *buf, const unsigned int cur) +__print_txpwr_ent(char *buf, size_t bufsz, const struct txpwr_ent *ent, + const s8 *bufp, const unsigned int cur, unsigned int *ate) { + char *p = buf, *end = buf + bufsz; unsigned int cnt, i; + unsigned int eaten; char *fmt; if (ent->nested) { - for (cnt = 0, i = 0; i < ent->len; i++) - cnt += __print_txpwr_ent(m, ent->ptr + i, buf, - cur + cnt); - return cnt; + for (cnt = 0, i = 0; i < ent->len; i++, cnt += eaten) + p += __print_txpwr_ent(p, end - p, ent->ptr + i, bufp, + cur + cnt, &eaten); + *ate = cnt; + goto out; } switch (ent->len) { case 0: - seq_printf(m, "\t<< %s >>\n", ent->txt); - return 0; + p += scnprintf(p, end - p, "\t<< %s >>\n", ent->txt); + *ate = 0; + goto out; case 2: fmt = "%s\t| %3d, %3d,\t\tdBm\n"; - seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1]); - return 2; + p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], + bufp[cur + 1]); + *ate = 2; + goto out; case 4: fmt = "%s\t| %3d, %3d, %3d, %3d,\tdBm\n"; - seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1], - buf[cur + 2], buf[cur + 3]); - return 4; + p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], + bufp[cur + 1], + bufp[cur + 2], bufp[cur + 3]); + *ate = 4; + goto out; case 8: fmt = "%s\t| %3d, %3d, %3d, %3d, %3d, %3d, %3d, %3d,\tdBm\n"; - seq_printf(m, fmt, ent->txt, buf[cur], buf[cur + 1], - buf[cur + 2], buf[cur + 3], buf[cur + 4], - buf[cur + 5], buf[cur + 6], buf[cur + 7]); - return 8; + p += scnprintf(p, end - p, fmt, ent->txt, bufp[cur], + bufp[cur + 1], + bufp[cur + 2], bufp[cur + 3], bufp[cur + 4], + bufp[cur + 5], bufp[cur + 6], bufp[cur + 7]); + *ate = 8; + goto out; default: return 0; } + +out: + return p - buf; } -static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev, - const struct txpwr_map *map) +static ssize_t __print_txpwr_map(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct txpwr_map *map) { u8 fct = rtwdev->chip->txpwr_factor_mac; u8 path_num = rtwdev->chip->rf_path_num; + char *p = buf, *end = buf + bufsz; unsigned int cur, i; + unsigned int eaten; u32 max_valid_addr; u32 val, addr; - s8 *buf, tmp; + s8 *bufp, tmp; int ret; - buf = vzalloc(map->addr_to - map->addr_from + 4); - if (!buf) + bufp = vzalloc(map->addr_to - map->addr_from + 4); + if (!bufp) return -ENOMEM; if (path_num == 1) @@ -773,52 +844,31 @@ static int __print_txpwr_map(struct seq_file *m, struct rtw89_dev *rtwdev, for (i = 0; i < 4; i++, val >>= 8) { /* signed 7 bits, and reserved BIT(7) */ tmp = sign_extend32(val, 6); - buf[cur + i] = tmp >> fct; + bufp[cur + i] = tmp >> fct; } } - for (cur = 0, i = 0; i < map->size; i++) - cur += __print_txpwr_ent(m, &map->ent[i], buf, cur); + for (cur = 0, i = 0; i < map->size; i++, cur += eaten) + p += __print_txpwr_ent(p, end - p, &map->ent[i], bufp, cur, &eaten); - vfree(buf); - return 0; + vfree(bufp); + return p - buf; } -#define case_REGD(_regd) \ - case RTW89_ ## _regd: \ - seq_puts(m, #_regd "\n"); \ - break - -static void __print_regd(struct seq_file *m, struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) +static int __print_regd(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct rtw89_chan *chan) { + const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + char *p = buf, *end = buf + bufsz; u8 band = chan->band_type; u8 regd = rtw89_regd_get(rtwdev, band); - switch (regd) { - default: - seq_printf(m, "UNKNOWN: %d\n", regd); - break; - case_REGD(WW); - case_REGD(ETSI); - case_REGD(FCC); - case_REGD(MKK); - case_REGD(NA); - case_REGD(IC); - case_REGD(KCC); - case_REGD(NCC); - case_REGD(CHILE); - case_REGD(ACMA); - case_REGD(MEXICO); - case_REGD(UKRAINE); - case_REGD(CN); - case_REGD(QATAR); - case_REGD(UK); - case_REGD(THAILAND); - } -} + p += scnprintf(p, end - p, "%s\n", rtw89_regd_get_string(regd)); + p += scnprintf(p, end - p, "\t(txpwr UK follow ETSI: %s)\n", + str_yes_no(regulatory->txpwr_uk_follow_etsi)); -#undef case_REGD + return p - buf; +} struct dbgfs_txpwr_table { const struct txpwr_map *byr; @@ -844,96 +894,95 @@ static const struct dbgfs_txpwr_table *dbgfs_txpwr_tables[RTW89_CHIP_GEN_NUM] = }; static -void rtw89_debug_priv_txpwr_table_get_regd(struct seq_file *m, - struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) +int rtw89_debug_priv_txpwr_table_get_regd(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + const struct rtw89_chan *chan) { const struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; const struct rtw89_reg_6ghz_tpe *tpe6 = ®ulatory->reg_6ghz_tpe; + char *p = buf, *end = buf + bufsz; - seq_printf(m, "[Chanctx] band %u, ch %u, bw %u\n", - chan->band_type, chan->channel, chan->band_width); + p += scnprintf(p, end - p, "[Chanctx] band %u, ch %u, bw %u\n", + chan->band_type, chan->channel, chan->band_width); - seq_puts(m, "[Regulatory] "); - __print_regd(m, rtwdev, chan); + p += scnprintf(p, end - p, "[Regulatory] "); + p += __print_regd(rtwdev, p, end - p, chan); if (chan->band_type == RTW89_BAND_6G) { - seq_printf(m, "[reg6_pwr_type] %u\n", regulatory->reg_6ghz_power); + p += scnprintf(p, end - p, "[reg6_pwr_type] %u\n", + regulatory->reg_6ghz_power); if (tpe6->valid) - seq_printf(m, "[TPE] %d dBm\n", tpe6->constraint); + p += scnprintf(p, end - p, "[TPE] %d dBm\n", + tpe6->constraint); } + + return p - buf; } -static int rtw89_debug_priv_txpwr_table_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_txpwr_table_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; enum rtw89_chip_gen chip_gen = rtwdev->chip->chip_gen; + struct rtw89_sar_parm sar_parm = {}; const struct dbgfs_txpwr_table *tbl; const struct rtw89_chan *chan; - int ret = 0; + char *p = buf, *end = buf + bufsz; + ssize_t n; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); - mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + sar_parm.center_freq = chan->freq; - rtw89_debug_priv_txpwr_table_get_regd(m, rtwdev, chan); + p += rtw89_debug_priv_txpwr_table_get_regd(rtwdev, p, end - p, chan); - seq_puts(m, "[SAR]\n"); - rtw89_print_sar(m, rtwdev, chan->freq); + p += scnprintf(p, end - p, "[SAR]\n"); + p += rtw89_print_sar(rtwdev, p, end - p, &sar_parm); - seq_puts(m, "[TAS]\n"); - rtw89_print_tas(m, rtwdev); + p += scnprintf(p, end - p, "[TAS]\n"); + p += rtw89_print_tas(rtwdev, p, end - p); - seq_puts(m, "[DAG]\n"); - rtw89_print_ant_gain(m, rtwdev, chan); + p += scnprintf(p, end - p, "[DAG]\n"); + p += rtw89_print_ant_gain(rtwdev, p, end - p, chan); tbl = dbgfs_txpwr_tables[chip_gen]; - if (!tbl) { - ret = -EOPNOTSUPP; - goto err; - } - - seq_puts(m, "\n[TX power byrate]\n"); - ret = __print_txpwr_map(m, rtwdev, tbl->byr); - if (ret) - goto err; - - seq_puts(m, "\n[TX power limit]\n"); - ret = __print_txpwr_map(m, rtwdev, tbl->lmt); - if (ret) - goto err; - - seq_puts(m, "\n[TX power limit_ru]\n"); - ret = __print_txpwr_map(m, rtwdev, tbl->lmt_ru); - if (ret) - goto err; + if (!tbl) + return -EOPNOTSUPP; -err: - mutex_unlock(&rtwdev->mutex); - return ret; + p += scnprintf(p, end - p, "\n[TX power byrate]\n"); + n = __print_txpwr_map(rtwdev, p, end - p, tbl->byr); + if (n < 0) + return n; + p += n; + + p += scnprintf(p, end - p, "\n[TX power limit]\n"); + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt); + if (n < 0) + return n; + p += n; + + p += scnprintf(p, end - p, "\n[TX power limit_ru]\n"); + n = __print_txpwr_map(rtwdev, p, end - p, tbl->lmt_ru); + if (n < 0) + return n; + p += n; + + return p - buf; } static ssize_t -rtw89_debug_priv_mac_reg_dump_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_mac_reg_dump_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; const struct rtw89_chip_info *chip = rtwdev->chip; - char buf[32]; - size_t buf_size; int sel; int ret; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; ret = kstrtoint(buf, 0, &sel); if (ret) return ret; @@ -957,99 +1006,91 @@ rtw89_debug_priv_mac_reg_dump_select(struct file *filp, #define RTW89_MAC_PAGE_SIZE 0x100 -static int rtw89_debug_priv_mac_reg_dump_get(struct seq_file *m, void *v) +static +ssize_t rtw89_debug_priv_mac_reg_dump_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; enum rtw89_debug_mac_reg_sel reg_sel = debugfs_priv->cb_data; - u32 start, end; + char *p = buf, *end = buf + bufsz; + u32 start, end_addr; u32 i, j, k, page; u32 val; switch (reg_sel) { case RTW89_DBG_SEL_MAC_00: - seq_puts(m, "Debug selected MAC page 0x00\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0x00\n"); start = 0x000; - end = 0x014; + end_addr = 0x014; break; case RTW89_DBG_SEL_MAC_30: - seq_puts(m, "Debug selected MAC page 0x30\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0x30\n"); start = 0x030; - end = 0x033; + end_addr = 0x033; break; case RTW89_DBG_SEL_MAC_40: - seq_puts(m, "Debug selected MAC page 0x40\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0x40\n"); start = 0x040; - end = 0x07f; + end_addr = 0x07f; break; case RTW89_DBG_SEL_MAC_80: - seq_puts(m, "Debug selected MAC page 0x80\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0x80\n"); start = 0x080; - end = 0x09f; + end_addr = 0x09f; break; case RTW89_DBG_SEL_MAC_C0: - seq_puts(m, "Debug selected MAC page 0xc0\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0xc0\n"); start = 0x0c0; - end = 0x0df; + end_addr = 0x0df; break; case RTW89_DBG_SEL_MAC_E0: - seq_puts(m, "Debug selected MAC page 0xe0\n"); + p += scnprintf(p, end - p, "Debug selected MAC page 0xe0\n"); start = 0x0e0; - end = 0x0ff; + end_addr = 0x0ff; break; case RTW89_DBG_SEL_BB: - seq_puts(m, "Debug selected BB register\n"); + p += scnprintf(p, end - p, "Debug selected BB register\n"); start = 0x100; - end = 0x17f; + end_addr = 0x17f; break; case RTW89_DBG_SEL_IQK: - seq_puts(m, "Debug selected IQK register\n"); + p += scnprintf(p, end - p, "Debug selected IQK register\n"); start = 0x180; - end = 0x1bf; + end_addr = 0x1bf; break; case RTW89_DBG_SEL_RFC: - seq_puts(m, "Debug selected RFC register\n"); + p += scnprintf(p, end - p, "Debug selected RFC register\n"); start = 0x1c0; - end = 0x1ff; + end_addr = 0x1ff; break; default: - seq_puts(m, "Selected invalid register page\n"); + p += scnprintf(p, end - p, "Selected invalid register page\n"); return -EINVAL; } - for (i = start; i <= end; i++) { + for (i = start; i <= end_addr; i++) { page = i << 8; for (j = page; j < page + RTW89_MAC_PAGE_SIZE; j += 16) { - seq_printf(m, "%08xh : ", 0x18600000 + j); + p += scnprintf(p, end - p, "%08xh : ", 0x18600000 + j); for (k = 0; k < 4; k++) { val = rtw89_read32(rtwdev, j + (k << 2)); - seq_printf(m, "%08x ", val); + p += scnprintf(p, end - p, "%08x ", val); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } } - return 0; + return p - buf; } static ssize_t -rtw89_debug_priv_mac_mem_dump_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_mac_mem_dump_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; u32 sel, start_addr, len; int num; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%x %x %x", &sel, &start_addr, &len); if (num != 3) { rtw89_info(rtwdev, "invalid format: <sel> <start> <len>\n"); @@ -1066,15 +1107,16 @@ rtw89_debug_priv_mac_mem_dump_select(struct file *filp, return count; } -static void rtw89_debug_dump_mac_mem(struct seq_file *m, - struct rtw89_dev *rtwdev, - u8 sel, u32 start_addr, u32 len) +static int rtw89_debug_dump_mac_mem(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + u8 sel, u32 start_addr, u32 len) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u32 filter_model_addr = mac->filter_model_addr; u32 indir_access_addr = mac->indir_access_addr; u32 base_addr, start_page, residue; - u32 i, j, p, pages; + char *p = buf, *end = buf + bufsz; + u32 i, j, pp, pages; u32 dump_len, remain; u32 val; @@ -1085,32 +1127,37 @@ static void rtw89_debug_dump_mac_mem(struct seq_file *m, base_addr = mac->mem_base_addrs[sel]; base_addr += start_page * MAC_MEM_DUMP_PAGE_SIZE; - for (p = 0; p < pages; p++) { + for (pp = 0; pp < pages; pp++) { dump_len = min_t(u32, remain, MAC_MEM_DUMP_PAGE_SIZE); rtw89_write32(rtwdev, filter_model_addr, base_addr); for (i = indir_access_addr + residue; i < indir_access_addr + dump_len;) { - seq_printf(m, "%08xh:", i); + p += scnprintf(p, end - p, "%08xh:", i); for (j = 0; j < 4 && i < indir_access_addr + dump_len; j++, i += 4) { val = rtw89_read32(rtwdev, i); - seq_printf(m, " %08x", val); + p += scnprintf(p, end - p, " %08x", val); remain -= 4; } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); } base_addr += MAC_MEM_DUMP_PAGE_SIZE; } + + return p - buf; } -static int -rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_mac_mem_dump_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *p = buf, *end = buf + bufsz; bool grant_read = false; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + if (debugfs_priv->mac_mem.sel >= RTW89_MAC_MEM_NUM) return -ENOENT; @@ -1127,40 +1174,28 @@ rtw89_debug_priv_mac_mem_dump_get(struct seq_file *m, void *v) } } - mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); if (grant_read) rtw89_write32_set(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); - rtw89_debug_dump_mac_mem(m, rtwdev, - debugfs_priv->mac_mem.sel, - debugfs_priv->mac_mem.start, - debugfs_priv->mac_mem.len); + p += rtw89_debug_dump_mac_mem(rtwdev, p, end - p, + debugfs_priv->mac_mem.sel, + debugfs_priv->mac_mem.start, + debugfs_priv->mac_mem.len); if (grant_read) rtw89_write32_clr(rtwdev, R_AX_TCR1, B_AX_TCR_FORCE_READ_TXDFIFO); - mutex_unlock(&rtwdev->mutex); - return 0; + return p - buf; } static ssize_t -rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_mac_dbg_port_dump_select(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - char buf[32]; - size_t buf_size; int sel, set; int num; bool enable; - buf_size = min(count, sizeof(buf) - 1); - if (copy_from_user(buf, user_buf, buf_size)) - return -EFAULT; - - buf[buf_size] = '\0'; num = sscanf(buf, "%d %d", &sel, &set); if (num != 2) { rtw89_info(rtwdev, "invalid format: <sel> <set>\n"); @@ -1196,13 +1231,13 @@ rtw89_debug_priv_mac_dbg_port_dump_select(struct file *filp, } static int rtw89_debug_mac_dump_ss_dbg(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { return 0; } static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { #define DLE_DFI_DUMP(__type, __target, __sel) \ ({ \ @@ -1231,7 +1266,7 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, __data; \ }) -#define DLE_DFI_FREE_PAGE_DUMP(__m, __type) \ +#define DLE_DFI_FREE_PAGE_DUMP(__p, __end, __type) \ ({ \ u32 __freepg, __pubpg; \ u32 __freepg_head, __freepg_tail, __pubpg_num; \ @@ -1241,24 +1276,25 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, __freepg_head = FIELD_GET(B_AX_DLE_FREE_HEADPG, __freepg); \ __freepg_tail = FIELD_GET(B_AX_DLE_FREE_TAILPG, __freepg); \ __pubpg_num = FIELD_GET(B_AX_DLE_PUB_PGNUM, __pubpg); \ - seq_printf(__m, "[%s] freepg head: %d\n", \ - #__type, __freepg_head); \ - seq_printf(__m, "[%s] freepg tail: %d\n", \ - #__type, __freepg_tail); \ - seq_printf(__m, "[%s] pubpg num : %d\n", \ - #__type, __pubpg_num); \ + __p += scnprintf(__p, __end - __p, "[%s] freepg head: %d\n", \ + #__type, __freepg_head); \ + __p += scnprintf(__p, __end - __p, "[%s] freepg tail: %d\n", \ + #__type, __freepg_tail); \ + __p += scnprintf(__p, __end - __p, "[%s] pubpg num : %d\n", \ + #__type, __pubpg_num); \ }) -#define case_QUOTA(__m, __type, __id) \ +#define case_QUOTA(__p, __end, __type, __id) \ case __type##_QTAID_##__id: \ - val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \ + val32 = DLE_DFI_DUMP(__type, QUOTA, __type##_QTAID_##__id); \ rsv_pgnum = FIELD_GET(B_AX_DLE_RSV_PGNUM, val32); \ use_pgnum = FIELD_GET(B_AX_DLE_USE_PGNUM, val32); \ - seq_printf(__m, "[%s][%s] rsv_pgnum: %d\n", \ - #__type, #__id, rsv_pgnum); \ - seq_printf(__m, "[%s][%s] use_pgnum: %d\n", \ - #__type, #__id, use_pgnum); \ + __p += scnprintf(__p, __end - __p, "[%s][%s] rsv_pgnum: %d\n", \ + #__type, #__id, rsv_pgnum); \ + __p += scnprintf(__p, __end - __p, "[%s][%s] use_pgnum: %d\n", \ + #__type, #__id, use_pgnum); \ break + char *p = buf, *end = buf + bufsz; u32 quota_id; u32 val32; u16 rsv_pgnum, use_pgnum; @@ -1266,38 +1302,39 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL); if (ret) { - seq_puts(m, "[DLE] : DMAC not enabled\n"); - return ret; + p += scnprintf(p, end - p, "[DLE] : DMAC not enabled\n"); + goto out; } - DLE_DFI_FREE_PAGE_DUMP(m, WDE); - DLE_DFI_FREE_PAGE_DUMP(m, PLE); + DLE_DFI_FREE_PAGE_DUMP(p, end, WDE); + DLE_DFI_FREE_PAGE_DUMP(p, end, PLE); for (quota_id = 0; quota_id <= WDE_QTAID_CPUIO; quota_id++) { switch (quota_id) { - case_QUOTA(m, WDE, HOST_IF); - case_QUOTA(m, WDE, WLAN_CPU); - case_QUOTA(m, WDE, DATA_CPU); - case_QUOTA(m, WDE, PKTIN); - case_QUOTA(m, WDE, CPUIO); + case_QUOTA(p, end, WDE, HOST_IF); + case_QUOTA(p, end, WDE, WLAN_CPU); + case_QUOTA(p, end, WDE, DATA_CPU); + case_QUOTA(p, end, WDE, PKTIN); + case_QUOTA(p, end, WDE, CPUIO); } } for (quota_id = 0; quota_id <= PLE_QTAID_CPUIO; quota_id++) { switch (quota_id) { - case_QUOTA(m, PLE, B0_TXPL); - case_QUOTA(m, PLE, B1_TXPL); - case_QUOTA(m, PLE, C2H); - case_QUOTA(m, PLE, H2C); - case_QUOTA(m, PLE, WLAN_CPU); - case_QUOTA(m, PLE, MPDU); - case_QUOTA(m, PLE, CMAC0_RX); - case_QUOTA(m, PLE, CMAC1_RX); - case_QUOTA(m, PLE, CMAC1_BBRPT); - case_QUOTA(m, PLE, WDRLS); - case_QUOTA(m, PLE, CPUIO); + case_QUOTA(p, end, PLE, B0_TXPL); + case_QUOTA(p, end, PLE, B1_TXPL); + case_QUOTA(p, end, PLE, C2H); + case_QUOTA(p, end, PLE, H2C); + case_QUOTA(p, end, PLE, WLAN_CPU); + case_QUOTA(p, end, PLE, MPDU); + case_QUOTA(p, end, PLE, CMAC0_RX); + case_QUOTA(p, end, PLE, CMAC1_RX); + case_QUOTA(p, end, PLE, CMAC1_BBRPT); + case_QUOTA(p, end, PLE, WDRLS); + case_QUOTA(p, end, PLE, CPUIO); } } - return 0; +out: + return p - buf; #undef case_QUOTA #undef DLE_DFI_DUMP @@ -1305,73 +1342,88 @@ static int rtw89_debug_mac_dump_dle_dbg(struct rtw89_dev *rtwdev, } static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; u32 dmac_err; int i, ret; ret = rtw89_mac_check_mac_en(rtwdev, 0, RTW89_DMAC_SEL); if (ret) { - seq_puts(m, "[DMAC] : DMAC not enabled\n"); - return ret; + p += scnprintf(p, end - p, "[DMAC] : DMAC not enabled\n"); + goto out; } dmac_err = rtw89_read32(rtwdev, R_AX_DMAC_ERR_ISR); - seq_printf(m, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err); - seq_printf(m, "R_AX_DMAC_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_DMAC_ERR_ISR=0x%08x\n", dmac_err); + p += scnprintf(p, end - p, "R_AX_DMAC_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_DMAC_ERR_IMR)); if (dmac_err) { - seq_printf(m, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1)); - seq_printf(m, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_FLAG_CFG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_FLAG_CFG_NUM1)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_CFG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_CFG_NUM1)); if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_PLE_ERRFLAG_MSG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG)); - seq_printf(m, "R_AX_WDE_ERRFLAG_MSG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG)); - seq_printf(m, "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN)); - seq_printf(m, "R_AX_PLE_DBGERR_STS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS)); + p += scnprintf(p, end - p, + "R_AX_PLE_ERRFLAG_MSG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERRFLAG_MSG)); + p += scnprintf(p, end - p, + "R_AX_WDE_ERRFLAG_MSG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERRFLAG_MSG)); + p += scnprintf(p, end - p, + "R_AX_PLE_DBGERR_LOCKEN=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_DBGERR_LOCKEN)); + p += scnprintf(p, end - p, + "R_AX_PLE_DBGERR_STS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_DBGERR_STS)); } } if (dmac_err & B_AX_WDRLS_ERR_FLAG) { - seq_printf(m, "R_AX_WDRLS_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR)); - seq_printf(m, "R_AX_WDRLS_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_WDRLS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDRLS_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_WDRLS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDRLS_ERR_ISR)); if (chip->chip_id == RTL8852C) - seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1)); + p += scnprintf(p, end - p, + "R_AX_RPQ_RXBD_IDX=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX_V1)); else - seq_printf(m, "R_AX_RPQ_RXBD_IDX=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX)); + p += scnprintf(p, end - p, + "R_AX_RPQ_RXBD_IDX=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RPQ_RXBD_IDX)); } if (dmac_err & B_AX_WSEC_ERR_FLAG) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_SEC_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR)); - seq_printf(m, "R_AX_SEC_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG)); - seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); - seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); - seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); - seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); - seq_printf(m, "R_AX_SEC_DEBUG1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_DEBUG1)); - seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); - seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG_IMR)); + p += scnprintf(p, end - p, + "R_AX_SEC_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ERROR_FLAG)); + p += scnprintf(p, end - p, + "R_AX_SEC_ENG_CTRL=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); + p += scnprintf(p, end - p, + "R_AX_SEC_MPDU_PROC=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_ACCESS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_RDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); + p += scnprintf(p, end - p, "R_AX_SEC_DEBUG1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_DEBUG1)); + p += scnprintf(p, end - p, + "R_AX_SEC_TX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_RX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, B_AX_DBG_SEL0, 0x8B); @@ -1382,187 +1434,229 @@ static int rtw89_debug_mac_dump_dmac_dbg(struct rtw89_dev *rtwdev, for (i = 0; i < 0x10; i++) { rtw89_write32_mask(rtwdev, R_AX_SEC_ENG_CTRL, B_AX_SEC_DBG_PORT_FIELD_MASK, i); - seq_printf(m, "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n", - i, rtw89_read32(rtwdev, R_AX_SEC_DEBUG2)); + p += scnprintf(p, end - p, + "sel=%x,R_AX_SEC_DEBUG2=0x%08x\n", + i, + rtw89_read32(rtwdev, R_AX_SEC_DEBUG2)); } } else { - seq_printf(m, "R_AX_SEC_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); - seq_printf(m, "R_AX_SEC_ENG_CTRL=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); - seq_printf(m, "R_AX_SEC_MPDU_PROC=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); - seq_printf(m, "R_AX_SEC_CAM_ACCESS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); - seq_printf(m, "R_AX_SEC_CAM_RDATA=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); - seq_printf(m, "R_AX_SEC_CAM_WDATA=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); - seq_printf(m, "R_AX_SEC_TX_DEBUG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); - seq_printf(m, "R_AX_SEC_RX_DEBUG=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); - seq_printf(m, "R_AX_SEC_TRX_PKT_CNT=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); - seq_printf(m, "R_AX_SEC_TRX_BLK_CNT=0x%08x\n", - rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT)); + p += scnprintf(p, end - p, + "R_AX_SEC_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_ENG_CTRL=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_ENG_CTRL)); + p += scnprintf(p, end - p, + "R_AX_SEC_MPDU_PROC=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_MPDU_PROC)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_ACCESS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_ACCESS)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_RDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_RDATA)); + p += scnprintf(p, end - p, + "R_AX_SEC_CAM_WDATA=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_CAM_WDATA)); + p += scnprintf(p, end - p, + "R_AX_SEC_TX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TX_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_RX_DEBUG=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_RX_DEBUG)); + p += scnprintf(p, end - p, + "R_AX_SEC_TRX_PKT_CNT=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TRX_PKT_CNT)); + p += scnprintf(p, end - p, + "R_AX_SEC_TRX_BLK_CNT=0x%08x\n", + rtw89_read32(rtwdev, R_AX_SEC_TRX_BLK_CNT)); } } if (dmac_err & B_AX_MPDU_ERR_FLAG) { - seq_printf(m, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR)); - seq_printf(m, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); - seq_printf(m, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR)); - seq_printf(m, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_MPDU_TX_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_MPDU_TX_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_TX_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_MPDU_RX_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_MPDU_RX_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_MPDU_RX_ERR_ISR)); } if (dmac_err & B_AX_STA_SCHEDULER_ERR_FLAG) { - seq_printf(m, "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR)); - seq_printf(m, "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_STA_SCHEDULER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_STA_SCHEDULER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_STA_SCHEDULER_ERR_ISR)); } if (dmac_err & B_AX_WDE_DLE_ERR_FLAG) { - seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); - seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); - seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); - seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); } if (dmac_err & B_AX_TXPKTCTRL_ERR_FLAG) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR)); - seq_printf(m, "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR)); - seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR)); - seq_printf(m, "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_B0_ERRFLAG_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_IMR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_B0_ERRFLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B0_ERRFLAG_ISR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_B1_ERRFLAG_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_IMR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_B1_ERRFLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_B1_ERRFLAG_ISR)); } else { - seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); - seq_printf(m, "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR)); + p += scnprintf(p, end - p, + "R_AX_TXPKTCTL_ERR_IMR_ISR_B1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_TXPKTCTL_ERR_IMR_ISR_B1)); } } if (dmac_err & B_AX_PLE_DLE_ERR_FLAG) { - seq_printf(m, "R_AX_WDE_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); - seq_printf(m, "R_AX_WDE_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); - seq_printf(m, "R_AX_PLE_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); - seq_printf(m, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); - seq_printf(m, "R_AX_WD_CPUQ_OP_0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0)); - seq_printf(m, "R_AX_WD_CPUQ_OP_1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1)); - seq_printf(m, "R_AX_WD_CPUQ_OP_2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2)); - seq_printf(m, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS)); - seq_printf(m, "R_AX_PL_CPUQ_OP_0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0)); - seq_printf(m, "R_AX_PL_CPUQ_OP_1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1)); - seq_printf(m, "R_AX_PL_CPUQ_OP_2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2)); - seq_printf(m, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_WDE_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WDE_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_PLE_ERR_FLAG_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PLE_ERR_FLAG_ISR)); + p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_0)); + p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_1)); + p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_2)); + p += scnprintf(p, end - p, "R_AX_WD_CPUQ_OP_STATUS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_WD_CPUQ_OP_STATUS)); + p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_0)); + p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_1)); + p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_2)); + p += scnprintf(p, end - p, "R_AX_PL_CPUQ_OP_STATUS=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PL_CPUQ_OP_STATUS)); if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_RX_CTRL0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RX_CTRL0)); - seq_printf(m, "R_AX_RX_CTRL1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RX_CTRL1)); - seq_printf(m, "R_AX_RX_CTRL2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RX_CTRL2)); + p += scnprintf(p, end - p, "R_AX_RX_CTRL0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL0)); + p += scnprintf(p, end - p, "R_AX_RX_CTRL1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL1)); + p += scnprintf(p, end - p, "R_AX_RX_CTRL2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RX_CTRL2)); } else { - seq_printf(m, "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); - seq_printf(m, "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); - seq_printf(m, "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", - rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2)); + p += scnprintf(p, end - p, + "R_AX_RXDMA_PKT_INFO_0=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_0)); + p += scnprintf(p, end - p, + "R_AX_RXDMA_PKT_INFO_1=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_1)); + p += scnprintf(p, end - p, + "R_AX_RXDMA_PKT_INFO_2=0x%08x\n", + rtw89_read32(rtwdev, R_AX_RXDMA_PKT_INFO_2)); } } if (dmac_err & B_AX_PKTIN_ERR_FLAG) { - seq_printf(m, "R_AX_PKTIN_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); - seq_printf(m, "R_AX_PKTIN_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); + p += scnprintf(p, end - p, "R_AX_PKTIN_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PKTIN_ERR_IMR)); + p += scnprintf(p, end - p, "R_AX_PKTIN_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_PKTIN_ERR_ISR)); } if (dmac_err & B_AX_DISPATCH_ERR_FLAG) { - seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR)); - seq_printf(m, "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); - seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR)); - seq_printf(m, "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); - seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR)); - seq_printf(m, "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_HOST_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_HOST_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HOST_DISPATCHER_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_CPU_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_CPU_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_CPU_DISPATCHER_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_OTHER_DISPATCHER_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_OTHER_DISPATCHER_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_OTHER_DISPATCHER_ERR_ISR)); } if (dmac_err & B_AX_BBRPT_ERR_FLAG) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR)); - seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); - seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_COM_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_COM_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); } else { - seq_printf(m, "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); - seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); - seq_printf(m, "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); - seq_printf(m, "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_COM_ERR_IMR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_COM_ERR_IMR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_CHINFO_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_ISR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_CHINFO_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_CHINFO_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_DFS_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_IMR)); + p += scnprintf(p, end - p, + "R_AX_BBRPT_DFS_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_BBRPT_DFS_ERR_ISR)); } } if (dmac_err & B_AX_HAXIDMA_ERR_FLAG && chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK)); - seq_printf(m, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n", - rtw89_read32(rtwdev, R_AX_HAXI_IDCT)); + p += scnprintf(p, end - p, "R_AX_HAXIDMA_ERR_IMR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HAXI_IDCT_MSK)); + p += scnprintf(p, end - p, "R_AX_HAXIDMA_ERR_ISR=0x%08x\n", + rtw89_read32(rtwdev, R_AX_HAXI_IDCT)); } - return 0; +out: + return p - buf; } static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev, - struct seq_file *m, + char *buf, size_t bufsz, enum rtw89_mac_idx band) { const struct rtw89_chip_info *chip = rtwdev->chip; + char *p = buf, *end = buf + bufsz; u32 offset = 0; u32 cmac_err; int ret; @@ -1570,96 +1664,127 @@ static int rtw89_debug_mac_dump_cmac_err(struct rtw89_dev *rtwdev, ret = rtw89_mac_check_mac_en(rtwdev, band, RTW89_CMAC_SEL); if (ret) { if (band) - seq_puts(m, "[CMAC] : CMAC1 not enabled\n"); + p += scnprintf(p, end - p, + "[CMAC] : CMAC1 not enabled\n"); else - seq_puts(m, "[CMAC] : CMAC0 not enabled\n"); - return ret; + p += scnprintf(p, end - p, + "[CMAC] : CMAC0 not enabled\n"); + goto out; } if (band) offset = RTW89_MAC_AX_BAND_REG_OFFSET; cmac_err = rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset); - seq_printf(m, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset)); - seq_printf(m, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset)); - seq_printf(m, "R_AX_CK_EN [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_CK_EN + offset)); + p += scnprintf(p, end - p, "R_AX_CMAC_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_ERR_ISR + offset)); + p += scnprintf(p, end - p, "R_AX_CMAC_FUNC_EN [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_FUNC_EN + offset)); + p += scnprintf(p, end - p, "R_AX_CK_EN [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CK_EN + offset)); if (cmac_err & B_AX_SCHEDULE_TOP_ERR_IND) { - seq_printf(m, "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset)); - seq_printf(m, "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset)); + p += scnprintf(p, end - p, + "R_AX_SCHEDULE_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_IMR + offset)); + p += scnprintf(p, end - p, + "R_AX_SCHEDULE_ERR_ISR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_SCHEDULE_ERR_ISR + offset)); } if (cmac_err & B_AX_PTCL_TOP_ERR_IND) { - seq_printf(m, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset)); - seq_printf(m, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset)); + p += scnprintf(p, end - p, "R_AX_PTCL_IMR0 [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PTCL_IMR0 + offset)); + p += scnprintf(p, end - p, "R_AX_PTCL_ISR0 [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PTCL_ISR0 + offset)); } if (cmac_err & B_AX_DMA_TOP_ERR_IND) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset)); - seq_printf(m, "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset)); + p += scnprintf(p, end - p, + "R_AX_RX_ERR_FLAG [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG + offset)); + p += scnprintf(p, end - p, + "R_AX_RX_ERR_FLAG_IMR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_RX_ERR_FLAG_IMR + offset)); } else { - seq_printf(m, "R_AX_DLE_CTRL [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset)); + p += scnprintf(p, end - p, + "R_AX_DLE_CTRL [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_DLE_CTRL + offset)); } } if (cmac_err & B_AX_DMA_TOP_ERR_IND || cmac_err & B_AX_WMAC_RX_ERR_IND) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset)); - seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); + p += scnprintf(p, end - p, + "R_AX_PHYINFO_ERR_ISR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_ISR + offset)); + p += scnprintf(p, end - p, + "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); } else { - seq_printf(m, "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); + p += scnprintf(p, end - p, + "R_AX_PHYINFO_ERR_IMR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_PHYINFO_ERR_IMR + offset)); } } if (cmac_err & B_AX_TXPWR_CTRL_ERR_IND) { - seq_printf(m, "R_AX_TXPWR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset)); - seq_printf(m, "R_AX_TXPWR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset)); + p += scnprintf(p, end - p, "R_AX_TXPWR_IMR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_TXPWR_IMR + offset)); + p += scnprintf(p, end - p, "R_AX_TXPWR_ISR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, R_AX_TXPWR_ISR + offset)); } if (cmac_err & B_AX_WMAC_TX_ERR_IND) { if (chip->chip_id == RTL8852C) { - seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA + offset)); - seq_printf(m, "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TRXPTCL_ERROR_INDICA_MASK + offset)); + p += scnprintf(p, end - p, + "R_AX_TRXPTCL_ERROR_INDICA [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, + R_AX_TRXPTCL_ERROR_INDICA + offset)); + p += scnprintf(p, end - p, + "R_AX_TRXPTCL_ERROR_INDICA_MASK [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, + R_AX_TRXPTCL_ERROR_INDICA_MASK + offset)); } else { - seq_printf(m, "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_TMAC_ERR_IMR_ISR + offset)); + p += scnprintf(p, end - p, + "R_AX_TMAC_ERR_IMR_ISR [%d]=0x%08x\n", + band, + rtw89_read32(rtwdev, + R_AX_TMAC_ERR_IMR_ISR + offset)); } - seq_printf(m, "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset)); + p += scnprintf(p, end - p, + "R_AX_DBGSEL_TRXPTCL [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_DBGSEL_TRXPTCL + offset)); } - seq_printf(m, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band, - rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset)); + p += scnprintf(p, end - p, "R_AX_CMAC_ERR_IMR [%d]=0x%08x\n", band, + rtw89_read32(rtwdev, R_AX_CMAC_ERR_IMR + offset)); - return 0; +out: + return p - buf; } static int rtw89_debug_mac_dump_cmac_dbg(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { - rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_0); + char *p = buf, *end = buf + bufsz; + + p += rtw89_debug_mac_dump_cmac_err(rtwdev, p, end - p, RTW89_MAC_0); if (rtwdev->dbcc_en) - rtw89_debug_mac_dump_cmac_err(rtwdev, m, RTW89_MAC_1); + p += rtw89_debug_mac_dump_cmac_err(rtwdev, p, end - p, RTW89_MAC_1); - return 0; + return p - buf; } static const struct rtw89_mac_dbg_port_info dbg_port_ptcl_c0 = { @@ -2465,11 +2590,12 @@ static const struct rtw89_mac_dbg_port_info dbg_port_pcie_misc2 = { .rd_msk = B_AX_DEBUG_ST_MASK }; -static const struct rtw89_mac_dbg_port_info * -rtw89_debug_mac_dbg_port_sel(struct seq_file *m, - struct rtw89_dev *rtwdev, u32 sel) +static int +rtw89_debug_mac_dbg_port_sel(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + u32 sel, const struct rtw89_mac_dbg_port_info **ppinfo) { - const struct rtw89_mac_dbg_port_info *info; + const struct rtw89_mac_dbg_port_info *info = NULL; + char *p = buf, *end = buf + bufsz; u32 index; u32 val32; u16 val16; @@ -2481,28 +2607,28 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG); val16 |= B_AX_PTCL_DBG_EN; rtw89_write16(rtwdev, R_AX_PTCL_DBG, val16); - seq_puts(m, "Enable PTCL C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable PTCL C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_PTCL_C1: info = &dbg_port_ptcl_c1; val16 = rtw89_read16(rtwdev, R_AX_PTCL_DBG_C1); val16 |= B_AX_PTCL_DBG_EN; rtw89_write16(rtwdev, R_AX_PTCL_DBG_C1, val16); - seq_puts(m, "Enable PTCL C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable PTCL C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_SCH_C0: info = &dbg_port_sch_c0; val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL); val32 |= B_AX_SCH_DBG_EN; rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL, val32); - seq_puts(m, "Enable SCH C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable SCH C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_SCH_C1: info = &dbg_port_sch_c1; val32 = rtw89_read32(rtwdev, R_AX_SCH_DBG_SEL_C1); val32 |= B_AX_SCH_DBG_EN; rtw89_write32(rtwdev, R_AX_SCH_DBG_SEL_C1, val32); - seq_puts(m, "Enable SCH C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable SCH C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TMAC_C0: info = &dbg_port_tmac_c0; @@ -2519,7 +2645,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1); val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK); rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); - seq_puts(m, "Enable TMAC C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable TMAC C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TMAC_C1: info = &dbg_port_tmac_c1; @@ -2536,7 +2662,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1); val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK); rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); - seq_puts(m, "Enable TMAC C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable TMAC C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMAC_C0: info = &dbg_port_rmac_c0; @@ -2558,7 +2684,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL, B_AX_DBGSEL_TRXPTCL_MASK); rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL, val8); - seq_puts(m, "Enable RMAC C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMAC_C1: info = &dbg_port_rmac_c1; @@ -2580,23 +2706,23 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val8 = u8_replace_bits(val8, RMAC_CMAC_DBG_SEL, B_AX_DBGSEL_TRXPTCL_MASK); rtw89_write8(rtwdev, R_AX_DBGSEL_TRXPTCL_C1, val8); - seq_puts(m, "Enable RMAC C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMACST_C0: info = &dbg_port_rmacst_c0; - seq_puts(m, "Enable RMAC state C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC state C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMACST_C1: info = &dbg_port_rmacst_c1; - seq_puts(m, "Enable RMAC state C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC state C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMAC_PLCP_C0: info = &dbg_port_rmac_plcp_c0; - seq_puts(m, "Enable RMAC PLCP C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC PLCP C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_RMAC_PLCP_C1: info = &dbg_port_rmac_plcp_c1; - seq_puts(m, "Enable RMAC PLCP C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable RMAC PLCP C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TRXPTCL_C0: info = &dbg_port_trxptcl_c0; @@ -2608,7 +2734,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1); val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK); rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); - seq_puts(m, "Enable TRXPTCL C0 dbgport.\n"); + p += scnprintf(p, end - p, "Enable TRXPTCL C0 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TRXPTCL_C1: info = &dbg_port_trxptcl_c1; @@ -2620,131 +2746,137 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = rtw89_read32(rtwdev, R_AX_SYS_STATUS1); val32 = u32_replace_bits(val32, MAC_DBG_SEL, B_AX_SEL_0XC0_MASK); rtw89_write32(rtwdev, R_AX_SYS_STATUS1, val32); - seq_puts(m, "Enable TRXPTCL C1 dbgport.\n"); + p += scnprintf(p, end - p, "Enable TRXPTCL C1 dbgport.\n"); break; case RTW89_DBG_PORT_SEL_TX_INFOL_C0: info = &dbg_port_tx_infol_c0; val32 = rtw89_read32(rtwdev, R_AX_TCR1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1, val32); - seq_puts(m, "Enable tx infol dump.\n"); + p += scnprintf(p, end - p, "Enable tx infol dump.\n"); break; case RTW89_DBG_PORT_SEL_TX_INFOH_C0: info = &dbg_port_tx_infoh_c0; val32 = rtw89_read32(rtwdev, R_AX_TCR1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1, val32); - seq_puts(m, "Enable tx infoh dump.\n"); + p += scnprintf(p, end - p, "Enable tx infoh dump.\n"); break; case RTW89_DBG_PORT_SEL_TX_INFOL_C1: info = &dbg_port_tx_infol_c1; val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); - seq_puts(m, "Enable tx infol dump.\n"); + p += scnprintf(p, end - p, "Enable tx infol dump.\n"); break; case RTW89_DBG_PORT_SEL_TX_INFOH_C1: info = &dbg_port_tx_infoh_c1; val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); - seq_puts(m, "Enable tx infoh dump.\n"); + p += scnprintf(p, end - p, "Enable tx infoh dump.\n"); break; case RTW89_DBG_PORT_SEL_TXTF_INFOL_C0: info = &dbg_port_txtf_infol_c0; val32 = rtw89_read32(rtwdev, R_AX_TCR1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1, val32); - seq_puts(m, "Enable tx tf infol dump.\n"); + p += scnprintf(p, end - p, "Enable tx tf infol dump.\n"); break; case RTW89_DBG_PORT_SEL_TXTF_INFOH_C0: info = &dbg_port_txtf_infoh_c0; val32 = rtw89_read32(rtwdev, R_AX_TCR1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1, val32); - seq_puts(m, "Enable tx tf infoh dump.\n"); + p += scnprintf(p, end - p, "Enable tx tf infoh dump.\n"); break; case RTW89_DBG_PORT_SEL_TXTF_INFOL_C1: info = &dbg_port_txtf_infol_c1; val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); - seq_puts(m, "Enable tx tf infol dump.\n"); + p += scnprintf(p, end - p, "Enable tx tf infol dump.\n"); break; case RTW89_DBG_PORT_SEL_TXTF_INFOH_C1: info = &dbg_port_txtf_infoh_c1; val32 = rtw89_read32(rtwdev, R_AX_TCR1_C1); val32 |= B_AX_TCR_FORCE_READ_TXDFIFO; rtw89_write32(rtwdev, R_AX_TCR1_C1, val32); - seq_puts(m, "Enable tx tf infoh dump.\n"); + p += scnprintf(p, end - p, "Enable tx tf infoh dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_BUFMGN_FREEPG: info = &dbg_port_wde_bufmgn_freepg; - seq_puts(m, "Enable wde bufmgn freepg dump.\n"); + p += scnprintf(p, end - p, "Enable wde bufmgn freepg dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_BUFMGN_QUOTA: info = &dbg_port_wde_bufmgn_quota; - seq_puts(m, "Enable wde bufmgn quota dump.\n"); + p += scnprintf(p, end - p, "Enable wde bufmgn quota dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PAGELLT: info = &dbg_port_wde_bufmgn_pagellt; - seq_puts(m, "Enable wde bufmgn pagellt dump.\n"); + p += scnprintf(p, end - p, + "Enable wde bufmgn pagellt dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_BUFMGN_PKTINFO: info = &dbg_port_wde_bufmgn_pktinfo; - seq_puts(m, "Enable wde bufmgn pktinfo dump.\n"); + p += scnprintf(p, end - p, + "Enable wde bufmgn pktinfo dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_QUEMGN_PREPKT: info = &dbg_port_wde_quemgn_prepkt; - seq_puts(m, "Enable wde quemgn prepkt dump.\n"); + p += scnprintf(p, end - p, "Enable wde quemgn prepkt dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_QUEMGN_NXTPKT: info = &dbg_port_wde_quemgn_nxtpkt; - seq_puts(m, "Enable wde quemgn nxtpkt dump.\n"); + p += scnprintf(p, end - p, "Enable wde quemgn nxtpkt dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QLNKTBL: info = &dbg_port_wde_quemgn_qlnktbl; - seq_puts(m, "Enable wde quemgn qlnktbl dump.\n"); + p += scnprintf(p, end - p, + "Enable wde quemgn qlnktbl dump.\n"); break; case RTW89_DBG_PORT_SEL_WDE_QUEMGN_QEMPTY: info = &dbg_port_wde_quemgn_qempty; - seq_puts(m, "Enable wde quemgn qempty dump.\n"); + p += scnprintf(p, end - p, "Enable wde quemgn qempty dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_BUFMGN_FREEPG: info = &dbg_port_ple_bufmgn_freepg; - seq_puts(m, "Enable ple bufmgn freepg dump.\n"); + p += scnprintf(p, end - p, "Enable ple bufmgn freepg dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_BUFMGN_QUOTA: info = &dbg_port_ple_bufmgn_quota; - seq_puts(m, "Enable ple bufmgn quota dump.\n"); + p += scnprintf(p, end - p, "Enable ple bufmgn quota dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PAGELLT: info = &dbg_port_ple_bufmgn_pagellt; - seq_puts(m, "Enable ple bufmgn pagellt dump.\n"); + p += scnprintf(p, end - p, + "Enable ple bufmgn pagellt dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_BUFMGN_PKTINFO: info = &dbg_port_ple_bufmgn_pktinfo; - seq_puts(m, "Enable ple bufmgn pktinfo dump.\n"); + p += scnprintf(p, end - p, + "Enable ple bufmgn pktinfo dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_QUEMGN_PREPKT: info = &dbg_port_ple_quemgn_prepkt; - seq_puts(m, "Enable ple quemgn prepkt dump.\n"); + p += scnprintf(p, end - p, "Enable ple quemgn prepkt dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_QUEMGN_NXTPKT: info = &dbg_port_ple_quemgn_nxtpkt; - seq_puts(m, "Enable ple quemgn nxtpkt dump.\n"); + p += scnprintf(p, end - p, "Enable ple quemgn nxtpkt dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QLNKTBL: info = &dbg_port_ple_quemgn_qlnktbl; - seq_puts(m, "Enable ple quemgn qlnktbl dump.\n"); + p += scnprintf(p, end - p, + "Enable ple quemgn qlnktbl dump.\n"); break; case RTW89_DBG_PORT_SEL_PLE_QUEMGN_QEMPTY: info = &dbg_port_ple_quemgn_qempty; - seq_puts(m, "Enable ple quemgn qempty dump.\n"); + p += scnprintf(p, end - p, "Enable ple quemgn qempty dump.\n"); break; case RTW89_DBG_PORT_SEL_PKTINFO: info = &dbg_port_pktinfo; - seq_puts(m, "Enable pktinfo dump.\n"); + p += scnprintf(p, end - p, "Enable pktinfo dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX0: rtw89_write32_mask(rtwdev, R_AX_DBG_CTRL, @@ -2763,7 +2895,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX6: info = &dbg_port_dspt_hdt_tx6; @@ -2771,7 +2904,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 6); - seq_puts(m, "Enable Dispatcher hdt tx6 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx6 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX7: info = &dbg_port_dspt_hdt_tx7; @@ -2779,7 +2913,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 7); - seq_puts(m, "Enable Dispatcher hdt tx7 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx7 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX8: info = &dbg_port_dspt_hdt_tx8; @@ -2787,7 +2922,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 8); - seq_puts(m, "Enable Dispatcher hdt tx8 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx8 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TX9: case RTW89_DBG_PORT_SEL_DSPT_HDT_TXA: @@ -2799,7 +2935,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher hdt tx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt tx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_TXD: info = &dbg_port_dspt_hdt_txD; @@ -2807,7 +2944,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 0); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 0xD); - seq_puts(m, "Enable Dispatcher hdt txD dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt txD dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX0: info = &dbg_port_dspt_cdt_tx0; @@ -2815,7 +2953,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 0); - seq_puts(m, "Enable Dispatcher cdt tx0 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx0 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX1: info = &dbg_port_dspt_cdt_tx1; @@ -2823,7 +2962,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 1); - seq_puts(m, "Enable Dispatcher cdt tx1 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx1 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX3: info = &dbg_port_dspt_cdt_tx3; @@ -2831,7 +2971,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 3); - seq_puts(m, "Enable Dispatcher cdt tx3 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx3 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX4: info = &dbg_port_dspt_cdt_tx4; @@ -2839,7 +2980,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 4); - seq_puts(m, "Enable Dispatcher cdt tx4 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx4 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX5: case RTW89_DBG_PORT_SEL_DSPT_CDT_TX6: @@ -2851,7 +2993,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TX9: info = &dbg_port_dspt_cdt_tx9; @@ -2859,7 +3002,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 9); - seq_puts(m, "Enable Dispatcher cdt tx9 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx9 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_TXA: case RTW89_DBG_PORT_SEL_DSPT_CDT_TXB: @@ -2870,7 +3014,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 1); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher cdt tx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt tx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX0: info = &dbg_port_dspt_hdt_rx0; @@ -2878,7 +3023,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 0); - seq_puts(m, "Enable Dispatcher hdt rx0 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx0 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX1: case RTW89_DBG_PORT_SEL_DSPT_HDT_RX2: @@ -2888,7 +3034,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, index); - seq_printf(m, "Enable Dispatcher hdt rx%x dump.\n", index); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx%x dump.\n", index); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX3: info = &dbg_port_dspt_hdt_rx3; @@ -2896,7 +3043,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 3); - seq_puts(m, "Enable Dispatcher hdt rx3 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx3 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX4: info = &dbg_port_dspt_hdt_rx4; @@ -2904,7 +3052,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 4); - seq_puts(m, "Enable Dispatcher hdt rx4 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx4 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_HDT_RX5: info = &dbg_port_dspt_hdt_rx5; @@ -2912,7 +3061,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 2); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 5); - seq_puts(m, "Enable Dispatcher hdt rx5 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher hdt rx5 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_0: info = &dbg_port_dspt_cdt_rx_p0_0; @@ -2920,7 +3070,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 3); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 0); - seq_puts(m, "Enable Dispatcher cdt rx part0 0 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt rx part0 0 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0: case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_1: @@ -2929,7 +3080,8 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 3); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 1); - seq_puts(m, "Enable Dispatcher cdt rx part0 1 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt rx part0 1 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P0_2: info = &dbg_port_dspt_cdt_rx_p0_2; @@ -2937,43 +3089,50 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, B_AX_DISPATCHER_INTN_SEL_MASK, 3); rtw89_write16_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_CH_SEL_MASK, 2); - seq_puts(m, "Enable Dispatcher cdt rx part0 2 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt rx part0 2 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_CDT_RX_P1: info = &dbg_port_dspt_cdt_rx_p1; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 3); - seq_puts(m, "Enable Dispatcher cdt rx part1 dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher cdt rx part1 dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_STF_CTRL: info = &dbg_port_dspt_stf_ctrl; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 4); - seq_puts(m, "Enable Dispatcher stf control dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher stf control dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_ADDR_CTRL: info = &dbg_port_dspt_addr_ctrl; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 5); - seq_puts(m, "Enable Dispatcher addr control dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher addr control dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_WDE_INTF: info = &dbg_port_dspt_wde_intf; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 6); - seq_puts(m, "Enable Dispatcher wde interface dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher wde interface dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_PLE_INTF: info = &dbg_port_dspt_ple_intf; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 7); - seq_puts(m, "Enable Dispatcher ple interface dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher ple interface dump.\n"); break; case RTW89_DBG_PORT_SEL_DSPT_FLOW_CTRL: info = &dbg_port_dspt_flow_ctrl; rtw89_write8_mask(rtwdev, info->sel_addr, B_AX_DISPATCHER_INTN_SEL_MASK, 8); - seq_puts(m, "Enable Dispatcher flow control dump.\n"); + p += scnprintf(p, end - p, + "Enable Dispatcher flow control dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_TXDMA: info = &dbg_port_pcie_txdma; @@ -2981,7 +3140,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_TXDMA_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie txdma dump.\n"); + p += scnprintf(p, end - p, "Enable pcie txdma dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_RXDMA: info = &dbg_port_pcie_rxdma; @@ -2989,7 +3148,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_RXDMA_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie rxdma dump.\n"); + p += scnprintf(p, end - p, "Enable pcie rxdma dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_CVT: info = &dbg_port_pcie_cvt; @@ -2997,7 +3156,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_CVT_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie cvt dump.\n"); + p += scnprintf(p, end - p, "Enable pcie cvt dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_CXPL: info = &dbg_port_pcie_cxpl; @@ -3005,7 +3164,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_CXPL_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie cxpl dump.\n"); + p += scnprintf(p, end - p, "Enable pcie cxpl dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_IO: info = &dbg_port_pcie_io; @@ -3013,7 +3172,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_IO_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie io dump.\n"); + p += scnprintf(p, end - p, "Enable pcie io dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_MISC: info = &dbg_port_pcie_misc; @@ -3021,7 +3180,7 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL0); val32 = u32_replace_bits(val32, PCIE_MISC_DBG_SEL, B_AX_DBG_SEL1); rtw89_write32(rtwdev, R_AX_DBG_CTRL, val32); - seq_puts(m, "Enable pcie misc dump.\n"); + p += scnprintf(p, end - p, "Enable pcie misc dump.\n"); break; case RTW89_DBG_PORT_SEL_PCIE_MISC2: info = &dbg_port_pcie_misc2; @@ -3029,14 +3188,16 @@ rtw89_debug_mac_dbg_port_sel(struct seq_file *m, val16 = u16_replace_bits(val16, PCIE_MISC2_DBG_SEL, B_AX_PCIE_DBG_SEL_MASK); rtw89_write16(rtwdev, R_AX_PCIE_DBG_CTRL, val16); - seq_puts(m, "Enable pcie misc2 dump.\n"); + p += scnprintf(p, end - p, "Enable pcie misc2 dump.\n"); break; default: - seq_puts(m, "Dbg port select err\n"); - return NULL; + p += scnprintf(p, end - p, "Dbg port select err\n"); + break; } - return info; + *ppinfo = info; + + return p - buf; } static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel) @@ -3070,23 +3231,25 @@ static bool is_dbg_port_valid(struct rtw89_dev *rtwdev, u32 sel) } static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, - struct seq_file *m, u32 sel) + char *buf, size_t bufsz, u32 sel) { - const struct rtw89_mac_dbg_port_info *info; - u8 val8; - u16 val16; + const struct rtw89_mac_dbg_port_info *info = NULL; + char *p = buf, *end = buf + bufsz; u32 val32; + u16 val16; + u8 val8; u32 i; - info = rtw89_debug_mac_dbg_port_sel(m, rtwdev, sel); + p += rtw89_debug_mac_dbg_port_sel(rtwdev, p, end - p, sel, &info); + if (!info) { rtw89_err(rtwdev, "failed to select debug port %d\n", sel); - return -EINVAL; + goto out; } #define case_DBG_SEL(__sel) \ case RTW89_DBG_PORT_SEL_##__sel: \ - seq_puts(m, "Dump debug port " #__sel ":\n"); \ + p += scnprintf(p, end - p, "Dump debug port " #__sel ":\n"); \ break switch (sel) { @@ -3182,8 +3345,8 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, #undef case_DBG_SEL - seq_printf(m, "Sel addr = 0x%X\n", info->sel_addr); - seq_printf(m, "Read addr = 0x%X\n", info->rd_addr); + p += scnprintf(p, end - p, "Sel addr = 0x%X\n", info->sel_addr); + p += scnprintf(p, end - p, "Read addr = 0x%X\n", info->rd_addr); for (i = info->srt; i <= info->end; i++) { switch (info->sel_byte) { @@ -3191,17 +3354,17 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, default: rtw89_write8_mask(rtwdev, info->sel_addr, info->sel_msk, i); - seq_printf(m, "0x%02X: ", i); + p += scnprintf(p, end - p, "0x%02X: ", i); break; case 2: rtw89_write16_mask(rtwdev, info->sel_addr, info->sel_msk, i); - seq_printf(m, "0x%04X: ", i); + p += scnprintf(p, end - p, "0x%04X: ", i); break; case 4: rtw89_write32_mask(rtwdev, info->sel_addr, info->sel_msk, i); - seq_printf(m, "0x%04X: ", i); + p += scnprintf(p, end - p, "0x%04X: ", i); break; } @@ -3212,77 +3375,75 @@ static int rtw89_debug_mac_dbg_port_dump(struct rtw89_dev *rtwdev, default: val8 = rtw89_read8_mask(rtwdev, info->rd_addr, info->rd_msk); - seq_printf(m, "0x%02X\n", val8); + p += scnprintf(p, end - p, "0x%02X\n", val8); break; case 2: val16 = rtw89_read16_mask(rtwdev, info->rd_addr, info->rd_msk); - seq_printf(m, "0x%04X\n", val16); + p += scnprintf(p, end - p, "0x%04X\n", val16); break; case 4: val32 = rtw89_read32_mask(rtwdev, info->rd_addr, info->rd_msk); - seq_printf(m, "0x%08X\n", val32); + p += scnprintf(p, end - p, "0x%08X\n", val32); break; } } - return 0; +out: + return p - buf; } static int rtw89_debug_mac_dump_dbg_port(struct rtw89_dev *rtwdev, - struct seq_file *m) + char *buf, size_t bufsz) { + char *p = buf, *end = buf + bufsz; + ssize_t n; u32 sel; - int ret = 0; for (sel = RTW89_DBG_PORT_SEL_PTCL_C0; sel < RTW89_DBG_PORT_SEL_LAST; sel++) { if (!is_dbg_port_valid(rtwdev, sel)) continue; - ret = rtw89_debug_mac_dbg_port_dump(rtwdev, m, sel); - if (ret) { + n = rtw89_debug_mac_dbg_port_dump(rtwdev, p, end - p, sel); + if (n < 0) { rtw89_err(rtwdev, "failed to dump debug port %d\n", sel); break; } + p += n; } - return ret; + return p - buf; } -static int -rtw89_debug_priv_mac_dbg_port_dump_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_mac_dbg_port_dump_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *p = buf, *end = buf + bufsz; if (debugfs_priv->dbgpkg_en.ss_dbg) - rtw89_debug_mac_dump_ss_dbg(rtwdev, m); + p += rtw89_debug_mac_dump_ss_dbg(rtwdev, p, end - p); if (debugfs_priv->dbgpkg_en.dle_dbg) - rtw89_debug_mac_dump_dle_dbg(rtwdev, m); + p += rtw89_debug_mac_dump_dle_dbg(rtwdev, p, end - p); if (debugfs_priv->dbgpkg_en.dmac_dbg) - rtw89_debug_mac_dump_dmac_dbg(rtwdev, m); + p += rtw89_debug_mac_dump_dmac_dbg(rtwdev, p, end - p); if (debugfs_priv->dbgpkg_en.cmac_dbg) - rtw89_debug_mac_dump_cmac_dbg(rtwdev, m); + p += rtw89_debug_mac_dump_cmac_dbg(rtwdev, p, end - p); if (debugfs_priv->dbgpkg_en.dbg_port) - rtw89_debug_mac_dump_dbg_port(rtwdev, m); + p += rtw89_debug_mac_dump_dbg_port(rtwdev, p, end - p); - return 0; + return p - buf; }; -static u8 *rtw89_hex2bin_user(struct rtw89_dev *rtwdev, - const char __user *user_buf, size_t count) +static u8 *rtw89_hex2bin(struct rtw89_dev *rtwdev, const char *buf, size_t count) { - char *buf; u8 *bin; int num; int err = 0; - buf = memdup_user(user_buf, count); - if (IS_ERR(buf)) - return buf; - num = count / 2; bin = kmalloc(num, GFP_KERNEL); if (!bin) { @@ -3297,22 +3458,18 @@ static u8 *rtw89_hex2bin_user(struct rtw89_dev *rtwdev, } out: - kfree(buf); - return err ? ERR_PTR(err) : bin; } -static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_send_h2c_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; u8 *h2c; int ret; u16 h2c_len = count / 2; - h2c = rtw89_hex2bin_user(rtwdev, user_buf, count); + h2c = rtw89_hex2bin(rtwdev, buf, count); if (IS_ERR(h2c)) return -EFAULT; @@ -3323,34 +3480,36 @@ static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp, return ret ? ret : count; } -static int -rtw89_debug_priv_early_h2c_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_early_h2c_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_early_h2c *early_h2c; + char *p = buf, *end = buf + bufsz; int seq = 0; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); + list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) - seq_printf(m, "%d: %*ph\n", ++seq, early_h2c->h2c_len, early_h2c->h2c); - mutex_unlock(&rtwdev->mutex); + p += scnprintf(p, end - p, "%d: %*ph\n", ++seq, + early_h2c->h2c_len, early_h2c->h2c); - return 0; + return p - buf; } static ssize_t -rtw89_debug_priv_early_h2c_set(struct file *filp, const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_early_h2c_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_early_h2c *early_h2c; u8 *h2c; u16 h2c_len = count / 2; - h2c = rtw89_hex2bin_user(rtwdev, user_buf, count); + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + h2c = rtw89_hex2bin(rtwdev, buf, count); if (IS_ERR(h2c)) return -EFAULT; @@ -3369,9 +3528,7 @@ rtw89_debug_priv_early_h2c_set(struct file *filp, const char __user *user_buf, early_h2c->h2c = h2c; early_h2c->h2c_len = h2c_len; - mutex_lock(&rtwdev->mutex); list_add_tail(&early_h2c->list, &rtwdev->early_h2c_list); - mutex_unlock(&rtwdev->mutex); out: return count; @@ -3404,15 +3561,16 @@ static int rtw89_dbg_trigger_ctrl_error(struct rtw89_dev *rtwdev) return 0; } -static int -rtw89_debug_priv_fw_crash_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_fw_crash_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; + char *p = buf, *end = buf + bufsz; - seq_printf(m, "%d\n", - test_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags)); - return 0; + p += scnprintf(p, end - p, "%d\n", + test_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags)); + return p - buf; } enum rtw89_dbg_crash_simulation_type { @@ -3421,17 +3579,17 @@ enum rtw89_dbg_crash_simulation_type { }; static ssize_t -rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_fw_crash_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; int (*sim)(struct rtw89_dev *rtwdev); u8 crash_type; int ret; - ret = kstrtou8_from_user(user_buf, count, 0, &crash_type); + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + ret = kstrtou8(buf, 0, &crash_type); if (ret) return -EINVAL; @@ -3448,10 +3606,8 @@ rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf, return -EINVAL; } - mutex_lock(&rtwdev->mutex); set_bit(RTW89_FLAG_CRASH_SIMULATING, rtwdev->flags); ret = sim(rtwdev); - mutex_unlock(&rtwdev->mutex); if (ret) return ret; @@ -3459,27 +3615,22 @@ rtw89_debug_priv_fw_crash_set(struct file *filp, const char __user *user_buf, return count; } -static int rtw89_debug_priv_btc_info_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_btc_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - - rtw89_btc_dump_info(rtwdev, m); - - return 0; + return rtw89_btc_dump_info(rtwdev, buf, bufsz); } -static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_btc_manual_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_btc *btc = &rtwdev->btc; const struct rtw89_btc_ver *ver = btc->ver; int ret; - ret = kstrtobool_from_user(user_buf, count, &btc->manual_ctrl); + ret = kstrtobool(buf, &btc->manual_ctrl); if (ret) return ret; @@ -3491,31 +3642,29 @@ static ssize_t rtw89_debug_priv_btc_manual_set(struct file *filp, return count; } -static ssize_t rtw89_debug_priv_fw_log_manual_set(struct file *filp, - const char __user *user_buf, - size_t count, loff_t *loff) +static ssize_t rtw89_debug_priv_fw_log_manual_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_fw_log *log = &rtwdev->fw.log; bool fw_log_manual; - if (kstrtobool_from_user(user_buf, count, &fw_log_manual)) + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (kstrtobool(buf, &fw_log_manual)) goto out; - mutex_lock(&rtwdev->mutex); log->enable = fw_log_manual; if (log->enable) rtw89_fw_log_prepare(rtwdev); rtw89_fw_h2c_fw_log(rtwdev, fw_log_manual); - mutex_unlock(&rtwdev->mutex); out: return count; } -static void rtw89_sta_link_info_get_iter(struct seq_file *m, - struct rtw89_dev *rtwdev, - struct rtw89_sta_link *rtwsta_link) +static int rtw89_sta_link_info_get_iter(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + struct rtw89_sta_link *rtwsta_link) { static const char * const he_gi_str[] = { [NL80211_RATE_INFO_HE_GI_0_8] = "0.8", @@ -3533,6 +3682,7 @@ static void rtw89_sta_link_info_get_iter(struct seq_file *m, u8 ant_num = hal->ant_diversity ? 2 : rtwdev->chip->rf_path_num; bool ant_asterisk = hal->tx_path_diversity || hal->ant_diversity; struct ieee80211_link_sta *link_sta; + char *p = buf, *end = buf + bufsz; u8 evm_min, evm_max, evm_1ss; u16 max_rc_amsdu_len; u8 rssi; @@ -3546,107 +3696,136 @@ static void rtw89_sta_link_info_get_iter(struct seq_file *m, rcu_read_unlock(); - seq_printf(m, "TX rate [%u, %u]: ", rtwsta_link->mac_id, rtwsta_link->link_id); + p += scnprintf(p, end - p, "TX rate [%u, %u]: ", rtwsta_link->mac_id, + rtwsta_link->link_id); if (rate->flags & RATE_INFO_FLAGS_MCS) - seq_printf(m, "HT MCS-%d%s", rate->mcs, - rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); + p += scnprintf(p, end - p, "HT MCS-%d%s", rate->mcs, + rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); else if (rate->flags & RATE_INFO_FLAGS_VHT_MCS) - seq_printf(m, "VHT %dSS MCS-%d%s", rate->nss, rate->mcs, - rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); + p += scnprintf(p, end - p, "VHT %dSS MCS-%d%s", rate->nss, + rate->mcs, + rate->flags & RATE_INFO_FLAGS_SHORT_GI ? " SGI" : ""); else if (rate->flags & RATE_INFO_FLAGS_HE_MCS) - seq_printf(m, "HE %dSS MCS-%d GI:%s", rate->nss, rate->mcs, - rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? - he_gi_str[rate->he_gi] : "N/A"); + p += scnprintf(p, end - p, "HE %dSS MCS-%d GI:%s", rate->nss, + rate->mcs, + rate->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? + he_gi_str[rate->he_gi] : "N/A"); else if (rate->flags & RATE_INFO_FLAGS_EHT_MCS) - seq_printf(m, "EHT %dSS MCS-%d GI:%s", rate->nss, rate->mcs, - rate->eht_gi < ARRAY_SIZE(eht_gi_str) ? - eht_gi_str[rate->eht_gi] : "N/A"); + p += scnprintf(p, end - p, "EHT %dSS MCS-%d GI:%s", rate->nss, + rate->mcs, + rate->eht_gi < ARRAY_SIZE(eht_gi_str) ? + eht_gi_str[rate->eht_gi] : "N/A"); else - seq_printf(m, "Legacy %d", rate->legacy); - seq_printf(m, "%s", rtwsta_link->ra_report.might_fallback_legacy ? " FB_G" : ""); - seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(rate->bw)); - seq_printf(m, " (hw_rate=0x%x)", rtwsta_link->ra_report.hw_rate); - seq_printf(m, " ==> agg_wait=%d (%d)\n", rtwsta_link->max_agg_wait, - max_rc_amsdu_len); - - seq_printf(m, "RX rate [%u, %u]: ", rtwsta_link->mac_id, rtwsta_link->link_id); + p += scnprintf(p, end - p, "Legacy %d", rate->legacy); + p += scnprintf(p, end - p, "%s", + rtwsta_link->ra_report.might_fallback_legacy ? " FB_G" : ""); + p += scnprintf(p, end - p, " BW:%u", + rtw89_rate_info_bw_to_mhz(rate->bw)); + p += scnprintf(p, end - p, " (hw_rate=0x%x)", + rtwsta_link->ra_report.hw_rate); + p += scnprintf(p, end - p, " ==> agg_wait=%d (%d)\n", + rtwsta_link->max_agg_wait, + max_rc_amsdu_len); + + p += scnprintf(p, end - p, "RX rate [%u, %u]: ", rtwsta_link->mac_id, + rtwsta_link->link_id); switch (status->encoding) { case RX_ENC_LEGACY: - seq_printf(m, "Legacy %d", status->rate_idx + - (status->band != NL80211_BAND_2GHZ ? 4 : 0)); + p += scnprintf(p, end - p, "Legacy %d", status->rate_idx + + (status->band != NL80211_BAND_2GHZ ? 4 : 0)); break; case RX_ENC_HT: - seq_printf(m, "HT MCS-%d%s", status->rate_idx, - status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); + p += scnprintf(p, end - p, "HT MCS-%d%s", status->rate_idx, + status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); break; case RX_ENC_VHT: - seq_printf(m, "VHT %dSS MCS-%d%s", status->nss, status->rate_idx, - status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); + p += scnprintf(p, end - p, "VHT %dSS MCS-%d%s", status->nss, + status->rate_idx, + status->enc_flags & RX_ENC_FLAG_SHORT_GI ? " SGI" : ""); break; case RX_ENC_HE: - seq_printf(m, "HE %dSS MCS-%d GI:%s", status->nss, status->rate_idx, - status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? - he_gi_str[status->he_gi] : "N/A"); + p += scnprintf(p, end - p, "HE %dSS MCS-%d GI:%s", + status->nss, status->rate_idx, + status->he_gi <= NL80211_RATE_INFO_HE_GI_3_2 ? + he_gi_str[status->he_gi] : "N/A"); break; case RX_ENC_EHT: - seq_printf(m, "EHT %dSS MCS-%d GI:%s", status->nss, status->rate_idx, - status->eht.gi < ARRAY_SIZE(eht_gi_str) ? - eht_gi_str[status->eht.gi] : "N/A"); + p += scnprintf(p, end - p, "EHT %dSS MCS-%d GI:%s", + status->nss, status->rate_idx, + status->eht.gi < ARRAY_SIZE(eht_gi_str) ? + eht_gi_str[status->eht.gi] : "N/A"); break; } - seq_printf(m, " BW:%u", rtw89_rate_info_bw_to_mhz(status->bw)); - seq_printf(m, " (hw_rate=0x%x)\n", rtwsta_link->rx_hw_rate); + p += scnprintf(p, end - p, " BW:%u", + rtw89_rate_info_bw_to_mhz(status->bw)); + p += scnprintf(p, end - p, " (hw_rate=0x%x)\n", + rtwsta_link->rx_hw_rate); rssi = ewma_rssi_read(&rtwsta_link->avg_rssi); - seq_printf(m, "RSSI: %d dBm (raw=%d, prev=%d) [", - RTW89_RSSI_RAW_TO_DBM(rssi), rssi, rtwsta_link->prev_rssi); + p += scnprintf(p, end - p, "RSSI: %d dBm (raw=%d, prev=%d) [", + RTW89_RSSI_RAW_TO_DBM(rssi), rssi, + rtwsta_link->prev_rssi); for (i = 0; i < ant_num; i++) { rssi = ewma_rssi_read(&rtwsta_link->rssi[i]); - seq_printf(m, "%d%s%s", RTW89_RSSI_RAW_TO_DBM(rssi), - ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "", - i + 1 == ant_num ? "" : ", "); + p += scnprintf(p, end - p, "%d%s%s", + RTW89_RSSI_RAW_TO_DBM(rssi), + ant_asterisk && (hal->antenna_tx & BIT(i)) ? "*" : "", + i + 1 == ant_num ? "" : ", "); } - seq_puts(m, "]\n"); + p += scnprintf(p, end - p, "]\n"); evm_1ss = ewma_evm_read(&rtwsta_link->evm_1ss); - seq_printf(m, "EVM: [%2u.%02u, ", evm_1ss >> 2, (evm_1ss & 0x3) * 25); + p += scnprintf(p, end - p, "EVM: [%2u.%02u, ", evm_1ss >> 2, + (evm_1ss & 0x3) * 25); for (i = 0; i < (hal->ant_diversity ? 2 : 1); i++) { evm_min = ewma_evm_read(&rtwsta_link->evm_min[i]); evm_max = ewma_evm_read(&rtwsta_link->evm_max[i]); - seq_printf(m, "%s(%2u.%02u, %2u.%02u)", i == 0 ? "" : " ", - evm_min >> 2, (evm_min & 0x3) * 25, - evm_max >> 2, (evm_max & 0x3) * 25); + p += scnprintf(p, end - p, "%s(%2u.%02u, %2u.%02u)", + i == 0 ? "" : " ", + evm_min >> 2, (evm_min & 0x3) * 25, + evm_max >> 2, (evm_max & 0x3) * 25); } - seq_puts(m, "]\t"); + p += scnprintf(p, end - p, "]\t"); snr = ewma_snr_read(&rtwsta_link->avg_snr); - seq_printf(m, "SNR: %u\n", snr); + p += scnprintf(p, end - p, "SNR: %u\n", snr); + + return p - buf; } static void rtw89_sta_info_get_iter(void *data, struct ieee80211_sta *sta) { - struct seq_file *m = (struct seq_file *)data; + struct rtw89_debugfs_iter_data *iter_data = + (struct rtw89_debugfs_iter_data *)data; struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); struct rtw89_dev *rtwdev = rtwsta->rtwdev; struct rtw89_sta_link *rtwsta_link; + size_t bufsz = iter_data->bufsz; + char *buf = iter_data->buf; + char *p = buf, *end = buf + bufsz; unsigned int link_id; rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id) - rtw89_sta_link_info_get_iter(m, rtwdev, rtwsta_link); + p += rtw89_sta_link_info_get_iter(rtwdev, p, end - p, rtwsta_link); + + rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf); } -static void -rtw89_debug_append_rx_rate(struct seq_file *m, struct rtw89_pkt_stat *pkt_stat, +static int +rtw89_debug_append_rx_rate(char *buf, size_t bufsz, struct rtw89_pkt_stat *pkt_stat, enum rtw89_hw_rate first_rate, int len) { + char *p = buf, *end = buf + bufsz; int i; for (i = 0; i < len; i++) - seq_printf(m, "%s%u", i == 0 ? "" : ", ", - pkt_stat->rx_rate_cnt[first_rate + i]); + p += scnprintf(p, end - p, "%s%u", i == 0 ? "" : ", ", + pkt_stat->rx_rate_cnt[first_rate + i]); + + return p - buf; } #define FIRST_RATE_SAME(rate) {RTW89_HW_RATE_ ## rate, RTW89_HW_RATE_ ## rate} @@ -3671,34 +3850,40 @@ static const struct rtw89_rx_rate_cnt_info { {FIRST_RATE_GEV1(EHT_NSS2_MCS0), 14, 0, "EHT 2SS:"}, }; -static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_phy_info_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_traffic_stats *stats = &rtwdev->stats; struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.last_pkt_stat; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_debugfs_iter_data iter_data; const struct rtw89_rx_rate_cnt_info *info; struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; enum rtw89_hw_rate first_rate; u8 rssi; int i; rssi = ewma_rssi_read(&rtwdev->phystat.bcn_rssi); - seq_printf(m, "TP TX: %u [%u] Mbps (lv: %d", - stats->tx_throughput, stats->tx_throughput_raw, stats->tx_tfc_lv); + p += scnprintf(p, end - p, "TP TX: %u [%u] Mbps (lv: %d", + stats->tx_throughput, stats->tx_throughput_raw, + stats->tx_tfc_lv); if (hal->thermal_prot_lv) - seq_printf(m, ", duty: %d%%", - 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); - seq_printf(m, "), RX: %u [%u] Mbps (lv: %d)\n", - stats->rx_throughput, stats->rx_throughput_raw, stats->rx_tfc_lv); - seq_printf(m, "Beacon: %u (%d dBm), TF: %u\n", pkt_stat->beacon_nr, - RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); - seq_printf(m, "Avg packet length: TX=%u, RX=%u\n", stats->tx_avg_len, - stats->rx_avg_len); - - seq_puts(m, "RX count:\n"); + p += scnprintf(p, end - p, ", duty: %d%%", + 100 - hal->thermal_prot_lv * RTW89_THERMAL_PROT_STEP); + p += scnprintf(p, end - p, "), RX: %u [%u] Mbps (lv: %d)\n", + stats->rx_throughput, stats->rx_throughput_raw, + stats->rx_tfc_lv); + p += scnprintf(p, end - p, "Beacon: %u (%d dBm), TF: %u\n", + pkt_stat->beacon_nr, + RTW89_RSSI_RAW_TO_DBM(rssi), stats->rx_tf_periodic); + p += scnprintf(p, end - p, "Avg packet length: TX=%u, RX=%u\n", + stats->tx_avg_len, + stats->rx_avg_len); + + p += scnprintf(p, end - p, "RX count:\n"); for (i = 0; i < ARRAY_SIZE(rtw89_rx_rate_cnt_infos); i++) { info = &rtw89_rx_rate_cnt_infos[i]; @@ -3706,189 +3891,279 @@ static int rtw89_debug_priv_phy_info_get(struct seq_file *m, void *v) if (first_rate >= RTW89_HW_RATE_NR) continue; - seq_printf(m, "%10s [", info->rate_mode); - rtw89_debug_append_rx_rate(m, pkt_stat, - first_rate, info->len); + p += scnprintf(p, end - p, "%10s [", info->rate_mode); + p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, + first_rate, info->len); if (info->ext) { - seq_puts(m, "]["); - rtw89_debug_append_rx_rate(m, pkt_stat, - first_rate + info->len, info->ext); + p += scnprintf(p, end - p, "]["); + p += rtw89_debug_append_rx_rate(p, end - p, pkt_stat, + first_rate + info->len, info->ext); } - seq_puts(m, "]\n"); + p += scnprintf(p, end - p, "]\n"); } - ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, m); + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_info_get_iter, &iter_data); + p += iter_data.written_sz; - return 0; + return p - buf; } -static void rtw89_dump_addr_cam(struct seq_file *m, - struct rtw89_dev *rtwdev, - struct rtw89_addr_cam_entry *addr_cam) +static int rtw89_dump_addr_cam(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + struct rtw89_addr_cam_entry *addr_cam) { struct rtw89_cam_info *cam_info = &rtwdev->cam_info; const struct rtw89_sec_cam_entry *sec_entry; + char *p = buf, *end = buf + bufsz; u8 sec_cam_idx; int i; - seq_printf(m, "\taddr_cam_idx=%u\n", addr_cam->addr_cam_idx); - seq_printf(m, "\t-> bssid_cam_idx=%u\n", addr_cam->bssid_cam_idx); - seq_printf(m, "\tsec_cam_bitmap=%*ph\n", (int)sizeof(addr_cam->sec_cam_map), - addr_cam->sec_cam_map); + p += scnprintf(p, end - p, "\taddr_cam_idx=%u\n", + addr_cam->addr_cam_idx); + p += scnprintf(p, end - p, "\t-> bssid_cam_idx=%u\n", + addr_cam->bssid_cam_idx); + p += scnprintf(p, end - p, "\tsec_cam_bitmap=%*ph\n", + (int)sizeof(addr_cam->sec_cam_map), + addr_cam->sec_cam_map); for_each_set_bit(i, addr_cam->sec_cam_map, RTW89_SEC_CAM_IN_ADDR_CAM) { sec_cam_idx = addr_cam->sec_ent[i]; sec_entry = cam_info->sec_entries[sec_cam_idx]; if (!sec_entry) continue; - seq_printf(m, "\tsec[%d]: sec_cam_idx %u", i, sec_entry->sec_cam_idx); + p += scnprintf(p, end - p, "\tsec[%d]: sec_cam_idx %u", i, + sec_entry->sec_cam_idx); if (sec_entry->ext_key) - seq_printf(m, ", %u", sec_entry->sec_cam_idx + 1); - seq_puts(m, "\n"); + p += scnprintf(p, end - p, ", %u", + sec_entry->sec_cam_idx + 1); + p += scnprintf(p, end - p, "\n"); } + + return p - buf; } -__printf(3, 4) -static void rtw89_dump_pkt_offload(struct seq_file *m, struct list_head *pkt_list, - const char *fmt, ...) +__printf(4, 5) +static int rtw89_dump_pkt_offload(char *buf, size_t bufsz, struct list_head *pkt_list, + const char *fmt, ...) { + char *p = buf, *end = buf + bufsz; struct rtw89_pktofld_info *info; struct va_format vaf; va_list args; if (list_empty(pkt_list)) - return; + return 0; va_start(args, fmt); vaf.va = &args; vaf.fmt = fmt; - seq_printf(m, "%pV", &vaf); + p += scnprintf(p, end - p, "%pV", &vaf); va_end(args); list_for_each_entry(info, pkt_list, list) - seq_printf(m, "%d ", info->id); + p += scnprintf(p, end - p, "%d ", info->id); + + p += scnprintf(p, end - p, "\n"); - seq_puts(m, "\n"); + return p - buf; } -static void rtw89_vif_link_ids_get(struct seq_file *m, u8 *mac, - struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link) +static int rtw89_vif_link_ids_get(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, u8 *mac, + struct rtw89_vif_link *rtwvif_link, + bool designated) { struct rtw89_bssid_cam_entry *bssid_cam = &rtwvif_link->bssid_cam; - - seq_printf(m, " [%u] %pM\n", rtwvif_link->mac_id, rtwvif_link->mac_addr); - seq_printf(m, "\tlink_id=%u\n", rtwvif_link->link_id); - seq_printf(m, "\tbssid_cam_idx=%u\n", bssid_cam->bssid_cam_idx); - rtw89_dump_addr_cam(m, rtwdev, &rtwvif_link->addr_cam); - rtw89_dump_pkt_offload(m, &rtwvif_link->general_pkt_list, - "\tpkt_ofld[GENERAL]: "); + char *p = buf, *end = buf + bufsz; + + p += scnprintf(p, end - p, " [%u] %pM\n", rtwvif_link->mac_id, + rtwvif_link->mac_addr); + p += scnprintf(p, end - p, "\tlink_id=%u%s\n", rtwvif_link->link_id, + designated ? " (*)" : ""); + p += scnprintf(p, end - p, "\tbssid_cam_idx=%u\n", + bssid_cam->bssid_cam_idx); + p += rtw89_dump_addr_cam(rtwdev, p, end - p, &rtwvif_link->addr_cam); + p += rtw89_dump_pkt_offload(p, end - p, &rtwvif_link->general_pkt_list, + "\tpkt_ofld[GENERAL]: "); + + return p - buf; } static void rtw89_vif_ids_get_iter(void *data, u8 *mac, struct ieee80211_vif *vif) { - struct seq_file *m = (struct seq_file *)data; + struct rtw89_debugfs_iter_data *iter_data = + (struct rtw89_debugfs_iter_data *)data; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_dev *rtwdev = rtwvif->rtwdev; + struct rtw89_vif_link *designated_link; struct rtw89_vif_link *rtwvif_link; + size_t bufsz = iter_data->bufsz; + char *buf = iter_data->buf; + char *p = buf, *end = buf + bufsz; unsigned int link_id; - seq_printf(m, "VIF %pM\n", rtwvif->mac_addr); + designated_link = rtw89_get_designated_link(rtwvif); + + p += scnprintf(p, end - p, "VIF %pM\n", rtwvif->mac_addr); rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) - rtw89_vif_link_ids_get(m, mac, rtwdev, rtwvif_link); + p += rtw89_vif_link_ids_get(rtwdev, p, end - p, mac, rtwvif_link, + rtwvif_link == designated_link); + + rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf); } -static void rtw89_dump_ba_cam(struct seq_file *m, struct rtw89_dev *rtwdev, - struct rtw89_sta_link *rtwsta_link) +static int rtw89_dump_ba_cam(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + struct rtw89_sta_link *rtwsta_link) { struct rtw89_ba_cam_entry *entry; + char *p = buf, *end = buf + bufsz; bool first = true; list_for_each_entry(entry, &rtwsta_link->ba_cam_list, list) { if (first) { - seq_puts(m, "\tba_cam "); + p += scnprintf(p, end - p, "\tba_cam "); first = false; } else { - seq_puts(m, ", "); + p += scnprintf(p, end - p, ", "); } - seq_printf(m, "tid[%u]=%d", entry->tid, - (int)(entry - rtwdev->cam_info.ba_cam_entry)); + p += scnprintf(p, end - p, "tid[%u]=%d", entry->tid, + (int)(entry - rtwdev->cam_info.ba_cam_entry)); } - seq_puts(m, "\n"); + p += scnprintf(p, end - p, "\n"); + + return p - buf; } -static void rtw89_sta_link_ids_get(struct seq_file *m, - struct rtw89_dev *rtwdev, - struct rtw89_sta_link *rtwsta_link) +static int rtw89_sta_link_ids_get(struct rtw89_dev *rtwdev, + char *buf, size_t bufsz, + struct rtw89_sta_link *rtwsta_link, + bool designated) { struct ieee80211_link_sta *link_sta; + char *p = buf, *end = buf + bufsz; rcu_read_lock(); link_sta = rtw89_sta_rcu_dereference_link(rtwsta_link, true); - seq_printf(m, " [%u] %pM\n", rtwsta_link->mac_id, link_sta->addr); + p += scnprintf(p, end - p, " [%u] %pM\n", rtwsta_link->mac_id, + link_sta->addr); rcu_read_unlock(); - seq_printf(m, "\tlink_id=%u\n", rtwsta_link->link_id); - rtw89_dump_addr_cam(m, rtwdev, &rtwsta_link->addr_cam); - rtw89_dump_ba_cam(m, rtwdev, rtwsta_link); + p += scnprintf(p, end - p, "\tlink_id=%u%s\n", rtwsta_link->link_id, + designated ? " (*)" : ""); + p += rtw89_dump_addr_cam(rtwdev, p, end - p, &rtwsta_link->addr_cam); + p += rtw89_dump_ba_cam(rtwdev, p, end - p, rtwsta_link); + + return p - buf; } static void rtw89_sta_ids_get_iter(void *data, struct ieee80211_sta *sta) { - struct seq_file *m = (struct seq_file *)data; + struct rtw89_debugfs_iter_data *iter_data = + (struct rtw89_debugfs_iter_data *)data; struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); struct rtw89_dev *rtwdev = rtwsta->rtwdev; + struct rtw89_sta_link *designated_link; struct rtw89_sta_link *rtwsta_link; + size_t bufsz = iter_data->bufsz; + char *buf = iter_data->buf; + char *p = buf, *end = buf + bufsz; unsigned int link_id; - seq_printf(m, "STA %pM %s\n", sta->addr, sta->tdls ? "(TDLS)" : ""); + designated_link = rtw89_get_designated_link(rtwsta); + + p += scnprintf(p, end - p, "STA %pM %s\n", sta->addr, + sta->tdls ? "(TDLS)" : ""); rtw89_sta_for_each_link(rtwsta, rtwsta_link, link_id) - rtw89_sta_link_ids_get(m, rtwdev, rtwsta_link); + p += rtw89_sta_link_ids_get(rtwdev, p, end - p, rtwsta_link, + rtwsta_link == designated_link); + + rtw89_debugfs_iter_data_next(iter_data, p, end - p, p - buf); } -static int rtw89_debug_priv_stations_get(struct seq_file *m, void *v) +static ssize_t rtw89_debug_priv_stations_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; struct rtw89_cam_info *cam_info = &rtwdev->cam_info; + struct rtw89_debugfs_iter_data iter_data; + char *p = buf, *end = buf + bufsz; u8 idx; - mutex_lock(&rtwdev->mutex); - - seq_puts(m, "map:\n"); - seq_printf(m, "\tmac_id: %*ph\n", (int)sizeof(rtwdev->mac_id_map), - rtwdev->mac_id_map); - seq_printf(m, "\taddr_cam: %*ph\n", (int)sizeof(cam_info->addr_cam_map), - cam_info->addr_cam_map); - seq_printf(m, "\tbssid_cam: %*ph\n", (int)sizeof(cam_info->bssid_cam_map), - cam_info->bssid_cam_map); - seq_printf(m, "\tsec_cam: %*ph\n", (int)sizeof(cam_info->sec_cam_map), - cam_info->sec_cam_map); - seq_printf(m, "\tba_cam: %*ph\n", (int)sizeof(cam_info->ba_cam_map), - cam_info->ba_cam_map); - seq_printf(m, "\tpkt_ofld: %*ph\n", (int)sizeof(rtwdev->pkt_offload), - rtwdev->pkt_offload); + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + p += scnprintf(p, end - p, "map:\n"); + p += scnprintf(p, end - p, "\tmac_id: %*ph\n", + (int)sizeof(rtwdev->mac_id_map), + rtwdev->mac_id_map); + p += scnprintf(p, end - p, "\taddr_cam: %*ph\n", + (int)sizeof(cam_info->addr_cam_map), + cam_info->addr_cam_map); + p += scnprintf(p, end - p, "\tbssid_cam: %*ph\n", + (int)sizeof(cam_info->bssid_cam_map), + cam_info->bssid_cam_map); + p += scnprintf(p, end - p, "\tsec_cam: %*ph\n", + (int)sizeof(cam_info->sec_cam_map), + cam_info->sec_cam_map); + p += scnprintf(p, end - p, "\tba_cam: %*ph\n", + (int)sizeof(cam_info->ba_cam_map), + cam_info->ba_cam_map); + p += scnprintf(p, end - p, "\tpkt_ofld: %*ph\n", + (int)sizeof(rtwdev->pkt_offload), + rtwdev->pkt_offload); for (idx = NL80211_BAND_2GHZ; idx < NUM_NL80211_BANDS; idx++) { if (!(rtwdev->chip->support_bands & BIT(idx))) continue; - rtw89_dump_pkt_offload(m, &rtwdev->scan_info.pkt_list[idx], - "\t\t[SCAN %u]: ", idx); + p += rtw89_dump_pkt_offload(p, end - p, &rtwdev->scan_info.pkt_list[idx], + "\t\t[SCAN %u]: ", idx); } + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); ieee80211_iterate_active_interfaces_atomic(rtwdev->hw, - IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, m); + IEEE80211_IFACE_ITER_NORMAL, rtw89_vif_ids_get_iter, &iter_data); + p += iter_data.written_sz; + + rtw89_debugfs_iter_data_setup(&iter_data, p, end - p); + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, &iter_data); + p += iter_data.written_sz; - ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_sta_ids_get_iter, m); + return p - buf; +} - mutex_unlock(&rtwdev->mutex); +static void rtw89_debug_disable_dm_cfg_bmap(struct rtw89_dev *rtwdev, u32 new) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u32 old = hal->disabled_dm_bitmap; - return 0; + if (new == old) + return; + + hal->disabled_dm_bitmap = new; + + rtw89_debug(rtwdev, RTW89_DBG_STATE, "Disable DM: 0x%x -> 0x%x\n", old, new); +} + +static void rtw89_debug_disable_dm_set_flag(struct rtw89_dev *rtwdev, u8 flag) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u32 cur = hal->disabled_dm_bitmap; + + rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur | BIT(flag)); +} + +static void rtw89_debug_disable_dm_clr_flag(struct rtw89_dev *rtwdev, u8 flag) +{ + struct rtw89_hal *hal = &rtwdev->hal; + u32 cur = hal->disabled_dm_bitmap; + + rtw89_debug_disable_dm_cfg_bmap(rtwdev, cur & ~BIT(flag)); } #define DM_INFO(type) {RTW89_DM_ ## type, #type} @@ -3899,92 +4174,187 @@ static const struct rtw89_disabled_dm_info { } rtw89_disabled_dm_infos[] = { DM_INFO(DYNAMIC_EDCCA), DM_INFO(THERMAL_PROTECT), + DM_INFO(TAS), + DM_INFO(MLO), }; -static int -rtw89_debug_priv_disable_dm_get(struct seq_file *m, void *v) +static ssize_t +rtw89_debug_priv_disable_dm_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) { - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; const struct rtw89_disabled_dm_info *info; struct rtw89_hal *hal = &rtwdev->hal; + char *p = buf, *end = buf + bufsz; u32 disabled; int i; - seq_printf(m, "Disabled DM: 0x%x\n", hal->disabled_dm_bitmap); + p += scnprintf(p, end - p, "Disabled DM: 0x%x\n", + hal->disabled_dm_bitmap); for (i = 0; i < ARRAY_SIZE(rtw89_disabled_dm_infos); i++) { info = &rtw89_disabled_dm_infos[i]; disabled = BIT(info->type) & hal->disabled_dm_bitmap; - seq_printf(m, "[%d] %s: %c\n", info->type, info->name, - disabled ? 'X' : 'O'); + p += scnprintf(p, end - p, "[%d] %s: %c\n", info->type, + info->name, + disabled ? 'X' : 'O'); } - return 0; + return p - buf; } static ssize_t -rtw89_debug_priv_disable_dm_set(struct file *filp, const char __user *user_buf, - size_t count, loff_t *loff) +rtw89_debug_priv_disable_dm_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) { - struct seq_file *m = (struct seq_file *)filp->private_data; - struct rtw89_debugfs_priv *debugfs_priv = m->private; - struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; - struct rtw89_hal *hal = &rtwdev->hal; u32 conf; int ret; - ret = kstrtou32_from_user(user_buf, count, 0, &conf); + ret = kstrtou32(buf, 0, &conf); if (ret) return -EINVAL; - hal->disabled_dm_bitmap = conf; + rtw89_debug_disable_dm_cfg_bmap(rtwdev, conf); + + return count; +} + +static void rtw89_debug_mlo_mode_set_mlsr(struct rtw89_dev *rtwdev, + unsigned int link_id) +{ + struct ieee80211_vif *vif; + struct rtw89_vif *rtwvif; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + vif = rtwvif_to_vif(rtwvif); + if (!ieee80211_vif_is_mld(vif)) + continue; + + rtw89_core_mlsr_switch(rtwdev, rtwvif, link_id); + } +} + +static ssize_t +rtw89_debug_priv_mlo_mode_get(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + char *buf, size_t bufsz) +{ + bool mlo_dm_dis = rtwdev->hal.disabled_dm_bitmap & BIT(RTW89_DM_MLO); + char *p = buf, *end = buf + bufsz; + struct ieee80211_vif *vif; + struct rtw89_vif *rtwvif; + int count = 0; + + p += scnprintf(p, end - p, "MLD(s) status: (MLO DM: %s)\n", + str_disable_enable(mlo_dm_dis)); + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + vif = rtwvif_to_vif(rtwvif); + if (!ieee80211_vif_is_mld(vif)) + continue; + + p += scnprintf(p, end - p, + "\t#%u: MLO mode %x, valid 0x%x, active 0x%x\n", + count++, rtwvif->mlo_mode, vif->valid_links, + vif->active_links); + } + + if (count == 0) + p += scnprintf(p, end - p, "\t(None)\n"); + + return p - buf; +} + +static ssize_t +rtw89_debug_priv_mlo_mode_set(struct rtw89_dev *rtwdev, + struct rtw89_debugfs_priv *debugfs_priv, + const char *buf, size_t count) +{ + u8 num, mlo_mode; + u32 argv; + + num = sscanf(buf, "%hhx %u", &mlo_mode, &argv); + if (num != 2) + return -EINVAL; + + rtw89_debug_disable_dm_set_flag(rtwdev, RTW89_DM_MLO); + + rtw89_debug(rtwdev, RTW89_DBG_STATE, "Set MLO mode to %x\n", mlo_mode); + + switch (mlo_mode) { + case RTW89_MLO_MODE_MLSR: + rtw89_debug_mlo_mode_set_mlsr(rtwdev, argv); + break; + default: + rtw89_debug(rtwdev, RTW89_DBG_STATE, "Unsupported MLO mode\n"); + rtw89_debug_disable_dm_clr_flag(rtwdev, RTW89_DM_MLO); + + return -EOPNOTSUPP; + } return count; } -#define rtw89_debug_priv_get(name) \ +#define rtw89_debug_priv_get(name, opts...) \ { \ .cb_read = rtw89_debug_priv_ ##name## _get, \ + .opt = { opts }, \ } -#define rtw89_debug_priv_set(name) \ +#define rtw89_debug_priv_set(name, opts...) \ { \ .cb_write = rtw89_debug_priv_ ##name## _set, \ + .opt = { opts }, \ } -#define rtw89_debug_priv_select_and_get(name) \ +#define rtw89_debug_priv_select_and_get(name, opts...) \ { \ .cb_write = rtw89_debug_priv_ ##name## _select, \ .cb_read = rtw89_debug_priv_ ##name## _get, \ + .opt = { opts }, \ } -#define rtw89_debug_priv_set_and_get(name) \ +#define rtw89_debug_priv_set_and_get(name, opts...) \ { \ .cb_write = rtw89_debug_priv_ ##name## _set, \ .cb_read = rtw89_debug_priv_ ##name## _get, \ + .opt = { opts }, \ } +#define RSIZE_8K .rsize = 0x2000 +#define RSIZE_12K .rsize = 0x3000 +#define RSIZE_16K .rsize = 0x4000 +#define RSIZE_20K .rsize = 0x5000 +#define RSIZE_32K .rsize = 0x8000 +#define RSIZE_64K .rsize = 0x10000 +#define RSIZE_128K .rsize = 0x20000 +#define RSIZE_1M .rsize = 0x100000 +#define RLOCK .rlock = 1 +#define WLOCK .wlock = 1 +#define RWLOCK RLOCK, WLOCK + static const struct rtw89_debugfs rtw89_debugfs_templ = { .read_reg = rtw89_debug_priv_select_and_get(read_reg), .write_reg = rtw89_debug_priv_set(write_reg), .read_rf = rtw89_debug_priv_select_and_get(read_rf), .write_rf = rtw89_debug_priv_set(write_rf), - .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump), - .txpwr_table = rtw89_debug_priv_get(txpwr_table), - .mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump), - .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump), - .mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump), + .rf_reg_dump = rtw89_debug_priv_get(rf_reg_dump, RSIZE_8K), + .txpwr_table = rtw89_debug_priv_get(txpwr_table, RSIZE_20K, RLOCK), + .mac_reg_dump = rtw89_debug_priv_select_and_get(mac_reg_dump, RSIZE_128K), + .mac_mem_dump = rtw89_debug_priv_select_and_get(mac_mem_dump, RSIZE_16K, RLOCK), + .mac_dbg_port_dump = rtw89_debug_priv_select_and_get(mac_dbg_port_dump, RSIZE_1M), .send_h2c = rtw89_debug_priv_set(send_h2c), - .early_h2c = rtw89_debug_priv_set_and_get(early_h2c), - .fw_crash = rtw89_debug_priv_set_and_get(fw_crash), - .btc_info = rtw89_debug_priv_get(btc_info), + .early_h2c = rtw89_debug_priv_set_and_get(early_h2c, RWLOCK), + .fw_crash = rtw89_debug_priv_set_and_get(fw_crash, WLOCK), + .btc_info = rtw89_debug_priv_get(btc_info, RSIZE_12K), .btc_manual = rtw89_debug_priv_set(btc_manual), - .fw_log_manual = rtw89_debug_priv_set(fw_log_manual), + .fw_log_manual = rtw89_debug_priv_set(fw_log_manual, WLOCK), .phy_info = rtw89_debug_priv_get(phy_info), - .stations = rtw89_debug_priv_get(stations), - .disable_dm = rtw89_debug_priv_set_and_get(disable_dm), + .stations = rtw89_debug_priv_get(stations, RLOCK), + .disable_dm = rtw89_debug_priv_set_and_get(disable_dm, RWLOCK), + .mlo_mode = rtw89_debug_priv_set_and_get(mlo_mode, RWLOCK), }; #define rtw89_debugfs_add(name, mode, fopname, parent) \ @@ -4029,6 +4399,7 @@ void rtw89_debugfs_add_sec1(struct rtw89_dev *rtwdev, struct dentry *debugfs_top rtw89_debugfs_add_r(phy_info); rtw89_debugfs_add_r(stations); rtw89_debugfs_add_rw(disable_dm); + rtw89_debugfs_add_rw(mlo_mode); } void rtw89_debugfs_init(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/fw.c b/drivers/net/wireless/realtek/rtw89/fw.c index 5d4ad23cc3bd..00b65b2995cf 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.c +++ b/drivers/net/wireless/realtek/rtw89/fw.c @@ -15,6 +15,8 @@ #include "util.h" #include "wow.h" +static bool rtw89_is_any_vif_connected_or_connecting(struct rtw89_dev *rtwdev); + struct rtw89_eapol_2_of_2 { u8 gtkbody[14]; u8 key_des_ver; @@ -38,6 +40,16 @@ struct rtw89_arp_rsp { static const u8 mss_signature[] = {0x4D, 0x53, 0x53, 0x4B, 0x50, 0x4F, 0x4F, 0x4C}; +const struct rtw89_fw_blacklist rtw89_fw_blacklist_default = { + .ver = 0x00, + .list = {0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + }, +}; +EXPORT_SYMBOL(rtw89_fw_blacklist_default); + union rtw89_fw_element_arg { size_t offset; enum rtw89_rf_path rf_path; @@ -314,7 +326,7 @@ static int __parse_formatted_mssc(struct rtw89_dev *rtwdev, if (!sec->secure_boot) goto out; - sb_sel_ver = le32_to_cpu(section_content->sb_sel_ver.v); + sb_sel_ver = get_unaligned_le32(§ion_content->sb_sel_ver.v); if (sb_sel_ver && sb_sel_ver != sec->sb_sel_mgn) goto ignore; @@ -344,6 +356,46 @@ ignore: return 0; } +static int __check_secure_blacklist(struct rtw89_dev *rtwdev, + struct rtw89_fw_bin_info *info, + struct rtw89_fw_hdr_section_info *section_info, + const void *content) +{ + const struct rtw89_fw_blacklist *chip_blacklist = rtwdev->chip->fw_blacklist; + const union rtw89_fw_section_mssc_content *section_content = content; + struct rtw89_fw_secure *sec = &rtwdev->fw.sec; + u8 byte_idx; + u8 bit_mask; + + if (!sec->secure_boot) + return 0; + + if (!info->secure_section_exist || section_info->ignore) + return 0; + + if (!chip_blacklist) { + rtw89_warn(rtwdev, "chip no blacklist for secure firmware\n"); + return -ENOENT; + } + + byte_idx = section_content->blacklist.bit_in_chip_list >> 3; + bit_mask = BIT(section_content->blacklist.bit_in_chip_list & 0x7); + + if (section_content->blacklist.ver > chip_blacklist->ver) { + rtw89_warn(rtwdev, "chip blacklist out of date (%u, %u)\n", + section_content->blacklist.ver, chip_blacklist->ver); + return -EINVAL; + } + + if (chip_blacklist->list[byte_idx] & bit_mask) { + rtw89_warn(rtwdev, "firmware %u in chip blacklist\n", + section_content->blacklist.ver); + return -EPERM; + } + + return 0; +} + static int __parse_security_section(struct rtw89_dev *rtwdev, struct rtw89_fw_bin_info *info, struct rtw89_fw_hdr_section_info *section_info, @@ -364,8 +416,11 @@ static int __parse_security_section(struct rtw89_dev *rtwdev, *mssc_len += section_info->mssc * FWDL_SECURITY_CHKSUM_LEN; if (sec->secure_boot) { - if (sec->mss_idx >= section_info->mssc) + if (sec->mss_idx >= section_info->mssc) { + rtw89_err(rtwdev, "unexpected MSS %d >= %d\n", + sec->mss_idx, section_info->mssc); return -EFAULT; + } section_info->key_addr = content + section_info->len + sec->mss_idx * FWDL_SECURITY_SIGLEN; section_info->key_len = FWDL_SECURITY_SIGLEN; @@ -374,6 +429,9 @@ static int __parse_security_section(struct rtw89_dev *rtwdev, info->secure_section_exist = true; } + ret = __check_secure_blacklist(rtwdev, info, section_info, content); + WARN_ONCE(ret, "Current firmware in blacklist. Please update firmware.\n"); + return 0; } @@ -490,18 +548,61 @@ static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, } static +const struct rtw89_mfw_hdr *rtw89_mfw_get_hdr_ptr(struct rtw89_dev *rtwdev, + const struct firmware *firmware) +{ + const struct rtw89_mfw_hdr *mfw_hdr; + + if (sizeof(*mfw_hdr) > firmware->size) + return NULL; + + mfw_hdr = (const struct rtw89_mfw_hdr *)&firmware->data[0]; + + if (mfw_hdr->sig != RTW89_MFW_SIG) + return NULL; + + return mfw_hdr; +} + +static int rtw89_mfw_validate_hdr(struct rtw89_dev *rtwdev, + const struct firmware *firmware, + const struct rtw89_mfw_hdr *mfw_hdr) +{ + const void *mfw = firmware->data; + u32 mfw_len = firmware->size; + u8 fw_nr = mfw_hdr->fw_nr; + const void *ptr; + + if (fw_nr == 0) { + rtw89_err(rtwdev, "mfw header has no fw entry\n"); + return -ENOENT; + } + + ptr = &mfw_hdr->info[fw_nr]; + + if (ptr > mfw + mfw_len) { + rtw89_err(rtwdev, "mfw header out of address\n"); + return -EFAULT; + } + + return 0; +} + +static int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, struct rtw89_fw_suit *fw_suit, bool nowarn) { struct rtw89_fw_info *fw_info = &rtwdev->fw; const struct firmware *firmware = fw_info->req.firmware; + const struct rtw89_mfw_info *mfw_info = NULL, *tmp; + const struct rtw89_mfw_hdr *mfw_hdr; const u8 *mfw = firmware->data; u32 mfw_len = firmware->size; - const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw; - const struct rtw89_mfw_info *mfw_info = NULL, *tmp; + int ret; int i; - if (mfw_hdr->sig != RTW89_MFW_SIG) { + mfw_hdr = rtw89_mfw_get_hdr_ptr(rtwdev, firmware); + if (!mfw_hdr) { rtw89_debug(rtwdev, RTW89_DBG_FW, "use legacy firmware\n"); /* legacy firmware support normal type only */ if (type != RTW89_FW_NORMAL) @@ -511,6 +612,10 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, return 0; } + ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr); + if (ret) + return ret; + for (i = 0; i < mfw_hdr->fw_nr; i++) { tmp = &mfw_hdr->info[i]; if (tmp->type != type) @@ -540,6 +645,12 @@ int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type, found: fw_suit->data = mfw + le32_to_cpu(mfw_info->shift); fw_suit->size = le32_to_cpu(mfw_info->size); + + if (fw_suit->data + fw_suit->size > mfw + mfw_len) { + rtw89_err(rtwdev, "fw_suit %d out of address\n", type); + return -EFAULT; + } + return 0; } @@ -547,16 +658,21 @@ static u32 rtw89_mfw_get_size(struct rtw89_dev *rtwdev) { struct rtw89_fw_info *fw_info = &rtwdev->fw; const struct firmware *firmware = fw_info->req.firmware; - const struct rtw89_mfw_hdr *mfw_hdr = - (const struct rtw89_mfw_hdr *)firmware->data; const struct rtw89_mfw_info *mfw_info; + const struct rtw89_mfw_hdr *mfw_hdr; u32 size; + int ret; - if (mfw_hdr->sig != RTW89_MFW_SIG) { + mfw_hdr = rtw89_mfw_get_hdr_ptr(rtwdev, firmware); + if (!mfw_hdr) { rtw89_warn(rtwdev, "not mfw format\n"); return 0; } + ret = rtw89_mfw_validate_hdr(rtwdev, firmware, mfw_hdr); + if (ret) + return ret; + mfw_info = &mfw_hdr->info[mfw_hdr->fw_nr - 1]; size = le32_to_cpu(mfw_info->shift) + le32_to_cpu(mfw_info->size); @@ -735,6 +851,8 @@ static const struct __fw_feat_cfg fw_feat_tbl[] = { __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 47, 0, CH_INFO_BE_V0), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 49, 0, RFK_PRE_NOTIFY_V1), __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 51, 0, NO_PHYCAP_P1), + __CFG_FW_FEAT(RTL8922A, lt, 0, 35, 64, 0, NO_POWER_DIFFERENCE), + __CFG_FW_FEAT(RTL8922A, ge, 0, 35, 71, 0, BEACON_LOSS_COUNT_V1), }; static void rtw89_fw_iterate_feature_cfg(struct rtw89_fw_info *fw, @@ -903,7 +1021,7 @@ int rtw89_build_phy_tbl_from_elm(struct rtw89_dev *rtwdev, } n_regs = le32_to_cpu(elm->size) / sizeof(tbl->regs[0]); - regs = kcalloc(n_regs, sizeof(tbl->regs[0]), GFP_KERNEL); + regs = kcalloc(n_regs, sizeof(*regs), GFP_KERNEL); if (!regs) goto out; @@ -988,7 +1106,7 @@ int rtw89_build_txpwr_trk_tbl_from_elm(struct rtw89_dev *rtwdev, bitmap = le32_to_cpu(elm->u.txpwr_trk.bitmap); if ((bitmap & needed_bitmap) != needed_bitmap) { - rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %0x8x\n", + rtw89_warn(rtwdev, "needed txpwr trk bitmap %08x but %08x\n", needed_bitmap, bitmap); return -ENOENT; } @@ -1056,6 +1174,101 @@ allocated: return 0; } +static bool rtw89_regd_entcpy(struct rtw89_regd *regd, const void *cursor, + u8 cursor_size) +{ + /* fill default values if needed for backward compatibility */ + struct rtw89_fw_regd_entry entry = { + .rule_2ghz = RTW89_NA, + .rule_5ghz = RTW89_NA, + .rule_6ghz = RTW89_NA, + .fmap = cpu_to_le32(0x0), + }; + u8 valid_size = min_t(u8, sizeof(entry), cursor_size); + unsigned int i; + u32 fmap; + + memcpy(&entry, cursor, valid_size); + memset(regd, 0, sizeof(*regd)); + + regd->alpha2[0] = entry.alpha2_0; + regd->alpha2[1] = entry.alpha2_1; + regd->alpha2[2] = '\0'; + + /* also need to consider forward compatibility */ + regd->txpwr_regd[RTW89_BAND_2G] = entry.rule_2ghz < RTW89_REGD_NUM ? + entry.rule_2ghz : RTW89_NA; + regd->txpwr_regd[RTW89_BAND_5G] = entry.rule_5ghz < RTW89_REGD_NUM ? + entry.rule_5ghz : RTW89_NA; + regd->txpwr_regd[RTW89_BAND_6G] = entry.rule_6ghz < RTW89_REGD_NUM ? + entry.rule_6ghz : RTW89_NA; + + BUILD_BUG_ON(sizeof(fmap) != sizeof(entry.fmap)); + BUILD_BUG_ON(sizeof(fmap) * 8 < NUM_OF_RTW89_REGD_FUNC); + + fmap = le32_to_cpu(entry.fmap); + for (i = 0; i < NUM_OF_RTW89_REGD_FUNC; i++) { + if (fmap & BIT(i)) + set_bit(i, regd->func_bitmap); + } + + return true; +} + +#define rtw89_for_each_in_regd_element(regd, element) \ + for (const void *cursor = (element)->content, \ + *end = (element)->content + \ + le32_to_cpu((element)->num_ents) * (element)->ent_sz; \ + cursor < end; cursor += (element)->ent_sz) \ + if (rtw89_regd_entcpy(regd, cursor, (element)->ent_sz)) + +static +int rtw89_recognize_regd_from_elm(struct rtw89_dev *rtwdev, + const struct rtw89_fw_element_hdr *elm, + const union rtw89_fw_element_arg arg) +{ + const struct __rtw89_fw_regd_element *regd_elm = &elm->u.regd; + struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + u32 num_ents = le32_to_cpu(regd_elm->num_ents); + struct rtw89_regd_data *p; + struct rtw89_regd regd; + u32 i = 0; + + if (num_ents > RTW89_REGD_MAX_COUNTRY_NUM) { + rtw89_warn(rtwdev, + "regd element ents (%d) are over max num (%d)\n", + num_ents, RTW89_REGD_MAX_COUNTRY_NUM); + rtw89_warn(rtwdev, + "regd element ignore and take another/common\n"); + return 1; + } + + if (elm_info->regd) { + rtw89_debug(rtwdev, RTW89_DBG_REGD, + "regd element take the latter\n"); + devm_kfree(rtwdev->dev, elm_info->regd); + elm_info->regd = NULL; + } + + p = devm_kzalloc(rtwdev->dev, struct_size(p, map, num_ents), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->nr = num_ents; + rtw89_for_each_in_regd_element(®d, regd_elm) + p->map[i++] = regd; + + if (i != num_ents) { + rtw89_err(rtwdev, "regd element has %d invalid ents\n", + num_ents - i); + devm_kfree(rtwdev->dev, p); + return -EINVAL; + } + + elm_info->regd = p; + return 0; +} + static const struct rtw89_fw_element_handler __fw_element_handlers[] = { [RTW89_FW_ELEMENT_ID_BBMCU0] = {__rtw89_fw_recognize_from_elm, { .fw_type = RTW89_FW_BBMCU0 }, NULL}, @@ -1088,6 +1301,18 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = { rtw89_fw_recognize_txpwr_from_elm, { .offset = offsetof(struct rtw89_rfe_data, lmt_6ghz.conf) }, NULL, }, + [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_2GHZ] = { + rtw89_fw_recognize_txpwr_from_elm, + { .offset = offsetof(struct rtw89_rfe_data, da_lmt_2ghz.conf) }, NULL, + }, + [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_5GHZ] = { + rtw89_fw_recognize_txpwr_from_elm, + { .offset = offsetof(struct rtw89_rfe_data, da_lmt_5ghz.conf) }, NULL, + }, + [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_6GHZ] = { + rtw89_fw_recognize_txpwr_from_elm, + { .offset = offsetof(struct rtw89_rfe_data, da_lmt_6ghz.conf) }, NULL, + }, [RTW89_FW_ELEMENT_ID_TXPWR_LMT_RU_2GHZ] = { rtw89_fw_recognize_txpwr_from_elm, { .offset = offsetof(struct rtw89_rfe_data, lmt_ru_2ghz.conf) }, NULL, @@ -1100,6 +1325,18 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = { rtw89_fw_recognize_txpwr_from_elm, { .offset = offsetof(struct rtw89_rfe_data, lmt_ru_6ghz.conf) }, NULL, }, + [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ] = { + rtw89_fw_recognize_txpwr_from_elm, + { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_2ghz.conf) }, NULL, + }, + [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ] = { + rtw89_fw_recognize_txpwr_from_elm, + { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_5ghz.conf) }, NULL, + }, + [RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ] = { + rtw89_fw_recognize_txpwr_from_elm, + { .offset = offsetof(struct rtw89_rfe_data, da_lmt_ru_6ghz.conf) }, NULL, + }, [RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT] = { rtw89_fw_recognize_txpwr_from_elm, { .offset = offsetof(struct rtw89_rfe_data, tx_shape_lmt.conf) }, NULL, @@ -1114,6 +1351,9 @@ static const struct rtw89_fw_element_handler __fw_element_handlers[] = { [RTW89_FW_ELEMENT_ID_RFKLOG_FMT] = { rtw89_build_rfk_log_fmt_from_elm, {}, NULL, }, + [RTW89_FW_ELEMENT_ID_REGD] = { + rtw89_recognize_regd_from_elm, {}, "REGD", + }, }; int rtw89_fw_recognize_elements(struct rtw89_dev *rtwdev) @@ -1322,7 +1562,6 @@ static int __rtw89_fw_download_hdr(struct rtw89_dev *rtwdev, ret = rtw89_h2c_tx(rtwdev, skb, false); if (ret) { rtw89_err(rtwdev, "failed to send h2c\n"); - ret = -1; goto fail; } @@ -1409,7 +1648,6 @@ static int __rtw89_fw_download_main(struct rtw89_dev *rtwdev, ret = rtw89_h2c_tx(rtwdev, skb, true); if (ret) { rtw89_err(rtwdev, "failed to send h2c\n"); - ret = -1; goto fail; } @@ -2272,7 +2510,7 @@ int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable) if (enable) comp = BIT(RTW89_FW_LOG_COMP_INIT) | BIT(RTW89_FW_LOG_COMP_TASK) | BIT(RTW89_FW_LOG_COMP_PS) | BIT(RTW89_FW_LOG_COMP_ERROR) | - BIT(RTW89_FW_LOG_COMP_SCAN); + BIT(RTW89_FW_LOG_COMP_MLO) | BIT(RTW89_FW_LOG_COMP_SCAN); skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_LOG_CFG_LEN); if (!skb) { @@ -2697,7 +2935,9 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, { const struct rtw89_phy_bb_gain_info_be *gain = &rtwdev->bb_gain.be; struct rtw89_pkt_stat *pkt_stat = &rtwdev->phystat.cur_pkt_stat; + static const u8 bcn_bw_ofst[] = {0, 0, 0, 3, 6, 9, 0, 12}; const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_efuse *efuse = &rtwdev->efuse; struct rtw89_h2c_lps_ml_cmn_info *h2c; struct rtw89_vif_link *rtwvif_link; const struct rtw89_chan *chan; @@ -2705,6 +2945,7 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, u32 len = sizeof(*h2c); unsigned int link_id; struct sk_buff *skb; + u8 beacon_bw_ofst; u8 gain_band; u32 done; u8 path; @@ -2722,9 +2963,10 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, skb_put(skb, len); h2c = (struct rtw89_h2c_lps_ml_cmn_info *)skb->data; - h2c->fmt_id = 0x1; + h2c->fmt_id = 0x3; h2c->mlo_dbcc_mode = cpu_to_le32(rtwdev->mlo_dbcc_mode); + h2c->rfe_type = efuse->rfe_type; rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) { path = rtwvif_link->phy_idx == RTW89_PHY_1 ? RF_PATH_B : RF_PATH_A; @@ -2745,9 +2987,21 @@ int rtw89_fw_h2c_lps_ml_cmn_info(struct rtw89_dev *rtwdev, h2c->tia_gain[rtwvif_link->phy_idx][i] = cpu_to_le16(gain->tia_gain[gain_band][bw_idx][path][i]); } + + if (rtwvif_link->bcn_bw_idx < ARRAY_SIZE(bcn_bw_ofst)) { + beacon_bw_ofst = bcn_bw_ofst[rtwvif_link->bcn_bw_idx]; + h2c->dup_bcn_ofst[rtwvif_link->phy_idx] = beacon_bw_ofst; + } + memcpy(h2c->lna_gain[rtwvif_link->phy_idx], gain->lna_gain[gain_band][bw_idx][path], LNA_GAIN_NUM); + memcpy(h2c->tia_lna_op1db[rtwvif_link->phy_idx], + gain->tia_lna_op1db[gain_band][bw_idx][path], + LNA_GAIN_NUM + 1); + memcpy(h2c->lna_op1db[rtwvif_link->phy_idx], + gain->lna_op1db[gain_band][bw_idx][path], + LNA_GAIN_NUM); } rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, @@ -2838,8 +3092,8 @@ static void __rtw89_fw_h2c_set_tx_path(struct rtw89_dev *rtwdev, ntx_path = RF_A; map_b = 0; } else { - ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_B; - map_b = hal->antenna_tx == RF_AB ? 1 : 0; + ntx_path = hal->antenna_tx ? hal->antenna_tx : RF_AB; + map_b = ntx_path == RF_AB ? 1 : 0; } SET_CMC_TBL_NTX_PATH_EN(skb->data, ntx_path); @@ -3281,9 +3535,10 @@ int rtw89_fw_h2c_assoc_cmac_tbl_g7(struct rtw89_dev *rtwdev, CCTLINFO_G7_W5_NOMINAL_PKT_PADDING3 | CCTLINFO_G7_W5_NOMINAL_PKT_PADDING4); - h2c->w6 = le32_encode_bits(vif->type == NL80211_IFTYPE_STATION ? 1 : 0, + h2c->w6 = le32_encode_bits(vif->cfg.aid, CCTLINFO_G7_W6_AID12_PAID) | + le32_encode_bits(vif->type == NL80211_IFTYPE_STATION ? 1 : 0, CCTLINFO_G7_W6_ULDL); - h2c->m6 = cpu_to_le32(CCTLINFO_G7_W6_ULDL); + h2c->m6 = cpu_to_le32(CCTLINFO_G7_W6_AID12_PAID | CCTLINFO_G7_W6_ULDL); if (rtwsta_link) { h2c->w8 = le32_encode_bits(link_sta->he_cap.has_he, @@ -3419,6 +3674,61 @@ fail: return ret; } +EXPORT_SYMBOL(rtw89_fw_h2c_txtime_cmac_tbl); + +int rtw89_fw_h2c_txtime_cmac_tbl_g7(struct rtw89_dev *rtwdev, + struct rtw89_sta_link *rtwsta_link) +{ + struct rtw89_h2c_cctlinfo_ud_g7 *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for txtime_cmac_g7\n"); + return -ENOMEM; + } + skb_put(skb, len); + h2c = (struct rtw89_h2c_cctlinfo_ud_g7 *)skb->data; + + h2c->c0 = le32_encode_bits(rtwsta_link->mac_id, CCTLINFO_G7_C0_MACID) | + le32_encode_bits(1, CCTLINFO_G7_C0_OP); + + if (rtwsta_link->cctl_tx_time) { + h2c->w3 |= le32_encode_bits(1, CCTLINFO_G7_W3_AMPDU_TIME_SEL); + h2c->m3 |= cpu_to_le32(CCTLINFO_G7_W3_AMPDU_TIME_SEL); + + h2c->w2 |= le32_encode_bits(rtwsta_link->ampdu_max_time, + CCTLINFO_G7_W2_AMPDU_MAX_TIME); + h2c->m2 |= cpu_to_le32(CCTLINFO_G7_W2_AMPDU_MAX_TIME); + } + if (rtwsta_link->cctl_tx_retry_limit) { + h2c->w2 |= le32_encode_bits(1, CCTLINFO_G7_W2_DATA_TXCNT_LMT_SEL) | + le32_encode_bits(rtwsta_link->data_tx_cnt_lmt, + CCTLINFO_G7_W2_DATA_TX_CNT_LMT); + h2c->m2 |= cpu_to_le32(CCTLINFO_G7_W2_DATA_TXCNT_LMT_SEL | + CCTLINFO_G7_W2_DATA_TX_CNT_LMT); + } + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, H2C_CL_MAC_FR_EXCHG, + H2C_FUNC_MAC_CCTLINFO_UD_G7, 0, 1, + len); + + ret = rtw89_h2c_tx(rtwdev, skb, false); + if (ret) { + rtw89_err(rtwdev, "failed to send h2c\n"); + goto fail; + } + + return 0; +fail: + dev_kfree_skb_any(skb); + + return ret; +} +EXPORT_SYMBOL(rtw89_fw_h2c_txtime_cmac_tbl_g7); int rtw89_fw_h2c_txpath_cmac_tbl(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link) @@ -3622,14 +3932,15 @@ fail: } EXPORT_SYMBOL(rtw89_fw_h2c_update_beacon_be); -#define H2C_ROLE_MAINTAIN_LEN 4 int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, enum rtw89_upd_mode upd_mode) { - struct sk_buff *skb; u8 mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id; + struct rtw89_h2c_role_maintain *h2c; + u32 len = sizeof(*h2c); + struct sk_buff *skb; u8 self_role; int ret; @@ -3642,21 +3953,27 @@ int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, self_role = rtwvif_link->self_role; } - skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, H2C_ROLE_MAINTAIN_LEN); + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { rtw89_err(rtwdev, "failed to alloc skb for h2c join\n"); return -ENOMEM; } - skb_put(skb, H2C_ROLE_MAINTAIN_LEN); - SET_FWROLE_MAINTAIN_MACID(skb->data, mac_id); - SET_FWROLE_MAINTAIN_SELF_ROLE(skb->data, self_role); - SET_FWROLE_MAINTAIN_UPD_MODE(skb->data, upd_mode); - SET_FWROLE_MAINTAIN_WIFI_ROLE(skb->data, rtwvif_link->wifi_role); + skb_put(skb, len); + h2c = (struct rtw89_h2c_role_maintain *)skb->data; + + h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_ROLE_MAINTAIN_W0_MACID) | + le32_encode_bits(self_role, RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE) | + le32_encode_bits(upd_mode, RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE) | + le32_encode_bits(rtwvif_link->wifi_role, + RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE) | + le32_encode_bits(rtwvif_link->mac_idx, + RTW89_H2C_ROLE_MAINTAIN_W0_BAND) | + le32_encode_bits(rtwvif_link->port, RTW89_H2C_ROLE_MAINTAIN_W0_PORT); rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, H2C_CAT_MAC, H2C_CL_MAC_MEDIA_RPT, H2C_FUNC_MAC_FWROLE_MAINTAIN, 0, 1, - H2C_ROLE_MAINTAIN_LEN); + len); ret = rtw89_h2c_tx(rtwdev, skb, false); if (ret) { @@ -3714,8 +4031,9 @@ out: int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, bool dis_conn) { - struct sk_buff *skb; u8 mac_id = rtwsta_link ? rtwsta_link->mac_id : rtwvif_link->mac_id; + struct ieee80211_vif *vif = rtwvif_link_to_vif(rtwvif_link); + bool is_mld = ieee80211_vif_is_mld(vif); u8 self_role = rtwvif_link->self_role; enum rtw89_fw_sta_type sta_type; u8 net_type = rtwvif_link->net_type; @@ -3723,6 +4041,9 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv struct rtw89_h2c_join *h2c; u32 len = sizeof(*h2c); bool format_v1 = false; + struct sk_buff *skb; + u8 main_mac_id; + bool init_ps; int ret; if (rtwdev->chip->chip_gen == RTW89_CHIP_BE) { @@ -3764,8 +4085,28 @@ int rtw89_fw_h2c_join_info(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwv h2c_v1 = (struct rtw89_h2c_join_v1 *)skb->data; sta_type = rtw89_fw_get_sta_type(rtwdev, rtwvif_link, rtwsta_link); + init_ps = rtwvif_link != rtw89_get_designated_link(rtwvif_link->rtwvif); + + if (rtwsta_link) + main_mac_id = rtw89_sta_get_main_macid(rtwsta_link->rtwsta); + else + main_mac_id = rtw89_vif_get_main_macid(rtwvif_link->rtwvif); + + h2c_v1->w1 = le32_encode_bits(sta_type, RTW89_H2C_JOININFO_W1_STA_TYPE) | + le32_encode_bits(is_mld, RTW89_H2C_JOININFO_W1_IS_MLD) | + le32_encode_bits(main_mac_id, RTW89_H2C_JOININFO_W1_MAIN_MACID) | + le32_encode_bits(RTW89_H2C_JOININFO_MLO_MODE_MLSR, + RTW89_H2C_JOININFO_W1_MLO_MODE) | + le32_encode_bits(0, RTW89_H2C_JOININFO_W1_EMLSR_CAB) | + le32_encode_bits(0, RTW89_H2C_JOININFO_W1_NSTR_EN) | + le32_encode_bits(init_ps, RTW89_H2C_JOININFO_W1_INIT_PWR_STATE) | + le32_encode_bits(IEEE80211_EML_CAP_EMLSR_PADDING_DELAY_256US, + RTW89_H2C_JOININFO_W1_EMLSR_PADDING) | + le32_encode_bits(IEEE80211_EML_CAP_EMLSR_TRANSITION_DELAY_256US, + RTW89_H2C_JOININFO_W1_EMLSR_TRANS_DELAY) | + le32_encode_bits(0, RTW89_H2C_JOININFO_W2_MACID_EXT) | + le32_encode_bits(0, RTW89_H2C_JOININFO_W2_MAIN_MACID_EXT); - h2c_v1->w1 = le32_encode_bits(sta_type, RTW89_H2C_JOININFO_W1_STA_TYPE); h2c_v1->w2 = 0; done: @@ -4049,6 +4390,7 @@ int rtw89_fw_h2c_set_bcn_fltr_cfg(struct rtw89_dev *rtwdev, struct rtw89_h2c_bcnfltr *h2c; u32 len = sizeof(*h2c); struct sk_buff *skb; + u8 max_cnt, cnt; int ret; if (!RTW89_CHK_FW_FEATURE(BEACON_FILTER, &rtwdev->fw)) @@ -4077,12 +4419,20 @@ int rtw89_fw_h2c_set_bcn_fltr_cfg(struct rtw89_dev *rtwdev, skb_put(skb, len); h2c = (struct rtw89_h2c_bcnfltr *)skb->data; + if (RTW89_CHK_FW_FEATURE(BEACON_LOSS_COUNT_V1, &rtwdev->fw)) + max_cnt = BIT(7) - 1; + else + max_cnt = BIT(4) - 1; + + cnt = min(RTW89_BCN_LOSS_CNT, max_cnt); + h2c->w0 = le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_RSSI) | le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_BCN) | le32_encode_bits(connect, RTW89_H2C_BCNFLTR_W0_MON_EN) | le32_encode_bits(RTW89_BCN_FLTR_OFFLOAD_MODE_DEFAULT, RTW89_H2C_BCNFLTR_W0_MODE) | - le32_encode_bits(RTW89_BCN_LOSS_CNT, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT) | + le32_encode_bits(cnt >> 4, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_H3) | + le32_encode_bits(cnt & 0xf, RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_L4) | le32_encode_bits(hyst, RTW89_H2C_BCNFLTR_W0_RSSI_HYST) | le32_encode_bits(thold + MAX_RSSI, RTW89_H2C_BCNFLTR_W0_RSSI_THRESHOLD) | @@ -5008,12 +5358,12 @@ int rtw89_fw_h2c_add_pkt_offload(struct rtw89_dev *rtwdev, u8 *id, } static -int rtw89_fw_h2c_scan_list_offload(struct rtw89_dev *rtwdev, int ch_num, - struct list_head *chan_list) +int rtw89_fw_h2c_scan_list_offload_ax(struct rtw89_dev *rtwdev, int ch_num, + struct list_head *chan_list) { struct rtw89_wait_info *wait = &rtwdev->mac.fw_ofld_wait; struct rtw89_h2c_chinfo_elem *elem; - struct rtw89_mac_chinfo *ch_info; + struct rtw89_mac_chinfo_ax *ch_info; struct rtw89_h2c_chinfo *h2c; struct sk_buff *skb; unsigned int cond; @@ -5187,7 +5537,7 @@ int rtw89_fw_h2c_scan_list_offload_be(struct rtw89_dev *rtwdev, int ch_num, return 0; } -#define RTW89_SCAN_DELAY_TSF_UNIT 104800 +#define RTW89_SCAN_DELAY_TSF_UNIT 1000000 int rtw89_fw_h2c_scan_offload_ax(struct rtw89_dev *rtwdev, struct rtw89_scan_option *option, struct rtw89_vif_link *rtwvif_link, @@ -5311,6 +5661,7 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, u8 macc_role_size = sizeof(*macc_role) * option->num_macc_role; u8 opch_size = sizeof(*opch) * option->num_opch; u8 probe_id[NUM_NL80211_BANDS]; + u8 scan_offload_ver = U8_MAX; u8 cfg_len = sizeof(*h2c); unsigned int cond; u8 ver = U8_MAX; @@ -5321,6 +5672,11 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, rtw89_scan_get_6g_disabled_chan(rtwdev, option); + if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { + cfg_len = offsetofend(typeof(*h2c), w8); + scan_offload_ver = 0; + } + len = cfg_len + macc_role_size + opch_size; skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); if (!skb) { @@ -5392,10 +5748,8 @@ int rtw89_fw_h2c_scan_offload_be(struct rtw89_dev *rtwdev, RTW89_H2C_SCANOFLD_BE_W8_PROBE_RATE_6GHZ); } - if (RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD_BE_V0, &rtwdev->fw)) { - cfg_len = offsetofend(typeof(*h2c), w8); + if (scan_offload_ver == 0) goto flex_member; - } h2c->w9 = le32_encode_bits(sizeof(*h2c) / sizeof(h2c->w0), RTW89_H2C_SCANOFLD_BE_W9_SIZE_CFG) | @@ -5447,7 +5801,7 @@ flex_member: RTW89_H2C_SCANOFLD_BE_OPCH_W2_PKTS_CTRL) | le32_encode_bits(0, RTW89_H2C_SCANOFLD_BE_OPCH_W2_SW_DEF) | - le32_encode_bits(2, + le32_encode_bits(rtw89_is_mlo_1_1(rtwdev) ? 1 : 2, RTW89_H2C_SCANOFLD_BE_OPCH_W2_SS); opch->w3 = le32_encode_bits(RTW89_SCANOFLD_PKT_NONE, @@ -5994,24 +6348,29 @@ void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev) { struct rtw89_early_h2c *early_h2c; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); list_for_each_entry(early_h2c, &rtwdev->early_h2c_list, list) { rtw89_fw_h2c_raw(rtwdev, early_h2c->h2c, early_h2c->h2c_len); } } -void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) +void __rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) { struct rtw89_early_h2c *early_h2c, *tmp; - mutex_lock(&rtwdev->mutex); list_for_each_entry_safe(early_h2c, tmp, &rtwdev->early_h2c_list, list) { list_del(&early_h2c->list); kfree(early_h2c->h2c); kfree(early_h2c); } - mutex_unlock(&rtwdev->mutex); +} + +void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev) +{ + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + __rtw89_fw_free_all_early_h2c(rtwdev); } static void rtw89_fw_c2h_parse_attr(struct sk_buff *c2h) @@ -6055,7 +6414,7 @@ void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h) enqueue: skb_queue_tail(&rtwdev->c2h_queue, c2h); - ieee80211_queue_work(rtwdev->hw, &rtwdev->c2h_work); + wiphy_work_queue(rtwdev->hw->wiphy, &rtwdev->c2h_work); } static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, @@ -6093,17 +6452,17 @@ static void rtw89_fw_c2h_cmd_handle(struct rtw89_dev *rtwdev, rtw89_hex_dump(rtwdev, RTW89_DBG_FW, "C2H: ", skb->data, skb->len); } -void rtw89_fw_c2h_work(struct work_struct *work) +void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, c2h_work); struct sk_buff *skb, *tmp; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + skb_queue_walk_safe(&rtwdev->c2h_queue, skb, tmp) { skb_unlink(skb, &rtwdev->c2h_queue); - mutex_lock(&rtwdev->mutex); rtw89_fw_c2h_cmd_handle(rtwdev, skb); - mutex_unlock(&rtwdev->mutex); dev_kfree_skb_any(skb); } } @@ -6184,7 +6543,7 @@ int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev, u32 ret; if (h2c_info && h2c_info->id != RTW89_FWCMD_H2CREG_FUNC_GET_FEATURE) - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (!h2c_info && !c2h_info) return -EINVAL; @@ -6226,7 +6585,7 @@ void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev) rtw89_fw_prog_cnt_dump(rtwdev); } -static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev) +static void rtw89_hw_scan_release_pkt_list(struct rtw89_dev *rtwdev) { struct list_head *pkt_list = rtwdev->scan_info.pkt_list; struct rtw89_pktofld_info *info, *tmp; @@ -6245,6 +6604,23 @@ static void rtw89_release_pkt_list(struct rtw89_dev *rtwdev) } } +static void rtw89_hw_scan_cleanup(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link) +{ + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; + + mac->free_chan_list(rtwdev); + rtw89_hw_scan_release_pkt_list(rtwdev); + + rtwvif->scan_req = NULL; + rtwvif->scan_ies = NULL; + scan_info->scanning_vif = NULL; + scan_info->abort = false; + scan_info->connected = false; +} + static bool rtw89_is_6ghz_wildcard_probe_req(struct rtw89_dev *rtwdev, struct cfg80211_scan_request *req, struct rtw89_pktofld_info *info, @@ -6313,7 +6689,8 @@ out: } static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link) + struct rtw89_vif_link *rtwvif_link, + const u8 *mac_addr) { struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; struct cfg80211_scan_request *req = rtwvif->scan_req; @@ -6322,7 +6699,7 @@ static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev, int ret; for (i = 0; i < num; i++) { - skb = ieee80211_probereq_get(rtwdev->hw, rtwvif_link->mac_addr, + skb = ieee80211_probereq_get(rtwdev->hw, mac_addr, req->ssids[i].ssid, req->ssids[i].ssid_len, req->ie_len); @@ -6339,10 +6716,10 @@ static int rtw89_hw_scan_update_probe_req(struct rtw89_dev *rtwdev, return 0; } -static int rtw89_update_6ghz_rnr_chan(struct rtw89_dev *rtwdev, - struct ieee80211_scan_ies *ies, - struct cfg80211_scan_request *req, - struct rtw89_mac_chinfo *ch_info) +static int rtw89_update_6ghz_rnr_chan_ax(struct rtw89_dev *rtwdev, + struct ieee80211_scan_ies *ies, + struct cfg80211_scan_request *req, + struct rtw89_mac_chinfo_ax *ch_info) { struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif; struct list_head *pkt_list = rtwdev->scan_info.pkt_list; @@ -6414,7 +6791,7 @@ out: static void rtw89_pno_scan_add_chan_ax(struct rtw89_dev *rtwdev, int chan_type, int ssid_num, - struct rtw89_mac_chinfo *ch_info) + struct rtw89_mac_chinfo_ax *ch_info) { struct rtw89_wow_param *rtw_wow = &rtwdev->wow; struct rtw89_pktofld_info *info; @@ -6462,9 +6839,9 @@ static void rtw89_pno_scan_add_chan_ax(struct rtw89_dev *rtwdev, } } -static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, - int ssid_num, - struct rtw89_mac_chinfo *ch_info) +static void rtw89_hw_scan_add_chan_ax(struct rtw89_dev *rtwdev, int chan_type, + int ssid_num, + struct rtw89_mac_chinfo_ax *ch_info) { struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif; @@ -6495,7 +6872,7 @@ static void rtw89_hw_scan_add_chan(struct rtw89_dev *rtwdev, int chan_type, } } - ret = rtw89_update_6ghz_rnr_chan(rtwdev, ies, req, ch_info); + ret = rtw89_update_6ghz_rnr_chan_ax(rtwdev, ies, req, ch_info); if (ret) rtw89_warn(rtwdev, "RNR fails: %d\n", ret); @@ -6651,7 +7028,7 @@ int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, { struct rtw89_wow_param *rtw_wow = &rtwdev->wow; struct cfg80211_sched_scan_request *nd_config = rtw_wow->nd_config; - struct rtw89_mac_chinfo *ch_info, *tmp; + struct rtw89_mac_chinfo_ax *ch_info, *tmp; struct ieee80211_channel *channel; struct list_head chan_list; int list_len; @@ -6685,7 +7062,7 @@ int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, rtw89_pno_scan_add_chan_ax(rtwdev, type, nd_config->n_match_sets, ch_info); list_add_tail(&ch_info->list, &chan_list); } - ret = rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list); + ret = rtw89_fw_h2c_scan_list_offload_ax(rtwdev, list_len, &chan_list); out: list_for_each_entry_safe(ch_info, tmp, &chan_list, list) { @@ -6696,24 +7073,24 @@ out: return ret; } -int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link, bool connected) +int rtw89_hw_scan_prep_chan_list_ax(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link) { + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; struct cfg80211_scan_request *req = rtwvif->scan_req; - struct rtw89_mac_chinfo *ch_info, *tmp; + struct rtw89_mac_chinfo_ax *ch_info, *tmp; struct ieee80211_channel *channel; struct list_head chan_list; bool random_seq = req->flags & NL80211_SCAN_FLAG_RANDOM_SN; - int list_len, off_chan_time = 0; enum rtw89_chan_type type; - int ret = 0; + int off_chan_time = 0; + int ret; u32 idx; INIT_LIST_HEAD(&chan_list); - for (idx = rtwdev->scan_info.last_chan_idx, list_len = 0; - idx < req->n_channels && list_len < RTW89_SCAN_LIST_LIMIT_AX; - idx++, list_len++) { + + for (idx = 0; idx < req->n_channels; idx++) { channel = req->channels[idx]; ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); if (!ch_info) { @@ -6740,9 +7117,9 @@ int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, type = RTW89_CHAN_DFS; else type = RTW89_CHAN_ACTIVE; - rtw89_hw_scan_add_chan(rtwdev, type, req->n_ssids, ch_info); + rtw89_hw_scan_add_chan_ax(rtwdev, type, req->n_ssids, ch_info); - if (connected && + if (scan_info->connected && off_chan_time + ch_info->period > RTW89_OFF_CHAN_TIME) { tmp = kzalloc(sizeof(*tmp), GFP_KERNEL); if (!tmp) { @@ -6754,16 +7131,16 @@ int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, type = RTW89_CHAN_OPERATE; tmp->period = req->duration_mandatory ? req->duration : RTW89_CHANNEL_TIME; - rtw89_hw_scan_add_chan(rtwdev, type, 0, tmp); + rtw89_hw_scan_add_chan_ax(rtwdev, type, 0, tmp); list_add_tail(&tmp->list, &chan_list); off_chan_time = 0; - list_len++; } list_add_tail(&ch_info->list, &chan_list); off_chan_time += ch_info->period; } - rtwdev->scan_info.last_chan_idx = idx; - ret = rtw89_fw_h2c_scan_list_offload(rtwdev, list_len, &chan_list); + + list_splice_tail(&chan_list, &scan_info->chan_list); + return 0; out: list_for_each_entry_safe(ch_info, tmp, &chan_list, list) { @@ -6774,6 +7151,46 @@ out: return ret; } +void rtw89_hw_scan_free_chan_list_ax(struct rtw89_dev *rtwdev) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_mac_chinfo_ax *ch_info, *tmp; + + list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) { + list_del(&ch_info->list); + kfree(ch_info); + } +} + +int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_mac_chinfo_ax *ch_info, *tmp; + unsigned int list_len = 0; + struct list_head list; + int ret; + + INIT_LIST_HEAD(&list); + + list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) { + list_move_tail(&ch_info->list, &list); + + list_len++; + if (list_len == RTW89_SCAN_LIST_LIMIT_AX) + break; + } + + ret = rtw89_fw_h2c_scan_list_offload_ax(rtwdev, list_len, &list); + + list_for_each_entry_safe(ch_info, tmp, &list, list) { + list_del(&ch_info->list); + kfree(ch_info); + } + + return ret; +} + int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { @@ -6827,25 +7244,24 @@ out: return ret; } -int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link, bool connected) +int rtw89_hw_scan_prep_chan_list_be(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link) { + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; struct cfg80211_scan_request *req = rtwvif->scan_req; struct rtw89_mac_chinfo_be *ch_info, *tmp; struct ieee80211_channel *channel; struct list_head chan_list; enum rtw89_chan_type type; - int list_len, ret; bool random_seq; + int ret; u32 idx; random_seq = !!(req->flags & NL80211_SCAN_FLAG_RANDOM_SN); INIT_LIST_HEAD(&chan_list); - for (idx = rtwdev->scan_info.last_chan_idx, list_len = 0; - idx < req->n_channels && list_len < RTW89_SCAN_LIST_LIMIT_BE; - idx++, list_len++) { + for (idx = 0; idx < req->n_channels; idx++) { channel = req->channels[idx]; ch_info = kzalloc(sizeof(*ch_info), GFP_KERNEL); if (!ch_info) { @@ -6875,9 +7291,8 @@ int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, list_add_tail(&ch_info->list, &chan_list); } - rtwdev->scan_info.last_chan_idx = idx; - ret = rtw89_fw_h2c_scan_list_offload_be(rtwdev, list_len, &chan_list, - rtwvif_link); + list_splice_tail(&chan_list, &scan_info->chan_list); + return 0; out: list_for_each_entry_safe(ch_info, tmp, &chan_list, list) { @@ -6888,51 +7303,182 @@ out: return ret; } +void rtw89_hw_scan_free_chan_list_be(struct rtw89_dev *rtwdev) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_mac_chinfo_be *ch_info, *tmp; + + list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) { + list_del(&ch_info->list); + kfree(ch_info); + } +} + +int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link) +{ + struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + struct rtw89_mac_chinfo_be *ch_info, *tmp; + unsigned int list_len = 0; + struct list_head list; + int ret; + + INIT_LIST_HEAD(&list); + + list_for_each_entry_safe(ch_info, tmp, &scan_info->chan_list, list) { + list_move_tail(&ch_info->list, &list); + + list_len++; + if (list_len == RTW89_SCAN_LIST_LIMIT_BE) + break; + } + + ret = rtw89_fw_h2c_scan_list_offload_be(rtwdev, list_len, &list, + rtwvif_link); + + list_for_each_entry_safe(ch_info, tmp, &list, list) { + list_del(&ch_info->list); + kfree(ch_info); + } + + return ret; +} + static int rtw89_hw_scan_prehandle(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link, bool connected) + struct rtw89_vif_link *rtwvif_link, + const u8 *mac_addr) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; int ret; - ret = rtw89_hw_scan_update_probe_req(rtwdev, rtwvif_link); + ret = rtw89_hw_scan_update_probe_req(rtwdev, rtwvif_link, mac_addr); if (ret) { rtw89_err(rtwdev, "Update probe request failed\n"); goto out; } - ret = mac->add_chan_list(rtwdev, rtwvif_link, connected); + ret = mac->prep_chan_list(rtwdev, rtwvif_link); out: return ret; } -void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link, - struct ieee80211_scan_request *scan_req) +static void rtw89_hw_scan_update_link_beacon_noa(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, + u16 tu) +{ + struct ieee80211_p2p_noa_desc noa_desc = {}; + u64 tsf; + int ret; + + ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf); + if (ret) { + rtw89_warn(rtwdev, "%s: failed to get tsf\n", __func__); + return; + } + + noa_desc.start_time = cpu_to_le32(tsf); + noa_desc.interval = cpu_to_le32(ieee80211_tu_to_usec(tu)); + noa_desc.duration = cpu_to_le32(ieee80211_tu_to_usec(tu)); + noa_desc.count = 1; + + rtw89_p2p_noa_renew(rtwvif_link); + rtw89_p2p_noa_append(rtwvif_link, &noa_desc); + rtw89_chip_h2c_update_beacon(rtwdev, rtwvif_link); +} + +static void rtw89_hw_scan_update_beacon_noa(struct rtw89_dev *rtwdev, + const struct cfg80211_scan_request *req) +{ + const struct rtw89_entity_mgnt *mgnt = &rtwdev->hal.entity_mgnt; + const struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; + const struct rtw89_chip_info *chip = rtwdev->chip; + struct rtw89_mac_chinfo_ax *chinfo_ax; + struct rtw89_mac_chinfo_be *chinfo_be; + struct rtw89_vif_link *rtwvif_link; + struct list_head *pos, *tmp; + struct ieee80211_vif *vif; + struct rtw89_vif *rtwvif; + u16 tu = 0; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + list_for_each_safe(pos, tmp, &scan_info->chan_list) { + switch (chip->chip_gen) { + case RTW89_CHIP_AX: + chinfo_ax = list_entry(pos, typeof(*chinfo_ax), list); + tu += chinfo_ax->period; + break; + case RTW89_CHIP_BE: + chinfo_be = list_entry(pos, typeof(*chinfo_be), list); + tu += chinfo_be->period; + break; + default: + rtw89_warn(rtwdev, "%s: invalid chip gen %d\n", + __func__, chip->chip_gen); + return; + } + } + + if (unlikely(tu == 0)) { + rtw89_debug(rtwdev, RTW89_DBG_HW_SCAN, + "%s: cannot estimate needed TU\n", __func__); + return; + } + + list_for_each_entry(rtwvif, &mgnt->active_list, mgnt_entry) { + unsigned int link_id; + + vif = rtwvif_to_vif(rtwvif); + if (vif->type != NL80211_IFTYPE_AP || !vif->p2p) + continue; + + rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) + rtw89_hw_scan_update_link_beacon_noa(rtwdev, rtwvif_link, tu); + } +} + +int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, + struct ieee80211_scan_request *scan_req) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; + enum rtw89_entity_mode mode = rtw89_get_entity_mode(rtwdev); struct cfg80211_scan_request *req = &scan_req->req; const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); struct rtw89_vif *rtwvif = rtwvif_link->rtwvif; + struct rtw89_chanctx_pause_parm pause_parm = { + .rsn = RTW89_CHANCTX_PAUSE_REASON_HW_SCAN, + .trigger = rtwvif_link, + }; u32 rx_fltr = rtwdev->hal.rx_fltr; u8 mac_addr[ETH_ALEN]; u32 reg; + int ret; /* clone op and keep it during scan */ rtwdev->scan_info.op_chan = *chan; + rtwdev->scan_info.connected = rtw89_is_any_vif_connected_or_connecting(rtwdev); rtwdev->scan_info.scanning_vif = rtwvif_link; - rtwdev->scan_info.last_chan_idx = 0; rtwdev->scan_info.abort = false; rtwvif->scan_ies = &scan_req->ies; rtwvif->scan_req = req; - ieee80211_stop_queues(rtwdev->hw); - rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, false); if (req->flags & NL80211_SCAN_FLAG_RANDOM_ADDR) get_random_mask_addr(mac_addr, req->mac_addr, req->mac_addr_mask); else ether_addr_copy(mac_addr, rtwvif_link->mac_addr); + + ret = rtw89_hw_scan_prehandle(rtwdev, rtwvif_link, mac_addr); + if (ret) { + rtw89_hw_scan_cleanup(rtwdev, rtwvif_link); + return ret; + } + + ieee80211_stop_queues(rtwdev->hw); + rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, false); + rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, true); rx_fltr &= ~B_AX_A_BCN_CHK_EN; @@ -6942,7 +7488,12 @@ void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rx_fltr); - rtw89_chanctx_pause(rtwdev, RTW89_CHANCTX_PAUSE_REASON_HW_SCAN); + rtw89_chanctx_pause(rtwdev, &pause_parm); + + if (mode == RTW89_ENTITY_MODE_MCC) + rtw89_hw_scan_update_beacon_noa(rtwdev, req); + + return 0; } struct rtw89_hw_scan_complete_cb_data { @@ -6953,20 +7504,16 @@ struct rtw89_hw_scan_complete_cb_data { static int rtw89_hw_scan_complete_cb(struct rtw89_dev *rtwdev, void *data) { const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; - struct rtw89_hw_scan_info *scan_info = &rtwdev->scan_info; struct rtw89_hw_scan_complete_cb_data *cb_data = data; struct rtw89_vif_link *rtwvif_link = cb_data->rtwvif_link; struct cfg80211_scan_info info = { .aborted = cb_data->aborted, }; - struct rtw89_vif *rtwvif; u32 reg; if (!rtwvif_link) return -EINVAL; - rtwvif = rtwvif_link->rtwvif; - reg = rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, rtwvif_link->mac_idx); rtw89_write32_mask(rtwdev, reg, B_AX_RX_FLTR_CFG_MASK, rtwdev->hal.rx_fltr); @@ -6976,12 +7523,7 @@ static int rtw89_hw_scan_complete_cb(struct rtw89_dev *rtwdev, void *data) rtw89_mac_port_cfg_rx_sync(rtwdev, rtwvif_link, true); rtw89_mac_enable_beacon_for_ap_vifs(rtwdev, true); - rtw89_release_pkt_list(rtwdev); - rtwvif->scan_req = NULL; - rtwvif->scan_ies = NULL; - scan_info->last_chan_idx = 0; - scan_info->scanning_vif = NULL; - scan_info->abort = false; + rtw89_hw_scan_cleanup(rtwdev, rtwvif_link); return 0; } @@ -7056,11 +7598,11 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, if (!rtwvif_link) return -EINVAL; - connected = rtw89_is_any_vif_connected_or_connecting(rtwdev); + connected = rtwdev->scan_info.connected; opt.enable = enable; opt.target_ch_mode = connected; if (enable) { - ret = rtw89_hw_scan_prehandle(rtwdev, rtwvif_link, connected); + ret = mac->add_chan_list(rtwdev, rtwvif_link); if (ret) goto out; } @@ -8420,6 +8962,47 @@ int rtw89_fw_h2c_ap_info_refcount(struct rtw89_dev *rtwdev, bool en) return 0; } +int rtw89_fw_h2c_mlo_link_cfg(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, + bool enable) +{ + struct rtw89_wait_info *wait = &rtwdev->mlo.wait; + struct rtw89_h2c_mlo_link_cfg *h2c; + u8 mac_id = rtwvif_link->mac_id; + u32 len = sizeof(*h2c); + struct sk_buff *skb; + unsigned int cond; + int ret; + + skb = rtw89_fw_h2c_alloc_skb_with_hdr(rtwdev, len); + if (!skb) { + rtw89_err(rtwdev, "failed to alloc skb for mlo link cfg\n"); + return -ENOMEM; + } + + skb_put(skb, len); + h2c = (struct rtw89_h2c_mlo_link_cfg *)skb->data; + + h2c->w0 = le32_encode_bits(mac_id, RTW89_H2C_MLO_LINK_CFG_W0_MACID) | + le32_encode_bits(enable, RTW89_H2C_MLO_LINK_CFG_W0_OPTION); + + rtw89_h2c_pkt_set_hdr(rtwdev, skb, FWCMD_TYPE_H2C, + H2C_CAT_MAC, + H2C_CL_MLO, + H2C_FUNC_MLO_LINK_CFG, 0, 0, + len); + + cond = RTW89_MLO_WAIT_COND(mac_id, H2C_FUNC_MLO_LINK_CFG); + + ret = rtw89_h2c_tx_and_wait(rtwdev, skb, wait, cond); + if (ret) { + rtw89_err(rtwdev, "mlo link cfg (%s link id %u) failed: %d\n", + str_enable_disable(enable), rtwvif_link->link_id, ret); + return ret; + } + + return 0; +} + static bool __fw_txpwr_entry_zero_ext(const void *ext_ptr, u8 ext_len) { static const u8 zeros[U8_MAX] = {}; @@ -8807,6 +9390,26 @@ void rtw89_fw_load_tx_shape_lmt_ru(struct rtw89_tx_shape_lmt_ru_data *data) } } +static bool rtw89_fw_has_da_txpwr_table(struct rtw89_dev *rtwdev, + const struct rtw89_rfe_parms *parms) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + if (chip->support_bands & BIT(NL80211_BAND_2GHZ) && + !(parms->rule_da_2ghz.lmt && parms->rule_da_2ghz.lmt_ru)) + return false; + + if (chip->support_bands & BIT(NL80211_BAND_5GHZ) && + !(parms->rule_da_5ghz.lmt && parms->rule_da_5ghz.lmt_ru)) + return false; + + if (chip->support_bands & BIT(NL80211_BAND_6GHZ) && + !(parms->rule_da_6ghz.lmt && parms->rule_da_6ghz.lmt_ru)) + return false; + + return true; +} + const struct rtw89_rfe_parms * rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev, const struct rtw89_rfe_parms *init) @@ -8843,6 +9446,21 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev, parms->rule_6ghz.lmt = &rfe_data->lmt_6ghz.v; } + if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_2ghz.conf)) { + rtw89_fw_load_txpwr_lmt_2ghz(&rfe_data->da_lmt_2ghz); + parms->rule_da_2ghz.lmt = &rfe_data->da_lmt_2ghz.v; + } + + if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_5ghz.conf)) { + rtw89_fw_load_txpwr_lmt_5ghz(&rfe_data->da_lmt_5ghz); + parms->rule_da_5ghz.lmt = &rfe_data->da_lmt_5ghz.v; + } + + if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_6ghz.conf)) { + rtw89_fw_load_txpwr_lmt_6ghz(&rfe_data->da_lmt_6ghz); + parms->rule_da_6ghz.lmt = &rfe_data->da_lmt_6ghz.v; + } + if (rtw89_txpwr_conf_valid(&rfe_data->lmt_ru_2ghz.conf)) { rtw89_fw_load_txpwr_lmt_ru_2ghz(&rfe_data->lmt_ru_2ghz); parms->rule_2ghz.lmt_ru = &rfe_data->lmt_ru_2ghz.v; @@ -8858,6 +9476,21 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev, parms->rule_6ghz.lmt_ru = &rfe_data->lmt_ru_6ghz.v; } + if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_2ghz.conf)) { + rtw89_fw_load_txpwr_lmt_ru_2ghz(&rfe_data->da_lmt_ru_2ghz); + parms->rule_da_2ghz.lmt_ru = &rfe_data->da_lmt_ru_2ghz.v; + } + + if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_5ghz.conf)) { + rtw89_fw_load_txpwr_lmt_ru_5ghz(&rfe_data->da_lmt_ru_5ghz); + parms->rule_da_5ghz.lmt_ru = &rfe_data->da_lmt_ru_5ghz.v; + } + + if (rtw89_txpwr_conf_valid(&rfe_data->da_lmt_ru_6ghz.conf)) { + rtw89_fw_load_txpwr_lmt_ru_6ghz(&rfe_data->da_lmt_ru_6ghz); + parms->rule_da_6ghz.lmt_ru = &rfe_data->da_lmt_ru_6ghz.v; + } + if (rtw89_txpwr_conf_valid(&rfe_data->tx_shape_lmt.conf)) { rtw89_fw_load_tx_shape_lmt(&rfe_data->tx_shape_lmt); parms->tx_shape.lmt = &rfe_data->tx_shape_lmt.v; @@ -8868,5 +9501,7 @@ rtw89_load_rfe_data_from_fw(struct rtw89_dev *rtwdev, parms->tx_shape.lmt_ru = &rfe_data->tx_shape_lmt_ru.v; } + parms->has_da = rtw89_fw_has_da_txpwr_table(rtwdev, parms); + return parms; } diff --git a/drivers/net/wireless/realtek/rtw89/fw.h b/drivers/net/wireless/realtek/rtw89/fw.h index 2026bc2fd2ac..0fcc824e41be 100644 --- a/drivers/net/wireless/realtek/rtw89/fw.h +++ b/drivers/net/wireless/realtek/rtw89/fw.h @@ -199,6 +199,7 @@ enum rtw89_fw_log_comp { RTW89_FW_LOG_COMP_TWT, RTW89_FW_LOG_COMP_RF, RTW89_FW_LOG_COMP_MCC = 20, + RTW89_FW_LOG_COMP_MLO = 26, RTW89_FW_LOG_COMP_SCAN = 28, }; @@ -333,9 +334,9 @@ struct rtw89_fw_macid_pause_sleep_grp { #define RTW89_SCAN_LIST_LIMIT_AX RTW89_SCAN_LIST_LIMIT(RTW89_MAC_CHINFO_SIZE) #define RTW89_SCAN_LIST_LIMIT_BE RTW89_SCAN_LIST_LIMIT(RTW89_MAC_CHINFO_SIZE_BE) -#define RTW89_BCN_LOSS_CNT 10 +#define RTW89_BCN_LOSS_CNT 60 -struct rtw89_mac_chinfo { +struct rtw89_mac_chinfo_ax { u8 period; u8 dwell_time; u8 central_ch; @@ -664,6 +665,11 @@ struct rtw89_fw_mss_pool_hdr { union rtw89_fw_section_mssc_content { struct { + u8 pad[0x20]; + u8 bit_in_chip_list; + u8 ver; + } __packed blacklist; + struct { u8 pad[58]; __le32 v; } __packed sb_sel_ver; @@ -673,6 +679,13 @@ union rtw89_fw_section_mssc_content { } __packed key_sign_len; } __packed; +struct rtw89_fw_blacklist { + u8 ver; + u8 list[32]; +}; + +extern const struct rtw89_fw_blacklist rtw89_fw_blacklist_default; + static inline void SET_CTRL_INFO_MACID(void *table, u32 val) { le32p_replace_bits((__le32 *)(table) + 0, val, GENMASK(6, 0)); @@ -1579,25 +1592,17 @@ struct rtw89_h2c_bcn_upd_be { #define RTW89_H2C_BCN_UPD_BE_W7_ECSA_OFST GENMASK(30, 16) #define RTW89_H2C_BCN_UPD_BE_W7_PROTECTION_KEY_ID BIT(31) -static inline void SET_FWROLE_MAINTAIN_MACID(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(7, 0)); -} - -static inline void SET_FWROLE_MAINTAIN_SELF_ROLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(9, 8)); -} - -static inline void SET_FWROLE_MAINTAIN_UPD_MODE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(12, 10)); -} +struct rtw89_h2c_role_maintain { + __le32 w0; +}; -static inline void SET_FWROLE_MAINTAIN_WIFI_ROLE(void *h2c, u32 val) -{ - le32p_replace_bits((__le32 *)h2c, val, GENMASK(16, 13)); -} +#define RTW89_H2C_ROLE_MAINTAIN_W0_MACID GENMASK(7, 0) +#define RTW89_H2C_ROLE_MAINTAIN_W0_SELF_ROLE GENMASK(9, 8) +#define RTW89_H2C_ROLE_MAINTAIN_W0_UPD_MODE GENMASK(12, 10) +#define RTW89_H2C_ROLE_MAINTAIN_W0_WIFI_ROLE GENMASK(16, 13) +#define RTW89_H2C_ROLE_MAINTAIN_W0_BAND GENMASK(18, 17) +#define RTW89_H2C_ROLE_MAINTAIN_W0_PORT GENMASK(21, 19) +#define RTW89_H2C_ROLE_MAINTAIN_W0_MACID_EXT GENMASK(31, 24) enum rtw89_fw_sta_type { /* value of RTW89_H2C_JOININFO_W1_STA_TYPE */ RTW89_FW_N_AC_STA = 0, @@ -1632,6 +1637,8 @@ struct rtw89_h2c_join_v1 { #define RTW89_H2C_JOININFO_W1_IS_MLD BIT(3) #define RTW89_H2C_JOININFO_W1_MAIN_MACID GENMASK(11, 4) #define RTW89_H2C_JOININFO_W1_MLO_MODE BIT(12) +#define RTW89_H2C_JOININFO_MLO_MODE_MLMR 0 +#define RTW89_H2C_JOININFO_MLO_MODE_MLSR 1 #define RTW89_H2C_JOININFO_W1_EMLSR_CAB BIT(13) #define RTW89_H2C_JOININFO_W1_NSTR_EN BIT(14) #define RTW89_H2C_JOININFO_W1_INIT_PWR_STATE BIT(15) @@ -1801,17 +1808,21 @@ struct rtw89_h2c_lps_ch_info { struct rtw89_h2c_lps_ml_cmn_info { u8 fmt_id; - u8 rsvd0[3]; + u8 rfe_type; + u8 rsvd0[2]; __le32 mlo_dbcc_mode; - u8 central_ch[RTW89_PHY_MAX]; - u8 pri_ch[RTW89_PHY_MAX]; - u8 bw[RTW89_PHY_MAX]; - u8 band[RTW89_PHY_MAX]; - u8 bcn_rate_type[RTW89_PHY_MAX]; + u8 central_ch[RTW89_PHY_NUM]; + u8 pri_ch[RTW89_PHY_NUM]; + u8 bw[RTW89_PHY_NUM]; + u8 band[RTW89_PHY_NUM]; + u8 bcn_rate_type[RTW89_PHY_NUM]; u8 rsvd1[2]; - __le16 tia_gain[RTW89_PHY_MAX][TIA_GAIN_NUM]; - u8 lna_gain[RTW89_PHY_MAX][LNA_GAIN_NUM]; + __le16 tia_gain[RTW89_PHY_NUM][TIA_GAIN_NUM]; + u8 lna_gain[RTW89_PHY_NUM][LNA_GAIN_NUM]; u8 rsvd2[2]; + u8 tia_lna_op1db[RTW89_PHY_NUM][LNA_GAIN_NUM + 1]; + u8 lna_op1db[RTW89_PHY_NUM][LNA_GAIN_NUM]; + u8 dup_bcn_ofst[RTW89_PHY_NUM]; } __packed; static inline void RTW89_SET_FWCMD_CPU_EXCEPTION_TYPE(void *cmd, u32 val) @@ -2855,6 +2866,13 @@ struct rtw89_h2c_fwips { #define RTW89_H2C_FW_IPS_W0_MACID GENMASK(7, 0) #define RTW89_H2C_FW_IPS_W0_ENABLE BIT(8) +struct rtw89_h2c_mlo_link_cfg { + __le32 w0; +}; + +#define RTW89_H2C_MLO_LINK_CFG_W0_MACID GENMASK(15, 0) +#define RTW89_H2C_MLO_LINK_CFG_W0_OPTION GENMASK(19, 16) + static inline void RTW89_SET_FWCMD_P2P_MACID(void *cmd, u32 val) { le32p_replace_bits((__le32 *)cmd, val, GENMASK(7, 0)); @@ -3554,6 +3572,7 @@ struct rtw89_c2h_done_ack { #define RTW89_C2H_DONE_ACK_W2_CLASS GENMASK(7, 2) #define RTW89_C2H_DONE_ACK_W2_FUNC GENMASK(15, 8) #define RTW89_C2H_DONE_ACK_W2_H2C_RETURN GENMASK(23, 16) +#define RTW89_C2H_SCAN_DONE_ACK_RETURN GENMASK(5, 0) #define RTW89_C2H_DONE_ACK_W2_H2C_SEQ GENMASK(31, 24) #define RTW89_GET_MAC_C2H_REV_ACK_CAT(c2h) \ @@ -3612,6 +3631,19 @@ struct rtw89_c2h_ra_rpt { #define RTW89_C2H_RA_RPT_W3_MD_SEL_B2 BIT(15) #define RTW89_C2H_RA_RPT_W3_BW_B2 BIT(16) +struct rtw89_c2h_fw_scan_rpt { + struct rtw89_c2h_hdr hdr; + u8 phy_idx; + u8 band; + u8 center_ch; + u8 ofdm_pd_idx; /* in unit of 2 dBm */ +#define PD_LOWER_BOUND_BASE 102 + s8 cck_pd_idx; + u8 rsvd0; + u8 rsvd1; + u8 rsvd2; +} __packed; + /* For WiFi 6 chips: * VHT, HE, HT-old: [6:4]: NSS, [3:0]: MCS * HT-new: [6:5]: NA, [4:0]: MCS @@ -3709,6 +3741,25 @@ static_assert(sizeof(struct rtw89_mac_mcc_tsf_rpt) <= RTW89_COMPLETION_BUF_SIZE) #define RTW89_GET_MAC_C2H_MCC_STATUS_RPT_TSF_HIGH(c2h) \ le32_get_bits(*((const __le32 *)(c2h) + 4), GENMASK(31, 0)) +struct rtw89_c2h_mlo_link_cfg_rpt { + struct rtw89_c2h_hdr hdr; + __le32 w2; +} __packed; + +#define RTW89_C2H_MLO_LINK_CFG_RPT_W2_MACID GENMASK(15, 0) +#define RTW89_C2H_MLO_LINK_CFG_RPT_W2_STATUS GENMASK(19, 16) + +enum rtw89_c2h_mlo_link_status { + RTW89_C2H_MLO_LINK_CFG_IDLE = 0, + RTW89_C2H_MLO_LINK_CFG_DONE = 1, + RTW89_C2H_MLO_LINK_CFG_ISSUE_NULL_FAIL = 2, + RTW89_C2H_MLO_LINK_CFG_TX_NULL_FAIL = 3, + RTW89_C2H_MLO_LINK_CFG_ROLE_NOT_EXIST = 4, + RTW89_C2H_MLO_LINK_CFG_NULL_1_TIMEOUT = 5, + RTW89_C2H_MLO_LINK_CFG_NULL_0_TIMEOUT = 6, + RTW89_C2H_MLO_LINK_CFG_RUNNING = 0xff, +}; + struct rtw89_mac_mrc_tsf_rpt { unsigned int num; u64 tsfs[RTW89_MAC_MRC_MAX_REQ_TSF_NUM]; @@ -3805,7 +3856,8 @@ struct rtw89_h2c_bcnfltr { #define RTW89_H2C_BCNFLTR_W0_MON_BCN BIT(1) #define RTW89_H2C_BCNFLTR_W0_MON_EN BIT(2) #define RTW89_H2C_BCNFLTR_W0_MODE GENMASK(4, 3) -#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT GENMASK(11, 8) +#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_H3 GENMASK(7, 5) +#define RTW89_H2C_BCNFLTR_W0_BCN_LOSS_CNT_L4 GENMASK(11, 8) #define RTW89_H2C_BCNFLTR_W0_RSSI_HYST GENMASK(15, 12) #define RTW89_H2C_BCNFLTR_W0_RSSI_THRESHOLD GENMASK(23, 16) #define RTW89_H2C_BCNFLTR_W0_MAC_ID GENMASK(31, 24) @@ -3882,6 +3934,13 @@ enum rtw89_fw_element_id { RTW89_FW_ELEMENT_ID_TX_SHAPE_LMT_RU = 17, RTW89_FW_ELEMENT_ID_TXPWR_TRK = 18, RTW89_FW_ELEMENT_ID_RFKLOG_FMT = 19, + RTW89_FW_ELEMENT_ID_REGD = 20, + RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_2GHZ = 21, + RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_5GHZ = 22, + RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_6GHZ = 23, + RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_2GHZ = 24, + RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_5GHZ = 25, + RTW89_FW_ELEMENT_ID_TXPWR_DA_LMT_RU_6GHZ = 26, RTW89_FW_ELEMENT_ID_NUM, }; @@ -3925,6 +3984,15 @@ struct __rtw89_fw_txpwr_element { u8 content[]; } __packed; +struct __rtw89_fw_regd_element { + u8 rsvd0; + u8 rsvd1; + u8 rsvd2; + u8 ent_sz; + __le32 num_ents; + u8 content[]; +} __packed; + enum rtw89_fw_txpwr_trk_type { __RTW89_FW_TXPWR_TRK_TYPE_6GHZ_START = 0, RTW89_FW_TXPWR_TRK_TYPE_6GB_N = 0, @@ -4016,6 +4084,7 @@ struct rtw89_fw_element_hdr { __le16 offset[]; } __packed rfk_log_fmt; struct __rtw89_fw_txpwr_element txpwr; + struct __rtw89_fw_regd_element regd; } __packed u; } __packed; @@ -4210,6 +4279,26 @@ enum rtw89_mcc_h2c_func { #define RTW89_MCC_WAIT_COND(group, func) \ ((group) * NUM_OF_RTW89_MCC_H2C_FUNC + (func)) +/* CLASS 20 - MLO */ +#define H2C_CL_MLO 0x14 +enum rtw89_mlo_h2c_func { + H2C_FUNC_MLO_TBL_CFG = 0x0, + H2C_FUNC_MLO_STA_CFG = 0x1, + H2C_FUNC_MLO_TTLM = 0x2, + H2C_FUNC_MLO_DM_CFG = 0x3, + H2C_FUNC_MLO_EMLSR_STA_CFG = 0x4, + H2C_FUNC_MLO_MCMLO_RELINK_DROP = 0x5, + H2C_FUNC_MLO_MCMLO_SN_SYNC = 0x6, + H2C_FUNC_MLO_RELINK = 0x7, + H2C_FUNC_MLO_LINK_CFG = 0x8, + H2C_FUNC_MLO_DM_DBG = 0x9, + + NUM_OF_RTW89_MLO_H2C_FUNC, +}; + +#define RTW89_MLO_WAIT_COND(macid, func) \ + ((macid) * NUM_OF_RTW89_MLO_H2C_FUNC + (func)) + /* CLASS 24 - MRC */ #define H2C_CL_MRC 0x18 enum rtw89_mrc_h2c_func { @@ -4524,6 +4613,12 @@ struct rtw89_c2h_rfk_report { u8 version; } __packed; +struct rtw89_c2h_rf_tas_info { + struct rtw89_c2h_hdr hdr; + __le32 cur_idx; + __le16 txpwr_history[20]; +} __packed; + #define RTW89_FW_RSVD_PLE_SIZE 0x800 #define RTW89_FW_BACKTRACE_INFO_SIZE 8 @@ -4573,6 +4668,8 @@ int rtw89_fw_h2c_ampdu_cmac_tbl_g7(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link); int rtw89_fw_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link); +int rtw89_fw_h2c_txtime_cmac_tbl_g7(struct rtw89_dev *rtwdev, + struct rtw89_sta_link *rtwsta_link); int rtw89_fw_h2c_txpath_cmac_tbl(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link); int rtw89_fw_h2c_update_beacon(struct rtw89_dev *rtwdev, @@ -4588,7 +4685,7 @@ int rtw89_fw_h2c_dctl_sec_cam_v2(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link); void rtw89_fw_c2h_irqsafe(struct rtw89_dev *rtwdev, struct sk_buff *c2h); -void rtw89_fw_c2h_work(struct work_struct *work); +void rtw89_fw_c2h_work(struct wiphy *wiphy, struct wiphy_work *work); int rtw89_fw_h2c_role_maintain(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, struct rtw89_sta_link *rtwsta_link, @@ -4654,6 +4751,7 @@ int rtw89_fw_h2c_raw_with_hdr(struct rtw89_dev *rtwdev, bool rack, bool dack); int rtw89_fw_h2c_raw(struct rtw89_dev *rtwdev, const u8 *buf, u16 len); void rtw89_fw_send_all_early_h2c(struct rtw89_dev *rtwdev); +void __rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev); void rtw89_fw_free_all_early_h2c(struct rtw89_dev *rtwdev); int rtw89_fw_h2c_general_pkt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, u8 macid); @@ -4687,9 +4785,9 @@ int rtw89_fw_msg_reg(struct rtw89_dev *rtwdev, struct rtw89_mac_c2h_info *c2h_info); int rtw89_fw_h2c_fw_log(struct rtw89_dev *rtwdev, bool enable); void rtw89_fw_st_dbg_dump(struct rtw89_dev *rtwdev); -void rtw89_hw_scan_start(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link, - struct ieee80211_scan_request *scan_req); +int rtw89_hw_scan_start(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link, + struct ieee80211_scan_request *scan_req); void rtw89_hw_scan_complete(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool aborted); @@ -4698,12 +4796,18 @@ int rtw89_hw_scan_offload(struct rtw89_dev *rtwdev, bool enable); void rtw89_hw_scan_abort(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); +int rtw89_hw_scan_prep_chan_list_ax(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link); +void rtw89_hw_scan_free_chan_list_ax(struct rtw89_dev *rtwdev); int rtw89_hw_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link, bool connected); + struct rtw89_vif_link *rtwvif_link); int rtw89_pno_scan_add_chan_list_ax(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); +int rtw89_hw_scan_prep_chan_list_be(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link); +void rtw89_hw_scan_free_chan_list_be(struct rtw89_dev *rtwdev); int rtw89_hw_scan_add_chan_list_be(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link, bool connected); + struct rtw89_vif_link *rtwvif_link); int rtw89_pno_scan_add_chan_list_be(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); int rtw89_fw_h2c_trigger_cpu_exception(struct rtw89_dev *rtwdev); @@ -4772,6 +4876,8 @@ int rtw89_fw_h2c_mrc_sync(struct rtw89_dev *rtwdev, int rtw89_fw_h2c_mrc_upd_duration(struct rtw89_dev *rtwdev, const struct rtw89_fw_mrc_upd_duration_arg *arg); int rtw89_fw_h2c_ap_info_refcount(struct rtw89_dev *rtwdev, bool en); +int rtw89_fw_h2c_mlo_link_cfg(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, + bool enable); static inline void rtw89_fw_h2c_init_ba_cam(struct rtw89_dev *rtwdev) { @@ -4854,6 +4960,15 @@ static inline int rtw89_chip_h2c_ampdu_cmac_tbl(struct rtw89_dev *rtwdev, } static inline +int rtw89_chip_h2c_txtime_cmac_tbl(struct rtw89_dev *rtwdev, + struct rtw89_sta_link *rtwsta_link) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return chip->ops->h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); +} + +static inline int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, bool valid, struct ieee80211_ampdu_params *params) { @@ -4874,6 +4989,18 @@ int rtw89_chip_h2c_ba_cam(struct rtw89_dev *rtwdev, struct rtw89_sta *rtwsta, return 0; } +/* Must consider compatibility; don't insert new in the mid. + * Fill each field's default value in rtw89_regd_entcpy(). + */ +struct rtw89_fw_regd_entry { + u8 alpha2_0; + u8 alpha2_1; + u8 rule_2ghz; + u8 rule_5ghz; + u8 rule_6ghz; + __le32 fmap; +} __packed; + /* must consider compatibility; don't insert new in the mid */ struct rtw89_fw_txpwr_byrate_entry { u8 band; diff --git a/drivers/net/wireless/realtek/rtw89/mac.c b/drivers/net/wireless/realtek/rtw89/mac.c index a37c6d525d6f..9f0e30e75009 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.c +++ b/drivers/net/wireless/realtek/rtw89/mac.c @@ -1495,6 +1495,21 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on) #undef PWR_ACT } +int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev) +{ + int ret; + + ret = rtw89_mac_power_switch(rtwdev, true); + if (ret) { + rtw89_mac_power_switch(rtwdev, false); + ret = rtw89_mac_power_switch(rtwdev, true); + if (ret) + return ret; + } + + return 0; +} + void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev) { rtw89_mac_power_switch(rtwdev, false); @@ -3996,14 +4011,6 @@ int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb) { int ret; - ret = rtw89_mac_power_switch(rtwdev, true); - if (ret) { - rtw89_mac_power_switch(rtwdev, false); - ret = rtw89_mac_power_switch(rtwdev, true); - if (ret) - return ret; - } - rtw89_mac_ctrl_hci_dma_trx(rtwdev, true); if (include_bb) { @@ -4036,6 +4043,10 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev) bool include_bb = !!chip->bbmcu_nr; int ret; + ret = rtw89_mac_pwr_on(rtwdev); + if (ret) + return ret; + ret = rtw89_mac_partial_init(rtwdev, include_bb); if (ret) goto fail; @@ -4067,7 +4078,7 @@ int rtw89_mac_init(struct rtw89_dev *rtwdev) return ret; fail: - rtw89_mac_power_switch(rtwdev, false); + rtw89_mac_pwr_off(rtwdev); return ret; } @@ -4620,11 +4631,17 @@ static void rtw89_mac_port_tsf_sync_rand(struct rtw89_dev *rtwdev, if (rtwvif_link->net_type != RTW89_NET_TYPE_AP_MODE || rtwvif_link == rtwvif_src) return; + if (rtwvif_link->rand_tsf_done) + goto out; + /* adjust offset randomly to avoid beacon conflict */ offset = offset - offset / 4 + get_random_u32() % (offset / 2); rtw89_mac_port_tsf_sync(rtwdev, rtwvif_link, rtwvif_src, (*n_offset) * offset); + rtwvif_link->rand_tsf_done = true; + +out: (*n_offset)++; } @@ -4826,9 +4843,37 @@ void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, rtw89_write32_set(rtwdev, reg, mac->narrow_bw_ru_dis.mask); } +void rtw89_mac_set_he_tb(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link) +{ + struct ieee80211_bss_conf *bss_conf; + bool set; + u32 reg; + + if (rtwdev->chip->chip_gen != RTW89_CHIP_BE) + return; + + rcu_read_lock(); + + bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); + set = bss_conf->he_support && !bss_conf->eht_support; + + rcu_read_unlock(); + + reg = rtw89_mac_reg_by_idx(rtwdev, R_BE_CLIENT_OM_CTRL, + rtwvif_link->mac_idx); + + if (set) + rtw89_write32_set(rtwdev, reg, B_BE_TRIG_DIS_EHTTB); + else + rtw89_write32_clr(rtwdev, reg, B_BE_TRIG_DIS_EHTTB); +} + void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { rtw89_mac_port_cfg_func_sw(rtwdev, rtwvif_link); + + rtwvif_link->rand_tsf_done = false; } int rtw89_mac_add_vif(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) @@ -4862,11 +4907,11 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb, struct rtw89_vif_link *rtwvif_link = rtwdev->scan_info.scanning_vif; struct rtw89_vif *rtwvif; struct rtw89_chan new; - u32 last_chan = rtwdev->scan_info.last_chan_idx, report_tsf; u16 actual_period, expect_period; u8 reason, status, tx_fail, band; u8 mac_idx, sw_def, fw_def; u8 ver = U8_MAX; + u32 report_tsf; u16 chan; int ret; @@ -4925,7 +4970,7 @@ rtw89_mac_c2h_scanofld_rsp(struct rtw89_dev *rtwdev, struct sk_buff *skb, return; if (rtwvif_link && rtwvif->scan_req && - last_chan < rtwvif->scan_req->n_channels) { + !list_empty(&rtwdev->scan_info.chan_list)) { ret = rtw89_hw_scan_offload(rtwdev, rtwvif_link, true); if (ret) { rtw89_hw_scan_abort(rtwdev, rtwvif_link); @@ -4980,7 +5025,8 @@ rtw89_mac_bcn_fltr_rpt(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l switch (type) { case RTW89_BCN_FLTR_BEACON_LOSS: - if (!rtwdev->scanning && !rtwvif->offchan) + if (!rtwdev->scanning && !rtwvif->offchan && + !rtwvif_link->noa_once.in_duration) ieee80211_connection_loss(vif); else rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); @@ -5073,12 +5119,14 @@ rtw89_mac_c2h_done_ack(struct rtw89_dev *rtwdev, struct sk_buff *skb_c2h, u32 le return; case H2C_FUNC_ADD_SCANOFLD_CH: cond = RTW89_SCANOFLD_WAIT_COND_ADD_CH; + h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; case H2C_FUNC_SCANOFLD: cond = RTW89_SCANOFLD_WAIT_COND_START; break; case H2C_FUNC_SCANOFLD_BE: cond = RTW89_SCANOFLD_BE_WAIT_COND_START; + h2c_return &= RTW89_C2H_SCAN_DONE_ACK_RETURN; break; } @@ -5362,6 +5410,27 @@ rtw89_mac_c2h_wow_aoac_rpt(struct rtw89_dev *rtwdev, struct sk_buff *skb, u32 le } static void +rtw89_mac_c2h_mlo_link_cfg_stat(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + const struct rtw89_c2h_mlo_link_cfg_rpt *c2h_rpt; + struct rtw89_wait_info *wait = &rtwdev->mlo.wait; + struct rtw89_completion_data data = {}; + unsigned int cond; + u16 mac_id; + u8 status; + + c2h_rpt = (const struct rtw89_c2h_mlo_link_cfg_rpt *)c2h->data; + + mac_id = le32_get_bits(c2h_rpt->w2, RTW89_C2H_MLO_LINK_CFG_RPT_W2_MACID); + status = le32_get_bits(c2h_rpt->w2, RTW89_C2H_MLO_LINK_CFG_RPT_W2_STATUS); + + data.err = status == RTW89_C2H_MLO_LINK_CFG_ROLE_NOT_EXIST || + status == RTW89_C2H_MLO_LINK_CFG_RUNNING; + cond = RTW89_MLO_WAIT_COND(mac_id, H2C_FUNC_MLO_LINK_CFG); + rtw89_complete_cond(wait, cond, &data); +} + +static void rtw89_mac_c2h_mrc_status_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { struct rtw89_wait_info *wait = &rtwdev->mcc.wait; @@ -5516,6 +5585,18 @@ void (* const rtw89_mac_c2h_mcc_handler[])(struct rtw89_dev *rtwdev, }; static +void (* const rtw89_mac_c2h_mlo_handler[])(struct rtw89_dev *rtwdev, + struct sk_buff *c2h, u32 len) = { + [RTW89_MAC_C2H_FUNC_MLO_GET_TBL] = NULL, + [RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE] = NULL, + [RTW89_MAC_C2H_FUNC_MLO_EMLSR_STA_CFG_DONE] = NULL, + [RTW89_MAC_C2H_FUNC_MCMLO_RELINK_RPT] = NULL, + [RTW89_MAC_C2H_FUNC_MCMLO_SN_SYNC_RPT] = NULL, + [RTW89_MAC_C2H_FUNC_MLO_LINK_CFG_STAT] = rtw89_mac_c2h_mlo_link_cfg_stat, + [RTW89_MAC_C2H_FUNC_MLO_DM_DBG_DUMP] = NULL, +}; + +static void (* const rtw89_mac_c2h_mrc_handler[])(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) = { [RTW89_MAC_C2H_FUNC_MRC_TSF_RPT] = rtw89_mac_c2h_mrc_tsf_rpt, @@ -5584,6 +5665,8 @@ bool rtw89_mac_c2h_chk_atomic(struct rtw89_dev *rtwdev, struct sk_buff *c2h, } case RTW89_MAC_C2H_CLASS_MCC: return true; + case RTW89_MAC_C2H_CLASS_MLO: + return true; case RTW89_MAC_C2H_CLASS_MRC: return true; case RTW89_MAC_C2H_CLASS_WOW: @@ -5617,6 +5700,10 @@ void rtw89_mac_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MCC) handler = rtw89_mac_c2h_mcc_handler[func]; break; + case RTW89_MAC_C2H_CLASS_MLO: + if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MLO) + handler = rtw89_mac_c2h_mlo_handler[func]; + break; case RTW89_MAC_C2H_CLASS_MRC: if (func < NUM_OF_RTW89_MAC_C2H_FUNC_MRC) handler = rtw89_mac_c2h_mrc_handler[func]; @@ -6031,7 +6118,7 @@ int rtw89_mac_cfg_ctrl_path_v1(struct rtw89_dev *rtwdev, bool wl) if (wl) return 0; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { g[i].gnt_bt_sw_en = 1; g[i].gnt_bt = 1; g[i].gnt_wl_sw_en = 1; @@ -6422,6 +6509,7 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ u32 tx_time) { #define MAC_AX_DFLT_TX_TIME 5280 + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx; u32 max_tx_time = tx_time == 0 ? MAC_AX_DFLT_TX_TIME : tx_time; u32 reg; @@ -6429,7 +6517,7 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ if (rtwsta_link->cctl_tx_time) { rtwsta_link->ampdu_max_time = (max_tx_time - 512) >> 9; - ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); + ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); } else { ret = rtw89_mac_check_mac_en(rtwdev, mac_idx, RTW89_CMAC_SEL); if (ret) { @@ -6437,8 +6525,8 @@ __rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_ return ret; } - reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_AMPDU_AGG_LIMIT, mac_idx); - rtw89_write32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK, + reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx); + rtw89_write32_mask(rtwdev, reg, mac->agg_limit.mask, max_tx_time >> 5); } @@ -6464,6 +6552,7 @@ int rtw89_mac_set_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwst int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link, u32 *tx_time) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx; u32 reg; int ret = 0; @@ -6477,8 +6566,8 @@ int rtw89_mac_get_tx_time(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwst return ret; } - reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_AMPDU_AGG_LIMIT, mac_idx); - *tx_time = rtw89_read32_mask(rtwdev, reg, B_AX_AMPDU_MAX_TIME_MASK) << 5; + reg = rtw89_mac_reg_by_idx(rtwdev, mac->agg_limit.addr, mac_idx); + *tx_time = rtw89_read32_mask(rtwdev, reg, mac->agg_limit.mask) << 5; } return ret; @@ -6494,9 +6583,9 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev, if (!resume) { rtwsta_link->cctl_tx_retry_limit = true; - ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); + ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); } else { - ret = rtw89_fw_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); + ret = rtw89_chip_h2c_txtime_cmac_tbl(rtwdev, rtwsta_link); rtwsta_link->cctl_tx_retry_limit = false; } @@ -6506,6 +6595,7 @@ int rtw89_mac_set_tx_retry_limit(struct rtw89_dev *rtwdev, int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link, u8 *tx_retry) { + const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u8 mac_idx = rtwsta_link->rtwvif_link->mac_idx; u32 reg; int ret = 0; @@ -6519,8 +6609,8 @@ int rtw89_mac_get_tx_retry_limit(struct rtw89_dev *rtwdev, return ret; } - reg = rtw89_mac_reg_by_idx(rtwdev, R_AX_TXCNT, mac_idx); - *tx_retry = rtw89_read32_mask(rtwdev, reg, B_AX_L_TXCNT_LMT_MASK); + reg = rtw89_mac_reg_by_idx(rtwdev, mac->txcnt_limit.addr, mac_idx); + *tx_retry = rtw89_read32_mask(rtwdev, reg, mac->txcnt_limit.mask); } return ret; @@ -6798,6 +6888,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .mask = B_AX_RXTRIG_RU26_DIS, }, .wow_ctrl = {.addr = R_AX_WOW_CTRL, .mask = B_AX_WOW_WOWEN,}, + .agg_limit = {.addr = R_AX_AMPDU_AGG_LIMIT, .mask = B_AX_AMPDU_MAX_TIME_MASK,}, + .txcnt_limit = {.addr = R_AX_TXCNT, .mask = B_AX_L_TXCNT_LMT_MASK,}, .check_mac_en = rtw89_mac_check_mac_en_ax, .sys_init = sys_init_ax, @@ -6847,6 +6939,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = { .is_txq_empty = mac_is_txq_empty_ax, + .prep_chan_list = rtw89_hw_scan_prep_chan_list_ax, + .free_chan_list = rtw89_hw_scan_free_chan_list_ax, .add_chan_list = rtw89_hw_scan_add_chan_list_ax, .add_chan_list_pno = rtw89_pno_scan_add_chan_list_ax, .scan_offload = rtw89_fw_h2c_scan_offload_ax, diff --git a/drivers/net/wireless/realtek/rtw89/mac.h b/drivers/net/wireless/realtek/rtw89/mac.h index 8edea96d037f..8013c852d5be 100644 --- a/drivers/net/wireless/realtek/rtw89/mac.h +++ b/drivers/net/wireless/realtek/rtw89/mac.h @@ -370,6 +370,7 @@ enum rtw89_mac_mem_sel { RTW89_MAC_MEM_TXD_FIFO_0_V1, RTW89_MAC_MEM_TXD_FIFO_1_V1, RTW89_MAC_MEM_WD_PAGE, + RTW89_MAC_MEM_MLD_TBL, /* keep last */ RTW89_MAC_MEM_NUM, @@ -427,6 +428,18 @@ enum rtw89_mac_c2h_mcc_func { NUM_OF_RTW89_MAC_C2H_FUNC_MCC, }; +enum rtw89_mac_c2h_mlo_func { + RTW89_MAC_C2H_FUNC_MLO_GET_TBL = 0x0, + RTW89_MAC_C2H_FUNC_MLO_EMLSR_TRANS_DONE = 0x1, + RTW89_MAC_C2H_FUNC_MLO_EMLSR_STA_CFG_DONE = 0x2, + RTW89_MAC_C2H_FUNC_MCMLO_RELINK_RPT = 0x3, + RTW89_MAC_C2H_FUNC_MCMLO_SN_SYNC_RPT = 0x4, + RTW89_MAC_C2H_FUNC_MLO_LINK_CFG_STAT = 0x5, + RTW89_MAC_C2H_FUNC_MLO_DM_DBG_DUMP = 0x6, + + NUM_OF_RTW89_MAC_C2H_FUNC_MLO, +}; + enum rtw89_mac_c2h_mrc_func { RTW89_MAC_C2H_FUNC_MRC_TSF_RPT = 0, RTW89_MAC_C2H_FUNC_MRC_STATUS_RPT = 1, @@ -453,6 +466,7 @@ enum rtw89_mac_c2h_class { RTW89_MAC_C2H_CLASS_WOW = 0x3, RTW89_MAC_C2H_CLASS_MCC = 0x4, RTW89_MAC_C2H_CLASS_FWDBG = 0x5, + RTW89_MAC_C2H_CLASS_MLO = 0xc, RTW89_MAC_C2H_CLASS_MRC = 0xe, RTW89_MAC_C2H_CLASS_AP = 0x18, RTW89_MAC_C2H_CLASS_MAX, @@ -964,6 +978,8 @@ struct rtw89_mac_gen_def { struct rtw89_reg_def bfee_ctrl; struct rtw89_reg_def narrow_bw_ru_dis; struct rtw89_reg_def wow_ctrl; + struct rtw89_reg_def agg_limit; + struct rtw89_reg_def txcnt_limit; int (*check_mac_en)(struct rtw89_dev *rtwdev, u8 band, enum rtw89_mac_hwmod_sel sel); @@ -1029,8 +1045,11 @@ struct rtw89_mac_gen_def { bool (*is_txq_empty)(struct rtw89_dev *rtwdev); + int (*prep_chan_list)(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link); + void (*free_chan_list)(struct rtw89_dev *rtwdev); int (*add_chan_list)(struct rtw89_dev *rtwdev, - struct rtw89_vif_link *rtwvif_link, bool connected); + struct rtw89_vif_link *rtwvif_link); int (*add_chan_list_pno)(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); int (*scan_offload)(struct rtw89_dev *rtwdev, @@ -1145,6 +1164,7 @@ rtw89_write32_port_set(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_l rtw89_write32_set(rtwdev, reg, bit); } +int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev); void rtw89_mac_pwr_off(struct rtw89_dev *rtwdev); int rtw89_mac_partial_init(struct rtw89_dev *rtwdev, bool include_bb); int rtw89_mac_init(struct rtw89_dev *rtwdev); @@ -1185,6 +1205,8 @@ void rtw89_mac_port_cfg_rx_sync(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link, bool en); void rtw89_mac_set_he_obss_narrow_bw_ru(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); +void rtw89_mac_set_he_tb(struct rtw89_dev *rtwdev, + struct rtw89_vif_link *rtwvif_link); void rtw89_mac_stop_ap(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); void rtw89_mac_enable_beacon_for_ap_vifs(struct rtw89_dev *rtwdev, bool en); int rtw89_mac_remove_vif(struct rtw89_dev *rtwdev, struct rtw89_vif_link *vif); diff --git a/drivers/net/wireless/realtek/rtw89/mac80211.c b/drivers/net/wireless/realtek/rtw89/mac80211.c index b3669e0074df..a47971003bd4 100644 --- a/drivers/net/wireless/realtek/rtw89/mac80211.c +++ b/drivers/net/wireless/realtek/rtw89/mac80211.c @@ -57,32 +57,30 @@ static void rtw89_ops_wake_tx_queue(struct ieee80211_hw *hw, static int rtw89_ops_start(struct ieee80211_hw *hw) { struct rtw89_dev *rtwdev = hw->priv; - int ret; - mutex_lock(&rtwdev->mutex); - ret = rtw89_core_start(rtwdev); - mutex_unlock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); - return ret; + return rtw89_core_start(rtwdev); } static void rtw89_ops_stop(struct ieee80211_hw *hw, bool suspend) { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_core_stop(rtwdev); - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) { struct rtw89_dev *rtwdev = hw->priv; + lockdep_assert_wiphy(hw->wiphy); + /* let previous ips work finish to ensure we don't leave ips twice */ - cancel_work_sync(&rtwdev->ips_work); + wiphy_work_cancel(hw->wiphy, &rtwdev->ips_work); - mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); if ((changed & IEEE80211_CONF_CHANGE_IDLE) && @@ -100,8 +98,6 @@ static int rtw89_ops_config(struct ieee80211_hw *hw, u32 changed) !rtwdev->scanning) rtw89_enter_ips(rtwdev); - mutex_unlock(&rtwdev->mutex); - return 0; } @@ -115,14 +111,17 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev, rtw89_vif_type_mapping(rtwvif_link, false); - INIT_WORK(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work); + wiphy_work_init(&rtwvif_link->update_beacon_work, rtw89_core_update_beacon_work); INIT_LIST_HEAD(&rtwvif_link->general_pkt_list); + rtw89_p2p_noa_once_init(rtwvif_link); + rtwvif_link->hit_rule = 0; rtwvif_link->bcn_hit_cond = 0; rtwvif_link->chanctx_assigned = false; rtwvif_link->chanctx_idx = RTW89_CHANCTX_0; rtwvif_link->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; + rtwvif_link->rand_tsf_done = false; rcu_read_lock(); @@ -142,9 +141,11 @@ static int __rtw89_ops_add_iface_link(struct rtw89_dev *rtwdev, static void __rtw89_ops_remove_iface_link(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link) { - mutex_unlock(&rtwdev->mutex); - cancel_work_sync(&rtwvif_link->update_beacon_work); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + wiphy_work_cancel(rtwdev->hw->wiphy, &rtwvif_link->update_beacon_work); + + rtw89_p2p_noa_once_deinit(rtwvif_link); rtw89_leave_ps_mode(rtwdev); @@ -162,11 +163,11 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, u8 mac_id, port; int ret = 0; + lockdep_assert_wiphy(hw->wiphy); + rtw89_debug(rtwdev, RTW89_DBG_STATE, "add vif %pM type %d, p2p %d\n", vif->addr, vif->type, vif->p2p); - mutex_lock(&rtwdev->mutex); - rtw89_leave_ips_by_hwflags(rtwdev); if (RTW89_CHK_FW_FEATURE(BEACON_FILTER, &rtwdev->fw)) @@ -174,10 +175,8 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, IEEE80211_VIF_SUPPORTS_CQM_RSSI; mac_id = rtw89_acquire_mac_id(rtwdev); - if (mac_id == RTW89_MAX_MAC_ID_NUM) { - ret = -ENOSPC; - goto err; - } + if (mac_id == RTW89_MAX_MAC_ID_NUM) + return -ENOSPC; port = rtw89_core_acquire_bit_map(rtwdev->hw_port, RTW89_PORT_NUM); if (port == RTW89_PORT_NUM) { @@ -192,13 +191,14 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, if (!rtw89_rtwvif_in_list(rtwdev, rtwvif)) { list_add_tail(&rtwvif->list, &rtwdev->rtwvifs_list); INIT_LIST_HEAD(&rtwvif->mgnt_entry); + INIT_LIST_HEAD(&rtwvif->dlink_pool); } ether_addr_copy(rtwvif->mac_addr, vif->addr); rtwvif->offchan = false; rtwvif->roc.state = RTW89_ROC_IDLE; - INIT_DELAYED_WORK(&rtwvif->roc.roc_work, rtw89_roc_work); + wiphy_delayed_work_init(&rtwvif->roc.roc_work, rtw89_roc_work); rtw89_traffic_stats_init(rtwdev, &rtwvif->stats); @@ -213,8 +213,6 @@ static int rtw89_ops_add_interface(struct ieee80211_hw *hw, goto unset_link; rtw89_recalc_lps(rtwdev); - - mutex_unlock(&rtwdev->mutex); return 0; unset_link: @@ -224,8 +222,6 @@ release_port: rtw89_core_release_bit_map(rtwdev->hw_port, port); release_macid: rtw89_release_mac_id(rtwdev, mac_id); -err: - mutex_unlock(&rtwdev->mutex); return ret; } @@ -239,12 +235,12 @@ static void rtw89_ops_remove_interface(struct ieee80211_hw *hw, u8 port = rtw89_vif_get_main_port(rtwvif); struct rtw89_vif_link *rtwvif_link; + lockdep_assert_wiphy(hw->wiphy); + rtw89_debug(rtwdev, RTW89_DBG_STATE, "remove vif %pM type %d p2p %d\n", vif->addr, vif->type, vif->p2p); - cancel_delayed_work_sync(&rtwvif->roc.roc_work); - - mutex_lock(&rtwdev->mutex); + wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); rtwvif_link = rtwvif->links[RTW89_VIF_IDLE_LINK_ID]; if (unlikely(!rtwvif_link)) { @@ -265,8 +261,6 @@ bottom: rtw89_recalc_lps(rtwdev); rtw89_enter_ips_by_hwflags(rtwdev); - - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_change_interface(struct ieee80211_hw *hw, @@ -304,7 +298,8 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def; u32 rx_fltr; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); *new_flags &= FIF_ALLMULTI | FIF_OTHER_BSS | FIF_FCSFAIL | @@ -367,14 +362,11 @@ static void rtw89_ops_configure_filter(struct ieee80211_hw *hw, B_AX_RX_FLTR_CFG_MASK, rx_fltr); if (!rtwdev->dbcc_en) - goto out; + return; rtw89_write32_mask(rtwdev, rtw89_mac_reg_by_idx(rtwdev, mac->rx_fltr, RTW89_MAC_1), B_AX_RX_FLTR_CFG_MASK, rx_fltr); - -out: - mutex_unlock(&rtwdev->mutex); } static const u8 ac_to_fw_idx[IEEE80211_NUM_ACS] = { @@ -493,6 +485,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev, int i; if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) { + rtwvif->mlo_mode = RTW89_MLO_MODE_MLSR; + /* for station mode, assign the mac_id from itself */ macid = rtw89_vif_get_main_macid(rtwvif); } else { @@ -508,6 +502,8 @@ static int __rtw89_ops_sta_add(struct rtw89_dev *rtwdev, for (i = 0; i < ARRAY_SIZE(sta->txq); i++) rtw89_core_txq_init(rtwdev, sta->txq[i]); + INIT_LIST_HEAD(&rtwsta->dlink_pool); + skb_queue_head_init(&rtwsta->roc_queue); bitmap_zero(rtwsta->pairwise_sec_cam_map, RTW89_MAX_SEC_CAM_NUM); @@ -670,6 +666,7 @@ static void __rtw89_ops_bss_link_assoc(struct rtw89_dev *rtwdev, rtw89_chip_cfg_txpwr_ul_tb_offset(rtwdev, rtwvif_link); rtw89_mac_port_update(rtwdev, rtwvif_link); rtw89_mac_set_he_obss_narrow_bw_ru(rtwdev, rtwvif_link); + rtw89_mac_set_he_tb(rtwdev, rtwvif_link); } static void __rtw89_ops_bss_assoc(struct rtw89_dev *rtwdev, @@ -689,7 +686,8 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); if (changed & BSS_CHANGED_ASSOC) { @@ -712,8 +710,6 @@ static void rtw89_ops_vif_cfg_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_ARP_FILTER) rtwvif->ip_addr = vif->cfg.arp_addr_list[0]; - - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, @@ -725,7 +721,8 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); rtwvif_link = rtwvif->links[conf->link_id]; @@ -733,7 +730,7 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, conf->link_id); - goto out; + return; } if (changed & BSS_CHANGED_BSSID) { @@ -763,9 +760,6 @@ static void rtw89_ops_link_info_changed(struct ieee80211_hw *hw, if (changed & BSS_CHANGED_TPE) rtw89_reg_6ghz_recalc(rtwdev, rtwvif_link, true); - -out: - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_start_ap(struct ieee80211_hw *hw, @@ -778,22 +772,19 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, const struct rtw89_chan *chan; int ret = 0; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[link_conf->link_id]; if (unlikely(!rtwvif_link)) { rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_conf->link_id); - ret = -ENOLINK; - goto out; + return -ENOLINK; } chan = rtw89_chan_get(rtwdev, rtwvif_link->chanctx_idx); - if (chan->band_type == RTW89_BAND_6G) { - mutex_unlock(&rtwdev->mutex); + if (chan->band_type == RTW89_BAND_6G) return -EOPNOTSUPP; - } if (rtwdev->scanning) rtw89_hw_scan_abort(rtwdev, rtwdev->scan_info.scanning_vif); @@ -810,15 +801,12 @@ static int rtw89_ops_start_ap(struct ieee80211_hw *hw, if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) { ret = rtw89_fw_h2c_ap_info_refcount(rtwdev, true); if (ret) - goto out; + return ret; } rtw89_queue_chanctx_work(rtwdev); -out: - mutex_unlock(&rtwdev->mutex); - - return ret; + return 0; } static @@ -829,14 +817,14 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[link_conf->link_id]; if (unlikely(!rtwvif_link)) { rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_conf->link_id); - goto out; + return; } if (RTW89_CHK_FW_FEATURE(NOTIFY_AP_INFO, &rtwdev->fw)) @@ -845,22 +833,18 @@ void rtw89_ops_stop_ap(struct ieee80211_hw *hw, struct ieee80211_vif *vif, rtw89_mac_stop_ap(rtwdev, rtwvif_link); rtw89_chip_h2c_assoc_cmac_tbl(rtwdev, rtwvif_link, NULL); rtw89_fw_h2c_join_info(rtwdev, rtwvif_link, NULL, true); - -out: - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set) { - struct rtw89_dev *rtwdev = hw->priv; struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); struct rtw89_vif *rtwvif = rtwsta->rtwvif; struct rtw89_vif_link *rtwvif_link; unsigned int link_id; rtw89_vif_for_each_link(rtwvif, rtwvif_link, link_id) - ieee80211_queue_work(rtwdev->hw, &rtwvif_link->update_beacon_work); + wiphy_work_queue(hw->wiphy, &rtwvif_link->update_beacon_work); return 0; } @@ -873,9 +857,9 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - int ret = 0; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); rtwvif_link = rtwvif->links[link_id]; @@ -883,17 +867,13 @@ static int rtw89_ops_conf_tx(struct ieee80211_hw *hw, rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_id); - ret = -ENOLINK; - goto out; + return -ENOLINK; } rtwvif_link->tx_params[ac] = *params; __rtw89_conf_tx(rtwdev, rtwvif_link, ac); -out: - mutex_unlock(&rtwdev->mutex); - - return ret; + return 0; } static int __rtw89_ops_sta_state(struct ieee80211_hw *hw, @@ -937,14 +917,11 @@ static int rtw89_ops_sta_state(struct ieee80211_hw *hw, enum ieee80211_sta_state new_state) { struct rtw89_dev *rtwdev = hw->priv; - int ret; - mutex_lock(&rtwdev->mutex); - rtw89_leave_ps_mode(rtwdev); - ret = __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state); - mutex_unlock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); - return ret; + rtw89_leave_ps_mode(rtwdev); + return __rtw89_ops_sta_state(hw, vif, sta, old_state, new_state); } static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, @@ -953,9 +930,10 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, struct ieee80211_key_conf *key) { struct rtw89_dev *rtwdev = hw->priv; - int ret = 0; + int ret; + + lockdep_assert_wiphy(hw->wiphy); - mutex_lock(&rtwdev->mutex); rtw89_leave_ps_mode(rtwdev); switch (cmd) { @@ -964,7 +942,7 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = rtw89_cam_sec_key_add(rtwdev, vif, sta, key); if (ret && ret != -EOPNOTSUPP) { rtw89_err(rtwdev, "failed to add key to sec cam\n"); - goto out; + return ret; } break; case DISABLE_KEY: @@ -974,14 +952,11 @@ static int rtw89_ops_set_key(struct ieee80211_hw *hw, enum set_key_cmd cmd, ret = rtw89_cam_sec_key_del(rtwdev, vif, sta, key, true); if (ret) { rtw89_err(rtwdev, "failed to remove key from sec cam\n"); - goto out; + return ret; } break; } -out: - mutex_unlock(&rtwdev->mutex); - return ret; } @@ -997,38 +972,32 @@ static int rtw89_ops_ampdu_action(struct ieee80211_hw *hw, struct ieee80211_txq *txq = sta->txq[tid]; struct rtw89_txq *rtwtxq = (struct rtw89_txq *)txq->drv_priv; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + switch (params->action) { case IEEE80211_AMPDU_TX_START: return IEEE80211_AMPDU_TX_START_IMMEDIATE; case IEEE80211_AMPDU_TX_STOP_CONT: case IEEE80211_AMPDU_TX_STOP_FLUSH: case IEEE80211_AMPDU_TX_STOP_FLUSH_CONT: - mutex_lock(&rtwdev->mutex); clear_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags); clear_bit(tid, rtwsta->ampdu_map); rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); - mutex_unlock(&rtwdev->mutex); ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid); break; case IEEE80211_AMPDU_TX_OPERATIONAL: - mutex_lock(&rtwdev->mutex); set_bit(RTW89_TXQ_F_AMPDU, &rtwtxq->flags); rtwsta->ampdu_params[tid].agg_num = params->buf_size; rtwsta->ampdu_params[tid].amsdu = params->amsdu; set_bit(tid, rtwsta->ampdu_map); rtw89_leave_ps_mode(rtwdev); rtw89_chip_h2c_ampdu_cmac_tbl(rtwdev, rtwvif, rtwsta); - mutex_unlock(&rtwdev->mutex); break; case IEEE80211_AMPDU_RX_START: - mutex_lock(&rtwdev->mutex); rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, true, params); - mutex_unlock(&rtwdev->mutex); break; case IEEE80211_AMPDU_RX_STOP: - mutex_lock(&rtwdev->mutex); rtw89_chip_h2c_ba_cam(rtwdev, rtwsta, false, params); - mutex_unlock(&rtwdev->mutex); break; default: WARN_ON(1); @@ -1042,11 +1011,11 @@ static int rtw89_ops_set_rts_threshold(struct ieee80211_hw *hw, u32 value) { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_ps_mode(rtwdev); if (test_bit(RTW89_FLAG_POWERON, rtwdev->flags)) rtw89_mac_update_rts_threshold(rtwdev); - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1059,7 +1028,7 @@ static void rtw89_ops_sta_statistics(struct ieee80211_hw *hw, struct rtw89_sta *rtwsta = sta_to_rtwsta(sta); struct rtw89_sta_link *rtwsta_link; - rtwsta_link = rtw89_sta_get_link_inst(rtwsta, 0); + rtwsta_link = rtw89_get_designated_link(rtwsta); if (unlikely(!rtwsta_link)) return; @@ -1086,7 +1055,8 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_leave_lps(rtwdev); rtw89_hci_flush_queues(rtwdev, queues, drop); @@ -1094,8 +1064,6 @@ static void rtw89_ops_flush(struct ieee80211_hw *hw, struct ieee80211_vif *vif, __rtw89_drop_packets(rtwdev, vif); else rtw89_mac_flush_txq(rtwdev, queues, drop); - - mutex_unlock(&rtwdev->mutex); } struct rtw89_iter_bitrate_mask_data { @@ -1142,10 +1110,10 @@ static int rtw89_ops_set_bitrate_mask(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_phy_rate_pattern_vif(rtwdev, vif, mask); rtw89_ra_mask_info_update(rtwdev, vif, mask); - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1156,6 +1124,8 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) struct rtw89_dev *rtwdev = hw->priv; struct rtw89_hal *hal = &rtwdev->hal; + lockdep_assert_wiphy(hw->wiphy); + if (hal->ant_diversity) { if (tx_ant != rx_ant || hweight32(tx_ant) != 1) return -EINVAL; @@ -1163,12 +1133,10 @@ int rtw89_ops_set_antenna(struct ieee80211_hw *hw, u32 tx_ant, u32 rx_ant) return -EINVAL; } - mutex_lock(&rtwdev->mutex); hal->antenna_tx = tx_ant; hal->antenna_rx = rx_ant; hal->tx_path_diversity = false; hal->ant_diversity_fixed = true; - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1193,18 +1161,17 @@ static void rtw89_ops_sw_scan_start(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); + rtwvif_link = rtw89_get_designated_link(rtwvif); if (unlikely(!rtwvif_link)) { - rtw89_err(rtwdev, "sw scan start: find no link on HW-0\n"); - goto out; + rtw89_err(rtwdev, "sw scan start: find no designated link\n"); + return; } - rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, false); + rtw89_leave_lps(rtwdev); -out: - mutex_unlock(&rtwdev->mutex); + rtw89_core_scan_start(rtwdev, rtwvif_link, mac_addr, false); } static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, @@ -1214,18 +1181,15 @@ static void rtw89_ops_sw_scan_complete(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); + rtwvif_link = rtw89_get_designated_link(rtwvif); if (unlikely(!rtwvif_link)) { - rtw89_err(rtwdev, "sw scan complete: find no link on HW-0\n"); - goto out; + rtw89_err(rtwdev, "sw scan complete: find no designated link\n"); + return; } rtw89_core_scan_complete(rtwdev, rtwvif_link, false); - -out: - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_reconfig_complete(struct ieee80211_hw *hw, @@ -1245,33 +1209,33 @@ static int rtw89_ops_hw_scan(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct rtw89_vif_link *rtwvif_link; int ret; + lockdep_assert_wiphy(hw->wiphy); + if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return 1; - mutex_lock(&rtwdev->mutex); - - if (rtwdev->scanning || rtwvif->offchan) { - ret = -EBUSY; - goto out; - } + if (rtwdev->scanning || rtwvif->offchan) + return -EBUSY; - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); + rtwvif_link = rtw89_get_designated_link(rtwvif); if (unlikely(!rtwvif_link)) { - rtw89_err(rtwdev, "hw scan: find no link on HW-0\n"); - ret = -ENOLINK; - goto out; + rtw89_err(rtwdev, "hw scan: find no designated link\n"); + return -ENOLINK; } - rtw89_hw_scan_start(rtwdev, rtwvif_link, req); + rtw89_leave_lps(rtwdev); + rtw89_leave_ips_by_hwflags(rtwdev); + + ret = rtw89_hw_scan_start(rtwdev, rtwvif_link, req); + if (ret) + return ret; + ret = rtw89_hw_scan_offload(rtwdev, rtwvif_link, true); if (ret) { rtw89_hw_scan_abort(rtwdev, rtwvif_link); rtw89_err(rtwdev, "HW scan failed with status: %d\n", ret); } -out: - mutex_unlock(&rtwdev->mutex); - return ret; } @@ -1282,24 +1246,21 @@ static void rtw89_ops_cancel_hw_scan(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; + lockdep_assert_wiphy(hw->wiphy); + if (!RTW89_CHK_FW_FEATURE(SCAN_OFFLOAD, &rtwdev->fw)) return; - mutex_lock(&rtwdev->mutex); - if (!rtwdev->scanning) - goto out; + return; - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); + rtwvif_link = rtw89_get_designated_link(rtwvif); if (unlikely(!rtwvif_link)) { - rtw89_err(rtwdev, "cancel hw scan: find no link on HW-0\n"); - goto out; + rtw89_err(rtwdev, "cancel hw scan: find no designated link\n"); + return; } rtw89_hw_scan_abort(rtwdev, rtwvif_link); - -out: - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_sta_rc_update(struct ieee80211_hw *hw, @@ -1322,13 +1283,10 @@ static int rtw89_ops_add_chanctx(struct ieee80211_hw *hw, struct ieee80211_chanctx_conf *ctx) { struct rtw89_dev *rtwdev = hw->priv; - int ret; - mutex_lock(&rtwdev->mutex); - ret = rtw89_chanctx_ops_add(rtwdev, ctx); - mutex_unlock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); - return ret; + return rtw89_chanctx_ops_add(rtwdev, ctx); } static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw, @@ -1336,9 +1294,9 @@ static void rtw89_ops_remove_chanctx(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_chanctx_ops_remove(rtwdev, ctx); - mutex_unlock(&rtwdev->mutex); } static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw, @@ -1347,9 +1305,9 @@ static void rtw89_ops_change_chanctx(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + rtw89_chanctx_ops_change(rtwdev, ctx, changed); - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw, @@ -1360,25 +1318,18 @@ static int rtw89_ops_assign_vif_chanctx(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - int ret; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[link_conf->link_id]; if (unlikely(!rtwvif_link)) { rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_conf->link_id); - ret = -ENOLINK; - goto out; + return -ENOLINK; } - ret = rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx); - -out: - mutex_unlock(&rtwdev->mutex); - - return ret; + return rtw89_chanctx_ops_assign_vif(rtwdev, rtwvif_link, ctx); } static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw, @@ -1390,11 +1341,10 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif(vif); struct rtw89_vif_link *rtwvif_link; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtwvif_link = rtwvif->links[link_conf->link_id]; if (unlikely(!rtwvif_link)) { - mutex_unlock(&rtwdev->mutex); rtw89_err(rtwdev, "%s: rtwvif link (link_id %u) is not active\n", __func__, link_conf->link_id); @@ -1402,7 +1352,6 @@ static void rtw89_ops_unassign_vif_chanctx(struct ieee80211_hw *hw, } rtw89_chanctx_ops_unassign_vif(rtwdev, rtwvif_link, ctx); - mutex_unlock(&rtwdev->mutex); } static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw, @@ -1415,13 +1364,12 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw, struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif); struct rtw89_roc *roc = &rtwvif->roc; + lockdep_assert_wiphy(hw->wiphy); + if (!rtwvif) return -EINVAL; - mutex_lock(&rtwdev->mutex); - if (roc->state != RTW89_ROC_IDLE) { - mutex_unlock(&rtwdev->mutex); return -EBUSY; } @@ -1439,8 +1387,6 @@ static int rtw89_ops_remain_on_channel(struct ieee80211_hw *hw, rtw89_roc_start(rtwdev, rtwvif); - mutex_unlock(&rtwdev->mutex); - return 0; } @@ -1450,14 +1396,14 @@ static int rtw89_ops_cancel_remain_on_channel(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; struct rtw89_vif *rtwvif = vif_to_rtwvif_safe(vif); + lockdep_assert_wiphy(hw->wiphy); + if (!rtwvif) return -EINVAL; - cancel_delayed_work_sync(&rtwvif->roc.roc_work); + wiphy_delayed_work_cancel(hw->wiphy, &rtwvif->roc.roc_work); - mutex_lock(&rtwdev->mutex); rtw89_roc_end(rtwdev, rtwvif); - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1478,14 +1424,14 @@ static int rtw89_ops_set_tid_config(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + if (sta) rtw89_core_set_tid_config(rtwdev, sta, tid_config); else ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_set_tid_config_iter, tid_config); - mutex_unlock(&rtwdev->mutex); return 0; } @@ -1509,7 +1455,7 @@ static bool rtw89_ops_can_activate_links(struct ieee80211_hw *hw, { struct rtw89_dev *rtwdev = hw->priv; - guard(mutex)(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); return rtw89_can_work_on_links(rtwdev, vif, active_links); } @@ -1571,7 +1517,7 @@ int rtw89_ops_change_vif_links(struct ieee80211_hw *hw, int ret = 0; int i; - guard(mutex)(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_debug(rtwdev, RTW89_DBG_STATE, "%s: old_links (0x%08x) -> new_links (0x%08x)\n", @@ -1720,7 +1666,7 @@ int rtw89_ops_change_sta_links(struct ieee80211_hw *hw, unsigned long set_links = new_links & ~old_links; int ret = 0; - guard(mutex)(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); rtw89_debug(rtwdev, RTW89_DBG_STATE, "%s: old_links (0x%08x) -> new_links (0x%08x)\n", @@ -1750,13 +1696,12 @@ static int rtw89_ops_suspend(struct ieee80211_hw *hw, struct rtw89_dev *rtwdev = hw->priv; int ret; + lockdep_assert_wiphy(hw->wiphy); + set_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); - cancel_delayed_work_sync(&rtwdev->track_work); + wiphy_delayed_work_cancel(hw->wiphy, &rtwdev->track_work); - mutex_lock(&rtwdev->mutex); ret = rtw89_wow_suspend(rtwdev, wowlan); - mutex_unlock(&rtwdev->mutex); - if (ret) { rtw89_warn(rtwdev, "failed to suspend for wow %d\n", ret); clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); @@ -1771,15 +1716,15 @@ static int rtw89_ops_resume(struct ieee80211_hw *hw) struct rtw89_dev *rtwdev = hw->priv; int ret; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); + ret = rtw89_wow_resume(rtwdev); if (ret) rtw89_warn(rtwdev, "failed to resume for wow %d\n", ret); - mutex_unlock(&rtwdev->mutex); clear_bit(RTW89_FLAG_FORBIDDEN_TRACK_WROK, rtwdev->flags); - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, - RTW89_TRACK_WORK_PERIOD); + wiphy_delayed_work_queue(hw->wiphy, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); return ret ? 1 : 0; } @@ -1799,18 +1744,16 @@ static void rtw89_set_rekey_data(struct ieee80211_hw *hw, struct rtw89_wow_param *rtw_wow = &rtwdev->wow; struct rtw89_wow_gtk_info *gtk_info = &rtw_wow->gtk_info; + lockdep_assert_wiphy(hw->wiphy); + if (data->kek_len > sizeof(gtk_info->kek) || data->kck_len > sizeof(gtk_info->kck)) { rtw89_warn(rtwdev, "kek or kck length over fw limit\n"); return; } - mutex_lock(&rtwdev->mutex); - memcpy(gtk_info->kek, data->kek, data->kek_len); memcpy(gtk_info->kck, data->kck, data->kck_len); - - mutex_unlock(&rtwdev->mutex); } #endif @@ -1818,16 +1761,13 @@ static void rtw89_ops_rfkill_poll(struct ieee80211_hw *hw) { struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(hw->wiphy); /* wl_disable GPIO get floating when entering LPS */ if (test_bit(RTW89_FLAG_RUNNING, rtwdev->flags)) - goto out; + return; rtw89_core_rfkill_poll(rtwdev, false); - -out: - mutex_unlock(&rtwdev->mutex); } const struct ieee80211_ops rtw89_ops = { diff --git a/drivers/net/wireless/realtek/rtw89/mac_be.c b/drivers/net/wireless/realtek/rtw89/mac_be.c index 2dbdeae904ad..8c9d326dc907 100644 --- a/drivers/net/wireless/realtek/rtw89/mac_be.c +++ b/drivers/net/wireless/realtek/rtw89/mac_be.c @@ -29,6 +29,7 @@ static const u32 rtw89_mac_mem_base_addrs_be[RTW89_MAC_MEM_NUM] = { [RTW89_MAC_MEM_CPU_LOCAL] = CPU_LOCAL_BASE_ADDR_BE, [RTW89_MAC_MEM_BSSID_CAM] = BSSID_CAM_BASE_ADDR_BE, [RTW89_MAC_MEM_WD_PAGE] = WD_PAGE_BASE_ADDR_BE, + [RTW89_MAC_MEM_MLD_TBL] = MLD_TBL_BASE_ADDR_BE, }; static const struct rtw89_port_reg rtw89_port_base_be = { @@ -708,8 +709,8 @@ static int sec_eng_init_be(struct rtw89_dev *rtwdev) val32 |= B_BE_CLK_EN_CGCMP | B_BE_CLK_EN_WAPI | B_BE_CLK_EN_WEP_TKIP | B_BE_SEC_TX_ENC | B_BE_SEC_RX_DEC | B_BE_MC_DEC | B_BE_BC_DEC | - B_BE_BMC_MGNT_DEC | B_BE_UC_MGNT_DEC; - val32 &= ~B_BE_SEC_PRE_ENQUE_TX; + B_BE_BMC_MGNT_DEC | B_BE_UC_MGNT_DEC | + B_BE_SEC_PRE_ENQUE_TX; rtw89_write32(rtwdev, R_BE_SEC_ENG_CTRL, val32); rtw89_write32_set(rtwdev, R_BE_SEC_MPDU_PROC, B_BE_APPEND_ICV | B_BE_APPEND_MIC); @@ -1865,7 +1866,7 @@ int rtw89_mac_cfg_ctrl_path_v2(struct rtw89_dev *rtwdev, bool wl) if (wl) return 0; - for (i = 0; i < RTW89_PHY_MAX; i++) { + for (i = 0; i < RTW89_PHY_NUM; i++) { g[i].gnt_bt_sw_en = 1; g[i].gnt_bt = 1; g[i].gnt_wl_sw_en = 1; @@ -2585,6 +2586,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .mask = B_BE_RXTRIG_RU26_DIS, }, .wow_ctrl = {.addr = R_BE_WOW_CTRL, .mask = B_BE_WOW_WOWEN,}, + .agg_limit = {.addr = R_BE_AMPDU_AGG_LIMIT, .mask = B_BE_AMPDU_MAX_TIME_MASK,}, + .txcnt_limit = {.addr = R_BE_TXCNT, .mask = B_BE_L_TXCNT_LMT_MASK,}, .check_mac_en = rtw89_mac_check_mac_en_be, .sys_init = sys_init_be, @@ -2634,6 +2637,8 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = { .is_txq_empty = mac_is_txq_empty_be, + .prep_chan_list = rtw89_hw_scan_prep_chan_list_be, + .free_chan_list = rtw89_hw_scan_free_chan_list_be, .add_chan_list = rtw89_hw_scan_add_chan_list_be, .add_chan_list_pno = rtw89_pno_scan_add_chan_list_be, .scan_offload = rtw89_fw_h2c_scan_offload_be, diff --git a/drivers/net/wireless/realtek/rtw89/pci.c b/drivers/net/wireless/realtek/rtw89/pci.c index c2fe5a898dc7..064f6a940107 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.c +++ b/drivers/net/wireless/realtek/rtw89/pci.c @@ -228,7 +228,7 @@ int rtw89_pci_sync_skb_for_device_and_validate_rx_info(struct rtw89_dev *rtwdev, struct sk_buff *skb) { struct rtw89_pci_rx_info *rx_info = RTW89_PCI_RX_SKB_CB(skb); - int rx_tag_retry = 100; + int rx_tag_retry = 1000; int ret; do { @@ -3105,17 +3105,26 @@ static bool rtw89_pci_is_dac_compatible_bridge(struct rtw89_dev *rtwdev) return false; } -static void rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev) +static int rtw89_pci_cfg_dac(struct rtw89_dev *rtwdev, bool force) { struct rtw89_pci *rtwpci = (struct rtw89_pci *)rtwdev->priv; + struct pci_dev *pdev = rtwpci->pdev; + int ret; + u8 val; - if (!rtwpci->enable_dac) - return; + if (!rtwpci->enable_dac && !force) + return 0; if (!rtw89_pci_chip_is_manual_dac(rtwdev)) - return; + return 0; - rtw89_pci_config_byte_set(rtwdev, RTW89_PCIE_L1_CTRL, RTW89_PCIE_BIT_EN_64BITS); + /* Configure DAC only via PCI config API, not DBI interfaces */ + ret = pci_read_config_byte(pdev, RTW89_PCIE_L1_CTRL, &val); + if (ret) + return ret; + + val |= RTW89_PCIE_BIT_EN_64BITS; + return pci_write_config_byte(pdev, RTW89_PCIE_L1_CTRL, val); } static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev, @@ -3133,13 +3142,16 @@ static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev, } if (!rtw89_pci_is_dac_compatible_bridge(rtwdev)) - goto no_dac; + goto try_dac_done; ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(36)); if (!ret) { - rtwpci->enable_dac = true; - rtw89_pci_cfg_dac(rtwdev); - } else { + ret = rtw89_pci_cfg_dac(rtwdev, true); + if (!ret) { + rtwpci->enable_dac = true; + goto try_dac_done; + } + ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); if (ret) { rtw89_err(rtwdev, @@ -3147,7 +3159,7 @@ static int rtw89_pci_setup_mapping(struct rtw89_dev *rtwdev, goto err_release_regions; } } -no_dac: +try_dac_done: resource_len = pci_resource_len(pdev, bar_id); rtwpci->mmap = pci_iomap(pdev, bar_id, resource_len); @@ -4302,7 +4314,7 @@ static void rtw89_pci_l2_hci_ldo(struct rtw89_dev *rtwdev) void rtw89_pci_basic_cfg(struct rtw89_dev *rtwdev, bool resume) { if (resume) - rtw89_pci_cfg_dac(rtwdev); + rtw89_pci_cfg_dac(rtwdev, false); rtw89_pci_disable_eq(rtwdev); rtw89_pci_filter_out(rtwdev); diff --git a/drivers/net/wireless/realtek/rtw89/pci.h b/drivers/net/wireless/realtek/rtw89/pci.h index 4d11c3dd60a5..79fef5f90140 100644 --- a/drivers/net/wireless/realtek/rtw89/pci.h +++ b/drivers/net/wireless/realtek/rtw89/pci.h @@ -455,34 +455,36 @@ #define B_BE_RX0DMA_INT_EN BIT(0) #define R_BE_HAXI_HISR00 0xB0B4 -#define B_BE_RDU_CH6_INT BIT(28) -#define B_BE_RDU_CH5_INT BIT(27) -#define B_BE_RDU_CH4_INT BIT(26) -#define B_BE_RDU_CH2_INT BIT(25) -#define B_BE_RDU_CH1_INT BIT(24) -#define B_BE_RDU_CH0_INT BIT(23) -#define B_BE_RXDMA_STUCK_INT BIT(22) -#define B_BE_TXDMA_STUCK_INT BIT(21) -#define B_BE_TXDMA_CH14_INT BIT(20) -#define B_BE_TXDMA_CH13_INT BIT(19) -#define B_BE_TXDMA_CH12_INT BIT(18) -#define B_BE_TXDMA_CH11_INT BIT(17) -#define B_BE_TXDMA_CH10_INT BIT(16) -#define B_BE_TXDMA_CH9_INT BIT(15) -#define B_BE_TXDMA_CH8_INT BIT(14) -#define B_BE_TXDMA_CH7_INT BIT(13) -#define B_BE_TXDMA_CH6_INT BIT(12) -#define B_BE_TXDMA_CH5_INT BIT(11) -#define B_BE_TXDMA_CH4_INT BIT(10) -#define B_BE_TXDMA_CH3_INT BIT(9) -#define B_BE_TXDMA_CH2_INT BIT(8) -#define B_BE_TXDMA_CH1_INT BIT(7) -#define B_BE_TXDMA_CH0_INT BIT(6) -#define B_BE_RPQ1DMA_INT BIT(5) -#define B_BE_RX1P1DMA_INT BIT(4) +#define B_BE_RDU_CH5_INT_V1 BIT(30) +#define B_BE_RDU_CH4_INT_V1 BIT(29) +#define B_BE_RDU_CH3_INT_V1 BIT(28) +#define B_BE_RDU_CH2_INT_V1 BIT(27) +#define B_BE_RDU_CH1_INT_V1 BIT(26) +#define B_BE_RDU_CH0_INT_V1 BIT(25) +#define B_BE_RXDMA_STUCK_INT_V1 BIT(24) +#define B_BE_TXDMA_STUCK_INT_V1 BIT(23) +#define B_BE_TXDMA_CH14_INT_V1 BIT(22) +#define B_BE_TXDMA_CH13_INT_V1 BIT(21) +#define B_BE_TXDMA_CH12_INT_V1 BIT(20) +#define B_BE_TXDMA_CH11_INT_V1 BIT(19) +#define B_BE_TXDMA_CH10_INT_V1 BIT(18) +#define B_BE_TXDMA_CH9_INT_V1 BIT(17) +#define B_BE_TXDMA_CH8_INT_V1 BIT(16) +#define B_BE_TXDMA_CH7_INT_V1 BIT(15) +#define B_BE_TXDMA_CH6_INT_V1 BIT(14) +#define B_BE_TXDMA_CH5_INT_V1 BIT(13) +#define B_BE_TXDMA_CH4_INT_V1 BIT(12) +#define B_BE_TXDMA_CH3_INT_V1 BIT(11) +#define B_BE_TXDMA_CH2_INT_V1 BIT(10) +#define B_BE_TXDMA_CH1_INT_V1 BIT(9) +#define B_BE_TXDMA_CH0_INT_V1 BIT(8) +#define B_BE_RX1P1DMA_INT_V1 BIT(7) +#define B_BE_RX0P1DMA_INT_V1 BIT(6) +#define B_BE_RO1DMA_INT BIT(5) +#define B_BE_RP1DMA_INT BIT(4) #define B_BE_RX1DMA_INT BIT(3) -#define B_BE_RPQ0DMA_INT BIT(2) -#define B_BE_RX0P1DMA_INT BIT(1) +#define B_BE_RO0DMA_INT BIT(2) +#define B_BE_RP0DMA_INT BIT(1) #define B_BE_RX0DMA_INT BIT(0) /* TX/RX */ diff --git a/drivers/net/wireless/realtek/rtw89/pci_be.c b/drivers/net/wireless/realtek/rtw89/pci_be.c index cd39eebe8186..12e6a0cbb889 100644 --- a/drivers/net/wireless/realtek/rtw89/pci_be.c +++ b/drivers/net/wireless/realtek/rtw89/pci_be.c @@ -666,7 +666,7 @@ SIMPLE_DEV_PM_OPS(rtw89_pm_ops_be, rtw89_pci_suspend_be, rtw89_pci_resume_be); EXPORT_SYMBOL(rtw89_pm_ops_be); const struct rtw89_pci_gen_def rtw89_pci_gen_be = { - .isr_rdu = B_BE_RDU_CH1_INT | B_BE_RDU_CH0_INT, + .isr_rdu = B_BE_RDU_CH1_INT_V1 | B_BE_RDU_CH0_INT_V1, .isr_halt_c2h = B_BE_HALT_C2H_INT, .isr_wdt_timeout = B_BE_WDT_TIMEOUT_INT, .isr_clear_rpq = {R_BE_PCIE_DMA_ISR, B_BE_PCIE_RX_RPQ0_ISR_V1}, diff --git a/drivers/net/wireless/realtek/rtw89/phy.c b/drivers/net/wireless/realtek/rtw89/phy.c index c7c05f7fda1d..76a2e26d4a10 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.c +++ b/drivers/net/wireless/realtek/rtw89/phy.c @@ -2034,33 +2034,47 @@ static s8 rtw89_phy_ant_gain_query(struct rtw89_dev *rtwdev, ant_gain->offset[path][subband_h]); } -static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u8 band, u32 center_freq) +static s8 rtw89_phy_ant_gain_offset(struct rtw89_dev *rtwdev, u32 center_freq) { + s8 offset_patha, offset_pathb; + + offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq); + offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, center_freq); + + if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw)) + return min(offset_patha, offset_pathb); + + return max(offset_patha, offset_pathb); +} + +static bool rtw89_can_apply_ant_gain(struct rtw89_dev *rtwdev, u8 band) +{ + const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; const struct rtw89_chip_info *chip = rtwdev->chip; u8 regd = rtw89_regd_get(rtwdev, band); - s8 offset_patha, offset_pathb; if (!chip->support_ant_gain) - return 0; + return false; - if (!(ant_gain->regd_enabled & BIT(regd))) - return 0; + if (ant_gain->block_country || !(ant_gain->regd_enabled & BIT(regd))) + return false; - offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, center_freq); - offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, center_freq); + if (!rfe_parms->has_da) + return false; - return max(offset_patha, offset_pathb); + return true; } s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan) { - struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; - u8 regd = rtw89_regd_get(rtwdev, chan->band_type); s8 offset_patha, offset_pathb; - if (!(ant_gain->regd_enabled & BIT(regd))) + if (!rtw89_can_apply_ant_gain(rtwdev, chan->band_type)) + return 0; + + if (RTW89_CHK_FW_FEATURE(NO_POWER_DIFFERENCE, &rtwdev->fw)) return 0; offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq); @@ -2070,24 +2084,25 @@ s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev, } EXPORT_SYMBOL(rtw89_phy_ant_gain_pwr_offset); -void rtw89_print_ant_gain(struct seq_file *m, struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan) +int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct rtw89_chan *chan) { - struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; - const struct rtw89_chip_info *chip = rtwdev->chip; - u8 regd = rtw89_regd_get(rtwdev, chan->band_type); + char *p = buf, *end = buf + bufsz; s8 offset_patha, offset_pathb; - if (!chip->support_ant_gain || !(ant_gain->regd_enabled & BIT(regd))) { - seq_puts(m, "no DAG is applied\n"); - return; + if (!rtw89_can_apply_ant_gain(rtwdev, chan->band_type)) { + p += scnprintf(p, end - p, "no DAG is applied\n"); + goto out; } offset_patha = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_A, chan->freq); offset_pathb = rtw89_phy_ant_gain_query(rtwdev, RF_PATH_B, chan->freq); - seq_printf(m, "ChainA offset: %d dBm\n", offset_patha); - seq_printf(m, "ChainB offset: %d dBm\n", offset_pathb); + p += scnprintf(p, end - p, "ChainA offset: %d dBm\n", offset_patha); + p += scnprintf(p, end - p, "ChainB offset: %d dBm\n", offset_pathb); + +out: + return p - buf; } static const u8 rtw89_rs_idx_num_ax[] = { @@ -2240,20 +2255,31 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, u8 bw, u8 ntx, u8 rs, u8 bf, u8 ch) { const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; + const struct rtw89_txpwr_rule_2ghz *rule_da_2ghz = &rfe_parms->rule_da_2ghz; + const struct rtw89_txpwr_rule_5ghz *rule_da_5ghz = &rfe_parms->rule_da_5ghz; + const struct rtw89_txpwr_rule_6ghz *rule_da_6ghz = &rfe_parms->rule_da_6ghz; const struct rtw89_txpwr_rule_2ghz *rule_2ghz = &rfe_parms->rule_2ghz; const struct rtw89_txpwr_rule_5ghz *rule_5ghz = &rfe_parms->rule_5ghz; const struct rtw89_txpwr_rule_6ghz *rule_6ghz = &rfe_parms->rule_6ghz; struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band); + bool has_ant_gain = rtw89_can_apply_ant_gain(rtwdev, band); u32 freq = ieee80211_channel_to_frequency(ch, nl_band); u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch); + s8 lmt = 0, da_lmt = S8_MAX, sar, offset = 0; u8 regd = rtw89_regd_get(rtwdev, band); u8 reg6 = regulatory->reg_6ghz_power; - s8 lmt = 0, sar, offset; + struct rtw89_sar_parm sar_parm = { + .center_freq = freq, + .ntx = ntx, + }; s8 cstr; switch (band) { case RTW89_BAND_2G: + if (has_ant_gain) + da_lmt = (*rule_da_2ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx]; + lmt = (*rule_2ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx]; if (lmt) break; @@ -2261,6 +2287,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, lmt = (*rule_2ghz->lmt)[bw][ntx][rs][bf][RTW89_WW][ch_idx]; break; case RTW89_BAND_5G: + if (has_ant_gain) + da_lmt = (*rule_da_5ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx]; + lmt = (*rule_5ghz->lmt)[bw][ntx][rs][bf][regd][ch_idx]; if (lmt) break; @@ -2268,6 +2297,9 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, lmt = (*rule_5ghz->lmt)[bw][ntx][rs][bf][RTW89_WW][ch_idx]; break; case RTW89_BAND_6G: + if (has_ant_gain) + da_lmt = (*rule_da_6ghz->lmt)[bw][ntx][rs][bf][regd][reg6][ch_idx]; + lmt = (*rule_6ghz->lmt)[bw][ntx][rs][bf][regd][reg6][ch_idx]; if (lmt) break; @@ -2281,9 +2313,12 @@ s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, return 0; } - offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq); - lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt + offset); - sar = rtw89_query_sar(rtwdev, freq); + da_lmt = da_lmt ?: S8_MAX; + if (da_lmt != S8_MAX) + offset = rtw89_phy_ant_gain_offset(rtwdev, freq); + + lmt = rtw89_phy_txpwr_rf_to_mac(rtwdev, min(lmt + offset, da_lmt)); + sar = rtw89_query_sar(rtwdev, &sar_parm); cstr = rtw89_phy_get_tpe_constraint(rtwdev, band); return min3(lmt, sar, cstr); @@ -2500,20 +2535,31 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band, u8 ru, u8 ntx, u8 ch) { const struct rtw89_rfe_parms *rfe_parms = rtwdev->rfe_parms; + const struct rtw89_txpwr_rule_2ghz *rule_da_2ghz = &rfe_parms->rule_da_2ghz; + const struct rtw89_txpwr_rule_5ghz *rule_da_5ghz = &rfe_parms->rule_da_5ghz; + const struct rtw89_txpwr_rule_6ghz *rule_da_6ghz = &rfe_parms->rule_da_6ghz; const struct rtw89_txpwr_rule_2ghz *rule_2ghz = &rfe_parms->rule_2ghz; const struct rtw89_txpwr_rule_5ghz *rule_5ghz = &rfe_parms->rule_5ghz; const struct rtw89_txpwr_rule_6ghz *rule_6ghz = &rfe_parms->rule_6ghz; struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; enum nl80211_band nl_band = rtw89_hw_to_nl80211_band(band); + bool has_ant_gain = rtw89_can_apply_ant_gain(rtwdev, band); u32 freq = ieee80211_channel_to_frequency(ch, nl_band); u8 ch_idx = rtw89_channel_to_idx(rtwdev, band, ch); + s8 lmt_ru = 0, da_lmt_ru = S8_MAX, sar, offset = 0; u8 regd = rtw89_regd_get(rtwdev, band); u8 reg6 = regulatory->reg_6ghz_power; - s8 lmt_ru = 0, sar, offset; + struct rtw89_sar_parm sar_parm = { + .center_freq = freq, + .ntx = ntx, + }; s8 cstr; switch (band) { case RTW89_BAND_2G: + if (has_ant_gain) + da_lmt_ru = (*rule_da_2ghz->lmt_ru)[ru][ntx][regd][ch_idx]; + lmt_ru = (*rule_2ghz->lmt_ru)[ru][ntx][regd][ch_idx]; if (lmt_ru) break; @@ -2521,6 +2567,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band, lmt_ru = (*rule_2ghz->lmt_ru)[ru][ntx][RTW89_WW][ch_idx]; break; case RTW89_BAND_5G: + if (has_ant_gain) + da_lmt_ru = (*rule_da_5ghz->lmt_ru)[ru][ntx][regd][ch_idx]; + lmt_ru = (*rule_5ghz->lmt_ru)[ru][ntx][regd][ch_idx]; if (lmt_ru) break; @@ -2528,6 +2577,9 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band, lmt_ru = (*rule_5ghz->lmt_ru)[ru][ntx][RTW89_WW][ch_idx]; break; case RTW89_BAND_6G: + if (has_ant_gain) + da_lmt_ru = (*rule_da_6ghz->lmt_ru)[ru][ntx][regd][reg6][ch_idx]; + lmt_ru = (*rule_6ghz->lmt_ru)[ru][ntx][regd][reg6][ch_idx]; if (lmt_ru) break; @@ -2541,9 +2593,12 @@ s8 rtw89_phy_read_txpwr_limit_ru(struct rtw89_dev *rtwdev, u8 band, return 0; } - offset = rtw89_phy_ant_gain_offset(rtwdev, band, freq); - lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, lmt_ru + offset); - sar = rtw89_query_sar(rtwdev, freq); + da_lmt_ru = da_lmt_ru ?: S8_MAX; + if (da_lmt_ru != S8_MAX) + offset = rtw89_phy_ant_gain_offset(rtwdev, freq); + + lmt_ru = rtw89_phy_txpwr_rf_to_mac(rtwdev, min(lmt_ru + offset, da_lmt_ru)); + sar = rtw89_query_sar(rtwdev, &sar_parm); cstr = rtw89_phy_get_tpe_constraint(rtwdev, band); return min3(lmt_ru, sar, cstr); @@ -3000,6 +3055,35 @@ void (* const rtw89_phy_c2h_ra_handler[])(struct rtw89_dev *rtwdev, [RTW89_PHY_C2H_FUNC_TXSTS] = NULL, }; +static void +rtw89_phy_c2h_lowrt_rty(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ +} + +static void +rtw89_phy_c2h_fw_scan_rpt(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) +{ + const struct rtw89_c2h_fw_scan_rpt *c2h_rpt = + (const struct rtw89_c2h_fw_scan_rpt *)c2h->data; + + rtw89_debug(rtwdev, RTW89_DBG_DIG, + "%s: band: %u, op_chan: %u, PD_low_bd(ofdm, cck): (-%d, %d), phy_idx: %u\n", + __func__, c2h_rpt->band, c2h_rpt->center_ch, + PD_LOWER_BOUND_BASE - (c2h_rpt->ofdm_pd_idx << 1), + c2h_rpt->cck_pd_idx, c2h_rpt->phy_idx); +} + +static +void (* const rtw89_phy_c2h_dm_handler[])(struct rtw89_dev *rtwdev, + struct sk_buff *c2h, u32 len) = { + [RTW89_PHY_C2H_DM_FUNC_FW_TEST] = NULL, + [RTW89_PHY_C2H_DM_FUNC_FW_TRIG_TX_RPT] = NULL, + [RTW89_PHY_C2H_DM_FUNC_SIGB] = NULL, + [RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY] = rtw89_phy_c2h_lowrt_rty, + [RTW89_PHY_C2H_DM_FUNC_MCC_DIG] = NULL, + [RTW89_PHY_C2H_DM_FUNC_FW_SCAN] = rtw89_phy_c2h_fw_scan_rpt, +}; + static void rtw89_phy_c2h_rfk_rpt_log(struct rtw89_dev *rtwdev, enum rtw89_phy_c2h_rfk_log_func func, void *content, u16 len) @@ -3455,6 +3539,30 @@ rtw89_phy_c2h_rfk_report_state(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u3 static void rtw89_phy_c2h_rfk_log_tas_pwr(struct rtw89_dev *rtwdev, struct sk_buff *c2h, u32 len) { + const struct rtw89_c2h_rf_tas_info *rf_tas = + (const struct rtw89_c2h_rf_tas_info *)c2h->data; + const enum rtw89_sar_sources src = rtwdev->sar.src; + struct rtw89_tas_info *tas = &rtwdev->tas; + u64 linear = 0; + u32 i, cur_idx; + s16 txpwr; + + if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) + return; + + cur_idx = le32_to_cpu(rf_tas->cur_idx); + for (i = 0; i < cur_idx; i++) { + txpwr = (s16)le16_to_cpu(rf_tas->txpwr_history[i]); + linear += rtw89_db_quarter_to_linear(txpwr); + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "tas: index: %u, txpwr: %d\n", i, txpwr); + } + + if (cur_idx == 0) + tas->instant_txpwr = rtw89_db_to_linear(0); + else + tas->instant_txpwr = DIV_ROUND_DOWN_ULL(linear, cur_idx); } static @@ -3511,9 +3619,9 @@ void rtw89_phy_c2h_handle(struct rtw89_dev *rtwdev, struct sk_buff *skb, handler = rtw89_phy_c2h_rfk_report_handler[func]; break; case RTW89_PHY_C2H_CLASS_DM: - if (func == RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY) - return; - fallthrough; + if (func < ARRAY_SIZE(rtw89_phy_c2h_dm_handler)) + handler = rtw89_phy_c2h_dm_handler[func]; + break; default: rtw89_info(rtwdev, "PHY c2h class %d not support\n", class); return; @@ -4600,7 +4708,7 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev) cfo->dcfo_avg = 0; rtw89_debug(rtwdev, RTW89_DBG_CFO, "CFO:total_sta_assoc=%d\n", rtwdev->total_sta_assoc); - if (rtwdev->total_sta_assoc == 0) { + if (rtwdev->total_sta_assoc == 0 || rtw89_is_mlo_1_1(rtwdev)) { rtw89_phy_cfo_reset(rtwdev); return; } @@ -4651,29 +4759,28 @@ static void rtw89_phy_cfo_dm(struct rtw89_dev *rtwdev) rtw89_phy_cfo_statistics_reset(rtwdev); } -void rtw89_phy_cfo_track_work(struct work_struct *work) +void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, cfo_track_work.work); struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); + if (!cfo->cfo_trig_by_timer_en) - goto out; + return; rtw89_leave_ps_mode(rtwdev); rtw89_phy_cfo_dm(rtwdev); - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->cfo_track_work, - msecs_to_jiffies(cfo->cfo_timer_ms)); -out: - mutex_unlock(&rtwdev->mutex); + wiphy_delayed_work_queue(wiphy, &rtwdev->cfo_track_work, + msecs_to_jiffies(cfo->cfo_timer_ms)); } static void rtw89_phy_cfo_start_work(struct rtw89_dev *rtwdev) { struct rtw89_cfo_tracking_info *cfo = &rtwdev->cfo_tracking; - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->cfo_track_work, - msecs_to_jiffies(cfo->cfo_timer_ms)); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->cfo_track_work, + msecs_to_jiffies(cfo->cfo_timer_ms)); } void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev) @@ -5115,7 +5222,6 @@ static void rtw89_phy_stat_thermal_update(struct rtw89_dev *rtwdev) struct rtw89_phy_iter_rssi_data { struct rtw89_dev *rtwdev; - struct rtw89_phy_ch_info *ch_info; bool rssi_changed; }; @@ -5123,10 +5229,15 @@ static void __rtw89_phy_stat_rssi_update_iter(struct rtw89_sta_link *rtwsta_link, struct rtw89_phy_iter_rssi_data *rssi_data) { - struct rtw89_phy_ch_info *ch_info = rssi_data->ch_info; + struct rtw89_vif_link *rtwvif_link = rtwsta_link->rtwvif_link; + struct rtw89_dev *rtwdev = rssi_data->rtwdev; + struct rtw89_phy_ch_info *ch_info; + struct rtw89_bb_ctx *bb; unsigned long rssi_curr; rssi_curr = ewma_rssi_read(&rtwsta_link->avg_rssi); + bb = rtw89_get_bb_ctx(rtwdev, rtwvif_link->phy_idx); + ch_info = &bb->ch_info; if (rssi_curr < ch_info->rssi_min) { ch_info->rssi_min = rssi_curr; @@ -5157,11 +5268,13 @@ static void rtw89_phy_stat_rssi_update_iter(void *data, static void rtw89_phy_stat_rssi_update(struct rtw89_dev *rtwdev) { - struct rtw89_phy_iter_rssi_data rssi_data = {0}; + struct rtw89_phy_iter_rssi_data rssi_data = {}; + struct rtw89_bb_ctx *bb; rssi_data.rtwdev = rtwdev; - rssi_data.ch_info = &rtwdev->ch_info; - rssi_data.ch_info->rssi_min = U8_MAX; + rtw89_for_each_active_bb(rtwdev, bb) + bb->ch_info.rssi_min = U8_MAX; + ieee80211_iterate_stations_atomic(rtwdev->hw, rtw89_phy_stat_rssi_update_iter, &rssi_data); @@ -5199,24 +5312,27 @@ void rtw89_phy_stat_track(struct rtw89_dev *rtwdev) memset(&phystat->cur_pkt_stat, 0, sizeof(phystat->cur_pkt_stat)); } -static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, u32 time_us) +static u16 rtw89_phy_ccx_us_to_idx(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u32 time_us) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; return time_us >> (ilog2(CCX_US_BASE_RATIO) + env->ccx_unit_idx); } -static u32 rtw89_phy_ccx_idx_to_us(struct rtw89_dev *rtwdev, u16 idx) +static u32 rtw89_phy_ccx_idx_to_us(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u16 idx) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; return idx << (ilog2(CCX_US_BASE_RATIO) + env->ccx_unit_idx); } -static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; env->ccx_manual_ctrl = false; @@ -5225,17 +5341,20 @@ static void rtw89_phy_ccx_top_setting_init(struct rtw89_dev *rtwdev) env->ccx_period = 0; env->ccx_unit_idx = RTW89_CCX_32_US; - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->en_mask, 1); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->trig_opt_mask, 1); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->edcca_opt_mask, - RTW89_CCX_EDCCA_BW20_0); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->en_mask, 1, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->trig_opt_mask, 1, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->edcca_opt_mask, + RTW89_CCX_EDCCA_BW20_0, bb->phy_idx); } -static u16 rtw89_phy_ccx_get_report(struct rtw89_dev *rtwdev, u16 report, - u16 score) +static u16 rtw89_phy_ccx_get_report(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, + u16 report, u16 score) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; u32 numer = 0; u16 ret = 0; @@ -5275,9 +5394,10 @@ static void rtw89_phy_ccx_ms_to_period_unit(struct rtw89_dev *rtwdev, *period, *unit_idx); } -static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "lv:(%d)->(0)\n", env->ccx_rac_lv); @@ -5288,9 +5408,10 @@ static void rtw89_phy_ccx_racing_release(struct rtw89_dev *rtwdev) } static bool rtw89_phy_ifs_clm_th_update_check(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, struct rtw89_ccx_para_info *para) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; bool is_update = env->ifs_clm_app != para->ifs_clm_app; u8 i = 0; u16 *ifs_th_l = env->ifs_clm_th_l; @@ -5325,12 +5446,12 @@ static bool rtw89_phy_ifs_clm_th_update_check(struct rtw89_dev *rtwdev, */ ifs_th_l[IFS_CLM_TH_START_IDX] = 0; ifs_th_h_us[IFS_CLM_TH_START_IDX] = ifs_th0_us; - ifs_th_h[IFS_CLM_TH_START_IDX] = rtw89_phy_ccx_us_to_idx(rtwdev, + ifs_th_h[IFS_CLM_TH_START_IDX] = rtw89_phy_ccx_us_to_idx(rtwdev, bb, ifs_th0_us); for (i = 1; i < RTW89_IFS_CLM_NUM; i++) { ifs_th_l[i] = ifs_th_h[i - 1] + 1; ifs_th_h_us[i] = ifs_th_h_us[i - 1] * ifs_th_times; - ifs_th_h[i] = rtw89_phy_ccx_us_to_idx(rtwdev, ifs_th_h_us[i]); + ifs_th_h[i] = rtw89_phy_ccx_us_to_idx(rtwdev, bb, ifs_th_h_us[i]); } ifs_update_finished: @@ -5341,30 +5462,31 @@ ifs_update_finished: return is_update; } -static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; u8 i = 0; - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_l_mask, - env->ifs_clm_th_l[0]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_l_mask, - env->ifs_clm_th_l[1]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_l_mask, - env->ifs_clm_th_l[2]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_l_mask, - env->ifs_clm_th_l[3]); - - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_h_mask, - env->ifs_clm_th_h[0]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_h_mask, - env->ifs_clm_th_h[1]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_h_mask, - env->ifs_clm_th_h[2]); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_h_mask, - env->ifs_clm_th_h[3]); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_l_mask, + env->ifs_clm_th_l[0], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_l_mask, + env->ifs_clm_th_l[1], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_l_mask, + env->ifs_clm_th_l[2], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_l_mask, + env->ifs_clm_th_l[3], bb->phy_idx); + + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_th_h_mask, + env->ifs_clm_th_h[0], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_th_h_mask, + env->ifs_clm_th_h[1], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_th_h_mask, + env->ifs_clm_th_h[2], bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_th_h_mask, + env->ifs_clm_th_h[3], bb->phy_idx); for (i = 0; i < RTW89_IFS_CLM_NUM; i++) rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, @@ -5372,31 +5494,38 @@ static void rtw89_phy_ifs_clm_set_th_reg(struct rtw89_dev *rtwdev) i + 1, env->ifs_clm_th_l[i], env->ifs_clm_th_h[i]); } -static void rtw89_phy_ifs_clm_setting_init(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_setting_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; - struct rtw89_ccx_para_info para = {0}; + struct rtw89_ccx_para_info para = {}; env->ifs_clm_app = RTW89_IFS_CLM_BACKGROUND; env->ifs_clm_mntr_time = 0; para.ifs_clm_app = RTW89_IFS_CLM_INIT; - if (rtw89_phy_ifs_clm_th_update_check(rtwdev, ¶)) - rtw89_phy_ifs_clm_set_th_reg(rtwdev); + if (rtw89_phy_ifs_clm_th_update_check(rtwdev, bb, ¶)) + rtw89_phy_ifs_clm_set_th_reg(rtwdev, bb); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_collect_en_mask, true); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_en_mask, true); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_en_mask, true); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_en_mask, true); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_en_mask, true); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_collect_en_mask, true, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t1_addr, ccx->ifs_t1_en_mask, true, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t2_addr, ccx->ifs_t2_en_mask, true, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t3_addr, ccx->ifs_t3_en_mask, true, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_t4_addr, ccx->ifs_t4_en_mask, true, + bb->phy_idx); } static int rtw89_phy_ccx_racing_ctrl(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, enum rtw89_env_racing_lv level) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; int ret = 0; if (level >= RTW89_RAC_MAX_NUM) { @@ -5425,56 +5554,62 @@ static int rtw89_phy_ccx_racing_ctrl(struct rtw89_dev *rtwdev, return ret; } -static void rtw89_phy_ccx_trigger(struct rtw89_dev *rtwdev) +static void rtw89_phy_ccx_trigger(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 0); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 0); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 1); - rtw89_phy_set_phy_regs(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 0, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 0, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, ccx->ifs_clm_cnt_clear_mask, 1, + bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->setting_addr, ccx->measurement_trig_mask, 1, + bb->phy_idx); env->ccx_ongoing = true; } -static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev) +static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; u8 i = 0; u32 res = 0; env->ifs_clm_tx_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_tx, PERCENT); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_tx, PERCENT); env->ifs_clm_edcca_excl_cca_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_edcca_excl_cca, + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_edcca_excl_cca, PERCENT); env->ifs_clm_cck_fa_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckfa, PERCENT); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckfa, PERCENT); env->ifs_clm_ofdm_fa_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmfa, PERCENT); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmfa, PERCENT); env->ifs_clm_cck_cca_excl_fa_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckcca_excl_fa, + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckcca_excl_fa, PERCENT); env->ifs_clm_ofdm_cca_excl_fa_ratio = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmcca_excl_fa, + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmcca_excl_fa, PERCENT); env->ifs_clm_cck_fa_permil = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_cckfa, PERMIL); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_cckfa, PERMIL); env->ifs_clm_ofdm_fa_permil = - rtw89_phy_ccx_get_report(rtwdev, env->ifs_clm_ofdmfa, PERMIL); + rtw89_phy_ccx_get_report(rtwdev, bb, env->ifs_clm_ofdmfa, PERMIL); for (i = 0; i < RTW89_IFS_CLM_NUM; i++) { if (env->ifs_clm_his[i] > ENV_MNTR_IFSCLM_HIS_MAX) { env->ifs_clm_ifs_avg[i] = ENV_MNTR_FAIL_DWORD; } else { env->ifs_clm_ifs_avg[i] = - rtw89_phy_ccx_idx_to_us(rtwdev, + rtw89_phy_ccx_idx_to_us(rtwdev, bb, env->ifs_clm_avg[i]); } - res = rtw89_phy_ccx_idx_to_us(rtwdev, env->ifs_clm_cca[i]); + res = rtw89_phy_ccx_idx_to_us(rtwdev, bb, env->ifs_clm_cca[i]); res += env->ifs_clm_his[i] >> 1; if (env->ifs_clm_his[i]) res /= env->ifs_clm_his[i]; @@ -5504,81 +5639,82 @@ static void rtw89_phy_ifs_clm_get_utility(struct rtw89_dev *rtwdev) env->ifs_clm_cca_avg[i]); } -static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev) +static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; u8 i = 0; - if (rtw89_phy_read32_mask(rtwdev, ccx->ifs_total_addr, - ccx->ifs_cnt_done_mask) == 0) { + if (rtw89_phy_read32_idx(rtwdev, ccx->ifs_total_addr, + ccx->ifs_cnt_done_mask, bb->phy_idx) == 0) { rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Get IFS_CLM report Fail\n"); return false; } env->ifs_clm_tx = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_tx_cnt_addr, - ccx->ifs_clm_tx_cnt_msk); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_tx_cnt_addr, + ccx->ifs_clm_tx_cnt_msk, bb->phy_idx); env->ifs_clm_edcca_excl_cca = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_tx_cnt_addr, - ccx->ifs_clm_edcca_excl_cca_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_tx_cnt_addr, + ccx->ifs_clm_edcca_excl_cca_fa_mask, bb->phy_idx); env->ifs_clm_cckcca_excl_fa = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_cca_addr, - ccx->ifs_clm_cckcca_excl_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_cca_addr, + ccx->ifs_clm_cckcca_excl_fa_mask, bb->phy_idx); env->ifs_clm_ofdmcca_excl_fa = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_cca_addr, - ccx->ifs_clm_ofdmcca_excl_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_cca_addr, + ccx->ifs_clm_ofdmcca_excl_fa_mask, bb->phy_idx); env->ifs_clm_cckfa = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_fa_addr, - ccx->ifs_clm_cck_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_fa_addr, + ccx->ifs_clm_cck_fa_mask, bb->phy_idx); env->ifs_clm_ofdmfa = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_clm_fa_addr, - ccx->ifs_clm_ofdm_fa_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_clm_fa_addr, + ccx->ifs_clm_ofdm_fa_mask, bb->phy_idx); env->ifs_clm_his[0] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, - ccx->ifs_t1_his_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, + ccx->ifs_t1_his_mask, bb->phy_idx); env->ifs_clm_his[1] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, - ccx->ifs_t2_his_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, + ccx->ifs_t2_his_mask, bb->phy_idx); env->ifs_clm_his[2] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, - ccx->ifs_t3_his_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, + ccx->ifs_t3_his_mask, bb->phy_idx); env->ifs_clm_his[3] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_his_addr, - ccx->ifs_t4_his_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_his_addr, + ccx->ifs_t4_his_mask, bb->phy_idx); env->ifs_clm_avg[0] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_l_addr, - ccx->ifs_t1_avg_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_l_addr, + ccx->ifs_t1_avg_mask, bb->phy_idx); env->ifs_clm_avg[1] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_l_addr, - ccx->ifs_t2_avg_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_l_addr, + ccx->ifs_t2_avg_mask, bb->phy_idx); env->ifs_clm_avg[2] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_h_addr, - ccx->ifs_t3_avg_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_h_addr, + ccx->ifs_t3_avg_mask, bb->phy_idx); env->ifs_clm_avg[3] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_avg_h_addr, - ccx->ifs_t4_avg_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_avg_h_addr, + ccx->ifs_t4_avg_mask, bb->phy_idx); env->ifs_clm_cca[0] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_l_addr, - ccx->ifs_t1_cca_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_l_addr, + ccx->ifs_t1_cca_mask, bb->phy_idx); env->ifs_clm_cca[1] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_l_addr, - ccx->ifs_t2_cca_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_l_addr, + ccx->ifs_t2_cca_mask, bb->phy_idx); env->ifs_clm_cca[2] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_h_addr, - ccx->ifs_t3_cca_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_h_addr, + ccx->ifs_t3_cca_mask, bb->phy_idx); env->ifs_clm_cca[3] = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_cca_h_addr, - ccx->ifs_t4_cca_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_cca_h_addr, + ccx->ifs_t4_cca_mask, bb->phy_idx); env->ifs_clm_total_ifs = - rtw89_phy_read32_mask(rtwdev, ccx->ifs_total_addr, - ccx->ifs_total_mask); + rtw89_phy_read32_idx(rtwdev, ccx->ifs_total_addr, + ccx->ifs_total_mask, bb->phy_idx); rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "IFS-CLM total_ifs = %d\n", env->ifs_clm_total_ifs); @@ -5598,16 +5734,17 @@ static bool rtw89_phy_ifs_clm_get_result(struct rtw89_dev *rtwdev) "T%d:[%d, %d, %d]\n", i + 1, env->ifs_clm_his[i], env->ifs_clm_avg[i], env->ifs_clm_cca[i]); - rtw89_phy_ifs_clm_get_utility(rtwdev); + rtw89_phy_ifs_clm_get_utility(rtwdev, bb); return true; } static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, struct rtw89_ccx_para_info *para) { const struct rtw89_phy_gen_def *phy = rtwdev->chip->phy_def; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_env_monitor_info *env = &bb->env_monitor; const struct rtw89_ccx_regs *ccx = phy->ccx; u32 period = 0; u32 unit_idx = 0; @@ -5618,17 +5755,17 @@ static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev, return -EINVAL; } - if (rtw89_phy_ccx_racing_ctrl(rtwdev, para->rac_lv)) + if (rtw89_phy_ccx_racing_ctrl(rtwdev, bb, para->rac_lv)) return -EINVAL; if (para->mntr_time != env->ifs_clm_mntr_time) { rtw89_phy_ccx_ms_to_period_unit(rtwdev, para->mntr_time, &period, &unit_idx); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, - ccx->ifs_clm_period_mask, period); - rtw89_phy_set_phy_regs(rtwdev, ccx->ifs_cnt_addr, - ccx->ifs_clm_cnt_unit_mask, - unit_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, + ccx->ifs_clm_period_mask, period, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, ccx->ifs_cnt_addr, + ccx->ifs_clm_cnt_unit_mask, + unit_idx, bb->phy_idx); rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "Update IFS-CLM time ((%d)) -> ((%d))\n", @@ -5639,18 +5776,19 @@ static int rtw89_phy_ifs_clm_set(struct rtw89_dev *rtwdev, env->ccx_unit_idx = (u8)unit_idx; } - if (rtw89_phy_ifs_clm_th_update_check(rtwdev, para)) { + if (rtw89_phy_ifs_clm_th_update_check(rtwdev, bb, para)) { env->ifs_clm_app = para->ifs_clm_app; - rtw89_phy_ifs_clm_set_th_reg(rtwdev); + rtw89_phy_ifs_clm_set_th_reg(rtwdev, bb); } return 0; } -void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) +static void __rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; - struct rtw89_ccx_para_info para = {0}; + struct rtw89_env_monitor_info *env = &bb->env_monitor; + struct rtw89_ccx_para_info para = {}; u8 chk_result = RTW89_PHY_ENV_MON_CCX_FAIL; env->ccx_watchdog_result = RTW89_PHY_ENV_MON_CCX_FAIL; @@ -5660,25 +5798,36 @@ void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) return; } + rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, + "BB-%d env_monitor track\n", bb->phy_idx); + /* only ifs_clm for now */ - if (rtw89_phy_ifs_clm_get_result(rtwdev)) + if (rtw89_phy_ifs_clm_get_result(rtwdev, bb)) env->ccx_watchdog_result |= RTW89_PHY_ENV_MON_IFS_CLM; - rtw89_phy_ccx_racing_release(rtwdev); + rtw89_phy_ccx_racing_release(rtwdev, bb); para.mntr_time = 1900; para.rac_lv = RTW89_RAC_LV_1; para.ifs_clm_app = RTW89_IFS_CLM_BACKGROUND; - if (rtw89_phy_ifs_clm_set(rtwdev, ¶) == 0) + if (rtw89_phy_ifs_clm_set(rtwdev, bb, ¶) == 0) chk_result |= RTW89_PHY_ENV_MON_IFS_CLM; if (chk_result) - rtw89_phy_ccx_trigger(rtwdev); + rtw89_phy_ccx_trigger(rtwdev, bb); rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, "get_result=0x%x, chk_result:0x%x\n", env->ccx_watchdog_result, chk_result); } +void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_active_bb(rtwdev, bb) + __rtw89_phy_env_monitor_track(rtwdev, bb); +} + static bool rtw89_physts_ie_page_valid(enum rtw89_phy_status_bitmap *ie_page) { if (*ie_page >= RTW89_PHYSTS_BITMAP_NUM || @@ -5799,11 +5948,12 @@ static void rtw89_physts_parsing_init(struct rtw89_dev *rtwdev) __rtw89_physts_parsing_init(rtwdev, RTW89_PHY_1); } -static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type) +static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, int type) { const struct rtw89_chip_info *chip = rtwdev->chip; - struct rtw89_dig_info *dig = &rtwdev->dig; const struct rtw89_phy_dig_gain_cfg *cfg; + struct rtw89_dig_info *dig = &bb->dig; const char *msg; u8 i; s8 gain_base; @@ -5840,8 +5990,8 @@ static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type) } for (i = 0; i < cfg->size; i++) { - tmp = rtw89_phy_read32_mask(rtwdev, cfg->table[i].addr, - cfg->table[i].mask); + tmp = rtw89_phy_read32_idx(rtwdev, cfg->table[i].addr, + cfg->table[i].mask, bb->phy_idx); tmp >>= DIG_GAIN_SHIFT; gain_arr[i] = sign_extend32(tmp, U4_MAX_BIT) + gain_base; gain_base += DIG_GAIN; @@ -5851,25 +6001,26 @@ static void rtw89_phy_dig_read_gain_table(struct rtw89_dev *rtwdev, int type) } } -static void rtw89_phy_dig_update_gain_para(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_gain_para(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; u32 tmp; u8 i; if (!rtwdev->hal.support_igi) return; - tmp = rtw89_phy_read32_mask(rtwdev, R_PATH0_IB_PKPW, - B_PATH0_IB_PKPW_MSK); + tmp = rtw89_phy_read32_idx(rtwdev, R_PATH0_IB_PKPW, + B_PATH0_IB_PKPW_MSK, bb->phy_idx); dig->ib_pkpwr = sign_extend32(tmp >> DIG_GAIN_SHIFT, U8_MAX_BIT); - dig->ib_pbk = rtw89_phy_read32_mask(rtwdev, R_PATH0_IB_PBK, - B_PATH0_IB_PBK_MSK); + dig->ib_pbk = rtw89_phy_read32_idx(rtwdev, R_PATH0_IB_PBK, + B_PATH0_IB_PBK_MSK, bb->phy_idx); rtw89_debug(rtwdev, RTW89_DBG_DIG, "ib_pkpwr=%d, ib_pbk=%d\n", dig->ib_pkpwr, dig->ib_pbk); for (i = RTW89_DIG_GAIN_LNA_G; i < RTW89_DIG_GAIN_MAX; i++) - rtw89_phy_dig_read_gain_table(rtwdev, i); + rtw89_phy_dig_read_gain_table(rtwdev, bb, i); } static const u8 rssi_nolink = 22; @@ -5878,10 +6029,11 @@ static const u16 fa_th_2g[FA_TH_NUM] = {22, 44, 66, 88}; static const u16 fa_th_5g[FA_TH_NUM] = {4, 8, 12, 16}; static const u16 fa_th_nolink[FA_TH_NUM] = {196, 352, 440, 528}; -static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info; - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_phy_ch_info *ch_info = &bb->ch_info; + struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; if (is_linked) { @@ -5892,10 +6044,11 @@ static void rtw89_phy_dig_update_rssi_info(struct rtw89_dev *rtwdev) } } -static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_update_para(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; - const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); + struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; const u16 *fa_th_src = NULL; @@ -5924,9 +6077,10 @@ static const u8 pd_low_th_offset = 16, dynamic_igi_min = 0x20; static const u8 igi_max_performance_mode = 0x5a; static const u8 dynamic_pd_threshold_max; -static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; dig->cur_gaincode.lna_idx = LNA_IDX_MAX; dig->cur_gaincode.tia_idx = TIA_IDX_MAX; @@ -5942,15 +6096,27 @@ static void rtw89_phy_dig_para_reset(struct rtw89_dev *rtwdev) dig->is_linked_pre = false; } +static void __rtw89_phy_dig_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + rtw89_debug(rtwdev, RTW89_DBG_DIG, "BB-%d dig_init\n", bb->phy_idx); + + rtw89_phy_dig_update_gain_para(rtwdev, bb); + rtw89_phy_dig_reset(rtwdev, bb); +} + static void rtw89_phy_dig_init(struct rtw89_dev *rtwdev) { - rtw89_phy_dig_update_gain_para(rtwdev); - rtw89_phy_dig_reset(rtwdev); + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + __rtw89_phy_dig_init(rtwdev, bb); } -static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) +static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rssi) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; u8 lna_idx; if (rssi < dig->igi_rssi_th[0]) @@ -5969,9 +6135,10 @@ static u8 rtw89_phy_dig_lna_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) return lna_idx; } -static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) +static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rssi) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; u8 tia_idx; if (rssi < dig->igi_rssi_th[0]) @@ -5984,10 +6151,11 @@ static u8 rtw89_phy_dig_tia_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi) #define IB_PBK_BASE 110 #define WB_RSSI_BASE 10 -static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, +static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rssi, struct rtw89_agc_gaincode_set *set) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; s8 lna_gain = dig->lna_gain[set->lna_idx]; s8 tia_gain = dig->tia_gain[set->tia_idx]; s32 wb_rssi = rssi + lna_gain + tia_gain; @@ -6003,12 +6171,13 @@ static u8 rtw89_phy_dig_rxb_idx_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, return rxb_idx; } -static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, +static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rssi, struct rtw89_agc_gaincode_set *set) { - set->lna_idx = rtw89_phy_dig_lna_idx_by_rssi(rtwdev, rssi); - set->tia_idx = rtw89_phy_dig_tia_idx_by_rssi(rtwdev, rssi); - set->rxb_idx = rtw89_phy_dig_rxb_idx_by_rssi(rtwdev, rssi, set); + set->lna_idx = rtw89_phy_dig_lna_idx_by_rssi(rtwdev, bb, rssi); + set->tia_idx = rtw89_phy_dig_tia_idx_by_rssi(rtwdev, bb, rssi); + set->rxb_idx = rtw89_phy_dig_rxb_idx_by_rssi(rtwdev, bb, rssi, set); rtw89_debug(rtwdev, RTW89_DBG_DIG, "final_rssi=%03d, (lna,tia,rab)=(%d,%d,%02d)\n", @@ -6017,10 +6186,11 @@ static void rtw89_phy_dig_gaincode_by_rssi(struct rtw89_dev *rtwdev, u8 rssi, #define IGI_OFFSET_MAX 25 #define IGI_OFFSET_MUL 2 -static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; + struct rtw89_dig_info *dig = &bb->dig; + struct rtw89_env_monitor_info *env = &bb->env_monitor; enum rtw89_dig_noisy_level noisy_lv; u8 igi_offset = dig->fa_rssi_ofst; u16 fa_ratio = 0; @@ -6057,92 +6227,99 @@ static void rtw89_phy_dig_igi_offset_by_env(struct rtw89_dev *rtwdev) noisy_lv, igi_offset); } -static void rtw89_phy_dig_set_lna_idx(struct rtw89_dev *rtwdev, u8 lna_idx) +static void rtw89_phy_dig_set_lna_idx(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 lna_idx) { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_lna_init.addr, - dig_regs->p0_lna_init.mask, lna_idx); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_lna_init.addr, - dig_regs->p1_lna_init.mask, lna_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_lna_init.addr, + dig_regs->p0_lna_init.mask, lna_idx, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_lna_init.addr, + dig_regs->p1_lna_init.mask, lna_idx, bb->phy_idx); } -static void rtw89_phy_dig_set_tia_idx(struct rtw89_dev *rtwdev, u8 tia_idx) +static void rtw89_phy_dig_set_tia_idx(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 tia_idx) { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_tia_init.addr, - dig_regs->p0_tia_init.mask, tia_idx); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_tia_init.addr, - dig_regs->p1_tia_init.mask, tia_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_tia_init.addr, + dig_regs->p0_tia_init.mask, tia_idx, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_tia_init.addr, + dig_regs->p1_tia_init.mask, tia_idx, bb->phy_idx); } -static void rtw89_phy_dig_set_rxb_idx(struct rtw89_dev *rtwdev, u8 rxb_idx) +static void rtw89_phy_dig_set_rxb_idx(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, u8 rxb_idx) { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_rxb_init.addr, - dig_regs->p0_rxb_init.mask, rxb_idx); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_rxb_init.addr, - dig_regs->p1_rxb_init.mask, rxb_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_rxb_init.addr, + dig_regs->p0_rxb_init.mask, rxb_idx, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_rxb_init.addr, + dig_regs->p1_rxb_init.mask, rxb_idx, bb->phy_idx); } static void rtw89_phy_dig_set_igi_cr(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, const struct rtw89_agc_gaincode_set set) { if (!rtwdev->hal.support_igi) return; - rtw89_phy_dig_set_lna_idx(rtwdev, set.lna_idx); - rtw89_phy_dig_set_tia_idx(rtwdev, set.tia_idx); - rtw89_phy_dig_set_rxb_idx(rtwdev, set.rxb_idx); + rtw89_phy_dig_set_lna_idx(rtwdev, bb, set.lna_idx); + rtw89_phy_dig_set_tia_idx(rtwdev, bb, set.tia_idx); + rtw89_phy_dig_set_rxb_idx(rtwdev, bb, set.rxb_idx); rtw89_debug(rtwdev, RTW89_DBG_DIG, "Set (lna,tia,rxb)=((%d,%d,%02d))\n", set.lna_idx, set.tia_idx, set.rxb_idx); } static void rtw89_phy_dig_sdagc_follow_pagc_config(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool enable) { const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_p20_pagcugc_en.addr, - dig_regs->p0_p20_pagcugc_en.mask, enable); - rtw89_phy_write32_mask(rtwdev, dig_regs->p0_s20_pagcugc_en.addr, - dig_regs->p0_s20_pagcugc_en.mask, enable); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_p20_pagcugc_en.addr, - dig_regs->p1_p20_pagcugc_en.mask, enable); - rtw89_phy_write32_mask(rtwdev, dig_regs->p1_s20_pagcugc_en.addr, - dig_regs->p1_s20_pagcugc_en.mask, enable); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_p20_pagcugc_en.addr, + dig_regs->p0_p20_pagcugc_en.mask, enable, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p0_s20_pagcugc_en.addr, + dig_regs->p0_s20_pagcugc_en.mask, enable, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_p20_pagcugc_en.addr, + dig_regs->p1_p20_pagcugc_en.mask, enable, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->p1_s20_pagcugc_en.addr, + dig_regs->p1_s20_pagcugc_en.mask, enable, bb->phy_idx); rtw89_debug(rtwdev, RTW89_DBG_DIG, "sdagc_follow_pagc=%d\n", enable); } -static void rtw89_phy_dig_config_igi(struct rtw89_dev *rtwdev) +static void rtw89_phy_dig_config_igi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; if (!rtwdev->hal.support_igi) return; if (dig->force_gaincode_idx_en) { - rtw89_phy_dig_set_igi_cr(rtwdev, dig->force_gaincode); + rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->force_gaincode); rtw89_debug(rtwdev, RTW89_DBG_DIG, "Force gaincode index enabled.\n"); } else { - rtw89_phy_dig_gaincode_by_rssi(rtwdev, dig->igi_fa_rssi, + rtw89_phy_dig_gaincode_by_rssi(rtwdev, bb, dig->igi_fa_rssi, &dig->cur_gaincode); - rtw89_phy_dig_set_igi_cr(rtwdev, dig->cur_gaincode); + rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->cur_gaincode); } } -static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, - bool enable) +static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, + u8 rssi, bool enable) { - const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + const struct rtw89_chan *chan = rtw89_mgnt_chan_get(rtwdev, bb->phy_idx); const struct rtw89_dig_regs *dig_regs = rtwdev->chip->dig_regs; enum rtw89_bandwidth cbw = chan->band_width; - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; u8 final_rssi = 0, under_region = dig->pd_low_th_ofst; u8 ofdm_cca_th; s8 cck_cca_th; @@ -6184,10 +6361,10 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, "Dynamic PD th disabled, Set PD_low_bd=0\n"); } - rtw89_phy_write32_mask(rtwdev, dig_regs->seg0_pd_reg, - dig_regs->pd_lower_bound_mask, pd_val); - rtw89_phy_write32_mask(rtwdev, dig_regs->seg0_pd_reg, - dig_regs->pd_spatial_reuse_en, enable); + rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, + dig_regs->pd_lower_bound_mask, pd_val, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->seg0_pd_reg, + dig_regs->pd_spatial_reuse_en, enable, bb->phy_idx); if (!rtwdev->hal.support_cckpd) return; @@ -6199,29 +6376,29 @@ static void rtw89_phy_dig_dyn_pd_th(struct rtw89_dev *rtwdev, u8 rssi, "igi=%d, cck_ccaTH=%d, backoff=%d, cck_PD_low=((%d))dB\n", final_rssi, cck_cca_th, under_region, pd_val); - rtw89_phy_write32_mask(rtwdev, dig_regs->bmode_pd_reg, - dig_regs->bmode_cca_rssi_limit_en, enable); - rtw89_phy_write32_mask(rtwdev, dig_regs->bmode_pd_lower_bound_reg, - dig_regs->bmode_rssi_nocca_low_th_mask, pd_val); + rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_reg, + dig_regs->bmode_cca_rssi_limit_en, enable, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, dig_regs->bmode_pd_lower_bound_reg, + dig_regs->bmode_rssi_nocca_low_th_mask, pd_val, bb->phy_idx); } -void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev) +void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; dig->bypass_dig = false; - rtw89_phy_dig_para_reset(rtwdev); - rtw89_phy_dig_set_igi_cr(rtwdev, dig->force_gaincode); - rtw89_phy_dig_dyn_pd_th(rtwdev, rssi_nolink, false); - rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, false); - rtw89_phy_dig_update_para(rtwdev); + rtw89_phy_dig_para_reset(rtwdev, bb); + rtw89_phy_dig_set_igi_cr(rtwdev, bb, dig->force_gaincode); + rtw89_phy_dig_dyn_pd_th(rtwdev, bb, rssi_nolink, false); + rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, false); + rtw89_phy_dig_update_para(rtwdev, bb); } #define IGI_RSSI_MIN 10 #define ABS_IGI_MIN 0xc -void rtw89_phy_dig(struct rtw89_dev *rtwdev) +static void __rtw89_phy_dig(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { - struct rtw89_dig_info *dig = &rtwdev->dig; + struct rtw89_dig_info *dig = &bb->dig; bool is_linked = rtwdev->total_sta_assoc > 0; u8 igi_min; @@ -6230,20 +6407,22 @@ void rtw89_phy_dig(struct rtw89_dev *rtwdev) return; } - rtw89_phy_dig_update_rssi_info(rtwdev); + rtw89_debug(rtwdev, RTW89_DBG_DIG, "BB-%d dig track\n", bb->phy_idx); + + rtw89_phy_dig_update_rssi_info(rtwdev, bb); if (!dig->is_linked_pre && is_linked) { rtw89_debug(rtwdev, RTW89_DBG_DIG, "First connected\n"); - rtw89_phy_dig_update_para(rtwdev); + rtw89_phy_dig_update_para(rtwdev, bb); dig->igi_fa_rssi = dig->igi_rssi; } else if (dig->is_linked_pre && !is_linked) { rtw89_debug(rtwdev, RTW89_DBG_DIG, "First disconnected\n"); - rtw89_phy_dig_update_para(rtwdev); + rtw89_phy_dig_update_para(rtwdev, bb); dig->igi_fa_rssi = dig->igi_rssi; } dig->is_linked_pre = is_linked; - rtw89_phy_dig_igi_offset_by_env(rtwdev); + rtw89_phy_dig_igi_offset_by_env(rtwdev, bb); igi_min = max_t(int, dig->igi_rssi - IGI_RSSI_MIN, 0); dig->dyn_igi_max = min(igi_min + IGI_OFFSET_MAX, igi_max_performance_mode); @@ -6262,14 +6441,22 @@ void rtw89_phy_dig(struct rtw89_dev *rtwdev) dig->igi_rssi, dig->dyn_igi_max, dig->dyn_igi_min, dig->igi_fa_rssi); - rtw89_phy_dig_config_igi(rtwdev); + rtw89_phy_dig_config_igi(rtwdev, bb); - rtw89_phy_dig_dyn_pd_th(rtwdev, dig->igi_fa_rssi, dig->dyn_pd_th_en); + rtw89_phy_dig_dyn_pd_th(rtwdev, bb, dig->igi_fa_rssi, dig->dyn_pd_th_en); if (dig->dyn_pd_th_en && dig->igi_fa_rssi > dig->dyn_pd_th_max) - rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, true); + rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, true); else - rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, false); + rtw89_phy_dig_sdagc_follow_pagc_config(rtwdev, bb, false); +} + +void rtw89_phy_dig(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_active_bb(rtwdev, bb) + __rtw89_phy_dig(rtwdev, bb); } static void __rtw89_phy_tx_path_div_sta_iter(struct rtw89_dev *rtwdev, @@ -6443,17 +6630,17 @@ static void rtw89_phy_antdiv_training_state(struct rtw89_dev *rtwdev) } antdiv->training_count++; - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, - state_period); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->antdiv_work, + state_period); } -void rtw89_phy_antdiv_work(struct work_struct *work) +void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work) { struct rtw89_dev *rtwdev = container_of(work, struct rtw89_dev, antdiv_work.work); struct rtw89_antdiv_info *antdiv = &rtwdev->antdiv; - mutex_lock(&rtwdev->mutex); + lockdep_assert_wiphy(wiphy); if (antdiv->training_count <= ANTDIV_TRAINNING_CNT) { rtw89_phy_antdiv_training_state(rtwdev); @@ -6461,8 +6648,6 @@ void rtw89_phy_antdiv_work(struct work_struct *work) rtw89_phy_antdiv_decision_state(rtwdev); rtw89_phy_antdiv_set_ant(rtwdev); } - - mutex_unlock(&rtwdev->mutex); } void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev) @@ -6483,19 +6668,34 @@ void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev) return; antdiv->training_count = 0; - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->antdiv_work, 0); + wiphy_delayed_work_queue(rtwdev->hw->wiphy, &rtwdev->antdiv_work, 0); +} + +static void __rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) +{ + rtw89_debug(rtwdev, RTW89_DBG_PHY_TRACK, + "BB-%d env_monitor init\n", bb->phy_idx); + + rtw89_phy_ccx_top_setting_init(rtwdev, bb); + rtw89_phy_ifs_clm_setting_init(rtwdev, bb); } static void rtw89_phy_env_monitor_init(struct rtw89_dev *rtwdev) { - rtw89_phy_ccx_top_setting_init(rtwdev); - rtw89_phy_ifs_clm_setting_init(rtwdev); + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + __rtw89_phy_env_monitor_init(rtwdev, bb); } -static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) +static void __rtw89_phy_edcca_init(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; - struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; + struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak; + + rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "BB-%d edcca init\n", bb->phy_idx); memset(edcca_bak, 0, sizeof(*edcca_bak)); @@ -6511,8 +6711,16 @@ static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) rtw89_phy_set_phy_regs(rtwdev, R_DFS_FFT_CG, B_DFS_FFT_EN, 1); } - rtw89_phy_write32_mask(rtwdev, edcca_regs->tx_collision_t2r_st, - edcca_regs->tx_collision_t2r_st_mask, 0x29); + rtw89_phy_write32_idx(rtwdev, edcca_regs->tx_collision_t2r_st, + edcca_regs->tx_collision_t2r_st_mask, 0x29, bb->phy_idx); +} + +static void rtw89_phy_edcca_init(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb; + + rtw89_for_each_capab_bb(rtwdev, bb) + __rtw89_phy_edcca_init(rtwdev, bb); } void rtw89_phy_dm_init(struct rtw89_dev *rtwdev) @@ -6875,44 +7083,46 @@ void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx, } EXPORT_SYMBOL(rtw89_decode_chan_idx); -void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan) +void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool scan) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; - struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; + struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak; if (scan) { edcca_bak->a = - rtw89_phy_read32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_mask); + rtw89_phy_read32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_mask, bb->phy_idx); edcca_bak->p = - rtw89_phy_read32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_p_mask); + rtw89_phy_read32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_p_mask, bb->phy_idx); edcca_bak->ppdu = - rtw89_phy_read32_mask(rtwdev, edcca_regs->ppdu_level, - edcca_regs->ppdu_mask); - - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_mask, EDCCA_MAX); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_p_mask, EDCCA_MAX); - rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, - edcca_regs->ppdu_mask, EDCCA_MAX); + rtw89_phy_read32_idx(rtwdev, edcca_regs->ppdu_level, + edcca_regs->ppdu_mask, bb->phy_idx); + + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_mask, EDCCA_MAX, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_p_mask, EDCCA_MAX, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, + edcca_regs->ppdu_mask, EDCCA_MAX, bb->phy_idx); } else { - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_mask, - edcca_bak->a); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_p_mask, - edcca_bak->p); - rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, - edcca_regs->ppdu_mask, - edcca_bak->ppdu); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_mask, + edcca_bak->a, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_p_mask, + edcca_bak->p, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, + edcca_regs->ppdu_mask, + edcca_bak->ppdu, bb->phy_idx); } } -static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev) +static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; + const struct rtw89_edcca_p_regs *edcca_p_regs; bool flag_fb, flag_p20, flag_s20, flag_s40, flag_s80; s8 pwdb_fb, pwdb_p20, pwdb_s20, pwdb_s40, pwdb_s80; u8 path, per20_bitmap; @@ -6922,13 +7132,18 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev) if (!rtw89_debug_is_enabled(rtwdev, RTW89_DBG_EDCCA)) return; + if (bb->phy_idx == RTW89_PHY_1) + edcca_p_regs = &edcca_regs->p[RTW89_PHY_1]; + else + edcca_p_regs = &edcca_regs->p[RTW89_PHY_0]; + if (rtwdev->chip->chip_id == RTL8922A) rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be, edcca_regs->rpt_sel_be_mask, 0); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 0); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 0); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b); path = u32_get_bits(tmp, B_EDCCA_RPT_B_PATH_MASK); flag_s80 = u32_get_bits(tmp, B_EDCCA_RPT_B_S80); flag_s40 = u32_get_bits(tmp, B_EDCCA_RPT_B_S40); @@ -6939,19 +7154,19 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev) pwdb_p20 = u32_get_bits(tmp, MASKBYTE2); pwdb_fb = u32_get_bits(tmp, MASKBYTE3); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 4); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 4); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b); pwdb_s80 = u32_get_bits(tmp, MASKBYTE1); pwdb_s40 = u32_get_bits(tmp, MASKBYTE2); - per20_bitmap = rtw89_phy_read32_mask(rtwdev, edcca_regs->rpt_a, + per20_bitmap = rtw89_phy_read32_mask(rtwdev, edcca_p_regs->rpt_a, MASKBYTE0); if (rtwdev->chip->chip_id == RTL8922A) { rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be, edcca_regs->rpt_sel_be_mask, 4); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b); pwdb[0] = u32_get_bits(tmp, MASKBYTE3); pwdb[1] = u32_get_bits(tmp, MASKBYTE2); pwdb[2] = u32_get_bits(tmp, MASKBYTE1); @@ -6959,33 +7174,33 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev) rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel_be, edcca_regs->rpt_sel_be_mask, 5); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_b); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_b); pwdb[4] = u32_get_bits(tmp, MASKBYTE3); pwdb[5] = u32_get_bits(tmp, MASKBYTE2); pwdb[6] = u32_get_bits(tmp, MASKBYTE1); pwdb[7] = u32_get_bits(tmp, MASKBYTE0); } else { - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 0); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 0); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a); pwdb[0] = u32_get_bits(tmp, MASKBYTE3); pwdb[1] = u32_get_bits(tmp, MASKBYTE2); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 1); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 1); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a); pwdb[2] = u32_get_bits(tmp, MASKBYTE3); pwdb[3] = u32_get_bits(tmp, MASKBYTE2); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 2); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 2); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a); pwdb[4] = u32_get_bits(tmp, MASKBYTE3); pwdb[5] = u32_get_bits(tmp, MASKBYTE2); - rtw89_phy_write32_mask(rtwdev, edcca_regs->rpt_sel, - edcca_regs->rpt_sel_mask, 3); - tmp = rtw89_phy_read32(rtwdev, edcca_regs->rpt_a); + rtw89_phy_write32_mask(rtwdev, edcca_p_regs->rpt_sel, + edcca_p_regs->rpt_sel_mask, 3); + tmp = rtw89_phy_read32(rtwdev, edcca_p_regs->rpt_a); pwdb[6] = u32_get_bits(tmp, MASKBYTE3); pwdb[7] = u32_get_bits(tmp, MASKBYTE2); } @@ -7007,9 +7222,10 @@ static void rtw89_phy_edcca_log(struct rtw89_dev *rtwdev) pwdb_fb, pwdb_p20, pwdb_s20, pwdb_s40, pwdb_s80); } -static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev) +static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb) { - struct rtw89_phy_ch_info *ch_info = &rtwdev->ch_info; + struct rtw89_phy_ch_info *ch_info = &bb->ch_info; bool is_linked = rtwdev->total_sta_assoc > 0; u8 rssi_min = ch_info->rssi_min >> 1; u8 edcca_thre; @@ -7025,13 +7241,13 @@ static u8 rtw89_phy_edcca_get_thre_by_rssi(struct rtw89_dev *rtwdev) return edcca_thre; } -void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev) +void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) { const struct rtw89_edcca_regs *edcca_regs = rtwdev->chip->edcca_regs; - struct rtw89_edcca_bak *edcca_bak = &rtwdev->hal.edcca_bak; + struct rtw89_edcca_bak *edcca_bak = &bb->edcca_bak; u8 th; - th = rtw89_phy_edcca_get_thre_by_rssi(rtwdev); + th = rtw89_phy_edcca_get_thre_by_rssi(rtwdev, bb); if (th == edcca_bak->th_old) return; @@ -7040,23 +7256,33 @@ void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev) rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "[EDCCA]: Normal Mode, EDCCA_th = %d\n", th); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_mask, th); - rtw89_phy_write32_mask(rtwdev, edcca_regs->edcca_level, - edcca_regs->edcca_p_mask, th); - rtw89_phy_write32_mask(rtwdev, edcca_regs->ppdu_level, - edcca_regs->ppdu_mask, th); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_mask, th, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->edcca_level, + edcca_regs->edcca_p_mask, th, bb->phy_idx); + rtw89_phy_write32_idx(rtwdev, edcca_regs->ppdu_level, + edcca_regs->ppdu_mask, th, bb->phy_idx); +} + +static +void __rtw89_phy_edcca_track(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb) +{ + rtw89_debug(rtwdev, RTW89_DBG_EDCCA, "BB-%d edcca track\n", bb->phy_idx); + + rtw89_phy_edcca_thre_calc(rtwdev, bb); + rtw89_phy_edcca_log(rtwdev, bb); } void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev) { struct rtw89_hal *hal = &rtwdev->hal; + struct rtw89_bb_ctx *bb; if (hal->disabled_dm_bitmap & BIT(RTW89_DM_DYNAMIC_EDCCA)) return; - rtw89_phy_edcca_thre_calc(rtwdev); - rtw89_phy_edcca_log(rtwdev); + rtw89_for_each_active_bb(rtwdev, bb) + __rtw89_phy_edcca_track(rtwdev, bb); } enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/phy.h b/drivers/net/wireless/realtek/rtw89/phy.h index 08b635c93ac3..5b451f1cfaac 100644 --- a/drivers/net/wireless/realtek/rtw89/phy.h +++ b/drivers/net/wireless/realtek/rtw89/phy.h @@ -164,6 +164,7 @@ enum rtw89_phy_c2h_dm_func { RTW89_PHY_C2H_DM_FUNC_SIGB, RTW89_PHY_C2H_DM_FUNC_LOWRT_RTY, RTW89_PHY_C2H_DM_FUNC_MCC_DIG, + RTW89_PHY_C2H_DM_FUNC_FW_SCAN = 0xc, RTW89_PHY_C2H_DM_FUNC_NUM, }; @@ -835,8 +836,8 @@ s8 rtw89_phy_read_txpwr_byrate(struct rtw89_dev *rtwdev, u8 band, u8 bw, void rtw89_phy_ant_gain_init(struct rtw89_dev *rtwdev); s16 rtw89_phy_ant_gain_pwr_offset(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan); -void rtw89_print_ant_gain(struct seq_file *m, struct rtw89_dev *rtwdev, - const struct rtw89_chan *chan); +int rtw89_print_ant_gain(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct rtw89_chan *chan); void rtw89_phy_load_txpwr_byrate(struct rtw89_dev *rtwdev, const struct rtw89_txpwr_table *tbl); s8 rtw89_phy_read_txpwr_limit(struct rtw89_dev *rtwdev, u8 band, @@ -914,6 +915,13 @@ static inline s8 rtw89_phy_txpwr_rf_to_bb(struct rtw89_dev *rtwdev, s8 txpwr_rf) return txpwr_rf << (chip->txpwr_factor_bb - chip->txpwr_factor_rf); } +static inline s8 rtw89_phy_txpwr_bb_to_rf(struct rtw89_dev *rtwdev, s8 txpwr_bb) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return txpwr_bb >> (chip->txpwr_factor_bb - chip->txpwr_factor_rf); +} + static inline s8 rtw89_phy_txpwr_rf_to_mac(struct rtw89_dev *rtwdev, s8 txpwr_rf) { const struct rtw89_chip_info *chip = rtwdev->chip; @@ -928,6 +936,20 @@ static inline s8 rtw89_phy_txpwr_dbm_to_mac(struct rtw89_dev *rtwdev, s8 dbm) return clamp_t(s16, dbm << chip->txpwr_factor_mac, -64, 63); } +static inline s16 rtw89_phy_txpwr_mac_to_rf(struct rtw89_dev *rtwdev, s8 txpwr_mac) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return txpwr_mac << (chip->txpwr_factor_rf - chip->txpwr_factor_mac); +} + +static inline s16 rtw89_phy_txpwr_mac_to_bb(struct rtw89_dev *rtwdev, s8 txpwr_mac) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + + return txpwr_mac << (chip->txpwr_factor_bb - chip->txpwr_factor_mac); +} + void rtw89_phy_ra_assoc(struct rtw89_dev *rtwdev, struct rtw89_sta_link *rtwsta_link); void rtw89_phy_ra_update(struct rtw89_dev *rtwdev); void rtw89_phy_ra_update_sta(struct rtw89_dev *rtwdev, struct ieee80211_sta *sta, @@ -978,20 +1000,20 @@ void rtw89_phy_rfk_tssi_fill_fwcmd_tmeter_tbl(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, struct rtw89_h2c_rf_tssi *h2c); void rtw89_phy_cfo_track(struct rtw89_dev *rtwdev); -void rtw89_phy_cfo_track_work(struct work_struct *work); +void rtw89_phy_cfo_track_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_phy_cfo_parse(struct rtw89_dev *rtwdev, s16 cfo_val, struct rtw89_rx_phy_ppdu *phy_ppdu); void rtw89_phy_stat_track(struct rtw89_dev *rtwdev); void rtw89_phy_env_monitor_track(struct rtw89_dev *rtwdev); void rtw89_phy_set_phy_regs(struct rtw89_dev *rtwdev, u32 addr, u32 mask, u32 val); -void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev); +void rtw89_phy_dig_reset(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb); void rtw89_phy_dig(struct rtw89_dev *rtwdev); void rtw89_phy_tx_path_div_track(struct rtw89_dev *rtwdev); void rtw89_phy_antdiv_parse(struct rtw89_dev *rtwdev, struct rtw89_rx_phy_ppdu *phy_ppdu); void rtw89_phy_antdiv_track(struct rtw89_dev *rtwdev); -void rtw89_phy_antdiv_work(struct work_struct *work); +void rtw89_phy_antdiv_work(struct wiphy *wiphy, struct wiphy_work *work); void rtw89_phy_set_bss_color(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvif_link); void rtw89_phy_tssi_ctrl_set_bandedge_cfg(struct rtw89_dev *rtwdev, @@ -1002,9 +1024,10 @@ void rtw89_phy_ul_tb_ctrl_track(struct rtw89_dev *rtwdev); u8 rtw89_encode_chan_idx(struct rtw89_dev *rtwdev, u8 central_ch, u8 band); void rtw89_decode_chan_idx(struct rtw89_dev *rtwdev, u8 chan_idx, u8 *ch, enum nl80211_band *band); -void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, bool scan); +void rtw89_phy_config_edcca(struct rtw89_dev *rtwdev, + struct rtw89_bb_ctx *bb, bool scan); void rtw89_phy_edcca_track(struct rtw89_dev *rtwdev); -void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev); +void rtw89_phy_edcca_thre_calc(struct rtw89_dev *rtwdev, struct rtw89_bb_ctx *bb); enum rtw89_rf_path_bit rtw89_phy_get_kpath(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx); enum rtw89_rf_path rtw89_phy_get_syn_sel(struct rtw89_dev *rtwdev, diff --git a/drivers/net/wireless/realtek/rtw89/phy_be.c b/drivers/net/wireless/realtek/rtw89/phy_be.c index 37d8f254ae32..d321cf1fc485 100644 --- a/drivers/net/wireless/realtek/rtw89/phy_be.c +++ b/drivers/net/wireless/realtek/rtw89/phy_be.c @@ -362,7 +362,7 @@ static void rtw89_phy_bb_wrap_force_cr_init(struct rtw89_dev *rtwdev, rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ENON, 0); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_RU_ON, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_FORCE_MACID, mac_idx); - rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ON, 0); + rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_MACID_ALL, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_COEX_CTRL, mac_idx); rtw89_write32_mask(rtwdev, addr, B_BE_PWR_FORCE_COEX_ON, 0); addr = rtw89_mac_reg_by_idx(rtwdev, R_BE_PWR_RATE_CTRL, mac_idx); diff --git a/drivers/net/wireless/realtek/rtw89/ps.c b/drivers/net/wireless/realtek/rtw89/ps.c index 96ea04d90cd3..8e4fe73e7d77 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.c +++ b/drivers/net/wireless/realtek/rtw89/ps.c @@ -113,7 +113,7 @@ static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev) { - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); __rtw89_leave_ps_mode(rtwdev); } @@ -125,7 +125,7 @@ void rtw89_enter_lps(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif, bool can_ps_mode = true; unsigned int link_id; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags)) return; @@ -162,7 +162,7 @@ void rtw89_leave_lps(struct rtw89_dev *rtwdev) struct rtw89_vif *rtwvif; unsigned int link_id; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags)) return; @@ -382,3 +382,150 @@ u8 rtw89_p2p_noa_fetch(struct rtw89_vif_link *rtwvif_link, void **data) tail = ie->noa_desc + setter->noa_count; return tail - *data; } + +static void rtw89_ps_noa_once_set_work(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct rtw89_ps_noa_once_handler *noa_once = + container_of(work, struct rtw89_ps_noa_once_handler, set_work.work); + + lockdep_assert_wiphy(wiphy); + + noa_once->in_duration = true; +} + +static void rtw89_ps_noa_once_clr_work(struct wiphy *wiphy, struct wiphy_work *work) +{ + struct rtw89_ps_noa_once_handler *noa_once = + container_of(work, struct rtw89_ps_noa_once_handler, clr_work.work); + struct rtw89_vif_link *rtwvif_link = + container_of(noa_once, struct rtw89_vif_link, noa_once); + struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; + + lockdep_assert_wiphy(wiphy); + + rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); + noa_once->in_duration = false; +} + +void rtw89_p2p_noa_once_init(struct rtw89_vif_link *rtwvif_link) +{ + struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; + + noa_once->in_duration = false; + noa_once->tsf_begin = 0; + noa_once->tsf_end = 0; + + wiphy_delayed_work_init(&noa_once->set_work, rtw89_ps_noa_once_set_work); + wiphy_delayed_work_init(&noa_once->clr_work, rtw89_ps_noa_once_clr_work); +} + +static void rtw89_p2p_noa_once_cancel(struct rtw89_vif_link *rtwvif_link) +{ + struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; + struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; + struct wiphy *wiphy = rtwdev->hw->wiphy; + + wiphy_delayed_work_cancel(wiphy, &noa_once->set_work); + wiphy_delayed_work_cancel(wiphy, &noa_once->clr_work); +} + +void rtw89_p2p_noa_once_deinit(struct rtw89_vif_link *rtwvif_link) +{ + rtw89_p2p_noa_once_cancel(rtwvif_link); + rtw89_p2p_noa_once_init(rtwvif_link); +} + +void rtw89_p2p_noa_once_recalc(struct rtw89_vif_link *rtwvif_link) +{ + struct rtw89_ps_noa_once_handler *noa_once = &rtwvif_link->noa_once; + struct rtw89_dev *rtwdev = rtwvif_link->rtwvif->rtwdev; + const struct ieee80211_p2p_noa_desc *noa_desc; + struct wiphy *wiphy = rtwdev->hw->wiphy; + struct ieee80211_bss_conf *bss_conf; + u64 tsf_begin = U64_MAX, tsf_end; + u64 set_delay_us = 0; + u64 clr_delay_us = 0; + u32 start_time; + u32 interval; + u32 duration; + u64 tsf; + int ret; + int i; + + lockdep_assert_wiphy(wiphy); + + ret = rtw89_mac_port_get_tsf(rtwdev, rtwvif_link, &tsf); + if (ret) { + rtw89_warn(rtwdev, "%s: failed to get tsf\n", __func__); + return; + } + + rcu_read_lock(); + + bss_conf = rtw89_vif_rcu_dereference_link(rtwvif_link, true); + + for (i = 0; i < ARRAY_SIZE(bss_conf->p2p_noa_attr.desc); i++) { + bool first = tsf_begin == U64_MAX; + u64 tmp; + + noa_desc = &bss_conf->p2p_noa_attr.desc[i]; + if (noa_desc->count == 0 || noa_desc->count == 255) + continue; + + start_time = le32_to_cpu(noa_desc->start_time); + interval = le32_to_cpu(noa_desc->interval); + duration = le32_to_cpu(noa_desc->duration); + + if (unlikely(duration == 0 || + (noa_desc->count > 1 && interval == 0))) + continue; + + tmp = start_time + interval * (noa_desc->count - 1) + duration; + tmp = (tsf & GENMASK_ULL(63, 32)) + tmp; + if (unlikely(tmp <= tsf)) + continue; + tsf_end = first ? tmp : max(tsf_end, tmp); + + tmp = (tsf & GENMASK_ULL(63, 32)) | start_time; + tsf_begin = first ? tmp : min(tsf_begin, tmp); + } + + rcu_read_unlock(); + + if (tsf_begin == U64_MAX) + return; + + rtw89_p2p_noa_once_cancel(rtwvif_link); + + if (noa_once->tsf_end > tsf) { + tsf_begin = min(tsf_begin, noa_once->tsf_begin); + tsf_end = max(tsf_end, noa_once->tsf_end); + } + + clr_delay_us = min_t(u64, tsf_end - tsf, UINT_MAX); + + if (tsf_begin <= tsf) { + noa_once->in_duration = true; + goto out; + } + + set_delay_us = tsf_begin - tsf; + if (unlikely(set_delay_us > UINT_MAX)) { + rtw89_warn(rtwdev, "%s: unhandled begin\n", __func__); + set_delay_us = 0; + clr_delay_us = 0; + rtw89_fw_h2c_set_bcn_fltr_cfg(rtwdev, rtwvif_link, true); + noa_once->in_duration = false; + } + +out: + if (set_delay_us) + wiphy_delayed_work_queue(wiphy, &noa_once->set_work, + usecs_to_jiffies(set_delay_us)); + if (clr_delay_us) + wiphy_delayed_work_queue(wiphy, &noa_once->clr_work, + usecs_to_jiffies(clr_delay_us)); + + noa_once->tsf_begin = tsf_begin; + noa_once->tsf_end = tsf_end; +} diff --git a/drivers/net/wireless/realtek/rtw89/ps.h b/drivers/net/wireless/realtek/rtw89/ps.h index 2b88f254a32d..b2c43d44820d 100644 --- a/drivers/net/wireless/realtek/rtw89/ps.h +++ b/drivers/net/wireless/realtek/rtw89/ps.h @@ -22,6 +22,9 @@ void rtw89_p2p_noa_renew(struct rtw89_vif_link *rtwvif_link); void rtw89_p2p_noa_append(struct rtw89_vif_link *rtwvif_link, const struct ieee80211_p2p_noa_desc *desc); u8 rtw89_p2p_noa_fetch(struct rtw89_vif_link *rtwvif_link, void **data); +void rtw89_p2p_noa_once_init(struct rtw89_vif_link *rtwvif_link); +void rtw89_p2p_noa_once_deinit(struct rtw89_vif_link *rtwvif_link); +void rtw89_p2p_noa_once_recalc(struct rtw89_vif_link *rtwvif_link); static inline void rtw89_leave_ips_by_hwflags(struct rtw89_dev *rtwdev) { diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h index 10d0efa7a58e..f05c81ae5869 100644 --- a/drivers/net/wireless/realtek/rtw89/reg.h +++ b/drivers/net/wireless/realtek/rtw89/reg.h @@ -6618,6 +6618,13 @@ #define B_BE_RTS_LIMIT_IN_OFDM6 BIT(1) #define B_BE_CHECK_CCK_EN BIT(0) +#define R_BE_TXCNT 0x1082C +#define R_BE_TXCNT_C1 0x1482C +#define B_BE_ADD_TXCNT_BY BIT(31) +#define B_BE_TOTAL_TC_OPT BIT(30) +#define B_BE_S_TXCNT_LMT_MASK GENMASK(29, 24) +#define B_BE_L_TXCNT_LMT_MASK GENMASK(21, 16) + #define R_BE_MBSSID_DROP_0 0x1083C #define R_BE_MBSSID_DROP_0_C1 0x1483C #define B_BE_GI_LTF_FB_SEL BIT(30) @@ -7095,6 +7102,10 @@ #define B_BE_MACLBK_RDY_NUM_MASK GENMASK(7, 3) #define B_BE_MACLBK_EN BIT(0) +#define R_BE_CLIENT_OM_CTRL 0x11040 +#define R_BE_CLIENT_OM_CTRL_C1 0x15040 +#define B_BE_TRIG_DIS_EHTTB BIT(24) + #define R_BE_WMAC_NAV_CTL 0x11080 #define R_BE_WMAC_NAV_CTL_C1 0x15080 #define B_BE_WMAC_NAV_UPPER_EN BIT(26) @@ -7590,7 +7601,15 @@ #define B_BE_PWR_FORCE_RU_ON BIT(18) #define B_BE_PWR_FORCE_RU_ENON BIT(28) #define R_BE_PWR_FORCE_MACID 0x11A48 -#define B_BE_PWR_FORCE_MACID_ON BIT(9) +#define B_BE_PWR_FORCE_MACID_DBM_ON BIT(9) +#define B_BE_PWR_FORCE_MACID_DBM_VAL GENMASK(17, 10) +#define B_BE_PWR_FORCE_MACID_EN_VAL BIT(18) +#define B_BE_PWR_FORCE_MACID_EN_ON BIT(19) +#define B_BE_PWR_FORCE_MACID_ALL \ + (B_BE_PWR_FORCE_MACID_DBM_ON | \ + B_BE_PWR_FORCE_MACID_DBM_VAL | \ + B_BE_PWR_FORCE_MACID_EN_VAL | \ + B_BE_PWR_FORCE_MACID_EN_ON) #define R_BE_PWR_REG_CTRL 0x11A50 #define B_BE_PWR_BT_EN BIT(23) @@ -8157,6 +8176,8 @@ #define B_EDCCA_RPT_B_S40 BIT(4) #define B_EDCCA_RPT_B_S80 BIT(3) #define B_EDCCA_RPT_B_PATH_MASK GENMASK(2, 1) +#define R_EDCCA_RPT_P1_A 0x1740 +#define R_EDCCA_RPT_P1_B 0x1744 #define R_SWSI_V1 0x174C #define B_SWSI_W_BUSY_V1 BIT(24) #define B_SWSI_R_BUSY_V1 BIT(25) @@ -8222,6 +8243,7 @@ #define B_TXCKEN_FORCE_ALL GENMASK(24, 0) #define R_EDCCA_RPT_SEL 0x20CC #define B_EDCCA_RPT_SEL_MSK GENMASK(2, 0) +#define B_EDCCA_RPT_SEL_P1_MSK GENMASK(5, 3) #define R_ADC_FIFO 0x20fc #define B_ADC_FIFO_RST GENMASK(31, 24) #define B_ADC_FIFO_RXK GENMASK(31, 16) @@ -8291,6 +8313,8 @@ #define B_P1_EN_SOUND_WO_NDP BIT(1) #define R_EDCCA_RPT_A_BE 0x2E38 #define R_EDCCA_RPT_B_BE 0x2E3C +#define R_EDCCA_RPT_P1_A_BE 0x2E40 +#define R_EDCCA_RPT_P1_B_BE 0x2E44 #define R_S1_HW_SI_DIS 0x3200 #define B_S1_HW_SI_DIS_W_R_TRIG GENMASK(30, 28) #define R_P1_RXCK 0x32A0 @@ -8721,8 +8745,10 @@ #define B_DPD_GDIS BIT(13) #define B_IQK_RFC_ON BIT(1) #define R_TXPWRB 0x56CC +#define R_P1_TXPWRB 0x76CC #define B_TXPWRB_ON BIT(28) #define B_TXPWRB_VAL GENMASK(27, 19) +#define B_TXPWRB_MAX GENMASK(8, 0) #define R_DPD_OFT_EN 0x5800 #define B_DPD_OFT_EN BIT(28) #define B_DPD_TSSI_CW GENMASK(26, 18) @@ -9169,6 +9195,16 @@ #define B_IQKINF2_FCNT GENMASK(23, 16) #define B_IQKINF2_KCNT GENMASK(15, 8) #define B_IQKINF2_NCTLV GENMASK(7, 0) +#define R_TXAGC_REF_DBM_RF1_P0 0xBC04 +#define B_TXAGC_OFDM_REF_DBM_RF1_P0 GENMASK(10, 2) +#define B_TXAGC_CCK_REF_DBM_RF1_P0 GENMASK(19, 11) +#define R_TSSI_K_RF1_P0 0xBC28 +#define B_TSSI_K_OFDM_RF1_P0 GENMASK(9, 0) +#define R_TXAGC_REF_DBM_RF1_P1 0xBD04 +#define B_TXAGC_OFDM_REF_DBM_RF1_P1 GENMASK(10, 2) +#define B_TXAGC_CCK_REF_DBM_RF1_P1 GENMASK(19, 11) +#define R_TSSI_K_RF1_P1 0xBD28 +#define B_TSSI_K_OFDM_RF1_P1 GENMASK(9, 0) #define R_RFK_ST 0xBFF8 #define R_DCOF0 0xC000 #define B_DCOF0_RST BIT(17) @@ -9334,20 +9370,25 @@ #define R_TSSI_PWR_P0 0xE610 #define R_TSSI_PWR_P1 0xE710 #define B_TSSI_CONT_EN BIT(3) +#define R_P0_TXPWRB_BE 0xE61C +#define R_P1_TXPWRB_BE 0xE71C +#define B_TXPWRB_MAX_BE GENMASK(20, 12) #define R_TSSI_MAP_OFST_P0 0xE620 #define R_TSSI_MAP_OFST_P1 0xE720 #define B_TSSI_MAP_OFST_OFDM GENMASK(17, 9) #define B_TSSI_MAP_OFST_CCK GENMASK(26, 18) -#define R_TXAGC_REF0_P0 0xE628 -#define R_TXAGC_REF0_P1 0xE728 -#define B_TXAGC_REF0_OFDM_DBM GENMASK(8, 0) -#define B_TXAGC_REF0_CCK_DBM GENMASK(17, 9) -#define B_TXAGC_REF0_OFDM_CW GENMASK(26, 18) -#define R_TXAGC_REF1_P0 0xE62C -#define R_TXAGC_REF1_P1 0xE72C -#define B_TXAGC_REF1_CCK_CW GENMASK(8, 0) +#define R_TXAGC_REF_DBM_P0 0xE628 +#define B_TXAGC_OFDM_REF_DBM_P0 GENMASK(8, 0) +#define B_TXAGC_CCK_REF_DBM_P0 GENMASK(17, 9) +#define R_TSSI_K_P0 0xE6A0 +#define B_TSSI_K_OFDM_P0 GENMASK(29, 20) #define R_TXPWR_RSTB 0xE70C #define B_TXPWR_RSTB BIT(16) +#define R_TXAGC_REF_DBM_P1 0xE728 +#define B_TXAGC_OFDM_REF_DBM_P1 GENMASK(8, 0) +#define B_TXAGC_CCK_REF_DBM_P1 GENMASK(17, 9) +#define R_TSSI_K_P1 0xE7A0 +#define B_TSSI_K_OFDM_P1 GENMASK(29, 20) /* WiFi CPU local domain */ #define R_AX_WDT_CTRL 0x0040 diff --git a/drivers/net/wireless/realtek/rtw89/regd.c b/drivers/net/wireless/realtek/rtw89/regd.c index 80b2f74589eb..3ad14cab1f58 100644 --- a/drivers/net/wireless/realtek/rtw89/regd.c +++ b/drivers/net/wireless/realtek/rtw89/regd.c @@ -7,257 +7,266 @@ #include "ps.h" #include "util.h" -#define COUNTRY_REGD(_alpha2, _txpwr_regd...) \ - {.alpha2 = (_alpha2), \ - .txpwr_regd = {_txpwr_regd}, \ +static +void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request); + +#define COUNTRY_REGD(_alpha2, _rule_2ghz, _rule_5ghz, _rule_6ghz, _fmap) \ + { \ + .alpha2 = _alpha2, \ + .txpwr_regd[RTW89_BAND_2G] = _rule_2ghz, \ + .txpwr_regd[RTW89_BAND_5G] = _rule_5ghz, \ + .txpwr_regd[RTW89_BAND_6G] = _rule_6ghz, \ + .func_bitmap = { _fmap, }, \ } +static_assert(BITS_PER_TYPE(unsigned long) >= NUM_OF_RTW89_REGD_FUNC); + static const struct rtw89_regd rtw89_ww_regd = - COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW); + COUNTRY_REGD("00", RTW89_WW, RTW89_WW, RTW89_WW, 0x0); static const struct rtw89_regd rtw89_regd_map[] = { - COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC), - COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("CL", RTW89_CHILE, RTW89_CHILE, RTW89_CHILE), - COUNTRY_REGD("CO", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("CR", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("EC", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("SV", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("GT", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("HN", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("MX", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC), - COUNTRY_REGD("NI", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("PA", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("PY", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("PE", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("US", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("UY", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("VE", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("PR", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("DO", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("AT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("DK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("EE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("FI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("FR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("DE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("HU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LV", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MC", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("NL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("NO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("PL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("PT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("ES", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GB", RTW89_UK, RTW89_UK, RTW89_UK), - COUNTRY_REGD("AL", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("HR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("EG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("IL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("JO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("KZ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("KW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("KG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LB", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("NA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("OM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("QA", RTW89_QATAR, RTW89_QATAR, RTW89_QATAR), - COUNTRY_REGD("RO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("RU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("RS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("ME", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ZA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("UA", RTW89_UKRAINE, RTW89_UKRAINE, RTW89_UKRAINE), - COUNTRY_REGD("AE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("YE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ZW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CN", RTW89_CN, RTW89_CN, RTW89_CN), - COUNTRY_REGD("HK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("IN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ID", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KR", RTW89_KCC, RTW89_KCC, RTW89_KCC), - COUNTRY_REGD("MY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("PK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("PH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TW", RTW89_FCC, RTW89_FCC, RTW89_ETSI), - COUNTRY_REGD("TH", RTW89_THAILAND, RTW89_THAILAND, RTW89_THAILAND), - COUNTRY_REGD("VN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AU", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA), - COUNTRY_REGD("NZ", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA), - COUNTRY_REGD("PG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CA", RTW89_IC, RTW89_IC, RTW89_IC), - COUNTRY_REGD("JP", RTW89_MKK, RTW89_MKK, RTW89_MKK), - COUNTRY_REGD("JM", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("AN", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("TT", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("TN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("DZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("AS", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("AD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("AQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("AG", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("AM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("AW", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BS", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BB", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BY", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BZ", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("BJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BM", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BT", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("BV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("IO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("VG", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("BN", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BF", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("BI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KY", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("CF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CX", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("CC", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("KM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("CK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CI", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("DJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("DM", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("GQ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("ER", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ET", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("FK", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("FO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("FJ", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("GF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("PF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GL", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GD", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("GP", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GU", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("GG", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("GW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GY", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("HT", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("HM", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("VA", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("IM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("JE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KI", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("XK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("LY", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MV", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ML", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MH", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("MQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("MU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("YT", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("FM", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("MD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("MS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NP", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NC", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("NU", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("NF", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("MP", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("PW", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("RE", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("RW", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("KN", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("LC", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("MF", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("SX", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("PM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("VC", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("WS", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("SM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ST", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SC", RTW89_FCC, RTW89_FCC, RTW89_NA), - COUNTRY_REGD("SL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("SB", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("GS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SR", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("SJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("TK", RTW89_ACMA, RTW89_ACMA, RTW89_NA), - COUNTRY_REGD("TO", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TC", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("TV", RTW89_ETSI, RTW89_NA, RTW89_NA), - COUNTRY_REGD("UG", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("VI", RTW89_FCC, RTW89_FCC, RTW89_FCC), - COUNTRY_REGD("UZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI), - COUNTRY_REGD("VU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("WF", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("EH", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("ZM", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("CU", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("IR", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("SY", RTW89_ETSI, RTW89_NA, RTW89_NA), - COUNTRY_REGD("SD", RTW89_ETSI, RTW89_ETSI, RTW89_NA), - COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA), + COUNTRY_REGD("AR", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0), + COUNTRY_REGD("BO", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("BR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("CL", RTW89_CHILE, RTW89_CHILE, RTW89_CHILE, 0x0), + COUNTRY_REGD("CO", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("CR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("EC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("SV", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("GT", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("HN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("MX", RTW89_MEXICO, RTW89_MEXICO, RTW89_FCC, 0x0), + COUNTRY_REGD("NI", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("PA", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("PY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("PE", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("US", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x1), + COUNTRY_REGD("UY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("VE", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("PR", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("DO", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("AT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("BE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("CY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("CZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("DK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("EE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("FI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("FR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("DE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("GR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("HU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("IS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("IE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("IT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("LV", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("LI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("LT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("LU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("MT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("MC", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("NL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("NO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("PL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("PT", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("SK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("SI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("ES", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("SE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("CH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("GB", RTW89_UK, RTW89_UK, RTW89_UK, 0x0), + COUNTRY_REGD("AL", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("BH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("BA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("HR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("EG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("IQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("IL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("JO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("KZ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("KW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("KG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LB", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("NA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("OM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("QA", RTW89_QATAR, RTW89_QATAR, RTW89_QATAR, 0x0), + COUNTRY_REGD("RO", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x2), + COUNTRY_REGD("RU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("SN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("RS", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("ME", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ZA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("UA", RTW89_UKRAINE, RTW89_UKRAINE, RTW89_UKRAINE, 0x0), + COUNTRY_REGD("AE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("YE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ZW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("BD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CN", RTW89_CN, RTW89_CN, RTW89_CN, 0x0), + COUNTRY_REGD("HK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("IN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ID", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KR", RTW89_KCC, RTW89_KCC, RTW89_KCC, 0x1), + COUNTRY_REGD("MY", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("PK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("PH", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("SG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TW", RTW89_FCC, RTW89_FCC, RTW89_ETSI, 0x0), + COUNTRY_REGD("TH", RTW89_THAILAND, RTW89_THAILAND, RTW89_THAILAND, 0x0), + COUNTRY_REGD("VN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AU", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA, 0x0), + COUNTRY_REGD("NZ", RTW89_ACMA, RTW89_ACMA, RTW89_ACMA, 0x0), + COUNTRY_REGD("PG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CA", RTW89_IC, RTW89_IC, RTW89_IC, 0x1), + COUNTRY_REGD("JP", RTW89_MKK, RTW89_MKK, RTW89_MKK, 0x0), + COUNTRY_REGD("JM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("AN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("TT", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("TN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("DZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("AS", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("AD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("AQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("AG", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("AM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("AW", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BS", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BB", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BY", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BZ", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("BJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BT", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BW", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("BV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("IO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("VG", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("BN", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BF", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("BI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KY", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("CF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CX", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("CC", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("KM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("CK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CI", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("DJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("DM", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("GQ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("ER", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ET", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("FK", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("FO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("FJ", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("GF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("PF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GM", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GE", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GI", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GL", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GD", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("GP", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GU", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("GG", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("GW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GY", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("HT", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("HM", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("VA", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("IM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("JE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KI", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("XK", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LA", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LR", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("LY", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MV", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ML", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MH", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("MQ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("MU", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("YT", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("FM", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("MD", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MN", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("MS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NP", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NC", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("NU", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("NF", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("MP", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("PW", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("RE", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("RW", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("KN", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("LC", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("MF", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("SX", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("PM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("VC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("WS", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("SM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ST", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SC", RTW89_FCC, RTW89_FCC, RTW89_NA, 0x0), + COUNTRY_REGD("SL", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("SB", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("GS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SR", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("SJ", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TJ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TG", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("TK", RTW89_ACMA, RTW89_ACMA, RTW89_NA, 0x0), + COUNTRY_REGD("TO", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TC", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("TV", RTW89_ETSI, RTW89_NA, RTW89_NA, 0x0), + COUNTRY_REGD("UG", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("VI", RTW89_FCC, RTW89_FCC, RTW89_FCC, 0x0), + COUNTRY_REGD("UZ", RTW89_ETSI, RTW89_ETSI, RTW89_ETSI, 0x0), + COUNTRY_REGD("VU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("WF", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("EH", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("ZM", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("CU", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("IR", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("SY", RTW89_ETSI, RTW89_NA, RTW89_NA, 0x0), + COUNTRY_REGD("SD", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), + COUNTRY_REGD("PS", RTW89_ETSI, RTW89_ETSI, RTW89_NA, 0x0), }; static const char rtw89_alpha2_list_eu[][3] = { @@ -295,13 +304,16 @@ static const char rtw89_alpha2_list_eu[][3] = { "RO", }; -static const struct rtw89_regd *rtw89_regd_find_reg_by_name(const char *alpha2) +static const struct rtw89_regd *rtw89_regd_find_reg_by_name(struct rtw89_dev *rtwdev, + const char *alpha2) { + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; u32 i; - for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { - if (!memcmp(rtw89_regd_map[i].alpha2, alpha2, 2)) - return &rtw89_regd_map[i]; + for (i = 0; i < regd_ctrl->nr; i++) { + if (!memcmp(regd_ctrl->map[i].alpha2, alpha2, 2)) + return ®d_ctrl->map[i]; } return &rtw89_ww_regd; @@ -312,22 +324,25 @@ static bool rtw89_regd_is_ww(const struct rtw89_regd *regd) return regd == &rtw89_ww_regd; } -static u8 rtw89_regd_get_index(const struct rtw89_regd *regd) +static u8 rtw89_regd_get_index(struct rtw89_dev *rtwdev, const struct rtw89_regd *regd) { + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; + BUILD_BUG_ON(ARRAY_SIZE(rtw89_regd_map) > RTW89_REGD_MAX_COUNTRY_NUM); if (rtw89_regd_is_ww(regd)) return RTW89_REGD_MAX_COUNTRY_NUM; - return regd - rtw89_regd_map; + return regd - regd_ctrl->map; } -static u8 rtw89_regd_get_index_by_name(const char *alpha2) +static u8 rtw89_regd_get_index_by_name(struct rtw89_dev *rtwdev, const char *alpha2) { const struct rtw89_regd *regd; - regd = rtw89_regd_find_reg_by_name(alpha2); - return rtw89_regd_get_index(regd); + regd = rtw89_regd_find_reg_by_name(rtwdev, alpha2); + return rtw89_regd_get_index(rtwdev, regd); } #define rtw89_debug_regd(_dev, _regd, _desc, _argv...) \ @@ -345,6 +360,7 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, struct wiphy *wiphy) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; const struct rtw89_chip_info *chip = rtwdev->chip; struct ieee80211_supported_band *sband; struct rtw89_acpi_dsm_result res = {}; @@ -382,8 +398,8 @@ static void rtw89_regd_setup_unii4(struct rtw89_dev *rtwdev, "acpi: eval if allow unii-4: 0x%x\n", val); bottom: - for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { - const struct rtw89_regd *regd = &rtw89_regd_map[i]; + for (i = 0; i < regd_ctrl->nr; i++) { + const struct rtw89_regd *regd = ®d_ctrl->map[i]; switch (regd->txpwr_regd[RTW89_BAND_5G]) { case RTW89_FCC: @@ -406,7 +422,7 @@ static void __rtw89_regd_setup_policy_6ghz(struct rtw89_dev *rtwdev, bool block, struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; u8 index; - index = rtw89_regd_get_index_by_name(alpha2); + index = rtw89_regd_get_index_by_name(rtwdev, alpha2); if (index == RTW89_REGD_MAX_COUNTRY_NUM) { rtw89_debug(rtwdev, RTW89_DBG_REGD, "%s: unknown alpha2 %c%c\n", __func__, alpha2[0], alpha2[1]); @@ -474,6 +490,7 @@ out: static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) { struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd_ctrl *regd_ctrl = ®ulatory->ctrl; const struct rtw89_acpi_policy_6ghz_sp *ptr; struct rtw89_acpi_dsm_result res = {}; bool enable_by_us; @@ -505,8 +522,8 @@ static void rtw89_regd_setup_policy_6ghz_sp(struct rtw89_dev *rtwdev) enable_by_us = u8_get_bits(ptr->conf, RTW89_ACPI_CONF_6GHZ_SP_US); - for (i = 0; i < ARRAY_SIZE(rtw89_regd_map); i++) { - const struct rtw89_regd *tmp = &rtw89_regd_map[i]; + for (i = 0; i < regd_ctrl->nr; i++) { + const struct rtw89_regd *tmp = ®d_ctrl->map[i]; if (enable_by_us && memcmp(tmp->alpha2, "US", 2) == 0) clear_bit(i, regulatory->block_6ghz_sp); @@ -571,10 +588,56 @@ bottom: kfree(sband); } +#define RTW89_DEF_REGD_STR(regd) \ + [RTW89_ ## regd] = #regd + +static const char * const rtw89_regd_string[] = { + RTW89_DEF_REGD_STR(WW), + RTW89_DEF_REGD_STR(ETSI), + RTW89_DEF_REGD_STR(FCC), + RTW89_DEF_REGD_STR(MKK), + RTW89_DEF_REGD_STR(NA), + RTW89_DEF_REGD_STR(IC), + RTW89_DEF_REGD_STR(KCC), + RTW89_DEF_REGD_STR(ACMA), + RTW89_DEF_REGD_STR(NCC), + RTW89_DEF_REGD_STR(MEXICO), + RTW89_DEF_REGD_STR(CHILE), + RTW89_DEF_REGD_STR(UKRAINE), + RTW89_DEF_REGD_STR(CN), + RTW89_DEF_REGD_STR(QATAR), + RTW89_DEF_REGD_STR(UK), + RTW89_DEF_REGD_STR(THAILAND), +}; + +static_assert(ARRAY_SIZE(rtw89_regd_string) == RTW89_REGD_NUM); + +const char *rtw89_regd_get_string(enum rtw89_regulation_type regd) +{ + if (regd < 0 || regd >= RTW89_REGD_NUM) + return "(unknown)"; + + return rtw89_regd_string[regd]; +} + int rtw89_regd_setup(struct rtw89_dev *rtwdev) { + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + struct rtw89_fw_elm_info *elm_info = &rtwdev->fw.elm_info; + const struct rtw89_regd_data *regd_data = elm_info->regd; struct wiphy *wiphy = rtwdev->hw->wiphy; + if (regd_data) { + regulatory->ctrl.nr = regd_data->nr; + regulatory->ctrl.map = regd_data->map; + } else { + regulatory->ctrl.nr = ARRAY_SIZE(rtw89_regd_map); + regulatory->ctrl.map = rtw89_regd_map; + } + + regulatory->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; + regulatory->txpwr_uk_follow_etsi = true; + if (!wiphy) return -EINVAL; @@ -585,21 +648,16 @@ int rtw89_regd_setup(struct rtw89_dev *rtwdev) return 0; } -int rtw89_regd_init(struct rtw89_dev *rtwdev, - void (*reg_notifier)(struct wiphy *wiphy, - struct regulatory_request *request)) +int rtw89_regd_init_hint(struct rtw89_dev *rtwdev) { - struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; const struct rtw89_regd *chip_regd; struct wiphy *wiphy = rtwdev->hw->wiphy; int ret; - regulatory->reg_6ghz_power = RTW89_REG_6GHZ_POWER_DFLT; - if (!wiphy) return -EINVAL; - chip_regd = rtw89_regd_find_reg_by_name(rtwdev->efuse.country_code); + chip_regd = rtw89_regd_find_reg_by_name(rtwdev, rtwdev->efuse.country_code); if (!rtw89_regd_is_ww(chip_regd)) { rtwdev->regulatory.regd = chip_regd; /* Ignore country ie if there is a country domain programmed in chip */ @@ -637,7 +695,7 @@ static void rtw89_regd_apply_policy_unii4(struct rtw89_dev *rtwdev, if (!chip->support_unii4) return; - index = rtw89_regd_get_index(regd); + index = rtw89_regd_get_index(rtwdev, regd); if (index != RTW89_REGD_MAX_COUNTRY_NUM && !test_bit(index, regulatory->block_unii4)) return; @@ -655,7 +713,7 @@ static bool regd_is_6ghz_blocked(struct rtw89_dev *rtwdev) const struct rtw89_regd *regd = regulatory->regd; u8 index; - index = rtw89_regd_get_index(regd); + index = rtw89_regd_get_index(rtwdev, regd); if (index != RTW89_REGD_MAX_COUNTRY_NUM && !test_bit(index, regulatory->block_6ghz)) return false; @@ -696,11 +754,47 @@ static void rtw89_regd_apply_policy_6ghz(struct rtw89_dev *rtwdev, sband->channels[i].flags |= IEEE80211_CHAN_DISABLED; } +static void rtw89_regd_apply_policy_tas(struct rtw89_dev *rtwdev) +{ + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + const struct rtw89_regd *regd = regulatory->regd; + struct rtw89_tas_info *tas = &rtwdev->tas; + u8 tas_country; + + if (!tas->enable) + return; + + if (memcmp("US", regd->alpha2, 2) == 0) + tas_country = RTW89_ACPI_CONF_TAS_US; + else if (memcmp("CA", regd->alpha2, 2) == 0) + tas_country = RTW89_ACPI_CONF_TAS_CA; + else if (memcmp("KR", regd->alpha2, 2) == 0) + tas_country = RTW89_ACPI_CONF_TAS_KR; + else + tas_country = RTW89_ACPI_CONF_TAS_OTHERS; + + tas->block_regd = !(tas->enabled_countries & tas_country && + test_bit(RTW89_REGD_FUNC_TAS, regd->func_bitmap)); +} + +static void rtw89_regd_apply_policy_ant_gain(struct rtw89_dev *rtwdev) +{ + struct rtw89_regulatory_info *regulatory = &rtwdev->regulatory; + struct rtw89_ant_gain_info *ant_gain = &rtwdev->ant_gain; + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_regd *regd = regulatory->regd; + + if (!chip->support_ant_gain) + return; + + ant_gain->block_country = !test_bit(RTW89_REGD_FUNC_DAG, regd->func_bitmap); +} + static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, struct wiphy *wiphy, struct regulatory_request *request) { - rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(request->alpha2); + rtwdev->regulatory.regd = rtw89_regd_find_reg_by_name(rtwdev, request->alpha2); /* This notification might be set from the system of distros, * and it does not expect the regulatory will be modified by * connecting to an AP (i.e. country ie). @@ -713,14 +807,17 @@ static void rtw89_regd_notifier_apply(struct rtw89_dev *rtwdev, rtw89_regd_apply_policy_unii4(rtwdev, wiphy); rtw89_regd_apply_policy_6ghz(rtwdev, wiphy); + rtw89_regd_apply_policy_tas(rtwdev); + rtw89_regd_apply_policy_ant_gain(rtwdev); } +static void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request) { struct ieee80211_hw *hw = wiphy_to_ieee80211_hw(wiphy); struct rtw89_dev *rtwdev = hw->priv; - mutex_lock(&rtwdev->mutex); + wiphy_lock(wiphy); rtw89_leave_ps_mode(rtwdev); if (wiphy->regd) { @@ -736,7 +833,7 @@ void rtw89_regd_notifier(struct wiphy *wiphy, struct regulatory_request *request rtw89_core_set_chip_txpwr(rtwdev); exit: - mutex_unlock(&rtwdev->mutex); + wiphy_unlock(wiphy); } /* Maximum Transmit Power field (@raw) can be EIRP or PSD. @@ -925,7 +1022,7 @@ static bool __rtw89_reg_6ghz_power_recalc(struct rtw89_dev *rtwdev) sel = RTW89_REG_6GHZ_POWER_DFLT; if (sel == RTW89_REG_6GHZ_POWER_STD) { - index = rtw89_regd_get_index(regd); + index = rtw89_regd_get_index(rtwdev, regd); if (index == RTW89_REGD_MAX_COUNTRY_NUM || test_bit(index, regulatory->block_6ghz_sp)) { rtw89_debug(rtwdev, RTW89_DBG_REGD, @@ -986,7 +1083,7 @@ int rtw89_reg_6ghz_recalc(struct rtw89_dev *rtwdev, struct rtw89_vif_link *rtwvi unsigned int changed = 0; int ret; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); /* The result of reg_6ghz_tpe may depend on reg_6ghz_power type, * so must do reg_6ghz_tpe_recalc() after reg_6ghz_power_recalc(). diff --git a/drivers/net/wireless/realtek/rtw89/rtw8851b.c b/drivers/net/wireless/realtek/rtw89/rtw8851b.c index c56f70267882..fafa200a9c8d 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8851b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8851b.c @@ -224,10 +224,17 @@ static const struct rtw89_edcca_regs rtw8851b_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL_V1, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; @@ -1596,10 +1603,16 @@ static void rtw8851b_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8851b_rx_dck(rtwdev, phy_idx, chanctx_idx); rtw8851b_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8851b_tssi(rtwdev, phy_idx, true, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8851b_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); } static void rtw8851b_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -2410,6 +2423,7 @@ static const struct rtw89_chip_ops rtw8851b_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, @@ -2445,6 +2459,7 @@ const struct rtw89_chip_info rtw8851b_chip_info = { .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = 0, + .fw_blacklist = NULL, .fifo_size = 196608, .small_fifo_size = true, .dle_scc_rsvd_size = 98304, @@ -2483,10 +2498,15 @@ const struct rtw89_chip_info rtw8851b_chip_info = { BIT(NL80211_CHAN_WIDTH_80), .support_unii4 = true, .support_ant_gain = false, + .support_tas = false, + .support_sar_by_ant = false, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, + .hw_tkip_crypto = false, + .hw_mlo_bmc_crypto = false, .rf_path_num = 1, .tx_nss = 1, .rx_nss = 1, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852a.c b/drivers/net/wireless/realtek/rtw89/rtw8852a.c index 9bd2842c27d5..cd5987fc52d7 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852a.c @@ -522,10 +522,17 @@ static const struct rtw89_edcca_regs rtw8852a_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; @@ -1356,10 +1363,16 @@ static void rtw8852a_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8852a_rx_dck(rtwdev, phy_idx, true, chanctx_idx); rtw8852a_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852a_tssi(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852a_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); } static void rtw8852a_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -2136,6 +2149,7 @@ static const struct rtw89_chip_ops rtw8852a_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, @@ -2162,6 +2176,7 @@ const struct rtw89_chip_info rtw8852a_chip_info = { .try_ce_fw = false, .bbmcu_nr = 0, .needed_fw_elms = 0, + .fw_blacklist = NULL, .fifo_size = 458752, .small_fifo_size = false, .dle_scc_rsvd_size = 0, @@ -2201,10 +2216,15 @@ const struct rtw89_chip_info rtw8852a_chip_info = { BIT(NL80211_CHAN_WIDTH_80), .support_unii4 = false, .support_ant_gain = false, + .support_tas = false, + .support_sar_by_ant = false, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, + .hw_tkip_crypto = false, + .hw_mlo_bmc_crypto = false, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b.c b/drivers/net/wireless/realtek/rtw89/rtw8852b.c index dfb2bf61b0b8..dacdb384de2c 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b.c @@ -189,10 +189,17 @@ static const struct rtw89_edcca_regs rtw8852b_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL_V1, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; @@ -568,10 +575,16 @@ static void rtw8852b_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8852b_rx_dck(rtwdev, phy_idx, chanctx_idx); rtw8852b_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852b_tssi(rtwdev, phy_idx, true, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852b_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); } static void rtw8852b_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -763,6 +776,7 @@ static const struct rtw89_chip_ops rtw8852b_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, @@ -798,6 +812,7 @@ const struct rtw89_chip_info rtw8852b_chip_info = { .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = 0, + .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 196608, .small_fifo_size = true, .dle_scc_rsvd_size = 98304, @@ -837,10 +852,15 @@ const struct rtw89_chip_info rtw8852b_chip_info = { BIT(NL80211_CHAN_WIDTH_80), .support_unii4 = true, .support_ant_gain = true, + .support_tas = false, + .support_sar_by_ant = true, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, + .hw_tkip_crypto = false, + .hw_mlo_bmc_crypto = false, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c index 0e094ce9c9b0..0cf03f18cbb1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_common.c @@ -8,6 +8,7 @@ #include "phy.h" #include "reg.h" #include "rtw8852b_common.h" +#include "sar.h" #include "util.h" static const struct rtw89_reg3_def rtw8852bx_pmac_ht20_mcs7_tbl[] = { @@ -621,9 +622,9 @@ static void rtw8852bt_ext_loss_avg_update(struct rtw89_dev *rtwdev, if (ext_loss_a == ext_loss_b) { ext_loss_avg = ext_loss_a; } else { - linear = rtw89_db_2_linear(abs(ext_loss_a - ext_loss_b)) + 1; - linear = DIV_ROUND_CLOSEST_ULL(linear / 2, 1 << RTW89_LINEAR_FRAC_BITS); - ext_loss_avg = rtw89_linear_2_db(linear); + linear = rtw89_db_to_linear(abs(ext_loss_a - ext_loss_b)) + 1; + linear /= 2; + ext_loss_avg = rtw89_linear_to_db(linear); ext_loss_avg += min(ext_loss_a, ext_loss_b); } @@ -1234,6 +1235,7 @@ static u32 rtw8852bx_bb_cal_txpwr_ref(struct rtw89_dev *rtwdev, u32_encode_bits(ref, B_DPD_REF); } +/* @pwr_ofst (unit: 1/8 dBm): power of path A minus power of path B */ static void rtw8852bx_set_txpwr_ref(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx, s16 pwr_ofst) { @@ -1336,6 +1338,27 @@ static void rtw8852bx_set_tx_shape(struct rtw89_dev *rtwdev, tx_shape_ofdm); } +static s16 rtw8852bx_get_txpwr_sar_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan) +{ + struct rtw89_sar_parm sar_parm = { + .center_freq = chan->freq, + .force_path = true, + }; + s16 sar_bb_a, sar_bb_b; + s8 sar_mac; + + sar_parm.path = RF_PATH_A; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_bb_a = rtw89_phy_txpwr_mac_to_bb(rtwdev, sar_mac); + + sar_parm.path = RF_PATH_B; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_bb_b = rtw89_phy_txpwr_mac_to_bb(rtwdev, sar_mac); + + return sar_bb_a - sar_bb_b; +} + static void rtw8852bx_set_txpwr_diff(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -1343,6 +1366,7 @@ static void rtw8852bx_set_txpwr_diff(struct rtw89_dev *rtwdev, s16 pwr_ofst; pwr_ofst = rtw89_phy_ant_gain_pwr_offset(rtwdev, chan); + pwr_ofst += rtw8852bx_get_txpwr_sar_diff(rtwdev, chan); rtw8852bx_set_txpwr_ref(rtwdev, phy_idx, pwr_ofst); } diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c index ef47a5facc83..fbf82d42687b 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852b_rfk.c @@ -3585,9 +3585,10 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel); struct rtw8852bx_bb_tssi_bak tssi_bak; s32 aliment_diff, tssi_cw_default; - u32 start_time, finish_time; u32 bb_reg_backup[8] = {0}; + ktime_t start_time; const s16 *power; + s64 this_time; u8 band; bool ok; u32 tmp; @@ -3613,7 +3614,7 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, return; } - start_time = ktime_get_ns(); + start_time = ktime_get(); if (chan->band_type == RTW89_BAND_2G) power = power_2g; @@ -3738,12 +3739,12 @@ out: rtw8852bx_bb_restore_tssi(rtwdev, phy, &tssi_bak); rtw8852bx_bb_tx_mode_switch(rtwdev, phy, 0); - finish_time = ktime_get_ns(); - tssi_info->tssi_alimk_time += finish_time - start_time; + this_time = ktime_us_delta(ktime_get(), start_time); + tssi_info->tssi_alimk_time += this_time; rtw89_debug(rtwdev, RTW89_DBG_RFK, - "[TSSI PA K] %s processing time = %d ms\n", __func__, - tssi_info->tssi_alimk_time); + "[TSSI PA K] %s processing time = %lld us (acc = %llu us)\n", + __func__, this_time, tssi_info->tssi_alimk_time); } void rtw8852b_dpk_init(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c index bde3e1fb7ca6..289dce688d72 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt.c @@ -187,10 +187,17 @@ static const struct rtw89_edcca_regs rtw8852bt_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL_V1, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; @@ -541,10 +548,16 @@ static void rtw8852bt_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_chanctx_idx chanctx_idx = rtwvif_link->chanctx_idx; enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8852bt_rx_dck(rtwdev, phy_idx, chanctx_idx); rtw8852bt_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852bt_tssi(rtwdev, phy_idx, true, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852bt_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); } static void rtw8852bt_rfk_band_changed(struct rtw89_dev *rtwdev, @@ -697,6 +710,7 @@ static const struct rtw89_chip_ops rtw8852bt_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, @@ -732,6 +746,7 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { .try_ce_fw = true, .bbmcu_nr = 0, .needed_fw_elms = RTW89_AX_GEN_DEF_NEEDED_FW_ELEMENTS_NO_6GHZ, + .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 458752, .small_fifo_size = true, .dle_scc_rsvd_size = 98304, @@ -770,10 +785,15 @@ const struct rtw89_chip_info rtw8852bt_chip_info = { BIT(NL80211_CHAN_WIDTH_80), .support_unii4 = true, .support_ant_gain = true, + .support_tas = false, + .support_sar_by_ant = true, .ul_tb_waveform_ctrl = true, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = true, .hw_sec_hdr = false, .hw_mgmt_tx_encrypt = false, + .hw_tkip_crypto = true, + .hw_mlo_bmc_crypto = false, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c index 336a83e1d46b..6e6889eea9a0 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852bt_rfk.c @@ -3663,9 +3663,10 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, u8 ch_idx = _tssi_ch_to_idx(rtwdev, channel); struct rtw8852bx_bb_tssi_bak tssi_bak; s32 aliment_diff, tssi_cw_default; - u32 start_time, finish_time; u32 bb_reg_backup[8] = {}; + ktime_t start_time; const s16 *power; + s64 this_time; u8 band; bool ok; u32 tmp; @@ -3675,7 +3676,7 @@ static void _tssi_alimentk(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, "======> %s channel=%d path=%d\n", __func__, channel, path); - start_time = ktime_get_ns(); + start_time = ktime_get(); if (chan->band_type == RTW89_BAND_2G) power = power_2g; @@ -3802,12 +3803,12 @@ out: rtw8852bx_bb_restore_tssi(rtwdev, phy, &tssi_bak); rtw8852bx_bb_tx_mode_switch(rtwdev, phy, 0); - finish_time = ktime_get_ns(); - tssi_info->tssi_alimk_time += finish_time - start_time; + this_time = ktime_us_delta(ktime_get(), start_time); + tssi_info->tssi_alimk_time += this_time; rtw89_debug(rtwdev, RTW89_DBG_RFK, - "[TSSI PA K] %s processing time = %d ms\n", __func__, - tssi_info->tssi_alimk_time); + "[TSSI PA K] %s processing time = %lld us (acc = %llu us)\n", + __func__, this_time, tssi_info->tssi_alimk_time); } void rtw8852bt_dpk_init(struct rtw89_dev *rtwdev) diff --git a/drivers/net/wireless/realtek/rtw89/rtw8852c.c b/drivers/net/wireless/realtek/rtw89/rtw8852c.c index bc84b15e7826..2a6143a8d256 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8852c.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8852c.c @@ -12,9 +12,10 @@ #include "rtw8852c.h" #include "rtw8852c_rfk.h" #include "rtw8852c_table.h" +#include "sar.h" #include "util.h" -#define RTW8852C_FW_FORMAT_MAX 1 +#define RTW8852C_FW_FORMAT_MAX 2 #define RTW8852C_FW_BASENAME "rtw89/rtw8852c_fw" #define RTW8852C_MODULE_FIRMWARE \ RTW8852C_FW_BASENAME "-" __stringify(RTW8852C_FW_FORMAT_MAX) ".bin" @@ -186,10 +187,17 @@ static const struct rtw89_edcca_regs rtw8852c_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_EDCCA_LVL, .ppdu_mask = B_EDCCA_LVL_MSK3, - .rpt_a = R_EDCCA_RPT_A, - .rpt_b = R_EDCCA_RPT_B, - .rpt_sel = R_EDCCA_RPT_SEL, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A, + .rpt_b = R_EDCCA_RPT_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A, + .rpt_b = R_EDCCA_RPT_P1_B, + .rpt_sel = R_EDCCA_RPT_SEL, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST, .tx_collision_t2r_st_mask = B_TX_COLLISION_T2R_ST_M, }; @@ -1853,10 +1861,16 @@ static void rtw8852c_rfk_channel(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy_idx = rtwvif_link->phy_idx; rtw8852c_mcc_get_ch_info(rtwdev, phy_idx); + rtw89_btc_ntfy_conn_rfk(rtwdev, true); + rtw8852c_rx_dck(rtwdev, phy_idx, false); rtw8852c_iqk(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852c_tssi(rtwdev, phy_idx, chanctx_idx); + rtw89_btc_ntfy_preserve_bt_time(rtwdev, 30); rtw8852c_dpk(rtwdev, phy_idx, chanctx_idx); + + rtw89_btc_ntfy_conn_rfk(rtwdev, false); rtw89_fw_h2c_rf_ntfy_mcc(rtwdev); } @@ -2065,6 +2079,31 @@ static void rtw8852c_set_txpwr_diff(struct rtw89_dev *rtwdev, rtw8852c_set_txpwr_ref(rtwdev, phy_idx, pwr_ofst); } +static void rtw8852c_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_sar_parm sar_parm = { + .center_freq = chan->freq, + .force_path = true, + }; + s16 sar_rf; + s8 sar_mac; + + if (phy_idx != RTW89_PHY_0) + return; + + sar_parm.path = RF_PATH_A; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_TXPWRB, B_TXPWRB_MAX, sar_rf); + + sar_parm.path = RF_PATH_B; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB, B_TXPWRB_MAX, sar_rf); +} + static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -2075,6 +2114,7 @@ static void rtw8852c_set_txpwr(struct rtw89_dev *rtwdev, rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); rtw8852c_set_txpwr_diff(rtwdev, chan, phy_idx); + rtw8852c_set_txpwr_sar_diff(rtwdev, chan, phy_idx); } static void rtw8852c_set_txpwr_ctrl(struct rtw89_dev *rtwdev, @@ -2867,6 +2907,7 @@ static int rtw8852c_mac_disable_bb_rf(struct rtw89_dev *rtwdev) static const struct rtw89_chanctx_listener rtw8852c_chanctx_listener = { .callbacks[RTW89_CHANCTX_CALLBACK_RFK] = rtw8852c_rfk_chanctx_cb, + .callbacks[RTW89_CHANCTX_CALLBACK_TAS] = rtw89_tas_chanctx_cb, }; #ifdef CONFIG_PM @@ -2928,6 +2969,7 @@ static const struct rtw89_chip_ops rtw8852c_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl, .h2c_ampdu_cmac_tbl = NULL, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl, .h2c_default_dmac_tbl = NULL, .h2c_update_beacon = rtw89_fw_h2c_update_beacon, .h2c_ba_cam = rtw89_fw_h2c_ba_cam, @@ -2954,6 +2996,7 @@ const struct rtw89_chip_info rtw8852c_chip_info = { .try_ce_fw = false, .bbmcu_nr = 0, .needed_fw_elms = 0, + .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 458752, .small_fifo_size = false, .dle_scc_rsvd_size = 0, @@ -2996,10 +3039,15 @@ const struct rtw89_chip_info rtw8852c_chip_info = { BIT(NL80211_CHAN_WIDTH_160), .support_unii4 = true, .support_ant_gain = true, + .support_tas = true, + .support_sar_by_ant = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = true, + .rx_freq_frome_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, + .hw_tkip_crypto = true, + .hw_mlo_bmc_crypto = false, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a.c b/drivers/net/wireless/realtek/rtw89/rtw8922a.c index 11d66bfceb15..1d0f6e7df497 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a.c @@ -12,6 +12,7 @@ #include "reg.h" #include "rtw8922a.h" #include "rtw8922a_rfk.h" +#include "sar.h" #include "util.h" #define RTW8922A_FW_FORMAT_MAX 3 @@ -205,10 +206,17 @@ static const struct rtw89_edcca_regs rtw8922a_edcca_regs = { .edcca_p_mask = B_EDCCA_LVL_MSK1, .ppdu_level = R_SEG0R_PPDU_LVL_BE, .ppdu_mask = B_EDCCA_LVL_MSK1, - .rpt_a = R_EDCCA_RPT_A_BE, - .rpt_b = R_EDCCA_RPT_B_BE, - .rpt_sel = R_EDCCA_RPT_SEL_BE, - .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + .p = {{ + .rpt_a = R_EDCCA_RPT_A_BE, + .rpt_b = R_EDCCA_RPT_B_BE, + .rpt_sel = R_EDCCA_RPT_SEL_BE, + .rpt_sel_mask = B_EDCCA_RPT_SEL_MSK, + }, { + .rpt_a = R_EDCCA_RPT_P1_A_BE, + .rpt_b = R_EDCCA_RPT_P1_B_BE, + .rpt_sel = R_EDCCA_RPT_SEL_BE, + .rpt_sel_mask = B_EDCCA_RPT_SEL_P1_MSK, + }}, .rpt_sel_be = R_EDCCA_RPTREG_SEL_BE, .rpt_sel_be_mask = B_EDCCA_RPTREG_SEL_BE_MSK, .tx_collision_t2r_st = R_TX_COLLISION_T2R_ST_BE, @@ -2063,7 +2071,8 @@ static void __rtw8922a_rfk_init_late(struct rtw89_dev *rtwdev, rtw89_phy_rfk_pre_ntfy_and_wait(rtwdev, phy_idx, 5); rtw89_phy_rfk_dack_and_wait(rtwdev, phy_idx, chan, 58); - rtw89_phy_rfk_rxdck_and_wait(rtwdev, phy_idx, chan, false, 32); + if (!test_bit(RTW89_FLAG_SER_HANDLING, rtwdev->flags)) + rtw89_phy_rfk_rxdck_and_wait(rtwdev, phy_idx, chan, false, 128); } static void rtw8922a_rfk_init_late(struct rtw89_dev *rtwdev) @@ -2149,6 +2158,56 @@ static void rtw8922a_set_txpwr_ref(struct rtw89_dev *rtwdev, B_BE_PWR_REF_CTRL_CCK, ref_cck); } +static const struct rtw89_reg_def rtw8922a_txpwr_ref[][3] = { + {{ .addr = R_TXAGC_REF_DBM_P0, .mask = B_TXAGC_OFDM_REF_DBM_P0}, + { .addr = R_TXAGC_REF_DBM_P0, .mask = B_TXAGC_CCK_REF_DBM_P0}, + { .addr = R_TSSI_K_P0, .mask = B_TSSI_K_OFDM_P0} + }, + {{ .addr = R_TXAGC_REF_DBM_RF1_P0, .mask = B_TXAGC_OFDM_REF_DBM_RF1_P0}, + { .addr = R_TXAGC_REF_DBM_RF1_P0, .mask = B_TXAGC_CCK_REF_DBM_RF1_P0}, + { .addr = R_TSSI_K_RF1_P0, .mask = B_TSSI_K_OFDM_RF1_P0} + }, +}; + +static void rtw8922a_set_txpwr_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + s16 pwr_ofst = rtw89_phy_ant_gain_pwr_offset(rtwdev, chan); + const struct rtw89_chip_info *chip = rtwdev->chip; + static const u32 path_ofst[] = {0x0, 0x100}; + const struct rtw89_reg_def *txpwr_ref; + static const s16 tssi_k_base = 0x12; + s16 tssi_k_ofst = abs(pwr_ofst) + tssi_k_base; + s16 ofst_dec[RF_PATH_NUM_8922A]; + s16 tssi_k[RF_PATH_NUM_8922A]; + s16 pwr_ref_ofst; + s16 pwr_ref = 0; + u8 i; + + if (rtwdev->hal.cv == CHIP_CAV) + pwr_ref = 16; + + pwr_ref <<= chip->txpwr_factor_rf; + pwr_ref_ofst = pwr_ref - rtw89_phy_txpwr_bb_to_rf(rtwdev, abs(pwr_ofst)); + + ofst_dec[RF_PATH_A] = pwr_ofst > 0 ? pwr_ref : pwr_ref_ofst; + ofst_dec[RF_PATH_B] = pwr_ofst > 0 ? pwr_ref_ofst : pwr_ref; + tssi_k[RF_PATH_A] = pwr_ofst > 0 ? tssi_k_base : tssi_k_ofst; + tssi_k[RF_PATH_B] = pwr_ofst > 0 ? tssi_k_ofst : tssi_k_base; + + for (i = 0; i < RF_PATH_NUM_8922A; i++) { + txpwr_ref = rtw8922a_txpwr_ref[phy_idx]; + + rtw89_phy_write32_mask(rtwdev, txpwr_ref[0].addr + path_ofst[i], + txpwr_ref[0].mask, ofst_dec[i]); + rtw89_phy_write32_mask(rtwdev, txpwr_ref[1].addr + path_ofst[i], + txpwr_ref[1].mask, ofst_dec[i]); + rtw89_phy_write32_mask(rtwdev, txpwr_ref[2].addr + path_ofst[i], + txpwr_ref[2].mask, tssi_k[i]); + } +} + static void rtw8922a_bb_tx_triangular(struct rtw89_dev *rtwdev, bool en, enum rtw89_phy_idx phy_idx) { @@ -2176,6 +2235,31 @@ static void rtw8922a_set_tx_shape(struct rtw89_dev *rtwdev, rtw8922a_bb_tx_triangular(rtwdev, true, phy_idx); } +static void rtw8922a_set_txpwr_sar_diff(struct rtw89_dev *rtwdev, + const struct rtw89_chan *chan, + enum rtw89_phy_idx phy_idx) +{ + struct rtw89_sar_parm sar_parm = { + .center_freq = chan->freq, + .force_path = true, + }; + s16 sar_rf; + s8 sar_mac; + + if (phy_idx != RTW89_PHY_0) + return; + + sar_parm.path = RF_PATH_A; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_P0_TXPWRB_BE, B_TXPWRB_MAX_BE, sar_rf); + + sar_parm.path = RF_PATH_B; + sar_mac = rtw89_query_sar(rtwdev, &sar_parm); + sar_rf = rtw89_phy_txpwr_mac_to_rf(rtwdev, sar_mac); + rtw89_phy_write32_mask(rtwdev, R_P1_TXPWRB_BE, B_TXPWRB_MAX_BE, sar_rf); +} + static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev, const struct rtw89_chan *chan, enum rtw89_phy_idx phy_idx) @@ -2185,6 +2269,9 @@ static void rtw8922a_set_txpwr(struct rtw89_dev *rtwdev, rtw8922a_set_tx_shape(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit(rtwdev, chan, phy_idx); rtw89_phy_set_txpwr_limit_ru(rtwdev, chan, phy_idx); + rtw8922a_set_txpwr_diff(rtwdev, chan, phy_idx); + rtw8922a_set_txpwr_ref(rtwdev, phy_idx); + rtw8922a_set_txpwr_sar_diff(rtwdev, chan, phy_idx); } static void rtw8922a_set_txpwr_ctrl(struct rtw89_dev *rtwdev, @@ -2695,6 +2782,7 @@ static const struct rtw89_chip_ops rtw8922a_chip_ops = { .h2c_default_cmac_tbl = rtw89_fw_h2c_default_cmac_tbl_g7, .h2c_assoc_cmac_tbl = rtw89_fw_h2c_assoc_cmac_tbl_g7, .h2c_ampdu_cmac_tbl = rtw89_fw_h2c_ampdu_cmac_tbl_g7, + .h2c_txtime_cmac_tbl = rtw89_fw_h2c_txtime_cmac_tbl_g7, .h2c_default_dmac_tbl = rtw89_fw_h2c_default_dmac_tbl_v2, .h2c_update_beacon = rtw89_fw_h2c_update_beacon_be, .h2c_ba_cam = rtw89_fw_h2c_ba_cam_v1, @@ -2721,6 +2809,7 @@ const struct rtw89_chip_info rtw8922a_chip_info = { .try_ce_fw = false, .bbmcu_nr = 1, .needed_fw_elms = RTW89_BE_GEN_DEF_NEEDED_FW_ELEMENTS, + .fw_blacklist = &rtw89_fw_blacklist_default, .fifo_size = 589824, .small_fifo_size = false, .dle_scc_rsvd_size = 0, @@ -2760,11 +2849,16 @@ const struct rtw89_chip_info rtw8922a_chip_info = { BIT(NL80211_CHAN_WIDTH_80) | BIT(NL80211_CHAN_WIDTH_160), .support_unii4 = true, - .support_ant_gain = false, + .support_ant_gain = true, + .support_tas = false, + .support_sar_by_ant = true, .ul_tb_waveform_ctrl = false, .ul_tb_pwr_diff = false, + .rx_freq_frome_ie = false, .hw_sec_hdr = true, .hw_mgmt_tx_encrypt = true, + .hw_tkip_crypto = true, + .hw_mlo_bmc_crypto = false, .rf_path_num = 2, .tx_nss = 2, .rx_nss = 2, diff --git a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c index c4c93f836a2f..1659ea64ade1 100644 --- a/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c +++ b/drivers/net/wireless/realtek/rtw89/rtw8922a_rfk.c @@ -77,11 +77,6 @@ void rtw8922a_ctl_band_ch_bw(struct rtw89_dev *rtwdev, enum rtw89_phy_idx phy, RR_CFGCH_BAND0 | RR_CFGCH_CH); rf_reg[path][i] |= u32_encode_bits(central_ch, RR_CFGCH_CH); - if (band == RTW89_BAND_2G) - rtw89_write_rf(rtwdev, path, RR_SMD, RR_VCO2, 0x0); - else - rtw89_write_rf(rtwdev, path, RR_SMD, RR_VCO2, 0x1); - switch (band) { case RTW89_BAND_2G: default: diff --git a/drivers/net/wireless/realtek/rtw89/sar.c b/drivers/net/wireless/realtek/rtw89/sar.c index 871f45a6508c..517b66022f18 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.c +++ b/drivers/net/wireless/realtek/rtw89/sar.c @@ -7,10 +7,16 @@ #include "phy.h" #include "reg.h" #include "sar.h" +#include "util.h" #define RTW89_TAS_FACTOR 2 /* unit: 0.25 dBm */ +#define RTW89_TAS_SAR_GAP (1 << RTW89_TAS_FACTOR) #define RTW89_TAS_DPR_GAP (1 << RTW89_TAS_FACTOR) #define RTW89_TAS_DELTA (2 << RTW89_TAS_FACTOR) +#define RTW89_TAS_TX_RATIO_THRESHOLD 70 +#define RTW89_TAS_DFLT_TX_RATIO 80 +#define RTW89_TAS_DPR_ON_OFFSET (RTW89_TAS_DELTA + RTW89_TAS_SAR_GAP) +#define RTW89_TAS_DPR_OFF_OFFSET (4 << RTW89_TAS_FACTOR) static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev, u32 center_freq) @@ -51,10 +57,12 @@ static enum rtw89_sar_subband rtw89_sar_get_subband(struct rtw89_dev *rtwdev, } static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, - u32 center_freq, s32 *cfg) + const struct rtw89_sar_parm *sar_parm, + s32 *cfg) { struct rtw89_sar_cfg_common *rtwsar = &rtwdev->sar.cfg_common; enum rtw89_sar_subband subband_l, subband_h; + u32 center_freq = sar_parm->center_freq; const struct rtw89_6ghz_span *span; span = rtw89_get_6ghz_span(rtwdev, center_freq); @@ -84,6 +92,93 @@ static int rtw89_query_sar_config_common(struct rtw89_dev *rtwdev, return 0; } +static const struct rtw89_sar_entry_from_acpi * +rtw89_sar_cfg_acpi_get_ent(const struct rtw89_sar_cfg_acpi *rtwsar, + enum rtw89_rf_path path, + enum rtw89_regulation_type regd) +{ + const struct rtw89_sar_indicator_from_acpi *ind = &rtwsar->indicator; + const struct rtw89_sar_table_from_acpi *tbl; + u8 sel; + + sel = ind->tblsel[path]; + tbl = &rtwsar->tables[sel]; + + return &tbl->entries[regd]; +} + +static +s32 rtw89_sar_cfg_acpi_get_min(const struct rtw89_sar_entry_from_acpi *ent, + enum rtw89_rf_path path, + enum rtw89_acpi_sar_subband subband_low, + enum rtw89_acpi_sar_subband subband_high) +{ + return min(ent->v[subband_low][path], ent->v[subband_high][path]); +} + +static int rtw89_query_sar_config_acpi(struct rtw89_dev *rtwdev, + const struct rtw89_sar_parm *sar_parm, + s32 *cfg) +{ + const struct rtw89_chip_info *chip = rtwdev->chip; + const struct rtw89_sar_cfg_acpi *rtwsar = &rtwdev->sar.cfg_acpi; + const struct rtw89_sar_entry_from_acpi *ent_a, *ent_b; + enum rtw89_acpi_sar_subband subband_l, subband_h; + u32 center_freq = sar_parm->center_freq; + const struct rtw89_6ghz_span *span; + enum rtw89_regulation_type regd; + enum rtw89_band band; + s32 cfg_a, cfg_b; + + span = rtw89_get_6ghz_span(rtwdev, center_freq); + + if (span && RTW89_ACPI_SAR_SPAN_VALID(span)) { + subband_l = span->acpi_sar_subband_low; + subband_h = span->acpi_sar_subband_high; + } else { + subband_l = rtw89_acpi_sar_get_subband(rtwdev, center_freq); + subband_h = subband_l; + } + + band = rtw89_acpi_sar_subband_to_band(rtwdev, subband_l); + regd = rtw89_regd_get(rtwdev, band); + + ent_a = rtw89_sar_cfg_acpi_get_ent(rtwsar, RF_PATH_A, regd); + ent_b = rtw89_sar_cfg_acpi_get_ent(rtwsar, RF_PATH_B, regd); + + cfg_a = rtw89_sar_cfg_acpi_get_min(ent_a, RF_PATH_A, subband_l, subband_h); + cfg_b = rtw89_sar_cfg_acpi_get_min(ent_b, RF_PATH_B, subband_l, subband_h); + + if (chip->support_sar_by_ant) { + /* With declaration of support_sar_by_ant, relax the general + * SAR querying to return the maximum between paths. However, + * expect chip has dealt with the corresponding SAR settings + * by path. (To get SAR for a given path, chip can then query + * with force_path.) + */ + if (sar_parm->force_path) { + switch (sar_parm->path) { + default: + case RF_PATH_A: + *cfg = cfg_a; + break; + case RF_PATH_B: + *cfg = cfg_b; + break; + } + } else { + *cfg = max(cfg_a, cfg_b); + } + } else { + *cfg = min(cfg_a, cfg_b); + } + + if (sar_parm->ntx == RTW89_2TX) + *cfg -= rtwsar->downgrade_2tx; + + return 0; +} + static const struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { [RTW89_SAR_SOURCE_COMMON] = { @@ -91,6 +186,11 @@ struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { .txpwr_factor_sar = 2, .query_sar_config = rtw89_query_sar_config_common, }, + [RTW89_SAR_SOURCE_ACPI] = { + .descr_sar_source = "RTW89_SAR_SOURCE_ACPI", + .txpwr_factor_sar = TXPWR_FACTOR_OF_RTW89_ACPI_SAR, + .query_sar_config = rtw89_query_sar_config_acpi, + }, }; #define rtw89_sar_set_src(_dev, _src, _cfg_name, _cfg_data) \ @@ -99,7 +199,7 @@ struct rtw89_sar_handler rtw89_sar_handlers[RTW89_SAR_SOURCE_NR] = { typeof(_dev) _d = (_dev); \ BUILD_BUG_ON(!rtw89_sar_handlers[_s].descr_sar_source); \ BUILD_BUG_ON(!rtw89_sar_handlers[_s].query_sar_config); \ - lockdep_assert_held(&_d->mutex); \ + lockdep_assert_wiphy(_d->hw->wiphy); \ _d->sar._cfg_name = *(_cfg_data); \ _d->sar.src = _s; \ } while (0) @@ -117,8 +217,8 @@ static s8 rtw89_txpwr_sar_to_mac(struct rtw89_dev *rtwdev, u8 fct, s32 cfg) RTW89_SAR_TXPWR_MAC_MAX); } -static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, - s8 cfg) +static s32 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, + s32 cfg) { const u8 fct = sar_hdl->txpwr_factor_sar; @@ -128,8 +228,8 @@ static s8 rtw89_txpwr_tas_to_sar(const struct rtw89_sar_handler *sar_hdl, return cfg >> (RTW89_TAS_FACTOR - fct); } -static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, - s8 cfg) +static s32 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, + s32 cfg) { const u8 fct = sar_hdl->txpwr_factor_sar; @@ -139,35 +239,67 @@ static s8 rtw89_txpwr_sar_to_tas(const struct rtw89_sar_handler *sar_hdl, return cfg << (RTW89_TAS_FACTOR - fct); } -s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) +static bool rtw89_tas_is_active(struct rtw89_dev *rtwdev) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + struct rtw89_vif *rtwvif; + + if (!tas->enable) + return false; + + rtw89_for_each_rtwvif(rtwdev, rtwvif) { + if (ieee80211_vif_is_mld(rtwvif_to_vif(rtwvif))) + return false; + } + + return true; +} + +static const char *rtw89_tas_state_str(enum rtw89_tas_state state) +{ + switch (state) { + case RTW89_TAS_STATE_DPR_OFF: + return "DPR OFF"; + case RTW89_TAS_STATE_DPR_ON: + return "DPR ON"; + case RTW89_TAS_STATE_STATIC_SAR: + return "STATIC SAR"; + default: + return NULL; + } +} + +s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm) { const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; struct rtw89_tas_info *tas = &rtwdev->tas; - s8 delta; + s32 offset; int ret; s32 cfg; u8 fct; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (src == RTW89_SAR_SOURCE_NONE) return RTW89_SAR_TXPWR_MAC_MAX; - ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); + ret = sar_hdl->query_sar_config(rtwdev, sar_parm, &cfg); if (ret) return RTW89_SAR_TXPWR_MAC_MAX; - if (tas->enable) { + if (rtw89_tas_is_active(rtwdev)) { switch (tas->state) { case RTW89_TAS_STATE_DPR_OFF: - return RTW89_SAR_TXPWR_MAC_MAX; + offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_OFF_OFFSET); + cfg += offset; + break; case RTW89_TAS_STATE_DPR_ON: - delta = rtw89_txpwr_tas_to_sar(sar_hdl, tas->delta); - cfg -= delta; + offset = rtw89_txpwr_tas_to_sar(sar_hdl, RTW89_TAS_DPR_ON_OFFSET); + cfg -= offset; break; - case RTW89_TAS_STATE_DPR_FORBID: + case RTW89_TAS_STATE_STATIC_SAR: default: break; } @@ -177,73 +309,86 @@ s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq) return rtw89_txpwr_sar_to_mac(rtwdev, fct, cfg); } +EXPORT_SYMBOL(rtw89_query_sar); -void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq) +int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct rtw89_sar_parm *sar_parm) { const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; const u8 fct_mac = rtwdev->chip->txpwr_factor_mac; + char *p = buf, *end = buf + bufsz; int ret; s32 cfg; u8 fct; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); if (src == RTW89_SAR_SOURCE_NONE) { - seq_puts(m, "no SAR is applied\n"); - return; + p += scnprintf(p, end - p, "no SAR is applied\n"); + goto out; } - seq_printf(m, "source: %d (%s)\n", src, sar_hdl->descr_sar_source); + p += scnprintf(p, end - p, "source: %d (%s)\n", src, + sar_hdl->descr_sar_source); - ret = sar_hdl->query_sar_config(rtwdev, center_freq, &cfg); + ret = sar_hdl->query_sar_config(rtwdev, sar_parm, &cfg); if (ret) { - seq_printf(m, "config: return code: %d\n", ret); - seq_printf(m, "assign: max setting: %d (unit: 1/%lu dBm)\n", - RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); - return; + p += scnprintf(p, end - p, "config: return code: %d\n", ret); + p += scnprintf(p, end - p, + "assign: max setting: %d (unit: 1/%lu dBm)\n", + RTW89_SAR_TXPWR_MAC_MAX, BIT(fct_mac)); + goto out; } fct = sar_hdl->txpwr_factor_sar; - seq_printf(m, "config: %d (unit: 1/%lu dBm)\n", cfg, BIT(fct)); + p += scnprintf(p, end - p, "config: %d (unit: 1/%lu dBm)\n", cfg, + BIT(fct)); + + p += scnprintf(p, end - p, "support different configs by antenna: %s\n", + str_yes_no(rtwdev->chip->support_sar_by_ant)); +out: + return p - buf; } -void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev) +int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz) { struct rtw89_tas_info *tas = &rtwdev->tas; + char *p = buf, *end = buf + bufsz; - if (!tas->enable) { - seq_puts(m, "no TAS is applied\n"); - return; + if (!rtw89_tas_is_active(rtwdev)) { + p += scnprintf(p, end - p, "no TAS is applied\n"); + goto out; } - seq_printf(m, "DPR gap: %d\n", tas->dpr_gap); - seq_printf(m, "TAS delta: %d\n", tas->delta); + p += scnprintf(p, end - p, "State: %s\n", + rtw89_tas_state_str(tas->state)); + p += scnprintf(p, end - p, "Average time: %d\n", + tas->window_size * 2); + p += scnprintf(p, end - p, "SAR gap: %d dBm\n", + RTW89_TAS_SAR_GAP >> RTW89_TAS_FACTOR); + p += scnprintf(p, end - p, "DPR gap: %d dBm\n", + RTW89_TAS_DPR_GAP >> RTW89_TAS_FACTOR); + p += scnprintf(p, end - p, "DPR ON offset: %d dBm\n", + RTW89_TAS_DPR_ON_OFFSET >> RTW89_TAS_FACTOR); + p += scnprintf(p, end - p, "DPR OFF offset: %d dBm\n", + RTW89_TAS_DPR_OFF_OFFSET >> RTW89_TAS_FACTOR); + +out: + return p - buf; } static int rtw89_apply_sar_common(struct rtw89_dev *rtwdev, const struct rtw89_sar_cfg_common *sar) { - enum rtw89_sar_sources src; - int ret = 0; - - mutex_lock(&rtwdev->mutex); - - src = rtwdev->sar.src; - if (src != RTW89_SAR_SOURCE_NONE && src != RTW89_SAR_SOURCE_COMMON) { - rtw89_warn(rtwdev, "SAR source: %d is in use", src); - ret = -EBUSY; - goto exit; - } - + /* let common SAR have the highest priority; always apply it */ rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_COMMON, cfg_common, sar); rtw89_core_set_chip_txpwr(rtwdev); + rtw89_tas_reset(rtwdev, false); -exit: - mutex_unlock(&rtwdev->mutex); - return ret; + return 0; } static const struct cfg80211_sar_freq_ranges rtw89_common_sar_freq_ranges[] = { @@ -279,6 +424,8 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, s32 power; u32 i, idx; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + if (sar->type != NL80211_SAR_TYPE_POWER) return -EINVAL; @@ -304,64 +451,256 @@ int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, return rtw89_apply_sar_common(rtwdev, &sar_common); } -static void rtw89_tas_state_update(struct rtw89_dev *rtwdev) +static void rtw89_apply_sar_acpi(struct rtw89_dev *rtwdev, + const struct rtw89_sar_cfg_acpi *sar) { + const struct rtw89_sar_table_from_acpi *tbl; + const struct rtw89_sar_entry_from_acpi *ent; + enum rtw89_sar_sources src; + unsigned int i, j, k; + + src = rtwdev->sar.src; + if (src != RTW89_SAR_SOURCE_NONE) { + rtw89_warn(rtwdev, "SAR source: %d is in use", src); + return; + } + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "SAR-ACPI downgrade 2TX: %u (unit: 1/%lu dBm)\n", + sar->downgrade_2tx, BIT(TXPWR_FACTOR_OF_RTW89_ACPI_SAR)); + + for (i = 0; i < sar->valid_num; i++) { + tbl = &sar->tables[i]; + + for (j = 0; j < RTW89_REGD_NUM; j++) { + ent = &tbl->entries[j]; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "SAR-ACPI-[%u] REGD-%s (unit: 1/%lu dBm)\n", + i, rtw89_regd_get_string(j), + BIT(TXPWR_FACTOR_OF_RTW89_ACPI_SAR)); + + for (k = 0; k < NUM_OF_RTW89_ACPI_SAR_SUBBAND; k++) + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "On subband %u, { %d, %d }\n", k, + ent->v[k][RF_PATH_A], ent->v[k][RF_PATH_B]); + } + } + + rtw89_sar_set_src(rtwdev, RTW89_SAR_SOURCE_ACPI, cfg_acpi, sar); + + /* SAR via ACPI is only configured in the early initial phase, so + * it does not seem necessary to reset txpwr related things here. + */ +} + +static void rtw89_set_sar_from_acpi(struct rtw89_dev *rtwdev) +{ + struct rtw89_sar_cfg_acpi *cfg; + int ret; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return; + + ret = rtw89_acpi_evaluate_sar(rtwdev, cfg); + if (ret) { + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "evaluating ACPI SAR returns %d\n", ret); + goto out; + } + + if (unlikely(!cfg->valid_num)) { + rtw89_debug(rtwdev, RTW89_DBG_SAR, "no valid SAR table from ACPI\n"); + goto out; + } + + rtw89_apply_sar_acpi(rtwdev, cfg); + +out: + kfree(cfg); +} + +static bool rtw89_tas_query_sar_config(struct rtw89_dev *rtwdev, s32 *cfg) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); const enum rtw89_sar_sources src = rtwdev->sar.src; /* its members are protected by rtw89_sar_set_src() */ const struct rtw89_sar_handler *sar_hdl = &rtw89_sar_handlers[src]; - struct rtw89_tas_info *tas = &rtwdev->tas; - s32 txpwr_avg = tas->total_txpwr / RTW89_TAS_MAX_WINDOW / PERCENT; - s32 dpr_on_threshold, dpr_off_threshold, cfg; - enum rtw89_tas_state state = tas->state; - const struct rtw89_chan *chan; + struct rtw89_sar_parm sar_parm = {}; int ret; - lockdep_assert_held(&rtwdev->mutex); - if (src == RTW89_SAR_SOURCE_NONE) - return; + return false; - chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); - ret = sar_hdl->query_sar_config(rtwdev, chan->freq, &cfg); + sar_parm.center_freq = chan->freq; + ret = sar_hdl->query_sar_config(rtwdev, &sar_parm, cfg); if (ret) + return false; + + *cfg = rtw89_txpwr_sar_to_tas(sar_hdl, *cfg); + + return true; +} + +static bool __rtw89_tas_state_update(struct rtw89_dev *rtwdev, + enum rtw89_tas_state state) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + + if (tas->state == state) + return false; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: switch state: %s -> %s\n", + rtw89_tas_state_str(tas->state), rtw89_tas_state_str(state)); + + tas->state = state; + return true; +} + +static void rtw89_tas_state_update(struct rtw89_dev *rtwdev, + enum rtw89_tas_state state) +{ + if (!__rtw89_tas_state_update(rtwdev, state)) return; - cfg = rtw89_txpwr_sar_to_tas(sar_hdl, cfg); + rtw89_core_set_chip_txpwr(rtwdev); +} + +static u32 rtw89_tas_get_window_size(struct rtw89_dev *rtwdev) +{ + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); + u8 band = chan->band_type; + u8 regd = rtw89_regd_get(rtwdev, band); - if (tas->delta >= cfg) { + switch (regd) { + default: rtw89_debug(rtwdev, RTW89_DBG_SAR, - "TAS delta exceed SAR limit\n"); - state = RTW89_TAS_STATE_DPR_FORBID; - goto out; + "tas: regd: %u is unhandled\n", regd); + fallthrough; + case RTW89_IC: + case RTW89_KCC: + return 180; + case RTW89_FCC: + switch (band) { + case RTW89_BAND_2G: + return 50; + case RTW89_BAND_5G: + return 30; + case RTW89_BAND_6G: + default: + return 15; + } + break; + } +} + +static void rtw89_tas_window_update(struct rtw89_dev *rtwdev) +{ + u32 window_size = rtw89_tas_get_window_size(rtwdev); + struct rtw89_tas_info *tas = &rtwdev->tas; + u64 total_txpwr = 0; + u8 head_idx; + u32 i, j; + + WARN_ON_ONCE(tas->window_size > RTW89_TAS_TXPWR_WINDOW); + + if (tas->window_size == window_size) + return; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, "tas: window update: %u -> %u\n", + tas->window_size, window_size); + + head_idx = (tas->txpwr_tail_idx - window_size + 1 + RTW89_TAS_TXPWR_WINDOW) % + RTW89_TAS_TXPWR_WINDOW; + for (i = 0; i < window_size; i++) { + j = (head_idx + i) % RTW89_TAS_TXPWR_WINDOW; + total_txpwr += tas->txpwr_history[j]; + } + + tas->window_size = window_size; + tas->total_txpwr = total_txpwr; + tas->txpwr_head_idx = head_idx; +} + +static void rtw89_tas_history_update(struct rtw89_dev *rtwdev) +{ + struct rtw89_bb_ctx *bb = rtw89_get_bb_ctx(rtwdev, RTW89_PHY_0); + struct rtw89_env_monitor_info *env = &bb->env_monitor; + struct rtw89_tas_info *tas = &rtwdev->tas; + u8 tx_ratio = env->ifs_clm_tx_ratio; + u64 instant_txpwr, txpwr; + + /* txpwr in unit of linear(mW) multiply by percentage */ + if (tx_ratio == 0) { + /* special case: idle tx power + * use -40 dBm * 100 tx ratio + */ + instant_txpwr = rtw89_db_to_linear(-40); + txpwr = instant_txpwr * 100; + } else { + instant_txpwr = tas->instant_txpwr; + txpwr = instant_txpwr * tx_ratio; } - dpr_on_threshold = cfg; - dpr_off_threshold = cfg - tas->dpr_gap; + tas->total_txpwr += txpwr - tas->txpwr_history[tas->txpwr_head_idx]; + tas->total_tx_ratio += tx_ratio - tas->tx_ratio_history[tas->tx_ratio_idx]; + tas->tx_ratio_history[tas->tx_ratio_idx] = tx_ratio; + + tas->txpwr_head_idx = (tas->txpwr_head_idx + 1) % RTW89_TAS_TXPWR_WINDOW; + tas->txpwr_tail_idx = (tas->txpwr_tail_idx + 1) % RTW89_TAS_TXPWR_WINDOW; + tas->tx_ratio_idx = (tas->tx_ratio_idx + 1) % RTW89_TAS_TX_RATIO_WINDOW; + tas->txpwr_history[tas->txpwr_tail_idx] = txpwr; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "tas: instant_txpwr: %d, tx_ratio: %u, txpwr: %d\n", + rtw89_linear_to_db_quarter(instant_txpwr), tx_ratio, + rtw89_linear_to_db_quarter(div_u64(txpwr, PERCENT))); +} + +static bool rtw89_tas_rolling_average(struct rtw89_dev *rtwdev) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + s32 dpr_on_threshold, dpr_off_threshold; + enum rtw89_tas_state state; + u16 tx_ratio_avg; + s32 txpwr_avg; + u64 linear; + + linear = DIV_ROUND_DOWN_ULL(tas->total_txpwr, tas->window_size * PERCENT); + txpwr_avg = rtw89_linear_to_db_quarter(linear); + tx_ratio_avg = tas->total_tx_ratio / RTW89_TAS_TX_RATIO_WINDOW; + dpr_on_threshold = tas->dpr_on_threshold; + dpr_off_threshold = tas->dpr_off_threshold; + rtw89_debug(rtwdev, RTW89_DBG_SAR, - "DPR_ON thold: %d, DPR_OFF thold: %d, txpwr_avg: %d\n", - dpr_on_threshold, dpr_off_threshold, txpwr_avg); + "tas: DPR_ON: %d, DPR_OFF: %d, txpwr_avg: %d, tx_ratio_avg: %u\n", + dpr_on_threshold, dpr_off_threshold, txpwr_avg, tx_ratio_avg); - if (txpwr_avg >= dpr_on_threshold) + if (tx_ratio_avg >= RTW89_TAS_TX_RATIO_THRESHOLD) + state = RTW89_TAS_STATE_STATIC_SAR; + else if (txpwr_avg >= dpr_on_threshold) state = RTW89_TAS_STATE_DPR_ON; else if (txpwr_avg < dpr_off_threshold) state = RTW89_TAS_STATE_DPR_OFF; + else + return false; -out: - if (tas->state == state) - return; - - rtw89_debug(rtwdev, RTW89_DBG_SAR, - "TAS old state: %d, new state: %d\n", tas->state, state); - tas->state = state; - rtw89_core_set_chip_txpwr(rtwdev); + return __rtw89_tas_state_update(rtwdev, state); } -void rtw89_tas_init(struct rtw89_dev *rtwdev) +static void rtw89_tas_init(struct rtw89_dev *rtwdev) { + const struct rtw89_chip_info *chip = rtwdev->chip; struct rtw89_tas_info *tas = &rtwdev->tas; + const struct rtw89_acpi_policy_tas *ptr; struct rtw89_acpi_dsm_result res = {}; int ret; - u8 val; + + if (!chip->support_tas) + return; ret = rtw89_acpi_evaluate_dsm(rtwdev, RTW89_ACPI_DSM_FUNC_TAS_EN, &res); if (ret) { @@ -370,8 +709,9 @@ void rtw89_tas_init(struct rtw89_dev *rtwdev) return; } - val = res.u.value; - switch (val) { + ptr = res.u.policy_tas; + + switch (ptr->enable) { case 0: tas->enable = false; break; @@ -384,66 +724,170 @@ void rtw89_tas_init(struct rtw89_dev *rtwdev) if (!tas->enable) { rtw89_debug(rtwdev, RTW89_DBG_SAR, "TAS not enable\n"); - return; + goto out; } - tas->dpr_gap = RTW89_TAS_DPR_GAP; - tas->delta = RTW89_TAS_DELTA; + tas->enabled_countries = ptr->enabled_countries; + +out: + kfree(ptr); } -void rtw89_tas_reset(struct rtw89_dev *rtwdev) +void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force) { + const struct rtw89_chan *chan = rtw89_chan_get(rtwdev, RTW89_CHANCTX_0); struct rtw89_tas_info *tas = &rtwdev->tas; + u64 linear; + s32 cfg; + int i; - if (!tas->enable) + if (!rtw89_tas_is_active(rtwdev)) + return; + + if (!rtw89_tas_query_sar_config(rtwdev, &cfg)) return; - memset(&tas->txpwr_history, 0, sizeof(tas->txpwr_history)); - tas->total_txpwr = 0; - tas->cur_idx = 0; + tas->dpr_on_threshold = cfg - RTW89_TAS_SAR_GAP; + tas->dpr_off_threshold = cfg - RTW89_TAS_SAR_GAP - RTW89_TAS_DPR_GAP; + + /* avoid history reset after new SAR apply */ + if (!force && tas->keep_history) + return; + + linear = rtw89_db_quarter_to_linear(cfg) * RTW89_TAS_DFLT_TX_RATIO; + for (i = 0; i < RTW89_TAS_TXPWR_WINDOW; i++) + tas->txpwr_history[i] = linear; + + for (i = 0; i < RTW89_TAS_TX_RATIO_WINDOW; i++) + tas->tx_ratio_history[i] = RTW89_TAS_DFLT_TX_RATIO; + + tas->total_tx_ratio = RTW89_TAS_DFLT_TX_RATIO * RTW89_TAS_TX_RATIO_WINDOW; + tas->total_txpwr = linear * RTW89_TAS_TXPWR_WINDOW; + tas->window_size = RTW89_TAS_TXPWR_WINDOW; + tas->txpwr_head_idx = 0; + tas->txpwr_tail_idx = RTW89_TAS_TXPWR_WINDOW - 1; + tas->tx_ratio_idx = 0; tas->state = RTW89_TAS_STATE_DPR_OFF; + tas->backup_state = RTW89_TAS_STATE_DPR_OFF; + tas->keep_history = true; + + rtw89_debug(rtwdev, RTW89_DBG_SAR, + "tas: band: %u, freq: %u\n", chan->band_type, chan->freq); } -static const struct rtw89_reg_def txpwr_regs[] = { - {R_PATH0_TXPWR, B_PATH0_TXPWR}, - {R_PATH1_TXPWR, B_PATH1_TXPWR}, -}; +static bool rtw89_tas_track(struct rtw89_dev *rtwdev) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + struct rtw89_hal *hal = &rtwdev->hal; + s32 cfg; + + if (hal->disabled_dm_bitmap & BIT(RTW89_DM_TAS)) + return false; + + if (!rtw89_tas_is_active(rtwdev)) + return false; + + if (!rtw89_tas_query_sar_config(rtwdev, &cfg) || tas->block_regd) + return __rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); -void rtw89_tas_track(struct rtw89_dev *rtwdev) + if (tas->pause) + return false; + + rtw89_tas_window_update(rtwdev); + rtw89_tas_history_update(rtwdev); + + return rtw89_tas_rolling_average(rtwdev); +} + +void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start) { - struct rtw89_env_monitor_info *env = &rtwdev->env_monitor; - const enum rtw89_sar_sources src = rtwdev->sar.src; - u8 max_nss_num = rtwdev->chip->rf_path_num; struct rtw89_tas_info *tas = &rtwdev->tas; - s16 tmp, txpwr, instant_txpwr = 0; - u32 val; - int i; + s32 cfg; + + if (!rtw89_tas_is_active(rtwdev)) + return; + + if (!rtw89_tas_query_sar_config(rtwdev, &cfg)) + return; + + if (start) { + tas->backup_state = tas->state; + rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); + } else { + rtw89_tas_state_update(rtwdev, tas->backup_state); + } +} - if (!tas->enable || src == RTW89_SAR_SOURCE_NONE) +void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev, + enum rtw89_chanctx_state state) +{ + struct rtw89_tas_info *tas = &rtwdev->tas; + s32 cfg; + + if (!rtw89_tas_is_active(rtwdev)) return; - if (env->ccx_watchdog_result != RTW89_PHY_ENV_MON_IFS_CLM) + if (!rtw89_tas_query_sar_config(rtwdev, &cfg)) return; - for (i = 0; i < max_nss_num; i++) { - val = rtw89_phy_read32_mask(rtwdev, txpwr_regs[i].addr, - txpwr_regs[i].mask); - tmp = sign_extend32(val, 8); - if (tmp <= 0) - return; - instant_txpwr += tmp; + switch (state) { + case RTW89_CHANCTX_STATE_MCC_START: + tas->pause = true; + rtw89_tas_state_update(rtwdev, RTW89_TAS_STATE_STATIC_SAR); + break; + case RTW89_CHANCTX_STATE_MCC_STOP: + tas->pause = false; + break; + default: + break; } +} +EXPORT_SYMBOL(rtw89_tas_chanctx_cb); + +void rtw89_sar_init(struct rtw89_dev *rtwdev) +{ + rtw89_set_sar_from_acpi(rtwdev); + rtw89_tas_init(rtwdev); +} + +static bool rtw89_sar_track_acpi(struct rtw89_dev *rtwdev) +{ + struct rtw89_sar_cfg_acpi *cfg = &rtwdev->sar.cfg_acpi; + struct rtw89_sar_indicator_from_acpi *ind = &cfg->indicator; + const enum rtw89_sar_sources src = rtwdev->sar.src; + bool changed; + int ret; + + lockdep_assert_wiphy(rtwdev->hw->wiphy); + + if (src != RTW89_SAR_SOURCE_ACPI) + return false; + + if (!ind->enable_sync) + return false; + + ret = rtw89_acpi_evaluate_dynamic_sar_indicator(rtwdev, cfg, &changed); + if (likely(!ret)) + return changed; - instant_txpwr /= max_nss_num; - /* in unit of 0.25 dBm multiply by percentage */ - txpwr = instant_txpwr * env->ifs_clm_tx_ratio; - tas->total_txpwr += txpwr - tas->txpwr_history[tas->cur_idx]; - tas->txpwr_history[tas->cur_idx] = txpwr; rtw89_debug(rtwdev, RTW89_DBG_SAR, - "instant_txpwr: %d, tx_ratio: %d, txpwr: %d\n", - instant_txpwr, env->ifs_clm_tx_ratio, txpwr); + "%s: failed to track indicator: %d; reset and disable\n", + __func__, ret); - tas->cur_idx = (tas->cur_idx + 1) % RTW89_TAS_MAX_WINDOW; + memset(ind->tblsel, 0, sizeof(ind->tblsel)); + ind->enable_sync = false; + return true; +} + +void rtw89_sar_track(struct rtw89_dev *rtwdev) +{ + unsigned int changes = 0; + + changes += rtw89_sar_track_acpi(rtwdev); + changes += rtw89_tas_track(rtwdev); - rtw89_tas_state_update(rtwdev); + if (!changes) + return; + + rtw89_core_set_chip_txpwr(rtwdev); } diff --git a/drivers/net/wireless/realtek/rtw89/sar.h b/drivers/net/wireless/realtek/rtw89/sar.h index 4ae081d2d3b4..4b7f3d44f57b 100644 --- a/drivers/net/wireless/realtek/rtw89/sar.h +++ b/drivers/net/wireless/realtek/rtw89/sar.h @@ -10,21 +10,34 @@ #define RTW89_SAR_TXPWR_MAC_MAX 63 #define RTW89_SAR_TXPWR_MAC_MIN -64 +struct rtw89_sar_parm { + u32 center_freq; + enum rtw89_ntx ntx; + + bool force_path; + enum rtw89_rf_path path; +}; + struct rtw89_sar_handler { const char *descr_sar_source; u8 txpwr_factor_sar; - int (*query_sar_config)(struct rtw89_dev *rtwdev, u32 center_freq, s32 *cfg); + int (*query_sar_config)(struct rtw89_dev *rtwdev, + const struct rtw89_sar_parm *sar_parm, s32 *cfg); }; extern const struct cfg80211_sar_capa rtw89_sar_capa; -s8 rtw89_query_sar(struct rtw89_dev *rtwdev, u32 center_freq); -void rtw89_print_sar(struct seq_file *m, struct rtw89_dev *rtwdev, u32 center_freq); -void rtw89_print_tas(struct seq_file *m, struct rtw89_dev *rtwdev); +s8 rtw89_query_sar(struct rtw89_dev *rtwdev, const struct rtw89_sar_parm *sar_parm); +int rtw89_print_sar(struct rtw89_dev *rtwdev, char *buf, size_t bufsz, + const struct rtw89_sar_parm *sar_parm); +int rtw89_print_tas(struct rtw89_dev *rtwdev, char *buf, size_t bufsz); int rtw89_ops_set_sar_specs(struct ieee80211_hw *hw, const struct cfg80211_sar_specs *sar); -void rtw89_tas_init(struct rtw89_dev *rtwdev); -void rtw89_tas_reset(struct rtw89_dev *rtwdev); -void rtw89_tas_track(struct rtw89_dev *rtwdev); +void rtw89_tas_reset(struct rtw89_dev *rtwdev, bool force); +void rtw89_tas_scan(struct rtw89_dev *rtwdev, bool start); +void rtw89_tas_chanctx_cb(struct rtw89_dev *rtwdev, + enum rtw89_chanctx_state state); +void rtw89_sar_init(struct rtw89_dev *rtwdev); +void rtw89_sar_track(struct rtw89_dev *rtwdev); #endif diff --git a/drivers/net/wireless/realtek/rtw89/ser.c b/drivers/net/wireless/realtek/rtw89/ser.c index 26a944d3b672..811c91481441 100644 --- a/drivers/net/wireless/realtek/rtw89/ser.c +++ b/drivers/net/wireless/realtek/rtw89/ser.c @@ -156,9 +156,9 @@ static void ser_state_run(struct rtw89_ser *ser, u8 evt) rtw89_debug(rtwdev, RTW89_DBG_SER, "ser: %s receive %s\n", ser_st_name(ser), ser_ev_name(ser, evt)); - mutex_lock(&rtwdev->mutex); + wiphy_lock(rtwdev->hw->wiphy); rtw89_leave_lps(rtwdev); - mutex_unlock(&rtwdev->mutex); + wiphy_unlock(rtwdev->hw->wiphy); ser->st_tbl[ser->state].st_func(ser, evt); } @@ -309,6 +309,9 @@ static void ser_reset_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif) rtw89_core_release_bit_map(rtwdev->hw_port, rtwvif_link->port); rtwvif_link->net_type = RTW89_NET_TYPE_NO_LINK; rtwvif_link->trigger = false; + rtwvif_link->rand_tsf_done = false; + + rtw89_p2p_noa_once_deinit(rtwvif_link); } } @@ -483,10 +486,13 @@ static void ser_l1_reset_pre_st_hdl(struct rtw89_ser *ser, u8 evt) static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) { struct rtw89_dev *rtwdev = container_of(ser, struct rtw89_dev, ser); + struct wiphy *wiphy = rtwdev->hw->wiphy; switch (evt) { case SER_EV_STATE_IN: - cancel_delayed_work_sync(&rtwdev->track_work); + wiphy_lock(wiphy); + wiphy_delayed_work_cancel(wiphy, &rtwdev->track_work); + wiphy_unlock(wiphy); drv_stop_tx(ser); if (hal_stop_dma(ser)) { @@ -517,8 +523,8 @@ static void ser_reset_trx_st_hdl(struct rtw89_ser *ser, u8 evt) hal_enable_dma(ser); drv_resume_rx(ser); drv_resume_tx(ser); - ieee80211_queue_delayed_work(rtwdev->hw, &rtwdev->track_work, - RTW89_TRACK_WORK_PERIOD); + wiphy_delayed_work_queue(wiphy, &rtwdev->track_work, + RTW89_TRACK_WORK_PERIOD); break; default: @@ -708,9 +714,9 @@ static void ser_l2_reset_st_hdl(struct rtw89_ser *ser, u8 evt) switch (evt) { case SER_EV_STATE_IN: - mutex_lock(&rtwdev->mutex); + wiphy_lock(rtwdev->hw->wiphy); ser_l2_reset_st_pre_hdl(ser); - mutex_unlock(&rtwdev->mutex); + wiphy_unlock(rtwdev->hw->wiphy); ieee80211_restart_hw(rtwdev->hw); ser_set_alarm(ser, SER_RECFG_TIMEOUT, SER_EV_L2_RECFG_TIMEOUT); diff --git a/drivers/net/wireless/realtek/rtw89/txrx.h b/drivers/net/wireless/realtek/rtw89/txrx.h index 70fe7cebc9d5..94f27a9ee9f7 100644 --- a/drivers/net/wireless/realtek/rtw89/txrx.h +++ b/drivers/net/wireless/realtek/rtw89/txrx.h @@ -712,6 +712,25 @@ static inline u8 rtw89_core_get_qsel(struct rtw89_dev *rtwdev, u8 tid) } } +static inline u8 +rtw89_core_get_qsel_mgmt(struct rtw89_dev *rtwdev, struct rtw89_core_tx_request *tx_req) +{ + struct rtw89_tx_desc_info *desc_info = &tx_req->desc_info; + struct rtw89_vif_link *rtwvif_link = tx_req->rtwvif_link; + + if (desc_info->hiq) { + if (rtwvif_link->mac_idx == RTW89_MAC_1) + return RTW89_TX_QSEL_B1_HI; + else + return RTW89_TX_QSEL_B0_HI; + } + + if (rtwvif_link->mac_idx == RTW89_MAC_1) + return RTW89_TX_QSEL_B1_MGMT; + else + return RTW89_TX_QSEL_B0_MGMT; +} + static inline u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel) { switch (qsel) { @@ -719,12 +738,24 @@ static inline u8 rtw89_core_get_ch_dma(struct rtw89_dev *rtwdev, u8 qsel) rtw89_warn(rtwdev, "Cannot map qsel to dma: %d\n", qsel); fallthrough; case RTW89_TX_QSEL_BE_0: + case RTW89_TX_QSEL_BE_1: + case RTW89_TX_QSEL_BE_2: + case RTW89_TX_QSEL_BE_3: return RTW89_TXCH_ACH0; case RTW89_TX_QSEL_BK_0: + case RTW89_TX_QSEL_BK_1: + case RTW89_TX_QSEL_BK_2: + case RTW89_TX_QSEL_BK_3: return RTW89_TXCH_ACH1; case RTW89_TX_QSEL_VI_0: + case RTW89_TX_QSEL_VI_1: + case RTW89_TX_QSEL_VI_2: + case RTW89_TX_QSEL_VI_3: return RTW89_TXCH_ACH2; case RTW89_TX_QSEL_VO_0: + case RTW89_TX_QSEL_VO_1: + case RTW89_TX_QSEL_VO_2: + case RTW89_TX_QSEL_VO_3: return RTW89_TXCH_ACH3; case RTW89_TX_QSEL_B0_MGMT: return RTW89_TXCH_CH8; diff --git a/drivers/net/wireless/realtek/rtw89/util.c b/drivers/net/wireless/realtek/rtw89/util.c index e71956ce9853..073714db26f2 100644 --- a/drivers/net/wireless/realtek/rtw89/util.c +++ b/drivers/net/wireless/realtek/rtw89/util.c @@ -4,103 +4,159 @@ #include "util.h" -#define FRAC_ROWS 3 -#define FRAC_ROW_MAX (FRAC_ROWS - 1) -#define NORM_ROW_MIN FRAC_ROWS - -static const u32 db_invert_table[12][8] = { - /* rows 0~2 in unit of U(32,3) */ - {10, 13, 16, 20, 25, 32, 40, 50}, - {64, 80, 101, 128, 160, 201, 256, 318}, - {401, 505, 635, 800, 1007, 1268, 1596, 2010}, - /* rows 3~11 in unit of U(32,0) */ - {316, 398, 501, 631, 794, 1000, 1259, 1585}, - {1995, 2512, 3162, 3981, 5012, 6310, 7943, 10000}, - {12589, 15849, 19953, 25119, 31623, 39811, 50119, 63098}, - {79433, 100000, 125893, 158489, 199526, 251189, 316228, 398107}, - {501187, 630957, 794328, 1000000, 1258925, 1584893, 1995262, 2511886}, - {3162278, 3981072, 5011872, 6309573, 7943282, 1000000, 12589254, - 15848932}, - {19952623, 25118864, 31622777, 39810717, 50118723, 63095734, 79432823, - 100000000}, - {125892541, 158489319, 199526232, 251188643, 316227766, 398107171, - 501187234, 630957345}, - {794328235, 1000000000, 1258925412, 1584893192, 1995262315, 2511886432U, - 3162277660U, 3981071706U}, +#define RTW89_DBM_QUARTER_FACTOR 2 +#define RTW89_MIN_DBM (-41.25 * (1 << RTW89_DBM_QUARTER_FACTOR)) +#define RTW89_MAX_DBM (96 * (1 << RTW89_DBM_QUARTER_FACTOR)) +#define RTW89_DB_INVERT_TABLE_OFFSET (-RTW89_MIN_DBM) + +static const u64 db_invert_table[] = { + /* in unit of 0.000001 */ + 75, 79, 84, 89, 94, 100, 106, 112, 119, 126, 133, 141, 150, 158, 168, 178, 188, + 200, 211, 224, 237, 251, 266, 282, 299, 316, 335, 355, 376, 398, 422, 447, 473, + 501, 531, 562, 596, 631, 668, 708, 750, 794, 841, 891, 944, 1000, 1059, 1122, 1189, + 1259, 1334, 1413, 1496, 1585, 1679, 1778, 1884, 1995, 2113, 2239, 2371, 2512, 2661, + 2818, 2985, 3162, 3350, 3548, 3758, 3981, 4217, 4467, 4732, 5012, 5309, 5623, 5957, + 6310, 6683, 7079, 7499, 7943, 8414, 8913, 9441, 10000, 10593, 11220, 11885, 12589, + 13335, 14125, 14962, 15849, 16788, 17783, 18836, 19953, 21135, 22387, 23714, 25119, + 26607, 28184, 29854, 31623, 33497, 35481, 37584, 39811, 42170, 44668, 47315, 50119, + 53088, 56234, 59566, 63096, 66834, 70795, 74989, 79433, 84140, 89125, 94406, 100000, + 105925, 112202, 118850, 125893, 133352, 141254, 149624, 158489, 167880, 177828, + 188365, 199526, 211349, 223872, 237137, 251189, 266073, 281838, 298538, 316228, + 334965, 354813, 375837, 398107, 421697, 446684, 473151, 501187, 530884, 562341, + 595662, 630957, 668344, 707946, 749894, 794328, 841395, 891251, 944061, 1000000, + 1059254, 1122018, 1188502, 1258925, 1333521, 1412538, 1496236, 1584893, 1678804, + 1778279, 1883649, 1995262, 2113489, 2238721, 2371374, 2511886, 2660725, 2818383, + 2985383, 3162278, 3349654, 3548134, 3758374, 3981072, 4216965, 4466836, 4731513, + 5011872, 5308844, 5623413, 5956621, 6309573, 6683439, 7079458, 7498942, 7943282, + 8413951, 8912509, 9440609, 10000000, 10592537, 11220185, 11885022, 12589254, + 13335214, 14125375, 14962357, 15848932, 16788040, 17782794, 18836491, 19952623, + 21134890, 22387211, 23713737, 25118864, 26607251, 28183829, 29853826, 31622777, + 33496544, 35481339, 37583740, 39810717, 42169650, 44668359, 47315126, 50118723, + 53088444, 56234133, 59566214, 63095734, 66834392, 70794578, 74989421, 79432823, + 84139514, 89125094, 94406088, 100000000, 105925373, 112201845, 118850223, 125892541, + 133352143, 141253754, 149623566, 158489319, 167880402, 177827941, 188364909, 199526231, + 211348904, 223872114, 237137371, 251188643, 266072506, 281838293, 298538262, 316227766, + 334965439, 354813389, 375837404, 398107171, 421696503, 446683592, 473151259, 501187234, + 530884444, 562341325, 595662144, 630957344, 668343918, 707945784, 749894209, 794328235, + 841395142, 891250938, 944060876, 1000000000, 1059253725, 1122018454, 1188502227, + 1258925412, 1333521432, 1412537545, 1496235656, 1584893192, 1678804018, 1778279410, + 1883649089, 1995262315, 2113489040, 2238721139, 2371373706, 2511886432, 2660725060, + 2818382931, 2985382619, 3162277660, 3349654392, 3548133892, 3758374043, 3981071706, + 4216965034, 4466835922ULL, 4731512590ULL, 5011872336ULL, 5308844442ULL, 5623413252ULL, + 5956621435ULL, 6309573445ULL, 6683439176ULL, 7079457844ULL, 7498942093ULL, + 7943282347ULL, 8413951416ULL, 8912509381ULL, 9440608763ULL, 10000000000ULL, + 10592537252ULL, 11220184543ULL, 11885022274ULL, 12589254118ULL, 13335214322ULL, + 14125375446ULL, 14962356561ULL, 15848931925ULL, 16788040181ULL, 17782794100ULL, + 18836490895ULL, 19952623150ULL, 21134890398ULL, 22387211386ULL, 23713737057ULL, + 25118864315ULL, 26607250598ULL, 28183829313ULL, 29853826189ULL, 31622776602ULL, + 33496543916ULL, 35481338923ULL, 37583740429ULL, 39810717055ULL, 42169650343ULL, + 44668359215ULL, 47315125896ULL, 50118723363ULL, 53088444423ULL, 56234132519ULL, + 59566214353ULL, 63095734448ULL, 66834391757ULL, 70794578438ULL, 74989420933ULL, + 79432823472ULL, 84139514165ULL, 89125093813ULL, 94406087629ULL, 100000000000ULL, + 105925372518ULL, 112201845430ULL, 118850222744ULL, 125892541179ULL, 133352143216ULL, + 141253754462ULL, 149623565609ULL, 158489319246ULL, 167880401812ULL, 177827941004ULL, + 188364908949ULL, 199526231497ULL, 211348903984ULL, 223872113857ULL, 237137370566ULL, + 251188643151ULL, 266072505980ULL, 281838293126ULL, 298538261892ULL, 316227766017ULL, + 334965439158ULL, 354813389234ULL, 375837404288ULL, 398107170553ULL, 421696503429ULL, + 446683592151ULL, 473151258961ULL, 501187233627ULL, 530884444231ULL, 562341325190ULL, + 595662143529ULL, 630957344480ULL, 668343917569ULL, 707945784384ULL, 749894209332ULL, + 794328234724ULL, 841395141645ULL, 891250938134ULL, 944060876286ULL, 1000000000000ULL, + 1059253725177ULL, 1122018454302ULL, 1188502227437ULL, 1258925411794ULL, + 1333521432163ULL, 1412537544623ULL, 1496235656094ULL, 1584893192461ULL, + 1678804018123ULL, 1778279410039ULL, 1883649089490ULL, 1995262314969ULL, + 2113489039837ULL, 2238721138568ULL, 2371373705662ULL, 2511886431510ULL, + 2660725059799ULL, 2818382931264ULL, 2985382618918ULL, 3162277660168ULL, + 3349654391578ULL, 3548133892336ULL, 3758374042884ULL, 3981071705535ULL, + 4216965034286ULL, 4466835921510ULL, 4731512589615ULL, 5011872336273ULL, + 5308844442310ULL, 5623413251904ULL, 5956621435290ULL, 6309573444802ULL, + 6683439175686ULL, 7079457843841ULL, 7498942093325ULL, 7943282347243ULL, + 8413951416452ULL, 8912509381337ULL, 9440608762859ULL, 10000000000000ULL, + 10592537251773ULL, 11220184543020ULL, 11885022274370ULL, 12589254117942ULL, + 13335214321633ULL, 14125375446228ULL, 14962356560944ULL, 15848931924611ULL, + 16788040181226ULL, 17782794100389ULL, 18836490894898ULL, 19952623149689ULL, + 21134890398367ULL, 22387211385683ULL, 23713737056617ULL, 25118864315096ULL, + 26607250597988ULL, 28183829312645ULL, 29853826189180ULL, 31622776601684ULL, + 33496543915783ULL, 35481338923358ULL, 37583740428845ULL, 39810717055350ULL, + 42169650342858ULL, 44668359215096ULL, 47315125896148ULL, 50118723362727ULL, + 53088444423099ULL, 56234132519035ULL, 59566214352901ULL, 63095734448019ULL, + 66834391756862ULL, 70794578438414ULL, 74989420933246ULL, 79432823472428ULL, + 84139514164520ULL, 89125093813375ULL, 94406087628593ULL, 100000000000000ULL, + 105925372517729ULL, 112201845430197ULL, 118850222743702ULL, 125892541179417ULL, + 133352143216332ULL, 141253754462276ULL, 149623565609444ULL, 158489319246111ULL, + 167880401812256ULL, 177827941003893ULL, 188364908948981ULL, 199526231496888ULL, + 211348903983664ULL, 223872113856834ULL, 237137370566166ULL, 251188643150958ULL, + 266072505979882ULL, 281838293126446ULL, 298538261891796ULL, 316227766016838ULL, + 334965439157829ULL, 354813389233577ULL, 375837404288444ULL, 398107170553497ULL, + 421696503428583ULL, 446683592150964ULL, 473151258961482ULL, 501187233627272ULL, + 530884444230989ULL, 562341325190350ULL, 595662143529011ULL, 630957344480196ULL, + 668343917568615ULL, 707945784384138ULL, 749894209332456ULL, 794328234724284ULL, + 841395141645198ULL, 891250938133745ULL, 944060876285923ULL, 1000000000000000ULL, + 1059253725177290ULL, 1122018454301970ULL, 1188502227437020ULL, 1258925411794170ULL, + 1333521432163330ULL, 1412537544622760ULL, 1496235656094440ULL, 1584893192461110ULL, + 1678804018122560ULL, 1778279410038920ULL, 1883649089489810ULL, 1995262314968890ULL, + 2113489039836650ULL, 2238721138568340ULL, 2371373705661660ULL, 2511886431509590ULL, + 2660725059798820ULL, 2818382931264460ULL, 2985382618917960ULL, 3162277660168380ULL, + 3349654391578280ULL, 3548133892335770ULL, 3758374042884440ULL, 3981071705534970ULL }; -u32 rtw89_linear_2_db(u64 val) +s32 rtw89_linear_to_db_quarter(u64 val) { - u8 i, j; - u32 dB; - - for (i = 0; i < 12; i++) { - for (j = 0; j < 8; j++) { - if (i <= FRAC_ROW_MAX && - (val << RTW89_LINEAR_FRAC_BITS) <= db_invert_table[i][j]) - goto cnt; - else if (i > FRAC_ROW_MAX && val <= db_invert_table[i][j]) - goto cnt; - } - } + int r = ARRAY_SIZE(db_invert_table) - 1; + int l = 0; + int m; - return 96; /* maximum 96 dB */ + while (l <= r) { + m = l + (r - l) / 2; -cnt: - /* special cases */ - if (j == 0 && i == 0) - goto end; + if (db_invert_table[m] == val) + return m - (s32)RTW89_DB_INVERT_TABLE_OFFSET; - if (i == NORM_ROW_MIN && j == 0) { - if (db_invert_table[NORM_ROW_MIN][0] - val > - val - (db_invert_table[FRAC_ROW_MAX][7] >> RTW89_LINEAR_FRAC_BITS)) { - i = FRAC_ROW_MAX; - j = 7; - } - goto end; + if (db_invert_table[m] > val) + r = m - 1; + else + l = m + 1; } - if (i <= FRAC_ROW_MAX) - val <<= RTW89_LINEAR_FRAC_BITS; - - /* compare difference to get precise dB */ - if (j == 0) { - if (db_invert_table[i][j] - val > - val - db_invert_table[i - 1][7]) { - i--; - j = 7; - } - } else { - if (db_invert_table[i][j] - val > - val - db_invert_table[i][j - 1]) { - j--; - } - } -end: - dB = (i << 3) + j + 1; + if (l >= ARRAY_SIZE(db_invert_table)) + return RTW89_MAX_DBM; + else if (r < 0) + return RTW89_MIN_DBM; + else if (val - db_invert_table[r] <= db_invert_table[l] - val) + return r - (s32)RTW89_DB_INVERT_TABLE_OFFSET; + else + return l - (s32)RTW89_DB_INVERT_TABLE_OFFSET; +} +EXPORT_SYMBOL(rtw89_linear_to_db_quarter); - return dB; +s32 rtw89_linear_to_db(u64 val) +{ + return rtw89_linear_to_db_quarter(val) >> RTW89_DBM_QUARTER_FACTOR; } -EXPORT_SYMBOL(rtw89_linear_2_db); +EXPORT_SYMBOL(rtw89_linear_to_db); -u64 rtw89_db_2_linear(u32 db) +u64 rtw89_db_quarter_to_linear(s32 db) { - u64 linear; - u8 i, j; + /* supported range -41.25 to 96 dBm, in unit of 0.25 dBm */ + db = clamp_t(s32, db, RTW89_MIN_DBM, RTW89_MAX_DBM); + db += (s32)RTW89_DB_INVERT_TABLE_OFFSET; - if (db > 96) - db = 96; - else if (db < 1) - return 1; + return db_invert_table[db]; +} +EXPORT_SYMBOL(rtw89_db_quarter_to_linear); - i = (db - 1) >> 3; - j = (db - 1) & 0x7; +u64 rtw89_db_to_linear(s32 db) +{ + return rtw89_db_quarter_to_linear(db << RTW89_DBM_QUARTER_FACTOR); +} +EXPORT_SYMBOL(rtw89_db_to_linear); - linear = db_invert_table[i][j]; +void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used) +{ + static const char ellipsis[] = "..."; - if (i >= NORM_ROW_MIN) - linear = linear << RTW89_LINEAR_FRAC_BITS; + /* length of null terminiator isn't included in 'used' */ + if (used + 1 < size || size < sizeof(ellipsis)) + return; - return linear; + memcpy(buf + size - sizeof(ellipsis), ellipsis, sizeof(ellipsis)); } -EXPORT_SYMBOL(rtw89_db_2_linear); diff --git a/drivers/net/wireless/realtek/rtw89/util.h b/drivers/net/wireless/realtek/rtw89/util.h index e669544cafd3..bd08495301e4 100644 --- a/drivers/net/wireless/realtek/rtw89/util.h +++ b/drivers/net/wireless/realtek/rtw89/util.h @@ -6,13 +6,11 @@ #include "core.h" -#define RTW89_LINEAR_FRAC_BITS 3 - #define rtw89_iterate_vifs_bh(rtwdev, iterator, data) \ ieee80211_iterate_active_interfaces_atomic((rtwdev)->hw, \ IEEE80211_IFACE_ITER_NORMAL, iterator, data) -/* call this function with rtwdev->mutex is held */ +/* call this function with wiphy mutex is held */ #define rtw89_for_each_rtwvif(rtwdev, rtwvif) \ list_for_each_entry(rtwvif, &(rtwdev)->rtwvifs_list, list) @@ -25,7 +23,7 @@ static inline bool rtw89_rtwvif_in_list(struct rtw89_dev *rtwdev, { struct rtw89_vif *rtwvif; - lockdep_assert_held(&rtwdev->mutex); + lockdep_assert_wiphy(rtwdev->hw->wiphy); rtw89_for_each_rtwvif(rtwdev, rtwvif) if (rtwvif == new) @@ -75,7 +73,10 @@ static inline void ether_addr_copy_mask(u8 *dst, const u8 *src, u8 mask) } } -u32 rtw89_linear_2_db(u64 linear); -u64 rtw89_db_2_linear(u32 db); +s32 rtw89_linear_to_db_quarter(u64 val); +s32 rtw89_linear_to_db(u64 val); +u64 rtw89_db_quarter_to_linear(s32 db); +u64 rtw89_db_to_linear(s32 db); +void rtw89_might_trailing_ellipsis(char *buf, size_t size, ssize_t used); #endif diff --git a/drivers/net/wireless/realtek/rtw89/wow.c b/drivers/net/wireless/realtek/rtw89/wow.c index 01754d031bb4..34a0ab49bd7a 100644 --- a/drivers/net/wireless/realtek/rtw89/wow.c +++ b/drivers/net/wireless/realtek/rtw89/wow.c @@ -604,6 +604,8 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev, struct ieee80211_key_conf *key; u8 sz; + lockdep_assert_wiphy(rtwdev->hw->wiphy); + cipher_info = rtw89_cipher_alg_recognize(cipher); sz = struct_size(rekey_conf, key, cipher_info->len); rekey_conf = kmalloc(sz, GFP_KERNEL); @@ -616,15 +618,10 @@ static struct ieee80211_key_conf *rtw89_wow_gtk_rekey(struct rtw89_dev *rtwdev, memcpy(rekey_conf->key, gtk, flex_array_size(rekey_conf, key, cipher_info->len)); - /* ieee80211_gtk_rekey_add() will call set_key(), therefore we - * need to unlock mutex - */ - mutex_unlock(&rtwdev->mutex); if (ieee80211_vif_is_mld(wow_vif)) key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, rtwvif_link->link_id); else key = ieee80211_gtk_rekey_add(wow_vif, rekey_conf, -1); - mutex_lock(&rtwdev->mutex); kfree(rekey_conf); if (IS_ERR(key)) { @@ -1089,8 +1086,7 @@ static int rtw89_wow_set_wakeups(struct rtw89_dev *rtwdev, rtw89_wow_init_pno(rtwdev, wowlan->nd_config); rtw89_for_each_rtwvif(rtwdev, rtwvif) { - /* use the link on HW-0 to do wow flow */ - rtwvif_link = rtw89_vif_get_link_inst(rtwvif, 0); + rtwvif_link = rtw89_get_designated_link(rtwvif); if (!rtwvif_link) continue; diff --git a/drivers/net/wireless/rsi/rsi_91x_hal.c b/drivers/net/wireless/rsi/rsi_91x_hal.c index 2cebe562a1f4..53827657abb2 100644 --- a/drivers/net/wireless/rsi/rsi_91x_hal.c +++ b/drivers/net/wireless/rsi/rsi_91x_hal.c @@ -493,7 +493,7 @@ static void bl_cmd_timeout(struct timer_list *t) struct rsi_hw *adapter = from_timer(adapter, t, bl_cmd_timer); adapter->blcmd_timer_expired = true; - del_timer(&adapter->bl_cmd_timer); + timer_delete(&adapter->bl_cmd_timer); } static int bl_start_cmd_timer(struct rsi_hw *adapter, u32 timeout) @@ -511,7 +511,7 @@ static int bl_stop_cmd_timer(struct rsi_hw *adapter) { adapter->blcmd_timer_expired = false; if (timer_pending(&adapter->bl_cmd_timer)) - del_timer(&adapter->bl_cmd_timer); + timer_delete(&adapter->bl_cmd_timer); return 0; } diff --git a/drivers/net/wireless/rsi/rsi_91x_mac80211.c b/drivers/net/wireless/rsi/rsi_91x_mac80211.c index 3425a473b9a1..9db08200f4fa 100644 --- a/drivers/net/wireless/rsi/rsi_91x_mac80211.c +++ b/drivers/net/wireless/rsi/rsi_91x_mac80211.c @@ -1754,7 +1754,7 @@ void rsi_roc_timeout(struct timer_list *t) ieee80211_remain_on_channel_expired(common->priv->hw); if (timer_pending(&common->roc_timer)) - del_timer(&common->roc_timer); + timer_delete(&common->roc_timer); rsi_resume_conn_channel(common); mutex_unlock(&common->mutex); @@ -1776,7 +1776,7 @@ static int rsi_mac80211_roc(struct ieee80211_hw *hw, struct ieee80211_vif *vif, if (timer_pending(&common->roc_timer)) { rsi_dbg(INFO_ZONE, "Stop on-going ROC\n"); - del_timer(&common->roc_timer); + timer_delete(&common->roc_timer); } common->roc_timer.expires = msecs_to_jiffies(duration) + jiffies; add_timer(&common->roc_timer); @@ -1820,7 +1820,7 @@ static int rsi_mac80211_cancel_roc(struct ieee80211_hw *hw, return 0; } - del_timer(&common->roc_timer); + timer_delete(&common->roc_timer); rsi_resume_conn_channel(common); mutex_unlock(&common->mutex); diff --git a/drivers/net/wireless/silabs/wfx/bus.h b/drivers/net/wireless/silabs/wfx/bus.h index ccadfdd6873c..79edaef20881 100644 --- a/drivers/net/wireless/silabs/wfx/bus.h +++ b/drivers/net/wireless/silabs/wfx/bus.h @@ -28,6 +28,7 @@ struct wfx_hwbus_ops { void (*lock)(void *bus_priv); void (*unlock)(void *bus_priv); size_t (*align_size)(void *bus_priv, size_t size); + void (*set_wakeup)(void *priv, bool enabled); }; extern struct sdio_driver wfx_sdio_driver; diff --git a/drivers/net/wireless/silabs/wfx/bus_sdio.c b/drivers/net/wireless/silabs/wfx/bus_sdio.c index f290eecde773..ab0793b9908f 100644 --- a/drivers/net/wireless/silabs/wfx/bus_sdio.c +++ b/drivers/net/wireless/silabs/wfx/bus_sdio.c @@ -14,6 +14,7 @@ #include <linux/of_irq.h> #include <linux/irq.h> #include <linux/align.h> +#include <linux/pm.h> #include "bus.h" #include "wfx.h" @@ -172,6 +173,13 @@ static size_t wfx_sdio_align_size(void *priv, size_t size) return sdio_align_size(bus->func, size); } +static void wfx_sdio_set_wakeup(void *priv, bool enabled) +{ + struct wfx_sdio_priv *bus = priv; + + device_set_wakeup_enable(&bus->func->dev, enabled); +} + static const struct wfx_hwbus_ops wfx_sdio_hwbus_ops = { .copy_from_io = wfx_sdio_copy_from_io, .copy_to_io = wfx_sdio_copy_to_io, @@ -180,6 +188,7 @@ static const struct wfx_hwbus_ops wfx_sdio_hwbus_ops = { .lock = wfx_sdio_lock, .unlock = wfx_sdio_unlock, .align_size = wfx_sdio_align_size, + .set_wakeup = wfx_sdio_set_wakeup, }; static const struct of_device_id wfx_sdio_of_match[] = { @@ -191,9 +200,48 @@ static const struct of_device_id wfx_sdio_of_match[] = { }; MODULE_DEVICE_TABLE(of, wfx_sdio_of_match); +static int wfx_sdio_suspend(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + int ret; + + if (!device_may_wakeup(dev)) + return 0; + + flush_work(&bus->core->hif.bh); + /* Either "wakeup-source" attribute or out-of-band IRQ is required for + * WoWLAN + */ + if (bus->of_irq) { + ret = enable_irq_wake(bus->of_irq); + if (ret) + return ret; + } else { + ret = sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ); + if (ret) + return ret; + } + return sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER); +} + +static int wfx_sdio_resume(struct device *dev) +{ + struct sdio_func *func = dev_to_sdio_func(dev); + struct wfx_sdio_priv *bus = sdio_get_drvdata(func); + + if (!device_may_wakeup(dev)) + return 0; + if (bus->of_irq) + return disable_irq_wake(bus->of_irq); + else + return 0; +} + static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) { const struct wfx_platform_data *pdata = of_device_get_match_data(&func->dev); + mmc_pm_flag_t pm_flag = sdio_get_host_pm_caps(func); struct device_node *np = func->dev.of_node; struct wfx_sdio_priv *bus; int ret; @@ -235,6 +283,9 @@ static int wfx_sdio_probe(struct sdio_func *func, const struct sdio_device_id *i if (ret) goto sdio_release; + if (pm_flag & MMC_PM_KEEP_POWER) + device_set_wakeup_capable(&func->dev, true); + return 0; sdio_release: @@ -261,6 +312,8 @@ static const struct sdio_device_id wfx_sdio_ids[] = { }; MODULE_DEVICE_TABLE(sdio, wfx_sdio_ids); +static DEFINE_SIMPLE_DEV_PM_OPS(wfx_sdio_pm_ops, wfx_sdio_suspend, wfx_sdio_resume); + struct sdio_driver wfx_sdio_driver = { .name = "wfx-sdio", .id_table = wfx_sdio_ids, @@ -268,5 +321,6 @@ struct sdio_driver wfx_sdio_driver = { .remove = wfx_sdio_remove, .drv = { .of_match_table = wfx_sdio_of_match, + .pm = &wfx_sdio_pm_ops, } }; diff --git a/drivers/net/wireless/silabs/wfx/bus_spi.c b/drivers/net/wireless/silabs/wfx/bus_spi.c index 160b90114aad..45ee19e1ecbf 100644 --- a/drivers/net/wireless/silabs/wfx/bus_spi.c +++ b/drivers/net/wireless/silabs/wfx/bus_spi.c @@ -13,6 +13,7 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/of.h> +#include <linux/pm.h> #include "bus.h" #include "wfx.h" @@ -179,6 +180,13 @@ static size_t wfx_spi_align_size(void *priv, size_t size) return ALIGN(size, 4); } +static void wfx_spi_set_wakeup(void *priv, bool enabled) +{ + struct wfx_spi_priv *bus = priv; + + device_set_wakeup_enable(&bus->func->dev, enabled); +} + static const struct wfx_hwbus_ops wfx_spi_hwbus_ops = { .copy_from_io = wfx_spi_copy_from_io, .copy_to_io = wfx_spi_copy_to_io, @@ -187,8 +195,29 @@ static const struct wfx_hwbus_ops wfx_spi_hwbus_ops = { .lock = wfx_spi_lock, .unlock = wfx_spi_unlock, .align_size = wfx_spi_align_size, + .set_wakeup = wfx_spi_set_wakeup, }; +static int wfx_spi_suspend(struct device *dev) +{ + struct spi_device *func = to_spi_device(dev); + struct wfx_spi_priv *bus = spi_get_drvdata(func); + + if (!device_may_wakeup(dev)) + return 0; + flush_work(&bus->core->hif.bh); + return enable_irq_wake(func->irq); +} + +static int wfx_spi_resume(struct device *dev) +{ + struct spi_device *func = to_spi_device(dev); + + if (!device_may_wakeup(dev)) + return 0; + return disable_irq_wake(func->irq); +} + static int wfx_spi_probe(struct spi_device *func) { struct wfx_platform_data *pdata; @@ -239,7 +268,12 @@ static int wfx_spi_probe(struct spi_device *func) if (!bus->core) return -EIO; - return wfx_probe(bus->core); + ret = wfx_probe(bus->core); + if (ret) + return ret; + + device_set_wakeup_capable(&func->dev, true); + return 0; } static void wfx_spi_remove(struct spi_device *func) @@ -273,12 +307,15 @@ static const struct of_device_id wfx_spi_of_match[] = { MODULE_DEVICE_TABLE(of, wfx_spi_of_match); #endif +static DEFINE_SIMPLE_DEV_PM_OPS(wfx_spi_pm_ops, wfx_spi_suspend, wfx_spi_resume); + struct spi_driver wfx_spi_driver = { + .id_table = wfx_spi_id, + .probe = wfx_spi_probe, + .remove = wfx_spi_remove, .driver = { .name = "wfx-spi", .of_match_table = of_match_ptr(wfx_spi_of_match), + .pm = &wfx_spi_pm_ops, }, - .id_table = wfx_spi_id, - .probe = wfx_spi_probe, - .remove = wfx_spi_remove, }; diff --git a/drivers/net/wireless/silabs/wfx/main.c b/drivers/net/wireless/silabs/wfx/main.c index 64441c8bc460..a61128debbad 100644 --- a/drivers/net/wireless/silabs/wfx/main.c +++ b/drivers/net/wireless/silabs/wfx/main.c @@ -121,6 +121,12 @@ static const struct ieee80211_iface_combination wfx_iface_combinations[] = { } }; +#ifdef CONFIG_PM +static const struct wiphy_wowlan_support wfx_wowlan_support = { + .flags = WIPHY_WOWLAN_ANY | WIPHY_WOWLAN_DISCONNECT, +}; +#endif + static const struct ieee80211_ops wfx_ops = { .start = wfx_start, .stop = wfx_stop, @@ -153,6 +159,11 @@ static const struct ieee80211_ops wfx_ops = { .unassign_vif_chanctx = wfx_unassign_vif_chanctx, .remain_on_channel = wfx_remain_on_channel, .cancel_remain_on_channel = wfx_cancel_remain_on_channel, +#ifdef CONFIG_PM + .suspend = wfx_suspend, + .resume = wfx_resume, + .set_wakeup = wfx_set_wakeup, +#endif }; bool wfx_api_older_than(struct wfx_dev *wdev, int major, int minor) @@ -289,6 +300,9 @@ struct wfx_dev *wfx_init_common(struct device *dev, const struct wfx_platform_da NL80211_PROBE_RESP_OFFLOAD_SUPPORT_P2P | NL80211_PROBE_RESP_OFFLOAD_SUPPORT_80211U; hw->wiphy->features |= NL80211_FEATURE_AP_SCAN; +#ifdef CONFIG_PM + hw->wiphy->wowlan = &wfx_wowlan_support; +#endif hw->wiphy->flags |= WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD; hw->wiphy->flags |= WIPHY_FLAG_AP_UAPSD; hw->wiphy->max_remain_on_channel_duration = 5000; diff --git a/drivers/net/wireless/silabs/wfx/sta.c b/drivers/net/wireless/silabs/wfx/sta.c index 7c04810dbf3d..e95b9ded17d9 100644 --- a/drivers/net/wireless/silabs/wfx/sta.c +++ b/drivers/net/wireless/silabs/wfx/sta.c @@ -10,6 +10,7 @@ #include "sta.h" #include "wfx.h" +#include "bus.h" #include "fwio.h" #include "bh.h" #include "key.h" @@ -803,6 +804,30 @@ void wfx_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) } } +#ifdef CONFIG_PM +int wfx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) +{ + /* FIXME: hardware also support WIPHY_WOWLAN_MAGIC_PKT and other filters */ + if (!wowlan->any || !wowlan->disconnect) + return -EINVAL; + return 0; +} + +int wfx_resume(struct ieee80211_hw *hw) +{ + return 0; +} + +void wfx_set_wakeup(struct ieee80211_hw *hw, bool enabled) +{ + struct wfx_dev *wdev = hw->priv; + + if (enabled) + dev_info(wdev->dev, "support for WoWLAN is experimental\n"); + wdev->hwbus_ops->set_wakeup(wdev->hwbus_priv, enabled); +} +#endif + int wfx_start(struct ieee80211_hw *hw) { return 0; diff --git a/drivers/net/wireless/silabs/wfx/sta.h b/drivers/net/wireless/silabs/wfx/sta.h index 7817c7c6f3dd..8702eed5267f 100644 --- a/drivers/net/wireless/silabs/wfx/sta.h +++ b/drivers/net/wireless/silabs/wfx/sta.h @@ -56,6 +56,9 @@ int wfx_assign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, void wfx_unassign_vif_chanctx(struct ieee80211_hw *hw, struct ieee80211_vif *vif, struct ieee80211_bss_conf *link_conf, struct ieee80211_chanctx_conf *conf); +int wfx_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); +int wfx_resume(struct ieee80211_hw *hw); +void wfx_set_wakeup(struct ieee80211_hw *hw, bool enabled); /* Hardware API Callbacks */ void wfx_cooling_timeout_work(struct work_struct *work); diff --git a/drivers/net/wireless/st/cw1200/main.c b/drivers/net/wireless/st/cw1200/main.c index a54a7b86864f..5d569eeb353f 100644 --- a/drivers/net/wireless/st/cw1200/main.c +++ b/drivers/net/wireless/st/cw1200/main.c @@ -458,7 +458,7 @@ static void cw1200_unregister_common(struct ieee80211_hw *dev) ieee80211_unregister_hw(dev); - del_timer_sync(&priv->mcast_timeout); + timer_delete_sync(&priv->mcast_timeout); cw1200_unregister_bh(priv); cw1200_debug_release(priv); diff --git a/drivers/net/wireless/st/cw1200/pm.c b/drivers/net/wireless/st/cw1200/pm.c index a20ab577a364..2002e3f9fe45 100644 --- a/drivers/net/wireless/st/cw1200/pm.c +++ b/drivers/net/wireless/st/cw1200/pm.c @@ -105,7 +105,7 @@ int cw1200_pm_init(struct cw1200_pm_state *pm, void cw1200_pm_deinit(struct cw1200_pm_state *pm) { - del_timer_sync(&pm->stay_awake); + timer_delete_sync(&pm->stay_awake); } void cw1200_pm_stay_awake(struct cw1200_pm_state *pm, diff --git a/drivers/net/wireless/st/cw1200/queue.c b/drivers/net/wireless/st/cw1200/queue.c index 259739e53fc1..4fd76183c368 100644 --- a/drivers/net/wireless/st/cw1200/queue.c +++ b/drivers/net/wireless/st/cw1200/queue.c @@ -244,7 +244,7 @@ void cw1200_queue_stats_deinit(struct cw1200_queue_stats *stats) void cw1200_queue_deinit(struct cw1200_queue *queue) { cw1200_queue_clear(queue); - del_timer_sync(&queue->gc); + timer_delete_sync(&queue->gc); INIT_LIST_HEAD(&queue->free_pool); kfree(queue->pool); kfree(queue->link_map_cache); diff --git a/drivers/net/wireless/st/cw1200/sta.c b/drivers/net/wireless/st/cw1200/sta.c index c259da8161e4..444272caf124 100644 --- a/drivers/net/wireless/st/cw1200/sta.c +++ b/drivers/net/wireless/st/cw1200/sta.c @@ -113,7 +113,7 @@ void cw1200_stop(struct ieee80211_hw *dev, bool suspend) cancel_work_sync(&priv->unjoin_work); cancel_delayed_work_sync(&priv->link_id_gc_work); flush_workqueue(priv->workqueue); - del_timer_sync(&priv->mcast_timeout); + timer_delete_sync(&priv->mcast_timeout); mutex_lock(&priv->conf_mutex); priv->mode = NL80211_IFTYPE_UNSPECIFIED; priv->listening = false; @@ -2102,7 +2102,7 @@ void cw1200_multicast_stop_work(struct work_struct *work) container_of(work, struct cw1200_common, multicast_stop_work); if (priv->aid0_bit_set) { - del_timer_sync(&priv->mcast_timeout); + timer_delete_sync(&priv->mcast_timeout); wsm_lock_tx(priv); priv->aid0_bit_set = false; cw1200_set_tim_impl(priv, false); @@ -2170,7 +2170,7 @@ void cw1200_suspend_resume(struct cw1200_common *priv, } spin_unlock_bh(&priv->ps_state_lock); if (cancel_tmo) - del_timer_sync(&priv->mcast_timeout); + timer_delete_sync(&priv->mcast_timeout); } else { spin_lock_bh(&priv->ps_state_lock); cw1200_ps_notify(priv, arg->link_id, arg->stop); diff --git a/drivers/net/wireless/ti/wl1251/tx.c b/drivers/net/wireless/ti/wl1251/tx.c index 474b603c121c..adb4840b0489 100644 --- a/drivers/net/wireless/ti/wl1251/tx.c +++ b/drivers/net/wireless/ti/wl1251/tx.c @@ -342,8 +342,10 @@ void wl1251_tx_work(struct work_struct *work) while ((skb = skb_dequeue(&wl->tx_queue))) { if (!woken_up) { ret = wl1251_ps_elp_wakeup(wl); - if (ret < 0) + if (ret < 0) { + skb_queue_head(&wl->tx_queue, skb); goto out; + } woken_up = true; } diff --git a/drivers/net/wireless/ti/wlcore/main.c b/drivers/net/wireless/ti/wlcore/main.c index 8fb58a5d911c..ea9bc4717a85 100644 --- a/drivers/net/wireless/ti/wlcore/main.c +++ b/drivers/net/wireless/ti/wlcore/main.c @@ -117,7 +117,7 @@ int wl1271_recalc_rx_streaming(struct wl1271 *wl, struct wl12xx_vif *wlvif) else { ret = wl1271_set_rx_streaming(wl, wlvif, false); /* don't cancel_work_sync since we might deadlock */ - del_timer_sync(&wlvif->rx_streaming_timer); + timer_delete_sync(&wlvif->rx_streaming_timer); } out: return ret; @@ -2841,7 +2841,7 @@ deinit: unlock: mutex_unlock(&wl->mutex); - del_timer_sync(&wlvif->rx_streaming_timer); + timer_delete_sync(&wlvif->rx_streaming_timer); cancel_work_sync(&wlvif->rx_streaming_enable_work); cancel_work_sync(&wlvif->rx_streaming_disable_work); cancel_work_sync(&wlvif->rc_update_work); diff --git a/drivers/net/wireless/virtual/mac80211_hwsim.c b/drivers/net/wireless/virtual/mac80211_hwsim.c index cf6a331d4042..f6add19d1da1 100644 --- a/drivers/net/wireless/virtual/mac80211_hwsim.c +++ b/drivers/net/wireless/virtual/mac80211_hwsim.c @@ -4,7 +4,7 @@ * Copyright (c) 2008, Jouni Malinen <j@w1.fi> * Copyright (c) 2011, Javier Lopez <jlopex@gmail.com> * Copyright (c) 2016 - 2017 Intel Deutschland GmbH - * Copyright (C) 2018 - 2024 Intel Corporation + * Copyright (C) 2018 - 2025 Intel Corporation */ /* @@ -1229,6 +1229,11 @@ static void mac80211_hwsim_set_tsf(struct ieee80211_hw *hw, /* MLD not supported here */ u32 bcn_int = data->link_data[0].beacon_int; u64 delta = abs(tsf - now); + struct ieee80211_bss_conf *conf; + + conf = link_conf_dereference_protected(vif, data->link_data[0].link_id); + if (conf && !conf->enable_beacon) + return; /* adjust after beaconing with new timestamp at old TBTT */ if (tsf > now) { @@ -1983,11 +1988,13 @@ static void mac80211_hwsim_tx(struct ieee80211_hw *hw, return; } - if (sta && sta->mlo) { - if (WARN_ON(!link_sta)) { - ieee80211_free_txskb(hw, skb); - return; - } + /* Do address translations only between shared links. It is + * possible that while an non-AP MLD station and an AP MLD + * station have shared links, the frame is intended to be sent + * on a link which is not shared (for example when sending a + * probe response). + */ + if (sta && sta->mlo && link_sta) { /* address translation to link addresses on TX */ ether_addr_copy(hdr->addr1, link_sta->addr); ether_addr_copy(hdr->addr2, bss_conf->addr); @@ -2271,7 +2278,7 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, { struct mac80211_hwsim_link_data *link_data = arg; u32 link_id = link_data->link_id; - struct ieee80211_bss_conf *link_conf; + struct ieee80211_bss_conf *link_conf, *tx_bss_conf; struct mac80211_hwsim_data *data = container_of(link_data, struct mac80211_hwsim_data, link_data[link_id]); @@ -2290,10 +2297,11 @@ static void mac80211_hwsim_beacon_tx(void *arg, u8 *mac, vif->type != NL80211_IFTYPE_OCB) return; - if (vif->mbssid_tx_vif && vif->mbssid_tx_vif != vif) + tx_bss_conf = rcu_access_pointer(link_conf->tx_bss_conf); + if (tx_bss_conf && tx_bss_conf != link_conf) return; - if (vif->bss_conf.ema_ap) { + if (link_conf->ema_ap) { struct ieee80211_ema_beacons *ema; u8 i = 0; @@ -5345,6 +5353,7 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, ieee80211_hw_set(hw, REPORTS_TX_ACK_STATUS); ieee80211_hw_set(hw, TDLS_WIDER_BW); ieee80211_hw_set(hw, SUPPORTS_MULTI_BSSID); + ieee80211_hw_set(hw, STRICT); if (param->mlo) { hw->wiphy->flags |= WIPHY_FLAG_SUPPORTS_MLO; @@ -5548,10 +5557,8 @@ static int mac80211_hwsim_new_radio(struct genl_info *info, wiphy_ext_feature_set(hw->wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); for (i = 0; i < ARRAY_SIZE(data->link_data); i++) { - hrtimer_init(&data->link_data[i].beacon_timer, CLOCK_MONOTONIC, - HRTIMER_MODE_ABS_SOFT); - data->link_data[i].beacon_timer.function = - mac80211_hwsim_beacon; + hrtimer_setup(&data->link_data[i].beacon_timer, mac80211_hwsim_beacon, + CLOCK_MONOTONIC, HRTIMER_MODE_ABS_SOFT); data->link_data[i].link_id = i; } diff --git a/drivers/net/wireless/virtual/virt_wifi.c b/drivers/net/wireless/virtual/virt_wifi.c index 4ee374080466..1fffeff2190c 100644 --- a/drivers/net/wireless/virtual/virt_wifi.c +++ b/drivers/net/wireless/virtual/virt_wifi.c @@ -146,7 +146,7 @@ static void virt_wifi_inform_bss(struct wiphy *wiphy) static const struct { u8 tag; u8 len; - u8 ssid[8]; + u8 ssid[8] __nonstring; } __packed ssid = { .tag = WLAN_EID_SSID, .len = VIRT_WIFI_SSID_LEN, @@ -519,11 +519,13 @@ static rx_handler_result_t virt_wifi_rx_handler(struct sk_buff **pskb) } /* Called with rtnl lock held. */ -static int virt_wifi_newlink(struct net *src_net, struct net_device *dev, - struct nlattr *tb[], struct nlattr *data[], +static int virt_wifi_newlink(struct net_device *dev, + struct rtnl_newlink_params *params, struct netlink_ext_ack *extack) { struct virt_wifi_netdev_priv *priv = netdev_priv(dev); + struct net *link_net = rtnl_newlink_link_net(params); + struct nlattr **tb = params->tb; int err; if (!tb[IFLA_LINK]) @@ -532,7 +534,7 @@ static int virt_wifi_newlink(struct net *src_net, struct net_device *dev, netif_carrier_off(dev); priv->upperdev = dev; - priv->lowerdev = __dev_get_by_index(src_net, + priv->lowerdev = __dev_get_by_index(link_net, nla_get_u32(tb[IFLA_LINK])); if (!priv->lowerdev) diff --git a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c index f90c33d19b39..9653dbaac3c0 100644 --- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c @@ -21,7 +21,7 @@ struct zd_reg_alpha2_map { u32 reg; - char alpha2[2]; + char alpha2[2] __nonstring; }; static struct zd_reg_alpha2_map reg_alpha2_map[] = { |