--- a/drivers/net/wireless/ath/ath11k/core.h +++ b/drivers/net/wireless/ath/ath11k/core.h @@ -24,6 +24,7 @@ #include "db_ring.h" #include "spectral.h" #include "thermal.h" +#include "vendor.h" #define SM(_v, _f) (((_v) << _f##_LSB) & _f##_MASK) @@ -445,6 +446,11 @@ struct ath11k_coex_info { u32 pta_priority; }; +enum ath11k_ap_ps_state { + ATH11K_AP_PS_STATE_OFF, + ATH11K_AP_PS_STATE_ON, +}; + struct ath11k { struct ath11k_base *ab; struct ath11k_pdev *pdev; @@ -569,6 +575,8 @@ struct ath11k { bool dfs_block_radar_events; struct ath11k_thermal thermal; struct completion fw_mode_reset; + int ap_ps_enabled; + enum ath11k_ap_ps_state ap_ps_state; }; struct ath11k_band_cap { --- a/drivers/net/wireless/ath/ath11k/mac.c +++ b/drivers/net/wireless/ath/ath11k/mac.c @@ -2791,6 +2791,33 @@ static void ath11k_mac_dec_num_stations( ar->num_stations--; } +int ath11k_mac_ap_ps_recalc(struct ath11k *ar) +{ + struct ath11k_vif *arvif; + bool has_sta_iface = false; + enum ath11k_ap_ps_state state = ATH11K_AP_PS_STATE_OFF; + int ret = 0; + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->vdev_type == WMI_VDEV_TYPE_STA) { + has_sta_iface = true; + break; + } + } + + if (!has_sta_iface && !ar->num_stations && ar->ap_ps_enabled) + state = ATH11K_AP_PS_STATE_ON; + + if (ar->ap_ps_state == state) + return ret; + + ret = ath11k_wmi_pdev_ap_ps_cmd_send(ar, ar->pdev->pdev_id, state); + if (!ret) + ar->ap_ps_state = state; + + return ret; +} + static int ath11k_mac_station_add(struct ath11k *ar, struct ieee80211_vif *vif, struct ieee80211_sta *sta) @@ -2830,6 +2857,12 @@ static int ath11k_mac_station_add(struct ath11k_dbg(ab, ATH11K_DBG_MAC, "Added peer: %pM for VDEV: %d\n", sta->addr, arvif->vdev_id); + ret = ath11k_mac_ap_ps_recalc(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to send ap ps ret %d\n", ret); + goto exit; + } + if (ath11k_debug_is_extd_tx_stats_enabled(ar)) { arsta->tx_stats = kzalloc(sizeof(*arsta->tx_stats), GFP_KERNEL); if (!arsta->tx_stats) { @@ -2928,6 +2961,9 @@ static int ath11k_mac_op_sta_state(struc kfree(arsta->tx_stats); arsta->tx_stats = NULL; + ret = ath11k_mac_ap_ps_recalc(ar); + if (ret) + ath11k_warn(ar->ab, "failed to send ap ps ret %d\n", ret); kfree(arsta->rx_stats); arsta->rx_stats = NULL; @@ -4030,6 +4066,7 @@ static void ath11k_mac_op_stop(struct ie clear_bit(ATH11K_CAC_RUNNING, &ar->dev_flags); ar->state = ATH11K_STATE_OFF; + ar->ap_ps_state = ATH11K_AP_PS_STATE_OFF; mutex_unlock(&ar->conf_mutex); cancel_delayed_work_sync(&ar->scan.timeout); @@ -4225,7 +4262,6 @@ static int ath11k_mac_op_add_interface(s arvif->vdev_id, ret); goto err; } - ar->num_created_vdevs++; ar->allocated_vdev_map |= 1LL << arvif->vdev_id; ab->free_vdev_map &= ~(1LL << arvif->vdev_id); @@ -4334,6 +4370,10 @@ static int ath11k_mac_op_add_interface(s ath11k_dp_vdev_tx_attach(ar, arvif); + ret = ath11k_mac_ap_ps_recalc(ar); + if (ret) + ath11k_warn(ar->ab, "failed to set ap ps ret %d\n", ret); + mutex_unlock(&ar->conf_mutex); return 0; @@ -4418,6 +4458,7 @@ static void ath11k_mac_op_remove_interfa /* Recalc txpower for remaining vdev */ ath11k_mac_txpower_recalc(ar); + ath11k_mac_ap_ps_recalc(ar); clear_bit(ATH11K_FLAG_MONITOR_ENABLED, &ar->monitor_flags); /* TODO: recal traffic pause state based on the available vdevs */ --- a/drivers/net/wireless/ath/ath11k/mac.h +++ b/drivers/net/wireless/ath/ath11k/mac.h @@ -118,6 +118,7 @@ struct ath11k_generic_iter { extern const struct htt_rx_ring_tlv_filter ath11k_mac_mon_status_filter_default; +int ath11k_mac_ap_ps_recalc(struct ath11k *ar); void ath11k_mac_destroy(struct ath11k_base *ab); void ath11k_mac_unregister(struct ath11k_base *ab); int ath11k_mac_register(struct ath11k_base *ab); --- a/drivers/net/wireless/ath/ath11k/vendor.c +++ b/drivers/net/wireless/ath/ath11k/vendor.c @@ -6,7 +6,6 @@ #include #include #include "core.h" -#include "vendor.h" #include "debug.h" static const struct nla_policy @@ -21,6 +20,11 @@ ath11k_vendor_wlan_prio_policy[QCA_WLAN_ [QCA_WLAN_VENDOR_ATTR_WLAN_PRIO_WEIGHT] = { .type = NLA_U8 }, }; +static const struct nla_policy +ath11k_vendor_set_wifi_config_policy[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1] = { + [QCA_WLAN_VENDOR_ATTR_CONFIG_GTX] = {.type = NLA_FLAG} +}; + static int ath11k_vendor_btcoex_configure(struct wiphy *wiphy, struct wireless_dev *wdev, const void *data, @@ -101,6 +105,51 @@ out: return ret; } +static int ath11k_vendor_set_wifi_config(struct wiphy *wihpy, + struct wireless_dev *wdev, + const void *data, + int data_len) +{ + struct ieee80211_vif *vif; + struct ath11k_vif *arvif; + struct ath11k *ar; + struct nlattr *tb[QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + 1]; + int ret = 0; + + if (!wdev) + return -EINVAL; + + vif = wdev_to_ieee80211_vif(wdev); + if (!vif) + return -EINVAL; + + arvif = (struct ath11k_vif*)vif->drv_priv; + if (!arvif) + return -EINVAL; + + ar = arvif->ar; + + mutex_lock(&ar->conf_mutex); + + ret = nla_parse(tb, QCA_WLAN_VENDOR_ATTR_CONFIG_MAX, data, data_len, + ath11k_vendor_set_wifi_config_policy, NULL); + if (ret) { + ath11k_warn(ar->ab, "invalid set wifi config policy attribute\n"); + goto exit; + } + + ar->ap_ps_enabled = nla_get_flag(tb[QCA_WLAN_VENDOR_ATTR_CONFIG_GTX]); + ret = ath11k_mac_ap_ps_recalc(ar); + if (ret) { + ath11k_warn(ar->ab, "failed to send ap ps ret %d\n", ret); + goto exit; + } + +exit: + mutex_unlock(&ar->conf_mutex); + return ret; +} + static struct wiphy_vendor_command ath11k_vendor_commands[] = { { .info.vendor_id = QCA_NL80211_VENDOR_ID, @@ -108,8 +157,18 @@ static struct wiphy_vendor_command ath11 .flags = WIPHY_VENDOR_CMD_NEED_WDEV | WIPHY_VENDOR_CMD_NEED_RUNNING, .doit = ath11k_vendor_btcoex_configure, - .policy = ath11k_vendor_btcoex_config_policy - } + .policy = ath11k_vendor_btcoex_config_policy, + .maxattr = QCA_WLAN_VENDOR_ATTR_BTCOEX_CONFIG_MAX + }, + { + .info.vendor_id = QCA_NL80211_VENDOR_ID, + .info.subcmd = QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION, + .flags = WIPHY_VENDOR_CMD_NEED_WDEV | + WIPHY_VENDOR_CMD_NEED_RUNNING, + .doit = ath11k_vendor_set_wifi_config, + .policy = ath11k_vendor_set_wifi_config_policy, + .maxattr = QCA_WLAN_VENDOR_ATTR_CONFIG_MAX + }, }; int ath11k_vendor_register(struct ath11k *ar) --- a/drivers/net/wireless/ath/ath11k/vendor.h +++ b/drivers/net/wireless/ath/ath11k/vendor.h @@ -9,6 +9,9 @@ #define QCA_NL80211_VENDOR_ID 0x001374 enum qca_nl80211_vendor_subcmds { + /* Wi-Fi configuration subcommand */ + QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION = 74, + /* QCA_NL80211_VENDOR_SUBCMD_BTCOEX_CONFIG: This command is used to * enable/disable BTCOEX and set priority for different type of WLAN * traffic over BT low priority traffic. This uses attributes in @@ -58,7 +61,17 @@ enum qca_wlan_vendor_attr_wlan_prio { QCA_WLAN_VENDOR_ATTR_WLAN_PRIO_LAST - 1, }; +/* Attributes for data used by + * QCA_NL80211_VENDOR_SUBCMD_SET_WIFI_CONFIGURATION + */ +enum qca_wlan_vendor_attr_config { + QCA_WLAN_VENDOR_ATTR_CONFIG_GTX = 57, + /* keep last */ + QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST, + QCA_WLAN_VENDOR_ATTR_CONFIG_MAX = + QCA_WLAN_VENDOR_ATTR_CONFIG_AFTER_LAST - 1, +}; /** * enum qca_wlan_vendor_attr_btcoex_config - Used by the vendor command --- a/drivers/net/wireless/ath/ath11k/wmi.c +++ b/drivers/net/wireless/ath/ath11k/wmi.c @@ -1174,6 +1174,38 @@ ath11k_wmi_rx_reord_queue_remove(struct return ret; } +int ath11k_wmi_pdev_ap_ps_cmd_send(struct ath11k *ar, u8 pdev_id, + u32 param_value) +{ + struct ath11k_pdev_wmi *wmi = ar->wmi; + struct wmi_pdev_ap_ps_cmd *cmd; + struct sk_buff *skb; + int ret; + + skb = ath11k_wmi_alloc_skb(wmi->wmi_ab, sizeof(*cmd)); + if (!skb) + return -ENOMEM; + + cmd = (struct wmi_pdev_ap_ps_cmd *)skb->data; + cmd->tlv_header = FIELD_PREP(WMI_TLV_TAG, + WMI_TAG_PDEV_GREEN_AP_PS_ENABLE_CMD) | + FIELD_PREP(WMI_TLV_LEN, sizeof(*cmd) - TLV_HDR_SIZE); + cmd->pdev_id = pdev_id; + cmd->param_value = param_value; + + ret = ath11k_wmi_cmd_send(wmi, skb, WMI_PDEV_GREEN_AP_PS_ENABLE_CMDID); + if (ret) { + ath11k_warn(ar->ab, "failed to send ap ps enable/disable cmd\n"); + dev_kfree_skb(skb); + } + + ath11k_dbg(ar->ab, ATH11K_DBG_WMI, + "wmi pdev ap ps set pdev id %d value %d\n", + pdev_id, param_value); + + return ret; +} + int ath11k_wmi_pdev_set_param(struct ath11k *ar, u32 param_id, u32 param_value, u8 pdev_id) { --- a/drivers/net/wireless/ath/ath11k/wmi.h +++ b/drivers/net/wireless/ath/ath11k/wmi.h @@ -2845,6 +2845,12 @@ struct set_fwtest_params { u32 value; }; +struct wmi_pdev_ap_ps_cmd { + u32 tlv_header; + u32 pdev_id; + u32 param_value; +} __packed; + struct wmi_fwtest_set_param_cmd_param { u32 tlv_header; u32 param_id; @@ -5079,6 +5085,7 @@ int ath11k_wmi_vdev_spectral_conf(struct struct ath11k_wmi_vdev_spectral_conf_param *param); int ath11k_send_coex_config_cmd(struct ath11k *ar, struct coex_config_arg *coex_config); +int ath11k_wmi_pdev_ap_ps_cmd_send(struct ath11k *ar, u8 pdev_id, u32 value); int ath11k_wmi_send_obss_color_collision_cfg_cmd(struct ath11k *ar, u32 vdev_id, u8 bss_color, u32 period, bool enable);