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); |