diff options
Diffstat (limited to 'drivers/net/wireless/iwlwifi/mvm/d3.c')
| -rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/d3.c | 515 |
1 files changed, 476 insertions, 39 deletions
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 417639f77b01..6f45966817bb 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c | |||
| @@ -67,6 +67,7 @@ | |||
| 67 | #include <net/cfg80211.h> | 67 | #include <net/cfg80211.h> |
| 68 | #include <net/ipv6.h> | 68 | #include <net/ipv6.h> |
| 69 | #include <net/tcp.h> | 69 | #include <net/tcp.h> |
| 70 | #include <net/addrconf.h> | ||
| 70 | #include "iwl-modparams.h" | 71 | #include "iwl-modparams.h" |
| 71 | #include "fw-api.h" | 72 | #include "fw-api.h" |
| 72 | #include "mvm.h" | 73 | #include "mvm.h" |
| @@ -381,14 +382,74 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, | |||
| 381 | union { | 382 | union { |
| 382 | struct iwl_proto_offload_cmd_v1 v1; | 383 | struct iwl_proto_offload_cmd_v1 v1; |
| 383 | struct iwl_proto_offload_cmd_v2 v2; | 384 | struct iwl_proto_offload_cmd_v2 v2; |
| 385 | struct iwl_proto_offload_cmd_v3_small v3s; | ||
| 386 | struct iwl_proto_offload_cmd_v3_large v3l; | ||
| 384 | } cmd = {}; | 387 | } cmd = {}; |
| 388 | struct iwl_host_cmd hcmd = { | ||
| 389 | .id = PROT_OFFLOAD_CONFIG_CMD, | ||
| 390 | .flags = CMD_SYNC, | ||
| 391 | .data[0] = &cmd, | ||
| 392 | .dataflags[0] = IWL_HCMD_DFL_DUP, | ||
| 393 | }; | ||
| 385 | struct iwl_proto_offload_cmd_common *common; | 394 | struct iwl_proto_offload_cmd_common *common; |
| 386 | u32 enabled = 0, size; | 395 | u32 enabled = 0, size; |
| 396 | u32 capa_flags = mvm->fw->ucode_capa.flags; | ||
| 387 | #if IS_ENABLED(CONFIG_IPV6) | 397 | #if IS_ENABLED(CONFIG_IPV6) |
| 388 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | 398 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); |
| 389 | int i; | 399 | int i; |
| 390 | 400 | ||
| 391 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { | 401 | if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || |
| 402 | capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { | ||
| 403 | struct iwl_ns_config *nsc; | ||
| 404 | struct iwl_targ_addr *addrs; | ||
| 405 | int n_nsc, n_addrs; | ||
| 406 | int c; | ||
| 407 | |||
| 408 | if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { | ||
| 409 | nsc = cmd.v3s.ns_config; | ||
| 410 | n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; | ||
| 411 | addrs = cmd.v3s.targ_addrs; | ||
| 412 | n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; | ||
| 413 | } else { | ||
| 414 | nsc = cmd.v3l.ns_config; | ||
| 415 | n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; | ||
| 416 | addrs = cmd.v3l.targ_addrs; | ||
| 417 | n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; | ||
| 418 | } | ||
| 419 | |||
| 420 | if (mvmvif->num_target_ipv6_addrs) | ||
| 421 | enabled |= IWL_D3_PROTO_OFFLOAD_NS; | ||
| 422 | |||
| 423 | /* | ||
| 424 | * For each address we have (and that will fit) fill a target | ||
| 425 | * address struct and combine for NS offload structs with the | ||
| 426 | * solicited node addresses. | ||
| 427 | */ | ||
| 428 | for (i = 0, c = 0; | ||
| 429 | i < mvmvif->num_target_ipv6_addrs && | ||
| 430 | i < n_addrs && c < n_nsc; i++) { | ||
| 431 | struct in6_addr solicited_addr; | ||
| 432 | int j; | ||
| 433 | |||
| 434 | addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], | ||
| 435 | &solicited_addr); | ||
| 436 | for (j = 0; j < c; j++) | ||
| 437 | if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, | ||
| 438 | &solicited_addr) == 0) | ||
| 439 | break; | ||
| 440 | if (j == c) | ||
| 441 | c++; | ||
| 442 | addrs[i].addr = mvmvif->target_ipv6_addrs[i]; | ||
| 443 | addrs[i].config_num = cpu_to_le32(j); | ||
| 444 | nsc[j].dest_ipv6_addr = solicited_addr; | ||
| 445 | memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); | ||
| 446 | } | ||
| 447 | |||
| 448 | if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) | ||
| 449 | cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); | ||
| 450 | else | ||
| 451 | cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); | ||
| 452 | } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { | ||
| 392 | if (mvmvif->num_target_ipv6_addrs) { | 453 | if (mvmvif->num_target_ipv6_addrs) { |
| 393 | enabled |= IWL_D3_PROTO_OFFLOAD_NS; | 454 | enabled |= IWL_D3_PROTO_OFFLOAD_NS; |
| 394 | memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); | 455 | memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); |
| @@ -419,7 +480,13 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, | |||
| 419 | } | 480 | } |
| 420 | #endif | 481 | #endif |
| 421 | 482 | ||
| 422 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { | 483 | if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { |
| 484 | common = &cmd.v3s.common; | ||
| 485 | size = sizeof(cmd.v3s); | ||
| 486 | } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { | ||
| 487 | common = &cmd.v3l.common; | ||
| 488 | size = sizeof(cmd.v3l); | ||
| 489 | } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { | ||
| 423 | common = &cmd.v2.common; | 490 | common = &cmd.v2.common; |
| 424 | size = sizeof(cmd.v2); | 491 | size = sizeof(cmd.v2); |
| 425 | } else { | 492 | } else { |
| @@ -438,8 +505,8 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, | |||
| 438 | 505 | ||
| 439 | common->enabled = cpu_to_le32(enabled); | 506 | common->enabled = cpu_to_le32(enabled); |
| 440 | 507 | ||
| 441 | return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC, | 508 | hcmd.len[0] = size; |
| 442 | size, &cmd); | 509 | return iwl_mvm_send_cmd(mvm, &hcmd); |
| 443 | } | 510 | } |
| 444 | 511 | ||
| 445 | enum iwl_mvm_tcp_packet_type { | 512 | enum iwl_mvm_tcp_packet_type { |
| @@ -793,6 +860,74 @@ static int iwl_mvm_d3_reprogram(struct iwl_mvm *mvm, struct ieee80211_vif *vif, | |||
| 793 | return 0; | 860 | return 0; |
| 794 | } | 861 | } |
| 795 | 862 | ||
| 863 | static int iwl_mvm_get_last_nonqos_seq(struct iwl_mvm *mvm, | ||
| 864 | struct ieee80211_vif *vif) | ||
| 865 | { | ||
| 866 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
| 867 | struct iwl_nonqos_seq_query_cmd query_cmd = { | ||
| 868 | .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_GET), | ||
| 869 | .mac_id_n_color = | ||
| 870 | cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, | ||
| 871 | mvmvif->color)), | ||
| 872 | }; | ||
| 873 | struct iwl_host_cmd cmd = { | ||
| 874 | .id = NON_QOS_TX_COUNTER_CMD, | ||
| 875 | .flags = CMD_SYNC | CMD_WANT_SKB, | ||
| 876 | }; | ||
| 877 | int err; | ||
| 878 | u32 size; | ||
| 879 | |||
| 880 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) { | ||
| 881 | cmd.data[0] = &query_cmd; | ||
| 882 | cmd.len[0] = sizeof(query_cmd); | ||
| 883 | } | ||
| 884 | |||
| 885 | err = iwl_mvm_send_cmd(mvm, &cmd); | ||
| 886 | if (err) | ||
| 887 | return err; | ||
| 888 | |||
| 889 | size = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; | ||
| 890 | size -= sizeof(cmd.resp_pkt->hdr); | ||
| 891 | if (size < sizeof(__le16)) { | ||
| 892 | err = -EINVAL; | ||
| 893 | } else { | ||
| 894 | err = le16_to_cpup((__le16 *)cmd.resp_pkt->data); | ||
| 895 | /* new API returns next, not last-used seqno */ | ||
| 896 | if (mvm->fw->ucode_capa.flags & | ||
| 897 | IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) | ||
| 898 | err -= 0x10; | ||
| 899 | } | ||
| 900 | |||
| 901 | iwl_free_resp(&cmd); | ||
| 902 | return err; | ||
| 903 | } | ||
| 904 | |||
| 905 | void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) | ||
| 906 | { | ||
| 907 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
| 908 | struct iwl_nonqos_seq_query_cmd query_cmd = { | ||
| 909 | .get_set_flag = cpu_to_le32(IWL_NONQOS_SEQ_SET), | ||
| 910 | .mac_id_n_color = | ||
| 911 | cpu_to_le32(FW_CMD_ID_AND_COLOR(mvmvif->id, | ||
| 912 | mvmvif->color)), | ||
| 913 | .value = cpu_to_le16(mvmvif->seqno), | ||
| 914 | }; | ||
| 915 | |||
| 916 | /* return if called during restart, not resume from D3 */ | ||
| 917 | if (!mvmvif->seqno_valid) | ||
| 918 | return; | ||
| 919 | |||
| 920 | mvmvif->seqno_valid = false; | ||
| 921 | |||
| 922 | if (!(mvm->fw->ucode_capa.flags & | ||
| 923 | IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API)) | ||
| 924 | return; | ||
| 925 | |||
| 926 | if (iwl_mvm_send_cmd_pdu(mvm, NON_QOS_TX_COUNTER_CMD, CMD_SYNC, | ||
| 927 | sizeof(query_cmd), &query_cmd)) | ||
| 928 | IWL_ERR(mvm, "failed to set non-QoS seqno\n"); | ||
| 929 | } | ||
| 930 | |||
| 796 | static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | 931 | static int __iwl_mvm_suspend(struct ieee80211_hw *hw, |
| 797 | struct cfg80211_wowlan *wowlan, | 932 | struct cfg80211_wowlan *wowlan, |
| 798 | bool test) | 933 | bool test) |
| @@ -829,7 +964,6 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
| 829 | }; | 964 | }; |
| 830 | int ret, i; | 965 | int ret, i; |
| 831 | int len __maybe_unused; | 966 | int len __maybe_unused; |
| 832 | u16 seq; | ||
| 833 | u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT; | 967 | u8 old_aux_sta_id, old_ap_sta_id = IWL_MVM_STATION_COUNT; |
| 834 | 968 | ||
| 835 | if (!wowlan) { | 969 | if (!wowlan) { |
| @@ -872,26 +1006,15 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
| 872 | 1006 | ||
| 873 | mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; | 1007 | mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; |
| 874 | 1008 | ||
| 875 | /* | ||
| 876 | * The D3 firmware still hardcodes the AP station ID for the | ||
| 877 | * BSS we're associated with as 0. Store the real STA ID here | ||
| 878 | * and assign 0. When we leave this function, we'll restore | ||
| 879 | * the original value for the resume code. | ||
| 880 | */ | ||
| 881 | old_ap_sta_id = mvm_ap_sta->sta_id; | ||
| 882 | mvm_ap_sta->sta_id = 0; | ||
| 883 | mvmvif->ap_sta_id = 0; | ||
| 884 | |||
| 885 | /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */ | 1009 | /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */ |
| 886 | 1010 | ||
| 887 | wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported; | 1011 | wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported; |
| 888 | 1012 | ||
| 889 | /* | 1013 | /* Query the last used seqno and set it */ |
| 890 | * We know the last used seqno, and the uCode expects to know that | 1014 | ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); |
| 891 | * one, it will increment before TX. | 1015 | if (ret < 0) |
| 892 | */ | 1016 | goto out_noreset; |
| 893 | seq = mvm_ap_sta->last_seq_ctl & IEEE80211_SCTL_SEQ; | 1017 | wowlan_config_cmd.non_qos_seq = cpu_to_le16(ret); |
| 894 | wowlan_config_cmd.non_qos_seq = cpu_to_le16(seq); | ||
| 895 | 1018 | ||
| 896 | /* | 1019 | /* |
| 897 | * For QoS counters, we store the one to use next, so subtract 0x10 | 1020 | * For QoS counters, we store the one to use next, so subtract 0x10 |
| @@ -899,7 +1022,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
| 899 | * increment after using the value (i.e. store the next value to use). | 1022 | * increment after using the value (i.e. store the next value to use). |
| 900 | */ | 1023 | */ |
| 901 | for (i = 0; i < IWL_MAX_TID_COUNT; i++) { | 1024 | for (i = 0; i < IWL_MAX_TID_COUNT; i++) { |
| 902 | seq = mvm_ap_sta->tid_data[i].seq_number; | 1025 | u16 seq = mvm_ap_sta->tid_data[i].seq_number; |
| 903 | seq -= 0x10; | 1026 | seq -= 0x10; |
| 904 | wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq); | 1027 | wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq); |
| 905 | } | 1028 | } |
| @@ -945,6 +1068,16 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
| 945 | iwl_trans_stop_device(mvm->trans); | 1068 | iwl_trans_stop_device(mvm->trans); |
| 946 | 1069 | ||
| 947 | /* | 1070 | /* |
| 1071 | * The D3 firmware still hardcodes the AP station ID for the | ||
| 1072 | * BSS we're associated with as 0. Store the real STA ID here | ||
| 1073 | * and assign 0. When we leave this function, we'll restore | ||
| 1074 | * the original value for the resume code. | ||
| 1075 | */ | ||
| 1076 | old_ap_sta_id = mvm_ap_sta->sta_id; | ||
| 1077 | mvm_ap_sta->sta_id = 0; | ||
| 1078 | mvmvif->ap_sta_id = 0; | ||
| 1079 | |||
| 1080 | /* | ||
| 948 | * Set the HW restart bit -- this is mostly true as we're | 1081 | * Set the HW restart bit -- this is mostly true as we're |
| 949 | * going to load new firmware and reprogram that, though | 1082 | * going to load new firmware and reprogram that, though |
| 950 | * the reprogramming is going to be manual to avoid adding | 1083 | * the reprogramming is going to be manual to avoid adding |
| @@ -1059,6 +1192,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
| 1059 | if (ret) | 1192 | if (ret) |
| 1060 | goto out; | 1193 | goto out; |
| 1061 | 1194 | ||
| 1195 | ret = iwl_mvm_power_update_device_mode(mvm); | ||
| 1196 | if (ret) | ||
| 1197 | goto out; | ||
| 1198 | |||
| 1062 | ret = iwl_mvm_power_update_mode(mvm, vif); | 1199 | ret = iwl_mvm_power_update_mode(mvm, vif); |
| 1063 | if (ret) | 1200 | if (ret) |
| 1064 | goto out; | 1201 | goto out; |
| @@ -1109,16 +1246,26 @@ int iwl_mvm_suspend(struct ieee80211_hw *hw, struct cfg80211_wowlan *wowlan) | |||
| 1109 | return __iwl_mvm_suspend(hw, wowlan, false); | 1246 | return __iwl_mvm_suspend(hw, wowlan, false); |
| 1110 | } | 1247 | } |
| 1111 | 1248 | ||
| 1249 | /* converted data from the different status responses */ | ||
| 1250 | struct iwl_wowlan_status_data { | ||
| 1251 | u16 pattern_number; | ||
| 1252 | u16 qos_seq_ctr[8]; | ||
| 1253 | u32 wakeup_reasons; | ||
| 1254 | u32 wake_packet_length; | ||
| 1255 | u32 wake_packet_bufsize; | ||
| 1256 | const u8 *wake_packet; | ||
| 1257 | }; | ||
| 1258 | |||
| 1112 | static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, | 1259 | static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, |
| 1113 | struct ieee80211_vif *vif, | 1260 | struct ieee80211_vif *vif, |
| 1114 | struct iwl_wowlan_status *status) | 1261 | struct iwl_wowlan_status_data *status) |
| 1115 | { | 1262 | { |
| 1116 | struct sk_buff *pkt = NULL; | 1263 | struct sk_buff *pkt = NULL; |
| 1117 | struct cfg80211_wowlan_wakeup wakeup = { | 1264 | struct cfg80211_wowlan_wakeup wakeup = { |
| 1118 | .pattern_idx = -1, | 1265 | .pattern_idx = -1, |
| 1119 | }; | 1266 | }; |
| 1120 | struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; | 1267 | struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup; |
| 1121 | u32 reasons = le32_to_cpu(status->wakeup_reasons); | 1268 | u32 reasons = status->wakeup_reasons; |
| 1122 | 1269 | ||
| 1123 | if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { | 1270 | if (reasons == IWL_WOWLAN_WAKEUP_BY_NON_WIRELESS) { |
| 1124 | wakeup_report = NULL; | 1271 | wakeup_report = NULL; |
| @@ -1130,7 +1277,7 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, | |||
| 1130 | 1277 | ||
| 1131 | if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) | 1278 | if (reasons & IWL_WOWLAN_WAKEUP_BY_PATTERN) |
| 1132 | wakeup.pattern_idx = | 1279 | wakeup.pattern_idx = |
| 1133 | le16_to_cpu(status->pattern_number); | 1280 | status->pattern_number; |
| 1134 | 1281 | ||
| 1135 | if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | | 1282 | if (reasons & (IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_MISSED_BEACON | |
| 1136 | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) | 1283 | IWL_WOWLAN_WAKEUP_BY_DISCONNECTION_ON_DEAUTH)) |
| @@ -1158,8 +1305,8 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, | |||
| 1158 | wakeup.tcp_match = true; | 1305 | wakeup.tcp_match = true; |
| 1159 | 1306 | ||
| 1160 | if (status->wake_packet_bufsize) { | 1307 | if (status->wake_packet_bufsize) { |
| 1161 | int pktsize = le32_to_cpu(status->wake_packet_bufsize); | 1308 | int pktsize = status->wake_packet_bufsize; |
| 1162 | int pktlen = le32_to_cpu(status->wake_packet_length); | 1309 | int pktlen = status->wake_packet_length; |
| 1163 | const u8 *pktdata = status->wake_packet; | 1310 | const u8 *pktdata = status->wake_packet; |
| 1164 | struct ieee80211_hdr *hdr = (void *)pktdata; | 1311 | struct ieee80211_hdr *hdr = (void *)pktdata; |
| 1165 | int truncated = pktlen - pktsize; | 1312 | int truncated = pktlen - pktsize; |
| @@ -1239,8 +1386,229 @@ static void iwl_mvm_report_wakeup_reasons(struct iwl_mvm *mvm, | |||
| 1239 | kfree_skb(pkt); | 1386 | kfree_skb(pkt); |
| 1240 | } | 1387 | } |
| 1241 | 1388 | ||
| 1389 | static void iwl_mvm_aes_sc_to_seq(struct aes_sc *sc, | ||
| 1390 | struct ieee80211_key_seq *seq) | ||
| 1391 | { | ||
| 1392 | u64 pn; | ||
| 1393 | |||
| 1394 | pn = le64_to_cpu(sc->pn); | ||
| 1395 | seq->ccmp.pn[0] = pn >> 40; | ||
| 1396 | seq->ccmp.pn[1] = pn >> 32; | ||
| 1397 | seq->ccmp.pn[2] = pn >> 24; | ||
| 1398 | seq->ccmp.pn[3] = pn >> 16; | ||
| 1399 | seq->ccmp.pn[4] = pn >> 8; | ||
| 1400 | seq->ccmp.pn[5] = pn; | ||
| 1401 | } | ||
| 1402 | |||
| 1403 | static void iwl_mvm_tkip_sc_to_seq(struct tkip_sc *sc, | ||
| 1404 | struct ieee80211_key_seq *seq) | ||
| 1405 | { | ||
| 1406 | seq->tkip.iv32 = le32_to_cpu(sc->iv32); | ||
| 1407 | seq->tkip.iv16 = le16_to_cpu(sc->iv16); | ||
| 1408 | } | ||
| 1409 | |||
| 1410 | static void iwl_mvm_set_aes_rx_seq(struct aes_sc *scs, | ||
| 1411 | struct ieee80211_key_conf *key) | ||
| 1412 | { | ||
| 1413 | int tid; | ||
| 1414 | |||
| 1415 | BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); | ||
| 1416 | |||
| 1417 | for (tid = 0; tid < IWL_NUM_RSC; tid++) { | ||
| 1418 | struct ieee80211_key_seq seq = {}; | ||
| 1419 | |||
| 1420 | iwl_mvm_aes_sc_to_seq(&scs[tid], &seq); | ||
| 1421 | ieee80211_set_key_rx_seq(key, tid, &seq); | ||
| 1422 | } | ||
| 1423 | } | ||
| 1424 | |||
| 1425 | static void iwl_mvm_set_tkip_rx_seq(struct tkip_sc *scs, | ||
| 1426 | struct ieee80211_key_conf *key) | ||
| 1427 | { | ||
| 1428 | int tid; | ||
| 1429 | |||
| 1430 | BUILD_BUG_ON(IWL_NUM_RSC != IEEE80211_NUM_TIDS); | ||
| 1431 | |||
| 1432 | for (tid = 0; tid < IWL_NUM_RSC; tid++) { | ||
| 1433 | struct ieee80211_key_seq seq = {}; | ||
| 1434 | |||
| 1435 | iwl_mvm_tkip_sc_to_seq(&scs[tid], &seq); | ||
| 1436 | ieee80211_set_key_rx_seq(key, tid, &seq); | ||
| 1437 | } | ||
| 1438 | } | ||
| 1439 | |||
| 1440 | static void iwl_mvm_set_key_rx_seq(struct ieee80211_key_conf *key, | ||
| 1441 | struct iwl_wowlan_status_v6 *status) | ||
| 1442 | { | ||
| 1443 | union iwl_all_tsc_rsc *rsc = &status->gtk.rsc.all_tsc_rsc; | ||
| 1444 | |||
| 1445 | switch (key->cipher) { | ||
| 1446 | case WLAN_CIPHER_SUITE_CCMP: | ||
| 1447 | iwl_mvm_set_aes_rx_seq(rsc->aes.multicast_rsc, key); | ||
| 1448 | break; | ||
| 1449 | case WLAN_CIPHER_SUITE_TKIP: | ||
| 1450 | iwl_mvm_set_tkip_rx_seq(rsc->tkip.multicast_rsc, key); | ||
| 1451 | break; | ||
| 1452 | default: | ||
| 1453 | WARN_ON(1); | ||
| 1454 | } | ||
| 1455 | } | ||
| 1456 | |||
| 1457 | struct iwl_mvm_d3_gtk_iter_data { | ||
| 1458 | struct iwl_wowlan_status_v6 *status; | ||
| 1459 | void *last_gtk; | ||
| 1460 | u32 cipher; | ||
| 1461 | bool find_phase, unhandled_cipher; | ||
| 1462 | int num_keys; | ||
| 1463 | }; | ||
| 1464 | |||
| 1465 | static void iwl_mvm_d3_update_gtks(struct ieee80211_hw *hw, | ||
| 1466 | struct ieee80211_vif *vif, | ||
| 1467 | struct ieee80211_sta *sta, | ||
| 1468 | struct ieee80211_key_conf *key, | ||
| 1469 | void *_data) | ||
| 1470 | { | ||
| 1471 | struct iwl_mvm_d3_gtk_iter_data *data = _data; | ||
| 1472 | |||
| 1473 | if (data->unhandled_cipher) | ||
| 1474 | return; | ||
| 1475 | |||
| 1476 | switch (key->cipher) { | ||
| 1477 | case WLAN_CIPHER_SUITE_WEP40: | ||
| 1478 | case WLAN_CIPHER_SUITE_WEP104: | ||
| 1479 | /* ignore WEP completely, nothing to do */ | ||
| 1480 | return; | ||
| 1481 | case WLAN_CIPHER_SUITE_CCMP: | ||
| 1482 | case WLAN_CIPHER_SUITE_TKIP: | ||
| 1483 | /* we support these */ | ||
| 1484 | break; | ||
| 1485 | default: | ||
| 1486 | /* everything else (even CMAC for MFP) - disconnect from AP */ | ||
| 1487 | data->unhandled_cipher = true; | ||
| 1488 | return; | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | data->num_keys++; | ||
| 1492 | |||
| 1493 | /* | ||
| 1494 | * pairwise key - update sequence counters only; | ||
| 1495 | * note that this assumes no TDLS sessions are active | ||
| 1496 | */ | ||
| 1497 | if (sta) { | ||
| 1498 | struct ieee80211_key_seq seq = {}; | ||
| 1499 | union iwl_all_tsc_rsc *sc = &data->status->gtk.rsc.all_tsc_rsc; | ||
| 1500 | |||
| 1501 | if (data->find_phase) | ||
| 1502 | return; | ||
| 1503 | |||
| 1504 | switch (key->cipher) { | ||
| 1505 | case WLAN_CIPHER_SUITE_CCMP: | ||
| 1506 | iwl_mvm_aes_sc_to_seq(&sc->aes.tsc, &seq); | ||
| 1507 | iwl_mvm_set_aes_rx_seq(sc->aes.unicast_rsc, key); | ||
| 1508 | break; | ||
| 1509 | case WLAN_CIPHER_SUITE_TKIP: | ||
| 1510 | iwl_mvm_tkip_sc_to_seq(&sc->tkip.tsc, &seq); | ||
| 1511 | iwl_mvm_set_tkip_rx_seq(sc->tkip.unicast_rsc, key); | ||
| 1512 | break; | ||
| 1513 | } | ||
| 1514 | ieee80211_set_key_tx_seq(key, &seq); | ||
| 1515 | |||
| 1516 | /* that's it for this key */ | ||
| 1517 | return; | ||
| 1518 | } | ||
| 1519 | |||
| 1520 | if (data->find_phase) { | ||
| 1521 | data->last_gtk = key; | ||
| 1522 | data->cipher = key->cipher; | ||
| 1523 | return; | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | if (data->status->num_of_gtk_rekeys) | ||
| 1527 | ieee80211_remove_key(key); | ||
| 1528 | else if (data->last_gtk == key) | ||
| 1529 | iwl_mvm_set_key_rx_seq(key, data->status); | ||
| 1530 | } | ||
| 1531 | |||
| 1532 | static bool iwl_mvm_setup_connection_keep(struct iwl_mvm *mvm, | ||
| 1533 | struct ieee80211_vif *vif, | ||
| 1534 | struct iwl_wowlan_status_v6 *status) | ||
| 1535 | { | ||
| 1536 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
| 1537 | struct iwl_mvm_d3_gtk_iter_data gtkdata = { | ||
| 1538 | .status = status, | ||
| 1539 | }; | ||
| 1540 | |||
| 1541 | if (!status || !vif->bss_conf.bssid) | ||
| 1542 | return false; | ||
| 1543 | |||
| 1544 | /* find last GTK that we used initially, if any */ | ||
| 1545 | gtkdata.find_phase = true; | ||
| 1546 | ieee80211_iter_keys(mvm->hw, vif, | ||
| 1547 | iwl_mvm_d3_update_gtks, >kdata); | ||
| 1548 | /* not trying to keep connections with MFP/unhandled ciphers */ | ||
| 1549 | if (gtkdata.unhandled_cipher) | ||
| 1550 | return false; | ||
| 1551 | if (!gtkdata.num_keys) | ||
| 1552 | return true; | ||
| 1553 | if (!gtkdata.last_gtk) | ||
| 1554 | return false; | ||
| 1555 | |||
| 1556 | /* | ||
| 1557 | * invalidate all other GTKs that might still exist and update | ||
| 1558 | * the one that we used | ||
| 1559 | */ | ||
| 1560 | gtkdata.find_phase = false; | ||
| 1561 | ieee80211_iter_keys(mvm->hw, vif, | ||
| 1562 | iwl_mvm_d3_update_gtks, >kdata); | ||
| 1563 | |||
| 1564 | if (status->num_of_gtk_rekeys) { | ||
| 1565 | struct ieee80211_key_conf *key; | ||
| 1566 | struct { | ||
| 1567 | struct ieee80211_key_conf conf; | ||
| 1568 | u8 key[32]; | ||
| 1569 | } conf = { | ||
| 1570 | .conf.cipher = gtkdata.cipher, | ||
| 1571 | .conf.keyidx = status->gtk.key_index, | ||
| 1572 | }; | ||
| 1573 | |||
| 1574 | switch (gtkdata.cipher) { | ||
| 1575 | case WLAN_CIPHER_SUITE_CCMP: | ||
| 1576 | conf.conf.keylen = WLAN_KEY_LEN_CCMP; | ||
| 1577 | memcpy(conf.conf.key, status->gtk.decrypt_key, | ||
| 1578 | WLAN_KEY_LEN_CCMP); | ||
| 1579 | break; | ||
| 1580 | case WLAN_CIPHER_SUITE_TKIP: | ||
| 1581 | conf.conf.keylen = WLAN_KEY_LEN_TKIP; | ||
| 1582 | memcpy(conf.conf.key, status->gtk.decrypt_key, 16); | ||
| 1583 | /* leave TX MIC key zeroed, we don't use it anyway */ | ||
| 1584 | memcpy(conf.conf.key + | ||
| 1585 | NL80211_TKIP_DATA_OFFSET_RX_MIC_KEY, | ||
| 1586 | status->gtk.tkip_mic_key, 8); | ||
| 1587 | break; | ||
| 1588 | } | ||
| 1589 | |||
| 1590 | key = ieee80211_gtk_rekey_add(vif, &conf.conf); | ||
| 1591 | if (IS_ERR(key)) | ||
| 1592 | return false; | ||
| 1593 | iwl_mvm_set_key_rx_seq(key, status); | ||
| 1594 | } | ||
| 1595 | |||
| 1596 | if (status->num_of_gtk_rekeys) { | ||
| 1597 | __be64 replay_ctr = | ||
| 1598 | cpu_to_be64(le64_to_cpu(status->replay_ctr)); | ||
| 1599 | ieee80211_gtk_rekey_notify(vif, vif->bss_conf.bssid, | ||
| 1600 | (void *)&replay_ctr, GFP_KERNEL); | ||
| 1601 | } | ||
| 1602 | |||
| 1603 | mvmvif->seqno_valid = true; | ||
| 1604 | /* +0x10 because the set API expects next-to-use, not last-used */ | ||
| 1605 | mvmvif->seqno = le16_to_cpu(status->non_qos_seq_ctr) + 0x10; | ||
| 1606 | |||
| 1607 | return true; | ||
| 1608 | } | ||
| 1609 | |||
| 1242 | /* releases the MVM mutex */ | 1610 | /* releases the MVM mutex */ |
| 1243 | static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, | 1611 | static bool iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, |
| 1244 | struct ieee80211_vif *vif) | 1612 | struct ieee80211_vif *vif) |
| 1245 | { | 1613 | { |
| 1246 | u32 base = mvm->error_event_table; | 1614 | u32 base = mvm->error_event_table; |
| @@ -1253,8 +1621,12 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, | |||
| 1253 | .id = WOWLAN_GET_STATUSES, | 1621 | .id = WOWLAN_GET_STATUSES, |
| 1254 | .flags = CMD_SYNC | CMD_WANT_SKB, | 1622 | .flags = CMD_SYNC | CMD_WANT_SKB, |
| 1255 | }; | 1623 | }; |
| 1256 | struct iwl_wowlan_status *status; | 1624 | struct iwl_wowlan_status_data status; |
| 1257 | int ret, len; | 1625 | struct iwl_wowlan_status_v6 *status_v6; |
| 1626 | int ret, len, status_size, i; | ||
| 1627 | bool keep; | ||
| 1628 | struct ieee80211_sta *ap_sta; | ||
| 1629 | struct iwl_mvm_sta *mvm_ap_sta; | ||
| 1258 | 1630 | ||
| 1259 | iwl_trans_read_mem_bytes(mvm->trans, base, | 1631 | iwl_trans_read_mem_bytes(mvm->trans, base, |
| 1260 | &err_info, sizeof(err_info)); | 1632 | &err_info, sizeof(err_info)); |
| @@ -1287,32 +1659,83 @@ static void iwl_mvm_query_wakeup_reasons(struct iwl_mvm *mvm, | |||
| 1287 | if (!cmd.resp_pkt) | 1659 | if (!cmd.resp_pkt) |
| 1288 | goto out_unlock; | 1660 | goto out_unlock; |
| 1289 | 1661 | ||
| 1662 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) | ||
| 1663 | status_size = sizeof(struct iwl_wowlan_status_v6); | ||
| 1664 | else | ||
| 1665 | status_size = sizeof(struct iwl_wowlan_status_v4); | ||
| 1666 | |||
| 1290 | len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; | 1667 | len = le32_to_cpu(cmd.resp_pkt->len_n_flags) & FH_RSCSR_FRAME_SIZE_MSK; |
| 1291 | if (len - sizeof(struct iwl_cmd_header) < sizeof(*status)) { | 1668 | if (len - sizeof(struct iwl_cmd_header) < status_size) { |
| 1292 | IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); | 1669 | IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); |
| 1293 | goto out_free_resp; | 1670 | goto out_free_resp; |
| 1294 | } | 1671 | } |
| 1295 | 1672 | ||
| 1296 | status = (void *)cmd.resp_pkt->data; | 1673 | if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_CONTINUITY_API) { |
| 1674 | status_v6 = (void *)cmd.resp_pkt->data; | ||
| 1675 | |||
| 1676 | status.pattern_number = le16_to_cpu(status_v6->pattern_number); | ||
| 1677 | for (i = 0; i < 8; i++) | ||
| 1678 | status.qos_seq_ctr[i] = | ||
| 1679 | le16_to_cpu(status_v6->qos_seq_ctr[i]); | ||
| 1680 | status.wakeup_reasons = le32_to_cpu(status_v6->wakeup_reasons); | ||
| 1681 | status.wake_packet_length = | ||
| 1682 | le32_to_cpu(status_v6->wake_packet_length); | ||
| 1683 | status.wake_packet_bufsize = | ||
| 1684 | le32_to_cpu(status_v6->wake_packet_bufsize); | ||
| 1685 | status.wake_packet = status_v6->wake_packet; | ||
| 1686 | } else { | ||
| 1687 | struct iwl_wowlan_status_v4 *status_v4; | ||
| 1688 | status_v6 = NULL; | ||
| 1689 | status_v4 = (void *)cmd.resp_pkt->data; | ||
| 1690 | |||
| 1691 | status.pattern_number = le16_to_cpu(status_v4->pattern_number); | ||
| 1692 | for (i = 0; i < 8; i++) | ||
| 1693 | status.qos_seq_ctr[i] = | ||
| 1694 | le16_to_cpu(status_v4->qos_seq_ctr[i]); | ||
| 1695 | status.wakeup_reasons = le32_to_cpu(status_v4->wakeup_reasons); | ||
| 1696 | status.wake_packet_length = | ||
| 1697 | le32_to_cpu(status_v4->wake_packet_length); | ||
| 1698 | status.wake_packet_bufsize = | ||
| 1699 | le32_to_cpu(status_v4->wake_packet_bufsize); | ||
| 1700 | status.wake_packet = status_v4->wake_packet; | ||
| 1701 | } | ||
| 1297 | 1702 | ||
| 1298 | if (len - sizeof(struct iwl_cmd_header) != | 1703 | if (len - sizeof(struct iwl_cmd_header) != |
| 1299 | sizeof(*status) + | 1704 | status_size + ALIGN(status.wake_packet_bufsize, 4)) { |
| 1300 | ALIGN(le32_to_cpu(status->wake_packet_bufsize), 4)) { | ||
| 1301 | IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); | 1705 | IWL_ERR(mvm, "Invalid WoWLAN status response!\n"); |
| 1302 | goto out_free_resp; | 1706 | goto out_free_resp; |
| 1303 | } | 1707 | } |
| 1304 | 1708 | ||
| 1709 | /* still at hard-coded place 0 for D3 image */ | ||
| 1710 | ap_sta = rcu_dereference_protected( | ||
| 1711 | mvm->fw_id_to_mac_id[0], | ||
| 1712 | lockdep_is_held(&mvm->mutex)); | ||
| 1713 | if (IS_ERR_OR_NULL(ap_sta)) | ||
| 1714 | goto out_free_resp; | ||
| 1715 | |||
| 1716 | mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; | ||
| 1717 | for (i = 0; i < IWL_MAX_TID_COUNT; i++) { | ||
| 1718 | u16 seq = status.qos_seq_ctr[i]; | ||
| 1719 | /* firmware stores last-used value, we store next value */ | ||
| 1720 | seq += 0x10; | ||
| 1721 | mvm_ap_sta->tid_data[i].seq_number = seq; | ||
| 1722 | } | ||
| 1723 | |||
| 1305 | /* now we have all the data we need, unlock to avoid mac80211 issues */ | 1724 | /* now we have all the data we need, unlock to avoid mac80211 issues */ |
| 1306 | mutex_unlock(&mvm->mutex); | 1725 | mutex_unlock(&mvm->mutex); |
| 1307 | 1726 | ||
| 1308 | iwl_mvm_report_wakeup_reasons(mvm, vif, status); | 1727 | iwl_mvm_report_wakeup_reasons(mvm, vif, &status); |
| 1728 | |||
| 1729 | keep = iwl_mvm_setup_connection_keep(mvm, vif, status_v6); | ||
| 1730 | |||
| 1309 | iwl_free_resp(&cmd); | 1731 | iwl_free_resp(&cmd); |
| 1310 | return; | 1732 | return keep; |
| 1311 | 1733 | ||
| 1312 | out_free_resp: | 1734 | out_free_resp: |
| 1313 | iwl_free_resp(&cmd); | 1735 | iwl_free_resp(&cmd); |
| 1314 | out_unlock: | 1736 | out_unlock: |
| 1315 | mutex_unlock(&mvm->mutex); | 1737 | mutex_unlock(&mvm->mutex); |
| 1738 | return false; | ||
| 1316 | } | 1739 | } |
| 1317 | 1740 | ||
| 1318 | static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) | 1741 | static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) |
| @@ -1335,6 +1758,17 @@ static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm) | |||
| 1335 | #endif | 1758 | #endif |
| 1336 | } | 1759 | } |
| 1337 | 1760 | ||
| 1761 | static void iwl_mvm_d3_disconnect_iter(void *data, u8 *mac, | ||
| 1762 | struct ieee80211_vif *vif) | ||
| 1763 | { | ||
| 1764 | /* skip the one we keep connection on */ | ||
| 1765 | if (data == vif) | ||
| 1766 | return; | ||
| 1767 | |||
| 1768 | if (vif->type == NL80211_IFTYPE_STATION) | ||
| 1769 | ieee80211_resume_disconnect(vif); | ||
| 1770 | } | ||
| 1771 | |||
| 1338 | static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) | 1772 | static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) |
| 1339 | { | 1773 | { |
| 1340 | struct iwl_d3_iter_data resume_iter_data = { | 1774 | struct iwl_d3_iter_data resume_iter_data = { |
| @@ -1343,6 +1777,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) | |||
| 1343 | struct ieee80211_vif *vif = NULL; | 1777 | struct ieee80211_vif *vif = NULL; |
| 1344 | int ret; | 1778 | int ret; |
| 1345 | enum iwl_d3_status d3_status; | 1779 | enum iwl_d3_status d3_status; |
| 1780 | bool keep = false; | ||
| 1346 | 1781 | ||
| 1347 | mutex_lock(&mvm->mutex); | 1782 | mutex_lock(&mvm->mutex); |
| 1348 | 1783 | ||
| @@ -1368,7 +1803,7 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) | |||
| 1368 | /* query SRAM first in case we want event logging */ | 1803 | /* query SRAM first in case we want event logging */ |
| 1369 | iwl_mvm_read_d3_sram(mvm); | 1804 | iwl_mvm_read_d3_sram(mvm); |
| 1370 | 1805 | ||
| 1371 | iwl_mvm_query_wakeup_reasons(mvm, vif); | 1806 | keep = iwl_mvm_query_wakeup_reasons(mvm, vif); |
| 1372 | /* has unlocked the mutex, so skip that */ | 1807 | /* has unlocked the mutex, so skip that */ |
| 1373 | goto out; | 1808 | goto out; |
| 1374 | 1809 | ||
| @@ -1376,8 +1811,10 @@ static int __iwl_mvm_resume(struct iwl_mvm *mvm, bool test) | |||
| 1376 | mutex_unlock(&mvm->mutex); | 1811 | mutex_unlock(&mvm->mutex); |
| 1377 | 1812 | ||
| 1378 | out: | 1813 | out: |
| 1379 | if (!test && vif) | 1814 | if (!test) |
| 1380 | ieee80211_resume_disconnect(vif); | 1815 | ieee80211_iterate_active_interfaces_rtnl(mvm->hw, |
| 1816 | IEEE80211_IFACE_ITER_NORMAL, | ||
| 1817 | iwl_mvm_d3_disconnect_iter, keep ? vif : NULL); | ||
| 1381 | 1818 | ||
| 1382 | /* return 1 to reconfigure the device */ | 1819 | /* return 1 to reconfigure the device */ |
| 1383 | set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); | 1820 | set_bit(IWL_MVM_STATUS_IN_HW_RESTART, &mvm->status); |
