mirror of
https://github.com/torvalds/linux.git
synced 2026-07-27 17:47:41 +02:00
Mostly driver updates:
- iwlwifi
- more UHR support
- NAN (multicast, schedule improvements, multi-station)
- cleanups, etc.
- ath12k
- thermal throttling/cooling device support
- 6 GHz incumbent interference detection
- channel 177 in 5 GHz
- hwsim: S1G fixes
- mac80211: NAN channel handling improvements
-----BEGIN PGP SIGNATURE-----
iQIzBAABCgAdFiEEpeA8sTs3M8SN2hR410qiO8sPaAAFAmoYNp4ACgkQ10qiO8sP
aACvLA//ULTpJxcURZgcIqcY54i8GoHZl0NaiaPpqnQ1OpvDoUsLwst54AyZjclq
aYj6G10gtPh4gYC/DgftPGNw5KWcdg7zZzqX1Zzg1ZKeJ/m8cW/2wnwf1IrSzx3g
tFRkpDBATxV2Qe9F0s9rLMauir6dimhkNe9UfaTX7s2tIM/agIrZyv1kDAZqI8SF
crH7rek2iaOgnAU17bwLFH8NZ5OtB5zzqSxXVMt7FxBGgps5jOX+g0KS3+5heMCZ
bZiEscfBbJ756j34iFYmX08mtCV5bfAWAsCA8rr5Qr8FQfBLJScgSm0iLHULoDxX
sO4fo1Y9YoKqAnxyDIM+UdfqphQokwTmkxp1camZGVyOLqalZ+Lq2ud6oZnOLw98
ZsfG+8Z2kE9pffwQJQjr6EMT8bmnBxIlZhNmGURZe3s+BCgZMknjGQ6uGk3VpHnZ
IzzX5Wcwn1pkvDo2QWbokbf1mDCO6VXe+aBwKS6erEvokhWcMq3MjvL1Vbw/1veu
zFTt1+NY8+B2lgQ7NPJHnBv45ayEhxQob+1AveBUCyFjpNygB0+8uFIwr/KJoOJt
ocOQaOEJw2XYYPf1Ij+wd0FTRclVsYnNkHrmecsMliHvzj73p0uCNEDrjBZrQBzW
bE8F0d4P84mJgwZ1GCm8I10QDr8bS+BMhLI5ZwVN4OqnXbZE6e4=
=F3sA
-----END PGP SIGNATURE-----
Merge tag 'wireless-next-2026-05-28' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next
Johannes Berg says:
====================
Mostly driver updates:
- iwlwifi
- more UHR support
- NAN (multicast, schedule improvements, multi-station)
- cleanups, etc.
- ath12k
- thermal throttling/cooling device support
- 6 GHz incumbent interference detection
- channel 177 in 5 GHz
- hwsim: S1G fixes
- mac80211: NAN channel handling improvements
* tag 'wireless-next-2026-05-28' of https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next: (143 commits)
wifi: cfg80211: use strscpy in cfg80211_wext_giwname
wifi: mac80211: fix channel evacuation logic
wifi: mac80211: refactor ieee80211_nan_try_evacuate
wifi: mac80211: add an option to filter out a channel in combinations check
wifi: mac80211_hwsim: add debug messages for link changes
wifi: nl80211: re-check wiphy netns in testmode and vendor dump continuations
wifi: mac80211_hwsim: modernise S1G channel list
wifi: mac80211_hwsim: don't run RC update on new STA on S1G vif
wifi: mwifiex: remove an unnecessary check
wifi: mac80211: add KUnit coverage for negotiated TTLM parser
wifi: ath12k: fix error unwind on arch_init() failure in PCI probe
wifi: iwlwifi: mld: fix indentation in iwl_mld_fill_supp_rates()
wifi: iwlwifi: transport: add memory read under NIC access
wifi: iwlwifi: dbg: remove unused 'range_len' arg from dump
wifi: iwlwifi: fw: separate out old-style dump code
wifi: iwlwifi: fw: dbg: always use non-tracing PRPH access
wifi: iwlwifi: fw: separate ini dump allocation
wifi: iwlwifi: fw: move struct iwl_fw_ini_dump_entry to dbg.c
wifi: iwlwifi: clean up location format/BW encoding
wifi: iwlwifi: Add names for Killer BE1735x and BE1730x
...
====================
Link: https://patch.msgid.link/20260528123911.284536-26-johannes@sipsolutions.net
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
commit
e7d6bd24e8
|
|
@ -1269,7 +1269,6 @@ struct ath10k {
|
|||
} testmode;
|
||||
|
||||
struct {
|
||||
struct gpio_led wifi_led;
|
||||
struct led_classdev cdev;
|
||||
char label[48];
|
||||
u32 gpio_state_pin;
|
||||
|
|
|
|||
|
|
@ -1353,7 +1353,7 @@ static int ath10k_htt_tx_hl(struct ath10k_htt *htt, enum ath10k_hw_txrx_mode txm
|
|||
msdu_id = res;
|
||||
}
|
||||
|
||||
/* As msdu is freed by mac80211 (in ieee80211_tx_status()) and by
|
||||
/* As msdu is freed by mac80211 (in ieee80211_tx_status_skb()) and by
|
||||
* ath10k (in ath10k_htt_htc_tx_complete()) we have to increase
|
||||
* reference by one to avoid a use-after-free case and a double
|
||||
* free.
|
||||
|
|
|
|||
|
|
@ -19,15 +19,13 @@ static int ath10k_leds_set_brightness_blocking(struct led_classdev *led_cdev,
|
|||
{
|
||||
struct ath10k *ar = container_of(led_cdev, struct ath10k,
|
||||
leds.cdev);
|
||||
struct gpio_led *led = &ar->leds.wifi_led;
|
||||
|
||||
mutex_lock(&ar->conf_mutex);
|
||||
|
||||
if (ar->state != ATH10K_STATE_ON)
|
||||
goto out;
|
||||
|
||||
ar->leds.gpio_state_pin = (brightness != LED_OFF) ^ led->active_low;
|
||||
ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, ar->leds.gpio_state_pin);
|
||||
ath10k_wmi_gpio_output(ar, ar->hw_params.led_pin, brightness == LED_OFF);
|
||||
|
||||
out:
|
||||
mutex_unlock(&ar->conf_mutex);
|
||||
|
|
@ -63,13 +61,9 @@ int ath10k_leds_register(struct ath10k *ar)
|
|||
|
||||
snprintf(ar->leds.label, sizeof(ar->leds.label), "ath10k-%s",
|
||||
wiphy_name(ar->hw->wiphy));
|
||||
ar->leds.wifi_led.active_low = 1;
|
||||
ar->leds.wifi_led.name = ar->leds.label;
|
||||
ar->leds.wifi_led.default_state = LEDS_GPIO_DEFSTATE_KEEP;
|
||||
|
||||
ar->leds.cdev.name = ar->leds.label;
|
||||
ar->leds.cdev.brightness_set_blocking = ath10k_leds_set_brightness_blocking;
|
||||
ar->leds.cdev.default_trigger = ar->leds.wifi_led.default_trigger;
|
||||
|
||||
ret = led_classdev_register(wiphy_dev(ar->hw->wiphy), &ar->leds.cdev);
|
||||
if (ret)
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: ISC
|
||||
/*
|
||||
* Copyright (c) 2018 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2022-2023 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/completion.h>
|
||||
|
|
@ -1112,7 +1112,7 @@ int ath10k_qmi_init(struct ath10k *ar, u32 msa_size)
|
|||
spin_lock_init(&qmi->event_lock);
|
||||
INIT_WORK(&qmi->event_work, ath10k_qmi_driver_event_work);
|
||||
|
||||
ret = qmi_add_lookup(&qmi->qmi_hdl, WLFW_SERVICE_ID_V01,
|
||||
ret = qmi_add_lookup(&qmi->qmi_hdl, QMI_SERVICE_ID_WLFW,
|
||||
WLFW_SERVICE_VERS_V01, 0);
|
||||
if (ret)
|
||||
goto err_qmi_lookup;
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
#ifndef WCN3990_QMI_SVC_V01_H
|
||||
#define WCN3990_QMI_SVC_V01_H
|
||||
|
||||
#define WLFW_SERVICE_ID_V01 0x45
|
||||
#define WLFW_SERVICE_VERS_V01 0x01
|
||||
|
||||
#define QMI_WLFW_BDF_DOWNLOAD_REQ_V01 0x0025
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
config ATH11K
|
||||
tristate "Qualcomm Technologies 802.11ax chipset support"
|
||||
tristate "Qualcomm 802.11ax chipset support"
|
||||
depends on MAC80211 && HAS_DMA
|
||||
select ATH_COMMON
|
||||
select QCOM_QMI_HELPERS
|
||||
|
|
|
|||
|
|
@ -1040,6 +1040,7 @@ void ath11k_dp_free(struct ath11k_base *ab)
|
|||
idr_destroy(&dp->tx_ring[i].txbuf_idr);
|
||||
spin_unlock_bh(&dp->tx_ring[i].tx_idr_lock);
|
||||
kfree(dp->tx_ring[i].tx_status);
|
||||
dp->tx_ring[i].tx_status = NULL;
|
||||
}
|
||||
|
||||
/* Deinit any SOC level resource */
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
/*
|
||||
* Copyright (c) 2020 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
#include <linux/msi.h>
|
||||
|
|
@ -282,8 +282,10 @@ static void ath11k_mhi_op_status_cb(struct mhi_controller *mhi_cntrl,
|
|||
break;
|
||||
}
|
||||
|
||||
spin_lock_bh(&ab->base_lock);
|
||||
if (!(test_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags)))
|
||||
queue_work(ab->workqueue_aux, &ab->reset_work);
|
||||
spin_unlock_bh(&ab->base_lock);
|
||||
|
||||
break;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -1210,6 +1210,14 @@ 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);
|
||||
|
||||
spin_lock_bh(&ab->base_lock);
|
||||
set_bit(ATH11K_FLAG_UNREGISTERING, &ab->dev_flags);
|
||||
spin_unlock_bh(&ab->base_lock);
|
||||
|
||||
cancel_work_sync(&ab->reset_work);
|
||||
cancel_work_sync(&ab->dump_work);
|
||||
|
||||
ath11k_pci_power_down(ab, false);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
/*
|
||||
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2021-2025 Qualcomm Innovation Center, Inc. All rights reserved.
|
||||
* Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
|
||||
*/
|
||||
|
||||
|
|
@ -3337,7 +3336,7 @@ int ath11k_qmi_init_service(struct ath11k_base *ab)
|
|||
spin_lock_init(&ab->qmi.event_lock);
|
||||
INIT_WORK(&ab->qmi.event_work, ath11k_qmi_driver_event_work);
|
||||
|
||||
ret = qmi_add_lookup(&ab->qmi.handle, ATH11K_QMI_WLFW_SERVICE_ID_V01,
|
||||
ret = qmi_add_lookup(&ab->qmi.handle, QMI_SERVICE_ID_WLFW,
|
||||
ATH11K_QMI_WLFW_SERVICE_VERS_V01,
|
||||
ab->qmi.service_ins_id);
|
||||
if (ret < 0) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#define ATH11K_QMI_MAX_BDF_FILE_NAME_SIZE 64
|
||||
#define ATH11K_QMI_CALDB_ADDRESS 0x4BA00000
|
||||
#define ATH11K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128
|
||||
#define ATH11K_QMI_WLFW_SERVICE_ID_V01 0x45
|
||||
#define ATH11K_QMI_WLFW_SERVICE_VERS_V01 0x01
|
||||
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01 0x02
|
||||
#define ATH11K_QMI_WLFW_SERVICE_INS_ID_V01_QCA6390 0x01
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# SPDX-License-Identifier: BSD-3-Clause-Clear
|
||||
config ATH12K
|
||||
tristate "Qualcomm Technologies Wi-Fi 7 support (ath12k)"
|
||||
tristate "Qualcomm Wi-Fi 7 support (ath12k)"
|
||||
depends on MAC80211 && HAS_DMA && PCI
|
||||
select QCOM_QMI_HELPERS
|
||||
select MHI_BUS
|
||||
|
|
@ -15,7 +15,7 @@ config ATH12K
|
|||
If you choose to build a module, it'll be called ath12k.
|
||||
|
||||
config ATH12K_AHB
|
||||
bool "QTI ath12k AHB support"
|
||||
bool "Qualcomm ath12k AHB support"
|
||||
depends on ATH12K && REMOTEPROC
|
||||
select QCOM_MDT_LOADER
|
||||
select QCOM_SCM
|
||||
|
|
@ -33,7 +33,7 @@ config ATH12K_DEBUG
|
|||
you want optimal performance choose N.
|
||||
|
||||
config ATH12K_DEBUGFS
|
||||
bool "QTI ath12k debugfs support"
|
||||
bool "Qualcomm ath12k debugfs support"
|
||||
depends on ATH12K && MAC80211_DEBUGFS
|
||||
help
|
||||
Enable ath12k debugfs support
|
||||
|
|
|
|||
|
|
@ -1006,6 +1006,27 @@ static void ath12k_core_device_cleanup(struct ath12k_base *ab)
|
|||
mutex_unlock(&ab->core_lock);
|
||||
}
|
||||
|
||||
static int ath12k_core_device_setup(struct ath12k_base *ab)
|
||||
{
|
||||
int ret;
|
||||
|
||||
guard(mutex)(&ab->core_lock);
|
||||
|
||||
ret = ath12k_core_pdev_create(ab);
|
||||
if (ret) {
|
||||
ath12k_err(ab, "failed to create pdev core %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ath12k_hif_irq_enable(ab);
|
||||
|
||||
ret = ath12k_core_rfkill_config(ab);
|
||||
if (ret && ret != -EOPNOTSUPP)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
|
||||
{
|
||||
struct ath12k_base *ab;
|
||||
|
|
@ -1015,10 +1036,6 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
|
|||
|
||||
clear_bit(ATH12K_GROUP_FLAG_REGISTERED, &ag->flags);
|
||||
|
||||
ath12k_mac_unregister(ag);
|
||||
|
||||
ath12k_mac_mlo_teardown(ag);
|
||||
|
||||
for (i = ag->num_devices - 1; i >= 0; i--) {
|
||||
ab = ag->ab[i];
|
||||
if (!ab)
|
||||
|
|
@ -1029,6 +1046,12 @@ static void ath12k_core_hw_group_stop(struct ath12k_hw_group *ag)
|
|||
ath12k_core_device_cleanup(ab);
|
||||
}
|
||||
|
||||
/* Unregister MAC (drops wiphys) only after per-device cleanup */
|
||||
ath12k_mac_unregister(ag);
|
||||
|
||||
/* Teardown MLO state after MAC unregister for symmetry */
|
||||
ath12k_mac_mlo_teardown(ag);
|
||||
|
||||
ath12k_mac_destroy(ag);
|
||||
}
|
||||
|
||||
|
|
@ -1165,26 +1188,11 @@ static int ath12k_core_hw_group_start(struct ath12k_hw_group *ag)
|
|||
if (!ab)
|
||||
continue;
|
||||
|
||||
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);
|
||||
mutex_unlock(&ab->core_lock);
|
||||
ret = ath12k_core_device_setup(ab);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
ath12k_hif_irq_enable(ab);
|
||||
|
||||
ret = ath12k_core_rfkill_config(ab);
|
||||
if (ret && ret != -EOPNOTSUPP) {
|
||||
mutex_unlock(&ab->core_lock);
|
||||
goto err;
|
||||
}
|
||||
|
||||
mutex_unlock(&ab->core_lock);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
|||
|
|
@ -542,8 +542,8 @@ struct ath12k_sta {
|
|||
#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_5GHZ_CHAN 173
|
||||
#define ATH12K_NUM_CHANS 102
|
||||
#define ATH12K_MAX_5GHZ_CHAN 177
|
||||
|
||||
static inline bool ath12k_is_2ghz_channel_freq(u32 freq)
|
||||
{
|
||||
|
|
@ -763,6 +763,14 @@ struct ath12k {
|
|||
struct ath12k_pdev_rssi_offsets rssi_info;
|
||||
|
||||
struct ath12k_thermal thermal;
|
||||
|
||||
/* Protected by ar->data_lock */
|
||||
struct ath12k_incumbent_signal_interference {
|
||||
u32 center_freq;
|
||||
enum nl80211_chan_width width;
|
||||
u32 chan_bw_interference_bitmap;
|
||||
bool handling_in_progress;
|
||||
} incumbent_signal_interference;
|
||||
};
|
||||
|
||||
struct ath12k_hw {
|
||||
|
|
|
|||
|
|
@ -1450,6 +1450,44 @@ static const struct file_operations fops_pdev_stats = {
|
|||
.llseek = default_llseek,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
ath12k_write_simulate_incumbent_signal_interference(struct file *file,
|
||||
const char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
struct ath12k *ar = file->private_data;
|
||||
struct ath12k_hw *ah = ath12k_ar_to_ah(ar);
|
||||
struct wiphy *wiphy = ath12k_ar_to_hw(ar)->wiphy;
|
||||
u32 chan_bw_interference_bitmap;
|
||||
int ret;
|
||||
|
||||
if (ah->state != ATH12K_HW_STATE_ON)
|
||||
return -ENETDOWN;
|
||||
|
||||
/*
|
||||
* Bitmap uses the firmware primary-based ordering documented in
|
||||
* ath12k_wmi_transform_interference_bitmap() & intf_map_80.
|
||||
*/
|
||||
if (kstrtou32_from_user(user_buf, count, 0, &chan_bw_interference_bitmap))
|
||||
return -EINVAL;
|
||||
|
||||
wiphy_lock(wiphy);
|
||||
ret = ath12k_wmi_simulate_incumbent_signal_interference(ar, chan_bw_interference_bitmap);
|
||||
if (ret)
|
||||
goto exit;
|
||||
|
||||
ret = count;
|
||||
|
||||
exit:
|
||||
wiphy_unlock(wiphy);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct file_operations fops_simulate_incumbent_signal_interference = {
|
||||
.write = ath12k_write_simulate_incumbent_signal_interference,
|
||||
.open = simple_open,
|
||||
};
|
||||
|
||||
static
|
||||
void ath12k_debugfs_fw_stats_register(struct ath12k *ar)
|
||||
{
|
||||
|
|
@ -1515,6 +1553,14 @@ void ath12k_debugfs_register(struct ath12k *ar)
|
|||
ar, &fops_tpc_stats_type);
|
||||
init_completion(&ar->debug.tpc_complete);
|
||||
|
||||
if (ar->mac.sbands[NL80211_BAND_6GHZ].channels &&
|
||||
test_bit(WMI_TLV_SERVICE_DCS_INCUMBENT_SIGNAL_INTERFERENCE_SUPPORT,
|
||||
ar->ab->wmi_ab.svc_map)) {
|
||||
debugfs_create_file("simulate_incumbent_signal_interference", 0200,
|
||||
ar->debug.debugfs_pdev, ar,
|
||||
&fops_simulate_incumbent_signal_interference);
|
||||
}
|
||||
|
||||
ath12k_debugfs_htt_stats_register(ar);
|
||||
ath12k_debugfs_fw_stats_register(ar);
|
||||
|
||||
|
|
|
|||
|
|
@ -17,6 +17,11 @@
|
|||
#include "dp_mon.h"
|
||||
#include "debugfs_htt_stats.h"
|
||||
|
||||
#define ATH12K_2GHZ_MIN_CHAN_NUM 1
|
||||
#define ATH12K_2GHZ_MAX_CHAN_NUM 14
|
||||
#define ATH12K_5GHZ_MIN_CHAN_NUM 36
|
||||
#define ATH12K_5GHZ_MAX_CHAN_NUM 177
|
||||
|
||||
static int ath12k_dp_rx_tid_delete_handler(struct ath12k_base *ab,
|
||||
struct ath12k_dp_rx_tid_rxq *rx_tid);
|
||||
|
||||
|
|
@ -1095,7 +1100,8 @@ static void ath12k_get_dot11_hdr_from_rx_desc(struct ath12k_pdev_dp *dp_pdev,
|
|||
static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev,
|
||||
struct sk_buff *msdu,
|
||||
enum hal_encrypt_type enctype,
|
||||
struct hal_rx_desc_data *rx_info)
|
||||
struct hal_rx_desc_data *rx_info,
|
||||
enum ath12k_dp_rx_decap_type decap_type)
|
||||
{
|
||||
struct ieee80211_hdr *hdr;
|
||||
struct ethhdr *eth;
|
||||
|
|
@ -1103,12 +1109,24 @@ static void ath12k_dp_rx_h_undecap_eth(struct ath12k_pdev_dp *dp_pdev,
|
|||
u8 sa[ETH_ALEN];
|
||||
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
|
||||
struct ath12k_dp_rx_rfc1042_hdr rfc = {0xaa, 0xaa, 0x03, {0x00, 0x00, 0x00}};
|
||||
struct ath12k_dp_rx_rfc1042_hdr *llc;
|
||||
|
||||
eth = (struct ethhdr *)msdu->data;
|
||||
ether_addr_copy(da, eth->h_dest);
|
||||
ether_addr_copy(sa, eth->h_source);
|
||||
rfc.snap_type = eth->h_proto;
|
||||
skb_pull(msdu, sizeof(*eth));
|
||||
if (decap_type == DP_RX_DECAP_TYPE_8023) {
|
||||
/*
|
||||
* For 802.3 frames, eth->h_proto carries a length field, not
|
||||
* an EtherType. The actual EtherType is in the LLC/SNAP header
|
||||
* that follows the Ethernet header.
|
||||
*/
|
||||
llc = (struct ath12k_dp_rx_rfc1042_hdr *)(msdu->data + sizeof(*eth));
|
||||
rfc.snap_type = llc->snap_type;
|
||||
skb_pull(msdu, sizeof(*eth) + sizeof(*llc));
|
||||
} else {
|
||||
rfc.snap_type = eth->h_proto;
|
||||
skb_pull(msdu, sizeof(*eth));
|
||||
}
|
||||
memcpy(skb_push(msdu, sizeof(rfc)), &rfc,
|
||||
sizeof(rfc));
|
||||
ath12k_get_dot11_hdr_from_rx_desc(dp_pdev, msdu, rxcb, enctype, rx_info);
|
||||
|
|
@ -1126,9 +1144,10 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu
|
|||
bool decrypted,
|
||||
struct hal_rx_desc_data *rx_info)
|
||||
{
|
||||
enum ath12k_dp_rx_decap_type decap_type = rx_info->decap_type;
|
||||
struct ethhdr *ehdr;
|
||||
|
||||
switch (rx_info->decap_type) {
|
||||
switch (decap_type) {
|
||||
case DP_RX_DECAP_TYPE_NATIVE_WIFI:
|
||||
ath12k_dp_rx_h_undecap_nwifi(dp_pdev, msdu, enctype, rx_info);
|
||||
break;
|
||||
|
|
@ -1142,19 +1161,33 @@ void ath12k_dp_rx_h_undecap(struct ath12k_pdev_dp *dp_pdev, struct sk_buff *msdu
|
|||
/* mac80211 allows fast path only for authorized STA */
|
||||
if (ehdr->h_proto == cpu_to_be16(ETH_P_PAE)) {
|
||||
ATH12K_SKB_RXCB(msdu)->is_eapol = true;
|
||||
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info);
|
||||
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
|
||||
decap_type);
|
||||
break;
|
||||
}
|
||||
|
||||
/* PN for mcast packets will be validated in mac80211;
|
||||
* remove eth header and add 802.11 header.
|
||||
*/
|
||||
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted)
|
||||
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info);
|
||||
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) {
|
||||
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
|
||||
decap_type);
|
||||
break;
|
||||
}
|
||||
|
||||
rx_info->rx_status->flag |= RX_FLAG_8023;
|
||||
break;
|
||||
case DP_RX_DECAP_TYPE_8023:
|
||||
/* TODO: Handle undecap for these formats */
|
||||
break;
|
||||
/*
|
||||
* Note that ethernet decap format indicates that the decapped
|
||||
* packet is either Ethernet 2 (DIX) or 802.3 (uses SNAP/LLC).
|
||||
*/
|
||||
if (ATH12K_SKB_RXCB(msdu)->is_mcbc && decrypted) {
|
||||
ath12k_dp_rx_h_undecap_eth(dp_pdev, msdu, enctype, rx_info,
|
||||
decap_type);
|
||||
break;
|
||||
}
|
||||
rx_info->rx_status->flag |= RX_FLAG_8023;
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(ath12k_dp_rx_h_undecap);
|
||||
|
|
@ -1289,9 +1322,11 @@ void ath12k_dp_rx_h_ppdu(struct ath12k_pdev_dp *dp_pdev,
|
|||
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) {
|
||||
} else if (channel_num >= ATH12K_2GHZ_MIN_CHAN_NUM &&
|
||||
channel_num <= ATH12K_2GHZ_MAX_CHAN_NUM) {
|
||||
rx_status->band = NL80211_BAND_2GHZ;
|
||||
} else if (channel_num >= 36 && channel_num <= 173) {
|
||||
} else if (channel_num >= ATH12K_5GHZ_MIN_CHAN_NUM &&
|
||||
channel_num <= ATH12K_5GHZ_MAX_CHAN_NUM) {
|
||||
rx_status->band = NL80211_BAND_5GHZ;
|
||||
}
|
||||
|
||||
|
|
@ -1336,9 +1371,7 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
|
|||
struct ath12k_dp_peer *peer;
|
||||
struct ath12k_skb_rxcb *rxcb = ATH12K_SKB_RXCB(msdu);
|
||||
struct ieee80211_rx_status *status = rx_info->rx_status;
|
||||
u8 decap = rx_info->decap_type;
|
||||
bool is_mcbc = rxcb->is_mcbc;
|
||||
bool is_eapol = rxcb->is_eapol;
|
||||
|
||||
peer = ath12k_dp_peer_find_by_peerid(dp_pdev, rxcb->peer_id);
|
||||
|
||||
|
|
@ -1383,15 +1416,6 @@ void ath12k_dp_rx_deliver_msdu(struct ath12k_pdev_dp *dp_pdev, struct napi_struc
|
|||
|
||||
/* TODO: trace rx packet */
|
||||
|
||||
/* PN for multicast packets are not validate in HW,
|
||||
* so skip 802.3 rx path
|
||||
* Also, fast_rx expects the STA to be authorized, hence
|
||||
* eapol packets are sent in slow path.
|
||||
*/
|
||||
if (decap == DP_RX_DECAP_TYPE_ETHERNET2_DIX && !is_eapol &&
|
||||
!(is_mcbc && rx_status->flag & RX_FLAG_DECRYPTED))
|
||||
rx_status->flag |= RX_FLAG_8023;
|
||||
|
||||
ieee80211_rx_napi(ath12k_pdev_dp_to_hw(dp_pdev), pubsta, msdu, napi);
|
||||
}
|
||||
EXPORT_SYMBOL(ath12k_dp_rx_deliver_msdu);
|
||||
|
|
|
|||
|
|
@ -51,6 +51,9 @@
|
|||
.max_power = 30, \
|
||||
}
|
||||
|
||||
#define ATH12K_5_9_GHZ_MIN_FREQ 5845
|
||||
#define ATH12K_5_9_GHZ_MAX_FREQ 5885
|
||||
|
||||
static const struct ieee80211_channel ath12k_2ghz_channels[] = {
|
||||
CHAN2G(1, 2412, 0),
|
||||
CHAN2G(2, 2417, 0),
|
||||
|
|
@ -96,6 +99,7 @@ static const struct ieee80211_channel ath12k_5ghz_channels[] = {
|
|||
CHAN5G(165, 5825, 0),
|
||||
CHAN5G(169, 5845, 0),
|
||||
CHAN5G(173, 5865, 0),
|
||||
CHAN5G(177, 5885, 0),
|
||||
};
|
||||
|
||||
static const struct ieee80211_channel ath12k_6ghz_channels[] = {
|
||||
|
|
@ -5615,12 +5619,14 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
|
|||
if (ret)
|
||||
goto exit;
|
||||
|
||||
arg = kzalloc_obj(*arg);
|
||||
arg = kzalloc_flex(*arg, chan_list, n_channels);
|
||||
if (!arg) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
arg->num_chan = n_channels;
|
||||
|
||||
ath12k_wmi_start_scan_init(ar, arg);
|
||||
arg->vdev_id = arvif->vdev_id;
|
||||
arg->scan_id = ATH12K_SCAN_ID;
|
||||
|
|
@ -5642,18 +5648,8 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
|
|||
arg->scan_f_passive = 1;
|
||||
}
|
||||
|
||||
if (n_channels) {
|
||||
arg->num_chan = n_channels;
|
||||
arg->chan_list = kcalloc(arg->num_chan, sizeof(*arg->chan_list),
|
||||
GFP_KERNEL);
|
||||
if (!arg->chan_list) {
|
||||
ret = -ENOMEM;
|
||||
goto exit;
|
||||
}
|
||||
|
||||
for (i = 0; i < arg->num_chan; i++)
|
||||
arg->chan_list[i] = chan_list[i]->center_freq;
|
||||
}
|
||||
for (i = 0; i < arg->num_chan; i++)
|
||||
arg->chan_list[i] = chan_list[i]->center_freq;
|
||||
|
||||
ret = ath12k_start_scan(ar, arg);
|
||||
if (ret) {
|
||||
|
|
@ -5678,7 +5674,6 @@ static int ath12k_mac_initiate_hw_scan(struct ieee80211_hw *hw,
|
|||
|
||||
exit:
|
||||
if (arg) {
|
||||
kfree(arg->chan_list);
|
||||
kfree(arg->extraie.ptr);
|
||||
kfree(arg);
|
||||
}
|
||||
|
|
@ -9650,6 +9645,10 @@ static int ath12k_mac_start(struct ath12k *ar)
|
|||
ar->allocated_vdev_map = 0;
|
||||
ar->chan_tx_pwr = ATH12K_PDEV_TX_POWER_INVALID;
|
||||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
ar->incumbent_signal_interference.handling_in_progress = false;
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
|
||||
/* Configure monitor status ring with default rx_filter to get rx status
|
||||
* such as rssi, rx_duration.
|
||||
*/
|
||||
|
|
@ -9677,6 +9676,12 @@ static int ath12k_mac_start(struct ath12k *ar)
|
|||
}
|
||||
}
|
||||
|
||||
ret = ath12k_thermal_throttling_config_default(ar);
|
||||
if (ret) {
|
||||
ath12k_err(ab, "failed to set thermal throttle: %d\n", ret);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rcu_assign_pointer(ab->pdevs_active[ar->pdev_idx],
|
||||
&ab->pdevs[ar->pdev_idx]);
|
||||
|
||||
|
|
@ -9857,6 +9862,10 @@ static void ath12k_mac_stop(struct ath12k *ar)
|
|||
synchronize_rcu();
|
||||
|
||||
atomic_set(&ar->num_pending_mgmt_tx, 0);
|
||||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
ar->incumbent_signal_interference.handling_in_progress = false;
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
}
|
||||
|
||||
void ath12k_mac_op_stop(struct ieee80211_hw *hw, bool suspend)
|
||||
|
|
@ -11443,8 +11452,10 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
|
|||
struct ieee80211_vif_chanctx_switch *vifs,
|
||||
int n_vifs)
|
||||
{
|
||||
struct ath12k_incumbent_signal_interference *incumbent;
|
||||
struct ath12k_wmi_vdev_up_params params = {};
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
struct cfg80211_chan_def *chandef;
|
||||
struct ath12k_base *ab = ar->ab;
|
||||
struct ath12k_link_vif *arvif;
|
||||
struct ieee80211_vif *vif;
|
||||
|
|
@ -11556,6 +11567,42 @@ ath12k_mac_update_vif_chan(struct ath12k *ar,
|
|||
if (!ath12k_mac_monitor_stop(ar))
|
||||
ath12k_mac_monitor_start(ar);
|
||||
}
|
||||
|
||||
incumbent = &ar->incumbent_signal_interference;
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
if (incumbent->handling_in_progress) {
|
||||
chandef = &vifs[0].new_ctx->def;
|
||||
if (incumbent->chan_bw_interference_bitmap &
|
||||
ATH12K_WMI_DCS_SEG_PRI20) {
|
||||
if (incumbent->center_freq !=
|
||||
chandef->chan->center_freq) {
|
||||
incumbent->chan_bw_interference_bitmap = 0;
|
||||
incumbent->handling_in_progress = false;
|
||||
ath12k_dbg(ab, ATH12K_DBG_MAC,
|
||||
"incumbent signal interference chan switch completed\n");
|
||||
} else {
|
||||
ath12k_warn(ab,
|
||||
"incumbent signal interference chan switch not done, freq %u\n",
|
||||
incumbent->center_freq);
|
||||
}
|
||||
} else {
|
||||
if (incumbent->center_freq !=
|
||||
chandef->chan->center_freq ||
|
||||
incumbent->width != chandef->width) {
|
||||
incumbent->chan_bw_interference_bitmap = 0;
|
||||
incumbent->handling_in_progress = false;
|
||||
ath12k_dbg(ab, ATH12K_DBG_MAC,
|
||||
"Bandwidth/channel change due to incumbent signal interference completed\n");
|
||||
} else {
|
||||
ath12k_warn(ab, "Bandwidth/channel change due to incumbent sig intf not done intf_freq %u chan_freq %u intf_width %u chan_width %u\n",
|
||||
incumbent->center_freq,
|
||||
chandef->chan->center_freq,
|
||||
incumbent->width,
|
||||
chandef->width);
|
||||
}
|
||||
}
|
||||
}
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -13739,19 +13786,13 @@ int ath12k_mac_op_remain_on_channel(struct ieee80211_hw *hw,
|
|||
scan_time_msec = hw->wiphy->max_remain_on_channel_duration * 2;
|
||||
|
||||
struct ath12k_wmi_scan_req_arg *arg __free(kfree) =
|
||||
kzalloc_obj(*arg);
|
||||
kzalloc_flex(*arg, chan_list, 1);
|
||||
if (!arg)
|
||||
return -ENOMEM;
|
||||
|
||||
ath12k_wmi_start_scan_init(ar, arg);
|
||||
arg->num_chan = 1;
|
||||
ath12k_wmi_start_scan_init(ar, arg);
|
||||
|
||||
u32 *chan_list __free(kfree) = kcalloc(arg->num_chan, sizeof(*chan_list),
|
||||
GFP_KERNEL);
|
||||
if (!chan_list)
|
||||
return -ENOMEM;
|
||||
|
||||
arg->chan_list = chan_list;
|
||||
arg->vdev_id = arvif->vdev_id;
|
||||
arg->scan_id = ATH12K_SCAN_ID;
|
||||
arg->chan_list[0] = chan->center_freq;
|
||||
|
|
@ -13904,6 +13945,26 @@ static int ath12k_mac_update_band(struct ath12k *ar,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void ath12k_mac_update_5_9_ghz_ch_list(struct ath12k *ar,
|
||||
struct ieee80211_supported_band *band)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (test_bit(WMI_TLV_SERVICE_5_9GHZ_SUPPORT,
|
||||
ar->ab->wmi_ab.svc_map))
|
||||
return;
|
||||
|
||||
guard(spinlock_bh)(&ar->ab->base_lock);
|
||||
if (ar->ab->dfs_region != ATH12K_DFS_REG_FCC)
|
||||
return;
|
||||
|
||||
for (i = 0; i < band->n_channels; i++) {
|
||||
if (band->channels[i].center_freq >= ATH12K_5_9_GHZ_MIN_FREQ &&
|
||||
band->channels[i].center_freq <= ATH12K_5_9_GHZ_MAX_FREQ)
|
||||
band->channels[i].flags |= IEEE80211_CHAN_DISABLED;
|
||||
}
|
||||
}
|
||||
|
||||
static int ath12k_mac_setup_channels_rates(struct ath12k *ar,
|
||||
u32 supported_bands,
|
||||
struct ieee80211_supported_band *bands[])
|
||||
|
|
@ -14037,6 +14098,8 @@ static int ath12k_mac_setup_channels_rates(struct ath12k *ar,
|
|||
band->n_bitrates = ath12k_a_rates_size;
|
||||
band->bitrates = ath12k_a_rates;
|
||||
|
||||
ath12k_mac_update_5_9_ghz_ch_list(ar, band);
|
||||
|
||||
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];
|
||||
|
|
@ -14465,6 +14528,8 @@ static int ath12k_mac_setup_register(struct ath12k *ar,
|
|||
ar->rssi_info.temp_offset = 0;
|
||||
ar->rssi_info.noise_floor = ar->rssi_info.min_nf_dbm + ar->rssi_info.temp_offset;
|
||||
|
||||
ath12k_thermal_init_configs(ar);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
@ -14817,6 +14882,7 @@ static void ath12k_mac_setup(struct ath12k *ar)
|
|||
init_completion(&ar->completed_11d_scan);
|
||||
init_completion(&ar->regd_update_completed);
|
||||
init_completion(&ar->thermal.wmi_sync);
|
||||
mutex_init(&ar->thermal.lock);
|
||||
|
||||
ar->thermal.temperature = 0;
|
||||
ar->thermal.hwmon_dev = NULL;
|
||||
|
|
|
|||
|
|
@ -1639,7 +1639,7 @@ static int ath12k_pci_probe(struct pci_dev *pdev,
|
|||
ret = ab_pci->device_family_ops->arch_init(ab);
|
||||
if (ret) {
|
||||
ath12k_err(ab, "PCI arch_init failed %d\n", ret);
|
||||
goto err_pci_msi_free;
|
||||
goto err_free_irq;
|
||||
}
|
||||
|
||||
ret = ath12k_core_init(ab);
|
||||
|
|
|
|||
|
|
@ -4061,7 +4061,7 @@ int ath12k_qmi_init_service(struct ath12k_base *ab)
|
|||
spin_lock_init(&ab->qmi.event_lock);
|
||||
INIT_WORK(&ab->qmi.event_work, ath12k_qmi_driver_event_work);
|
||||
|
||||
ret = qmi_add_lookup(&ab->qmi.handle, ATH12K_QMI_WLFW_SERVICE_ID_V01,
|
||||
ret = qmi_add_lookup(&ab->qmi.handle, QMI_SERVICE_ID_WLFW,
|
||||
ATH12K_QMI_WLFW_SERVICE_VERS_V01,
|
||||
ab->qmi.service_ins_id);
|
||||
if (ret < 0) {
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@
|
|||
#define ATH12K_QMI_MAX_BDF_FILE_NAME_SIZE 64
|
||||
#define ATH12K_QMI_CALDB_ADDRESS 0x4BA00000
|
||||
#define ATH12K_QMI_WLANFW_MAX_BUILD_ID_LEN_V01 128
|
||||
#define ATH12K_QMI_WLFW_SERVICE_ID_V01 0x45
|
||||
#define ATH12K_QMI_WLFW_SERVICE_VERS_V01 0x01
|
||||
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01 0x02
|
||||
#define ATH12K_QMI_WLFW_SERVICE_INS_ID_V01_WCN7850 0x1
|
||||
|
|
|
|||
|
|
@ -12,6 +12,137 @@
|
|||
#include "core.h"
|
||||
#include "debug.h"
|
||||
|
||||
static const struct ath12k_wmi_tt_level_config_param
|
||||
tt_level_configs[ATH12K_TT_CFG_IDX_MAX][ENHANCED_THERMAL_LEVELS] = {
|
||||
[ATH12K_TT_CFG_IDX_IPA] = {
|
||||
[0] = { .tmplwm = -100, .tmphwm = 115, .dcoffpercent = 0,
|
||||
.pout_reduction_db = 0 },
|
||||
[1] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 0,
|
||||
.pout_reduction_db = 12 },
|
||||
[2] = { .tmplwm = 115, .tmphwm = 125, .dcoffpercent = 50,
|
||||
.pout_reduction_db = 12 },
|
||||
[3] = { .tmplwm = 120, .tmphwm = 130, .dcoffpercent = 90,
|
||||
.pout_reduction_db = 12 },
|
||||
[4] = { .tmplwm = 125, .tmphwm = 130, .dcoffpercent = 100,
|
||||
.pout_reduction_db = 12 },
|
||||
},
|
||||
[ATH12K_TT_CFG_IDX_XFEM] = {
|
||||
[0] = { .tmplwm = -100, .tmphwm = 105, .dcoffpercent = 0,
|
||||
.pout_reduction_db = 0 },
|
||||
[1] = { .tmplwm = 100, .tmphwm = 110, .dcoffpercent = 0,
|
||||
.pout_reduction_db = 0 },
|
||||
[2] = { .tmplwm = 105, .tmphwm = 115, .dcoffpercent = 50,
|
||||
.pout_reduction_db = 0 },
|
||||
[3] = { .tmplwm = 110, .tmphwm = 120, .dcoffpercent = 90,
|
||||
.pout_reduction_db = 0 },
|
||||
[4] = { .tmplwm = 115, .tmphwm = 120, .dcoffpercent = 100,
|
||||
.pout_reduction_db = 0 },
|
||||
},
|
||||
};
|
||||
|
||||
static enum ath12k_thermal_cfg_idx ath12k_thermal_cfg_index(struct ath12k *ar)
|
||||
{
|
||||
if (test_bit(WMI_TLV_SERVICE_IS_TARGET_IPA, ar->ab->wmi_ab.svc_map))
|
||||
return ATH12K_TT_CFG_IDX_IPA;
|
||||
|
||||
return ATH12K_TT_CFG_IDX_XFEM;
|
||||
}
|
||||
|
||||
int ath12k_thermal_throttling_config_default(struct ath12k *ar)
|
||||
{
|
||||
struct ath12k_wmi_thermal_mitigation_arg param = {};
|
||||
int ret;
|
||||
|
||||
if (test_bit(WMI_TLV_SERVICE_THERM_THROT_5_LEVELS, ar->ab->wmi_ab.svc_map))
|
||||
param.num_levels = ENHANCED_THERMAL_LEVELS;
|
||||
else
|
||||
param.num_levels = THERMAL_LEVELS;
|
||||
|
||||
param.levelconf = ar->thermal.tt_level_configs;
|
||||
|
||||
ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m);
|
||||
if (ret)
|
||||
ath12k_warn(ar->ab,
|
||||
"failed to send thermal mitigation cmd for default config: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ath12k_thermal_init_configs(struct ath12k *ar)
|
||||
{
|
||||
enum ath12k_thermal_cfg_idx cfg_idx;
|
||||
|
||||
cfg_idx = ath12k_thermal_cfg_index(ar);
|
||||
ar->thermal.tt_level_configs = &tt_level_configs[cfg_idx][0];
|
||||
}
|
||||
|
||||
static int
|
||||
ath12k_thermal_get_max_throttle_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
*state = ATH12K_THERMAL_THROTTLE_MAX;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
ath12k_thermal_get_cur_throttle_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long *state)
|
||||
{
|
||||
struct ath12k *ar = cdev->devdata;
|
||||
|
||||
mutex_lock(&ar->thermal.lock);
|
||||
*state = ar->thermal.throttle_state;
|
||||
mutex_unlock(&ar->thermal.lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state)
|
||||
{
|
||||
struct ath12k_wmi_thermal_mitigation_arg param = {};
|
||||
struct ath12k_wmi_tt_level_config_param cfg = {};
|
||||
int ret;
|
||||
|
||||
param.num_levels = 1;
|
||||
cfg.dcoffpercent = throttle_state;
|
||||
param.levelconf = &cfg;
|
||||
|
||||
ret = ath12k_wmi_send_thermal_mitigation_cmd(ar, ¶m);
|
||||
if (ret)
|
||||
ath12k_warn(ar->ab, "failed to send thermal mitigation cmd: %d\n",
|
||||
ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
ath12k_thermal_set_cur_throttle_state(struct thermal_cooling_device *cdev,
|
||||
unsigned long throttle_state)
|
||||
{
|
||||
struct ath12k *ar = cdev->devdata;
|
||||
|
||||
if (throttle_state > ATH12K_THERMAL_THROTTLE_MAX)
|
||||
return -EINVAL;
|
||||
|
||||
scoped_guard(mutex, &ar->thermal.lock) {
|
||||
if (ar->thermal.throttle_state == throttle_state)
|
||||
return 0;
|
||||
ar->thermal.throttle_state = throttle_state;
|
||||
}
|
||||
|
||||
if (throttle_state == 0)
|
||||
return ath12k_thermal_throttling_config_default(ar);
|
||||
|
||||
return ath12k_thermal_set_throttling(ar, throttle_state);
|
||||
}
|
||||
|
||||
static const struct thermal_cooling_device_ops ath12k_thermal_ops = {
|
||||
.get_max_state = ath12k_thermal_get_max_throttle_state,
|
||||
.get_cur_state = ath12k_thermal_get_cur_throttle_state,
|
||||
.set_cur_state = ath12k_thermal_set_cur_throttle_state,
|
||||
};
|
||||
|
||||
static ssize_t ath12k_thermal_temp_show(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
|
@ -66,59 +197,108 @@ static struct attribute *ath12k_hwmon_attrs[] = {
|
|||
};
|
||||
ATTRIBUTE_GROUPS(ath12k_hwmon);
|
||||
|
||||
static int ath12k_thermal_setup_radio(struct ath12k_base *ab, int i)
|
||||
{
|
||||
char pdev_name[20];
|
||||
struct ath12k *ar;
|
||||
int ret;
|
||||
|
||||
ar = ab->pdevs[i].ar;
|
||||
if (!ar)
|
||||
return 0;
|
||||
|
||||
ar->thermal.cdev =
|
||||
thermal_cooling_device_register("ath12k_thermal", ar,
|
||||
&ath12k_thermal_ops);
|
||||
if (IS_ERR(ar->thermal.cdev)) {
|
||||
ret = PTR_ERR(ar->thermal.cdev);
|
||||
ar->thermal.cdev = NULL;
|
||||
ath12k_err(ar->ab, "failed to register cooling device: %d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u",
|
||||
ar->hw_link_id);
|
||||
|
||||
ret = sysfs_create_link(&ar->ah->hw->wiphy->dev.kobj,
|
||||
&ar->thermal.cdev->device.kobj, pdev_name);
|
||||
if (ret) {
|
||||
ath12k_err(ab, "failed to create cooling device symlink: %d\n",
|
||||
ret);
|
||||
goto unregister_cdev;
|
||||
}
|
||||
|
||||
ar->thermal.hwmon_dev =
|
||||
hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
|
||||
"ath12k_hwmon", ar,
|
||||
ath12k_hwmon_groups);
|
||||
if (IS_ERR(ar->thermal.hwmon_dev)) {
|
||||
ret = PTR_ERR(ar->thermal.hwmon_dev);
|
||||
ar->thermal.hwmon_dev = NULL;
|
||||
ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
|
||||
ret);
|
||||
goto remove_sysfs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
remove_sysfs:
|
||||
sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name);
|
||||
unregister_cdev:
|
||||
thermal_cooling_device_unregister(ar->thermal.cdev);
|
||||
ar->thermal.cdev = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ath12k_thermal_cleanup_radio(struct ath12k_base *ab, int i)
|
||||
{
|
||||
char pdev_name[20];
|
||||
struct ath12k *ar;
|
||||
|
||||
ar = ab->pdevs[i].ar;
|
||||
if (!ar)
|
||||
return;
|
||||
|
||||
hwmon_device_unregister(ar->thermal.hwmon_dev);
|
||||
ar->thermal.hwmon_dev = NULL;
|
||||
|
||||
scnprintf(pdev_name, sizeof(pdev_name), "cooling_device%u",
|
||||
ar->hw_link_id);
|
||||
sysfs_remove_link(&ar->ah->hw->wiphy->dev.kobj, pdev_name);
|
||||
|
||||
thermal_cooling_device_unregister(ar->thermal.cdev);
|
||||
ar->thermal.cdev = NULL;
|
||||
}
|
||||
|
||||
int ath12k_thermal_register(struct ath12k_base *ab)
|
||||
{
|
||||
struct ath12k *ar;
|
||||
int i, j, ret;
|
||||
int i, ret;
|
||||
|
||||
if (!IS_REACHABLE(CONFIG_HWMON))
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < ab->num_radios; i++) {
|
||||
ar = ab->pdevs[i].ar;
|
||||
if (!ar)
|
||||
continue;
|
||||
|
||||
ar->thermal.hwmon_dev =
|
||||
hwmon_device_register_with_groups(&ar->ah->hw->wiphy->dev,
|
||||
"ath12k_hwmon", ar,
|
||||
ath12k_hwmon_groups);
|
||||
if (IS_ERR(ar->thermal.hwmon_dev)) {
|
||||
ret = PTR_ERR(ar->thermal.hwmon_dev);
|
||||
ar->thermal.hwmon_dev = NULL;
|
||||
ath12k_err(ar->ab, "failed to register hwmon device: %d\n",
|
||||
ret);
|
||||
for (j = i - 1; j >= 0; j--) {
|
||||
ar = ab->pdevs[j].ar;
|
||||
if (!ar)
|
||||
continue;
|
||||
|
||||
hwmon_device_unregister(ar->thermal.hwmon_dev);
|
||||
ar->thermal.hwmon_dev = NULL;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
ret = ath12k_thermal_setup_radio(ab, i);
|
||||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
|
||||
return 0;
|
||||
out:
|
||||
for (i--; i >= 0; i--)
|
||||
ath12k_thermal_cleanup_radio(ab, i);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void ath12k_thermal_unregister(struct ath12k_base *ab)
|
||||
{
|
||||
struct ath12k *ar;
|
||||
int i;
|
||||
|
||||
if (!IS_REACHABLE(CONFIG_HWMON))
|
||||
return;
|
||||
|
||||
for (i = 0; i < ab->num_radios; i++) {
|
||||
ar = ab->pdevs[i].ar;
|
||||
if (!ar)
|
||||
continue;
|
||||
|
||||
if (ar->thermal.hwmon_dev) {
|
||||
hwmon_device_unregister(ar->thermal.hwmon_dev);
|
||||
ar->thermal.hwmon_dev = NULL;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < ab->num_radios; i++)
|
||||
ath12k_thermal_cleanup_radio(ab, i);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,20 +7,41 @@
|
|||
#ifndef _ATH12K_THERMAL_
|
||||
#define _ATH12K_THERMAL_
|
||||
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define ATH12K_THERMAL_SYNC_TIMEOUT_HZ (5 * HZ)
|
||||
|
||||
#define ATH12K_THERMAL_DEFAULT_DUTY_CYCLE 100
|
||||
#define ATH12K_THERMAL_THROTTLE_MAX 100
|
||||
|
||||
enum ath12k_thermal_cfg_idx {
|
||||
/* Internal Power Amplifier Device */
|
||||
ATH12K_TT_CFG_IDX_IPA,
|
||||
/* External Power Amplifier Device or External Front End Module */
|
||||
ATH12K_TT_CFG_IDX_XFEM,
|
||||
ATH12K_TT_CFG_IDX_MAX,
|
||||
};
|
||||
|
||||
struct ath12k_thermal {
|
||||
struct completion wmi_sync;
|
||||
|
||||
/* temperature value in Celsius degree protected by data_lock. */
|
||||
int temperature;
|
||||
struct device *hwmon_dev;
|
||||
const struct ath12k_wmi_tt_level_config_param *tt_level_configs;
|
||||
struct thermal_cooling_device *cdev;
|
||||
/* Serialize thermal operations and hwmon reads */
|
||||
struct mutex lock;
|
||||
u32 throttle_state;
|
||||
};
|
||||
|
||||
#if IS_REACHABLE(CONFIG_THERMAL)
|
||||
int ath12k_thermal_register(struct ath12k_base *ab);
|
||||
void ath12k_thermal_unregister(struct ath12k_base *ab);
|
||||
void ath12k_thermal_event_temperature(struct ath12k *ar, int temperature);
|
||||
int ath12k_thermal_throttling_config_default(struct ath12k *ar);
|
||||
void ath12k_thermal_init_configs(struct ath12k *ar);
|
||||
int ath12k_thermal_set_throttling(struct ath12k *ar, u32 throttle_state);
|
||||
#else
|
||||
static inline int ath12k_thermal_register(struct ath12k_base *ab)
|
||||
{
|
||||
|
|
@ -36,5 +57,19 @@ static inline void ath12k_thermal_event_temperature(struct ath12k *ar,
|
|||
{
|
||||
}
|
||||
|
||||
static inline int ath12k_thermal_throttling_config_default(struct ath12k *ar)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ath12k_thermal_init_configs(struct ath12k *ar)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int ath12k_thermal_set_throttling(struct ath12k *ar,
|
||||
u32 throttle_state)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
#endif /* _ATH12K_THERMAL_ */
|
||||
|
|
|
|||
|
|
@ -234,6 +234,68 @@ static const int ath12k_hw_mode_pri_map[] = {
|
|||
PRIMAP(WMI_HOST_HW_MODE_MAX),
|
||||
};
|
||||
|
||||
/*
|
||||
* Interference bitmap transform maps used by
|
||||
* ath12k_wmi_transform_interference_bitmap().
|
||||
*
|
||||
* Firmware reports bitmap bits in a primary-based order where:
|
||||
* - bit 0 is always the primary 20 MHz segment,
|
||||
* - bit 1 is the adjacent 20 MHz in the same 40 MHz block,
|
||||
* - bit 2 is the lower 20 MHz segment of the adjacent 40 MHz segment
|
||||
* - bit 3 is the higher 20 MHz segment of the adjacent 40 MHz segment
|
||||
* - remaining bits continue outward in 80/160/320 MHz groups.
|
||||
*
|
||||
* cfg80211 userspace notification expects absolute frequency order where:
|
||||
* - bit 0 is the lowest-frequency 20 MHz segment in the current chandef,
|
||||
* - bit N increases monotonically toward higher frequency.
|
||||
*
|
||||
* For each bandwidth-specific map:
|
||||
* - row index = primary 20 MHz index in absolute (low->high) order,
|
||||
* - column index = source bit position from firmware bitmap,
|
||||
* - value = destination bit position in absolute order bitmap.
|
||||
*
|
||||
* Example for 80 MHz: if primary index is 2 (third 20 MHz chunk from low
|
||||
* frequency), row intf_map_80[2] = { 2, 3, 0, 1 } means firmware bits {0,1,2,3}
|
||||
* are remapped to destination bits {2,3,0,1} before notifying cfg80211.
|
||||
*/
|
||||
|
||||
static const int intf_map_80[4][4] = {
|
||||
{ 0, 1, 2, 3 },
|
||||
{ 1, 0, 2, 3 },
|
||||
{ 2, 3, 0, 1 },
|
||||
{ 3, 2, 0, 1 }
|
||||
};
|
||||
|
||||
static const int intf_map_160[8][8] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 1, 0, 2, 3, 4, 5, 6, 7 },
|
||||
{ 2, 3, 0, 1, 4, 5, 6, 7 },
|
||||
{ 3, 2, 0, 1, 4, 5, 6, 7 },
|
||||
{ 4, 5, 6, 7, 0, 1, 2, 3 },
|
||||
{ 5, 4, 6, 7, 0, 1, 2, 3 },
|
||||
{ 6, 7, 4, 5, 0, 1, 2, 3 },
|
||||
{ 7, 6, 4, 5, 0, 1, 2, 3 }
|
||||
};
|
||||
|
||||
static const int intf_map_320[16][16] = {
|
||||
{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 2, 3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 3, 2, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 5, 4, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 6, 7, 4, 5, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 7, 6, 4, 5, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15 },
|
||||
{ 8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 9, 8, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 10, 11, 8, 9, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 11, 10, 8, 9, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 12, 13, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 13, 12, 14, 15, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 14, 15, 12, 13, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 },
|
||||
{ 15, 14, 12, 13, 8, 9, 10, 11, 0, 1, 2, 3, 4, 5, 6, 7 }
|
||||
};
|
||||
|
||||
static int
|
||||
ath12k_wmi_tlv_iter(struct ath12k_base *ab, const void *ptr, size_t len,
|
||||
int (*iter)(struct ath12k_base *ab, u16 tag, u16 len,
|
||||
|
|
@ -3380,6 +3442,75 @@ int ath12k_wmi_send_set_current_country_cmd(struct ath12k *ar,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int
|
||||
ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar,
|
||||
struct ath12k_wmi_thermal_mitigation_arg *arg)
|
||||
{
|
||||
struct ath12k_wmi_therm_throt_level_config_param *lvl_conf;
|
||||
struct ath12k_wmi_therm_throt_config_request_cmd *cmd;
|
||||
struct ath12k_wmi_pdev *wmi = ar->wmi;
|
||||
struct wmi_tlv *tlv;
|
||||
struct sk_buff *skb;
|
||||
int i, ret, len;
|
||||
|
||||
len = sizeof(*cmd) + TLV_HDR_SIZE + (arg->num_levels * sizeof(*lvl_conf));
|
||||
|
||||
skb = ath12k_wmi_alloc_skb(wmi->wmi_ab, len);
|
||||
if (!skb)
|
||||
return -ENOMEM;
|
||||
|
||||
cmd = (struct ath12k_wmi_therm_throt_config_request_cmd *)skb->data;
|
||||
cmd->tlv_header = ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_CONFIG_REQUEST,
|
||||
sizeof(*cmd));
|
||||
cmd->pdev_id = cpu_to_le32(ar->pdev->pdev_id);
|
||||
cmd->enable = cpu_to_le32(1);
|
||||
cmd->dc = cpu_to_le32(100);
|
||||
cmd->dc_per_event = cpu_to_le32(0xffffffff);
|
||||
cmd->therm_throt_levels = cpu_to_le32(arg->num_levels);
|
||||
|
||||
tlv = (struct wmi_tlv *)(skb->data + sizeof(*cmd));
|
||||
tlv->header = ath12k_wmi_tlv_hdr(WMI_TAG_ARRAY_STRUCT,
|
||||
arg->num_levels * sizeof(*lvl_conf));
|
||||
|
||||
lvl_conf = (struct ath12k_wmi_therm_throt_level_config_param *)tlv->value;
|
||||
|
||||
for (i = 0; i < arg->num_levels; i++) {
|
||||
lvl_conf->tlv_header =
|
||||
ath12k_wmi_tlv_cmd_hdr(WMI_TAG_THERM_THROT_LEVEL_CONFIG_INFO,
|
||||
sizeof(*lvl_conf));
|
||||
|
||||
lvl_conf->temp_lwm = a_cpu_to_sle32(arg->levelconf[i].tmplwm);
|
||||
lvl_conf->temp_hwm = a_cpu_to_sle32(arg->levelconf[i].tmphwm);
|
||||
lvl_conf->dc_off_percent = cpu_to_le32(arg->levelconf[i].dcoffpercent);
|
||||
|
||||
if (test_bit(WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION,
|
||||
ar->ab->wmi_ab.svc_map))
|
||||
lvl_conf->pout_reduction_25db =
|
||||
cpu_to_le32(arg->levelconf[i].pout_reduction_db);
|
||||
|
||||
if (test_bit(WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK,
|
||||
ar->ab->wmi_ab.svc_map))
|
||||
lvl_conf->tx_chain_mask = cpu_to_le32(ar->cfg_tx_chainmask);
|
||||
|
||||
lvl_conf->duty_cycle = cpu_to_le32(ATH12K_THERMAL_DEFAULT_DUTY_CYCLE);
|
||||
lvl_conf++;
|
||||
}
|
||||
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"WMI vdev set thermal throt pdev_id %u enable dc 100 dc_per_event 0xffffffff levels %d\n",
|
||||
ar->pdev->pdev_id, arg->num_levels);
|
||||
|
||||
ret = ath12k_wmi_cmd_send(wmi, skb, WMI_THERM_THROT_SET_CONF_CMDID);
|
||||
if (ret) {
|
||||
ath12k_warn(ar->ab,
|
||||
"failed to send WMI_THERM_THROT_SET_CONF cmd: %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)
|
||||
{
|
||||
|
|
@ -4199,12 +4330,9 @@ int ath12k_wmi_pdev_lro_cfg(struct ath12k *ar,
|
|||
if (ret) {
|
||||
ath12k_warn(ar->ab,
|
||||
"failed to send lro cfg req wmi cmd\n");
|
||||
goto err;
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -4335,12 +4463,9 @@ int ath12k_wmi_vdev_spectral_conf(struct ath12k *ar,
|
|||
if (ret) {
|
||||
ath12k_warn(ar->ab,
|
||||
"failed to send spectral scan config wmi cmd\n");
|
||||
goto err;
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -4372,12 +4497,9 @@ int ath12k_wmi_vdev_spectral_enable(struct ath12k *ar, u32 vdev_id,
|
|||
if (ret) {
|
||||
ath12k_warn(ar->ab,
|
||||
"failed to send spectral enable wmi cmd\n");
|
||||
goto err;
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -4418,12 +4540,9 @@ int ath12k_wmi_pdev_dma_ring_cfg(struct ath12k *ar,
|
|||
if (ret) {
|
||||
ath12k_warn(ar->ab,
|
||||
"failed to send dma ring cfg req wmi cmd\n");
|
||||
goto err;
|
||||
dev_kfree_skb(skb);
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
@ -8540,6 +8659,330 @@ static void ath12k_pdev_ctl_failsafe_check_event(struct ath12k_base *ab,
|
|||
ev->ctl_failsafe_status);
|
||||
}
|
||||
|
||||
static int
|
||||
ath12k_wmi_incumbent_signal_interference_subtlv_parser(struct ath12k_base *ab,
|
||||
u16 tag, u16 len,
|
||||
const void *ptr,
|
||||
void *data)
|
||||
{
|
||||
const struct ath12k_wmi_incumbent_signal_interference_params *info;
|
||||
struct ath12k_wmi_incumbent_signal_interference_arg *arg = data;
|
||||
|
||||
switch (tag) {
|
||||
case WMI_TAG_DCS_INCUMBENT_SIGNAL_INTERFERENCE_TYPE:
|
||||
if (len < sizeof(*info)) {
|
||||
ath12k_warn(ab,
|
||||
"DCS incumbent signal interference subtlv 0x%x invalid len %u\n",
|
||||
tag, len);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
info = ptr;
|
||||
|
||||
arg->chan_width = le32_to_cpu(info->chan_width);
|
||||
arg->chan_freq = le32_to_cpu(info->chan_freq);
|
||||
arg->center_freq0 = le32_to_cpu(info->center_freq0);
|
||||
arg->center_freq1 = le32_to_cpu(info->center_freq1);
|
||||
arg->chan_bw_interference_bitmap =
|
||||
le32_to_cpu(info->chan_bw_interference_bitmap);
|
||||
|
||||
ath12k_dbg(ab, ATH12K_DBG_WMI,
|
||||
"incumbent signal interference chan width %u freq %u center_freq0 %u center_freq1 %u bitmap 0x%x\n",
|
||||
arg->chan_width, arg->chan_freq,
|
||||
arg->center_freq0, arg->center_freq1,
|
||||
arg->chan_bw_interference_bitmap);
|
||||
break;
|
||||
default:
|
||||
ath12k_warn(ab, "Received invalid tag 0x%x for WMI DCS interference in subtlvs\n",
|
||||
tag);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ath12k_wmi_dcs_interference_event_parser(struct ath12k_base *ab,
|
||||
u16 tag, u16 len,
|
||||
const void *ptr, void *data)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
switch (tag) {
|
||||
case WMI_TAG_DCS_INTERFERENCE_EVENT:
|
||||
/* Fixed param should already be processed */
|
||||
break;
|
||||
case WMI_TAG_ARRAY_STRUCT:
|
||||
ret = ath12k_wmi_tlv_iter(ab, ptr, len,
|
||||
ath12k_wmi_incumbent_signal_interference_subtlv_parser,
|
||||
data);
|
||||
break;
|
||||
default:
|
||||
ath12k_warn(ab, "Received invalid tag 0x%x for WMI DCS interference event\n",
|
||||
tag);
|
||||
ret = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool
|
||||
ath12k_wmi_validate_interference_info(struct ath12k *ar,
|
||||
struct ath12k_wmi_incumbent_signal_interference_arg *info)
|
||||
{
|
||||
switch (info->chan_width) {
|
||||
case WMI_CHAN_WIDTH_20:
|
||||
if (info->chan_bw_interference_bitmap > ATH12K_WMI_DCS_SEG_PRI20) {
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"DCS interference event received with wrong chan width bmap 0x%x for 20 MHz",
|
||||
info->chan_bw_interference_bitmap);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case WMI_CHAN_WIDTH_40:
|
||||
if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 |
|
||||
ATH12K_WMI_DCS_SEG_SEC20)) {
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"DCS interference event received with wrong chan width bmap 0x%x for 40 MHz",
|
||||
info->chan_bw_interference_bitmap);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case WMI_CHAN_WIDTH_80:
|
||||
if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 |
|
||||
ATH12K_WMI_DCS_SEG_SEC20 |
|
||||
ATH12K_WMI_DCS_SEG_SEC40)) {
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"DCS interference event received with wrong chan width bmap 0x%x for 80 MHz",
|
||||
info->chan_bw_interference_bitmap);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case WMI_CHAN_WIDTH_160:
|
||||
if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 |
|
||||
ATH12K_WMI_DCS_SEG_SEC20 |
|
||||
ATH12K_WMI_DCS_SEG_SEC40 |
|
||||
ATH12K_WMI_DCS_SEG_SEC80)) {
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"DCS interference event received with wrong chan width bmap 0x%x for 160 MHz",
|
||||
info->chan_bw_interference_bitmap);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case WMI_CHAN_WIDTH_320:
|
||||
if (info->chan_bw_interference_bitmap > (ATH12K_WMI_DCS_SEG_PRI20 |
|
||||
ATH12K_WMI_DCS_SEG_SEC20 |
|
||||
ATH12K_WMI_DCS_SEG_SEC40 |
|
||||
ATH12K_WMI_DCS_SEG_SEC80 |
|
||||
ATH12K_WMI_DCS_SEG_SEC160)) {
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"DCS interference event received with wrong chan width bmap 0x%x for 320 MHz",
|
||||
info->chan_bw_interference_bitmap);
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"DCS interference event received with unknown channel width %u",
|
||||
info->chan_width);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32
|
||||
ath12k_wmi_transform_interference_bitmap(int input_bitmap,
|
||||
struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
u16 output_bits[ATH12K_MAX_20MHZ_SEGMENTS] = {};
|
||||
u16 input_bits[ATH12K_MAX_20MHZ_SEGMENTS] = {};
|
||||
u32 start_freq, segment_freq;
|
||||
int primary_index = -1;
|
||||
u32 output_bitmap = 0;
|
||||
u16 num_sub_chans;
|
||||
int bandwidth;
|
||||
|
||||
bandwidth = nl80211_chan_width_to_mhz(chandef->width);
|
||||
if (bandwidth < 0)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Firmware reports bit 0 as primary 20 MHz irrespective of absolute
|
||||
* frequency position. Convert to standardized lowest-to-highest 20 MHz
|
||||
* ordering expected by cfg80211/mac80211 userspace consumers.
|
||||
*/
|
||||
num_sub_chans = bandwidth / 20;
|
||||
start_freq = (chandef->center_freq1 - bandwidth / 2) + 10;
|
||||
|
||||
for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; i++) {
|
||||
segment_freq = start_freq + (i * 20);
|
||||
if (segment_freq == chandef->chan->center_freq) {
|
||||
primary_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (primary_index == -1)
|
||||
return 0;
|
||||
|
||||
for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; ++i)
|
||||
input_bits[i] = BIT(i) & input_bitmap;
|
||||
|
||||
for (int i = 0; i < num_sub_chans; ++i) {
|
||||
int src = i, dst = i;
|
||||
|
||||
switch (bandwidth) {
|
||||
case 40:
|
||||
if (primary_index == 1)
|
||||
dst = 1 - i;
|
||||
break;
|
||||
case 80:
|
||||
dst = intf_map_80[primary_index][i];
|
||||
break;
|
||||
case 160:
|
||||
dst = intf_map_160[primary_index][i];
|
||||
break;
|
||||
case 320:
|
||||
dst = intf_map_320[primary_index][i];
|
||||
break;
|
||||
}
|
||||
output_bits[dst] = input_bits[src];
|
||||
}
|
||||
|
||||
for (int i = 0; i < ATH12K_MAX_20MHZ_SEGMENTS; ++i)
|
||||
output_bitmap |= output_bits[i] ? BIT(i) : 0;
|
||||
|
||||
return output_bitmap;
|
||||
}
|
||||
|
||||
static void
|
||||
ath12k_wmi_process_incumbent_signal_interference_evt(struct ath12k_base *ab,
|
||||
struct sk_buff *skb,
|
||||
const struct ath12k_wmi_intf_arg *intf_arg)
|
||||
{
|
||||
struct ath12k_wmi_incumbent_signal_interference_arg info = {};
|
||||
struct ath12k_incumbent_signal_interference *incumbent;
|
||||
struct ath12k_mac_get_any_chanctx_conf_arg arg;
|
||||
u32 transformed_intf_bitmap;
|
||||
struct ieee80211_hw *hw;
|
||||
struct ath12k *ar;
|
||||
int ret;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
ar = ath12k_mac_get_ar_by_pdev_id(ab, intf_arg->pdev_id);
|
||||
if (!ar) {
|
||||
ath12k_warn(ab, "incumbent signal interference detected on invalid pdev %d\n",
|
||||
intf_arg->pdev_id);
|
||||
return;
|
||||
}
|
||||
if (!ar->supports_6ghz) {
|
||||
ath12k_warn(ab, "pdev does not support 6 GHz, dropping DCS interference event\n");
|
||||
return;
|
||||
}
|
||||
|
||||
incumbent = &ar->incumbent_signal_interference;
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
if (incumbent->handling_in_progress) {
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"incumbent signal interference handling ongoing, dropping DCS interference event");
|
||||
return;
|
||||
}
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
|
||||
ret = ath12k_wmi_tlv_iter(ab, skb->data, skb->len,
|
||||
ath12k_wmi_dcs_interference_event_parser,
|
||||
&info);
|
||||
if (ret) {
|
||||
ath12k_warn(ab,
|
||||
"failed to parse incumbent signal interference TLV. Error %d\n",
|
||||
ret);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ath12k_wmi_validate_interference_info(ar, &info)) {
|
||||
ath12k_warn(ab, "invalid DCS incumbent signal interference TLV - Skipping event");
|
||||
return;
|
||||
}
|
||||
|
||||
arg.ar = ar;
|
||||
arg.chanctx_conf = NULL;
|
||||
hw = ath12k_ar_to_hw(ar);
|
||||
ieee80211_iter_chan_contexts_atomic(hw,
|
||||
ath12k_mac_get_any_chanctx_conf_iter,
|
||||
&arg);
|
||||
if (!arg.chanctx_conf) {
|
||||
ath12k_warn(ab, "failed to find valid chanctx_conf in incumbent signal intf detected event\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (info.chan_freq != arg.chanctx_conf->def.chan->center_freq) {
|
||||
ath12k_dbg(ab, ATH12K_DBG_WMI,
|
||||
"dcs interference event received with wrong channel %d (ctx freq %d)",
|
||||
info.chan_freq, arg.chanctx_conf->def.chan->center_freq);
|
||||
return;
|
||||
}
|
||||
|
||||
spin_lock_bh(&ar->data_lock);
|
||||
incumbent->center_freq = arg.chanctx_conf->def.chan->center_freq;
|
||||
incumbent->width = arg.chanctx_conf->def.width;
|
||||
incumbent->chan_bw_interference_bitmap = info.chan_bw_interference_bitmap;
|
||||
incumbent->handling_in_progress = true;
|
||||
spin_unlock_bh(&ar->data_lock);
|
||||
transformed_intf_bitmap =
|
||||
ath12k_wmi_transform_interference_bitmap(info.chan_bw_interference_bitmap,
|
||||
&arg.chanctx_conf->def);
|
||||
ath12k_dbg(ab, ATH12K_DBG_WMI,
|
||||
"incumbent signal interference bitmap 0x%x (transformed 0x%x)\n",
|
||||
info.chan_bw_interference_bitmap, transformed_intf_bitmap);
|
||||
cfg80211_incumbent_signal_notify(hw->wiphy,
|
||||
&arg.chanctx_conf->def,
|
||||
transformed_intf_bitmap,
|
||||
GFP_ATOMIC);
|
||||
}
|
||||
|
||||
static void
|
||||
ath12k_wmi_dcs_interference_event(struct ath12k_base *ab,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
const struct ath12k_wmi_dcs_interference_ev_fixed_params *dcs_intf_ev;
|
||||
struct ath12k_wmi_intf_arg dcs_intf_arg;
|
||||
const struct wmi_tlv *tlv;
|
||||
u16 tlv_tag;
|
||||
u8 *ptr;
|
||||
|
||||
if (skb->len < (sizeof(*dcs_intf_ev) + TLV_HDR_SIZE)) {
|
||||
ath12k_warn(ab, "DCS interference event is of incorrect length\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = skb->data;
|
||||
tlv = (struct wmi_tlv *)ptr;
|
||||
tlv_tag = le32_get_bits(tlv->header, WMI_TLV_TAG);
|
||||
ptr += sizeof(*tlv);
|
||||
|
||||
if (tlv_tag != WMI_TAG_DCS_INTERFERENCE_EVENT) {
|
||||
ath12k_warn(ab, "DCS interference event received with wrong tag\n");
|
||||
return;
|
||||
}
|
||||
|
||||
dcs_intf_ev = (struct ath12k_wmi_dcs_interference_ev_fixed_params *)ptr;
|
||||
|
||||
dcs_intf_arg.interference_type =
|
||||
le32_to_cpu(dcs_intf_ev->interference_type);
|
||||
dcs_intf_arg.pdev_id = le32_to_cpu(dcs_intf_ev->pdev_id);
|
||||
|
||||
if (dcs_intf_arg.interference_type ==
|
||||
ATH12K_WMI_DCS_INCUMBENT_SIGNAL_INTERFERENCE) {
|
||||
ath12k_dbg(ab, ATH12K_DBG_WMI,
|
||||
"incumbent signal interference (Type %u) detected on pdev %u.",
|
||||
dcs_intf_arg.interference_type,
|
||||
dcs_intf_arg.pdev_id);
|
||||
ath12k_wmi_process_incumbent_signal_interference_evt(ab, skb,
|
||||
&dcs_intf_arg);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
ath12k_wmi_process_csa_switch_count_event(struct ath12k_base *ab,
|
||||
const struct ath12k_wmi_pdev_csa_event *ev,
|
||||
|
|
@ -8762,6 +9205,42 @@ ath12k_wmi_pdev_temperature_event(struct ath12k_base *ab,
|
|||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
static void ath12k_wmi_thermal_throt_stats_event(struct ath12k_base *ab,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
const struct wmi_therm_throt_stats_event *ev;
|
||||
struct ath12k *ar;
|
||||
const void **tb;
|
||||
|
||||
tb = ath12k_wmi_tlv_parse(ab, skb);
|
||||
if (IS_ERR(tb)) {
|
||||
ath12k_err(ab, "failed to parse thermal throttling stats tlv: %ld\n",
|
||||
PTR_ERR(tb));
|
||||
return;
|
||||
}
|
||||
|
||||
ev = tb[WMI_TAG_THERM_THROT_STATS_EVENT];
|
||||
if (!ev) {
|
||||
ath12k_err(ab, "failed to fetch thermal throt stats ev\n");
|
||||
return;
|
||||
}
|
||||
|
||||
rcu_read_lock();
|
||||
ar = ath12k_mac_get_ar_by_pdev_id(ab, le32_to_cpu(ev->pdev_id));
|
||||
if (!ar) {
|
||||
ath12k_warn(ab, "received thermal_throt_stats in invalid pdev %u\n",
|
||||
le32_to_cpu(ev->pdev_id));
|
||||
rcu_read_unlock();
|
||||
return;
|
||||
}
|
||||
rcu_read_unlock();
|
||||
|
||||
ath12k_dbg(ab, ATH12K_DBG_WMI,
|
||||
"thermal stats ev level %u pdev_id %u temp %u throt_levels %u\n",
|
||||
le32_to_cpu(ev->level), le32_to_cpu(ev->pdev_id),
|
||||
le32_to_cpu(ev->temp), le32_to_cpu(ev->therm_throt_levels));
|
||||
}
|
||||
|
||||
static void ath12k_fils_discovery_event(struct ath12k_base *ab,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
|
|
@ -9811,6 +10290,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
|
|||
case WMI_PDEV_TEMPERATURE_EVENTID:
|
||||
ath12k_wmi_pdev_temperature_event(ab, skb);
|
||||
break;
|
||||
case WMI_THERM_THROT_STATS_EVENTID:
|
||||
ath12k_wmi_thermal_throt_stats_event(ab, skb);
|
||||
break;
|
||||
case WMI_PDEV_DMA_RING_BUF_RELEASE_EVENTID:
|
||||
ath12k_wmi_pdev_dma_ring_buf_release_event(ab, skb);
|
||||
break;
|
||||
|
|
@ -9865,6 +10347,9 @@ static void ath12k_wmi_op_rx(struct ath12k_base *ab, struct sk_buff *skb)
|
|||
case WMI_OBSS_COLOR_COLLISION_DETECTION_EVENTID:
|
||||
ath12k_wmi_obss_color_collision_event(ab, skb);
|
||||
break;
|
||||
case WMI_DCS_INTERFERENCE_EVENTID:
|
||||
ath12k_wmi_dcs_interference_event(ab, skb);
|
||||
break;
|
||||
/* add Unsupported events (rare) here */
|
||||
case WMI_TBTTOFFSET_EXT_UPDATE_EVENTID:
|
||||
case WMI_PEER_OPER_MODE_CHANGE_EVENTID:
|
||||
|
|
@ -10083,6 +10568,42 @@ int ath12k_wmi_send_tpc_stats_request(struct ath12k *ar,
|
|||
return ret;
|
||||
}
|
||||
|
||||
int ath12k_wmi_simulate_incumbent_signal_interference(struct ath12k *ar,
|
||||
u32 chan_bw_interference_bitmap)
|
||||
{
|
||||
struct wmi_unit_test_arg wmi_ut = {};
|
||||
struct ath12k_link_vif *arvif;
|
||||
struct ath12k_vif *ahvif;
|
||||
bool arvif_found = false;
|
||||
|
||||
list_for_each_entry(arvif, &ar->arvifs, list) {
|
||||
ahvif = arvif->ahvif;
|
||||
if (arvif->is_started && ahvif->vdev_type == WMI_VDEV_TYPE_AP) {
|
||||
arvif_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!arvif_found)
|
||||
return -EINVAL;
|
||||
|
||||
wmi_ut.args[ATH12K_WMI_INCUMBENT_SIGNAL_TEST_INTF] =
|
||||
ATH12K_WMI_UNIT_TEST_INCUMBENT_SIGNAL_INTF_TYPE;
|
||||
wmi_ut.args[ATH12K_WMI_INCUMBENT_SIGNAL_TEST_BITMAP] =
|
||||
chan_bw_interference_bitmap;
|
||||
|
||||
wmi_ut.vdev_id = arvif->vdev_id;
|
||||
wmi_ut.module_id = ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_MODULE;
|
||||
wmi_ut.num_args = ATH12K_WMI_INCUMBENT_SIGNAL_MAX_TEST_ARGS;
|
||||
wmi_ut.diag_token = ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_TOKEN;
|
||||
|
||||
ath12k_dbg(ar->ab, ATH12K_DBG_WMI,
|
||||
"Triggering incumbent signal interference simulation, interference bitmap: 0x%x\n",
|
||||
chan_bw_interference_bitmap);
|
||||
|
||||
return ath12k_wmi_send_unit_test_cmd(ar, &wmi_ut);
|
||||
}
|
||||
|
||||
int ath12k_wmi_connect(struct ath12k_base *ab)
|
||||
{
|
||||
u32 i;
|
||||
|
|
@ -10874,10 +11395,9 @@ int ath12k_wmi_mlo_setup(struct ath12k *ar, struct wmi_mlo_setup_arg *mlo_params
|
|||
ath12k_warn(ar->ab, "failed to submit WMI_MLO_SETUP_CMDID command: %d\n",
|
||||
ret);
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ath12k_wmi_mlo_ready(struct ath12k *ar)
|
||||
|
|
@ -10902,10 +11422,9 @@ int ath12k_wmi_mlo_ready(struct ath12k *ar)
|
|||
ath12k_warn(ar->ab, "failed to submit WMI_MLO_READY_CMDID command: %d\n",
|
||||
ret);
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ath12k_wmi_mlo_teardown(struct ath12k *ar)
|
||||
|
|
@ -10931,10 +11450,9 @@ int ath12k_wmi_mlo_teardown(struct ath12k *ar)
|
|||
ath12k_warn(ar->ab, "failed to submit WMI MLO teardown command: %d\n",
|
||||
ret);
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool ath12k_wmi_supports_6ghz_cc_ext(struct ath12k *ar)
|
||||
|
|
@ -10997,10 +11515,9 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
|
|||
if (ret) {
|
||||
ath12k_warn(ar->ab, "failed to send WMI_VDEV_SET_TPC_POWER_CMDID\n");
|
||||
dev_kfree_skb(skb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
|
|||
|
|
@ -870,6 +870,7 @@ enum wmi_tlv_event_id {
|
|||
WMI_READ_DATA_FROM_FLASH_EVENTID,
|
||||
WMI_REPORT_RX_AGGR_FAILURE_EVENTID,
|
||||
WMI_PKGID_EVENTID,
|
||||
WMI_THERM_THROT_STATS_EVENTID,
|
||||
WMI_GPIO_INPUT_EVENTID = WMI_TLV_CMD(WMI_GRP_GPIO),
|
||||
WMI_UPLOADH_EVENTID,
|
||||
WMI_CAPTUREH_EVENTID,
|
||||
|
|
@ -2014,7 +2015,7 @@ enum wmi_tlv_tag {
|
|||
WMI_TAG_VDEV_CH_POWER_INFO,
|
||||
WMI_TAG_MLO_LINK_SET_ACTIVE_CMD = 0x3BE,
|
||||
WMI_TAG_EHT_RATE_SET = 0x3C4,
|
||||
WMI_TAG_DCS_AWGN_INT_TYPE = 0x3C5,
|
||||
WMI_TAG_DCS_INCUMBENT_SIGNAL_INTERFERENCE_TYPE = 0x3C5,
|
||||
WMI_TAG_MLO_TX_SEND_PARAMS,
|
||||
WMI_TAG_MLO_PARTNER_LINK_PARAMS,
|
||||
WMI_TAG_MLO_PARTNER_LINK_PARAMS_PEER_ASSOC,
|
||||
|
|
@ -2259,6 +2260,7 @@ enum wmi_tlv_service {
|
|||
WMI_TLV_SERVICE_FREQINFO_IN_METADATA = 219,
|
||||
WMI_TLV_SERVICE_EXT2_MSG = 220,
|
||||
WMI_TLV_SERVICE_BEACON_PROTECTION_SUPPORT = 244,
|
||||
WMI_TLV_SERVICE_5_9GHZ_SUPPORT = 247,
|
||||
WMI_TLV_SERVICE_SRG_SRP_SPATIAL_REUSE_SUPPORT = 249,
|
||||
WMI_TLV_SERVICE_MBSS_PARAM_IN_VDEV_START_SUPPORT = 253,
|
||||
|
||||
|
|
@ -2268,11 +2270,17 @@ enum wmi_tlv_service {
|
|||
|
||||
WMI_TLV_SERVICE_REG_CC_EXT_EVENT_SUPPORT = 281,
|
||||
|
||||
WMI_TLV_SERVICE_DCS_INCUMBENT_SIGNAL_INTERFERENCE_SUPPORT = 286,
|
||||
|
||||
WMI_TLV_SERVICE_11BE = 289,
|
||||
|
||||
WMI_TLV_SERVICE_WMSK_COMPACTION_RX_TLVS = 361,
|
||||
|
||||
WMI_TLV_SERVICE_PEER_METADATA_V1A_V1B_SUPPORT = 365,
|
||||
WMI_TLV_SERVICE_THERM_THROT_POUT_REDUCTION = 410,
|
||||
WMI_TLV_SERVICE_IS_TARGET_IPA = 425,
|
||||
WMI_TLV_SERVICE_THERM_THROT_TX_CHAIN_MASK = 426,
|
||||
WMI_TLV_SERVICE_THERM_THROT_5_LEVELS = 429,
|
||||
WMI_TLV_SERVICE_ETH_OFFLOAD = 461,
|
||||
|
||||
WMI_MAX_EXT2_SERVICE,
|
||||
|
|
@ -3586,7 +3594,6 @@ struct ath12k_wmi_scan_req_arg {
|
|||
u32 num_bssid;
|
||||
u32 num_ssids;
|
||||
u32 n_probes;
|
||||
u32 *chan_list;
|
||||
u32 notify_scan_events;
|
||||
struct cfg80211_ssid ssid[WLAN_SCAN_MAX_NUM_SSID];
|
||||
struct ath12k_wmi_mac_addr_params bssid_list[WLAN_SCAN_MAX_NUM_BSSID];
|
||||
|
|
@ -3595,6 +3602,7 @@ struct ath12k_wmi_scan_req_arg {
|
|||
u32 num_hint_bssid;
|
||||
struct ath12k_wmi_hint_short_ssid_arg hint_s_ssid[WLAN_SCAN_MAX_HINT_S_SSID];
|
||||
struct ath12k_wmi_hint_bssid_arg hint_bssid[WLAN_SCAN_MAX_HINT_BSSID];
|
||||
u32 chan_list[] __counted_by(num_chan);
|
||||
};
|
||||
|
||||
struct wmi_ssid_arg {
|
||||
|
|
@ -4120,6 +4128,49 @@ enum set_init_cc_flags {
|
|||
ALPHA_IS_SET,
|
||||
};
|
||||
|
||||
struct wmi_therm_throt_stats_event {
|
||||
__le32 pdev_id;
|
||||
__le32 temp;
|
||||
__le32 level;
|
||||
__le32 therm_throt_levels;
|
||||
} __packed;
|
||||
|
||||
#define THERMAL_LEVELS 4
|
||||
#define ENHANCED_THERMAL_LEVELS 5
|
||||
|
||||
struct ath12k_wmi_tt_level_config_param {
|
||||
s32 tmplwm;
|
||||
s32 tmphwm;
|
||||
u32 dcoffpercent;
|
||||
u32 pout_reduction_db;
|
||||
};
|
||||
|
||||
struct ath12k_wmi_therm_throt_config_request_cmd {
|
||||
__le32 tlv_header;
|
||||
__le32 pdev_id;
|
||||
__le32 enable;
|
||||
__le32 dc;
|
||||
/* After how many duty cycles the firmware sends stats to host */
|
||||
__le32 dc_per_event;
|
||||
__le32 therm_throt_levels;
|
||||
} __packed;
|
||||
|
||||
struct ath12k_wmi_therm_throt_level_config_param {
|
||||
__le32 tlv_header;
|
||||
a_sle32 temp_lwm;
|
||||
a_sle32 temp_hwm;
|
||||
__le32 dc_off_percent;
|
||||
__le32 prio;
|
||||
__le32 pout_reduction_25db;
|
||||
__le32 tx_chain_mask;
|
||||
__le32 duty_cycle;
|
||||
} __packed;
|
||||
|
||||
struct ath12k_wmi_thermal_mitigation_arg {
|
||||
int num_levels;
|
||||
const struct ath12k_wmi_tt_level_config_param *levelconf;
|
||||
};
|
||||
|
||||
struct ath12k_wmi_init_country_arg {
|
||||
union {
|
||||
u16 country_code;
|
||||
|
|
@ -4203,6 +4254,16 @@ enum dfs_test_args_idx {
|
|||
DFS_MAX_TEST_ARGS,
|
||||
};
|
||||
|
||||
#define ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_MODULE 0x18
|
||||
#define ATH12K_WMI_INCUMBENT_SIGNAL_UNIT_TEST_TOKEN 0
|
||||
#define ATH12K_WMI_UNIT_TEST_INCUMBENT_SIGNAL_INTF_TYPE 1
|
||||
|
||||
enum ath12k_wmi_incumbent_signal_test_args_idx {
|
||||
ATH12K_WMI_INCUMBENT_SIGNAL_TEST_INTF,
|
||||
ATH12K_WMI_INCUMBENT_SIGNAL_TEST_BITMAP,
|
||||
ATH12K_WMI_INCUMBENT_SIGNAL_MAX_TEST_ARGS,
|
||||
};
|
||||
|
||||
/* update if another test command requires more */
|
||||
#define WMI_UNIT_TEST_ARGS_MAX DFS_MAX_TEST_ARGS
|
||||
|
||||
|
|
@ -4487,6 +4548,62 @@ struct ath12k_wmi_pdev_radar_event {
|
|||
a_sle32 sidx;
|
||||
} __packed;
|
||||
|
||||
#define ATH12K_WMI_DCS_INCUMBENT_SIGNAL_INTERFERENCE 0x04
|
||||
|
||||
struct ath12k_wmi_dcs_interference_ev_fixed_params {
|
||||
__le32 interference_type;
|
||||
__le32 pdev_id;
|
||||
} __packed;
|
||||
|
||||
struct ath12k_wmi_incumbent_signal_interference_params {
|
||||
__le32 chan_width;
|
||||
__le32 chan_freq;
|
||||
__le32 center_freq0;
|
||||
__le32 center_freq1;
|
||||
__le32 chan_bw_interference_bitmap;
|
||||
} __packed;
|
||||
|
||||
struct ath12k_wmi_incumbent_signal_interference_arg {
|
||||
u32 chan_width;
|
||||
u32 chan_freq;
|
||||
u32 center_freq0;
|
||||
u32 center_freq1;
|
||||
u32 chan_bw_interference_bitmap;
|
||||
};
|
||||
|
||||
struct ath12k_wmi_intf_arg {
|
||||
u32 interference_type;
|
||||
u32 pdev_id;
|
||||
};
|
||||
|
||||
enum ath12k_wmi_dcs_interference_chan_segment {
|
||||
/*
|
||||
* Firmware reports interference bitmap in primary-based order.
|
||||
* Bit 0 is the primary 20 MHz, bit 1 is the adjacent 20 MHz within
|
||||
* the primary 40 MHz. Bits 2-3 cover the secondary 40 MHz, bits 4-7
|
||||
* cover the secondary 80 MHz, and bits 8-15 cover the secondary 160 MHz.
|
||||
*/
|
||||
ATH12K_WMI_DCS_SEG_PRI20 = 0x1,
|
||||
ATH12K_WMI_DCS_SEG_SEC20 = 0x2,
|
||||
ATH12K_WMI_DCS_SEG_SEC40_LOW = 0x4,
|
||||
ATH12K_WMI_DCS_SEG_SEC40_UP = 0x8,
|
||||
ATH12K_WMI_DCS_SEG_SEC40 = 0xC,
|
||||
ATH12K_WMI_DCS_SEG_SEC80_LOW = 0x10,
|
||||
ATH12K_WMI_DCS_SEG_SEC80_LOW_UP = 0x20,
|
||||
ATH12K_WMI_DCS_SEG_SEC80_UP_LOW = 0x40,
|
||||
ATH12K_WMI_DCS_SEG_SEC80_UP = 0x80,
|
||||
ATH12K_WMI_DCS_SEG_SEC80 = 0xF0,
|
||||
ATH12K_WMI_DCS_SEG_SEC160_LOW = 0x0100,
|
||||
ATH12K_WMI_DCS_SEG_SEC160_LOW_UP = 0x0200,
|
||||
ATH12K_WMI_DCS_SEG_SEC160_LOW_UP_UP = 0x0400,
|
||||
ATH12K_WMI_DCS_SEG_SEC160_LOW_UP_UP_UP = 0x0800,
|
||||
ATH12K_WMI_DCS_SEG_SEC160_UP_LOW_LOW_LOW = 0x1000,
|
||||
ATH12K_WMI_DCS_SEG_SEC160_UP_LOW_LOW = 0x2000,
|
||||
ATH12K_WMI_DCS_SEG_SEC160_UP_LOW = 0x4000,
|
||||
ATH12K_WMI_DCS_SEG_SEC160_UP = 0x8000,
|
||||
ATH12K_WMI_DCS_SEG_SEC160 = 0xFF00,
|
||||
};
|
||||
|
||||
struct wmi_pdev_temperature_event {
|
||||
/* temperature value in Celsius degree */
|
||||
a_sle32 temp;
|
||||
|
|
@ -6514,6 +6631,8 @@ __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);
|
||||
int ath12k_wmi_send_thermal_mitigation_cmd(struct ath12k *ar,
|
||||
struct ath12k_wmi_thermal_mitigation_arg *arg);
|
||||
|
||||
static inline u32
|
||||
ath12k_wmi_caps_ext_get_pdev_id(const struct ath12k_wmi_caps_ext_params *param)
|
||||
|
|
@ -6576,6 +6695,8 @@ int ath12k_wmi_send_vdev_set_tpc_power(struct ath12k *ar,
|
|||
struct ath12k_reg_tpc_power_info *param);
|
||||
int ath12k_wmi_send_mlo_link_set_active_cmd(struct ath12k_base *ab,
|
||||
struct wmi_mlo_link_set_active_arg *param);
|
||||
int ath12k_wmi_simulate_incumbent_signal_interference(struct ath12k *ar,
|
||||
u32 chan_bw_interference_bitmap);
|
||||
int ath12k_wmi_alloc(void);
|
||||
void ath12k_wmi_free(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -80,14 +80,14 @@ static int ar9002_hw_init_mode_regs(struct ath_hw *ah)
|
|||
/* iniAddac needs to be modified for these chips */
|
||||
if (AR_SREV_9160(ah) || !AR_SREV_5416_22_OR_LATER(ah)) {
|
||||
struct ar5416IniArray *addac = &ah->iniAddac;
|
||||
u32 size = sizeof(u32) * addac->ia_rows * addac->ia_columns;
|
||||
u32 n = addac->ia_rows * addac->ia_columns;
|
||||
u32 *data;
|
||||
|
||||
data = devm_kzalloc(ah->dev, size, GFP_KERNEL);
|
||||
data = devm_kmemdup_array(ah->dev, addac->ia_array, n, sizeof(u32),
|
||||
GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(data, addac->ia_array, size);
|
||||
addac->ia_array = data;
|
||||
|
||||
if (!AR_SREV_5416_22_OR_LATER(ah)) {
|
||||
|
|
|
|||
|
|
@ -137,24 +137,6 @@ static void owl_fw_cb(const struct firmware *fw, void *context)
|
|||
release_firmware(fw);
|
||||
}
|
||||
|
||||
static const char *owl_get_eeprom_name(struct pci_dev *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
char *eeprom_name;
|
||||
|
||||
dev_dbg(dev, "using auto-generated eeprom filename\n");
|
||||
|
||||
eeprom_name = devm_kzalloc(dev, EEPROM_FILENAME_LEN, GFP_KERNEL);
|
||||
if (!eeprom_name)
|
||||
return NULL;
|
||||
|
||||
/* this should match the pattern used in ath9k/init.c */
|
||||
scnprintf(eeprom_name, EEPROM_FILENAME_LEN, "ath9k-eeprom-pci-%s.bin",
|
||||
dev_name(dev));
|
||||
|
||||
return eeprom_name;
|
||||
}
|
||||
|
||||
static void owl_nvmem_work(struct work_struct *work)
|
||||
{
|
||||
struct owl_ctx *ctx = container_of(work, struct owl_ctx, work);
|
||||
|
|
@ -195,8 +177,9 @@ static int owl_nvmem_probe(struct owl_ctx *ctx)
|
|||
static int owl_probe(struct pci_dev *pdev,
|
||||
const struct pci_device_id *id)
|
||||
{
|
||||
char eeprom_name[EEPROM_FILENAME_LEN];
|
||||
struct device *dev = &pdev->dev;
|
||||
struct owl_ctx *ctx;
|
||||
const char *eeprom_name;
|
||||
int err = 0;
|
||||
|
||||
if (pci_enable_device(pdev))
|
||||
|
|
@ -215,11 +198,11 @@ static int owl_probe(struct pci_dev *pdev,
|
|||
if (err <= 0)
|
||||
return err;
|
||||
|
||||
eeprom_name = owl_get_eeprom_name(pdev);
|
||||
if (!eeprom_name) {
|
||||
dev_err(&pdev->dev, "no eeprom filename found.\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
dev_dbg(dev, "using auto-generated eeprom filename\n");
|
||||
|
||||
/* this should match the pattern used in ath9k/init.c */
|
||||
scnprintf(eeprom_name, sizeof(eeprom_name), "ath9k-eeprom-pci-%s.bin",
|
||||
dev_name(dev));
|
||||
|
||||
err = request_firmware_nowait(THIS_MODULE, true, eeprom_name,
|
||||
&pdev->dev, GFP_KERNEL, ctx, owl_fw_cb);
|
||||
|
|
|
|||
|
|
@ -133,13 +133,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common)
|
|||
ATH9K_NUM_CHANNELS);
|
||||
|
||||
if (ah->caps.hw_caps & ATH9K_HW_CAP_2GHZ) {
|
||||
channels = devm_kzalloc(ah->dev,
|
||||
channels = devm_kmemdup(ah->dev, ath9k_2ghz_chantable,
|
||||
sizeof(ath9k_2ghz_chantable), GFP_KERNEL);
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(channels, ath9k_2ghz_chantable,
|
||||
sizeof(ath9k_2ghz_chantable));
|
||||
common->sbands[NL80211_BAND_2GHZ].channels = channels;
|
||||
common->sbands[NL80211_BAND_2GHZ].band = NL80211_BAND_2GHZ;
|
||||
common->sbands[NL80211_BAND_2GHZ].n_channels =
|
||||
|
|
@ -150,13 +148,11 @@ int ath9k_cmn_init_channels_rates(struct ath_common *common)
|
|||
}
|
||||
|
||||
if (ah->caps.hw_caps & ATH9K_HW_CAP_5GHZ) {
|
||||
channels = devm_kzalloc(ah->dev,
|
||||
channels = devm_kmemdup(ah->dev, ath9k_5ghz_chantable,
|
||||
sizeof(ath9k_5ghz_chantable), GFP_KERNEL);
|
||||
if (!channels)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy(channels, ath9k_5ghz_chantable,
|
||||
sizeof(ath9k_5ghz_chantable));
|
||||
common->sbands[NL80211_BAND_5GHZ].channels = channels;
|
||||
common->sbands[NL80211_BAND_5GHZ].band = NL80211_BAND_5GHZ;
|
||||
common->sbands[NL80211_BAND_5GHZ].n_channels =
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
|
|||
{
|
||||
struct ath_common *common = ath9k_hw_common(sc->sc_ah);
|
||||
u8 *ds;
|
||||
int i, bsize, desc_len;
|
||||
int i, desc_len;
|
||||
|
||||
ath_dbg(common, CONFIG, "%s DMA: %u buffers %u desc/buf\n",
|
||||
name, nbuf, ndesc);
|
||||
|
|
@ -339,8 +339,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
|
|||
if (is_tx) {
|
||||
struct ath_buf *bf;
|
||||
|
||||
bsize = sizeof(struct ath_buf) * nbuf;
|
||||
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
|
||||
bf = devm_kcalloc(sc->dev, sizeof(*bf), nbuf, GFP_KERNEL);
|
||||
if (!bf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -370,8 +369,7 @@ int ath_descdma_setup(struct ath_softc *sc, struct ath_descdma *dd,
|
|||
} else {
|
||||
struct ath_rxbuf *bf;
|
||||
|
||||
bsize = sizeof(struct ath_rxbuf) * nbuf;
|
||||
bf = devm_kzalloc(sc->dev, bsize, GFP_KERNEL);
|
||||
bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbuf, GFP_KERNEL);
|
||||
if (!bf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
@ -576,7 +574,7 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc)
|
|||
size_t len;
|
||||
int err;
|
||||
|
||||
cell = devm_nvmem_cell_get(sc->dev, "calibration");
|
||||
cell = nvmem_cell_get(sc->dev, "calibration");
|
||||
if (IS_ERR(cell)) {
|
||||
err = PTR_ERR(cell);
|
||||
|
||||
|
|
@ -593,6 +591,7 @@ static int ath9k_nvmem_request_eeprom(struct ath_softc *sc)
|
|||
}
|
||||
|
||||
buf = nvmem_cell_read(cell, &len);
|
||||
nvmem_cell_put(cell);
|
||||
if (IS_ERR(buf))
|
||||
return PTR_ERR(buf);
|
||||
|
||||
|
|
|
|||
|
|
@ -202,7 +202,6 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
|
|||
struct sk_buff *skb;
|
||||
struct ath_rxbuf *bf;
|
||||
int error = 0, i;
|
||||
u32 size;
|
||||
|
||||
ath9k_hw_set_rx_bufsize(ah, common->rx_bufsize -
|
||||
ah->caps.rx_status_len);
|
||||
|
|
@ -212,8 +211,7 @@ static int ath_rx_edma_init(struct ath_softc *sc, int nbufs)
|
|||
ath_rx_edma_init_queue(&sc->rx.rx_edma[ATH9K_RX_QUEUE_HP],
|
||||
ah->caps.rx_hp_qdepth);
|
||||
|
||||
size = sizeof(struct ath_rxbuf) * nbufs;
|
||||
bf = devm_kzalloc(sc->dev, size, GFP_KERNEL);
|
||||
bf = devm_kcalloc(sc->dev, sizeof(struct ath_rxbuf), nbufs, GFP_KERNEL);
|
||||
if (!bf)
|
||||
return -ENOMEM;
|
||||
|
||||
|
|
|
|||
|
|
@ -1993,7 +1993,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
|
|||
|
||||
ieee80211_txq_schedule_start(hw, txq->mac80211_qnum);
|
||||
spin_lock_bh(&sc->chan_lock);
|
||||
rcu_read_lock();
|
||||
|
||||
if (sc->cur_chan->stopped)
|
||||
goto out;
|
||||
|
|
@ -2011,7 +2010,6 @@ void ath_txq_schedule(struct ath_softc *sc, struct ath_txq *txq)
|
|||
}
|
||||
|
||||
out:
|
||||
rcu_read_unlock();
|
||||
spin_unlock_bh(&sc->chan_lock);
|
||||
ieee80211_txq_schedule_end(hw, txq->mac80211_qnum);
|
||||
}
|
||||
|
|
@ -2746,6 +2744,11 @@ void ath_tx_edma_tasklet(struct ath_softc *sc)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (ts.qid >= ATH9K_NUM_TX_QUEUES) {
|
||||
ath_dbg(common, XMIT, "invalid qid %d\n", ts.qid);
|
||||
continue;
|
||||
}
|
||||
|
||||
txq = &sc->tx.txq[ts.qid];
|
||||
|
||||
ath_txq_lock(sc, txq);
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ iwlwifi-objs += iwl-dbg-tlv.o
|
|||
iwlwifi-objs += iwl-trans.o
|
||||
|
||||
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/dbg.o fw/dbg-old.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
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -15,26 +15,26 @@
|
|||
/* Lowest firmware API version supported */
|
||||
#define IWL_22000_UCODE_API_MIN 77
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_22000_SMEM_OFFSET 0x400000
|
||||
#define IWL_22000_SMEM_LEN 0xD0000
|
||||
|
||||
#define IWL_CC_A_FW_PRE "iwlwifi-cc-a0"
|
||||
#define IWL_QU_B_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0"
|
||||
#define IWL_QU_C_HR_B_FW_PRE "iwlwifi-Qu-c0-hr-b0"
|
||||
#define IWL_QUZ_A_HR_B_FW_PRE "iwlwifi-QuZ-a0-hr-b0"
|
||||
|
||||
#define IWL_CC_A_MODULE_FIRMWARE(api) \
|
||||
IWL_CC_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QU_B_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QU_B_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QUZ_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QUZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QU_C_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QU_C_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
static const struct iwl_family_base_params iwl_22000_base = {
|
||||
.num_of_queues = 512,
|
||||
.max_tfd_queue_size = 256,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = true,
|
||||
.pcie_l1_allowed = true,
|
||||
.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,
|
||||
|
|
@ -113,4 +113,7 @@ 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)";
|
||||
|
||||
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_QUZ_A_HR_B_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_CC_A_MODULE_FIRMWARE(IWL_22000_UCODE_API_MAX));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2020, 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2020, 2023, 2025-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -53,10 +53,7 @@ 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,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = true,
|
||||
.pcie_l1_allowed = true,
|
||||
.apmg_wake_up_wa = true,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2014, 2018-2020, 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2014, 2018-2020, 2023, 2025-2026 Intel Corporation
|
||||
* Copyright (C) 2014-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -39,10 +39,7 @@ 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,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = true,
|
||||
.pcie_l1_allowed = true,
|
||||
.nvm_hw_section_num = 10,
|
||||
|
|
|
|||
|
|
@ -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, 2025 Intel Corporation
|
||||
* Copyright (C) 2018-2021, 2023, 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -30,10 +30,7 @@ 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,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.max_event_log_size = 512,
|
||||
.shadow_reg_enable = true,
|
||||
.pcie_l1_allowed = true,
|
||||
.smem_offset = IWL9000_SMEM_OFFSET,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -15,21 +15,30 @@
|
|||
/* Lowest firmware API version supported */
|
||||
#define IWL_AX210_UCODE_API_MIN 89
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_AX210_SMEM_OFFSET 0x400000
|
||||
#define IWL_AX210_SMEM_LEN 0xD0000
|
||||
#define IWL_SO_A_GF_A_FW_PRE "iwlwifi-so-a0-gf-a0"
|
||||
#define IWL_TY_A_GF_A_FW_PRE "iwlwifi-ty-a0-gf-a0"
|
||||
#define IWL_MA_A_GF_A_FW_PRE "iwlwifi-ma-a0-gf-a0"
|
||||
#define IWL_MA_B_GF_A_FW_PRE "iwlwifi-ma-b0-gf-a0"
|
||||
#define IWL_SO_A_GF4_A_FW_PRE "iwlwifi-so-a0-gf4-a0"
|
||||
#define IWL_MA_A_GF4_A_FW_PRE "iwlwifi-ma-a0-gf4-a0"
|
||||
#define IWL_MA_B_GF4_A_FW_PRE "iwlwifi-ma-b0-gf4-a0"
|
||||
#define IWL_SO_A_HR_B_FW_PRE "iwlwifi-so-a0-hr-b0"
|
||||
#define IWL_MA_A_HR_B_FW_PRE "iwlwifi-ma-a0-hr-b0"
|
||||
#define IWL_MA_B_HR_B_FW_PRE "iwlwifi-ma-b0-hr-b0"
|
||||
|
||||
#define IWL_SO_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_SO_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_MA_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_MA_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_MA_B_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_MA_B_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
static const struct iwl_family_base_params iwl_ax210_base = {
|
||||
.num_of_queues = 512,
|
||||
.max_tfd_queue_size = 65536,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.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,
|
||||
|
|
@ -121,3 +130,14 @@ const struct iwl_mac_cfg iwl_ma_mac_cfg = {
|
|||
.integrated = true,
|
||||
.umac_prph_offset = 0x300000
|
||||
};
|
||||
|
||||
IWL_FW_AND_PNVM(IWL_SO_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_TY_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_SO_A_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF4_A_FW_PRE, IWL_AX210_UCODE_API_MAX);
|
||||
MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_MA_A_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_MA_B_HR_B_MODULE_FIRMWARE(IWL_AX210_UCODE_API_MAX));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -10,26 +10,17 @@
|
|||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_BZ_UCODE_CORE_MAX 102
|
||||
#define IWL_BZ_UCODE_CORE_MAX 105
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_BZ_UCODE_API_MIN 100
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_BZ_SMEM_OFFSET 0x400000
|
||||
#define IWL_BZ_SMEM_LEN 0xD0000
|
||||
/* Lowest firmware core release supported */
|
||||
#define IWL_BZ_UCODE_CORE_MIN 101
|
||||
|
||||
static const struct iwl_family_base_params iwl_bz_base = {
|
||||
.num_of_queues = 512,
|
||||
.max_tfd_queue_size = 65536,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.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,
|
||||
.d3_debug_data_base_addr = 0x401000,
|
||||
|
|
@ -69,7 +60,7 @@ static const struct iwl_family_base_params iwl_bz_base = {
|
|||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_BZ_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_BZ_UCODE_API_MIN,
|
||||
.ucode_api_min = ENCODE_CORE_AS_API(IWL_BZ_UCODE_CORE_MIN),
|
||||
};
|
||||
|
||||
const struct iwl_mac_cfg iwl_bz_mac_cfg = {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -9,28 +9,19 @@
|
|||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_DR_UCODE_CORE_MAX 102
|
||||
#define IWL_DR_UCODE_CORE_MAX 105
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_DR_UCODE_API_MIN 100
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_DR_SMEM_OFFSET 0x400000
|
||||
#define IWL_DR_SMEM_LEN 0xD0000
|
||||
/* Lowest firmware core release supported */
|
||||
#define IWL_DR_UCODE_CORE_MIN 101
|
||||
|
||||
#define IWL_DR_A_PE_A_FW_PRE "iwlwifi-dr-a0-pe-a0"
|
||||
|
||||
static const struct iwl_family_base_params iwl_dr_base = {
|
||||
.num_of_queues = 512,
|
||||
.max_tfd_queue_size = 65536,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.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,
|
||||
.d3_debug_data_base_addr = 0x401000,
|
||||
|
|
@ -70,7 +61,7 @@ static const struct iwl_family_base_params iwl_dr_base = {
|
|||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_DR_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_DR_UCODE_API_MIN,
|
||||
.ucode_api_min = ENCODE_CORE_AS_API(IWL_DR_UCODE_CORE_MIN),
|
||||
};
|
||||
|
||||
const struct iwl_mac_cfg iwl_dr_mac_cfg = {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-config.h"
|
||||
|
||||
|
|
@ -11,9 +11,6 @@
|
|||
#define IWL_GL_B_FM_B_FW_PRE "iwlwifi-gl-b0-fm-b0"
|
||||
#define IWL_GL_C_FM_C_FW_PRE "iwlwifi-gl-c0-fm-c0"
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_FM_NVM_VERSION 0x0a1d
|
||||
|
||||
#define IWL_DEVICE_FM \
|
||||
.ht_params = { \
|
||||
.stbc = true, \
|
||||
|
|
@ -27,7 +24,6 @@
|
|||
.uhb_supported = true, \
|
||||
.eht_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 = {
|
||||
|
|
@ -51,6 +47,8 @@ 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_killer_be1730x_name[] =
|
||||
"Killer(TM) Wi-Fi 7 BE1730x 160MHz Wireless Network Adapter (BE202)";
|
||||
|
||||
const char iwl_be201_name[] = "Intel(R) Wi-Fi 7 BE201 320MHz";
|
||||
const char iwl_be200_name[] = "Intel(R) Wi-Fi 7 BE200 320MHz";
|
||||
|
|
|
|||
|
|
@ -11,13 +11,6 @@
|
|||
/* Lowest firmware API version supported */
|
||||
#define IWL_GF_UCODE_API_MIN 100
|
||||
|
||||
#define IWL_SO_A_GF_A_FW_PRE "iwlwifi-so-a0-gf-a0"
|
||||
#define IWL_TY_A_GF_A_FW_PRE "iwlwifi-ty-a0-gf-a0"
|
||||
#define IWL_MA_A_GF_A_FW_PRE "iwlwifi-ma-a0-gf-a0"
|
||||
#define IWL_MA_B_GF_A_FW_PRE "iwlwifi-ma-b0-gf-a0"
|
||||
#define IWL_SO_A_GF4_A_FW_PRE "iwlwifi-so-a0-gf4-a0"
|
||||
#define IWL_MA_A_GF4_A_FW_PRE "iwlwifi-ma-a0-gf4-a0"
|
||||
#define IWL_MA_B_GF4_A_FW_PRE "iwlwifi-ma-b0-gf4-a0"
|
||||
#define IWL_BZ_A_GF_A_FW_PRE "iwlwifi-bz-a0-gf-a0"
|
||||
#define IWL_BZ_A_GF4_A_FW_PRE "iwlwifi-bz-a0-gf4-a0"
|
||||
#define IWL_SC_A_GF_A_FW_PRE "iwlwifi-sc-a0-gf-a0"
|
||||
|
|
@ -35,9 +28,6 @@
|
|||
#define IWL_SC_A_GF4_A_MODULE_FIRMWARE(api) \
|
||||
IWL_SC_A_GF4_A_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
/* 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,
|
||||
|
|
@ -49,7 +39,6 @@ const struct iwl_rf_cfg iwl_rf_gf = {
|
|||
.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,
|
||||
.ucode_api_min = IWL_GF_UCODE_API_MIN,
|
||||
|
|
@ -73,12 +62,6 @@ 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";
|
||||
|
||||
IWL_FW_AND_PNVM(IWL_SO_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_TY_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_A_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
IWL_FW_AND_PNVM(IWL_MA_B_GF4_A_FW_PRE, IWL_GF_UCODE_API_MAX);
|
||||
MODULE_FIRMWARE(IWL_BZ_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_BZ_A_GF4_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SC_A_GF_A_MODULE_FIRMWARE(IWL_GF_UCODE_API_MAX));
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-config.h"
|
||||
|
||||
|
|
@ -11,28 +11,10 @@
|
|||
/* Lowest firmware API version supported */
|
||||
#define IWL_HR_UCODE_API_MIN 100
|
||||
|
||||
#define IWL_QU_B_HR_B_FW_PRE "iwlwifi-Qu-b0-hr-b0"
|
||||
#define IWL_QU_C_HR_B_FW_PRE "iwlwifi-Qu-c0-hr-b0"
|
||||
#define IWL_QUZ_A_HR_B_FW_PRE "iwlwifi-QuZ-a0-hr-b0"
|
||||
#define IWL_SO_A_HR_B_FW_PRE "iwlwifi-so-a0-hr-b0"
|
||||
#define IWL_MA_A_HR_B_FW_PRE "iwlwifi-ma-a0-hr-b0"
|
||||
#define IWL_MA_B_HR_B_FW_PRE "iwlwifi-ma-b0-hr-b0"
|
||||
#define IWL_BZ_A_HR_B_FW_PRE "iwlwifi-bz-a0-hr-b0"
|
||||
#define IWL_SC_A_HR_A_FW_PRE "iwlwifi-sc-a0-hr-b0"
|
||||
#define IWL_SC_A_HR_B_FW_PRE "iwlwifi-sc-a0-hr-b0"
|
||||
|
||||
#define IWL_QU_B_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QU_B_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QUZ_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QUZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_QU_C_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_QU_C_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_SO_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_SO_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_MA_A_HR_B_FW_MODULE_FIRMWARE(api) \
|
||||
IWL_MA_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(api) \
|
||||
IWL_MA_B_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_BZ_A_HR_B_MODULE_FIRMWARE(api) \
|
||||
IWL_BZ_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
#define IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(api) \
|
||||
|
|
@ -40,9 +22,6 @@
|
|||
#define IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(api) \
|
||||
IWL_SC_A_HR_B_FW_PRE "-" __stringify(api) ".ucode"
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_HR_NVM_VERSION 0x0a1d
|
||||
|
||||
#define IWL_DEVICE_HR \
|
||||
.led_mode = IWL_LED_RF_STATE, \
|
||||
.non_shared_ant = ANT_B, \
|
||||
|
|
@ -54,7 +33,6 @@
|
|||
BIT(NL80211_BAND_5GHZ), \
|
||||
}, \
|
||||
.num_rbds = IWL_NUM_RBDS_HE, \
|
||||
.nvm_ver = IWL_HR_NVM_VERSION, \
|
||||
.nvm_type = IWL_NVM_EXT, \
|
||||
.ucode_api_min = IWL_HR_UCODE_API_MIN, \
|
||||
.ucode_api_max = IWL_HR_UCODE_API_MAX
|
||||
|
|
@ -78,12 +56,6 @@ 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";
|
||||
|
||||
MODULE_FIRMWARE(IWL_QU_B_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_QU_C_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_QUZ_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SO_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_MA_A_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_MA_B_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_BZ_A_HR_B_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SC_A_HR_A_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
MODULE_FIRMWARE(IWL_SC_A_HR_B_FW_MODULE_FIRMWARE(IWL_HR_UCODE_API_MAX));
|
||||
|
|
|
|||
|
|
@ -1,10 +1,28 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2025 Intel Corporation
|
||||
* Copyright (C) 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-config.h"
|
||||
|
||||
/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */
|
||||
#define IWL_DEVICE_PE \
|
||||
.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, \
|
||||
.eht_supported = true, \
|
||||
.uhr_supported = true, \
|
||||
.num_rbds = IWL_NUM_RBDS_EHT, \
|
||||
.nvm_type = IWL_NVM_EXT
|
||||
|
||||
const struct iwl_rf_cfg iwl_rf_pe = {
|
||||
IWL_DEVICE_PE,
|
||||
};
|
||||
|
||||
const char iwl_killer_bn1850w2_name[] =
|
||||
"Killer(R) Wi-Fi 8 BN1850w2 320MHz Wireless Network Adapter (BN201.D2W)";
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2025 Intel Corporation
|
||||
* Copyright (C) 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include "iwl-config.h"
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_WH_NVM_VERSION 0x0a1d
|
||||
|
||||
#define IWL_DEVICE_WH \
|
||||
.ht_params = { \
|
||||
.stbc = true, \
|
||||
|
|
@ -19,7 +16,6 @@
|
|||
.vht_mu_mimo_supported = true, \
|
||||
.uhb_supported = true, \
|
||||
.num_rbds = IWL_NUM_RBDS_EHT, \
|
||||
.nvm_ver = IWL_WH_NVM_VERSION, \
|
||||
.nvm_type = IWL_NVM_EXT
|
||||
|
||||
/* currently iwl_rf_wh/iwl_rf_wh_160mhz are just defines for the FM ones */
|
||||
|
|
@ -33,6 +29,8 @@ 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_killer_be1735x_name[] =
|
||||
"Killer(TM) Wi-Fi 7 BE1735x 160MHz Wireless Network Adapter (BE213)";
|
||||
|
||||
const char iwl_be211_name[] = "Intel(R) Wi-Fi 7 BE211 320MHz";
|
||||
const char iwl_be213_name[] = "Intel(R) Wi-Fi 7 BE213 160MHz";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/stringify.h>
|
||||
|
|
@ -10,17 +10,10 @@
|
|||
#include "fw/api/txq.h"
|
||||
|
||||
/* Highest firmware core release supported */
|
||||
#define IWL_SC_UCODE_CORE_MAX 102
|
||||
#define IWL_SC_UCODE_CORE_MAX 105
|
||||
|
||||
/* Lowest firmware API version supported */
|
||||
#define IWL_SC_UCODE_API_MIN 100
|
||||
|
||||
/* NVM versions */
|
||||
#define IWL_SC_NVM_VERSION 0x0a1d
|
||||
|
||||
/* Memory offsets and lengths */
|
||||
#define IWL_SC_SMEM_OFFSET 0x400000
|
||||
#define IWL_SC_SMEM_LEN 0xD0000
|
||||
/* Lowest firmware core release supported */
|
||||
#define IWL_SC_UCODE_CORE_MIN 101
|
||||
|
||||
#define IWL_SC_A_FM_B_FW_PRE "iwlwifi-sc-a0-fm-b0"
|
||||
#define IWL_SC_A_FM_C_FW_PRE "iwlwifi-sc-a0-fm-c0"
|
||||
|
|
@ -31,14 +24,9 @@
|
|||
static const struct iwl_family_base_params iwl_sc_base = {
|
||||
.num_of_queues = 512,
|
||||
.max_tfd_queue_size = 65536,
|
||||
.shadow_ram_support = true,
|
||||
.led_compensation = 57,
|
||||
.wd_timeout = IWL_LONG_WD_TIMEOUT,
|
||||
.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,
|
||||
.d3_debug_data_base_addr = 0x401000,
|
||||
|
|
@ -78,7 +66,7 @@ static const struct iwl_family_base_params iwl_sc_base = {
|
|||
},
|
||||
.features = IWL_TX_CSUM_NETIF_FLAGS | NETIF_F_RXCSUM,
|
||||
.ucode_api_max = ENCODE_CORE_AS_API(IWL_SC_UCODE_CORE_MAX),
|
||||
.ucode_api_min = IWL_SC_UCODE_API_MIN,
|
||||
.ucode_api_min = ENCODE_CORE_AS_API(IWL_SC_UCODE_CORE_MIN),
|
||||
};
|
||||
|
||||
const struct iwl_mac_cfg iwl_sc_mac_cfg = {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2022, 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2018-2022, 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_fw_api_commands_h__
|
||||
#define __iwl_fw_api_commands_h__
|
||||
|
|
@ -22,7 +22,6 @@
|
|||
* &enum iwl_data_path_subcmd_ids
|
||||
* @SCAN_GROUP: scan group, uses command IDs from
|
||||
* &enum iwl_scan_subcmd_ids
|
||||
* @NAN_GROUP: NAN group, uses command IDs from &enum iwl_nan_subcmd_ids
|
||||
* @LOCATION_GROUP: location group, uses command IDs from
|
||||
* &enum iwl_location_subcmd_ids
|
||||
* @BT_COEX_GROUP: bt coex group, uses command IDs from
|
||||
|
|
@ -43,7 +42,6 @@ enum iwl_mvm_command_groups {
|
|||
PHY_OPS_GROUP = 0x4,
|
||||
DATA_PATH_GROUP = 0x5,
|
||||
SCAN_GROUP = 0x6,
|
||||
NAN_GROUP = 0x7,
|
||||
LOCATION_GROUP = 0x8,
|
||||
BT_COEX_GROUP = 0x9,
|
||||
PROT_OFFLOAD_GROUP = 0xb,
|
||||
|
|
@ -59,8 +57,7 @@ enum iwl_legacy_cmds {
|
|||
/**
|
||||
* @UCODE_ALIVE_NTFY:
|
||||
* Alive data from the firmware, as described in
|
||||
* &struct iwl_alive_ntf_v3 or &struct iwl_alive_ntf_v4 or
|
||||
* &struct iwl_alive_ntf_v5 or &struct iwl_alive_ntf_v7.
|
||||
* &struct iwl_alive_ntf_v3 or &struct iwl_alive_ntf_v7.
|
||||
*/
|
||||
UCODE_ALIVE_NTFY = 0x1,
|
||||
|
||||
|
|
@ -386,7 +383,7 @@ enum iwl_legacy_cmds {
|
|||
* @STATISTICS_NOTIFICATION:
|
||||
* one of &struct iwl_notif_statistics_v10,
|
||||
* &struct iwl_notif_statistics_v11,
|
||||
* &struct iwl_notif_statistic,
|
||||
* &struct iwl_notif_statistics,
|
||||
* &struct iwl_statistics_operational_ntfy_ver_14
|
||||
* &struct iwl_statistics_operational_ntfy
|
||||
*/
|
||||
|
|
@ -560,7 +557,7 @@ enum iwl_legacy_cmds {
|
|||
WOWLAN_CONFIGURATION = 0xe1,
|
||||
|
||||
/**
|
||||
* @WOWLAN_TSC_RSC_PARAM: &struct iwl_wowlan_rsc_tsc_params_cmd_v4,
|
||||
* @WOWLAN_TSC_RSC_PARAM: &struct iwl_wowlan_rsc_tsc_params_cmd_ver_2,
|
||||
* &struct iwl_wowlan_rsc_tsc_params_cmd
|
||||
*/
|
||||
WOWLAN_TSC_RSC_PARAM = 0xe2,
|
||||
|
|
@ -653,7 +650,7 @@ enum iwl_system_subcmd_ids {
|
|||
enum iwl_statistics_subcmd_ids {
|
||||
/**
|
||||
* @STATISTICS_OPER_NOTIF: Notification about operational
|
||||
* statistics &struct iwl_system_statistics_notif_oper
|
||||
* statistics &struct iwl_system_statistics_notif_oper_v3
|
||||
*/
|
||||
STATISTICS_OPER_NOTIF = 0x0,
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 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,6 +91,13 @@ enum iwl_data_path_subcmd_ids {
|
|||
*/
|
||||
SEC_KEY_CMD = 0x18,
|
||||
|
||||
/**
|
||||
* @RSC_NOTIF: notification to update each Rx queue with the RSC. This
|
||||
* notification is sent after resume and uses
|
||||
* &struct iwl_wowlan_all_rsc_tsc_v5.
|
||||
*/
|
||||
RSC_NOTIF = 0xF1,
|
||||
|
||||
/**
|
||||
* @ESR_MODE_NOTIF: notification to recommend/force a wanted esr mode,
|
||||
* uses &struct iwl_esr_mode_notif or &struct iwl_esr_mode_notif_v1
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
/*
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2018-2022 Intel Corporation
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_fw_api_location_h__
|
||||
#define __iwl_fw_api_location_h__
|
||||
|
|
@ -81,12 +81,53 @@ enum iwl_location_subcmd_ids {
|
|||
* @TOF_RANGE_RESPONSE_NOTIF: ranging response, using one of
|
||||
* &struct iwl_tof_range_rsp_ntfy_v5,
|
||||
* &struct iwl_tof_range_rsp_ntfy_v6,
|
||||
* &struct iwl_tof_range_rsp_ntfy_v7 or
|
||||
* &struct iwl_tof_range_rsp_ntfy_v8
|
||||
* &struct iwl_tof_range_rsp_ntfy_v7,
|
||||
* &struct iwl_tof_range_rsp_ntfy_v9 or
|
||||
* &struct iwl_tof_range_rsp_ntfy
|
||||
*/
|
||||
TOF_RANGE_RESPONSE_NOTIF = 0xFF,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_location_frame_format - location frame formats
|
||||
* @IWL_LOCATION_FRAME_FORMAT_LEGACY: legacy
|
||||
* @IWL_LOCATION_FRAME_FORMAT_HT: HT
|
||||
* @IWL_LOCATION_FRAME_FORMAT_VHT: VHT
|
||||
* @IWL_LOCATION_FRAME_FORMAT_HE: HE
|
||||
*/
|
||||
enum iwl_location_frame_format {
|
||||
IWL_LOCATION_FRAME_FORMAT_LEGACY,
|
||||
IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FRAME_FORMAT_VHT,
|
||||
IWL_LOCATION_FRAME_FORMAT_HE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_location_bw - location bandwidth selection
|
||||
* @IWL_LOCATION_BW_20MHZ: 20 MHz
|
||||
* @IWL_LOCATION_BW_40MHZ: 40 MHz
|
||||
* @IWL_LOCATION_BW_80MHZ: 80 MHz
|
||||
* @IWL_LOCATION_BW_160MHZ: 160 MHz
|
||||
* @IWL_LOCATION_BW_320MHZ: 320 MHz
|
||||
*/
|
||||
enum iwl_location_bw {
|
||||
IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_BW_40MHZ,
|
||||
IWL_LOCATION_BW_80MHZ,
|
||||
IWL_LOCATION_BW_160MHZ,
|
||||
IWL_LOCATION_BW_320MHZ,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_location_format_bw - format/BW encoding
|
||||
* @IWL_LOCATION_FMT_BW_FORMAT: &enum iwl_location_frame_format
|
||||
* @IWL_LOCATION_FMT_BW_BANDWIDTH: &enum iwl_location_bw
|
||||
*/
|
||||
enum iwl_location_format_bw {
|
||||
IWL_LOCATION_FMT_BW_FORMAT = 0x0f,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH = 0xf0,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_tof_config_cmd - ToF configuration
|
||||
* @tof_disabled: indicates if ToF is disabled (or not)
|
||||
|
|
@ -263,8 +304,7 @@ struct iwl_tof_responder_config_cmd_v6 {
|
|||
* struct iwl_tof_responder_config_cmd_v7 - ToF AP mode (for debug)
|
||||
* @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
|
||||
* @responder_cfg_flags: &iwl_tof_responder_cfg_flags
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @rate: current AP rate
|
||||
* @channel_num: current AP Channel
|
||||
* @ctrl_ch_position: coding of the control channel position relative to
|
||||
|
|
@ -302,8 +342,7 @@ struct iwl_tof_responder_config_cmd_v7 {
|
|||
* struct iwl_tof_responder_config_cmd_v8 - ToF AP mode (for debug)
|
||||
* @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
|
||||
* @responder_cfg_flags: &iwl_tof_responder_cfg_flags
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @rate: current AP rate
|
||||
* @channel_num: current AP Channel
|
||||
* @ctrl_ch_position: coding of the control channel position relative to
|
||||
|
|
@ -348,8 +387,7 @@ struct iwl_tof_responder_config_cmd_v8 {
|
|||
* struct iwl_tof_responder_config_cmd_v9 - ToF AP mode (for debug)
|
||||
* @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
|
||||
* @responder_cfg_flags: &iwl_tof_responder_cfg_flags
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @bss_color: current AP bss_color
|
||||
* @channel_num: current AP Channel
|
||||
* @ctrl_ch_position: coding of the control channel position relative to
|
||||
|
|
@ -400,8 +438,7 @@ struct iwl_tof_responder_config_cmd_v9 {
|
|||
* struct iwl_tof_responder_config_cmd - ToF AP mode
|
||||
* @cmd_valid_fields: &iwl_tof_responder_cmd_valid_field
|
||||
* @responder_cfg_flags: &iwl_tof_responder_cfg_flags
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @bss_color: current AP bss_color
|
||||
* @channel_num: current AP Channel
|
||||
* @ctrl_ch_position: coding of the control channel position relative to
|
||||
|
|
@ -676,44 +713,13 @@ struct iwl_tof_range_req_ap_entry_v3 {
|
|||
__le32 tsf_delta;
|
||||
} __packed; /* LOCATION_RANGE_REQ_AP_ENTRY_CMD_API_S_VER_3 */
|
||||
|
||||
/**
|
||||
* enum iwl_location_frame_format - location frame formats
|
||||
* @IWL_LOCATION_FRAME_FORMAT_LEGACY: legacy
|
||||
* @IWL_LOCATION_FRAME_FORMAT_HT: HT
|
||||
* @IWL_LOCATION_FRAME_FORMAT_VHT: VHT
|
||||
* @IWL_LOCATION_FRAME_FORMAT_HE: HE
|
||||
*/
|
||||
enum iwl_location_frame_format {
|
||||
IWL_LOCATION_FRAME_FORMAT_LEGACY,
|
||||
IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FRAME_FORMAT_VHT,
|
||||
IWL_LOCATION_FRAME_FORMAT_HE,
|
||||
};
|
||||
|
||||
/**
|
||||
* enum iwl_location_bw - location bandwidth selection
|
||||
* @IWL_LOCATION_BW_20MHZ: 20MHz
|
||||
* @IWL_LOCATION_BW_40MHZ: 40MHz
|
||||
* @IWL_LOCATION_BW_80MHZ: 80MHz
|
||||
* @IWL_LOCATION_BW_160MHZ: 160MHz
|
||||
*/
|
||||
enum iwl_location_bw {
|
||||
IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_BW_40MHZ,
|
||||
IWL_LOCATION_BW_80MHZ,
|
||||
IWL_LOCATION_BW_160MHZ,
|
||||
};
|
||||
|
||||
#define TK_11AZ_LEN 32
|
||||
|
||||
#define LOCATION_BW_POS 4
|
||||
|
||||
/**
|
||||
* struct iwl_tof_range_req_ap_entry_v4 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -763,8 +769,7 @@ enum iwl_location_cipher {
|
|||
* struct iwl_tof_range_req_ap_entry_v6 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -810,8 +815,7 @@ struct iwl_tof_range_req_ap_entry_v6 {
|
|||
* struct iwl_tof_range_req_ap_entry_v7 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -868,8 +872,7 @@ struct iwl_tof_range_req_ap_entry_v7 {
|
|||
* struct iwl_tof_range_req_ap_entry_v8 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -939,8 +942,7 @@ struct iwl_tof_range_req_ap_entry_v8 {
|
|||
* struct iwl_tof_range_req_ap_entry_v9 - AP configuration parameters
|
||||
* @initiator_ap_flags: see &enum iwl_initiator_ap_flags.
|
||||
* @channel_num: AP Channel number
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @ftmr_max_retries: Max number of retries to send the FTMR in case of no
|
||||
|
|
@ -1024,8 +1026,7 @@ struct iwl_tof_range_req_ap_entry_v9 {
|
|||
* @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
|
||||
* @format_bw: bits 0 - 3: &enum iwl_location_frame_format.
|
||||
* bits 4 - 7: &enum iwl_location_bw.
|
||||
* @format_bw: &enum iwl_location_format_bw
|
||||
* @ctrl_ch_position: Coding of the control channel position relative to the
|
||||
* center frequency, see iwl_mvm_get_ctrl_pos().
|
||||
* @bssid: AP's BSSID
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#define __iwl_fw_api_mac_cfg_h__
|
||||
|
||||
#include "mac.h"
|
||||
#include "phy-ctxt.h"
|
||||
|
||||
/**
|
||||
* enum iwl_mac_conf_subcmd_ids - mac configuration command IDs
|
||||
|
|
@ -71,6 +72,23 @@ enum iwl_mac_conf_subcmd_ids {
|
|||
* @NAN_CFG_CMD: &struct iwl_nan_config_cmd
|
||||
*/
|
||||
NAN_CFG_CMD = 0x12,
|
||||
/**
|
||||
* @NAN_SCHEDULE_CMD: &struct iwl_nan_schedule_cmd
|
||||
*/
|
||||
NAN_SCHEDULE_CMD = 0x13,
|
||||
/**
|
||||
* @NAN_PEER_CMD: &struct iwl_nan_peer_cmd
|
||||
*/
|
||||
NAN_PEER_CMD = 0x14,
|
||||
/**
|
||||
* @NAN_ULW_ATTR_NOTIF: &struct iwl_nan_ulw_attr_notif
|
||||
*/
|
||||
NAN_ULW_ATTR_NOTIF = 0xf2,
|
||||
/**
|
||||
* @NAN_SCHED_UPDATE_COMPLETED_NOTIF:
|
||||
* &struct iwl_nan_sched_update_completed_notif
|
||||
*/
|
||||
NAN_SCHED_UPDATE_COMPLETED_NOTIF = 0xf3,
|
||||
/**
|
||||
* @NAN_DW_END_NOTIF: &struct iwl_nan_dw_end_notif
|
||||
*/
|
||||
|
|
@ -536,12 +554,20 @@ enum iwl_link_ctx_protection_flags {
|
|||
* radar pulses).
|
||||
* @LINK_FLG_NDP_FEEDBACK_ENABLED: mark support for NDP feedback and change
|
||||
* of threshold
|
||||
* @LINK_FLG_NPCA: NPCA enabled
|
||||
* @LINK_FLG_DPS: AP is a DPS assisting AP
|
||||
* @LINK_FLG_MLPM: AP supports UHR multi-link PM
|
||||
* @LINK_FLG_DUO: AP supports UHR DUO
|
||||
*/
|
||||
enum iwl_link_ctx_flags {
|
||||
LINK_FLG_BSS_COLOR_DIS = BIT(0),
|
||||
LINK_FLG_MU_EDCA_CW = BIT(1),
|
||||
LINK_FLG_RU_2MHZ_BLOCK = BIT(2),
|
||||
LINK_FLG_NDP_FEEDBACK_ENABLED = BIT(3),
|
||||
LINK_FLG_NPCA = BIT(4),
|
||||
LINK_FLG_DPS = BIT(6),
|
||||
LINK_FLG_MLPM = BIT(7),
|
||||
LINK_FLG_DUO = BIT(8),
|
||||
}; /* LINK_CONTEXT_FLAG_E_VER_1 */
|
||||
|
||||
/**
|
||||
|
|
@ -578,7 +604,7 @@ enum iwl_npca_flags {
|
|||
* @initial_qsrc: Indicates the value that is used to initialize the
|
||||
* EDCAF QSRC[AC] variables
|
||||
* @min_dur_threshold: minimum PPDU time to switch to the non-primary
|
||||
* NPCA channel (usec)
|
||||
* NPCA channel (spec representation)
|
||||
* @flags: NPCA flags, see &enum iwl_npca_flags
|
||||
* @reserved: reserved for alignment purposes
|
||||
*/
|
||||
|
|
@ -726,6 +752,12 @@ struct iwl_link_config_cmd {
|
|||
* @STATION_TYPE_NAN_PEER_NDI: NAN data peer station type. A station
|
||||
* of this type can have any number of links (even none) set in the
|
||||
* link_mask. (Supported since version 3.)
|
||||
* @STATION_TYPE_NAN_BCAST: NAN station used for synchronization and
|
||||
* discovery. No queue is associated with this station.
|
||||
* @STATION_TYPE_NAN_MGMT: NAN station used for NAN management frames, e.g.,
|
||||
* SDFs and NAFs.
|
||||
* @STATION_TYPE_NAN_MCAST_DATA: NAN station used for multicast NAN data
|
||||
* frames.
|
||||
* @STATION_TYPE_MAX: maximum number of FW station types
|
||||
* @STATION_TYPE_AUX: aux sta. In the FW there is no need for a special type
|
||||
* for the aux sta, so this type is only for driver - internal use.
|
||||
|
|
@ -736,6 +768,9 @@ enum iwl_fw_sta_type {
|
|||
STATION_TYPE_MCAST,
|
||||
STATION_TYPE_NAN_PEER_NMI,
|
||||
STATION_TYPE_NAN_PEER_NDI,
|
||||
STATION_TYPE_NAN_BCAST,
|
||||
STATION_TYPE_NAN_MGMT,
|
||||
STATION_TYPE_NAN_MCAST_DATA,
|
||||
STATION_TYPE_MAX,
|
||||
STATION_TYPE_AUX = STATION_TYPE_MAX /* this doesn't exist in FW */
|
||||
}; /* STATION_TYPE_E_VER_1, _VER_2 */
|
||||
|
|
@ -873,7 +908,9 @@ struct iwl_sta_cfg_cmd_v2 {
|
|||
* ( STA_CONFIG_CMD = 0xA )
|
||||
*
|
||||
* @sta_id: index of station in uCode's station table
|
||||
* @link_mask: bitmap of link FW IDs used with this STA
|
||||
* @link_mask: bitmap of link FW IDs used with this STA. Should be set to 0
|
||||
* for STATION_TYPE_NAN_BCAST and STATION_TYPE_NAN_MGMT as they are not
|
||||
* associated with any link added by the driver.
|
||||
* @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
|
||||
|
|
@ -908,7 +945,8 @@ struct iwl_sta_cfg_cmd_v2 {
|
|||
* @mic_prep_pad_delay: MIC prep time padding
|
||||
* @mic_compute_pad_delay: MIC compute time padding
|
||||
* @nmi_sta_id: for an NDI peer STA, the NMI peer STA ID it relates to
|
||||
* @ndi_local_addr: for an NDI peer STA, the local NDI interface MAC address
|
||||
* @ndi_local_addr: for an NDI peer STA or NAN multicast data station,
|
||||
* the local NDI interface MAC address
|
||||
* @reserved: Reserved for alignment
|
||||
*/
|
||||
struct iwl_sta_cfg_cmd {
|
||||
|
|
@ -1204,7 +1242,8 @@ enum iwl_nan_flags {
|
|||
* @discovery_beacon_interval: discovery beacon interval in TUs
|
||||
* @cluster_id: lower last two bytes of the cluster ID, in case the local
|
||||
* device starts a cluster
|
||||
* @sta_id: station ID of the NAN station
|
||||
* @sta_id: station ID of the NAN station. Used only in version 1, in version 2
|
||||
* it is reserved.
|
||||
* @hb_channel: channel for 5 GHz if the device supports operation on 5 GHz.
|
||||
* Valid values are 44 and 149, which correspond to the 5 GHz channel, and
|
||||
* 0 which means that NAN operation on the 5 GHz band is disabled.
|
||||
|
|
@ -1242,7 +1281,102 @@ struct iwl_nan_config_cmd {
|
|||
__le32 nan_attr_len;
|
||||
__le32 nan_vendor_elems_len;
|
||||
u8 beacon_data[];
|
||||
} __packed; /* NAN_CONFIG_CMD_API_S_VER_1 */
|
||||
} __packed; /* NAN_CONFIG_CMD_API_S_VER_1, NAN_CONFIG_CMD_API_S_VER_2 */
|
||||
|
||||
/**
|
||||
* struct iwl_nan_schedule_cmd_v1 - NAN schedule command
|
||||
* @channels: per channel information
|
||||
* @channels.availability_map: bitmap of slots this channel is advertising
|
||||
* availability on, will be ULW'ed out if no link/inactive link is
|
||||
* referenced by the link ID below
|
||||
* @channels.channel_entry: NAN channel entry descriptor
|
||||
* @channels.link_id: FW link ID, or %0xFF for unset
|
||||
* @channels.reserved: (reserved)
|
||||
*/
|
||||
struct iwl_nan_schedule_cmd_v1 {
|
||||
struct {
|
||||
__le32 availability_map;
|
||||
u8 channel_entry[6];
|
||||
u8 link_id;
|
||||
u8 reserved;
|
||||
} __packed channels[NUM_PHY_CTX];
|
||||
} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_1 */
|
||||
|
||||
#define IWL_MAX_AVAILABILITY_ATTR_LEN 54
|
||||
|
||||
/**
|
||||
* struct iwl_nan_schedule_cmd - NAN schedule command
|
||||
* @channels: per channel information
|
||||
* @channels.availability_map: bitmap of slots this channel is advertising
|
||||
* availability on, will be ULW'ed out if no link/inactive link is
|
||||
* referenced by the link ID below
|
||||
* @channels.channel_entry: NAN channel entry descriptor
|
||||
* @channels.link_id: FW link ID, or %0xFF for unset
|
||||
* @channels.reserved: (reserved)
|
||||
* @avail_attr: NAN availability attribute information
|
||||
* @avail_attr.attr_len: length of the availability attribute
|
||||
* @avail_attr.reserved: reserved
|
||||
* @avail_attr.attr: the availability attribute including the attribute header
|
||||
* @deferred: true if the firmware should defer applying the schedule until
|
||||
* notifying all peers. For a deferred schedule update, the firmware should
|
||||
* send a notification to the driver after the new schedule is applied.
|
||||
* @reserved: reserved
|
||||
*/
|
||||
struct iwl_nan_schedule_cmd {
|
||||
struct {
|
||||
__le32 availability_map;
|
||||
u8 channel_entry[6];
|
||||
u8 link_id;
|
||||
u8 reserved;
|
||||
} __packed channels[NUM_PHY_CTX];
|
||||
|
||||
struct {
|
||||
u8 attr_len;
|
||||
u8 reserved;
|
||||
u8 attr[IWL_MAX_AVAILABILITY_ATTR_LEN];
|
||||
} __packed avail_attr;
|
||||
|
||||
u8 deferred;
|
||||
u8 reserved[3];
|
||||
} __packed; /* NAN_SCHEDULE_CMD_API_S_VER_2 */
|
||||
|
||||
/**
|
||||
* struct iwl_nan_peer_cmd - NAN peer command
|
||||
* @nmi_sta_id: NAN management station ID
|
||||
* @sequence_id: NAN Availability attribute sequence ID
|
||||
* @committed_dw_info: committed DW info from the NAN Device
|
||||
* Capability attribute
|
||||
* @max_channel_switch_time: maximum channel switch time
|
||||
* (in microseconds); 0 means unavailable
|
||||
* @reserved: (reserved)
|
||||
* @per_phy: per-PHY information for this peer, indexed by PHY ID
|
||||
* @per_phy.availability_map: bitmap of which slots this peer
|
||||
* is available in on this PHY. 0 indicates the this per-PHY entry
|
||||
* is unused.
|
||||
* @per_phy.channel_entry: the channel description the peer is using,
|
||||
* used for comparisons in ULW management
|
||||
* @per_phy.link_id: FW link ID, should be a valid id.
|
||||
* @per_phy.map_id: map ID from peer's NAN Availability attributec
|
||||
* @initial_ulw_size: size of the initial ULW blob
|
||||
* @initial_ulw: initial ULW data from the peer
|
||||
*/
|
||||
struct iwl_nan_peer_cmd {
|
||||
u8 nmi_sta_id;
|
||||
u8 sequence_id;
|
||||
__le16 committed_dw_info;
|
||||
__le16 max_channel_switch_time;
|
||||
__le16 reserved;
|
||||
|
||||
struct {
|
||||
__le32 availability_map;
|
||||
u8 channel_entry[6];
|
||||
u8 link_id;
|
||||
u8 map_id;
|
||||
} __packed per_phy[NUM_PHY_CTX];
|
||||
|
||||
__le32 initial_ulw_size;
|
||||
u8 initial_ulw[];
|
||||
} __packed; /* NAN_PEER_SCHEDULE_CMD_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* enum iwl_nan_cluster_notif_flags - flags for the cluster notification
|
||||
|
|
@ -1280,4 +1414,44 @@ struct iwl_nan_dw_end_notif {
|
|||
u8 reserved[3];
|
||||
} __packed; /* NAN_DW_END_NTF_API_S_VER_1 */
|
||||
|
||||
#define IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN 48
|
||||
|
||||
/**
|
||||
* struct iwl_nan_ulw_attr_notif - sent to notify the host of a change in the
|
||||
* ULW attribute
|
||||
*
|
||||
* @attr_len: length of the ULW attribute in bytes
|
||||
* @reserved: reserved
|
||||
* @attr: the ULW attribute including the attribute header
|
||||
*/
|
||||
struct iwl_nan_ulw_attr_notif {
|
||||
u8 attr_len;
|
||||
u8 reserved[3];
|
||||
u8 attr[IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN];
|
||||
} __packed; /* NAN_ULW_ATTR_NOTIF_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* enum iwl_nan_sched_update_status - NAN schedule update status
|
||||
*
|
||||
* @IWL_NAN_SCHED_UPDATE_SUCCESS: schedule update completed successfully
|
||||
* @IWL_NAN_SCHED_UPDATE_FAILURE: schedule update failed. Currently not expected
|
||||
* to happen, but reserved for future use.
|
||||
*/
|
||||
enum iwl_nan_sched_update_status {
|
||||
IWL_NAN_SCHED_UPDATE_SUCCESS = 0,
|
||||
IWL_NAN_SCHED_UPDATE_FAILURE = 1,
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_nan_sched_update_completed_notif - NAN schedule update completed
|
||||
*
|
||||
* @status: status of the schedule update operation. See
|
||||
* &enum iwl_nan_sched_update_status
|
||||
* @reserved: reserved
|
||||
*/
|
||||
struct iwl_nan_sched_update_completed_notif {
|
||||
u8 status;
|
||||
u8 reserved[3];
|
||||
} __packed; /* NAN_SCHED_UPDATE_COMPLETED_NTF_API_S_VER_1 */
|
||||
|
||||
#endif /* __iwl_fw_api_mac_cfg_h__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -85,12 +85,13 @@ struct iwl_ltr_config_cmd {
|
|||
* '1' PM could sleep over DTIM till listen Interval.
|
||||
* @POWER_FLAGS_SNOOZE_ENA_MSK: Enable snoozing only if uAPSD is enabled and all
|
||||
* access categories are both delivery and trigger enabled.
|
||||
* (Not supported since version 3)
|
||||
* @POWER_FLAGS_BT_SCO_ENA: Enable BT SCO coex only if uAPSD and
|
||||
* PBW Snoozing enabled
|
||||
* @POWER_FLAGS_ADVANCE_PM_ENA_MSK: Advanced PM (uAPSD) enable mask
|
||||
* @POWER_FLAGS_LPRX_ENA_MSK: Low Power RX enable.
|
||||
* @POWER_FLAGS_UAPSD_MISBEHAVING_ENA_MSK: AP/GO's uAPSD misbehaving
|
||||
* detection enablement
|
||||
* detection enablement (Not supported since version 3)
|
||||
* @POWER_FLAGS_ENABLE_SMPS_MSK: SMPS is allowed for this vif
|
||||
*/
|
||||
enum iwl_power_flags {
|
||||
|
|
@ -175,9 +176,9 @@ struct iwl_device_power_cmd {
|
|||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_mac_power_cmd - New power command containing uAPSD support
|
||||
* struct iwl_mac_power_cmd_v2 - power command V2 containing uAPSD support
|
||||
* MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
|
||||
* @id_and_color: MAC contex identifier, &enum iwl_ctxt_id_and_color
|
||||
* @id_and_color: MAC context identifier, &enum iwl_ctxt_id_and_color
|
||||
* @flags: Power table command flags from POWER_FLAGS_*
|
||||
* @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
|
||||
* Minimum allowed:- 3 * DTIM. Keep alive period must be
|
||||
|
|
@ -216,7 +217,7 @@ struct iwl_device_power_cmd {
|
|||
* @limited_ps_threshold: (unused)
|
||||
* @reserved: reserved (padding)
|
||||
*/
|
||||
struct iwl_mac_power_cmd {
|
||||
struct iwl_mac_power_cmd_v2 {
|
||||
/* CONTEXT_DESC_API_T_VER_1 */
|
||||
__le32 id_and_color;
|
||||
|
||||
|
|
@ -242,6 +243,43 @@ struct iwl_mac_power_cmd {
|
|||
u8 reserved;
|
||||
} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_1, VER_2 */
|
||||
|
||||
/**
|
||||
* struct iwl_mac_power_cmd - power command
|
||||
* MAC_PM_POWER_TABLE = 0xA9 (command, has simple generic response)
|
||||
* @id_and_color: MAC context identifier, &enum iwl_ctxt_id_and_color
|
||||
* @flags: Power table command flags from POWER_FLAGS_*
|
||||
* @keep_alive_seconds: Keep alive period in seconds. Default - 25 sec.
|
||||
* Minimum allowed:- 3 * DTIM. Keep alive period must be
|
||||
* set regardless of power scheme or current power state.
|
||||
* FW use this value also when PM is disabled.
|
||||
* @rx_data_timeout: Minimum time (usec) from last Rx packet for AM to
|
||||
* PSM transition - legacy PM
|
||||
* @tx_data_timeout: Minimum time (usec) from last Tx packet for AM to
|
||||
* PSM transition - legacy PM
|
||||
* @lprx_rssi_threshold: Signal strength up to which LP RX can be enabled.
|
||||
* Default: 80dbm
|
||||
* @skip_dtim_periods: Number of DTIM periods to skip if Skip over DTIM flag
|
||||
* is set. For example, if it is required to skip over
|
||||
* one DTIM, this value need to be set to 2 (DTIM periods).
|
||||
* @qndp_tid: TID client shall use for uAPSD QNDP triggers
|
||||
* @uapsd_ac_flags: Set trigger-enabled and delivery-enabled indication for
|
||||
* each corresponding AC.
|
||||
* Use IEEE80211_WMM_IE_STA_QOSINFO_AC* for correct values.
|
||||
*/
|
||||
struct iwl_mac_power_cmd {
|
||||
/* CONTEXT_DESC_API_T_VER_1 */
|
||||
__le32 id_and_color;
|
||||
|
||||
__le16 flags;
|
||||
__le16 keep_alive_seconds;
|
||||
__le32 rx_data_timeout;
|
||||
__le32 tx_data_timeout;
|
||||
u8 lprx_rssi_threshold;
|
||||
u8 skip_dtim_periods;
|
||||
u8 qndp_tid;
|
||||
u8 uapsd_ac_flags;
|
||||
} __packed; /* CLIENT_PM_POWER_TABLE_S_VER_3 */
|
||||
|
||||
/*
|
||||
* struct iwl_uapsd_misbehaving_ap_notif - FW sends this notification when
|
||||
* associated AP is identified as improperly implementing uAPSD protocol.
|
||||
|
|
|
|||
|
|
@ -263,6 +263,9 @@ enum iwl_rx_mpdu_reorder_data {
|
|||
|
||||
enum iwl_rx_mpdu_phy_info {
|
||||
IWL_RX_MPDU_PHY_EOF_INDICATION = BIT(0),
|
||||
IWL_RX_MPDU_PHY_UEQM = BIT(1) | BIT(2),
|
||||
IWL_RX_MPDU_PHY_2X_LDPC = BIT(3),
|
||||
IWL_RX_MPDU_PHY_NPCA = BIT(4),
|
||||
IWL_RX_MPDU_PHY_AMPDU = BIT(5),
|
||||
IWL_RX_MPDU_PHY_AMPDU_TOGGLE = BIT(6),
|
||||
IWL_RX_MPDU_PHY_SHORT_PREAMBLE = BIT(7),
|
||||
|
|
@ -732,6 +735,8 @@ struct iwl_rx_mpdu_desc {
|
|||
* RX_MPDU_RES_START_API_S_VER_4,
|
||||
* RX_MPDU_RES_START_API_S_VER_5,
|
||||
* RX_MPDU_RES_START_API_S_VER_6
|
||||
* RX_MPDU_RES_START_API_S_VER_7
|
||||
* RX_MPDU_RES_START_API_S_VER_8
|
||||
*/
|
||||
|
||||
#define IWL_RX_DESC_SIZE_V1 offsetofend(struct iwl_rx_mpdu_desc, v1)
|
||||
|
|
@ -1169,8 +1174,14 @@ struct iwl_he_tb_sigs {
|
|||
#define OFDM_UCODE_TRIG_BASE_RX_BANDWIDTH 0x00007000
|
||||
#define OFDM_UCODE_TRIG_BASE_PS160 0x00008000
|
||||
#define OFDM_UCODE_EHT_TRIG_CONTROL_CHANNEL 0x000f0000
|
||||
/* for UHR, since PE RF: */
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_MCS_UHR_AND_LATER 0x01f00000
|
||||
#define OFDM_UCODE_RX_IS_DRU 0x02000000
|
||||
#define OFDM_UCODE_RX_IS_2XLDPC_ENABLED 0x04000000
|
||||
#define OFDM_UCODE_RX_DRU_DBW 0x60000000
|
||||
__le32 tb_rx0;
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_MCS 0x0000000f
|
||||
/* prior to PE RF, cannot have 5 bits needed for UHR: */
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_MCS_PRE_UHR 0x0000000f
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_DCM 0x00000010
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_GI_LTF_TYPE 0x00000060
|
||||
#define OFDM_UCODE_TRIG_BASE_RX_NSTS 0x00000380
|
||||
|
|
@ -1275,15 +1286,16 @@ struct iwl_uhr_sigs {
|
|||
__le32 usig_a1;
|
||||
#define OFDM_RX_FRAME_UHR_BSS_COLOR2 0x0000003f
|
||||
__le32 usig_a1_uhr;
|
||||
#define OFDM_RX_FRAME_UHR_PPDU_TYPE 0x00000003
|
||||
#define OFDM_RX_FRAME_UHR_COBF_CSR_DISABLE 0x00000004
|
||||
#define OFDM_RX_FRAME_UHR_PUNC_CHANNEL 0x000000f8
|
||||
#define OFDM_RX_FRAME_UHR_USIG2_VALIDATE_B8 0x00000100
|
||||
#define OFDM_RX_FRAME_UHR_SIG_MCS 0x00000600
|
||||
#define OFDM_RX_FRAME_UHR_SIG_SYM_NUM 0x0000f800
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_1 0x000f0000
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_2 0x00f00000
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_USIG2_DISREGARD 0x1f000000
|
||||
#define OFDM_RX_FRAME_UHR_PPDU_TYPE 0x00000003
|
||||
#define OFDM_RX_FRAME_UHR_COBF_CSR_DISABLE 0x00000004
|
||||
#define OFDM_RX_FRAME_UHR_PUNC_CHANNEL 0x000000f8
|
||||
#define OFDM_RX_FRAME_UHR_USIG2_VALIDATE_B8 0x00000100
|
||||
#define OFDM_RX_FRAME_UHR_SIG_MCS 0x00000600
|
||||
#define OFDM_RX_FRAME_UHR_SIG_SYM_NUM 0x0000f800
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_1 0x000f0000
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_SPATIAL_REUSE_2 0x00f00000
|
||||
#define OFDM_RX_FRAME_UHR_TRIG_USIG2_DISREGARD 0x1f000000
|
||||
#define OFDM_RX_FRAME_UHR_USIG_CRC_OK 0x40000000
|
||||
__le32 usig_a2_uhr;
|
||||
#define OFDM_RX_FRAME_UHR_SPATIAL_REUSE 0x0000000f
|
||||
#define OFDM_RX_FRAME_UHR_GI_LTF_TYPE 0x00000030
|
||||
|
|
@ -1305,6 +1317,7 @@ struct iwl_uhr_sigs {
|
|||
#define OFDM_RX_FRAME_UHR_CODING 0x00000200
|
||||
#define OFDM_RX_FRAME_UHR_SPATIAL_CONFIG 0x00003c00
|
||||
#define OFDM_RX_FRAME_UHR_STA_RU 0x003fc000
|
||||
#define OFDM_RX_FRAME_UHR_STA_RU_P80 0x00004000
|
||||
#define OFDM_RX_FRAME_UHR_STA_RU_PS160 0x00400000
|
||||
#define OFDM_RX_FRAME_UHR_UEQM 0x00800000
|
||||
#define OFDM_RX_FRAME_UHR_2XLDPC 0x01000000
|
||||
|
|
@ -1330,7 +1343,12 @@ struct iwl_uhr_tb_sigs {
|
|||
|
||||
struct iwl_uhr_elr_sigs {
|
||||
/* same as UHR above */
|
||||
__le32 usig_a1, usig_a2_uhr;
|
||||
__le32 usig_a1;
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_PPDU_TYPE 0x00000003
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_USIG_STA_ID 0x00001ffc
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_VALIDATE 0x0000e000
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_CRC_OK 0x00010000
|
||||
__le32 usig_a2_uhr_elr;
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_VER_ID 0x00000007
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_UPLINK_FLAG 0x00000008
|
||||
#define OFDM_RX_VECTOR_UHR_ELR_MCS 0x00000010
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018-2021, 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018-2021, 2023, 2025-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -191,7 +191,6 @@ 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)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2012-2014, 2018, 2020-2021, 2023-2025 Intel Corporation
|
||||
* Copyright (C) 2012-2014, 2018, 2020-2021, 2023-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -557,7 +557,7 @@ struct iwl_stats_ntfy_per_mac {
|
|||
|
||||
#define IWL_STATS_MAX_BW_INDEX 5
|
||||
/**
|
||||
* struct iwl_stats_ntfy_per_phy - per PHY statistics
|
||||
* struct iwl_stats_ntfy_per_phy_v1 - per PHY statistics
|
||||
* @channel_load: channel load
|
||||
* @channel_load_by_us: device contribution to MCLM
|
||||
* @channel_load_not_by_us: other devices' contribution to MCLM
|
||||
|
|
@ -572,7 +572,7 @@ struct iwl_stats_ntfy_per_mac {
|
|||
* per channel BW. note BACK counted as 1
|
||||
* @last_tx_ch_width_indx: last txed frame channel width index
|
||||
*/
|
||||
struct iwl_stats_ntfy_per_phy {
|
||||
struct iwl_stats_ntfy_per_phy_v1 {
|
||||
__le32 channel_load;
|
||||
__le32 channel_load_by_us;
|
||||
__le32 channel_load_not_by_us;
|
||||
|
|
@ -585,6 +585,67 @@ struct iwl_stats_ntfy_per_phy {
|
|||
__le32 last_tx_ch_width_indx;
|
||||
} __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* struct iwl_stats_ntfy_coex - coex statistics
|
||||
*
|
||||
* @wifi_kill_cnt: count of wifi frames killed by BT
|
||||
* @wifi_tx_cts_kill_cnt: count of wifi Tx CTS frames killed by BT
|
||||
* @ttc2_ppdu_error_count: Count PPDU errors on TTC2 - BT Tx indication rises
|
||||
* within wifi Tx packet on non-shared antenna and wifi is NOT killed by
|
||||
* PTA/TCL.
|
||||
* @trc2_ppdu_error_count: count PPDU errors on TRC2 - BT Rx indication rises
|
||||
* within wifi Tx packet on non-shared antenna and wifi is NOT killed by
|
||||
* PTA/TCL
|
||||
* @rrc1_collision_count: count RRC1 - BT Rx indication rises within wifi Rx
|
||||
* packet on shared antenna
|
||||
* @rrc2_collision_count: count RRC2 - BT Rx indication rises within wifi Rx
|
||||
* packet on non-shared antenna
|
||||
* @rtc2_collision_count: count RTC2 - BT Tx indication rises within wifi Rx
|
||||
* packet on non-shared antenna
|
||||
* @reserved: reserved
|
||||
*/
|
||||
struct iwl_stats_ntfy_coex {
|
||||
__le16 wifi_kill_cnt;
|
||||
__le16 wifi_tx_cts_kill_cnt;
|
||||
__le16 ttc2_ppdu_error_count;
|
||||
__le16 trc2_ppdu_error_count;
|
||||
__le16 rrc1_collision_count;
|
||||
__le16 rrc2_collision_count;
|
||||
__le16 rtc2_collision_count;
|
||||
__le16 reserved;
|
||||
} __packed; /* STATISTICS_FW_NTFY_COEX_TELEMETRY_API_S_VER_1 */
|
||||
|
||||
/**
|
||||
* struct iwl_stats_ntfy_per_phy - per PHY statistics
|
||||
* @channel_load: channel load
|
||||
* @channel_load_by_us: device contribution to MCLM
|
||||
* @channel_load_not_by_us: other devices' contribution to MCLM
|
||||
* @clt: CLT HW timer (TIM_CH_LOAD2)
|
||||
* @act: active accumulator SW
|
||||
* @elp: elapsed time accumulator SW
|
||||
* @rx_detected_per_ch_width: number of deferred TX per channel width,
|
||||
* 0 - 20, 1/2/3 - 40/80/160
|
||||
* @success_per_ch_width: number of frames that got ACK/BACK/CTS
|
||||
* per channel BW. note, BACK counted as 1
|
||||
* @fail_per_ch_width: number of frames that didn't get ACK/BACK/CTS
|
||||
* per channel BW. note BACK counted as 1
|
||||
* @last_tx_ch_width_indx: last txed frame channel width index
|
||||
* @coex: coex related data
|
||||
*/
|
||||
struct iwl_stats_ntfy_per_phy {
|
||||
__le32 channel_load;
|
||||
__le32 channel_load_by_us;
|
||||
__le32 channel_load_not_by_us;
|
||||
__le32 clt;
|
||||
__le32 act;
|
||||
__le32 elp;
|
||||
__le32 rx_detected_per_ch_width[IWL_STATS_MAX_BW_INDEX];
|
||||
__le32 success_per_ch_width[IWL_STATS_MAX_BW_INDEX];
|
||||
__le32 fail_per_ch_width[IWL_STATS_MAX_BW_INDEX];
|
||||
__le32 last_tx_ch_width_indx;
|
||||
struct iwl_stats_ntfy_coex coex;
|
||||
} __packed; /* STATISTICS_NTFY_PER_PHY_API_S_VER_2 */
|
||||
|
||||
/* unknown channel load (due to not being active on channel) */
|
||||
#define IWL_STATS_UNKNOWN_CHANNEL_LOAD 0xffffffff
|
||||
|
||||
|
|
@ -599,12 +660,27 @@ struct iwl_stats_ntfy_per_sta {
|
|||
|
||||
#define IWL_STATS_MAX_PHY_OPERATIONAL 3
|
||||
|
||||
/**
|
||||
* struct iwl_system_statistics_notif_oper_v3 - statistics notification
|
||||
*
|
||||
* @time_stamp: time when the notification is sent from firmware
|
||||
* @per_link: per link statistics, &struct iwl_stats_ntfy_per_link
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1
|
||||
* @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta
|
||||
*/
|
||||
struct iwl_system_statistics_notif_oper_v3 {
|
||||
__le32 time_stamp;
|
||||
struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS];
|
||||
struct iwl_stats_ntfy_per_phy_v1 per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
|
||||
struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
|
||||
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */
|
||||
|
||||
/**
|
||||
* struct iwl_system_statistics_notif_oper - statistics notification
|
||||
*
|
||||
* @time_stamp: time when the notification is sent from firmware
|
||||
* @per_link: per link statistics, &struct iwl_stats_ntfy_per_link
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1
|
||||
* @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta
|
||||
*/
|
||||
struct iwl_system_statistics_notif_oper {
|
||||
|
|
@ -612,7 +688,7 @@ struct iwl_system_statistics_notif_oper {
|
|||
struct iwl_stats_ntfy_per_link per_link[IWL_FW_MAX_LINKS];
|
||||
struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
|
||||
struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
|
||||
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_3 */
|
||||
} __packed; /* STATISTICS_FW_NTFY_OPERATIONAL_API_S_VER_4 */
|
||||
|
||||
/**
|
||||
* struct iwl_system_statistics_part1_notif_oper - part1 stats notification
|
||||
|
|
@ -642,7 +718,7 @@ struct iwl_system_statistics_end_notif {
|
|||
* @hdr: general statistics header
|
||||
* @flags: bitmap of possible notification structures
|
||||
* @per_mac: per mac statistics, &struct iwl_stats_ntfy_per_mac
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy
|
||||
* @per_phy: per phy statistics, &struct iwl_stats_ntfy_per_phy_v1
|
||||
* @per_sta: per sta statistics, &struct iwl_stats_ntfy_per_sta
|
||||
* @rx_time: rx time
|
||||
* @tx_time: usec the radio is transmitting.
|
||||
|
|
@ -653,7 +729,7 @@ struct iwl_statistics_operational_ntfy {
|
|||
struct iwl_statistics_ntfy_hdr hdr;
|
||||
__le32 flags;
|
||||
struct iwl_stats_ntfy_per_mac per_mac[MAC_INDEX_AUX];
|
||||
struct iwl_stats_ntfy_per_phy per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
|
||||
struct iwl_stats_ntfy_per_phy_v1 per_phy[IWL_STATS_MAX_PHY_OPERATIONAL];
|
||||
struct iwl_stats_ntfy_per_sta per_sta[IWL_STATION_COUNT_MAX];
|
||||
__le64 rx_time;
|
||||
__le64 tx_time;
|
||||
|
|
|
|||
1022
drivers/net/wireless/intel/iwlwifi/fw/dbg-old.c
Normal file
1022
drivers/net/wireless/intel/iwlwifi/fw/dbg-old.c
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2019, 2021-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2019, 2021-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2015-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -38,6 +38,11 @@ struct iwl_fw_dbg_params {
|
|||
u32 out_ctrl;
|
||||
};
|
||||
|
||||
/* old-style dump entry point */
|
||||
void iwl_fw_error_dump(struct iwl_fw_runtime *fwrt,
|
||||
struct iwl_fwrt_dump_data *dump_data);
|
||||
struct scatterlist *iwl_fw_dbg_alloc_sgtable(ssize_t size);
|
||||
|
||||
extern const struct iwl_fw_dump_desc iwl_dump_desc_assert;
|
||||
|
||||
int iwl_fw_dbg_collect_desc(struct iwl_fw_runtime *fwrt,
|
||||
|
|
|
|||
|
|
@ -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, 2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -275,16 +275,19 @@ static ssize_t iwl_dbgfs_send_hcmd_write(struct iwl_fw_runtime *fwrt, char *buf,
|
|||
goto out;
|
||||
}
|
||||
|
||||
/* ignore this flag, we cannot use the response */
|
||||
hcmd.flags &= ~CMD_WANT_SKB;
|
||||
/* reject flags other than async, they cannot be used this way */
|
||||
if (hcmd.flags & ~CMD_ASYNC) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (fwrt->ops && fwrt->ops->send_hcmd)
|
||||
ret = fwrt->ops->send_hcmd(fwrt->ops_ctx, &hcmd);
|
||||
else
|
||||
ret = -EPERM;
|
||||
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
if (hcmd.flags & CMD_WANT_SKB)
|
||||
iwl_free_resp(&hcmd);
|
||||
out:
|
||||
kfree(data);
|
||||
return ret ?: count;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2014-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -265,18 +265,6 @@ struct iwl_fw_ini_error_dump_data {
|
|||
__u8 data[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_fw_ini_dump_entry - dump entry descriptor
|
||||
* @list: list of dump entries
|
||||
* @size: size of the data
|
||||
* @data: entry data
|
||||
*/
|
||||
struct iwl_fw_ini_dump_entry {
|
||||
struct list_head list;
|
||||
u32 size;
|
||||
u8 data[];
|
||||
} __packed;
|
||||
|
||||
/**
|
||||
* struct iwl_fw_ini_dump_file_hdr - header of dump file
|
||||
* @barker: must be %IWL_FW_INI_ERROR_DUMP_BARKER
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2008-2014, 2018-2024 Intel Corporation
|
||||
* Copyright (C) 2008-2014, 2018-2024, 2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -111,6 +111,7 @@ enum iwl_ucode_tlv_type {
|
|||
IWL_UCODE_TLV_FW_NUM_STATIONS = IWL_UCODE_TLV_CONST_BASE + 0,
|
||||
IWL_UCODE_TLV_FW_NUM_LINKS = IWL_UCODE_TLV_CONST_BASE + 1,
|
||||
IWL_UCODE_TLV_FW_NUM_BEACONS = IWL_UCODE_TLV_CONST_BASE + 2,
|
||||
IWL_UCODE_TLV_FW_NUM_MCAST_KEY_ENTRIES = IWL_UCODE_TLV_CONST_BASE + 3,
|
||||
|
||||
IWL_UCODE_TLV_TYPE_DEBUG_INFO = IWL_UCODE_TLV_DEBUG_BASE + 0,
|
||||
IWL_UCODE_TLV_TYPE_BUFFER_ALLOCATION = IWL_UCODE_TLV_DEBUG_BASE + 1,
|
||||
|
|
@ -1063,10 +1064,15 @@ struct iwl_fw_dump_exclude {
|
|||
__le32 addr, size;
|
||||
};
|
||||
|
||||
struct iwl_fw_fseq_bin_version {
|
||||
struct iwl_fw_fseq_bin_version_v1 {
|
||||
__le32 major, minor;
|
||||
}; /* FW_TLV_FSEQ_BIN_VERSION_S */
|
||||
|
||||
struct iwl_fw_fseq_bin_version {
|
||||
/* rf_id is currently unused and always zero */
|
||||
__le32 mac_id, rf_id, 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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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-2024, 2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -53,6 +53,7 @@ struct iwl_ucode_capabilities {
|
|||
u32 num_stations;
|
||||
u32 num_links;
|
||||
u32 num_beacons;
|
||||
u32 num_mcast_key_entries;
|
||||
DECLARE_BITMAP(_api, NUM_IWL_UCODE_TLV_API);
|
||||
DECLARE_BITMAP(_capa, NUM_IWL_UCODE_TLV_CAPA);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2023, 2025-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/dmi.h>
|
||||
#include "iwl-drv.h"
|
||||
|
|
@ -112,6 +112,11 @@ static const struct dmi_system_id dmi_ppag_approved_list[] = {
|
|||
DMI_MATCH(DMI_SYS_VENDOR, "WIKO"),
|
||||
},
|
||||
},
|
||||
{ .ident = "XIAOMI",
|
||||
.matches = {
|
||||
DMI_MATCH(DMI_SYS_VENDOR, "XIAOMI"),
|
||||
},
|
||||
},
|
||||
{}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2021-2022, 2025 Intel Corporation
|
||||
* Copyright (C) 2021-2022, 2025-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <net/mac80211.h>
|
||||
|
|
@ -124,6 +124,9 @@ int rs_pretty_print_rate(char *buf, int bufsz, const u32 rate)
|
|||
case RATE_MCS_MOD_TYPE_EHT:
|
||||
type = "EHT";
|
||||
break;
|
||||
case RATE_MCS_MOD_TYPE_UHR:
|
||||
type = "UHR";
|
||||
break;
|
||||
default:
|
||||
type = "Unknown"; /* shouldn't happen */
|
||||
}
|
||||
|
|
|
|||
|
|
@ -416,6 +416,7 @@ struct iwl_mac_cfg {
|
|||
* @nvm_type: see &enum iwl_nvm_type
|
||||
* @uhb_supported: ultra high band channels supported
|
||||
* @eht_supported: EHT supported
|
||||
* @uhr_supported: UHR supported
|
||||
* @num_rbds: number of receive buffer descriptors to use
|
||||
* (only used for multi-queue capable devices)
|
||||
*
|
||||
|
|
@ -449,7 +450,8 @@ struct iwl_rf_cfg {
|
|||
lp_xtal_workaround:1,
|
||||
vht_mu_mimo_supported:1,
|
||||
uhb_supported:1,
|
||||
eht_supported:1;
|
||||
eht_supported:1,
|
||||
uhr_supported:1;
|
||||
u8 valid_tx_ant;
|
||||
u8 valid_rx_ant;
|
||||
u8 non_shared_ant;
|
||||
|
|
@ -674,6 +676,7 @@ 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_killer_be1730x_name[];
|
||||
extern const char iwl_be201_name[];
|
||||
extern const char iwl_be200_name[];
|
||||
extern const char iwl_be202_name[];
|
||||
|
|
@ -681,6 +684,7 @@ 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_killer_be1735x_name[];
|
||||
extern const char iwl_be211_name[];
|
||||
extern const char iwl_killer_bn1850w2_name[];
|
||||
extern const char iwl_killer_bn1850i_name[];
|
||||
|
|
@ -744,7 +748,8 @@ 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
|
||||
extern const struct iwl_rf_cfg iwl_rf_wh_non_eht;
|
||||
#define iwl_rf_pe iwl_rf_fm
|
||||
extern const struct iwl_rf_cfg iwl_rf_pe;
|
||||
#define iwl_rf_pe_no_uhr iwl_rf_fm
|
||||
#endif /* CONFIG_IWLMLD */
|
||||
|
||||
#endif /* __IWL_CONFIG_H__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2014 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -366,6 +366,7 @@ enum {
|
|||
#define CSR_HW_RF_ID_TYPE_GF4 (0x0010E000)
|
||||
#define CSR_HW_RF_ID_TYPE_FM (0x00112000)
|
||||
#define CSR_HW_RF_ID_TYPE_WP (0x00113000)
|
||||
#define CSR_HW_RF_ID_TYPE_PE (0x00114000)
|
||||
|
||||
/* HW_RF CHIP STEP */
|
||||
#define CSR_HW_RF_STEP(_val) (((_val) >> 8) & 0xF)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -1294,8 +1294,9 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
|
|||
|
||||
if (tlv_len != sizeof(*fseq_ver))
|
||||
goto invalid_tlv_len;
|
||||
IWL_DEBUG_INFO(drv, "TLV_FW_FSEQ_VERSION: %.32s\n",
|
||||
fseq_ver->version);
|
||||
IWL_DEBUG_INFO(drv,
|
||||
"TLV_FW_FSEQ_VERSION: %.32s (sha1: %.20s)\n",
|
||||
fseq_ver->version, fseq_ver->sha1);
|
||||
}
|
||||
break;
|
||||
case IWL_UCODE_TLV_FW_NUM_STATIONS:
|
||||
|
|
@ -1330,6 +1331,12 @@ static int iwl_parse_tlv_firmware(struct iwl_drv *drv,
|
|||
capa->num_beacons =
|
||||
le32_to_cpup((const __le32 *)tlv_data);
|
||||
break;
|
||||
case IWL_UCODE_TLV_FW_NUM_MCAST_KEY_ENTRIES:
|
||||
if (tlv_len != sizeof(u32))
|
||||
goto invalid_tlv_len;
|
||||
capa->num_mcast_key_entries =
|
||||
le32_to_cpup((const __le32 *)tlv_data);
|
||||
break;
|
||||
case IWL_UCODE_TLV_UMAC_DEBUG_ADDRS: {
|
||||
const struct iwl_umac_debug_addrs *dbg_ptrs =
|
||||
(const void *)tlv_data;
|
||||
|
|
@ -1640,6 +1647,7 @@ static void iwl_req_fw_callback(const struct firmware *ucode_raw, void *context)
|
|||
fw->ucode_capa.n_scan_channels = IWL_DEFAULT_SCAN_CHANNELS;
|
||||
fw->ucode_capa.num_stations = IWL_STATION_COUNT_MAX;
|
||||
fw->ucode_capa.num_beacons = 1;
|
||||
fw->ucode_capa.num_mcast_key_entries = 2;
|
||||
/* dump all fw memory areas by default */
|
||||
fw->dbg.dump_mask = 0xffffffff;
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2003-2014, 2018-2022, 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2003-2014, 2018-2022, 2024-2026 Intel Corporation
|
||||
* Copyright (C) 2015-2016 Intel Deutschland GmbH
|
||||
*/
|
||||
#include <linux/device.h>
|
||||
|
|
@ -12,7 +12,6 @@
|
|||
#include "iwl-debug.h"
|
||||
#include "iwl-prph.h"
|
||||
#include "iwl-fh.h"
|
||||
#include "pcie/gen1_2/internal.h"
|
||||
|
||||
void iwl_write8(struct iwl_trans *trans, u32 ofs, u8 val)
|
||||
{
|
||||
|
|
@ -168,6 +167,22 @@ int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
|
|||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
int iwl_poll_umac_prph_bits_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
u32 bits, u32 mask, int timeout)
|
||||
{
|
||||
int t = 0;
|
||||
|
||||
do {
|
||||
if ((iwl_read_umac_prph_no_grab(trans, addr) & mask) ==
|
||||
(bits & mask))
|
||||
return 0;
|
||||
udelay(IWL_POLL_INTERVAL);
|
||||
t += IWL_POLL_INTERVAL;
|
||||
} while (t < timeout);
|
||||
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask)
|
||||
{
|
||||
if (iwl_trans_grab_nic_access(trans)) {
|
||||
|
|
@ -396,12 +411,6 @@ int iwl_dump_fh(struct iwl_trans *trans, char **buf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans)
|
||||
{
|
||||
return iwl_pcie_gen1_2_activate_nic(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_activate_nic);
|
||||
|
||||
void iwl_trans_sync_nmi_with_addr(struct iwl_trans *trans, u32 inta_addr,
|
||||
u32 sw_err_bit)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2018-2021, 2025 Intel Corporation
|
||||
* Copyright (C) 2018-2021, 2025-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_io_h__
|
||||
#define __iwl_io_h__
|
||||
|
|
@ -51,14 +51,14 @@ static inline void iwl_write_prph(struct iwl_trans *trans, u32 ofs, u32 val)
|
|||
|
||||
int iwl_poll_prph_bit(struct iwl_trans *trans, u32 addr,
|
||||
u32 bits, u32 mask, int timeout);
|
||||
int iwl_poll_umac_prph_bits_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
u32 bits, u32 mask, int timeout);
|
||||
void iwl_set_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
|
||||
void iwl_set_bits_mask_prph(struct iwl_trans *trans, u32 ofs,
|
||||
u32 bits, u32 mask);
|
||||
void iwl_clear_bits_prph(struct iwl_trans *trans, u32 ofs, u32 mask);
|
||||
void iwl_force_nmi(struct iwl_trans *trans);
|
||||
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans);
|
||||
|
||||
/* Error handling */
|
||||
int iwl_dump_fh(struct iwl_trans *trans, char **buf);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2023, 2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2023, 2025-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -18,7 +18,6 @@
|
|||
#include "iwl-prph.h"
|
||||
#include "iwl-io.h"
|
||||
#include "iwl-csr.h"
|
||||
#include "fw/acpi.h"
|
||||
#include "fw/api/nvm-reg.h"
|
||||
#include "fw/api/commands.h"
|
||||
#include "fw/api/cmdhdr.h"
|
||||
|
|
@ -206,28 +205,30 @@ enum iwl_reg_capa_flags_v2 {
|
|||
}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_2 */
|
||||
|
||||
/**
|
||||
* enum iwl_reg_capa_flags_v4 - global flags applied for the whole regulatory
|
||||
* enum iwl_reg_capa_flags_v5 - global flags applied for the whole regulatory
|
||||
* domain.
|
||||
* @REG_CAPA_V4_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
|
||||
* @REG_CAPA_V5_160MHZ_ALLOWED: 11ac channel with a width of 160Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5Ghz).
|
||||
* @REG_CAPA_V4_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
|
||||
* @REG_CAPA_V5_80MHZ_ALLOWED: 11ac channel with a width of 80Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5Ghz).
|
||||
* @REG_CAPA_V4_MCS_12_ALLOWED: 11ac with MCS 12 is allowed.
|
||||
* @REG_CAPA_V4_MCS_13_ALLOWED: 11ac with MCS 13 is allowed.
|
||||
* @REG_CAPA_V4_11BE_DISABLED: 11be is forbidden for this regulatory domain.
|
||||
* @REG_CAPA_V4_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
|
||||
* @REG_CAPA_V4_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed
|
||||
* @REG_CAPA_V5_MCS_12_ALLOWED: 11ac with MCS 12 is allowed.
|
||||
* @REG_CAPA_V5_MCS_13_ALLOWED: 11ac with MCS 13 is allowed.
|
||||
* @REG_CAPA_V5_11BE_DISABLED: 11be is forbidden for this regulatory domain.
|
||||
* @REG_CAPA_V5_11AX_DISABLED: 11ax is forbidden for this regulatory domain.
|
||||
* @REG_CAPA_V5_320MHZ_ALLOWED: 11be channel with a width of 320Mhz is allowed
|
||||
* for this regulatory domain (valid only in 5GHz).
|
||||
* @REG_CAPA_V5_11BN_DISABLED: UHR is not allowed for this regulatory domain
|
||||
*/
|
||||
enum iwl_reg_capa_flags_v4 {
|
||||
REG_CAPA_V4_160MHZ_ALLOWED = BIT(3),
|
||||
REG_CAPA_V4_80MHZ_ALLOWED = BIT(4),
|
||||
REG_CAPA_V4_MCS_12_ALLOWED = BIT(5),
|
||||
REG_CAPA_V4_MCS_13_ALLOWED = BIT(6),
|
||||
REG_CAPA_V4_11BE_DISABLED = BIT(8),
|
||||
REG_CAPA_V4_11AX_DISABLED = BIT(13),
|
||||
REG_CAPA_V4_320MHZ_ALLOWED = BIT(16),
|
||||
}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4 */
|
||||
enum iwl_reg_capa_flags_v5 {
|
||||
REG_CAPA_V5_160MHZ_ALLOWED = BIT(3),
|
||||
REG_CAPA_V5_80MHZ_ALLOWED = BIT(4),
|
||||
REG_CAPA_V5_MCS_12_ALLOWED = BIT(5),
|
||||
REG_CAPA_V5_MCS_13_ALLOWED = BIT(6),
|
||||
REG_CAPA_V5_11BE_DISABLED = BIT(8),
|
||||
REG_CAPA_V5_11AX_DISABLED = BIT(13),
|
||||
REG_CAPA_V5_320MHZ_ALLOWED = BIT(16),
|
||||
REG_CAPA_V5_11BN_DISABLED = BIT(17),
|
||||
}; /* GEO_CHANNEL_CAPABILITIES_API_S_VER_4, 5 */
|
||||
|
||||
/*
|
||||
* API v2 for reg_capa_flags is relevant from version 6 and onwards of the
|
||||
|
|
@ -546,7 +547,7 @@ static const u8 iwl_vendor_caps[] = {
|
|||
0x00
|
||||
};
|
||||
|
||||
static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
|
||||
static const struct ieee80211_sband_iftype_data iwl_iftype_cap[] = {
|
||||
{
|
||||
.types_mask = BIT(NL80211_IFTYPE_STATION) |
|
||||
BIT(NL80211_IFTYPE_P2P_CLIENT),
|
||||
|
|
@ -690,6 +691,16 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
|
|||
*/
|
||||
.eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
|
||||
},
|
||||
.uhr_cap = {
|
||||
.has_uhr = true,
|
||||
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
|
||||
IEEE80211_UHR_PHY_CAP_ELR_TX,
|
||||
.mac.mac_cap = {
|
||||
[0] = IEEE80211_UHR_MAC_CAP0_NPCA_SUPP |
|
||||
IEEE80211_UHR_MAC_CAP0_DPS_SUPP,
|
||||
[1] = IEEE80211_UHR_MAC_CAP1_DUO_SUPP,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
.types_mask = BIT(NL80211_IFTYPE_AP) |
|
||||
|
|
@ -788,6 +799,11 @@ static const struct ieee80211_sband_iftype_data iwl_he_eht_capa[] = {
|
|||
*/
|
||||
.eht_ppe_thres = {0xc1, 0x0e, 0xe0 }
|
||||
},
|
||||
.uhr_cap = {
|
||||
.has_uhr = true,
|
||||
.phy.cap = IEEE80211_UHR_PHY_CAP_ELR_RX |
|
||||
IEEE80211_UHR_PHY_CAP_ELR_TX,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -855,6 +871,9 @@ iwl_nvm_fixup_sband_iftd(struct iwl_trans *trans,
|
|||
fips_enabled)
|
||||
iftype_data->eht_cap.has_eht = false;
|
||||
|
||||
if (!data->sku_cap_11bn_enable || !iftype_data->eht_cap.has_eht)
|
||||
iftype_data->uhr_cap.has_uhr = false;
|
||||
|
||||
/* Advertise an A-MPDU exponent extension based on
|
||||
* operating band
|
||||
*/
|
||||
|
|
@ -1023,9 +1042,9 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
|
|||
struct ieee80211_sband_iftype_data *iftype_data;
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_he_eht_capa));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_he_eht_capa));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_he_eht_capa));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.low) != sizeof(iwl_iftype_cap));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.high) != sizeof(iwl_iftype_cap));
|
||||
BUILD_BUG_ON(sizeof(data->iftd.uhb) != sizeof(iwl_iftype_cap));
|
||||
|
||||
switch (sband->band) {
|
||||
case NL80211_BAND_2GHZ:
|
||||
|
|
@ -1042,10 +1061,10 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
|
|||
return;
|
||||
}
|
||||
|
||||
memcpy(iftype_data, iwl_he_eht_capa, sizeof(iwl_he_eht_capa));
|
||||
memcpy(iftype_data, iwl_iftype_cap, sizeof(iwl_iftype_cap));
|
||||
|
||||
_ieee80211_set_sband_iftype_data(sband, iftype_data,
|
||||
ARRAY_SIZE(iwl_he_eht_capa));
|
||||
ARRAY_SIZE(iwl_iftype_cap));
|
||||
|
||||
for (i = 0; i < sband->n_iftype_data; i++)
|
||||
iwl_nvm_fixup_sband_iftd(trans, data, sband, &iftype_data[i],
|
||||
|
|
@ -1054,6 +1073,46 @@ static void iwl_init_he_hw_capab(struct iwl_trans *trans,
|
|||
iwl_init_he_6ghz_capa(trans, data, sband, tx_chains, rx_chains);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_init_nan_phy_capa(const struct iwl_fw *fw, struct iwl_nvm_data *data)
|
||||
{
|
||||
const struct ieee80211_sta_he_cap *he_cap;
|
||||
|
||||
if (!fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT))
|
||||
return;
|
||||
|
||||
data->nan_phy_capa.ht = data->bands[NL80211_BAND_2GHZ].ht_cap;
|
||||
data->nan_phy_capa.vht = data->bands[NL80211_BAND_5GHZ].vht_cap;
|
||||
|
||||
he_cap = ieee80211_get_he_iftype_cap(&data->bands[NL80211_BAND_2GHZ],
|
||||
NL80211_IFTYPE_STATION);
|
||||
if (he_cap) {
|
||||
data->nan_phy_capa.he = *he_cap;
|
||||
data->nan_phy_capa.he.he_cap_elem.phy_cap_info[0] |=
|
||||
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G |
|
||||
IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G;
|
||||
}
|
||||
|
||||
/*
|
||||
* FIXME: we copied HE capabilities from the 2.4 GHz band,
|
||||
* but there are bits that are band-dependent:
|
||||
*
|
||||
* IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_1 - 2.4 GHz - set
|
||||
* IEEE80211_HE_MAC_CAP3_MAX_AMPDU_LEN_EXP_EXT_3 - 5 GHz - not set
|
||||
* IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_IN_2G - set
|
||||
* IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_40MHZ_80MHZ_IN_5G - set
|
||||
* IEEE80211_HE_PHY_CAP0_CHANNEL_WIDTH_SET_160MHZ_IN_5G - set
|
||||
*
|
||||
* We copied from STA iftype - so we have the following bits set:
|
||||
* IEEE80211_HE_PHY_CAP1_MIDAMBLE_RX_TX_MAX_NSTS
|
||||
* IEEE80211_HE_PHY_CAP2_MIDAMBLE_RX_TX_MAX_NSTS
|
||||
* IEEE80211_HE_PHY_CAP7_MAX_NC_1
|
||||
* IEEE80211_HE_PHY_CAP2_UL_MU_FULL_MU_MIMO
|
||||
*
|
||||
* Need to check which one should actually be set for NAN.
|
||||
*/
|
||||
}
|
||||
|
||||
void iwl_reinit_cab(struct iwl_trans *trans, struct iwl_nvm_data *data,
|
||||
u8 tx_chains, u8 rx_chains, const struct iwl_fw *fw)
|
||||
{
|
||||
|
|
@ -1082,6 +1141,8 @@ void iwl_reinit_cab(struct iwl_trans *trans, struct iwl_nvm_data *data,
|
|||
if (data->sku_cap_11ax_enable && !iwlwifi_mod_params.disable_11ax)
|
||||
iwl_init_he_hw_capab(trans, data, sband, tx_chains, rx_chains,
|
||||
fw);
|
||||
|
||||
iwl_init_nan_phy_capa(fw, data);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_reinit_cab);
|
||||
|
||||
|
|
@ -1611,6 +1672,9 @@ u32 iwl_nvm_get_regdom_bw_flags(const u16 *nvm_chan,
|
|||
if (reg_capa.disable_11be)
|
||||
flags |= NL80211_RRF_NO_EHT;
|
||||
|
||||
if (reg_capa.disable_11bn)
|
||||
flags |= NL80211_RRF_NO_UHR;
|
||||
|
||||
return flags;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_nvm_get_regdom_bw_flags);
|
||||
|
|
@ -1621,11 +1685,13 @@ static struct iwl_reg_capa iwl_get_reg_capa(u32 flags, u8 resp_ver)
|
|||
|
||||
if (resp_ver >= REG_CAPA_V4_RESP_VER) {
|
||||
reg_capa.allow_40mhz = true;
|
||||
reg_capa.allow_80mhz = flags & REG_CAPA_V4_80MHZ_ALLOWED;
|
||||
reg_capa.allow_160mhz = flags & REG_CAPA_V4_160MHZ_ALLOWED;
|
||||
reg_capa.allow_320mhz = flags & REG_CAPA_V4_320MHZ_ALLOWED;
|
||||
reg_capa.disable_11ax = flags & REG_CAPA_V4_11AX_DISABLED;
|
||||
reg_capa.disable_11be = flags & REG_CAPA_V4_11BE_DISABLED;
|
||||
reg_capa.allow_80mhz = flags & REG_CAPA_V5_80MHZ_ALLOWED;
|
||||
reg_capa.allow_160mhz = flags & REG_CAPA_V5_160MHZ_ALLOWED;
|
||||
reg_capa.allow_320mhz = flags & REG_CAPA_V5_320MHZ_ALLOWED;
|
||||
reg_capa.disable_11ax = flags & REG_CAPA_V5_11AX_DISABLED;
|
||||
reg_capa.disable_11be = flags & REG_CAPA_V5_11BE_DISABLED;
|
||||
/* can check: was reserved and irrelevant for pre-UHR devices */
|
||||
reg_capa.disable_11bn = flags & REG_CAPA_V5_11BN_DISABLED;
|
||||
} else if (resp_ver >= REG_CAPA_V2_RESP_VER) {
|
||||
reg_capa.allow_40mhz = flags & REG_CAPA_V2_40MHZ_ALLOWED;
|
||||
reg_capa.allow_80mhz = flags & REG_CAPA_V2_80MHZ_ALLOWED;
|
||||
|
|
@ -1683,8 +1749,16 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
|
|||
IWL_DEBUG_DEV(dev, IWL_DL_LAR, "building regdom for %d channels\n",
|
||||
num_of_ch);
|
||||
|
||||
/* build a regdomain rule for every valid channel */
|
||||
regd = kzalloc_flex(*regd, reg_rules, num_of_ch);
|
||||
/* build a regdomain rule for every valid channel.
|
||||
* Certain firmware versions might report no valid channels
|
||||
* if booted in RF-kill, i.e. not all calibrations etc. are
|
||||
* running. We'll get out of this situation later when the
|
||||
* rfkill is removed and we update the regdomain again, but
|
||||
* since cfg80211 doesn't accept an empty regdomain, we need
|
||||
* to allocate space for at least one rule to add a dummy
|
||||
* (unusable) rule in this case so we can init.
|
||||
*/
|
||||
regd = kzalloc_flex(*regd, reg_rules, num_of_ch ?: 1);
|
||||
if (!regd)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
|
|
@ -1758,14 +1832,7 @@ iwl_parse_nvm_mcc_info(struct iwl_trans *trans,
|
|||
reg_query_regdb_wmm(regd->alpha2, center_freq, rule);
|
||||
}
|
||||
|
||||
/*
|
||||
* Certain firmware versions might report no valid channels
|
||||
* if booted in RF-kill, i.e. not all calibrations etc. are
|
||||
* running. We'll get out of this situation later when the
|
||||
* rfkill is removed and we update the regdomain again, but
|
||||
* since cfg80211 doesn't accept an empty regdomain, add a
|
||||
* dummy (unusable) rule here in this case so we can init.
|
||||
*/
|
||||
/* If no valid rules were found, add a dummy rule */
|
||||
if (!valid_rules) {
|
||||
valid_rules = 1;
|
||||
rule = ®d->reg_rules[valid_rules - 1];
|
||||
|
|
@ -2079,6 +2146,7 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
|
|||
!!(mac_flags & NVM_MAC_SKU_FLAGS_MIMO_DISABLED);
|
||||
if (trans->cfg->eht_supported)
|
||||
nvm->sku_cap_11be_enable = true;
|
||||
nvm->sku_cap_11bn_enable = trans->cfg->uhr_supported;
|
||||
|
||||
/* Initialize PHY sku data */
|
||||
nvm->valid_tx_ant = (u8)le32_to_cpu(rsp->phy_sku.tx_chains);
|
||||
|
|
@ -2106,6 +2174,8 @@ struct iwl_nvm_data *iwl_get_nvm(struct iwl_trans *trans,
|
|||
iwl_init_sbands(trans, nvm, channel_profile, tx_ant, rx_ant,
|
||||
sbands_flags, v4, fw);
|
||||
|
||||
iwl_init_nan_phy_capa(fw, nvm);
|
||||
|
||||
iwl_free_resp(&hcmd);
|
||||
return nvm;
|
||||
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ enum iwl_nvm_sbands_flags {
|
|||
* for this regulatory domain (valid only in 6 Ghz).
|
||||
* @disable_11ax: 11ax is forbidden for this regulatory domain.
|
||||
* @disable_11be: 11be is forbidden for this regulatory domain.
|
||||
* @disable_11bn: UHR/11bn is not allowed for this regulatory domain
|
||||
*/
|
||||
struct iwl_reg_capa {
|
||||
bool allow_40mhz;
|
||||
|
|
@ -43,6 +44,7 @@ struct iwl_reg_capa {
|
|||
bool allow_320mhz;
|
||||
bool disable_11ax;
|
||||
bool disable_11be;
|
||||
bool disable_11bn;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018, 2020-2023 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018, 2020-2023, 2026 Intel Corporation
|
||||
* Copyright (C) 2015 Intel Mobile Communications GmbH
|
||||
*/
|
||||
#ifndef __iwl_eeprom_parse_h__
|
||||
|
|
@ -32,6 +32,7 @@ struct iwl_nvm_data {
|
|||
bool sku_cap_ipan_enable;
|
||||
bool sku_cap_mimo_disabled;
|
||||
bool sku_cap_11be_enable;
|
||||
bool sku_cap_11bn_enable;
|
||||
|
||||
u16 radio_cfg_type;
|
||||
u8 radio_cfg_step;
|
||||
|
|
@ -55,6 +56,12 @@ struct iwl_nvm_data {
|
|||
struct ieee80211_sband_iftype_data uhb[2];
|
||||
} iftd;
|
||||
|
||||
struct {
|
||||
struct ieee80211_sta_ht_cap ht;
|
||||
struct ieee80211_sta_vht_cap vht;
|
||||
struct ieee80211_sta_he_cap he;
|
||||
} nan_phy_capa;
|
||||
|
||||
struct ieee80211_channel channels[];
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -411,6 +411,11 @@ enum {
|
|||
#define HPM_SECONDARY_DEVICE_STATE 0xa03404
|
||||
#define WFPM_MAC_OTP_CFG7_ADDR 0xa03338
|
||||
#define WFPM_MAC_OTP_CFG7_DATA 0xa0333c
|
||||
#define WFPM_RSRCS_4PHS_REQ_STTS 0xa033f8
|
||||
#define WFPM_RSRCS_4PHS_ACK_STTS 0xa033fc
|
||||
|
||||
#define RSRC_REQ_CNVR_TOP BIT(6)
|
||||
#define RSRC_ACK_CNVR_TOP BIT(6)
|
||||
|
||||
|
||||
/* For UMAG_GEN_HW_STATUS reg check */
|
||||
|
|
|
|||
|
|
@ -2,16 +2,14 @@
|
|||
/*
|
||||
* Copyright (C) 2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
* Copyright (C) 2019-2021, 2023-2025 Intel Corporation
|
||||
* Copyright (C) 2019-2021, 2023-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/bsearch.h>
|
||||
#include <linux/list.h>
|
||||
|
||||
#include "fw/api/tx.h"
|
||||
#include "iwl-trans.h"
|
||||
#include "iwl-drv.h"
|
||||
#include "iwl-fh.h"
|
||||
#include <linux/dmapool.h>
|
||||
#include "fw/api/commands.h"
|
||||
#include "pcie/gen1_2/internal.h"
|
||||
|
|
@ -461,6 +459,12 @@ int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr,
|
|||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_read_mem);
|
||||
|
||||
int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, u32 dwords)
|
||||
{
|
||||
return iwl_trans_pcie_read_mem_no_grab(trans, addr, buf, dwords);
|
||||
}
|
||||
|
||||
int iwl_trans_write_mem(struct iwl_trans *trans, u32 addr,
|
||||
const void *buf, int dwords)
|
||||
{
|
||||
|
|
@ -822,3 +826,10 @@ bool iwl_trans_is_ltr_enabled(struct iwl_trans *trans)
|
|||
return iwl_pcie_gen1_2_is_ltr_enabled(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_is_ltr_enabled);
|
||||
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans)
|
||||
{
|
||||
return iwl_pcie_gen1_2_activate_nic(trans);
|
||||
}
|
||||
IWL_EXPORT_SYMBOL(iwl_trans_activate_nic);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2005-2014, 2018-2025 Intel Corporation
|
||||
* Copyright (C) 2005-2014, 2018-2026 Intel Corporation
|
||||
* Copyright (C) 2013-2015 Intel Mobile Communications GmbH
|
||||
* Copyright (C) 2016-2017 Intel Deutschland GmbH
|
||||
*/
|
||||
|
|
@ -700,106 +700,6 @@ struct iwl_cmd_meta {
|
|||
u32 tbs;
|
||||
};
|
||||
|
||||
/*
|
||||
* The FH will write back to the first TB only, so we need to copy some data
|
||||
* into the buffer regardless of whether it should be mapped or not.
|
||||
* This indicates how big the first TB must be to include the scratch buffer
|
||||
* and the assigned PN.
|
||||
* Since PN location is 8 bytes at offset 12, it's 20 now.
|
||||
* If we make it bigger then allocations will be bigger and copy slower, so
|
||||
* that's probably not useful.
|
||||
*/
|
||||
#define IWL_FIRST_TB_SIZE 20
|
||||
#define IWL_FIRST_TB_SIZE_ALIGN ALIGN(IWL_FIRST_TB_SIZE, 64)
|
||||
|
||||
struct iwl_pcie_txq_entry {
|
||||
void *cmd;
|
||||
struct sk_buff *skb;
|
||||
/* buffer to free after command completes */
|
||||
const void *free_buf;
|
||||
struct iwl_cmd_meta meta;
|
||||
};
|
||||
|
||||
struct iwl_pcie_first_tb_buf {
|
||||
u8 buf[IWL_FIRST_TB_SIZE_ALIGN];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_txq - Tx Queue for DMA
|
||||
* @tfds: transmit frame descriptors (DMA memory)
|
||||
* @first_tb_bufs: start of command headers, including scratch buffers, for
|
||||
* the writeback -- this is DMA memory and an array holding one buffer
|
||||
* for each command on the queue
|
||||
* @first_tb_dma: DMA address for the first_tb_bufs start
|
||||
* @entries: transmit entries (driver state)
|
||||
* @lock: queue lock
|
||||
* @reclaim_lock: reclaim lock
|
||||
* @stuck_timer: timer that fires if queue gets stuck
|
||||
* @trans: pointer back to transport (for timer)
|
||||
* @need_update: indicates need to update read/write index
|
||||
* @ampdu: true if this queue is an ampdu queue for an specific RA/TID
|
||||
* @wd_timeout: queue watchdog timeout (jiffies) - per queue
|
||||
* @frozen: tx stuck queue timer is frozen
|
||||
* @frozen_expiry_remainder: remember how long until the timer fires
|
||||
* @block: queue is blocked
|
||||
* @bc_tbl: byte count table of the queue (relevant only for gen2 transport)
|
||||
* @write_ptr: 1-st empty entry (index) host_w
|
||||
* @read_ptr: last used entry (index) host_r
|
||||
* @dma_addr: physical addr for BD's
|
||||
* @n_window: safe queue window
|
||||
* @id: queue id
|
||||
* @low_mark: low watermark, resume queue if free space more than this
|
||||
* @high_mark: high watermark, stop queue if free space less than this
|
||||
* @overflow_q: overflow queue for handling frames that didn't fit on HW queue
|
||||
* @overflow_tx: need to transmit from overflow
|
||||
*
|
||||
* A Tx queue consists of circular buffer of BDs (a.k.a. TFDs, transmit frame
|
||||
* descriptors) and required locking structures.
|
||||
*
|
||||
* Note the difference between TFD_QUEUE_SIZE_MAX and n_window: the hardware
|
||||
* always assumes 256 descriptors, so TFD_QUEUE_SIZE_MAX is always 256 (unless
|
||||
* there might be HW changes in the future). For the normal TX
|
||||
* queues, n_window, which is the size of the software queue data
|
||||
* is also 256; however, for the command queue, n_window is only
|
||||
* 32 since we don't need so many commands pending. Since the HW
|
||||
* still uses 256 BDs for DMA though, TFD_QUEUE_SIZE_MAX stays 256.
|
||||
* This means that we end up with the following:
|
||||
* HW entries: | 0 | ... | N * 32 | ... | N * 32 + 31 | ... | 255 |
|
||||
* SW entries: | 0 | ... | 31 |
|
||||
* where N is a number between 0 and 7. This means that the SW
|
||||
* data is a window overlayed over the HW queue.
|
||||
*/
|
||||
struct iwl_txq {
|
||||
void *tfds;
|
||||
struct iwl_pcie_first_tb_buf *first_tb_bufs;
|
||||
dma_addr_t first_tb_dma;
|
||||
struct iwl_pcie_txq_entry *entries;
|
||||
/* lock for syncing changes on the queue */
|
||||
spinlock_t lock;
|
||||
/* lock to prevent concurrent reclaim */
|
||||
spinlock_t reclaim_lock;
|
||||
unsigned long frozen_expiry_remainder;
|
||||
struct timer_list stuck_timer;
|
||||
struct iwl_trans *trans;
|
||||
bool need_update;
|
||||
bool frozen;
|
||||
bool ampdu;
|
||||
int block;
|
||||
unsigned long wd_timeout;
|
||||
struct sk_buff_head overflow_q;
|
||||
struct iwl_dma_ptr bc_tbl;
|
||||
|
||||
int write_ptr;
|
||||
int read_ptr;
|
||||
dma_addr_t dma_addr;
|
||||
int n_window;
|
||||
u32 id;
|
||||
int low_mark;
|
||||
int high_mark;
|
||||
|
||||
bool overflow_tx;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_trans_info - transport info for outside use
|
||||
* @name: the device name
|
||||
|
|
@ -1019,6 +919,14 @@ void iwl_trans_write_prph(struct iwl_trans *trans, u32 ofs, u32 val);
|
|||
int iwl_trans_read_mem(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, int dwords);
|
||||
|
||||
/*
|
||||
* Note the special calling convention - it's allowed to drop the
|
||||
* internal transport lock and re-enable BHs temporarily, but will
|
||||
* not release NIC access.
|
||||
*/
|
||||
int iwl_trans_read_mem_no_grab(struct iwl_trans *trans, u32 addr,
|
||||
void *buf, u32 dwords);
|
||||
|
||||
int iwl_trans_read_config32(struct iwl_trans *trans, u32 ofs,
|
||||
u32 *val);
|
||||
|
||||
|
|
@ -1034,6 +942,14 @@ void iwl_trans_debugfs_cleanup(struct iwl_trans *trans);
|
|||
(bufsize) / sizeof(u32)); \
|
||||
})
|
||||
|
||||
static inline int
|
||||
iwl_trans_read_mem_bytes_no_grab(struct iwl_trans *trans,
|
||||
u32 addr, void *buf, u32 bufsize)
|
||||
{
|
||||
return iwl_trans_read_mem_no_grab(trans, addr, buf,
|
||||
bufsize / sizeof(u32));
|
||||
}
|
||||
|
||||
int iwl_trans_write_imr_mem(struct iwl_trans *trans, u32 dst_addr,
|
||||
u64 src_addr, u32 byte_cnt);
|
||||
|
||||
|
|
@ -1153,6 +1069,8 @@ static inline bool iwl_trans_dbg_ini_valid(struct iwl_trans *trans)
|
|||
|
||||
void iwl_trans_interrupts(struct iwl_trans *trans, bool enable);
|
||||
|
||||
int iwl_trans_activate_nic(struct iwl_trans *trans);
|
||||
|
||||
static inline void iwl_trans_finish_sw_reset(struct iwl_trans *trans)
|
||||
{
|
||||
clear_bit(STATUS_IN_SW_RESET, &trans->status);
|
||||
|
|
@ -1236,9 +1154,6 @@ enum iwl_reset_mode {
|
|||
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)
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ static void iwl_mld_release_frames_from_notif(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
/* pick any STA ID to find the pointer */
|
||||
if (WARN_ON_ONCE(!ba_data->sta_mask))
|
||||
goto out_unlock;
|
||||
|
||||
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))
|
||||
|
|
@ -166,6 +169,9 @@ void iwl_mld_del_ba(struct iwl_mld *mld, int queue,
|
|||
goto out_unlock;
|
||||
|
||||
/* pick any STA ID to find the pointer */
|
||||
if (WARN_ON_ONCE(!ba_data->sta_mask))
|
||||
goto out_unlock;
|
||||
|
||||
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))
|
||||
|
|
@ -347,6 +353,9 @@ static void iwl_mld_rx_agg_session_expired(struct timer_list *t)
|
|||
}
|
||||
|
||||
/* timer expired, pick any STA ID to find the pointer */
|
||||
if (WARN_ON_ONCE(!ba_data->sta_mask))
|
||||
goto unlock;
|
||||
|
||||
sta_id = ffs(ba_data->sta_mask) - 1;
|
||||
link_sta = rcu_dereference(ba_data->mld->fw_id_to_link_sta[sta_id]);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
* Copyright (C) 2024, 2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/crc32.h>
|
||||
|
||||
|
|
@ -239,6 +239,58 @@ int iwl_mld_store_ap_early_key(struct iwl_mld *mld,
|
|||
return -ENOSPC;
|
||||
}
|
||||
|
||||
static void iwl_mld_stop_beacon(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);
|
||||
struct iwl_mac_beacon_cmd cmd = {};
|
||||
int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, BEACON_TEMPLATE_CMD, 14);
|
||||
|
||||
if (WARN_ON(!mld_link))
|
||||
return;
|
||||
|
||||
if (cmd_ver < 15)
|
||||
return;
|
||||
|
||||
/* leave byte_cnt 0 */
|
||||
cmd.link_id = cpu_to_le32(mld_link->fw_id);
|
||||
|
||||
iwl_mld_send_cmd_pdu(mld, BEACON_TEMPLATE_CMD, &cmd);
|
||||
}
|
||||
|
||||
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) {
|
||||
WARN_ON(!link->enable_beacon);
|
||||
iwl_mld_update_beacon_template(mld, vif, link);
|
||||
}
|
||||
|
||||
/* Enabling beacons was already covered above */
|
||||
if ((changes & BSS_CHANGED_BEACON_ENABLED) && !link->enable_beacon)
|
||||
iwl_mld_stop_beacon(mld, vif, link);
|
||||
}
|
||||
|
||||
static int iwl_mld_send_ap_early_keys(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link)
|
||||
|
|
@ -276,10 +328,6 @@ int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw,
|
|||
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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
* Copyright (C) 2024, 2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_ap_h__
|
||||
#define __iwl_ap_h__
|
||||
|
|
@ -14,6 +14,12 @@ int iwl_mld_update_beacon_template(struct iwl_mld *mld,
|
|||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf);
|
||||
|
||||
void
|
||||
iwl_mld_link_info_changed_ap_ibss(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link,
|
||||
u64 changes);
|
||||
|
||||
int iwl_mld_start_ap_ibss(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include "mld.h"
|
||||
|
||||
|
|
@ -43,6 +43,12 @@ struct iwl_mld_resume_key_iter_data {
|
|||
struct iwl_mld_wowlan_status *wowlan_status;
|
||||
};
|
||||
|
||||
struct iwl_mld_rsc_resume_iter_data {
|
||||
struct iwl_mld *mld;
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *notif;
|
||||
int queue;
|
||||
};
|
||||
|
||||
struct iwl_mld_suspend_key_iter_data {
|
||||
struct iwl_wowlan_rsc_tsc_params_cmd *rsc;
|
||||
bool have_rsc;
|
||||
|
|
@ -282,7 +288,8 @@ 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 *gtk_data,
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *sc)
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *sc,
|
||||
int rsc_notif_ver)
|
||||
{
|
||||
int status_idx = 0;
|
||||
|
||||
|
|
@ -305,14 +312,18 @@ iwl_mld_convert_gtk_resume_data(struct iwl_mld *mld,
|
|||
wowlan_status->gtk[status_idx].id =
|
||||
wowlan_status->gtk[status_idx].flags &
|
||||
IWL_WOWLAN_GTK_IDX_MASK;
|
||||
/* 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 RSC_NOTIF is not supported */
|
||||
if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN) {
|
||||
/* 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 (key_status == IWL_WOWLAN_STATUS_NEW_KEY) {
|
||||
memcpy(wowlan_status->gtk[status_idx].key,
|
||||
|
|
@ -598,6 +609,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
|
|||
PROT_OFFLOAD_GROUP,
|
||||
WOWLAN_INFO_NOTIFICATION,
|
||||
IWL_FW_CMD_VER_UNKNOWN);
|
||||
int rsc_notif_ver = iwl_fw_lookup_notif_ver(mld->fw,
|
||||
DATA_PATH_GROUP,
|
||||
RSC_NOTIF,
|
||||
IWL_FW_CMD_VER_UNKNOWN);
|
||||
|
||||
if (wowlan_info_ver == 5) {
|
||||
/* v5 format - validate before conversion */
|
||||
|
|
@ -642,8 +657,10 @@ iwl_mld_handle_wowlan_info_notif(struct iwl_mld *mld,
|
|||
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);
|
||||
¬if->gtk[0].sc, rsc_notif_ver);
|
||||
if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN)
|
||||
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);
|
||||
|
|
@ -902,8 +919,14 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw,
|
|||
struct iwl_mld_resume_key_iter_data *data = _data;
|
||||
struct iwl_mld_wowlan_status *wowlan_status = data->wowlan_status;
|
||||
u8 status_idx;
|
||||
int rsc_notif_ver = iwl_fw_lookup_notif_ver(data->mld->fw,
|
||||
DATA_PATH_GROUP,
|
||||
RSC_NOTIF,
|
||||
IWL_FW_CMD_VER_UNKNOWN);
|
||||
|
||||
if (key->keyidx >= 0 && key->keyidx <= 3) {
|
||||
/* If RSC_NOTIF is not supported */
|
||||
if (rsc_notif_ver == IWL_FW_CMD_VER_UNKNOWN &&
|
||||
key->keyidx >= 0 && key->keyidx <= 3) {
|
||||
/* PTK */
|
||||
if (sta) {
|
||||
iwl_mld_update_ptk_rx_seq(data->mld, wowlan_status,
|
||||
|
|
@ -932,6 +955,105 @@ iwl_mld_resume_keys_iter(struct ieee80211_hw *hw,
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_rsc_update_key_iter(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key,
|
||||
void *_data)
|
||||
{
|
||||
struct iwl_mld_rsc_resume_iter_data *data = _data;
|
||||
struct ieee80211_key_seq seq;
|
||||
|
||||
if (key->keyidx > 3)
|
||||
return;
|
||||
|
||||
if (sta) {
|
||||
/* PTK */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(data->notif->ucast_rsc) !=
|
||||
IWL_MAX_TID_COUNT);
|
||||
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP) {
|
||||
/* TKIP: just update key sequences */
|
||||
for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
|
||||
iwl_mld_le64_to_tkip_seq(data->notif->ucast_rsc[tid],
|
||||
&seq);
|
||||
ieee80211_set_key_rx_seq(key, tid, &seq);
|
||||
}
|
||||
} else {
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
struct iwl_mld_ptk_pn *mld_ptk_pn =
|
||||
rcu_dereference_wiphy(data->mld->wiphy,
|
||||
mld_sta->ptk_pn[key->keyidx]);
|
||||
|
||||
if (WARN_ON(!mld_ptk_pn))
|
||||
return;
|
||||
|
||||
if (WARN_ON(data->queue >=
|
||||
data->mld->trans->info.num_rxqs))
|
||||
return;
|
||||
|
||||
for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
|
||||
iwl_mld_le64_to_aes_seq(data->notif->ucast_rsc[tid],
|
||||
&seq);
|
||||
ieee80211_set_key_rx_seq(key, tid, &seq);
|
||||
memcpy(mld_ptk_pn->q[data->queue].pn[tid],
|
||||
seq.ccmp.pn,
|
||||
IEEE80211_CCMP_PN_LEN);
|
||||
}
|
||||
}
|
||||
|
||||
IWL_DEBUG_WOWLAN(data->mld,
|
||||
"Updated PTK RSC for key %d on queue %d\n",
|
||||
key->keyidx, data->queue);
|
||||
} else {
|
||||
/* GTK */
|
||||
int rsc_idx = data->notif->mcast_key_id_map[key->keyidx];
|
||||
|
||||
if (rsc_idx == IWL_MCAST_KEY_MAP_INVALID)
|
||||
return;
|
||||
|
||||
if (IWL_FW_CHECK(data->mld,
|
||||
rsc_idx >= ARRAY_SIZE(data->notif->mcast_rsc),
|
||||
"Invalid mcast key mapping: %d for key %d\n",
|
||||
rsc_idx, key->keyidx))
|
||||
return;
|
||||
|
||||
for (int tid = 0; tid < IWL_MAX_TID_COUNT; tid++) {
|
||||
__le64 rsc =
|
||||
data->notif->mcast_rsc[rsc_idx][tid];
|
||||
|
||||
if (key->cipher == WLAN_CIPHER_SUITE_TKIP)
|
||||
iwl_mld_le64_to_tkip_seq(rsc, &seq);
|
||||
else
|
||||
iwl_mld_le64_to_aes_seq(rsc, &seq);
|
||||
ieee80211_set_key_rx_seq(key, tid, &seq);
|
||||
}
|
||||
|
||||
IWL_DEBUG_WOWLAN(data->mld,
|
||||
"Updated GTK %d RSC (rsc_idx %d) on queue %d\n",
|
||||
key->keyidx, rsc_idx, data->queue);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
iwl_mld_process_rsc_notification(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *notif,
|
||||
int queue)
|
||||
{
|
||||
struct iwl_mld_rsc_resume_iter_data iter_data = {
|
||||
.mld = mld,
|
||||
.notif = notif,
|
||||
.queue = queue,
|
||||
};
|
||||
|
||||
/* Iterate through all active keys and update RSC */
|
||||
ieee80211_iter_keys_rcu(mld->hw, vif,
|
||||
iwl_mld_rsc_update_key_iter,
|
||||
&iter_data);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
|
||||
struct iwl_mld *mld,
|
||||
|
|
@ -951,19 +1073,25 @@ iwl_mld_add_mcast_rekey(struct ieee80211_vif *vif,
|
|||
|
||||
iwl_mld_update_mcast_rx_seq(key_config, key_data);
|
||||
|
||||
/* The FW holds only one igtk so we keep track of the valid one */
|
||||
/* 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);
|
||||
struct iwl_mld_link_sta *mld_ap_link_sta;
|
||||
|
||||
mld_ap_link_sta = iwl_mld_get_ap_link_sta(vif,
|
||||
link_conf->link_id);
|
||||
if (WARN_ON(!mld_ap_link_sta))
|
||||
return;
|
||||
|
||||
/* If we had more than one rekey, mac80211 will tell us to
|
||||
* remove the old and add the new so we will update the IGTK in
|
||||
* drv_set_key
|
||||
*/
|
||||
if (mld_link->igtk && mld_link->igtk != key_config) {
|
||||
if (mld_ap_link_sta->rx_igtk &&
|
||||
mld_ap_link_sta->rx_igtk != key_config) {
|
||||
/* mark the old IGTK as not in FW */
|
||||
mld_link->igtk->hw_key_idx = STA_KEY_IDX_INVALID;
|
||||
mld_link->igtk = key_config;
|
||||
mld_ap_link_sta->rx_igtk->hw_key_idx =
|
||||
STA_KEY_IDX_INVALID;
|
||||
mld_ap_link_sta->rx_igtk = key_config;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
* Copyright (C) 2024, 2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_d3_h__
|
||||
#define __iwl_mld_d3_h__
|
||||
|
|
@ -42,6 +42,10 @@ 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);
|
||||
void iwl_mld_process_rsc_notification(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *notif,
|
||||
int queue);
|
||||
#if IS_ENABLED(CONFIG_IPV6)
|
||||
void iwl_mld_ipv6_addr_change(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "mld.h"
|
||||
|
|
@ -979,15 +979,39 @@ void iwl_mld_add_vif_debugfs(struct ieee80211_hw *hw,
|
|||
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) \
|
||||
|
||||
#define LINK_DEBUGFS_WIPHY_WRITE_FILE_OPS(name, bufsz) \
|
||||
WIPHY_DEBUGFS_WRITE_FILE_OPS(link_##name, bufsz, bss_conf)
|
||||
|
||||
/*
|
||||
* Note: no locking is provided, so the function must have its own,
|
||||
* but it cannot acquire the wiphy mutex.
|
||||
*/
|
||||
#define LINK_DEBUGFS_READ_FILE_OPS(name, bufsz) \
|
||||
_MLD_DEBUGFS_READ_FILE_OPS(link_##name, bufsz, struct ieee80211_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)
|
||||
|
||||
static ssize_t iwl_dbgfs_link_fw_id_read(struct ieee80211_bss_conf *link_conf,
|
||||
size_t buflen, void *buf)
|
||||
{
|
||||
struct iwl_mld_link *mld_link;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
mld_link = iwl_mld_link_from_mac80211(link_conf);
|
||||
if (!mld_link)
|
||||
return -EINVAL;
|
||||
|
||||
return scnprintf(buf, buflen, "%d\n", mld_link->fw_id);
|
||||
}
|
||||
|
||||
LINK_DEBUGFS_READ_FILE_OPS(fw_id, 64);
|
||||
|
||||
void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
|
|
@ -1008,6 +1032,8 @@ void iwl_mld_add_link_debugfs(struct ieee80211_hw *hw,
|
|||
/* Release the reference from debugfs_lookup */
|
||||
dput(mld_link_dir);
|
||||
}
|
||||
|
||||
LINK_DEBUGFS_ADD_FILE(fw_id, mld_link_dir, 0400);
|
||||
}
|
||||
|
||||
static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
||||
|
|
@ -1035,7 +1061,6 @@ static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
|||
|
||||
/* 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 :
|
||||
|
|
@ -1050,20 +1075,22 @@ static ssize_t _iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
|||
return ret ? : count;
|
||||
}
|
||||
|
||||
static ssize_t iwl_dbgfs_fixed_rate_write(struct iwl_mld *mld, char *buf,
|
||||
size_t count, void *data)
|
||||
static ssize_t
|
||||
iwl_dbgfs_link_sta_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)
|
||||
static ssize_t
|
||||
iwl_dbgfs_link_sta_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)
|
||||
static ssize_t iwl_dbgfs_link_sta_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;
|
||||
|
|
@ -1090,18 +1117,42 @@ static ssize_t iwl_dbgfs_tlc_dhc_write(struct iwl_mld *mld, char *buf,
|
|||
return ret ? : count;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
iwl_dbgfs_link_sta_fw_id_read(struct ieee80211_link_sta *link_sta,
|
||||
size_t buflen, void *buf)
|
||||
{
|
||||
struct iwl_mld_link_sta *mld_link_sta;
|
||||
|
||||
guard(rcu)();
|
||||
|
||||
mld_link_sta = iwl_mld_link_sta_from_mac80211(link_sta);
|
||||
if (!mld_link_sta)
|
||||
return -EINVAL;
|
||||
|
||||
return scnprintf(buf, buflen, "%u\n", mld_link_sta->fw_id);
|
||||
}
|
||||
|
||||
#define LINK_STA_DEBUGFS_ADD_FILE_ALIAS(alias, name, parent, mode) \
|
||||
debugfs_create_file(alias, mode, parent, link_sta, \
|
||||
&iwl_dbgfs_##name##_ops)
|
||||
&iwl_dbgfs_link_sta_##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)
|
||||
WIPHY_DEBUGFS_WRITE_FILE_OPS(link_sta_##name, bufsz, link_sta)
|
||||
|
||||
/*
|
||||
* Note: no locking is provided, so the function must have its own,
|
||||
* but it cannot acquire the wiphy mutex.
|
||||
*/
|
||||
#define LINK_STA_DEBUGFS_READ_OPS(name, bufsz) \
|
||||
_MLD_DEBUGFS_READ_FILE_OPS(link_sta_##name, bufsz, \
|
||||
struct ieee80211_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);
|
||||
LINK_STA_DEBUGFS_READ_OPS(fw_id, 64);
|
||||
|
||||
void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
|
|
@ -1111,4 +1162,5 @@ void iwl_mld_add_link_sta_debugfs(struct ieee80211_hw *hw,
|
|||
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);
|
||||
LINK_STA_DEBUGFS_ADD_FILE(fw_id, dir, 0400);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,24 +71,34 @@ iwl_mld_ftm_set_target_chandef(struct iwl_mld *mld,
|
|||
|
||||
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;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_LEGACY,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
|
||||
target->format_bw |= IWL_LOCATION_BW_20MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_20MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_HT;
|
||||
target->format_bw |= IWL_LOCATION_BW_40MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_40MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_VHT;
|
||||
target->format_bw |= IWL_LOCATION_BW_80MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_VHT,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_80MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
target->format_bw = IWL_LOCATION_FRAME_FORMAT_HE;
|
||||
target->format_bw |= IWL_LOCATION_BW_160MHZ << LOCATION_BW_POS;
|
||||
target->format_bw = u8_encode_bits(IWL_LOCATION_FRAME_FORMAT_HE,
|
||||
IWL_LOCATION_FMT_BW_FORMAT);
|
||||
target->format_bw |= u8_encode_bits(IWL_LOCATION_BW_160MHZ,
|
||||
IWL_LOCATION_FMT_BW_BANDWIDTH);
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(mld, "Unsupported BW in FTM request (%d)\n",
|
||||
|
|
|
|||
|
|
@ -1,10 +1,11 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <net/cfg80211.h>
|
||||
|
||||
#include "iface.h"
|
||||
#include "nan.h"
|
||||
#include "hcmd.h"
|
||||
#include "key.h"
|
||||
#include "mlo.h"
|
||||
|
|
@ -55,6 +56,24 @@ void iwl_mld_cleanup_vif(void *data, u8 *mac, struct ieee80211_vif *vif)
|
|||
|
||||
ieee80211_iter_keys(mld->hw, vif, iwl_mld_cleanup_keys_iter, NULL);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
mld_vif->nan.mac_added = false;
|
||||
/* Clean up NAN links */
|
||||
for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++)
|
||||
iwl_mld_cleanup_nan_link(&mld_vif->nan.links[i]);
|
||||
|
||||
if (mld_vif->nan.bcast_sta.sta_id != IWL_INVALID_STA)
|
||||
iwl_mld_free_internal_sta(mld, &mld_vif->nan.bcast_sta);
|
||||
if (mld_vif->nan.mgmt_sta.sta_id != IWL_INVALID_STA)
|
||||
iwl_mld_free_internal_sta(mld, &mld_vif->nan.mgmt_sta);
|
||||
|
||||
mld_vif->nan.tx_igtk = NULL;
|
||||
}
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA &&
|
||||
mld_vif->nan.mcast_data_sta.sta_id != IWL_INVALID_STA)
|
||||
iwl_mld_free_internal_sta(mld, &mld_vif->nan.mcast_data_sta);
|
||||
|
||||
CLEANUP_STRUCT(mld_vif);
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +113,8 @@ static int iwl_mld_mac80211_iftype_to_fw(const struct ieee80211_vif *vif)
|
|||
return FW_MAC_TYPE_P2P_DEVICE;
|
||||
case NL80211_IFTYPE_ADHOC:
|
||||
return FW_MAC_TYPE_IBSS;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
return FW_MAC_TYPE_NAN;
|
||||
default:
|
||||
WARN_ON_ONCE(1);
|
||||
}
|
||||
|
|
@ -362,6 +383,42 @@ static void iwl_mld_fill_mac_cmd_ibss(struct iwl_mld *mld,
|
|||
MAC_CFG_FILTER_ACCEPT_GRP);
|
||||
}
|
||||
|
||||
static int iwl_mld_fill_mac_cmd_nan(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_vif *ndi_being_added,
|
||||
struct iwl_mac_config_cmd *cmd)
|
||||
{
|
||||
struct ieee80211_vif *iter;
|
||||
u32 idx = 0;
|
||||
|
||||
cmd->filter_flags = cpu_to_le32(MAC_CFG_FILTER_ACCEPT_CONTROL_AND_MGMT);
|
||||
|
||||
/*
|
||||
* A NAN_DATA vif might be in the process of being added - it won't
|
||||
* be found by the iteration below since it's not yet active/in-driver.
|
||||
* In hw restart, the iteration below will find the ndi_being_added.
|
||||
*/
|
||||
if (ndi_being_added && !mld->fw_status.in_hw_restart) {
|
||||
memcpy(cmd->nan.ndi_addrs[idx].addr, ndi_being_added->addr, ETH_ALEN);
|
||||
idx++;
|
||||
}
|
||||
|
||||
for_each_active_interface(iter, mld->hw) {
|
||||
if (iter->type != NL80211_IFTYPE_NAN_DATA)
|
||||
continue;
|
||||
|
||||
if (WARN_ON_ONCE(idx >= ARRAY_SIZE(cmd->nan.ndi_addrs)))
|
||||
return -EINVAL;
|
||||
|
||||
memcpy(cmd->nan.ndi_addrs[idx].addr, iter->addr, ETH_ALEN);
|
||||
idx++;
|
||||
}
|
||||
|
||||
cmd->nan.ndi_addrs_count = cpu_to_le32(idx);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
|
|
@ -374,16 +431,23 @@ iwl_mld_rm_mac_from_fw(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
|||
return iwl_mld_send_mac_cmd(mld, &cmd);
|
||||
}
|
||||
|
||||
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
u32 action)
|
||||
static int
|
||||
__iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
u32 action, struct ieee80211_vif *ndi_being_added)
|
||||
{
|
||||
struct iwl_mac_config_cmd cmd = {};
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
/* NAN interface type is not known to FW */
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
return 0;
|
||||
/* NAN_DATA interface type is not known to FW */
|
||||
if (WARN_ON(vif->type == NL80211_IFTYPE_NAN_DATA))
|
||||
return -EINVAL;
|
||||
|
||||
/* ndi_being_added is only relevant for NAN and when adding a NAN_DATA interface */
|
||||
if (WARN_ON(ndi_being_added &&
|
||||
(vif->type != NL80211_IFTYPE_NAN || action != FW_CTXT_ACTION_MODIFY)))
|
||||
return -EINVAL;
|
||||
|
||||
if (action == FW_CTXT_ACTION_REMOVE)
|
||||
return iwl_mld_rm_mac_from_fw(mld, vif);
|
||||
|
|
@ -411,6 +475,11 @@ int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
|||
case NL80211_IFTYPE_ADHOC:
|
||||
iwl_mld_fill_mac_cmd_ibss(mld, vif, &cmd);
|
||||
break;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
ret = iwl_mld_fill_mac_cmd_nan(mld, vif, ndi_being_added, &cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
break;
|
||||
default:
|
||||
WARN(1, "not supported yet\n");
|
||||
return -EOPNOTSUPP;
|
||||
|
|
@ -419,6 +488,12 @@ int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
|||
return iwl_mld_send_mac_cmd(mld, &cmd);
|
||||
}
|
||||
|
||||
int iwl_mld_mac_fw_action(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
u32 action)
|
||||
{
|
||||
return __iwl_mld_mac_fw_action(mld, vif, action, NULL);
|
||||
}
|
||||
|
||||
static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,
|
||||
struct wiphy_work *wk)
|
||||
{
|
||||
|
|
@ -430,7 +505,7 @@ static void iwl_mld_mlo_scan_start_wk(struct wiphy *wiphy,
|
|||
iwl_mld_int_mlo_scan(mld, iwl_mld_vif_to_mac80211(mld_vif));
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(vif, vif)
|
||||
IWL_MLD_ALLOC_FN_STATIC(vif, vif)
|
||||
|
||||
/* Constructor function for struct iwl_mld_vif */
|
||||
static void
|
||||
|
|
@ -456,9 +531,40 @@ iwl_mld_init_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
|||
wiphy_delayed_work_init(&mld_vif->mlo_scan_start_wk,
|
||||
iwl_mld_mlo_scan_start_wk);
|
||||
}
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
for (int i = 0; i < ARRAY_SIZE(mld_vif->nan.links); i++) {
|
||||
memset(&mld_vif->nan.links[i], 0, sizeof(mld_vif->nan.links[i]));
|
||||
mld_vif->nan.links[i].fw_id = FW_CTXT_ID_INVALID;
|
||||
}
|
||||
|
||||
iwl_mld_init_internal_sta(&mld_vif->nan.bcast_sta);
|
||||
iwl_mld_init_internal_sta(&mld_vif->nan.mgmt_sta);
|
||||
} else if (vif->type == NL80211_IFTYPE_NAN_DATA) {
|
||||
iwl_mld_init_internal_sta(&mld_vif->nan.mcast_data_sta);
|
||||
}
|
||||
|
||||
iwl_mld_init_internal_sta(&mld_vif->aux_sta);
|
||||
}
|
||||
|
||||
static int iwl_mld_update_nan_mac(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *ndi_being_added)
|
||||
{
|
||||
struct ieee80211_vif *vif = mld->nan_device_vif;
|
||||
struct iwl_mld_vif *mld_vif;
|
||||
|
||||
if (WARN_ON_ONCE(!vif))
|
||||
return -ENODEV;
|
||||
|
||||
mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
if (!iwl_mld_vif_fw_id_valid(mld_vif))
|
||||
return 0;
|
||||
|
||||
return __iwl_mld_mac_fw_action(mld, vif, FW_CTXT_ACTION_MODIFY,
|
||||
ndi_being_added);
|
||||
}
|
||||
|
||||
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);
|
||||
|
|
@ -468,10 +574,14 @@ int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
|||
|
||||
iwl_mld_init_vif(mld, vif);
|
||||
|
||||
/* NAN interface type is not known to FW */
|
||||
/* NAN MACs are added to FW only when a schedule is set */
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
return 0;
|
||||
|
||||
/* NAN_DATA interface type is not known to FW, but we need to update NAN MAC */
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA)
|
||||
return iwl_mld_update_nan_mac(mld, vif);
|
||||
|
||||
ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, vif);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
|
@ -483,23 +593,52 @@ int iwl_mld_add_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
|||
return ret;
|
||||
}
|
||||
|
||||
int iwl_mld_add_nan_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);
|
||||
|
||||
if (WARN_ON(vif->type != NL80211_IFTYPE_NAN))
|
||||
return -EINVAL;
|
||||
|
||||
ret = iwl_mld_allocate_vif_fw_id(mld, &mld_vif->fw_id, 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;
|
||||
}
|
||||
|
||||
mld_vif->nan.mac_added = true;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iwl_mld_rm_vif(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
/* NAN interface type is not known to FW */
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA) {
|
||||
iwl_mld_update_nan_mac(mld, NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!iwl_mld_vif_fw_id_valid(mld_vif))
|
||||
return;
|
||||
|
||||
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;
|
||||
|
||||
RCU_INIT_POINTER(mld->fw_id_to_vif[mld_vif->fw_id], NULL);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
mld_vif->nan.mac_added = false;
|
||||
|
||||
iwl_mld_cancel_notifications_of_object(mld, IWL_MLD_OBJECT_TYPE_VIF,
|
||||
mld_vif->fw_id);
|
||||
}
|
||||
|
|
@ -616,24 +755,6 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
|
|||
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)
|
||||
{
|
||||
|
|
@ -748,6 +869,6 @@ struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld)
|
|||
|
||||
fw_id = __ffs(fw_id_bitmap);
|
||||
|
||||
return wiphy_dereference(mld->wiphy,
|
||||
mld->fw_id_to_vif[fw_id]);
|
||||
return rcu_dereference_wiphy(mld->wiphy,
|
||||
mld->fw_id_to_vif[fw_id]);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <net/mac80211.h>
|
||||
|
||||
#include "link.h"
|
||||
#include "nan.h"
|
||||
#include "session-protect.h"
|
||||
#include "d3.h"
|
||||
#include "fw/api/time-event.h"
|
||||
|
|
@ -137,7 +138,6 @@ struct iwl_mld_emlsr {
|
|||
* @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
|
||||
* @last_link_activation_time: last time a link was activated, for
|
||||
* deferring MLO scans (to make them more reliable)
|
||||
* @mld: pointer to the mld structure.
|
||||
|
|
@ -152,6 +152,16 @@ struct iwl_mld_emlsr {
|
|||
* p2p device only. Set to %ROC_NUM_ACTIVITIES when not in use.
|
||||
* @aux_sta: station used for remain on channel. Used in P2P device.
|
||||
* @mlo_scan_start_wk: worker to start a deferred MLO scan
|
||||
* @nan: NAN parameters
|
||||
* @nan.links: NAN links for FW (indexed by FW link ID)
|
||||
* @nan.mac_added: track whether or not the MAC was added to FW
|
||||
* @nan.bcast_sta: internal station used for NAN synchronization and discovery
|
||||
* activities. No queue is associated with it.
|
||||
* @nan.mgmt_sta: internal station used for NAN management frames, e.g., SDFs
|
||||
* and NAFs.
|
||||
* @nan.mcast_data_sta: internal station used for multicast NAN Data frames.
|
||||
* @nan.tx_igtk: TX IGTK key for NAN, tracked separately since NAN does not
|
||||
* use the vif links.
|
||||
*/
|
||||
struct iwl_mld_vif {
|
||||
/* Add here fields that need clean up on restart */
|
||||
|
|
@ -167,7 +177,6 @@ struct iwl_mld_vif {
|
|||
bool beacon_inject_active;
|
||||
#endif
|
||||
u8 low_latency_causes;
|
||||
bool ps_disabled;
|
||||
time64_t last_link_activation_time;
|
||||
);
|
||||
/* And here fields that survive a fw restart */
|
||||
|
|
@ -175,6 +184,16 @@ struct iwl_mld_vif {
|
|||
struct iwl_mld_link deflink;
|
||||
struct iwl_mld_link __rcu *link[IEEE80211_MLD_MAX_NUM_LINKS];
|
||||
|
||||
struct {
|
||||
/* use only with wiphy protection */
|
||||
struct iwl_mld_nan_link links[IWL_FW_MAX_LINKS];
|
||||
bool mac_added;
|
||||
struct iwl_mld_int_sta bcast_sta;
|
||||
struct iwl_mld_int_sta mgmt_sta;
|
||||
struct iwl_mld_int_sta mcast_data_sta;
|
||||
struct ieee80211_key_conf *tx_igtk;
|
||||
} nan;
|
||||
|
||||
struct iwl_mld_emlsr emlsr;
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
|
@ -206,6 +225,20 @@ iwl_mld_vif_to_mac80211(struct iwl_mld_vif *mld_vif)
|
|||
/* Call only for interfaces that were added to the driver! */
|
||||
static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif)
|
||||
{
|
||||
struct ieee80211_vif *vif = iwl_mld_vif_to_mac80211(mld_vif);
|
||||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_NAN_DATA:
|
||||
return false;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
if (!mld_vif->nan.mac_added)
|
||||
return false;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
/* Should be added to FW */
|
||||
if (WARN_ON(mld_vif->fw_id >= ARRAY_SIZE(mld_vif->mld->fw_id_to_vif)))
|
||||
return false;
|
||||
|
||||
|
|
@ -221,6 +254,12 @@ static inline bool iwl_mld_vif_fw_id_valid(struct iwl_mld_vif *mld_vif)
|
|||
link_id++) \
|
||||
if ((mld_link = iwl_mld_link_dereference_check(mld_vif, link_id)))
|
||||
|
||||
#define for_each_mld_nan_valid_link(mld_vif, nan_link) \
|
||||
for (nan_link = &(mld_vif)->nan.links[0]; \
|
||||
nan_link < &(mld_vif)->nan.links[ARRAY_SIZE((mld_vif)->nan.links)]; \
|
||||
nan_link++) \
|
||||
if (nan_link->fw_id != FW_CTXT_ID_INVALID)
|
||||
|
||||
/* 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)
|
||||
|
|
@ -235,6 +274,7 @@ 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_add_nan_vif(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
void 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);
|
||||
|
|
@ -245,9 +285,6 @@ void iwl_mld_handle_probe_resp_data_notif(struct iwl_mld *mld,
|
|||
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);
|
||||
|
||||
|
|
@ -258,4 +295,19 @@ static inline bool iwl_mld_vif_low_latency(const struct iwl_mld_vif *mld_vif)
|
|||
|
||||
struct ieee80211_vif *iwl_mld_get_bss_vif(struct iwl_mld *mld);
|
||||
|
||||
static inline struct iwl_mld_link_sta *
|
||||
iwl_mld_get_ap_link_sta(struct ieee80211_vif *vif, int link_id)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct ieee80211_sta *ap_sta = mld_vif->ap_sta;
|
||||
struct iwl_mld_sta *mld_ap_sta;
|
||||
|
||||
if (!ap_sta)
|
||||
return NULL;
|
||||
|
||||
mld_ap_sta = iwl_mld_sta_from_mac80211(ap_sta);
|
||||
|
||||
return iwl_mld_link_sta_dereference_check(mld_ap_sta, link_id);
|
||||
}
|
||||
|
||||
#endif /* __iwl_mld_iface_h__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024 Intel Corporation
|
||||
* Copyright (C) 2024, 2026 Intel Corporation
|
||||
*/
|
||||
#include "key.h"
|
||||
#include "iface.h"
|
||||
|
|
@ -12,7 +12,6 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
|
|||
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;
|
||||
|
|
@ -38,9 +37,6 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
|
|||
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.
|
||||
|
|
@ -53,6 +49,12 @@ static u32 iwl_mld_get_key_flags(struct iwl_mld *mld,
|
|||
if (key->flags & IEEE80211_KEY_FLAG_SPP_AMSDU)
|
||||
flags |= IWL_SEC_KEY_FLAG_SPP_AMSDU;
|
||||
|
||||
/* When a GTK is configured for a station, it can only be
|
||||
* used for Rx and never for Tx. Thus, set the NO TX flag.
|
||||
*/
|
||||
if (!pairwise && sta)
|
||||
flags |= IWL_SEC_KEY_FLAG_NO_TX;
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
|
|
@ -67,6 +69,40 @@ static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,
|
|||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA && !sta) {
|
||||
/* Older firmware versions do not support transmission of
|
||||
* multicast data frames.
|
||||
*/
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
return 0;
|
||||
|
||||
if (WARN_ON(mld_vif->nan.mcast_data_sta.sta_id ==
|
||||
IWL_INVALID_STA))
|
||||
return 0;
|
||||
|
||||
return BIT(mld_vif->nan.mcast_data_sta.sta_id);
|
||||
}
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN && !sta) {
|
||||
/* Older firmware versions do not support installation of
|
||||
* IGTK/BIGTK keys.
|
||||
*/
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
return 0;
|
||||
|
||||
if (WARN_ON(mld_vif->nan.bcast_sta.sta_id == IWL_INVALID_STA ||
|
||||
mld_vif->nan.mgmt_sta.sta_id == IWL_INVALID_STA))
|
||||
return 0;
|
||||
|
||||
if (key->keyidx >= 4 && key->keyidx <= 5)
|
||||
return BIT(mld_vif->nan.mgmt_sta.sta_id);
|
||||
|
||||
if (key->keyidx >= 6 && key->keyidx <= 7)
|
||||
return BIT(mld_vif->nan.bcast_sta.sta_id);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* 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)) {
|
||||
|
|
@ -92,11 +128,7 @@ static u32 iwl_mld_get_key_sta_mask(struct iwl_mld *mld,
|
|||
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 */
|
||||
/* STA should be non-NULL */
|
||||
if (WARN_ON(!sta))
|
||||
return 0;
|
||||
|
||||
|
|
@ -178,34 +210,69 @@ static void iwl_mld_remove_key_from_fw(struct iwl_mld *mld, u32 sta_mask,
|
|||
iwl_mld_send_cmd_pdu(mld, WIDE_ID(DATA_PATH_GROUP, SEC_KEY_CMD), &cmd);
|
||||
}
|
||||
|
||||
static struct ieee80211_key_conf **
|
||||
iwl_mld_get_igtk_ptr(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);
|
||||
/* key's link ID is set to -1 for non-MLO */
|
||||
int link_id = key->link_id < 0 ? 0 : key->link_id;
|
||||
struct iwl_mld_link_sta *mld_ap_link_sta;
|
||||
struct iwl_mld_link *mld_link;
|
||||
struct iwl_mld_sta *mld_sta;
|
||||
|
||||
if (key->keyidx != 4 && key->keyidx != 5)
|
||||
return NULL;
|
||||
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_STATION:
|
||||
if (WARN_ON(!sta))
|
||||
return NULL;
|
||||
|
||||
mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
mld_ap_link_sta = iwl_mld_link_sta_dereference_check(mld_sta,
|
||||
link_id);
|
||||
if (WARN_ON(!mld_ap_link_sta))
|
||||
return NULL;
|
||||
|
||||
return &mld_ap_link_sta->rx_igtk;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
if (sta) {
|
||||
mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
|
||||
return &mld_sta->deflink.rx_igtk;
|
||||
}
|
||||
|
||||
return &mld_vif->nan.tx_igtk;
|
||||
case NL80211_IFTYPE_AP:
|
||||
mld_link = iwl_mld_link_dereference_check(mld_vif, link_id);
|
||||
if (WARN_ON(!mld_link))
|
||||
return NULL;
|
||||
|
||||
return &mld_link->tx_igtk;
|
||||
default:
|
||||
WARN_ONCE(1, "invalid iftype %d for IGTK\n", vif->type);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
struct ieee80211_key_conf **igtk_ptr;
|
||||
|
||||
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;
|
||||
|
||||
igtk_ptr = iwl_mld_get_igtk_ptr(vif, sta, key);
|
||||
if (igtk_ptr && *igtk_ptr == key) {
|
||||
*igtk_ptr = NULL;
|
||||
mld->num_igtks--;
|
||||
}
|
||||
|
||||
|
|
@ -222,46 +289,43 @@ int iwl_mld_add_key(struct iwl_mld *mld,
|
|||
{
|
||||
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;
|
||||
struct ieee80211_key_conf **igtk_ptr;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (!sta_mask)
|
||||
return -EINVAL;
|
||||
if (!sta_mask) {
|
||||
/* for NAN (GTK) indicate SW-only, it's not used at all */
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA && !sta &&
|
||||
!iwl_mld_nan_use_nan_stations(mld))
|
||||
return 1;
|
||||
|
||||
if (igtk) {
|
||||
if (mld->num_igtks == IWL_MAX_NUM_IGTKS)
|
||||
/* otherwise that's not valid */
|
||||
IWL_WARN(mld, "empty STA mask for key %d\n", key->keyidx);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
igtk_ptr = iwl_mld_get_igtk_ptr(vif, sta, key);
|
||||
if (igtk_ptr) {
|
||||
if (mld->num_igtks == mld->fw->ucode_capa.num_mcast_key_entries)
|
||||
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) {
|
||||
if (*igtk_ptr) {
|
||||
IWL_DEBUG_MAC80211(mld, "remove old IGTK %d\n",
|
||||
mld_link->igtk->keyidx);
|
||||
iwl_mld_remove_key(mld, vif, sta, mld_link->igtk);
|
||||
(*igtk_ptr)->keyidx);
|
||||
iwl_mld_remove_key(mld, vif, sta, *igtk_ptr);
|
||||
}
|
||||
|
||||
WARN_ON(mld_link->igtk);
|
||||
}
|
||||
|
||||
ret = iwl_mld_add_key_to_fw(mld, sta_mask, key_flags, key);
|
||||
if (ret)
|
||||
if (ret) {
|
||||
IWL_WARN(mld, "failed to add key to FW (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (mld_link) {
|
||||
mld_link->igtk = key;
|
||||
if (igtk_ptr) {
|
||||
WARN_ON(*igtk_ptr);
|
||||
*igtk_ptr = key;
|
||||
mld->num_igtks++;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -16,14 +16,34 @@
|
|||
#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)
|
||||
/**
|
||||
* struct iwl_mld_link_chan_load_threshold - channel load thresholds
|
||||
* @high_lim: level up transition thresholds, in percentage
|
||||
* @low_lim: level down transition thresholds, in percentage
|
||||
*/
|
||||
struct iwl_mld_link_chan_load_threshold {
|
||||
u8 high_lim;
|
||||
u8 low_lim;
|
||||
};
|
||||
|
||||
static const struct iwl_mld_link_chan_load_threshold
|
||||
link_chan_load_thresh_tbl[] = {
|
||||
[LINK_CHAN_LOAD_LVL1] = { .high_lim = 45, .low_lim = 40 },
|
||||
[LINK_CHAN_LOAD_LVL2] = { .high_lim = 70, .low_lim = 65 },
|
||||
[LINK_CHAN_LOAD_LVL3] = { .high_lim = 85, .low_lim = 80 },
|
||||
};
|
||||
|
||||
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);
|
||||
|
||||
if (WARN_ON_ONCE(cmd->link_id == cpu_to_le32(FW_CTXT_ID_INVALID)))
|
||||
return -EINVAL;
|
||||
|
||||
cmd->action = cpu_to_le32(action);
|
||||
ret = iwl_mld_send_cmd_pdu(mld,
|
||||
WIDE_ID(MAC_CONF_GROUP, LINK_CONFIG_CMD),
|
||||
|
|
@ -315,10 +335,28 @@ iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
|
|||
link_sta_dereference_check(mld_vif->ap_sta,
|
||||
link->link_id);
|
||||
|
||||
if (!WARN_ON(!link_sta) && link_sta->he_cap.has_he &&
|
||||
if (WARN_ON(!link_sta))
|
||||
return -EINVAL;
|
||||
|
||||
if (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;
|
||||
|
||||
if (link_sta->uhr_cap.has_uhr &&
|
||||
link_sta->uhr_cap.mac.mac_cap[0] &
|
||||
IEEE80211_UHR_MAC_CAP0_DPS_ASSIST_SUPP)
|
||||
flags |= LINK_FLG_DPS;
|
||||
|
||||
if (link_sta->uhr_cap.has_uhr &&
|
||||
link_sta->uhr_cap.mac.mac_cap[1] &
|
||||
IEEE80211_UHR_MAC_CAP1_DUO_SUPP)
|
||||
flags |= LINK_FLG_DUO;
|
||||
|
||||
if (link_sta->uhr_cap.has_uhr &&
|
||||
mld_vif->ap_sta->ext_mld_capa_ops &
|
||||
IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM)
|
||||
flags |= LINK_FLG_MLPM;
|
||||
}
|
||||
|
||||
cmd.htc_trig_based_pkt_ext = link->htc_trig_based_pkt_ext;
|
||||
|
|
@ -354,6 +392,21 @@ iwl_mld_change_link_in_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link,
|
|||
if (WARN_ON(changes & LINK_CONTEXT_MODIFY_EHT_PARAMS))
|
||||
changes &= ~LINK_CONTEXT_MODIFY_EHT_PARAMS;
|
||||
|
||||
if (link->uhr_support && link->npca.enabled) {
|
||||
flags |= LINK_FLG_NPCA;
|
||||
if (link->npca.moplen)
|
||||
cmd.npca_params.flags |= IWL_NPCA_FLAG_MAC_HDR_BASED;
|
||||
cmd.npca_params.dis_subch_bmap =
|
||||
cpu_to_le16(link->chanreq.oper.npca_punctured);
|
||||
cmd.npca_params.initial_qsrc = link->npca.init_qsrc;
|
||||
cmd.npca_params.min_dur_threshold = link->npca.min_dur_thresh;
|
||||
/* spec/mac80211 have these in units of 4 usec */
|
||||
cmd.npca_params.switch_delay =
|
||||
4 * link->npca.switch_delay;
|
||||
cmd.npca_params.switch_back_delay =
|
||||
4 * link->npca.switch_back_delay;
|
||||
}
|
||||
|
||||
send_cmd:
|
||||
cmd.modify_mask = cpu_to_le32(changes);
|
||||
cmd.flags = cpu_to_le32(flags);
|
||||
|
|
@ -437,7 +490,8 @@ iwl_mld_rm_link_from_fw(struct iwl_mld *mld, struct ieee80211_bss_conf *link)
|
|||
iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(link, bss_conf)
|
||||
IWL_MLD_ALLOC_FN(link, bss_conf)
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_allocate_link_fw_id);
|
||||
|
||||
/* Constructor function for struct iwl_mld_link */
|
||||
static int
|
||||
|
|
@ -608,15 +662,20 @@ void iwl_mld_handle_missed_beacon_notif(struct iwl_mld *mld,
|
|||
* 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.
|
||||
*
|
||||
* When both links lose beacons, keep the primary (symmetric failure).
|
||||
* When only the current link is sick, keep the other link.
|
||||
*/
|
||||
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)) {
|
||||
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) {
|
||||
iwl_mld_exit_emlsr(mld, vif, IWL_MLD_EMLSR_EXIT_MISSED_BEACON,
|
||||
iwl_mld_get_primary_link(vif));
|
||||
} else if (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_other_link(vif, link_id));
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_handle_missed_beacon_notif);
|
||||
|
|
@ -651,35 +710,131 @@ struct iwl_mld_rssi_to_grade {
|
|||
u16 grade;
|
||||
};
|
||||
|
||||
#define RSSI_TO_GRADE_LINE(_lb, _hb_uhb, _grade) \
|
||||
#define RSSI_TO_GRADE_LINE_WITH_LB(_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 RSSI_TO_GRADE_LINE(_rssi, _grade) \
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(_rssi, _rssi, _grade)
|
||||
|
||||
/* Tables must be sorted by increasing RSSI */
|
||||
|
||||
/* 20 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_20mhz[] = {
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-94, -95, 9),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-92, -93, 17),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-90, -90, 34),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-87, -87, 52),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-83, -84, 69),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-79, -80, 103),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-75, -75, 137),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-72, -73, 155),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-70, -71, 172),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-67, -68, 206),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-64, -65, 230),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-59, -60, 258),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-57, -58, 287),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-52, -53, 310),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-50, -50, 345),
|
||||
};
|
||||
|
||||
#define MAX_GRADE (rssi_to_grade_map[ARRAY_SIZE(rssi_to_grade_map) - 1].grade)
|
||||
/* 40 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_40mhz[] = {
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-95, -95, 9),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-93, -93, 17),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-91, -92, 18),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-89, -90, 34),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-87, -87, 68),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-84, -84, 104),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-80, -81, 138),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-77, -77, 206),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-72, -72, 274),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-69, -70, 310),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-67, -68, 344),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-64, -65, 412),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-61, -62, 460),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-56, -57, 516),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-54, -55, 574),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-49, -50, 620),
|
||||
RSSI_TO_GRADE_LINE_WITH_LB(-46, -47, 690),
|
||||
};
|
||||
|
||||
/* 80 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_80mhz[] = {
|
||||
RSSI_TO_GRADE_LINE(-95, 9),
|
||||
RSSI_TO_GRADE_LINE(-93, 17),
|
||||
RSSI_TO_GRADE_LINE(-92, 18),
|
||||
RSSI_TO_GRADE_LINE(-90, 34),
|
||||
RSSI_TO_GRADE_LINE(-89, 36),
|
||||
RSSI_TO_GRADE_LINE(-87, 68),
|
||||
RSSI_TO_GRADE_LINE(-83, 136),
|
||||
RSSI_TO_GRADE_LINE(-80, 208),
|
||||
RSSI_TO_GRADE_LINE(-77, 276),
|
||||
RSSI_TO_GRADE_LINE(-74, 412),
|
||||
RSSI_TO_GRADE_LINE(-69, 548),
|
||||
RSSI_TO_GRADE_LINE(-67, 620),
|
||||
RSSI_TO_GRADE_LINE(-66, 688),
|
||||
RSSI_TO_GRADE_LINE(-61, 824),
|
||||
RSSI_TO_GRADE_LINE(-59, 920),
|
||||
RSSI_TO_GRADE_LINE(-54, 1032),
|
||||
RSSI_TO_GRADE_LINE(-52, 1148),
|
||||
RSSI_TO_GRADE_LINE(-47, 1240),
|
||||
RSSI_TO_GRADE_LINE(-44, 1380),
|
||||
};
|
||||
|
||||
/* 160 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_160mhz[] = {
|
||||
RSSI_TO_GRADE_LINE(-95, 9),
|
||||
RSSI_TO_GRADE_LINE(-93, 17),
|
||||
RSSI_TO_GRADE_LINE(-92, 18),
|
||||
RSSI_TO_GRADE_LINE(-90, 34),
|
||||
RSSI_TO_GRADE_LINE(-89, 36),
|
||||
RSSI_TO_GRADE_LINE(-87, 68),
|
||||
RSSI_TO_GRADE_LINE(-86, 72),
|
||||
RSSI_TO_GRADE_LINE(-84, 136),
|
||||
RSSI_TO_GRADE_LINE(-81, 272),
|
||||
RSSI_TO_GRADE_LINE(-78, 416),
|
||||
RSSI_TO_GRADE_LINE(-75, 552),
|
||||
RSSI_TO_GRADE_LINE(-71, 824),
|
||||
RSSI_TO_GRADE_LINE(-67, 1096),
|
||||
RSSI_TO_GRADE_LINE(-65, 1240),
|
||||
RSSI_TO_GRADE_LINE(-63, 1376),
|
||||
RSSI_TO_GRADE_LINE(-59, 1648),
|
||||
RSSI_TO_GRADE_LINE(-57, 1840),
|
||||
RSSI_TO_GRADE_LINE(-52, 2064),
|
||||
RSSI_TO_GRADE_LINE(-50, 2296),
|
||||
RSSI_TO_GRADE_LINE(-46, 2480),
|
||||
RSSI_TO_GRADE_LINE(-42, 2760),
|
||||
};
|
||||
|
||||
/* 320 MHz Operational BW Grading Table */
|
||||
static const struct iwl_mld_rssi_to_grade rssi_to_grade_320mhz[] = {
|
||||
RSSI_TO_GRADE_LINE(-95, 9),
|
||||
RSSI_TO_GRADE_LINE(-93, 17),
|
||||
RSSI_TO_GRADE_LINE(-92, 18),
|
||||
RSSI_TO_GRADE_LINE(-90, 34),
|
||||
RSSI_TO_GRADE_LINE(-89, 36),
|
||||
RSSI_TO_GRADE_LINE(-87, 68),
|
||||
RSSI_TO_GRADE_LINE(-86, 72),
|
||||
RSSI_TO_GRADE_LINE(-84, 136),
|
||||
RSSI_TO_GRADE_LINE(-83, 144),
|
||||
RSSI_TO_GRADE_LINE(-81, 272),
|
||||
RSSI_TO_GRADE_LINE(-78, 544),
|
||||
RSSI_TO_GRADE_LINE(-75, 832),
|
||||
RSSI_TO_GRADE_LINE(-72, 1104),
|
||||
RSSI_TO_GRADE_LINE(-69, 1648),
|
||||
RSSI_TO_GRADE_LINE(-64, 2192),
|
||||
RSSI_TO_GRADE_LINE(-62, 2480),
|
||||
RSSI_TO_GRADE_LINE(-61, 2752),
|
||||
RSSI_TO_GRADE_LINE(-57, 3296),
|
||||
RSSI_TO_GRADE_LINE(-55, 3680),
|
||||
RSSI_TO_GRADE_LINE(-50, 4128),
|
||||
RSSI_TO_GRADE_LINE(-47, 4592),
|
||||
RSSI_TO_GRADE_LINE(-43, 4960),
|
||||
RSSI_TO_GRADE_LINE(-40, 5520),
|
||||
};
|
||||
|
||||
#define DEFAULT_CHAN_LOAD_2GHZ 30
|
||||
#define DEFAULT_CHAN_LOAD_5GHZ 15
|
||||
|
|
@ -689,31 +844,27 @@ static const struct iwl_mld_rssi_to_grade rssi_to_grade_map[] = {
|
|||
#define SCALE_FACTOR 256
|
||||
#define MAX_CHAN_LOAD 256
|
||||
|
||||
static unsigned int
|
||||
iwl_mld_get_n_subchannels(const struct ieee80211_bss_conf *link_conf)
|
||||
static void
|
||||
iwl_mld_apply_puncturing_penalty(const struct ieee80211_bss_conf *link_conf,
|
||||
unsigned int *grade, int bw_mhz)
|
||||
{
|
||||
enum nl80211_chan_width chan_width =
|
||||
link_conf->chanreq.oper.width;
|
||||
int mhz = nl80211_chan_width_to_mhz(chan_width);
|
||||
unsigned int n_subchannels;
|
||||
unsigned int n_punctured, n_subchannels;
|
||||
|
||||
if (WARN_ONCE(mhz < 20 || mhz > 320,
|
||||
"Invalid channel width : (%d)\n", mhz))
|
||||
return 1;
|
||||
/* Puncturing only applicable for BW >= 80 MHz */
|
||||
if (bw_mhz < 80)
|
||||
return;
|
||||
|
||||
/* total number of subchannels */
|
||||
n_subchannels = mhz / 20;
|
||||
n_punctured = hweight16(link_conf->chanreq.oper.punctured);
|
||||
if (n_punctured == 0)
|
||||
return;
|
||||
|
||||
/* No puncturing if less than 80 MHz */
|
||||
if (mhz >= 80)
|
||||
n_subchannels -= hweight16(link_conf->chanreq.oper.punctured);
|
||||
n_subchannels = bw_mhz / 20;
|
||||
|
||||
return n_subchannels;
|
||||
*grade = *grade * (n_subchannels - n_punctured) / n_subchannels;
|
||||
}
|
||||
|
||||
static int
|
||||
iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
int iwl_mld_get_chan_load_from_element(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
const struct cfg80211_bss_ies *ies;
|
||||
const struct element *bss_load_elem = NULL;
|
||||
|
|
@ -787,6 +938,51 @@ int iwl_mld_get_chan_load_by_others(struct iwl_mld *mld,
|
|||
return chan_load;
|
||||
}
|
||||
|
||||
/* Returns whether internal MLO Scan needs to be triggered */
|
||||
bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
u32 new_chan_load)
|
||||
{
|
||||
struct iwl_mld_link *mld_link = iwl_mld_link_from_mac80211(link_conf);
|
||||
enum iwl_mld_link_chan_load_level new_lvl;
|
||||
bool scan_trig = false;
|
||||
|
||||
if (WARN_ON(!mld_link))
|
||||
return false;
|
||||
|
||||
/* For each Level,
|
||||
* First check if high limit threshold crosses
|
||||
* If not then, check if low limit threshold crosses
|
||||
* Set new level based on low limit thresh only if old level
|
||||
* is not lower than level threshold
|
||||
*/
|
||||
for (new_lvl = LINK_CHAN_LOAD_LVL_MAX;
|
||||
new_lvl > LINK_CHAN_LOAD_LVL_NONE; new_lvl--) {
|
||||
if (new_chan_load >=
|
||||
link_chan_load_thresh_tbl[new_lvl].high_lim)
|
||||
break;
|
||||
if (new_chan_load >=
|
||||
link_chan_load_thresh_tbl[new_lvl].low_lim &&
|
||||
mld_link->chan_load_lvl >= new_lvl)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Trigger scan only for Level Up Transition */
|
||||
if (new_lvl > mld_link->chan_load_lvl)
|
||||
scan_trig = true;
|
||||
|
||||
IWL_DEBUG_EHT(mld,
|
||||
"Link %d: chan_load=%d%%, old_lvl=%d, new_lvl=%d, scan_trig=%d\n",
|
||||
link_conf->link_id, new_chan_load,
|
||||
mld_link->chan_load_lvl, new_lvl, scan_trig);
|
||||
|
||||
/* Update computed new level */
|
||||
mld_link->chan_load_lvl = new_lvl;
|
||||
|
||||
return scan_trig;
|
||||
}
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_chan_load_requires_scan);
|
||||
|
||||
static unsigned int
|
||||
iwl_mld_get_default_chan_load(struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
|
|
@ -828,16 +1024,214 @@ iwl_mld_get_avail_chan_load(struct iwl_mld *mld,
|
|||
return MAX_CHAN_LOAD - iwl_mld_get_chan_load(mld, link_conf);
|
||||
}
|
||||
|
||||
static s8
|
||||
iwl_mld_get_dup_beacon_rssi_adjust(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
const struct ieee80211_he_6ghz_oper *he_6ghz_oper;
|
||||
const struct cfg80211_bss_ies *beacon_ies;
|
||||
const struct element *elem;
|
||||
|
||||
/* Duplicated beacon feature is only specific to 6 GHz */
|
||||
if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ,
|
||||
"Unexpected band %d\n",
|
||||
link_conf->chanreq.oper.chan->band))
|
||||
return 0;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
beacon_ies = wiphy_dereference(mld->wiphy, link_conf->bss->beacon_ies);
|
||||
if (!beacon_ies)
|
||||
return 0;
|
||||
|
||||
elem = cfg80211_find_ext_elem(WLAN_EID_EXT_HE_OPERATION,
|
||||
beacon_ies->data, beacon_ies->len);
|
||||
if (!elem ||
|
||||
elem->datalen < sizeof(struct ieee80211_he_operation) + 1 ||
|
||||
elem->datalen < ieee80211_he_oper_size(&elem->data[1]))
|
||||
return 0;
|
||||
|
||||
he_6ghz_oper = ieee80211_he_6ghz_oper((const void *)&elem->data[1]);
|
||||
if (!he_6ghz_oper)
|
||||
return 0;
|
||||
|
||||
if (!(he_6ghz_oper->control & IEEE80211_HE_6GHZ_OPER_CTRL_DUP_BEACON))
|
||||
return 0;
|
||||
|
||||
/* Apply adjustment based on operational bandwidth */
|
||||
switch (link_conf->chanreq.oper.width) {
|
||||
case NL80211_CHAN_WIDTH_20:
|
||||
case NL80211_CHAN_WIDTH_20_NOHT:
|
||||
return 0;
|
||||
case NL80211_CHAN_WIDTH_40:
|
||||
return 3;
|
||||
case NL80211_CHAN_WIDTH_80:
|
||||
return 6;
|
||||
case NL80211_CHAN_WIDTH_160:
|
||||
return 9;
|
||||
case NL80211_CHAN_WIDTH_320:
|
||||
return 12;
|
||||
default:
|
||||
WARN_ONCE(1, "Unexpected channel width: %d\n",
|
||||
link_conf->chanreq.oper.width);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static s8 iwl_mld_get_primary_psd(const struct ieee80211_parsed_tpe_psd *psd,
|
||||
const struct cfg80211_chan_def *chandef,
|
||||
int bw_mhz)
|
||||
{
|
||||
int start_freq, primary_idx;
|
||||
|
||||
if (!psd->valid)
|
||||
return S8_MAX;
|
||||
|
||||
start_freq = chandef->center_freq1 - (bw_mhz / 2);
|
||||
primary_idx = (chandef->chan->center_freq - start_freq - 10) / 20;
|
||||
|
||||
if (primary_idx < 0 || primary_idx >= psd->count)
|
||||
return S8_MAX;
|
||||
|
||||
/* TPE element stores PSD limit as value * 2 */
|
||||
return psd->power[primary_idx] / 2;
|
||||
}
|
||||
|
||||
static s8 iwl_mld_get_psd_eirp_rssi_adjust(struct ieee80211_bss_conf *link_conf)
|
||||
{
|
||||
const struct ieee80211_parsed_tpe *tpe = &link_conf->tpe;
|
||||
s8 psd_20mhz, psd_oper, psd_local, psd_reg, psd_boost;
|
||||
s8 min_20mhz, min_oper, adjustment, ap_power_limit;
|
||||
s8 psd_avg_local = S8_MAX, psd_avg_reg = S8_MAX;
|
||||
s8 eirp_20mhz, eirp_oper, eirp_local, eirp_reg;
|
||||
int bw_mhz, num_subchans;
|
||||
u8 bw_index;
|
||||
|
||||
/* PSD/EIRP adjustment is only specific to 6 GHz */
|
||||
if (WARN_ONCE(link_conf->chanreq.oper.chan->band != NL80211_BAND_6GHZ,
|
||||
"PSD/EIRP adjustment called for non-6 GHz band %d\n",
|
||||
link_conf->chanreq.oper.chan->band))
|
||||
return 0;
|
||||
|
||||
bw_mhz = nl80211_chan_width_to_mhz(link_conf->chanreq.oper.width);
|
||||
|
||||
switch (bw_mhz) {
|
||||
case 20:
|
||||
bw_index = 0;
|
||||
break;
|
||||
case 40:
|
||||
bw_index = 1;
|
||||
break;
|
||||
case 80:
|
||||
bw_index = 2;
|
||||
break;
|
||||
case 160:
|
||||
bw_index = 3;
|
||||
break;
|
||||
case 320:
|
||||
bw_index = 4;
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Unexpected bandwidth: %d MHz\n", bw_mhz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (link_conf->power_type == IEEE80211_REG_VLP_AP)
|
||||
ap_power_limit = 14;
|
||||
else
|
||||
ap_power_limit = 23;
|
||||
|
||||
/* Primary 20 MHz PSD */
|
||||
psd_local = iwl_mld_get_primary_psd(&tpe->psd_local[0],
|
||||
&link_conf->chanreq.oper,
|
||||
bw_mhz);
|
||||
psd_reg = iwl_mld_get_primary_psd(&tpe->psd_reg_client[0],
|
||||
&link_conf->chanreq.oper,
|
||||
bw_mhz);
|
||||
psd_20mhz = min(psd_local, psd_reg);
|
||||
|
||||
/* TPE element stores EIRP limit as value * 2 */
|
||||
eirp_local = (tpe->max_local[0].valid && tpe->max_local[0].count > 0) ?
|
||||
tpe->max_local[0].power[0] / 2 : S8_MAX;
|
||||
eirp_reg = (tpe->max_reg_client[0].valid &&
|
||||
tpe->max_reg_client[0].count > 0) ?
|
||||
tpe->max_reg_client[0].power[0] / 2 : S8_MAX;
|
||||
eirp_20mhz = min(eirp_local, eirp_reg);
|
||||
|
||||
num_subchans = bw_mhz / 20;
|
||||
|
||||
if (tpe->psd_local[0].valid) {
|
||||
int sum_local = 0, valid_local = 0;
|
||||
int count_local = min(num_subchans, tpe->psd_local[0].count);
|
||||
|
||||
for (int i = 0; i < count_local; i++) {
|
||||
if (tpe->psd_local[0].power[i] != S8_MIN) {
|
||||
sum_local += tpe->psd_local[0].power[i];
|
||||
valid_local++;
|
||||
}
|
||||
}
|
||||
/* TPE element stores PSD limit as value * 2 */
|
||||
if (valid_local > 0)
|
||||
psd_avg_local = sum_local / valid_local / 2;
|
||||
}
|
||||
|
||||
if (tpe->psd_reg_client[0].valid) {
|
||||
int sum_reg = 0, valid_reg = 0;
|
||||
int count_reg = min(num_subchans, tpe->psd_reg_client[0].count);
|
||||
|
||||
for (int i = 0; i < count_reg; i++) {
|
||||
if (tpe->psd_reg_client[0].power[i] != S8_MIN) {
|
||||
sum_reg +=
|
||||
tpe->psd_reg_client[0].power[i];
|
||||
valid_reg++;
|
||||
}
|
||||
}
|
||||
/* TPE element stores PSD limit as value * 2 */
|
||||
if (valid_reg > 0)
|
||||
psd_avg_reg = sum_reg / valid_reg / 2;
|
||||
}
|
||||
|
||||
psd_oper = min(psd_avg_local, psd_avg_reg);
|
||||
|
||||
/* TPE element stores EIRP limit as value * 2 */
|
||||
eirp_local = (tpe->max_local[0].valid &&
|
||||
tpe->max_local[0].count > bw_index) ?
|
||||
tpe->max_local[0].power[bw_index] / 2 : S8_MAX;
|
||||
eirp_reg = (tpe->max_reg_client[0].valid &&
|
||||
tpe->max_reg_client[0].count > bw_index) ?
|
||||
tpe->max_reg_client[0].power[bw_index] / 2 : S8_MAX;
|
||||
eirp_oper = min(eirp_local, eirp_reg);
|
||||
|
||||
min_20mhz = min(ap_power_limit, min(eirp_20mhz, psd_20mhz));
|
||||
|
||||
/* PSD boost: 10*log10(BW/20) approximated as 3*ilog2(BW/20) */
|
||||
psd_boost = 3 * ilog2(bw_mhz / 20);
|
||||
|
||||
/* Use int for psd_oper + psd_boost to prevent s8 overflow */
|
||||
min_oper = min(ap_power_limit,
|
||||
min(eirp_oper,
|
||||
(s8)min_t(int, psd_oper + psd_boost, S8_MAX)));
|
||||
|
||||
adjustment = max(min_oper - min_20mhz, 0);
|
||||
|
||||
return adjustment;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
const struct iwl_mld_rssi_to_grade *grade_table;
|
||||
enum nl80211_band band;
|
||||
int rssi_idx;
|
||||
int rssi_idx, table_size, bw_mhz;
|
||||
s32 link_rssi;
|
||||
unsigned int grade = MAX_GRADE;
|
||||
unsigned int grade;
|
||||
|
||||
if (WARN_ON_ONCE(!link_conf))
|
||||
if (WARN_ON_ONCE(!link_conf || !link_conf->bss))
|
||||
return 0;
|
||||
|
||||
bw_mhz = nl80211_chan_width_to_mhz(link_conf->chanreq.oper.width);
|
||||
if (bw_mhz < 0)
|
||||
return 0;
|
||||
|
||||
band = link_conf->chanreq.oper.chan->band;
|
||||
|
|
@ -852,25 +1246,64 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
|
|||
* 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;
|
||||
if (band == NL80211_BAND_6GHZ && link_rssi) {
|
||||
s8 rssi_adj_6g =
|
||||
iwl_mld_get_dup_beacon_rssi_adjust(mld, link_conf);
|
||||
|
||||
if (!rssi_adj_6g)
|
||||
rssi_adj_6g =
|
||||
iwl_mld_get_psd_eirp_rssi_adjust(link_conf);
|
||||
|
||||
if (!rssi_adj_6g)
|
||||
rssi_adj_6g = 4;
|
||||
|
||||
link_rssi += rssi_adj_6g;
|
||||
}
|
||||
|
||||
/* Select grading table based on operational bandwidth */
|
||||
switch (bw_mhz) {
|
||||
case 20:
|
||||
grade_table = rssi_to_grade_20mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_20mhz);
|
||||
break;
|
||||
case 40:
|
||||
grade_table = rssi_to_grade_40mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_40mhz);
|
||||
break;
|
||||
case 80:
|
||||
grade_table = rssi_to_grade_80mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_80mhz);
|
||||
break;
|
||||
case 160:
|
||||
grade_table = rssi_to_grade_160mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_160mhz);
|
||||
break;
|
||||
case 320:
|
||||
grade_table = rssi_to_grade_320mhz;
|
||||
table_size = ARRAY_SIZE(rssi_to_grade_320mhz);
|
||||
break;
|
||||
default:
|
||||
WARN_ONCE(1, "Invalid bandwidth: %d MHz\n", bw_mhz);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Initialize grade to maximum value from selected table */
|
||||
grade = grade_table[table_size - 1].grade;
|
||||
|
||||
rssi_idx = band == NL80211_BAND_2GHZ ? 0 : 1;
|
||||
|
||||
/* No valid RSSI - take the lowest grade */
|
||||
/* No valid RSSI - take the lowest grade from selected table */
|
||||
if (!link_rssi)
|
||||
link_rssi = rssi_to_grade_map[0].rssi[rssi_idx];
|
||||
link_rssi = grade_table[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,
|
||||
"Calculating grade of link %d: band = %d, BW = %d, punct subchannels = 0x%x RSSI = %d\n",
|
||||
link_conf->link_id, band, bw_mhz,
|
||||
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];
|
||||
/* Get grade based on RSSI from the bandwidth-specific table */
|
||||
for (int i = 0; i < table_size; i++) {
|
||||
const struct iwl_mld_rssi_to_grade *line = &grade_table[i];
|
||||
|
||||
if (link_rssi > line->rssi[rssi_idx])
|
||||
continue;
|
||||
|
|
@ -879,8 +1312,10 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
/* 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);
|
||||
grade = grade * iwl_mld_get_avail_chan_load(mld, link_conf) /
|
||||
SCALE_FACTOR;
|
||||
|
||||
iwl_mld_apply_puncturing_penalty(link_conf, &grade, bw_mhz);
|
||||
|
||||
IWL_DEBUG_EHT(mld, "Link %d's grade: %d\n", link_conf->link_id, grade);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_link_h__
|
||||
#define __iwl_mld_link_h__
|
||||
|
|
@ -10,6 +10,14 @@
|
|||
#include "mld.h"
|
||||
#include "sta.h"
|
||||
|
||||
enum iwl_mld_link_chan_load_level {
|
||||
LINK_CHAN_LOAD_LVL_NONE,
|
||||
LINK_CHAN_LOAD_LVL1,
|
||||
LINK_CHAN_LOAD_LVL2,
|
||||
LINK_CHAN_LOAD_LVL3,
|
||||
LINK_CHAN_LOAD_LVL_MAX = LINK_CHAN_LOAD_LVL3
|
||||
};
|
||||
|
||||
/**
|
||||
* struct iwl_probe_resp_data - data for NoA/CSA updates
|
||||
* @rcu_head: used for freeing the data on update
|
||||
|
|
@ -28,14 +36,17 @@ struct iwl_probe_resp_data {
|
|||
* @rcu_head: RCU head for freeing this data.
|
||||
* @fw_id: the fw id of the link.
|
||||
* @active: if the link is active or not.
|
||||
* @avg_signal: The current average signal of beacons [dBm] retrieved from
|
||||
* firmware per-link periodic stats (STATISTICS_OPER_NOTIF).
|
||||
* @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.
|
||||
* @tx_igtk: FW can only have one IGTK per MAC at a time, whereas mac80211 can
|
||||
* have two. This tracks the one IGTK that currently exists in FW, for TX
|
||||
* purposes. The RX IGTKs are tracked per station.
|
||||
* @bigtks: BIGTKs of the AP. Only valid for STA mode.
|
||||
* @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.
|
||||
|
|
@ -49,6 +60,8 @@ struct iwl_probe_resp_data {
|
|||
* @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.
|
||||
* @chan_load_lvl: current channel load level for a link, computed based on
|
||||
* channel load by others on a link.
|
||||
*/
|
||||
struct iwl_mld_link {
|
||||
struct rcu_head rcu_head;
|
||||
|
|
@ -57,11 +70,13 @@ struct iwl_mld_link {
|
|||
struct_group(zeroed_on_hw_restart,
|
||||
u8 fw_id;
|
||||
bool active;
|
||||
s8 avg_signal;
|
||||
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;
|
||||
struct ieee80211_key_conf *tx_igtk;
|
||||
struct ieee80211_key_conf __rcu *bigtks[2];
|
||||
enum iwl_mld_link_chan_load_level chan_load_lvl;
|
||||
);
|
||||
/* And here fields that survive a fw restart */
|
||||
struct iwl_mld_int_sta bcast_sta;
|
||||
|
|
@ -99,6 +114,13 @@ iwl_mld_cleanup_link(struct iwl_mld *mld, struct iwl_mld_link *link)
|
|||
/* Convert a percentage from [0,100] to [0,255] */
|
||||
#define NORMALIZE_PERCENT_TO_255(percentage) ((percentage) * 256 / 100)
|
||||
|
||||
int iwl_mld_allocate_link_fw_id(struct iwl_mld *mld, u8 *fw_id,
|
||||
struct ieee80211_bss_conf *mac80211_ptr);
|
||||
|
||||
int iwl_mld_send_link_cmd(struct iwl_mld *mld,
|
||||
struct iwl_link_config_cmd *cmd,
|
||||
enum iwl_ctxt_action action);
|
||||
|
||||
int iwl_mld_add_link(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *bss_conf);
|
||||
void iwl_mld_remove_link(struct iwl_mld *mld,
|
||||
|
|
@ -123,10 +145,17 @@ unsigned int iwl_mld_get_link_grade(struct iwl_mld *mld,
|
|||
unsigned int iwl_mld_get_chan_load(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf);
|
||||
|
||||
int iwl_mld_get_chan_load_from_element(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);
|
||||
|
||||
bool iwl_mld_chan_load_requires_scan(struct iwl_mld *mld,
|
||||
struct ieee80211_bss_conf *link_conf,
|
||||
u32 new_chan_load);
|
||||
|
||||
void iwl_mld_handle_beacon_filter_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <net/mac80211.h>
|
||||
|
|
@ -62,17 +62,16 @@ static const struct ieee80211_iface_limit iwl_mld_limits_ap[] = {
|
|||
|
||||
static const struct ieee80211_iface_limit iwl_mld_limits_nan[] = {
|
||||
{
|
||||
.max = 2,
|
||||
.max = 1,
|
||||
.types = BIT(NL80211_IFTYPE_STATION),
|
||||
},
|
||||
{
|
||||
.max = 1,
|
||||
.types = BIT(NL80211_IFTYPE_NAN),
|
||||
},
|
||||
/* Removed when two channels are permitted */
|
||||
{
|
||||
.max = 1,
|
||||
.types = BIT(NL80211_IFTYPE_AP),
|
||||
.max = 2,
|
||||
.types = BIT(NL80211_IFTYPE_NAN_DATA),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -90,19 +89,13 @@ iwl_mld_iface_combinations[] = {
|
|||
.limits = iwl_mld_limits_ap,
|
||||
.n_limits = ARRAY_SIZE(iwl_mld_limits_ap),
|
||||
},
|
||||
/* NAN combinations follow, these exclude P2P */
|
||||
/* NAN combination follow, this excludes P2P and AP */
|
||||
{
|
||||
.num_different_channels = 2,
|
||||
.max_interfaces = 3,
|
||||
.limits = iwl_mld_limits_nan,
|
||||
.n_limits = ARRAY_SIZE(iwl_mld_limits_nan) - 1,
|
||||
},
|
||||
{
|
||||
.num_different_channels = 1,
|
||||
.num_different_channels = 3,
|
||||
.max_interfaces = 4,
|
||||
.limits = iwl_mld_limits_nan,
|
||||
.n_limits = ARRAY_SIZE(iwl_mld_limits_nan),
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
static const u8 ext_capa_base[IWL_MLD_STA_EXT_CAPA_SIZE] = {
|
||||
|
|
@ -272,6 +265,40 @@ static void iwl_mac_hw_set_flags(struct iwl_mld *mld)
|
|||
ieee80211_hw_set(hw, TDLS_WIDER_BW);
|
||||
}
|
||||
|
||||
static void iwl_mld_hw_set_nan(struct iwl_mld *mld)
|
||||
{
|
||||
struct ieee80211_hw *hw = mld->hw;
|
||||
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN);
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN_DATA);
|
||||
|
||||
hw->wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ);
|
||||
if (mld->nvm_data->bands[NL80211_BAND_5GHZ].n_channels)
|
||||
hw->wiphy->nan_supported_bands |=
|
||||
BIT(NL80211_BAND_5GHZ);
|
||||
|
||||
hw->wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC |
|
||||
WIPHY_NAN_FLAGS_USERSPACE_DE;
|
||||
|
||||
hw->wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_VHT |
|
||||
NAN_OP_MODE_PHY_MODE_HE |
|
||||
NAN_OP_MODE_160MHZ;
|
||||
|
||||
hw->wiphy->nan_capa.n_antennas =
|
||||
(hweight32(hw->wiphy->available_antennas_tx) &
|
||||
NAN_DEV_CAPA_NUM_TX_ANT_MASK) |
|
||||
((hweight32(hw->wiphy->available_antennas_rx) <<
|
||||
NAN_DEV_CAPA_NUM_RX_ANT_POS) &
|
||||
NAN_DEV_CAPA_NUM_RX_ANT_MASK);
|
||||
|
||||
/* Maximal channel switch time is 4 msec */
|
||||
hw->wiphy->nan_capa.max_channel_switch_time = 4 * USEC_PER_MSEC;
|
||||
|
||||
hw->wiphy->nan_capa.phy.ht = mld->nvm_data->nan_phy_capa.ht;
|
||||
hw->wiphy->nan_capa.phy.vht = mld->nvm_data->nan_phy_capa.vht;
|
||||
hw->wiphy->nan_capa.phy.he = mld->nvm_data->nan_phy_capa.he;
|
||||
}
|
||||
|
||||
static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
|
||||
{
|
||||
struct ieee80211_hw *hw = mld->hw;
|
||||
|
|
@ -334,37 +361,16 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
|
|||
|
||||
wiphy->hw_timestamp_max_peers = 1;
|
||||
|
||||
wiphy->iface_combinations = iwl_mld_iface_combinations;
|
||||
|
||||
if (iwl_mld_nan_supported(mld)) {
|
||||
hw->wiphy->interface_modes |= BIT(NL80211_IFTYPE_NAN);
|
||||
hw->wiphy->iface_combinations = iwl_mld_iface_combinations;
|
||||
hw->wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(iwl_mld_iface_combinations);
|
||||
|
||||
hw->wiphy->nan_supported_bands = BIT(NL80211_BAND_2GHZ);
|
||||
if (mld->nvm_data->bands[NL80211_BAND_5GHZ].n_channels)
|
||||
hw->wiphy->nan_supported_bands |=
|
||||
BIT(NL80211_BAND_5GHZ);
|
||||
|
||||
hw->wiphy->nan_capa.flags = WIPHY_NAN_FLAGS_CONFIGURABLE_SYNC |
|
||||
WIPHY_NAN_FLAGS_USERSPACE_DE;
|
||||
|
||||
hw->wiphy->nan_capa.op_mode = NAN_OP_MODE_PHY_MODE_MASK |
|
||||
NAN_OP_MODE_80P80MHZ |
|
||||
NAN_OP_MODE_160MHZ;
|
||||
|
||||
/* Support 2 antenna's for Tx and Rx */
|
||||
hw->wiphy->nan_capa.n_antennas = 0x22;
|
||||
|
||||
/* Maximal channel switch time is 4 msec */
|
||||
hw->wiphy->nan_capa.max_channel_switch_time = 4;
|
||||
hw->wiphy->nan_capa.dev_capabilities =
|
||||
NAN_DEV_CAPA_EXT_KEY_ID_SUPPORTED |
|
||||
NAN_DEV_CAPA_NDPE_SUPPORTED;
|
||||
} else {
|
||||
wiphy->iface_combinations = iwl_mld_iface_combinations;
|
||||
/* Do not include NAN combinations */
|
||||
wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(iwl_mld_iface_combinations) - 2;
|
||||
ARRAY_SIZE(iwl_mld_iface_combinations);
|
||||
iwl_mld_hw_set_nan(mld);
|
||||
} else {
|
||||
/* Do not include NAN combination */
|
||||
wiphy->n_iface_combinations =
|
||||
ARRAY_SIZE(iwl_mld_iface_combinations) - 1;
|
||||
}
|
||||
|
||||
wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_VHT_IBSS);
|
||||
|
|
@ -414,6 +420,8 @@ static void iwl_mac_hw_set_wiphy(struct iwl_mld *mld)
|
|||
|
||||
mld->ext_capab[0].eml_capabilities = IWL_MLD_EMLSR_CAPA;
|
||||
mld->ext_capab[0].mld_capa_and_ops = IWL_MLD_CAPA_OPS;
|
||||
mld->ext_capab[0].ext_mld_capa_and_ops =
|
||||
IEEE80211_UHR_ML_EXT_MLD_CAPA_ML_PM;
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -449,22 +457,6 @@ static void iwl_mac_hw_set_misc(struct iwl_mld *mld)
|
|||
|
||||
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, LONG_GROUP, TX_CMD, 0) >= 9);
|
||||
|
||||
if (ratecheck != 0 && ratecheck != 4) {
|
||||
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;
|
||||
|
|
@ -673,6 +665,28 @@ int iwl_mld_mac80211_add_interface(struct ieee80211_hw *hw,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN_DATA) {
|
||||
if (WARN_ON(!mld->nan_device_vif)) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (iwl_mld_nan_use_nan_stations(mld)) {
|
||||
struct iwl_mld_vif *mld_vif =
|
||||
iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld_int_sta *sta =
|
||||
&mld_vif->nan.mcast_data_sta;
|
||||
|
||||
ret = iwl_mld_add_nan_mcast_data_sta(mld,
|
||||
vif->addr,
|
||||
sta);
|
||||
if (ret)
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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.
|
||||
|
|
@ -739,10 +753,17 @@ void iwl_mld_mac80211_remove_interface(struct ieee80211_hw *hw,
|
|||
if (vif->type == NL80211_IFTYPE_P2P_DEVICE)
|
||||
mld->p2p_device_vif = NULL;
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
mld->nan_device_vif = NULL;
|
||||
else
|
||||
} else if (vif->type != NL80211_IFTYPE_NAN_DATA) {
|
||||
iwl_mld_remove_link(mld, &vif->bss_conf);
|
||||
} else if (iwl_mld_nan_use_nan_stations(mld)) {
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld_int_sta *sta = &mld_vif->nan.mcast_data_sta;
|
||||
|
||||
if (sta->sta_id != IWL_INVALID_STA)
|
||||
iwl_mld_remove_nan_mcast_data_sta(mld, sta);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
debugfs_remove(iwl_mld_vif_from_mac80211(vif)->dbgfs_slink);
|
||||
|
|
@ -988,6 +1009,30 @@ void iwl_mld_remove_chanctx(struct ieee80211_hw *hw,
|
|||
mld->used_phy_ids &= ~BIT(phy->fw_id);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_update_link_npca_puncturing(struct ieee80211_hw *hw,
|
||||
struct ieee80211_chanctx_conf *ctx)
|
||||
{
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
struct ieee80211_vif *vif;
|
||||
|
||||
for_each_active_interface(vif, hw) {
|
||||
struct ieee80211_bss_conf *link;
|
||||
int link_id;
|
||||
|
||||
for_each_vif_active_link(vif, link, link_id) {
|
||||
if (rcu_access_pointer(link->chanctx_conf) != ctx)
|
||||
continue;
|
||||
|
||||
if (!link->npca.enabled)
|
||||
continue;
|
||||
|
||||
iwl_mld_change_link_in_fw(mld, link,
|
||||
LINK_CONTEXT_MODIFY_UHR_PARAMS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static
|
||||
void iwl_mld_change_chanctx(struct ieee80211_hw *hw,
|
||||
struct ieee80211_chanctx_conf *ctx, u32 changed)
|
||||
|
|
@ -1003,9 +1048,19 @@ void iwl_mld_change_chanctx(struct ieee80211_hw *hw,
|
|||
IEEE80211_CHANCTX_CHANGE_CHANNEL)))
|
||||
return;
|
||||
|
||||
/* NPCA puncturing is in link API for FW */
|
||||
if (changed & IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT) {
|
||||
iwl_mld_update_link_npca_puncturing(hw, ctx);
|
||||
changed &= ~IEEE80211_CHANCTX_CHANGE_NPCA_PUNCT;
|
||||
}
|
||||
|
||||
/* Check if a FW update is required */
|
||||
|
||||
if (changed & IEEE80211_CHANCTX_CHANGE_AP)
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
if (changed & IEEE80211_CHANCTX_CHANGE_AP ||
|
||||
changed & IEEE80211_CHANCTX_CHANGE_NPCA)
|
||||
goto update;
|
||||
|
||||
if (chandef->chan == phy->chandef.chan &&
|
||||
|
|
@ -1231,33 +1286,6 @@ int iwl_mld_mac80211_set_rts_threshold(struct ieee80211_hw *hw, int radio_idx,
|
|||
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,
|
||||
|
|
@ -1289,6 +1317,9 @@ u32 iwl_mld_link_changed_mapping(struct iwl_mld *mld,
|
|||
link_changes |= LINK_CONTEXT_MODIFY_HE_PARAMS;
|
||||
}
|
||||
|
||||
if (changes & BSS_CHANGED_NPCA)
|
||||
link_changes |= LINK_CONTEXT_MODIFY_UHR_PARAMS;
|
||||
|
||||
return link_changes;
|
||||
}
|
||||
|
||||
|
|
@ -1367,6 +1398,10 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw,
|
|||
if (changes & BSS_CHANGED_MU_GROUPS)
|
||||
iwl_mld_update_mu_groups(mld, link_conf);
|
||||
break;
|
||||
case NL80211_IFTYPE_NAN:
|
||||
case NL80211_IFTYPE_NAN_DATA:
|
||||
/* NAN has no links */
|
||||
break;
|
||||
default:
|
||||
/* shouldn't happen */
|
||||
WARN_ON_ONCE(1);
|
||||
|
|
@ -1382,28 +1417,6 @@ iwl_mld_mac80211_link_info_changed(struct ieee80211_hw *hw,
|
|||
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,
|
||||
|
|
@ -1414,6 +1427,11 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw,
|
|||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN) {
|
||||
iwl_mld_nan_vif_cfg_changed(mld, vif, changes);
|
||||
return;
|
||||
}
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_STATION)
|
||||
return;
|
||||
|
||||
|
|
@ -1436,10 +1454,8 @@ void iwl_mld_mac80211_vif_cfg_changed(struct ieee80211_hw *hw,
|
|||
}
|
||||
}
|
||||
|
||||
if (changes & BSS_CHANGED_PS) {
|
||||
iwl_mld_smps_workaround(mld, vif, vif->cfg.ps);
|
||||
if (changes & BSS_CHANGED_PS)
|
||||
iwl_mld_update_mac_power(mld, vif, false);
|
||||
}
|
||||
|
||||
/* TODO: task=MLO BSS_CHANGED_MLD_VALID_LINKS/CHANGED_MLD_TTLM */
|
||||
}
|
||||
|
|
@ -1609,7 +1625,7 @@ iwl_mld_mac80211_conf_tx(struct ieee80211_hw *hw,
|
|||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
if (vif->type == NL80211_IFTYPE_NAN)
|
||||
if (vif->type == NL80211_IFTYPE_NAN || vif->type == NL80211_IFTYPE_NAN_DATA)
|
||||
return 0;
|
||||
|
||||
link = iwl_mld_link_dereference_check(mld_vif, link_id);
|
||||
|
|
@ -1825,7 +1841,7 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
|
|||
new_state == IEEE80211_STA_AUTHORIZED) {
|
||||
ret = 0;
|
||||
|
||||
if (!sta->tdls) {
|
||||
if (vif->type == NL80211_IFTYPE_STATION && !sta->tdls) {
|
||||
mld_vif->authorized = true;
|
||||
|
||||
/* Ensure any block due to a non-BSS link is synced */
|
||||
|
|
@ -1846,7 +1862,6 @@ static int iwl_mld_move_sta_state_up(struct iwl_mld *mld,
|
|||
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.
|
||||
|
|
@ -1891,7 +1906,6 @@ static int iwl_mld_move_sta_state_down(struct iwl_mld *mld,
|
|||
&mld_vif->mlo_scan_start_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
|
||||
|
|
@ -2236,7 +2250,6 @@ static int iwl_mld_set_key_add(struct iwl_mld *mld,
|
|||
|
||||
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);
|
||||
|
|
@ -2278,7 +2291,9 @@ static void iwl_mld_set_key_remove(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
/* if this key was stored to be added later to the FW - free it here */
|
||||
if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE))
|
||||
if (!(key->flags & IEEE80211_KEY_FLAG_PAIRWISE) &&
|
||||
(vif->type == NL80211_IFTYPE_AP ||
|
||||
vif->type == NL80211_IFTYPE_ADHOC))
|
||||
iwl_mld_free_ap_early_key(mld, key, mld_vif);
|
||||
|
||||
/* We already removed it */
|
||||
|
|
@ -2296,9 +2311,20 @@ static int iwl_mld_mac80211_set_key(struct ieee80211_hw *hw,
|
|||
struct ieee80211_sta *sta,
|
||||
struct ieee80211_key_conf *key)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* FW always needs the AP STA for client mode.
|
||||
* Note that during removal this could already
|
||||
* be NULL (mac80211 removes keys after STAs)
|
||||
* but then we'll already have removed the key
|
||||
* and set hw_key_idx = STA_KEY_IDX_INVALID.
|
||||
*/
|
||||
if (!sta && vif->type == NL80211_IFTYPE_STATION)
|
||||
sta = mld_vif->ap_sta;
|
||||
|
||||
switch (cmd) {
|
||||
case SET_KEY:
|
||||
ret = iwl_mld_set_key_add(mld, vif, sta, key);
|
||||
|
|
@ -2559,15 +2585,108 @@ iwl_mld_mac80211_mgd_protect_tdls_discover(struct ieee80211_hw *hw,
|
|||
ret);
|
||||
}
|
||||
|
||||
static int iwl_mld_count_free_link_ids(struct iwl_mld *mld)
|
||||
{
|
||||
int free_count = 0;
|
||||
|
||||
for (int i = 0; i < mld->fw->ucode_capa.num_links; i++) {
|
||||
if (!rcu_access_pointer(mld->fw_id_to_bss_conf[i]))
|
||||
free_count++;
|
||||
}
|
||||
|
||||
return free_count;
|
||||
}
|
||||
|
||||
static bool
|
||||
iwl_mld_chanctx_used_by_other_vif(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_chanctx_conf *chanctx_conf)
|
||||
{
|
||||
struct ieee80211_bss_conf *iter_link;
|
||||
struct ieee80211_vif *iter_vif;
|
||||
int link_id;
|
||||
|
||||
for_each_active_interface(iter_vif, hw) {
|
||||
if (vif == iter_vif)
|
||||
continue;
|
||||
|
||||
/* NAN doesn't have active links, so we don't count NAN users */
|
||||
for_each_vif_active_link(iter_vif, iter_link, link_id) {
|
||||
if (rcu_access_pointer(iter_link->chanctx_conf) ==
|
||||
chanctx_conf)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
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);
|
||||
int n_add = hweight16(desired_links & ~vif->active_links);
|
||||
unsigned long to_deactivate = vif->active_links & ~desired_links;
|
||||
int free_link_ids;
|
||||
int i;
|
||||
|
||||
/* Check if HW supports the wanted number of links */
|
||||
return n_links <= iwl_mld_max_active_links(mld, vif);
|
||||
if (n_links > iwl_mld_max_active_links(mld, vif))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* During link switch, mac80211 first adds the new links, then removes
|
||||
* the old ones. This means we temporarily need extra link objects
|
||||
* during the transition. Check if we have enough free link IDs.
|
||||
*/
|
||||
|
||||
free_link_ids = iwl_mld_count_free_link_ids(mld);
|
||||
|
||||
if (free_link_ids >= n_add)
|
||||
return true;
|
||||
|
||||
if (!mld->nan_device_vif)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* Not enough free link IDs. First try to evacuate NAN from the
|
||||
* channel context of a link that is going to be deactivated.
|
||||
*/
|
||||
for_each_set_bit(i, &to_deactivate, IEEE80211_MLD_MAX_NUM_LINKS) {
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
struct ieee80211_chanctx_conf *chanctx_conf;
|
||||
|
||||
link_conf = link_conf_dereference_protected(vif, i);
|
||||
if (!link_conf)
|
||||
continue;
|
||||
|
||||
chanctx_conf = wiphy_dereference(mld->wiphy, link_conf->chanctx_conf);
|
||||
if (!chanctx_conf)
|
||||
continue;
|
||||
|
||||
if (iwl_mld_chanctx_used_by_other_vif(hw, vif, chanctx_conf))
|
||||
continue;
|
||||
|
||||
if (ieee80211_nan_try_evacuate(hw, chanctx_conf)) {
|
||||
free_link_ids = iwl_mld_count_free_link_ids(mld);
|
||||
/*
|
||||
* Evacuation of one channel should do the job. If not,
|
||||
* something bad is happening. Don't try to evacuate more
|
||||
*/
|
||||
return free_link_ids >= n_add;
|
||||
}
|
||||
}
|
||||
|
||||
/* Couldn't find/evacuate any channel going to go unused, try any */
|
||||
if (ieee80211_nan_try_evacuate(hw, NULL)) {
|
||||
free_link_ids = iwl_mld_count_free_link_ids(mld);
|
||||
if (free_link_ids >= n_add)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static int
|
||||
|
|
@ -2847,4 +2966,5 @@ const struct ieee80211_ops iwl_mld_hw_ops = {
|
|||
.start_nan = iwl_mld_start_nan,
|
||||
.stop_nan = iwl_mld_stop_nan,
|
||||
.nan_change_conf = iwl_mld_nan_change_config,
|
||||
.nan_peer_sched_changed = iwl_mld_mac802111_nan_peer_sched_changed,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <net/cfg80211.h>
|
||||
|
|
@ -129,7 +129,7 @@ iwl_mld_get_regdomain(struct iwl_mld *mld,
|
|||
|
||||
mld->mcc_src = resp->source_id;
|
||||
|
||||
/* FM is the earliest supported and later always do puncturing */
|
||||
/* FM follows BIOS/MCC policy, WH disallows puncturing only in US/CA. */
|
||||
if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) == IWL_CFG_RF_TYPE_FM) {
|
||||
if (!iwl_puncturing_is_allowed_in_bios(mld->bios_enable_puncturing,
|
||||
le16_to_cpu(resp->mcc)))
|
||||
|
|
@ -137,6 +137,15 @@ iwl_mld_get_regdomain(struct iwl_mld *mld,
|
|||
else
|
||||
__clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING,
|
||||
mld->hw->flags);
|
||||
} else if (CSR_HW_RFID_TYPE(mld->trans->info.hw_rf_id) ==
|
||||
IWL_CFG_RF_TYPE_WH) {
|
||||
u16 mcc = le16_to_cpu(resp->mcc);
|
||||
|
||||
if (mcc == IWL_MCC_US || mcc == IWL_MCC_CANADA)
|
||||
ieee80211_hw_set(mld->hw, DISALLOW_PUNCTURING);
|
||||
else
|
||||
__clear_bit(IEEE80211_HW_DISALLOW_PUNCTURING,
|
||||
mld->hw->flags);
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <net/mac80211.h>
|
||||
|
|
@ -162,7 +162,6 @@ static const struct iwl_hcmd_names iwl_mld_legacy_names[] = {
|
|||
HCMD_NAME(PHY_CONFIGURATION_CMD),
|
||||
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),
|
||||
|
|
@ -236,6 +235,10 @@ static const struct iwl_hcmd_names iwl_mld_mac_conf_names[] = {
|
|||
HCMD_NAME(STA_REMOVE_CMD),
|
||||
HCMD_NAME(ROC_CMD),
|
||||
HCMD_NAME(NAN_CFG_CMD),
|
||||
HCMD_NAME(NAN_SCHEDULE_CMD),
|
||||
HCMD_NAME(NAN_PEER_CMD),
|
||||
HCMD_NAME(NAN_ULW_ATTR_NOTIF),
|
||||
HCMD_NAME(NAN_SCHED_UPDATE_COMPLETED_NOTIF),
|
||||
HCMD_NAME(NAN_DW_END_NOTIF),
|
||||
HCMD_NAME(NAN_JOINED_CLUSTER_NOTIF),
|
||||
HCMD_NAME(MISSED_BEACONS_NOTIF),
|
||||
|
|
@ -259,6 +262,7 @@ static const struct iwl_hcmd_names iwl_mld_data_path_names[] = {
|
|||
HCMD_NAME(RX_BAID_ALLOCATION_CONFIG_CMD),
|
||||
HCMD_NAME(SCD_QUEUE_CONFIG_CMD),
|
||||
HCMD_NAME(SEC_KEY_CMD),
|
||||
HCMD_NAME(RSC_NOTIF),
|
||||
HCMD_NAME(ESR_MODE_NOTIF),
|
||||
HCMD_NAME(MONITOR_NOTIF),
|
||||
HCMD_NAME(TLC_MNG_UPDATE_NOTIF),
|
||||
|
|
@ -411,9 +415,6 @@ iwl_op_mode_mld_start(struct iwl_trans *trans, const struct iwl_rf_cfg *cfg,
|
|||
|
||||
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);
|
||||
|
|
@ -675,6 +676,15 @@ iwl_mld_nic_error(struct iwl_op_mode *op_mode,
|
|||
if (type != IWL_ERR_TYPE_RESET_HS_TIMEOUT &&
|
||||
mld->fw_status.running)
|
||||
mld->fw_status.in_hw_restart = true;
|
||||
|
||||
/* FW is dead. We don't want to process its notifications.
|
||||
* Right, we cancel them also in iwl_mld_stop_fw, but
|
||||
* iwl_mld_async_handlers_wk might be executed before
|
||||
* ieee80211_restart_work.
|
||||
* In addition, in case of an error during recovery,
|
||||
* iwl_mld_stop_fw might be too late.
|
||||
*/
|
||||
iwl_mld_cancel_async_notifications(mld);
|
||||
}
|
||||
|
||||
static void iwl_mld_dump_error(struct iwl_op_mode *op_mode,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_h__
|
||||
#define __iwl_mld_h__
|
||||
|
|
@ -189,7 +189,6 @@
|
|||
* TX rate_n_flags for non-STA mgmt frames (toggles on every TX failure).
|
||||
* @set_tx_ant: stores the last TX antenna bitmask set by user space (if any)
|
||||
* @set_rx_ant: stores the last RX antenna bitmask set by user space (if any)
|
||||
* @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
|
||||
|
|
@ -299,8 +298,6 @@ struct iwl_mld {
|
|||
u8 set_tx_ant;
|
||||
u8 set_rx_ant;
|
||||
|
||||
bool fw_rates_ver_3;
|
||||
|
||||
struct iwl_mld_low_latency low_latency;
|
||||
|
||||
bool ibss_manager;
|
||||
|
|
@ -555,15 +552,24 @@ iwl_mld_allocate_##_type##_fw_id(struct iwl_mld *mld, \
|
|||
return -ENOSPC; \
|
||||
}
|
||||
|
||||
#define IWL_MLD_ALLOC_FN_STATIC(_type, _mac80211_type) \
|
||||
static IWL_MLD_ALLOC_FN(_type, _mac80211_type)
|
||||
|
||||
static inline struct ieee80211_bss_conf *
|
||||
iwl_mld_fw_id_to_link_conf(struct iwl_mld *mld, u8 fw_link_id)
|
||||
{
|
||||
struct ieee80211_bss_conf *link;
|
||||
|
||||
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,
|
||||
link = wiphy_dereference(mld->wiphy,
|
||||
mld->fw_id_to_bss_conf[fw_link_id]);
|
||||
if (IS_ERR(link))
|
||||
return NULL;
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
#define MSEC_TO_TU(_msec) ((_msec) * 1000 / 1024)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include "mlo.h"
|
||||
#include "phy.h"
|
||||
|
|
@ -1081,8 +1081,13 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
|
|||
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;
|
||||
u32 new_chan_load = phy->avg_channel_load_not_by_us;
|
||||
struct ieee80211_bss_conf *prim_link, *link_conf;
|
||||
unsigned int prim_link_id;
|
||||
int link_id;
|
||||
|
||||
if (!ieee80211_vif_is_mld(vif) || hweight16(vif->valid_links) <= 1)
|
||||
return;
|
||||
|
||||
prim_link_id = iwl_mld_get_primary_link(vif);
|
||||
prim_link = link_conf_dereference_protected(vif, prim_link_id);
|
||||
|
|
@ -1090,6 +1095,32 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
|
|||
if (WARN_ON(!prim_link))
|
||||
return;
|
||||
|
||||
/* Evaluate MLO Internal Scan for high chan load beyond thresholds */
|
||||
for_each_vif_active_link(vif, link_conf, link_id) {
|
||||
if (rcu_access_pointer(link_conf->chanctx_conf) != chanctx)
|
||||
continue;
|
||||
|
||||
/* No QBSS IE - links will be selected based on default channel
|
||||
* load values, so the same link will be selected again.
|
||||
* No point in scan.
|
||||
*/
|
||||
if (iwl_mld_get_chan_load_from_element(mld, link_conf) < 0)
|
||||
continue;
|
||||
|
||||
if (iwl_mld_chan_load_requires_scan(mld,
|
||||
link_conf,
|
||||
new_chan_load)) {
|
||||
/* When EMLSR is active, only trigger scan based on
|
||||
* primary link
|
||||
*/
|
||||
if (iwl_mld_emlsr_active(vif) && link_conf != prim_link)
|
||||
continue;
|
||||
|
||||
iwl_mld_int_mlo_scan(mld, vif);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (chanctx != rcu_access_pointer(prim_link->chanctx_conf))
|
||||
return;
|
||||
|
||||
|
|
@ -1107,7 +1138,6 @@ static void iwl_mld_chan_load_update_iter(void *_data, u8 *mac,
|
|||
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) \
|
||||
|
|
|
|||
|
|
@ -5,8 +5,12 @@
|
|||
|
||||
#include "mld.h"
|
||||
#include "iface.h"
|
||||
#include "link.h"
|
||||
#include "mlo.h"
|
||||
#include "tlc.h"
|
||||
#include "fw/api/mac-cfg.h"
|
||||
#include "fw/api/mac.h"
|
||||
#include "fw/api/rs.h"
|
||||
|
||||
#define IWL_NAN_DISOVERY_BEACON_INTERNVAL_TU 512
|
||||
#define IWL_NAN_RSSI_CLOSE 55
|
||||
|
|
@ -14,8 +18,16 @@
|
|||
|
||||
bool iwl_mld_nan_supported(struct iwl_mld *mld)
|
||||
{
|
||||
return fw_has_capa(&mld->fw->ucode_capa,
|
||||
IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT);
|
||||
const struct iwl_fw *fw = mld->fw;
|
||||
|
||||
if (fw_has_capa(&fw->ucode_capa, IWL_UCODE_TLV_CAPA_NAN_SYNC_SUPPORT) &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, NAN_SCHEDULE_CMD), 0) >= 1 &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, NAN_PEER_CMD), 0) >= 1 &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, STA_CONFIG_CMD), 0) >= 3 &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(MAC_CONF_GROUP, MAC_CONFIG_CMD), 0) >= 4 &&
|
||||
iwl_fw_lookup_cmd_ver(fw, WIDE_ID(DATA_PATH_GROUP, TLC_MNG_CONFIG_CMD), 0) >= 6)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld,
|
||||
|
|
@ -38,6 +50,98 @@ static int iwl_mld_nan_send_config_cmd(struct iwl_mld *mld,
|
|||
return iwl_mld_send_cmd(mld, &hcmd);
|
||||
}
|
||||
|
||||
bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld)
|
||||
{
|
||||
/*
|
||||
* If the FW supports version 1 of the NAN config command, it means that
|
||||
* it needs to receive the station ID of the auxiliary station in the
|
||||
* NAN configuration command. Otherwise, use the NAN dedicated station
|
||||
* types.
|
||||
*/
|
||||
return iwl_fw_lookup_cmd_ver(mld->fw,
|
||||
WIDE_ID(MAC_CONF_GROUP,
|
||||
NAN_CFG_CMD), 1) != 1;
|
||||
}
|
||||
|
||||
static const struct iwl_mld_int_sta *
|
||||
iwl_mld_nan_get_mgmt_sta(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
const struct iwl_mld_int_sta *sta;
|
||||
|
||||
if (iwl_mld_nan_use_nan_stations(mld))
|
||||
sta = &mld_vif->nan.mgmt_sta;
|
||||
else
|
||||
sta = &mld_vif->aux_sta;
|
||||
|
||||
if (WARN_ON(sta->sta_id == IWL_INVALID_STA))
|
||||
return NULL;
|
||||
|
||||
return sta;
|
||||
}
|
||||
|
||||
int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif);
|
||||
|
||||
if (!sta)
|
||||
return IWL_MLD_INVALID_QUEUE;
|
||||
|
||||
return sta->queue_id;
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_flush(struct iwl_mld *mld, struct ieee80211_vif *vif)
|
||||
{
|
||||
const struct iwl_mld_int_sta *sta = iwl_mld_nan_get_mgmt_sta(mld, vif);
|
||||
|
||||
if (!sta)
|
||||
return;
|
||||
|
||||
if (WARN_ON(sta->queue_id == IWL_MLD_INVALID_QUEUE))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_INFO(mld, "NAN: flush queues for sta=%u\n",
|
||||
sta->sta_id);
|
||||
|
||||
iwl_mld_flush_link_sta_txqs(mld, sta->sta_id);
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_remove_stations(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
iwl_mld_nan_flush(mld, vif);
|
||||
|
||||
if (!iwl_mld_nan_use_nan_stations(mld)) {
|
||||
iwl_mld_remove_aux_sta(mld, vif);
|
||||
return;
|
||||
}
|
||||
|
||||
iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
|
||||
iwl_mld_remove_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta);
|
||||
}
|
||||
|
||||
static int iwl_mld_nan_add_stations(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
int ret;
|
||||
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
return iwl_mld_add_aux_sta(mld, &mld_vif->aux_sta);
|
||||
|
||||
ret = iwl_mld_add_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iwl_mld_add_nan_mgmt_sta(mld, &mld_vif->nan.mgmt_sta);
|
||||
if (ret)
|
||||
iwl_mld_remove_nan_bcast_sta(mld, &mld_vif->nan.bcast_sta);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int iwl_mld_nan_config(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct cfg80211_nan_conf *conf,
|
||||
|
|
@ -114,7 +218,12 @@ static int iwl_mld_nan_config(struct iwl_mld *mld,
|
|||
conf->vendor_elems_len);
|
||||
}
|
||||
|
||||
cmd.sta_id = mld_vif->aux_sta.sta_id;
|
||||
/* FW needs to know about the station ID only with version 1 of the
|
||||
* NAN configuration command
|
||||
*/
|
||||
if (!iwl_mld_nan_use_nan_stations(mld))
|
||||
cmd.sta_id = mld_vif->aux_sta.sta_id;
|
||||
|
||||
return iwl_mld_nan_send_config_cmd(mld, &cmd, data,
|
||||
conf->extra_nan_attrs_len +
|
||||
conf->vendor_elems_len);
|
||||
|
|
@ -124,8 +233,6 @@ int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|||
struct cfg80211_nan_conf *conf)
|
||||
{
|
||||
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;
|
||||
int ret;
|
||||
|
||||
IWL_DEBUG_MAC80211(mld, "NAN: start: bands=0x%x\n", conf->bands);
|
||||
|
|
@ -134,19 +241,20 @@ int iwl_mld_start_nan(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = iwl_mld_add_aux_sta(mld, aux_sta);
|
||||
ret = iwl_mld_nan_add_stations(mld, vif);
|
||||
if (ret)
|
||||
goto unblock_emlsr;
|
||||
|
||||
ret = iwl_mld_nan_config(mld, vif, conf, FW_CTXT_ACTION_ADD);
|
||||
if (ret) {
|
||||
IWL_ERR(mld, "Failed to start NAN. ret=%d\n", ret);
|
||||
goto remove_aux;
|
||||
goto remove_stas;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
remove_aux:
|
||||
iwl_mld_remove_aux_sta(mld, vif);
|
||||
remove_stas:
|
||||
iwl_mld_nan_remove_stations(mld, vif);
|
||||
unblock_emlsr:
|
||||
iwl_mld_update_emlsr_block(mld, false, IWL_MLD_EMLSR_BLOCKED_NAN);
|
||||
|
||||
|
|
@ -174,7 +282,6 @@ int iwl_mld_stop_nan(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_nan_config_cmd cmd = {
|
||||
.action = cpu_to_le32(FW_CTXT_ACTION_REMOVE),
|
||||
};
|
||||
|
|
@ -191,8 +298,7 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw,
|
|||
/* assume that higher layer guarantees that no additional frames are
|
||||
* added before calling this callback
|
||||
*/
|
||||
iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
|
||||
iwl_mld_remove_aux_sta(mld, vif);
|
||||
iwl_mld_nan_remove_stations(mld, vif);
|
||||
|
||||
/* cancel based on object type being NAN, as the NAN objects do
|
||||
* not have a unique identifier associated with them
|
||||
|
|
@ -247,6 +353,39 @@ bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
|
|||
return true;
|
||||
}
|
||||
|
||||
bool iwl_mld_cancel_nan_ulw_attr_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void iwl_mld_handle_nan_ulw_attr_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
struct iwl_nan_ulw_attr_notif *notif = (void *)pkt->data;
|
||||
struct wireless_dev *wdev;
|
||||
|
||||
IWL_DEBUG_INFO(mld, "NAN: ULW attr update: len=%u\n", notif->attr_len);
|
||||
|
||||
if (IWL_FW_CHECK(mld, !mld->nan_device_vif,
|
||||
"NAN: ULW attr update without NAN vif\n"))
|
||||
return;
|
||||
|
||||
if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(mld->nan_device_vif),
|
||||
"NAN: ULW attr update without NAN started\n"))
|
||||
return;
|
||||
|
||||
if (IWL_FW_CHECK(mld,
|
||||
notif->attr_len > IWL_NAN_MAX_ENDLESS_ULW_ATTR_LEN,
|
||||
"NAN: ULW attr update invalid len %u\n",
|
||||
notif->attr_len))
|
||||
return;
|
||||
|
||||
wdev = ieee80211_vif_to_wdev(mld->nan_device_vif);
|
||||
cfg80211_nan_ulw_update(wdev, notif->attr, notif->attr_len, GFP_KERNEL);
|
||||
}
|
||||
|
||||
void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
|
|
@ -257,7 +396,7 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
|||
struct wireless_dev *wdev;
|
||||
struct ieee80211_channel *chan;
|
||||
|
||||
IWL_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
|
||||
IWL_DEBUG_INFO(mld, "NAN: DW end: band=%u\n", notif->band);
|
||||
|
||||
if (IWL_FW_CHECK(mld, !mld_vif, "NAN: DW end without mld_vif\n"))
|
||||
return;
|
||||
|
|
@ -266,13 +405,7 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
|||
"NAN: DW end without NAN started\n"))
|
||||
return;
|
||||
|
||||
if (WARN_ON(mld_vif->aux_sta.sta_id == IWL_INVALID_STA))
|
||||
return;
|
||||
|
||||
IWL_DEBUG_INFO(mld, "NAN: flush queues for aux sta=%u\n",
|
||||
mld_vif->aux_sta.sta_id);
|
||||
|
||||
iwl_mld_flush_link_sta_txqs(mld, mld_vif->aux_sta.sta_id);
|
||||
iwl_mld_nan_flush(mld, mld->nan_device_vif);
|
||||
|
||||
/* TODO: currently the notification specified the band on which the DW
|
||||
* ended. Need to change that to the actual channel on which the next DW
|
||||
|
|
@ -293,6 +426,581 @@ void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
|||
return;
|
||||
}
|
||||
|
||||
if (WARN_ON_ONCE(!chan))
|
||||
return;
|
||||
|
||||
wdev = ieee80211_vif_to_wdev(mld->nan_device_vif);
|
||||
cfg80211_next_nan_dw_notif(wdev, chan, GFP_KERNEL);
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_fill_rates(struct iwl_link_config_cmd *cmd)
|
||||
{
|
||||
u32 ofdm = 0;
|
||||
|
||||
/* All OFDM rates - NAN uses OFDM only, no CCK */
|
||||
ofdm |= IWL_RATE_BIT_MSK(6) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(9) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(12) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(18) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(24) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(36) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(48) >> IWL_FIRST_OFDM_RATE;
|
||||
ofdm |= IWL_RATE_BIT_MSK(54) >> IWL_FIRST_OFDM_RATE;
|
||||
|
||||
cmd->ofdm_rates = cpu_to_le32(ofdm);
|
||||
cmd->short_slot = cpu_to_le32(1);
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_fill_qos(struct iwl_ac_qos *ac, __le32 *qos_flags)
|
||||
{
|
||||
/* AC_BK: CWmin=15, CWmax=1023, AIFSN=7, TXOP=0 */
|
||||
ac[AC_BK].cw_min = cpu_to_le16(15);
|
||||
ac[AC_BK].cw_max = cpu_to_le16(1023);
|
||||
ac[AC_BK].aifsn = 7;
|
||||
ac[AC_BK].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BK);
|
||||
ac[AC_BK].edca_txop = 0;
|
||||
|
||||
/* AC_BE: CWmin=15, CWmax=1023, AIFSN=3, TXOP=0 */
|
||||
ac[AC_BE].cw_min = cpu_to_le16(15);
|
||||
ac[AC_BE].cw_max = cpu_to_le16(1023);
|
||||
ac[AC_BE].aifsn = 3;
|
||||
ac[AC_BE].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_BE);
|
||||
ac[AC_BE].edca_txop = 0;
|
||||
|
||||
/* AC_VI: CWmin=7, CWmax=15, AIFSN=2, TXOP=3008us */
|
||||
ac[AC_VI].cw_min = cpu_to_le16(7);
|
||||
ac[AC_VI].cw_max = cpu_to_le16(15);
|
||||
ac[AC_VI].aifsn = 2;
|
||||
ac[AC_VI].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VI);
|
||||
ac[AC_VI].edca_txop = cpu_to_le16(3008);
|
||||
|
||||
/* AC_VO: CWmin=3, CWmax=7, AIFSN=2, TXOP=1504us */
|
||||
ac[AC_VO].cw_min = cpu_to_le16(3);
|
||||
ac[AC_VO].cw_max = cpu_to_le16(7);
|
||||
ac[AC_VO].aifsn = 2;
|
||||
ac[AC_VO].fifos_mask = BIT(IWL_BZ_EDCA_TX_FIFO_VO);
|
||||
ac[AC_VO].edca_txop = cpu_to_le16(1504);
|
||||
|
||||
*qos_flags |= cpu_to_le32(MAC_QOS_FLG_UPDATE_EDCA);
|
||||
}
|
||||
|
||||
static void
|
||||
iwl_mld_nan_link_prep_cmd(struct iwl_mld *mld,
|
||||
struct iwl_mld_nan_link *nan_link,
|
||||
struct iwl_link_config_cmd *cmd,
|
||||
u32 modify_flags)
|
||||
{
|
||||
struct ieee80211_vif *vif = mld->nan_device_vif;
|
||||
struct iwl_mld_vif *mld_vif;
|
||||
|
||||
if (WARN_ON_ONCE(!vif))
|
||||
return;
|
||||
|
||||
mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
memset(cmd, 0, sizeof(*cmd));
|
||||
|
||||
if (!nan_link->chanctx) {
|
||||
cmd->phy_id = cpu_to_le32(FW_CTXT_ID_INVALID);
|
||||
} else {
|
||||
struct iwl_mld_phy *mld_phy;
|
||||
|
||||
mld_phy = iwl_mld_phy_from_mac80211(nan_link->chanctx);
|
||||
cmd->phy_id = cpu_to_le32(mld_phy->fw_id);
|
||||
}
|
||||
|
||||
if (modify_flags & LINK_CONTEXT_MODIFY_RATES_INFO)
|
||||
iwl_mld_nan_fill_rates(cmd);
|
||||
|
||||
if (modify_flags & LINK_CONTEXT_MODIFY_QOS_PARAMS)
|
||||
iwl_mld_nan_fill_qos(cmd->ac, &cmd->qos_flags);
|
||||
|
||||
cmd->link_id = cpu_to_le32(nan_link->fw_id);
|
||||
cmd->mac_id = cpu_to_le32(mld_vif->fw_id);
|
||||
cmd->active = cpu_to_le32(nan_link->active);
|
||||
|
||||
ether_addr_copy(cmd->local_link_addr, vif->addr);
|
||||
|
||||
cmd->modify_mask = cpu_to_le32(modify_flags);
|
||||
}
|
||||
|
||||
static struct iwl_mld_nan_link *
|
||||
iwl_mld_nan_link_add(struct iwl_mld *mld,
|
||||
struct iwl_mld_vif *mld_vif,
|
||||
struct ieee80211_chanctx_conf *chanctx)
|
||||
{
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
struct iwl_link_config_cmd cmd;
|
||||
u8 fw_id;
|
||||
int ret;
|
||||
|
||||
lockdep_assert_wiphy(mld->wiphy);
|
||||
|
||||
ret = iwl_mld_allocate_link_fw_id(mld, &fw_id, ERR_PTR(-ENODEV));
|
||||
/*
|
||||
* We should always have enough links. The schedule contains up to 3,
|
||||
* and the BSS vif cannot do EMLSR - so can only have 1.
|
||||
*/
|
||||
if (WARN_ON(ret < 0))
|
||||
return NULL;
|
||||
|
||||
nan_link = &mld_vif->nan.links[fw_id];
|
||||
|
||||
if (WARN_ON_ONCE(nan_link->fw_id != FW_CTXT_ID_INVALID))
|
||||
goto err;
|
||||
|
||||
nan_link->fw_id = fw_id;
|
||||
nan_link->chanctx = chanctx;
|
||||
|
||||
iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
|
||||
LINK_CONTEXT_MODIFY_RATES_INFO |
|
||||
LINK_CONTEXT_MODIFY_QOS_PARAMS);
|
||||
|
||||
ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_ADD);
|
||||
if (ret) {
|
||||
nan_link->fw_id = FW_CTXT_ID_INVALID;
|
||||
nan_link->chanctx = NULL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
return nan_link;
|
||||
err:
|
||||
RCU_INIT_POINTER(mld->fw_id_to_bss_conf[fw_id], NULL);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int iwl_mld_nan_link_set_active(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mld_nan_link *nan_link,
|
||||
bool active)
|
||||
{
|
||||
struct iwl_link_config_cmd cmd;
|
||||
struct ieee80211_sta *sta;
|
||||
int ret;
|
||||
|
||||
if (nan_link->active == active)
|
||||
return 0;
|
||||
|
||||
if (active) {
|
||||
for_each_station(sta, mld->hw) {
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
|
||||
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI)
|
||||
iwl_mld_config_tlc(mld, mld_sta->vif, sta);
|
||||
}
|
||||
}
|
||||
|
||||
nan_link->active = active;
|
||||
|
||||
iwl_mld_nan_link_prep_cmd(mld, nan_link, &cmd,
|
||||
LINK_CONTEXT_MODIFY_ACTIVE);
|
||||
|
||||
ret = iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_MODIFY);
|
||||
if (ret) {
|
||||
nan_link->active = !nan_link->active;
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!active) {
|
||||
nan_link->chanctx = NULL;
|
||||
/* TODO: when FW is ready, Update phy in TLC to invalid after */
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_link_remove(struct iwl_mld *mld,
|
||||
struct iwl_mld_nan_link *nan_link,
|
||||
u32 link_id)
|
||||
{
|
||||
struct iwl_link_config_cmd cmd = {
|
||||
.link_id = cpu_to_le32(link_id),
|
||||
.phy_id = cpu_to_le32(FW_CTXT_ID_INVALID),
|
||||
};
|
||||
|
||||
iwl_mld_send_link_cmd(mld, &cmd, FW_CTXT_ACTION_REMOVE);
|
||||
|
||||
RCU_INIT_POINTER(mld->fw_id_to_bss_conf[link_id], NULL);
|
||||
nan_link->fw_id = FW_CTXT_ID_INVALID;
|
||||
nan_link->active = false;
|
||||
nan_link->chanctx = NULL;
|
||||
}
|
||||
|
||||
static bool iwl_mld_nan_have_links(struct iwl_mld_vif *mld_vif)
|
||||
{
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
|
||||
for_each_mld_nan_valid_link(mld_vif, nan_link)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static struct iwl_mld_nan_link *
|
||||
iwl_mld_nan_find_link(struct iwl_mld_vif *mld_vif,
|
||||
struct ieee80211_chanctx_conf *chanctx)
|
||||
{
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
|
||||
for_each_mld_nan_valid_link(mld_vif, nan_link) {
|
||||
if (nan_link->chanctx == chanctx)
|
||||
return nan_link;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void iwl_mld_nan_set_mcast_data_links(struct ieee80211_vif *vif)
|
||||
{
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
|
||||
if (vif->type != NL80211_IFTYPE_NAN_DATA)
|
||||
return;
|
||||
|
||||
/* Note that all errors are handled internally so nothing to do
|
||||
* with the return value (used only to silence compilation warnings)
|
||||
*/
|
||||
iwl_mld_update_nan_mcast_data_sta(mld_vif->mld, vif->addr,
|
||||
&mld_vif->nan.mcast_data_sta);
|
||||
}
|
||||
|
||||
void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
u64 changes)
|
||||
{
|
||||
struct iwl_nan_schedule_cmd cmd = {};
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(vif);
|
||||
bool previously_empty_schedule = !iwl_mld_nan_have_links(mld_vif);
|
||||
struct ieee80211_nan_sched_cfg *sched_cfg = &vif->cfg.nan_sched;
|
||||
struct iwl_mld_nan_link *links[ARRAY_SIZE(sched_cfg->channels)] = {};
|
||||
struct ieee80211_nan_channel **slots = sched_cfg->schedule;
|
||||
bool link_used[ARRAY_SIZE(mld_vif->nan.links)] = {};
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
unsigned long remove_link_ids = 0;
|
||||
bool added_links = false;
|
||||
bool empty_schedule = true;
|
||||
int ret, i;
|
||||
u16 cmd_size;
|
||||
u32 cmd_id = WIDE_ID(MAC_CONF_GROUP, NAN_SCHEDULE_CMD);
|
||||
u8 version = iwl_fw_lookup_cmd_ver(mld->fw, cmd_id, 0);
|
||||
|
||||
if (!(changes & BSS_CHANGED_NAN_LOCAL_SCHED))
|
||||
return;
|
||||
|
||||
switch (version) {
|
||||
case 1:
|
||||
if (sched_cfg->deferred) {
|
||||
IWL_ERR(mld,
|
||||
"NAN: deferred schedule not supported by FW\n");
|
||||
return;
|
||||
}
|
||||
|
||||
cmd_size = sizeof(struct iwl_nan_schedule_cmd_v1);
|
||||
break;
|
||||
case 2:
|
||||
cmd_size = sizeof(struct iwl_nan_schedule_cmd);
|
||||
|
||||
if (sched_cfg->deferred)
|
||||
cmd.deferred = 1;
|
||||
|
||||
if (sched_cfg->avail_blob_len &&
|
||||
!WARN_ON(sched_cfg->avail_blob_len >
|
||||
sizeof(cmd.avail_attr.attr))) {
|
||||
cmd.avail_attr.attr_len = sched_cfg->avail_blob_len;
|
||||
memcpy(cmd.avail_attr.attr, sched_cfg->avail_blob,
|
||||
sched_cfg->avail_blob_len);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
IWL_ERR(mld, "NAN: unsupported NAN schedule cmd version %d\n",
|
||||
version);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
|
||||
if (!sched_cfg->channels[i].chanreq.oper.chan)
|
||||
continue;
|
||||
empty_schedule = false;
|
||||
break;
|
||||
}
|
||||
|
||||
/* add the MAC if needed (before adding links) */
|
||||
if (!empty_schedule && previously_empty_schedule) {
|
||||
WARN_ON(mld_vif->nan.mac_added);
|
||||
ret = iwl_mld_add_nan_vif(mld, vif);
|
||||
|
||||
if (ret) {
|
||||
IWL_ERR(mld, "NAN: failed to add MAC (%d)\n", ret);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mld_vif->nan.mac_added) {
|
||||
/* nothing to do */
|
||||
return;
|
||||
}
|
||||
|
||||
/* this currently just uses the same index */
|
||||
BUILD_BUG_ON(ARRAY_SIZE(sched_cfg->channels) !=
|
||||
ARRAY_SIZE(cmd.channels));
|
||||
|
||||
/*
|
||||
* mac80211 removes unused channels before adding new ones, so it may
|
||||
* update an empty schedule with an availability attribute because it
|
||||
* is going to add channels later. Since the firmware does not expect
|
||||
* an availability attribute without channels, ignore it in that case.
|
||||
*/
|
||||
if (empty_schedule)
|
||||
cmd.avail_attr.attr_len = 0;
|
||||
|
||||
/* find links we can keep (same chanctx/PHY) */
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
|
||||
struct ieee80211_chanctx_conf *chanctx;
|
||||
struct iwl_mld_nan_link *link;
|
||||
|
||||
chanctx = sched_cfg->channels[i].chanctx_conf;
|
||||
/* ULW */
|
||||
if (!chanctx)
|
||||
continue;
|
||||
|
||||
link = iwl_mld_nan_find_link(mld_vif, chanctx);
|
||||
links[i] = link;
|
||||
if (link)
|
||||
link_used[link->fw_id] = true;
|
||||
}
|
||||
|
||||
/* add/reassign links for new channels */
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
|
||||
struct ieee80211_chanctx_conf *chanctx;
|
||||
|
||||
/* already have an existing active link */
|
||||
if (links[i])
|
||||
continue;
|
||||
|
||||
chanctx = sched_cfg->channels[i].chanctx_conf;
|
||||
/* ULW or unused slot */
|
||||
if (!chanctx)
|
||||
continue;
|
||||
|
||||
/*
|
||||
* if this fails we still update the schedule, but
|
||||
* without a valid link we'll always ULW it
|
||||
*/
|
||||
links[i] = iwl_mld_nan_link_add(mld, mld_vif, chanctx);
|
||||
|
||||
/* we have a link, activate it */
|
||||
if (links[i]) {
|
||||
added_links = true;
|
||||
link_used[links[i]->fw_id] = true;
|
||||
iwl_mld_nan_link_set_active(mld, vif, links[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
/* fill the command */
|
||||
for (i = 0; i < ARRAY_SIZE(sched_cfg->channels); i++) {
|
||||
cmd.channels[i].link_id = FW_CTXT_ID_INVALID;
|
||||
|
||||
if (!sched_cfg->channels[i].chanreq.oper.chan)
|
||||
continue;
|
||||
|
||||
memcpy(cmd.channels[i].channel_entry,
|
||||
sched_cfg->channels[i].channel_entry, 6);
|
||||
cmd.channels[i].link_id =
|
||||
links[i] ? links[i]->fw_id : FW_CTXT_ID_INVALID;
|
||||
}
|
||||
|
||||
for (i = 0; i < CFG80211_NAN_SCHED_NUM_TIME_SLOTS; i++) {
|
||||
int chan_idx;
|
||||
|
||||
if (!slots[i])
|
||||
continue;
|
||||
|
||||
chan_idx = slots[i] - sched_cfg->channels;
|
||||
if (WARN_ON_ONCE(chan_idx < 0 ||
|
||||
chan_idx >= ARRAY_SIZE(cmd.channels)))
|
||||
continue;
|
||||
|
||||
cmd.channels[chan_idx].availability_map |= cpu_to_le32(BIT(i));
|
||||
}
|
||||
|
||||
ret = iwl_mld_send_cmd_pdu(mld, cmd_id, &cmd, cmd_size);
|
||||
if (ret)
|
||||
IWL_ERR(mld, "NAN: failed to update schedule (%d)\n", ret);
|
||||
|
||||
/* prepare stations for links we'll remove */
|
||||
for_each_mld_nan_valid_link(mld_vif, nan_link) {
|
||||
if (!link_used[nan_link->fw_id]) {
|
||||
iwl_mld_nan_link_set_active(mld, vif, nan_link, false);
|
||||
remove_link_ids |= BIT(nan_link->fw_id);
|
||||
/* mark unused for STA updates */
|
||||
nan_link->fw_id = FW_CTXT_ID_INVALID;
|
||||
}
|
||||
}
|
||||
|
||||
if (added_links || remove_link_ids) {
|
||||
struct ieee80211_sta *sta;
|
||||
|
||||
for_each_station(sta, mld->hw) {
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
|
||||
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NMI ||
|
||||
mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI)
|
||||
iwl_mld_add_modify_sta_cmd(mld, &sta->deflink);
|
||||
}
|
||||
|
||||
/*
|
||||
* Iterate over all the NAN Data interfaces and update the links
|
||||
* for the internal multicast data station.
|
||||
* In recovery - the station will be added later in
|
||||
* drv_add_interface
|
||||
*/
|
||||
if (iwl_mld_nan_use_nan_stations(mld) && !mld->fw_status.in_hw_restart) {
|
||||
struct ieee80211_vif *iter;
|
||||
|
||||
for_each_active_interface(iter, mld->hw)
|
||||
iwl_mld_nan_set_mcast_data_links(iter);
|
||||
}
|
||||
}
|
||||
|
||||
/* delete unused links */
|
||||
for_each_set_bit(i, &remove_link_ids, ARRAY_SIZE(mld_vif->nan.links))
|
||||
iwl_mld_nan_link_remove(mld, &mld_vif->nan.links[i], i);
|
||||
|
||||
/* remove MAC if needed */
|
||||
if (!previously_empty_schedule && empty_schedule) {
|
||||
/* must have been added */
|
||||
WARN_ON_ONCE(!mld_vif->nan.mac_added);
|
||||
|
||||
/* mac80211 should reconfigure same state */
|
||||
if (!WARN_ON_ONCE(mld->fw_status.in_hw_restart &&
|
||||
!iwl_mld_error_before_recovery(mld)))
|
||||
iwl_mld_rm_vif(mld, vif);
|
||||
}
|
||||
}
|
||||
|
||||
bool iwl_mld_cancel_nan_sched_update_completed_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void iwl_mld_handle_nan_sched_update_completed_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
struct iwl_nan_sched_update_completed_notif *notif = (void *)pkt->data;
|
||||
struct ieee80211_vif *vif = mld->nan_device_vif;
|
||||
|
||||
if (IWL_FW_CHECK(mld, !vif,
|
||||
"NAN: schedule update completed without NAN vif\n"))
|
||||
return;
|
||||
|
||||
if (IWL_FW_CHECK(mld, !ieee80211_vif_nan_started(vif),
|
||||
"NAN: schedule update completed without NAN started\n"))
|
||||
return;
|
||||
|
||||
/*
|
||||
* Deferred schedule update should not fail in firmware since all
|
||||
* channels and links were added.
|
||||
*/
|
||||
IWL_FW_CHECK(mld, notif->status != IWL_NAN_SCHED_UPDATE_SUCCESS,
|
||||
"NAN: deferred schedule update failed\n");
|
||||
|
||||
if (WARN_ON(!vif->cfg.nan_sched.deferred))
|
||||
return;
|
||||
|
||||
ieee80211_nan_sched_update_done(vif);
|
||||
}
|
||||
|
||||
int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw,
|
||||
struct ieee80211_sta *sta)
|
||||
{
|
||||
struct iwl_mld_sta *mld_sta = iwl_mld_sta_from_mac80211(sta);
|
||||
struct ieee80211_nan_peer_sched *sched = sta->nan_sched;
|
||||
struct iwl_mld_vif *mld_vif = iwl_mld_vif_from_mac80211(mld_sta->vif);
|
||||
struct iwl_mld *mld = IWL_MAC80211_GET_MLD(hw);
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
struct iwl_nan_peer_cmd cmd = {
|
||||
.nmi_sta_id = mld_sta->deflink.fw_id,
|
||||
.sequence_id = sched->seq_id,
|
||||
.committed_dw_info = cpu_to_le16(sched->committed_dw),
|
||||
.max_channel_switch_time = cpu_to_le16(sched->max_chan_switch),
|
||||
.initial_ulw_size = cpu_to_le32(sched->ulw_size),
|
||||
.per_phy[0 ... NUM_PHY_CTX - 1] = {
|
||||
/* unused by FW if availability_map == 0 */
|
||||
.map_id = CFG80211_NAN_INVALID_MAP_ID,
|
||||
.link_id = FW_CTXT_ID_INVALID,
|
||||
},
|
||||
/* .initial_ulw directly provided below by data[1]/len[1] */
|
||||
};
|
||||
struct iwl_host_cmd hcmd = {
|
||||
.id = WIDE_ID(MAC_CONF_GROUP, NAN_PEER_CMD),
|
||||
.data[0] = &cmd,
|
||||
.len[0] = sizeof(cmd),
|
||||
.data[1] = sched->init_ulw,
|
||||
.len[1] = sched->ulw_size,
|
||||
.dataflags[1] = IWL_HCMD_DFL_DUP,
|
||||
};
|
||||
struct ieee80211_sta *iter;
|
||||
|
||||
/* Update TLC in case peer channels were added/removed/updated */
|
||||
for_each_station(iter, mld->hw) {
|
||||
struct iwl_mld_sta *tmp = iwl_mld_sta_from_mac80211(iter);
|
||||
|
||||
if (tmp->sta_type == STATION_TYPE_NAN_PEER_NDI)
|
||||
iwl_mld_config_tlc(mld, tmp->vif, iter);
|
||||
}
|
||||
|
||||
for (int i = 0; i < ARRAY_SIZE(sched->maps); i++) {
|
||||
if (sched->maps[i].map_id == CFG80211_NAN_INVALID_MAP_ID)
|
||||
continue;
|
||||
|
||||
BUILD_BUG_ON(ARRAY_SIZE(sched->maps[i].slots) != 32);
|
||||
for (int slot = 0;
|
||||
slot < ARRAY_SIZE(sched->maps[i].slots);
|
||||
slot++) {
|
||||
struct ieee80211_chanctx_conf *ctx;
|
||||
struct ieee80211_nan_channel *chan;
|
||||
struct iwl_mld_phy *phy;
|
||||
|
||||
chan = sched->maps[i].slots[slot];
|
||||
if (!chan)
|
||||
continue;
|
||||
|
||||
ctx = chan->chanctx_conf;
|
||||
if (!ctx)
|
||||
continue;
|
||||
|
||||
phy = iwl_mld_phy_from_mac80211(ctx);
|
||||
|
||||
for_each_mld_nan_valid_link(mld_vif, nan_link) {
|
||||
if (nan_link->chanctx == ctx) {
|
||||
cmd.per_phy[phy->fw_id].link_id =
|
||||
nan_link->fw_id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (WARN_ON(cmd.per_phy[phy->fw_id].link_id ==
|
||||
FW_CTXT_ID_INVALID))
|
||||
continue;
|
||||
|
||||
/*
|
||||
* each channel can only appear in one map,
|
||||
* upper layers enforce that
|
||||
*/
|
||||
if (WARN_ON(cmd.per_phy[phy->fw_id].map_id != CFG80211_NAN_INVALID_MAP_ID &&
|
||||
cmd.per_phy[phy->fw_id].map_id != sched->maps[i].map_id))
|
||||
continue;
|
||||
|
||||
cmd.per_phy[phy->fw_id].map_id = sched->maps[i].map_id;
|
||||
memcpy(cmd.per_phy[phy->fw_id].channel_entry,
|
||||
chan->channel_entry,
|
||||
sizeof(cmd.per_phy[phy->fw_id].channel_entry));
|
||||
cmd.per_phy[phy->fw_id].availability_map |=
|
||||
cpu_to_le32(BIT(slot));
|
||||
}
|
||||
}
|
||||
|
||||
return iwl_mld_send_cmd(mld, &hcmd);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,12 +1,31 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2025 Intel Corporation
|
||||
* Copyright (C) 2025-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_nan_h__
|
||||
#define __iwl_mld_nan_h__
|
||||
#include <net/cfg80211.h>
|
||||
#include <linux/etherdevice.h>
|
||||
|
||||
/**
|
||||
* struct iwl_mld_nan_link - struct representing a NAN link
|
||||
* @chanctx: the channel context
|
||||
* @active: indicates the NAN link is currently active
|
||||
* @fw_id: FW link ID
|
||||
*/
|
||||
struct iwl_mld_nan_link {
|
||||
struct ieee80211_chanctx_conf *chanctx;
|
||||
bool active;
|
||||
u8 fw_id;
|
||||
};
|
||||
|
||||
/* Cleanup function for struct iwl_mld_nan_link, will be called in restart */
|
||||
static inline void iwl_mld_cleanup_nan_link(struct iwl_mld_nan_link *nan_link)
|
||||
{
|
||||
memset(nan_link, 0, sizeof(*nan_link));
|
||||
nan_link->fw_id = FW_CTXT_ID_INVALID;
|
||||
}
|
||||
|
||||
bool iwl_mld_nan_supported(struct iwl_mld *mld);
|
||||
int iwl_mld_start_nan(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
|
|
@ -19,13 +38,33 @@ int iwl_mld_stop_nan(struct ieee80211_hw *hw,
|
|||
struct ieee80211_vif *vif);
|
||||
void iwl_mld_handle_nan_cluster_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
void iwl_mld_handle_nan_ulw_attr_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
void iwl_mld_handle_nan_dw_end_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
void iwl_mld_handle_nan_sched_update_completed_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt);
|
||||
bool iwl_mld_cancel_nan_cluster_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id);
|
||||
bool iwl_mld_cancel_nan_ulw_attr_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id);
|
||||
bool iwl_mld_cancel_nan_dw_end_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id);
|
||||
bool iwl_mld_cancel_nan_sched_update_completed_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt,
|
||||
u32 obj_id);
|
||||
void iwl_mld_nan_vif_cfg_changed(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
u64 changes);
|
||||
|
||||
int iwl_mld_mac802111_nan_peer_sched_changed(struct ieee80211_hw *hw,
|
||||
struct ieee80211_sta *sta);
|
||||
|
||||
int iwl_mld_nan_get_mgmt_queue(struct iwl_mld *mld, struct ieee80211_vif *vif);
|
||||
|
||||
bool iwl_mld_nan_use_nan_stations(struct iwl_mld *mld);
|
||||
|
||||
#endif /* __iwl_mld_nan_h__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "mld.h"
|
||||
|
|
@ -307,11 +307,8 @@ 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))
|
||||
|
|
@ -330,7 +327,8 @@ CMD_VERSIONS(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_VER_ENTRY(3, iwl_system_statistics_notif_oper_v3)
|
||||
CMD_VER_ENTRY(4, 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,
|
||||
|
|
@ -341,8 +339,6 @@ CMD_VERSIONS(emlsr_mode_notif,
|
|||
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,
|
||||
|
|
@ -350,7 +346,10 @@ CMD_VERSIONS(time_sync_confirm_notif,
|
|||
CMD_VERSIONS(ftm_resp_notif, CMD_VER_ENTRY(10, iwl_tof_range_rsp_ntfy))
|
||||
CMD_VERSIONS(beacon_filter_notif, CMD_VER_ENTRY(2, iwl_beacon_filter_notif))
|
||||
CMD_VERSIONS(nan_cluster_notif, CMD_VER_ENTRY(1, iwl_nan_cluster_notif))
|
||||
CMD_VERSIONS(nan_ulw_attr_notif, CMD_VER_ENTRY(1, iwl_nan_ulw_attr_notif))
|
||||
CMD_VERSIONS(nan_dw_end_notif, CMD_VER_ENTRY(1, iwl_nan_dw_end_notif))
|
||||
CMD_VERSIONS(nan_sched_update_completed_notif,
|
||||
CMD_VER_ENTRY(1, iwl_nan_sched_update_completed_notif))
|
||||
|
||||
DEFINE_SIMPLE_CANCELLATION(session_prot, iwl_session_prot_notif, mac_link_id)
|
||||
DEFINE_SIMPLE_CANCELLATION(tlc, iwl_tlc_update_notif, sta_id)
|
||||
|
|
@ -365,8 +364,6 @@ DEFINE_SIMPLE_CANCELLATION(scan_complete, iwl_umac_scan_complete, uid)
|
|||
DEFINE_SIMPLE_CANCELLATION(scan_start, iwl_umac_scan_start, 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_SIMPLE_CANCELLATION(ftm_resp, iwl_tof_range_rsp_ntfy, request_id)
|
||||
DEFINE_SIMPLE_CANCELLATION(beacon_filter, iwl_beacon_filter_notif, link_id)
|
||||
|
||||
|
|
@ -457,8 +454,6 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
|
|||
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)
|
||||
|
|
@ -471,8 +466,12 @@ const struct iwl_rx_handler iwl_mld_rx_handlers[] = {
|
|||
ftm_resp_notif)
|
||||
RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_JOINED_CLUSTER_NOTIF,
|
||||
nan_cluster_notif)
|
||||
RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_ULW_ATTR_NOTIF,
|
||||
nan_ulw_attr_notif)
|
||||
RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_DW_END_NOTIF,
|
||||
nan_dw_end_notif)
|
||||
RX_HANDLER_OF_NAN(MAC_CONF_GROUP, NAN_SCHED_UPDATE_COMPLETED_NOTIF,
|
||||
nan_sched_update_completed_notif)
|
||||
};
|
||||
EXPORT_SYMBOL_IF_IWLWIFI_KUNIT(iwl_mld_rx_handlers);
|
||||
|
||||
|
|
@ -607,6 +606,11 @@ void iwl_mld_rx(struct iwl_op_mode *op_mode, struct napi_struct *napi,
|
|||
else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
|
||||
RX_QUEUES_NOTIFICATION)))
|
||||
iwl_mld_handle_rx_queues_sync_notif(mld, napi, pkt, 0);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
|
||||
RSC_NOTIF)))
|
||||
iwl_mld_handle_rsc_notif(mld, pkt, 0);
|
||||
#endif
|
||||
else if (cmd_id == WIDE_ID(DATA_PATH_GROUP, PHY_AIR_SNIFFER_NOTIF))
|
||||
iwl_mld_handle_phy_air_sniffer_notif(mld, napi, pkt);
|
||||
else
|
||||
|
|
@ -630,6 +634,11 @@ void iwl_mld_rx_rss(struct iwl_op_mode *op_mode, struct napi_struct *napi,
|
|||
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);
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
else if (unlikely(cmd_id == WIDE_ID(DATA_PATH_GROUP,
|
||||
RSC_NOTIF)))
|
||||
iwl_mld_handle_rsc_notif(mld, pkt, queue);
|
||||
#endif
|
||||
}
|
||||
|
||||
void iwl_mld_delete_handlers(struct iwl_mld *mld, const u16 *cmds, int n_cmds)
|
||||
|
|
@ -685,10 +694,6 @@ 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);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <net/mac80211.h>
|
||||
|
||||
|
|
@ -99,9 +99,9 @@ iwl_mld_nl80211_width_to_fw(enum nl80211_chan_width width)
|
|||
/* 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)
|
||||
static u8 _iwl_mld_get_fw_ctrl_pos(u32 control, u32 cf1)
|
||||
{
|
||||
int offs = chandef->chan->center_freq - chandef->center_freq1;
|
||||
int offs = control - cf1;
|
||||
int abs_offs = abs(offs);
|
||||
u8 ret;
|
||||
|
||||
|
|
@ -127,6 +127,12 @@ u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)
|
|||
return ret;
|
||||
}
|
||||
|
||||
u8 iwl_mld_get_fw_ctrl_pos(const struct cfg80211_chan_def *chandef)
|
||||
{
|
||||
return _iwl_mld_get_fw_ctrl_pos(chandef->chan->center_freq,
|
||||
chandef->center_freq1);
|
||||
}
|
||||
|
||||
int iwl_mld_phy_fw_action(struct iwl_mld *mld,
|
||||
struct ieee80211_chanctx_conf *ctx, u32 action)
|
||||
{
|
||||
|
|
@ -150,6 +156,18 @@ int iwl_mld_phy_fw_action(struct iwl_mld *mld,
|
|||
cmd.sbb_ctrl_channel_loc = iwl_mld_get_fw_ctrl_pos(&ctx->ap);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set NPCA channel if NPCA is used; if not used, just set it to an
|
||||
* arbitrary channel on the other side to help firmware.
|
||||
*/
|
||||
if (chandef->npca_chan)
|
||||
cmd.secondary_ctrl_chnl_loc =
|
||||
_iwl_mld_get_fw_ctrl_pos(chandef->npca_chan->center_freq,
|
||||
chandef->center_freq1);
|
||||
else
|
||||
cmd.secondary_ctrl_chnl_loc =
|
||||
cmd.ci.ctrl_pos ^ IWL_PHY_CTRL_POS_ABOVE;
|
||||
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#include <net/mac80211.h>
|
||||
|
||||
|
|
@ -11,36 +11,11 @@
|
|||
#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)
|
||||
if (iwlmld_mod_params.power_scheme != IWL_POWER_SCHEME_CAM)
|
||||
cmd.flags |=
|
||||
cpu_to_le16(DEVICE_POWER_FLAGS_POWER_SAVE_ENA_MSK);
|
||||
|
||||
|
|
@ -113,10 +88,10 @@ static bool iwl_mld_power_is_radar(struct iwl_mld *mld,
|
|||
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)
|
||||
static void iwl_mld_power_configure_uapsd_v2(struct iwl_mld *mld,
|
||||
struct iwl_mld_link *link,
|
||||
struct iwl_mac_power_cmd_v2 *cmd,
|
||||
bool ps_poll)
|
||||
{
|
||||
bool tid_found = false;
|
||||
|
||||
|
|
@ -175,10 +150,54 @@ static void iwl_mld_power_configure_uapsd(struct iwl_mld *mld,
|
|||
cmd->uapsd_max_sp = mld->hw->uapsd_max_sp_len;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
/* 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);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
u8 *skip_dtim_periods, __le16 *flags)
|
||||
{
|
||||
unsigned int dtimper_tu;
|
||||
unsigned int dtimper;
|
||||
|
|
@ -196,15 +215,15 @@ iwl_mld_power_config_skip_dtim(struct iwl_mld *mld,
|
|||
/* 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);
|
||||
*skip_dtim_periods = skip;
|
||||
*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)
|
||||
static void iwl_mld_power_build_cmd_v2(struct iwl_mld *mld,
|
||||
struct ieee80211_vif *vif,
|
||||
struct iwl_mac_power_cmd_v2 *cmd,
|
||||
bool d3)
|
||||
{
|
||||
int dtimper, bi;
|
||||
int keep_alive;
|
||||
|
|
@ -252,9 +271,7 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
|||
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);
|
||||
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 &&
|
||||
|
|
@ -265,7 +282,9 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
if (d3) {
|
||||
iwl_mld_power_config_skip_dtim(mld, link_conf, cmd);
|
||||
iwl_mld_power_config_skip_dtim(mld, link_conf,
|
||||
&cmd->skip_dtim_periods,
|
||||
&cmd->flags);
|
||||
cmd->rx_data_timeout =
|
||||
cpu_to_le32(IWL_MLD_WOWLAN_PS_RX_DATA_TIMEOUT);
|
||||
cmd->tx_data_timeout =
|
||||
|
|
@ -286,6 +305,95 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
|||
* 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_v2(mld, link, cmd, ps_poll);
|
||||
}
|
||||
|
||||
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;
|
||||
__le32 fw_id = 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->id_and_color = fw_id;
|
||||
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) {
|
||||
cmd->flags |= cpu_to_le16(POWER_FLAGS_POWER_MANAGEMENT_ENA_MSK);
|
||||
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->skip_dtim_periods,
|
||||
&cmd->flags);
|
||||
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);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_IWLWIFI_DEBUGFS
|
||||
ps_poll = mld_vif->use_ps_poll;
|
||||
#endif
|
||||
|
|
@ -295,11 +403,23 @@ static void iwl_mld_power_build_cmd(struct iwl_mld *mld,
|
|||
int iwl_mld_update_mac_power(struct iwl_mld *mld, struct ieee80211_vif *vif,
|
||||
bool d3)
|
||||
{
|
||||
struct iwl_mac_power_cmd cmd = {};
|
||||
int cmd_ver = iwl_fw_lookup_cmd_ver(mld->fw, MAC_PM_POWER_TABLE, 0);
|
||||
|
||||
iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
|
||||
if (cmd_ver >= 3) {
|
||||
struct iwl_mac_power_cmd cmd = {};
|
||||
|
||||
return iwl_mld_send_cmd_pdu(mld, MAC_PM_POWER_TABLE, &cmd);
|
||||
iwl_mld_power_build_cmd(mld, vif, &cmd, d3);
|
||||
return iwl_mld_send_cmd_with_flags_pdu(mld,
|
||||
MAC_PM_POWER_TABLE, 0,
|
||||
&cmd, sizeof(cmd));
|
||||
} else {
|
||||
struct iwl_mac_power_cmd_v2 cmd = {};
|
||||
|
||||
iwl_mld_power_build_cmd_v2(mld, vif, &cmd, d3);
|
||||
return iwl_mld_send_cmd_with_flags_pdu(mld,
|
||||
MAC_PM_POWER_TABLE, 0,
|
||||
&cmd, sizeof(cmd));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
|
|||
|
|
@ -321,10 +321,10 @@ void iwl_mld_ptp_remove(struct iwl_mld *mld)
|
|||
mld->ptp_data.ptp_clock_info.name,
|
||||
ptp_clock_index(mld->ptp_data.ptp_clock));
|
||||
|
||||
cancel_delayed_work_sync(&mld->ptp_data.dwork);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ iwl_mld_fill_phy_data_from_mpdu(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
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->rate_n_flags = le32_to_cpu(desc->v3.rate_n_flags);
|
||||
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;
|
||||
|
|
@ -158,7 +157,7 @@ static bool iwl_mld_used_average_energy(struct iwl_mld *mld, int link_id,
|
|||
guard(rcu)();
|
||||
|
||||
link_conf = rcu_dereference(mld->fw_id_to_bss_conf[link_id]);
|
||||
if (!link_conf)
|
||||
if (IS_ERR_OR_NULL(link_conf))
|
||||
return false;
|
||||
|
||||
mld_link = iwl_mld_link_from_mac80211(link_conf);
|
||||
|
|
@ -1206,6 +1205,13 @@ static void iwl_mld_decode_eht_non_tb(struct iwl_mld_rx_phy_data *phy_data,
|
|||
iwl_mld_eht_set_ru_alloc(rx_status,
|
||||
le32_get_bits(phy_data->ntfy->sigs.eht.b2,
|
||||
OFDM_RX_FRAME_EHT_STA_RU));
|
||||
|
||||
if (phy_data->with_data)
|
||||
eht->user_info[0] |=
|
||||
cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) |
|
||||
LE32_DEC_ENC(phy_data->ntfy->sigs.eht.user_id,
|
||||
OFDM_RX_FRAME_EHT_USER_FIELD_ID,
|
||||
IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID);
|
||||
}
|
||||
|
||||
static void iwl_mld_decode_eht_phy_data(struct iwl_mld_rx_phy_data *phy_data,
|
||||
|
|
@ -1314,14 +1320,6 @@ static void iwl_mld_rx_eht(struct iwl_mld *mld, struct sk_buff *skb,
|
|||
if (likely(!phy_data->ntfy))
|
||||
return;
|
||||
|
||||
if (phy_data->with_data) {
|
||||
eht->user_info[0] |=
|
||||
cpu_to_le32(IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID_KNOWN) |
|
||||
LE32_DEC_ENC(phy_data->ntfy->sigs.eht.user_id,
|
||||
OFDM_RX_FRAME_EHT_USER_FIELD_ID,
|
||||
IEEE80211_RADIOTAP_EHT_USER_INFO_STA_ID);
|
||||
}
|
||||
|
||||
iwl_mld_decode_eht_usig(phy_data, skb);
|
||||
iwl_mld_decode_eht_phy_data(phy_data, rx_status, eht);
|
||||
}
|
||||
|
|
@ -2262,6 +2260,30 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
|
|||
wake_up(&mld->rxq_sync.waitq);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt, int queue)
|
||||
{
|
||||
const struct iwl_wowlan_all_rsc_tsc_v5 *notif = (void *)pkt->data;
|
||||
u32 len = iwl_rx_packet_payload_len(pkt);
|
||||
struct ieee80211_vif *bss_vif;
|
||||
|
||||
if (IWL_FW_CHECK(mld, len != sizeof(*notif),
|
||||
"invalid notification size %u (%zu)\n",
|
||||
len, sizeof(*notif)))
|
||||
return;
|
||||
|
||||
/* for the bss lookup and updating the keys' pn */
|
||||
guard(rcu)();
|
||||
|
||||
bss_vif = iwl_mld_get_bss_vif(mld);
|
||||
if (WARN_ON(!bss_vif))
|
||||
return;
|
||||
|
||||
iwl_mld_process_rsc_notification(mld, bss_vif, notif, queue);
|
||||
}
|
||||
#endif /* CONFIG_PM_SLEEP */
|
||||
|
||||
static void iwl_mld_no_data_rx(struct iwl_mld *mld,
|
||||
struct napi_struct *napi,
|
||||
struct iwl_rx_phy_air_sniffer_ntfy *ntfy)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
#ifndef __iwl_mld_rx_h__
|
||||
#define __iwl_mld_rx_h__
|
||||
|
|
@ -61,6 +61,11 @@ void iwl_mld_handle_rx_queues_sync_notif(struct iwl_mld *mld,
|
|||
struct napi_struct *napi,
|
||||
struct iwl_rx_packet *pkt, int queue);
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
void iwl_mld_handle_rsc_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt, int queue);
|
||||
#endif
|
||||
|
||||
void iwl_mld_pass_packet_to_mac80211(struct iwl_mld *mld,
|
||||
struct napi_struct *napi,
|
||||
struct sk_buff *skb, int queue,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include <linux/ieee80211.h>
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
#include "key.h"
|
||||
#include "agg.h"
|
||||
#include "tlc.h"
|
||||
#include "nan.h"
|
||||
#include "fw/api/sta.h"
|
||||
#include "fw/api/mac.h"
|
||||
#include "fw/api/rx.h"
|
||||
|
|
@ -43,13 +44,13 @@ int iwl_mld_fw_sta_id_from_link_sta(struct iwl_mld *mld,
|
|||
|
||||
static void
|
||||
iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta,
|
||||
struct ieee80211_bss_conf *link,
|
||||
bool is_6ghz,
|
||||
__le32 *tx_ampdu_max_size,
|
||||
__le32 *tx_ampdu_spacing)
|
||||
{
|
||||
u32 agg_size = 0, mpdu_dens = 0;
|
||||
|
||||
if (WARN_ON(!link_sta || !link))
|
||||
if (WARN_ON(!link_sta))
|
||||
return;
|
||||
|
||||
/* Note that we always use only legacy & highest supported PPDUs, so
|
||||
|
|
@ -63,7 +64,7 @@ iwl_mld_fill_ampdu_size_and_dens(struct ieee80211_link_sta *link_sta,
|
|||
mpdu_dens = link_sta->ht_cap.ampdu_density;
|
||||
}
|
||||
|
||||
if (link->chanreq.oper.chan->band == NL80211_BAND_6GHZ) {
|
||||
if (is_6ghz) {
|
||||
/* overwrite HT values on 6 GHz */
|
||||
mpdu_dens =
|
||||
le16_get_bits(link_sta->he_6ghz_capa.capa,
|
||||
|
|
@ -427,8 +428,14 @@ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
|
|||
|
||||
len = sizeof(struct iwl_sta_cfg_cmd_v2);
|
||||
cmd_v2->link_id = cpu_to_le32(__ffs(le32_to_cpu(cmd->link_mask)));
|
||||
} else if (cmd->station_type ==
|
||||
cpu_to_le32(STATION_TYPE_NAN_MCAST_DATA)) {
|
||||
if (WARN_ON(!hweight32(le32_to_cpu(cmd->link_mask))))
|
||||
return -EINVAL;
|
||||
} else if (WARN_ON(cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NMI) &&
|
||||
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_PEER_NDI) &&
|
||||
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_BCAST) &&
|
||||
cmd->station_type != cpu_to_le32(STATION_TYPE_NAN_MGMT) &&
|
||||
hweight32(le32_to_cpu(cmd->link_mask)) != 1)) {
|
||||
return -EINVAL;
|
||||
}
|
||||
|
|
@ -439,29 +446,61 @@ static int iwl_mld_send_sta_cmd(struct iwl_mld *mld,
|
|||
return ret;
|
||||
}
|
||||
|
||||
static int
|
||||
iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_link_sta *link_sta)
|
||||
static u32 iwl_mld_get_nan_link_mask(struct iwl_mld *mld)
|
||||
{
|
||||
struct iwl_mld_vif *nan_dev =
|
||||
iwl_mld_vif_from_mac80211(mld->nan_device_vif);
|
||||
struct iwl_mld_nan_link *nan_link;
|
||||
u32 link_mask = 0;
|
||||
|
||||
for_each_mld_nan_valid_link(nan_dev, nan_link)
|
||||
link_mask |= BIT(nan_link->fw_id);
|
||||
|
||||
return link_mask;
|
||||
}
|
||||
|
||||
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);
|
||||
bool is_6ghz, uora_exists;
|
||||
u32 link_mask;
|
||||
|
||||
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)
|
||||
if (WARN_ON(fw_id < 0))
|
||||
return -EINVAL;
|
||||
|
||||
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NMI ||
|
||||
mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) {
|
||||
if (WARN_ON(!mld->nan_device_vif))
|
||||
return -EINVAL;
|
||||
|
||||
is_6ghz = false;
|
||||
uora_exists = false;
|
||||
|
||||
link_mask = iwl_mld_get_nan_link_mask(mld);
|
||||
} else {
|
||||
struct ieee80211_bss_conf *link;
|
||||
struct iwl_mld_link *mld_link;
|
||||
|
||||
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))
|
||||
return -EINVAL;
|
||||
|
||||
link_mask = BIT(mld_link->fw_id);
|
||||
is_6ghz = link->chanreq.oper.chan->band == NL80211_BAND_6GHZ;
|
||||
uora_exists = link->uora_exists;
|
||||
}
|
||||
|
||||
cmd.sta_id = cpu_to_le32(fw_id);
|
||||
cmd.link_mask = cpu_to_le32(BIT(mld_link->fw_id));
|
||||
cmd.link_mask = cpu_to_le32(link_mask);
|
||||
cmd.station_type = cpu_to_le32(mld_sta->sta_type);
|
||||
|
||||
memcpy(&cmd.peer_mld_address, sta->addr, ETH_ALEN);
|
||||
|
|
@ -499,7 +538,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
|||
break;
|
||||
}
|
||||
|
||||
iwl_mld_fill_ampdu_size_and_dens(link_sta, link,
|
||||
iwl_mld_fill_ampdu_size_and_dens(link_sta, is_6ghz,
|
||||
&cmd.tx_ampdu_max_size,
|
||||
&cmd.tx_ampdu_spacing);
|
||||
|
||||
|
|
@ -511,7 +550,7 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
|||
|
||||
if (link_sta->he_cap.has_he) {
|
||||
cmd.trig_rnd_alloc =
|
||||
cpu_to_le32(link->uora_exists ? 1 : 0);
|
||||
cpu_to_le32(uora_exists ? 1 : 0);
|
||||
|
||||
/* PPE Thresholds */
|
||||
iwl_mld_fill_pkt_ext(mld, link_sta, &cmd.pkt_ext);
|
||||
|
|
@ -525,10 +564,29 @@ iwl_mld_add_modify_sta_cmd(struct iwl_mld *mld,
|
|||
cmd.ack_enabled = cpu_to_le32(1);
|
||||
}
|
||||
|
||||
if (mld_sta->sta_type == STATION_TYPE_NAN_PEER_NDI) {
|
||||
struct ieee80211_sta *nmi_sta =
|
||||
wiphy_dereference(mld->wiphy, sta->nmi);
|
||||
int nmi_fw_id;
|
||||
|
||||
/* copy the local NDI address */
|
||||
ether_addr_copy(cmd.ndi_local_addr, mld_sta->vif->addr);
|
||||
|
||||
if (WARN_ON(!nmi_sta))
|
||||
return -EINVAL;
|
||||
|
||||
nmi_fw_id = iwl_mld_fw_sta_id_from_link_sta(mld,
|
||||
&nmi_sta->deflink);
|
||||
if (nmi_fw_id < 0)
|
||||
return -EINVAL;
|
||||
|
||||
cmd.nmi_sta_id = (u8) nmi_fw_id;
|
||||
}
|
||||
|
||||
return iwl_mld_send_sta_cmd(mld, &cmd);
|
||||
}
|
||||
|
||||
static IWL_MLD_ALLOC_FN(link_sta, link_sta)
|
||||
IWL_MLD_ALLOC_FN_STATIC(link_sta, link_sta)
|
||||
|
||||
static int
|
||||
iwl_mld_add_link_sta(struct iwl_mld *mld, struct ieee80211_link_sta *link_sta)
|
||||
|
|
@ -759,10 +817,23 @@ int iwl_mld_add_sta(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;
|
||||
enum iwl_fw_sta_type type;
|
||||
int link_id;
|
||||
int ret;
|
||||
|
||||
ret = iwl_mld_init_sta(mld, sta, vif, STATION_TYPE_PEER);
|
||||
switch (vif->type) {
|
||||
case NL80211_IFTYPE_NAN:
|
||||
type = STATION_TYPE_NAN_PEER_NMI;
|
||||
break;
|
||||
case NL80211_IFTYPE_NAN_DATA:
|
||||
type = STATION_TYPE_NAN_PEER_NDI;
|
||||
break;
|
||||
default:
|
||||
type = STATION_TYPE_PEER;
|
||||
break;
|
||||
}
|
||||
|
||||
ret = iwl_mld_init_sta(mld, sta, vif, type);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
@ -1001,10 +1072,9 @@ static int iwl_mld_send_aux_sta_cmd(struct iwl_mld *mld,
|
|||
}
|
||||
|
||||
static int
|
||||
iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
|
||||
iwl_mld_set_internal_sta_to_fw(struct iwl_mld *mld,
|
||||
const struct iwl_mld_int_sta *internal_sta,
|
||||
u8 fw_link_id,
|
||||
const u8 *addr)
|
||||
u32 link_mask, const u8 *addr)
|
||||
{
|
||||
struct iwl_sta_cfg_cmd cmd = {};
|
||||
|
||||
|
|
@ -1012,20 +1082,30 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
|
|||
return iwl_mld_send_aux_sta_cmd(mld, internal_sta);
|
||||
|
||||
cmd.sta_id = cpu_to_le32((u8)internal_sta->sta_id);
|
||||
cmd.link_mask = cpu_to_le32(BIT(fw_link_id));
|
||||
cmd.link_mask = cpu_to_le32(link_mask);
|
||||
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.
|
||||
*
|
||||
* For NAN stations associated with a NAN Device, the MFP bit must be
|
||||
* set to 1, as otherwise the FW will assert when a key associated with
|
||||
* these stations would be added.
|
||||
*/
|
||||
if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT)
|
||||
if (internal_sta->sta_type == STATION_TYPE_BCAST_MGMT ||
|
||||
internal_sta->sta_type == STATION_TYPE_NAN_BCAST ||
|
||||
internal_sta->sta_type == STATION_TYPE_NAN_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);
|
||||
if (internal_sta->sta_type == STATION_TYPE_NAN_MCAST_DATA) {
|
||||
ether_addr_copy(cmd.ndi_local_addr, addr);
|
||||
} else {
|
||||
memcpy(cmd.peer_mld_address, addr, ETH_ALEN);
|
||||
memcpy(cmd.peer_link_address, addr, ETH_ALEN);
|
||||
}
|
||||
}
|
||||
|
||||
return iwl_mld_send_sta_cmd(mld, &cmd);
|
||||
|
|
@ -1034,7 +1114,8 @@ iwl_mld_add_internal_sta_to_fw(struct iwl_mld *mld,
|
|||
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)
|
||||
u32 link_mask, const u8 *addr,
|
||||
u8 tid, bool add_txq)
|
||||
{
|
||||
int ret, queue_id;
|
||||
|
||||
|
|
@ -1046,11 +1127,14 @@ static int iwl_mld_add_internal_sta(struct iwl_mld *mld,
|
|||
|
||||
internal_sta->sta_type = sta_type;
|
||||
|
||||
ret = iwl_mld_add_internal_sta_to_fw(mld, internal_sta, fw_link_id,
|
||||
ret = iwl_mld_set_internal_sta_to_fw(mld, internal_sta, link_mask,
|
||||
addr);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
if (!add_txq)
|
||||
return 0;
|
||||
|
||||
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);
|
||||
|
|
@ -1085,8 +1169,8 @@ int iwl_mld_add_bcast_sta(struct iwl_mld *mld,
|
|||
|
||||
return iwl_mld_add_internal_sta(mld, &mld_link->bcast_sta,
|
||||
STATION_TYPE_BCAST_MGMT,
|
||||
mld_link->fw_id, addr,
|
||||
IWL_MGMT_TID);
|
||||
BIT(mld_link->fw_id), addr,
|
||||
IWL_MGMT_TID, true);
|
||||
}
|
||||
|
||||
int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
|
||||
|
|
@ -1105,14 +1189,16 @@ int iwl_mld_add_mcast_sta(struct iwl_mld *mld,
|
|||
|
||||
return iwl_mld_add_internal_sta(mld, &mld_link->mcast_sta,
|
||||
STATION_TYPE_MCAST,
|
||||
mld_link->fw_id, mcast_addr, 0);
|
||||
BIT(mld_link->fw_id), mcast_addr,
|
||||
0, true);
|
||||
}
|
||||
|
||||
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);
|
||||
0, NULL, IWL_MAX_TID_COUNT,
|
||||
true);
|
||||
}
|
||||
|
||||
int iwl_mld_add_mon_sta(struct iwl_mld *mld,
|
||||
|
|
@ -1129,23 +1215,25 @@ int iwl_mld_add_mon_sta(struct iwl_mld *mld,
|
|||
|
||||
return iwl_mld_add_internal_sta(mld, &mld_link->mon_sta,
|
||||
STATION_TYPE_BCAST_MGMT,
|
||||
mld_link->fw_id, NULL,
|
||||
IWL_MAX_TID_COUNT);
|
||||
BIT(mld_link->fw_id), NULL,
|
||||
IWL_MAX_TID_COUNT,
|
||||
true);
|
||||
}
|
||||
|
||||
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))
|
||||
if (WARN_ON_ONCE(internal_sta->sta_id == IWL_INVALID_STA))
|
||||
return;
|
||||
|
||||
if (flush)
|
||||
if (flush && !WARN_ON_ONCE(internal_sta->queue_id ==
|
||||
IWL_MLD_INVALID_QUEUE))
|
||||
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);
|
||||
if (internal_sta->queue_id != IWL_MLD_INVALID_QUEUE)
|
||||
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);
|
||||
|
||||
|
|
@ -1346,3 +1434,82 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld,
|
|||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
const u8 bcast_addr[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
||||
|
||||
return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_BCAST,
|
||||
0, bcast_addr, 0, false);
|
||||
}
|
||||
|
||||
int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
return iwl_mld_add_internal_sta(mld, sta, STATION_TYPE_NAN_MGMT,
|
||||
0, NULL, IWL_MAX_TID_COUNT, true);
|
||||
}
|
||||
|
||||
int iwl_mld_add_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
const u8 *ndi_addr,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
u32 link_mask = iwl_mld_get_nan_link_mask(mld);
|
||||
|
||||
/* In case that there are no NAN links, nothing to do */
|
||||
if (!link_mask)
|
||||
return 0;
|
||||
|
||||
return iwl_mld_add_internal_sta(mld, sta,
|
||||
STATION_TYPE_NAN_MCAST_DATA,
|
||||
link_mask, ndi_addr,
|
||||
0, true);
|
||||
}
|
||||
|
||||
int iwl_mld_update_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
const u8 *ndi_addr,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
u32 link_mask;
|
||||
|
||||
if (WARN_ON(!mld->nan_device_vif))
|
||||
return -EINVAL;
|
||||
|
||||
/* If the sta doesn't exist, add it */
|
||||
if (sta->sta_id == IWL_INVALID_STA)
|
||||
return iwl_mld_add_nan_mcast_data_sta(mld, ndi_addr, sta);
|
||||
|
||||
/* The station was already added */
|
||||
if (WARN_ON(sta->sta_type != STATION_TYPE_NAN_MCAST_DATA))
|
||||
return -EINVAL;
|
||||
|
||||
link_mask = iwl_mld_get_nan_link_mask(mld);
|
||||
if (link_mask)
|
||||
return iwl_mld_set_internal_sta_to_fw(mld,
|
||||
sta, link_mask,
|
||||
ndi_addr);
|
||||
|
||||
/* If no links are associated with NAN, remove the station */
|
||||
iwl_mld_remove_nan_mcast_data_sta(mld, sta);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
iwl_mld_remove_internal_sta(mld, sta, false, 0);
|
||||
}
|
||||
|
||||
void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
iwl_mld_remove_internal_sta(mld, sta, true, IWL_MAX_TID_COUNT);
|
||||
}
|
||||
|
||||
void iwl_mld_remove_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta)
|
||||
{
|
||||
iwl_mld_remove_internal_sta(mld, sta, true, 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#ifndef __iwl_mld_sta_h__
|
||||
|
|
@ -28,6 +28,9 @@ struct iwl_mld_rxq_dup_data {
|
|||
* This represents the link-level sta - the driver level equivalent to the
|
||||
* ieee80211_link_sta
|
||||
*
|
||||
* @rx_igtk: FW can only have one IGTK for RX at a time, whereas mac80211 will
|
||||
* have two. This tracks the one IGTK that currently exists in FW, to
|
||||
* remove it there when a new one is installed.
|
||||
* @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)
|
||||
|
|
@ -37,6 +40,7 @@ struct iwl_mld_rxq_dup_data {
|
|||
struct iwl_mld_link_sta {
|
||||
/* Add here fields that need clean up on restart */
|
||||
struct_group(zeroed_on_hw_restart,
|
||||
struct ieee80211_key_conf *rx_igtk;
|
||||
u32 last_rate_n_flags;
|
||||
bool in_fw;
|
||||
s8 signal_avg;
|
||||
|
|
@ -195,6 +199,8 @@ 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_add_modify_sta_cmd(struct iwl_mld *mld,
|
||||
struct ieee80211_link_sta *link_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);
|
||||
|
|
@ -270,4 +276,28 @@ int iwl_mld_update_link_stas(struct iwl_mld *mld,
|
|||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
u16 old_links, u16 new_links);
|
||||
|
||||
int iwl_mld_add_nan_bcast_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
int iwl_mld_add_nan_mgmt_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
int iwl_mld_add_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
const u8 *ndi_addr,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
int iwl_mld_update_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
const u8 *ndi_addr,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
void iwl_mld_remove_nan_bcast_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
void iwl_mld_remove_nan_mgmt_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
void iwl_mld_remove_nan_mcast_data_sta(struct iwl_mld *mld,
|
||||
struct iwl_mld_int_sta *sta);
|
||||
|
||||
#endif /* __iwl_mld_sta_h__ */
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/*
|
||||
* Copyright (C) 2024-2025 Intel Corporation
|
||||
* Copyright (C) 2024-2026 Intel Corporation
|
||||
*/
|
||||
|
||||
#include "mld.h"
|
||||
|
|
@ -40,13 +40,21 @@ 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];
|
||||
const struct iwl_stats_ntfy_per_sta *per_sta;
|
||||
struct ieee80211_link_sta *link_sta;
|
||||
struct iwl_mld_link_sta *mld_link_sta;
|
||||
|
||||
if (iwl_fw_lookup_notif_ver(mld->fw, STATISTICS_GROUP,
|
||||
STATISTICS_OPER_NOTIF, 3) >= 4) {
|
||||
const struct iwl_system_statistics_notif_oper *notif =
|
||||
(void *)&pkt->data;
|
||||
per_sta = ¬if->per_sta[fw_sta_id];
|
||||
} else {
|
||||
const struct iwl_system_statistics_notif_oper_v3 *notif =
|
||||
(void *)&pkt->data;
|
||||
per_sta = ¬if->per_sta[fw_sta_id];
|
||||
}
|
||||
|
||||
/* 0 isn't a valid value, but FW might send 0.
|
||||
* In that case, set the latest non-zero value we stored
|
||||
*/
|
||||
|
|
@ -303,6 +311,40 @@ static void iwl_mld_sta_stats_fill_txrate(struct iwl_mld_sta *mld_sta,
|
|||
}
|
||||
}
|
||||
|
||||
static void iwl_mld_sta_stats_fill_beacon_signal_avg(struct ieee80211_vif *vif,
|
||||
struct station_info *sinfo)
|
||||
{
|
||||
struct ieee80211_bss_conf *link_conf;
|
||||
struct iwl_mld_link *link;
|
||||
u8 link_id;
|
||||
|
||||
if (iwl_mld_emlsr_active(vif))
|
||||
return;
|
||||
|
||||
/* TODO: support statistics for NAN */
|
||||
if (vif->type == NL80211_IFTYPE_NAN ||
|
||||
vif->type == NL80211_IFTYPE_NAN_DATA)
|
||||
return;
|
||||
|
||||
link_id = iwl_mld_get_primary_link(vif);
|
||||
link_conf = link_conf_dereference_protected(vif, link_id);
|
||||
|
||||
if (WARN_ONCE(!link_conf,
|
||||
"link_conf is NULL for link_id=%u\n", link_id))
|
||||
return;
|
||||
|
||||
link = iwl_mld_link_from_mac80211(link_conf);
|
||||
if (WARN_ONCE(!link,
|
||||
"iwl_mld_link is NULL for link_id=%u\n", link_id))
|
||||
return;
|
||||
|
||||
if (!link->avg_signal)
|
||||
return;
|
||||
|
||||
sinfo->rx_beacon_signal_avg = link->avg_signal;
|
||||
sinfo->filled |= BIT_ULL(NL80211_STA_INFO_BEACON_SIGNAL_AVG);
|
||||
}
|
||||
|
||||
void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw,
|
||||
struct ieee80211_vif *vif,
|
||||
struct ieee80211_sta *sta,
|
||||
|
|
@ -321,9 +363,9 @@ void iwl_mld_mac80211_sta_statistics(struct ieee80211_hw *hw,
|
|||
|
||||
iwl_mld_sta_stats_fill_txrate(mld_sta, sinfo);
|
||||
|
||||
/* TODO: NL80211_STA_INFO_BEACON_RX */
|
||||
iwl_mld_sta_stats_fill_beacon_signal_avg(vif, sinfo);
|
||||
|
||||
/* TODO: NL80211_STA_INFO_BEACON_SIGNAL_AVG */
|
||||
/* TODO: NL80211_STA_INFO_BEACON_RX */
|
||||
}
|
||||
|
||||
#define IWL_MLD_TRAFFIC_LOAD_MEDIUM_THRESH 10 /* percentage */
|
||||
|
|
@ -435,6 +477,8 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld,
|
|||
fw_id++) {
|
||||
const struct iwl_stats_ntfy_per_link *link_stats;
|
||||
struct ieee80211_bss_conf *bss_conf;
|
||||
struct iwl_mld_link *link;
|
||||
u32 avg_raw;
|
||||
int sig;
|
||||
|
||||
bss_conf = iwl_mld_fw_id_to_link_conf(mld, fw_id);
|
||||
|
|
@ -448,6 +492,13 @@ iwl_mld_process_per_link_stats(struct iwl_mld *mld,
|
|||
sig = -le32_to_cpu(link_stats->beacon_filter_average_energy);
|
||||
iwl_mld_update_link_sig(bss_conf->vif, sig, bss_conf);
|
||||
|
||||
link = iwl_mld_link_from_mac80211(bss_conf);
|
||||
if (WARN_ON_ONCE(!link))
|
||||
continue;
|
||||
|
||||
avg_raw = le32_to_cpu(link_stats->beacon_average_energy);
|
||||
link->avg_signal = clamp_t(int, -(int)avg_raw, S8_MIN, 0);
|
||||
|
||||
/* TODO: parse more fields here (task=statistics)*/
|
||||
}
|
||||
|
||||
|
|
@ -507,6 +558,10 @@ static void iwl_mld_fill_chanctx_stats(struct ieee80211_hw *hw,
|
|||
(old_load >> 1);
|
||||
}
|
||||
|
||||
IWL_DEBUG_EHT(phy->mld,
|
||||
"PHY %d: load_by_us=%u%% load_not_by_us=%u%%\n",
|
||||
phy->fw_id, phy->channel_load_by_us, new_load);
|
||||
|
||||
iwl_mld_emlsr_check_chan_load(hw, phy, old_load);
|
||||
}
|
||||
|
||||
|
|
@ -523,17 +578,42 @@ iwl_mld_process_per_phy_stats(struct iwl_mld *mld,
|
|||
void iwl_mld_handle_stats_oper_notif(struct iwl_mld *mld,
|
||||
struct iwl_rx_packet *pkt)
|
||||
{
|
||||
const struct iwl_system_statistics_notif_oper *stats =
|
||||
struct iwl_system_statistics_notif_oper *_notif __free(kfree) = NULL;
|
||||
const struct iwl_system_statistics_notif_oper *notif =
|
||||
(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) <
|
||||
BUILD_BUG_ON(ARRAY_SIZE(notif->per_sta) != IWL_STATION_COUNT_MAX);
|
||||
BUILD_BUG_ON(ARRAY_SIZE(notif->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);
|
||||
if (iwl_fw_lookup_notif_ver(mld->fw, STATISTICS_GROUP,
|
||||
STATISTICS_OPER_NOTIF, 3) == 3) {
|
||||
const struct iwl_system_statistics_notif_oper_v3 *stats =
|
||||
(void *)&pkt->data;
|
||||
_notif = kzalloc_obj(*_notif);
|
||||
|
||||
if (!_notif)
|
||||
return;
|
||||
|
||||
_notif->time_stamp = stats->time_stamp;
|
||||
for (int i = 0; i < ARRAY_SIZE(_notif->per_link); i++)
|
||||
_notif->per_link[i] = stats->per_link[i];
|
||||
|
||||
BUILD_BUG_ON(sizeof(_notif->per_phy[0]) <
|
||||
sizeof(stats->per_phy[0]));
|
||||
for (int i = 0; i < ARRAY_SIZE(_notif->per_phy); i++)
|
||||
memcpy(&_notif->per_phy[i], &stats->per_phy[i],
|
||||
sizeof(stats->per_phy[i]));
|
||||
for (int i = 0; i < ARRAY_SIZE(_notif->per_sta); i++)
|
||||
_notif->per_sta[i] = stats->per_sta[i];
|
||||
|
||||
notif = _notif;
|
||||
}
|
||||
|
||||
iwl_mld_process_per_link_stats(mld, notif->per_link,
|
||||
le32_to_cpu(notif->time_stamp));
|
||||
iwl_mld_process_per_sta_stats(mld, notif->per_sta);
|
||||
iwl_mld_process_per_phy_stats(mld, notif->per_phy);
|
||||
}
|
||||
|
||||
void iwl_mld_handle_stats_oper_part1_notif(struct iwl_mld *mld,
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user