diff --git a/drivers/net/wireless/ath/ath10k/core.h b/drivers/net/wireless/ath/ath10k/core.h index f194d434b97c..2ef2e1ec040a 100644 --- a/drivers/net/wireless/ath/ath10k/core.h +++ b/drivers/net/wireless/ath/ath10k/core.h @@ -433,6 +433,7 @@ struct ath10k_vif { struct cfg80211_bitrate_mask bitrate_mask; struct wmi_ns_arp_offload_req arp_offload; struct wmi_ns_arp_offload_req ns_offload; + struct wmi_gtk_rekey_data gtk_rekey_data; }; struct ath10k_vif_iter { diff --git a/drivers/net/wireless/ath/ath10k/mac.c b/drivers/net/wireless/ath/ath10k/mac.c index 9fcc2866d830..16a5c5fd3925 100644 --- a/drivers/net/wireless/ath/ath10k/mac.c +++ b/drivers/net/wireless/ath/ath10k/mac.c @@ -7583,6 +7583,7 @@ static const struct ieee80211_ops ath10k_ops = { .suspend = ath10k_wow_op_suspend, .resume = ath10k_wow_op_resume, .set_wakeup = ath10k_wow_op_set_wakeup, + .set_rekey_data = ath10k_wow_op_set_rekey_data, #endif #ifdef CONFIG_MAC80211_DEBUGFS .sta_add_debugfs = ath10k_sta_add_debugfs, diff --git a/drivers/net/wireless/ath/ath10k/wmi-ops.h b/drivers/net/wireless/ath/ath10k/wmi-ops.h index dcb0da51530a..cf738efd45c5 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-ops.h +++ b/drivers/net/wireless/ath/ath10k/wmi-ops.h @@ -162,6 +162,8 @@ struct wmi_ops { const struct wmi_sta_keepalive_arg *arg); struct sk_buff *(*gen_set_arp_ns_offload)(struct ath10k *ar, struct ath10k_vif *arvif); + struct sk_buff *(*gen_gtk_offload)(struct ath10k *ar, + struct ath10k_vif *arvif); struct sk_buff *(*gen_wow_enable)(struct ath10k *ar); struct sk_buff *(*gen_wow_add_wakeup_event)(struct ath10k *ar, u32 vdev_id, enum wmi_wow_wakeup_event event, @@ -1205,6 +1207,23 @@ ath10k_wmi_set_arp_ns_offload(struct ath10k *ar, struct ath10k_vif *arvif) return ath10k_wmi_cmd_send(ar, skb, cmd_id); } +static inline int +ath10k_wmi_gtk_offload(struct ath10k *ar, struct ath10k_vif *arvif) +{ + struct sk_buff *skb; + u32 cmd_id; + + if (!ar->wmi.ops->gen_gtk_offload) + return -EOPNOTSUPP; + + skb = ar->wmi.ops->gen_gtk_offload(ar, arvif); + if (IS_ERR(skb)) + return PTR_ERR(skb); + + cmd_id = ar->wmi.cmd->gtk_offload_cmdid; + return ath10k_wmi_cmd_send(ar, skb, cmd_id); +} + static inline int ath10k_wmi_wow_enable(struct ath10k *ar) { diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.c b/drivers/net/wireless/ath/ath10k/wmi-tlv.c index ad27abc61deb..5ce4fdfca724 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.c +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.c @@ -3014,6 +3014,40 @@ ath10k_wmi_tlv_op_gen_tdls_peer_update(struct ath10k *ar, return skb; } +static struct sk_buff * +ath10k_wmi_op_gen_gtk_offload(struct ath10k *ar, struct ath10k_vif *arvif) +{ + struct wmi_tlv_gtk_offload_cmd *cmd; + struct wmi_tlv *tlv; + struct sk_buff *skb; + struct wmi_gtk_rekey_data *rekey_data = &arvif->gtk_rekey_data; + int len; + + len = sizeof(*cmd) + sizeof(*tlv); + skb = ath10k_wmi_alloc_skb(ar, len); + if (!skb) + return ERR_PTR(-ENOMEM); + + tlv = (void *)skb->data; + tlv->tag = __cpu_to_le16(WMI_TLV_TAG_STRUCT_GTK_OFFLOAD_CMD); + tlv->len = __cpu_to_le16(sizeof(*cmd)); + cmd = (void *)tlv->value; + if (rekey_data->enable_offload) { + cmd->vdev_id = __cpu_to_le32(arvif->vdev_id); + cmd->flags |= __cpu_to_le32(WMI_GTK_OFFLOAD_ENABLE_OPCODE); + memcpy(cmd->kek, rekey_data->kek, NL80211_KEK_LEN); + memcpy(cmd->kck, rekey_data->kck, NL80211_KCK_LEN); + cmd->replay_ctr = __cpu_to_le64(rekey_data->replay_ctr); + } else { + cmd->vdev_id = __cpu_to_le32(arvif->vdev_id); + cmd->flags |= __cpu_to_le32(WMI_GTK_OFFLOAD_DISABLE_OPCODE); + } + + ath10k_dbg(ar, ATH10K_DBG_WMI, + "wmi GTK offload for vdev: %d\n", arvif->vdev_id); + return skb; +} + static struct sk_buff * ath10k_wmi_tlv_op_gen_set_arp_ns_offload(struct ath10k *ar, struct ath10k_vif *arvif) @@ -3778,6 +3812,7 @@ static const struct wmi_ops wmi_tlv_ops = { .gen_vdev_sta_uapsd = ath10k_wmi_tlv_op_gen_vdev_sta_uapsd, .gen_sta_keepalive = ath10k_wmi_tlv_op_gen_sta_keepalive, .gen_set_arp_ns_offload = ath10k_wmi_tlv_op_gen_set_arp_ns_offload, + .gen_gtk_offload = ath10k_wmi_op_gen_gtk_offload, .gen_wow_enable = ath10k_wmi_tlv_op_gen_wow_enable, .gen_wow_add_wakeup_event = ath10k_wmi_tlv_op_gen_wow_add_wakeup_event, .gen_wow_host_wakeup_ind = ath10k_wmi_tlv_gen_wow_host_wakeup_ind, diff --git a/drivers/net/wireless/ath/ath10k/wmi-tlv.h b/drivers/net/wireless/ath/ath10k/wmi-tlv.h index 39951d273244..18327daade8d 100644 --- a/drivers/net/wireless/ath/ath10k/wmi-tlv.h +++ b/drivers/net/wireless/ath/ath10k/wmi-tlv.h @@ -1567,6 +1567,14 @@ struct wmi_tlv_arp_ns_offload_cmd { __le32 num_ns_ext_tuples; } __packed; +struct wmi_tlv_gtk_offload_cmd { + __le32 vdev_id; + __le32 flags; + u8 kek[NL80211_KEK_LEN]; + u8 kck[NL80211_KCK_LEN]; + __le64 replay_ctr; +} __packed; + struct wmi_tlv_wow_host_wakeup_ind { __le32 reserved; } __packed; diff --git a/drivers/net/wireless/ath/ath10k/wmi.h b/drivers/net/wireless/ath/ath10k/wmi.h index 44c237eec157..2694b6aa8b77 100644 --- a/drivers/net/wireless/ath/ath10k/wmi.h +++ b/drivers/net/wireless/ath/ath10k/wmi.h @@ -2933,6 +2933,20 @@ struct wmi_arp_offload { struct wmi_mac_addr target_mac; } __packed; +/* GTK offload data structure */ +#define WMI_GTK_OFFLOAD_ENABLE_OPCODE BIT(24) +#define WMI_GTK_OFFLOAD_DISABLE_OPCODE BIT(25) +#define WMI_GTK_OFFLOAD_ENABLE 1 +#define WMI_GTK_OFFLOAD_DISABLE 0 + +struct wmi_gtk_rekey_data { + bool valid; + bool enable_offload; + u8 kck[NL80211_KCK_LEN]; + u8 kek[NL80211_KEK_LEN]; + __le64 replay_ctr; +} __packed; + struct wmi_start_scan_tlvs { /* TLV parameters. These includes channel list, ssid list, bssid list, * extra ies. diff --git a/drivers/net/wireless/ath/ath10k/wow.c b/drivers/net/wireless/ath/ath10k/wow.c index 429fcce3dd1e..6bbcf8b79d9a 100644 --- a/drivers/net/wireless/ath/ath10k/wow.c +++ b/drivers/net/wireless/ath/ath10k/wow.c @@ -320,6 +320,56 @@ static int ath10k_config_wow_listen_interval(struct ath10k *ar) return 0; } +void ath10k_wow_op_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data) +{ + struct ath10k *ar = hw->priv; + struct ath10k_vif *arvif = ath10k_vif_to_arvif(vif); + + mutex_lock(&ar->conf_mutex); + memcpy(&arvif->gtk_rekey_data.kek, data->kek, NL80211_KEK_LEN); + memcpy(&arvif->gtk_rekey_data.kck, data->kck, NL80211_KCK_LEN); + arvif->gtk_rekey_data.replay_ctr = + __cpu_to_le64(*(__le64 *)data->replay_ctr); + arvif->gtk_rekey_data.valid = true; + mutex_unlock(&ar->conf_mutex); +} + +static int ath10k_wow_config_gtk_offload(struct ath10k *ar, bool gtk_offload) +{ + struct ath10k_vif *arvif; + struct ieee80211_bss_conf *bss; + struct wmi_gtk_rekey_data *rekey_data; + int ret; + + list_for_each_entry(arvif, &ar->arvifs, list) { + if (arvif->vdev_type != WMI_VDEV_TYPE_STA) + continue; + + bss = &arvif->vif->bss_conf; + if (!arvif->is_up || !bss->assoc) + continue; + + rekey_data = &arvif->gtk_rekey_data; + if (!rekey_data->valid) + continue; + + if (gtk_offload) + rekey_data->enable_offload = WMI_GTK_OFFLOAD_ENABLE; + else + rekey_data->enable_offload = WMI_GTK_OFFLOAD_DISABLE; + ret = ath10k_wmi_gtk_offload(ar, arvif); + if (ret) { + ath10k_err(ar, "GTK offload failed for vdev_id: %d\n", + arvif->vdev_id); + return ret; + } + } + + return 0; +} + int ath10k_wow_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) { @@ -334,10 +384,16 @@ int ath10k_wow_op_suspend(struct ieee80211_hw *hw, goto exit; } + ret = ath10k_wow_config_gtk_offload(ar, true); + if (ret) { + ath10k_warn(ar, "failed to enable GTK offload: %d\n", ret); + goto exit; + } + ret = ath10k_wow_enable_ns_arp_offload(ar, true); if (ret) { ath10k_warn(ar, "failed to enable ARP-NS offload: %d\n", ret); - goto exit; + goto disable_gtk_offload; } ret = ath10k_wow_cleanup(ar); @@ -384,6 +440,8 @@ cleanup: disable_ns_arp_offload: ath10k_wow_enable_ns_arp_offload(ar, false); +disable_gtk_offload: + ath10k_wow_config_gtk_offload(ar, false); exit: mutex_unlock(&ar->conf_mutex); return ret ? 1 : 0; @@ -427,8 +485,14 @@ int ath10k_wow_op_resume(struct ieee80211_hw *hw) } ret = ath10k_wow_enable_ns_arp_offload(ar, false); - if (ret) + if (ret) { ath10k_warn(ar, "failed to disable ARP-NS offload: %d\n", ret); + goto exit; + } + + ret = ath10k_wow_config_gtk_offload(ar, false); + if (ret) + ath10k_warn(ar, "failed to disable GTK offload: %d\n", ret); exit: if (ret) { diff --git a/drivers/net/wireless/ath/ath10k/wow.h b/drivers/net/wireless/ath/ath10k/wow.h index 9745b9ddc7f5..ce79908cce19 100644 --- a/drivers/net/wireless/ath/ath10k/wow.h +++ b/drivers/net/wireless/ath/ath10k/wow.h @@ -29,7 +29,9 @@ int ath10k_wow_op_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan); int ath10k_wow_op_resume(struct ieee80211_hw *hw); void ath10k_wow_op_set_wakeup(struct ieee80211_hw *hw, bool enabled); - +void ath10k_wow_op_set_rekey_data(struct ieee80211_hw *hw, + struct ieee80211_vif *vif, + struct cfg80211_gtk_rekey_data *data); #else static inline int ath10k_wow_init(struct ath10k *ar)