diff options
author | Eliad Peller <eliad@wizery.com> | 2013-11-21 12:19:52 -0500 |
---|---|---|
committer | Emmanuel Grumbach <emmanuel.grumbach@intel.com> | 2014-03-16 07:45:23 -0400 |
commit | 1a95c8df7ed1ddf5e1d732a594f5a1b09da9a8c5 (patch) | |
tree | c4a8c745468f89ed75f32ea47673064f2b04c3d5 /drivers/net | |
parent | c63722cfd441bd3a99c65fa4c40bc65d7776e772 (diff) |
iwlwifi: mvm: configure seq_num to D0i3
Configure the QoS counters when entering D0i3.
The fw might use them later when performing protocol
offloading (we'll update the the counters back on
d0i3 exit in a following patch).
Non-QoS counter is handled internally in the fw, so
no need to configure it.
Also, add support for a new version of WOWLAN_CONFIG_CMD
Signed-off-by: Eliad Peller <eliad@wizery.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Diffstat (limited to 'drivers/net')
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-fw.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/Makefile | 2 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/d3.c | 189 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h | 8 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/mvm.h | 5 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/offloading.c | 214 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/ops.c | 41 |
7 files changed, 300 insertions, 167 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 18f867c4df55..d14f19339d61 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h | |||
@@ -126,6 +126,14 @@ enum iwl_ucode_tlv_flag { | |||
126 | }; | 126 | }; |
127 | 127 | ||
128 | /** | 128 | /** |
129 | * enum iwl_ucode_tlv_api - ucode api | ||
130 | * @IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID: wowlan config includes tid field. | ||
131 | */ | ||
132 | enum iwl_ucode_tlv_api { | ||
133 | IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID = BIT(0), | ||
134 | }; | ||
135 | |||
136 | /** | ||
129 | * enum iwl_ucode_tlv_capa - ucode capabilities | 137 | * enum iwl_ucode_tlv_capa - ucode capabilities |
130 | * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3 | 138 | * @IWL_UCODE_TLV_CAPA_D0I3_SUPPORT: supports D0i3 |
131 | */ | 139 | */ |
diff --git a/drivers/net/wireless/iwlwifi/mvm/Makefile b/drivers/net/wireless/iwlwifi/mvm/Makefile index 9798aa5b7645..ccdd3b7c4cce 100644 --- a/drivers/net/wireless/iwlwifi/mvm/Makefile +++ b/drivers/net/wireless/iwlwifi/mvm/Makefile | |||
@@ -3,7 +3,7 @@ iwlmvm-y += fw.o mac80211.o nvm.o ops.o phy-ctxt.o mac-ctxt.o | |||
3 | iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o | 3 | iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o |
4 | iwlmvm-y += scan.o time-event.o rs.o | 4 | iwlmvm-y += scan.o time-event.o rs.o |
5 | iwlmvm-y += power.o coex.o | 5 | iwlmvm-y += power.o coex.o |
6 | iwlmvm-y += led.o tt.o | 6 | iwlmvm-y += led.o tt.o offloading.o |
7 | iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o | 7 | iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o |
8 | iwlmvm-$(CONFIG_PM_SLEEP) += d3.o | 8 | iwlmvm-$(CONFIG_PM_SLEEP) += d3.o |
9 | 9 | ||
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index a08756456e10..02fb950c031c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/d3.c +++ b/drivers/net/wireless/iwlwifi/mvm/d3.c | |||
@@ -376,139 +376,6 @@ static int iwl_mvm_send_patterns(struct iwl_mvm *mvm, | |||
376 | return err; | 376 | return err; |
377 | } | 377 | } |
378 | 378 | ||
379 | static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, | ||
380 | struct ieee80211_vif *vif) | ||
381 | { | ||
382 | union { | ||
383 | struct iwl_proto_offload_cmd_v1 v1; | ||
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; | ||
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 | }; | ||
394 | struct iwl_proto_offload_cmd_common *common; | ||
395 | u32 enabled = 0, size; | ||
396 | u32 capa_flags = mvm->fw->ucode_capa.flags; | ||
397 | #if IS_ENABLED(CONFIG_IPV6) | ||
398 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
399 | int i; | ||
400 | |||
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) { | ||
453 | if (mvmvif->num_target_ipv6_addrs) { | ||
454 | enabled |= IWL_D3_PROTO_OFFLOAD_NS; | ||
455 | memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); | ||
456 | } | ||
457 | |||
458 | BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != | ||
459 | sizeof(mvmvif->target_ipv6_addrs[0])); | ||
460 | |||
461 | for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, | ||
462 | IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) | ||
463 | memcpy(cmd.v2.target_ipv6_addr[i], | ||
464 | &mvmvif->target_ipv6_addrs[i], | ||
465 | sizeof(cmd.v2.target_ipv6_addr[i])); | ||
466 | } else { | ||
467 | if (mvmvif->num_target_ipv6_addrs) { | ||
468 | enabled |= IWL_D3_PROTO_OFFLOAD_NS; | ||
469 | memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); | ||
470 | } | ||
471 | |||
472 | BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != | ||
473 | sizeof(mvmvif->target_ipv6_addrs[0])); | ||
474 | |||
475 | for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, | ||
476 | IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) | ||
477 | memcpy(cmd.v1.target_ipv6_addr[i], | ||
478 | &mvmvif->target_ipv6_addrs[i], | ||
479 | sizeof(cmd.v1.target_ipv6_addr[i])); | ||
480 | } | ||
481 | #endif | ||
482 | |||
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) { | ||
490 | common = &cmd.v2.common; | ||
491 | size = sizeof(cmd.v2); | ||
492 | } else { | ||
493 | common = &cmd.v1.common; | ||
494 | size = sizeof(cmd.v1); | ||
495 | } | ||
496 | |||
497 | if (vif->bss_conf.arp_addr_cnt) { | ||
498 | enabled |= IWL_D3_PROTO_OFFLOAD_ARP; | ||
499 | common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; | ||
500 | memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); | ||
501 | } | ||
502 | |||
503 | if (!enabled) | ||
504 | return 0; | ||
505 | |||
506 | common->enabled = cpu_to_le32(enabled); | ||
507 | |||
508 | hcmd.len[0] = size; | ||
509 | return iwl_mvm_send_cmd(mvm, &hcmd); | ||
510 | } | ||
511 | |||
512 | enum iwl_mvm_tcp_packet_type { | 379 | enum iwl_mvm_tcp_packet_type { |
513 | MVM_TCP_TX_SYN, | 380 | MVM_TCP_TX_SYN, |
514 | MVM_TCP_RX_SYNACK, | 381 | MVM_TCP_RX_SYNACK, |
@@ -927,6 +794,20 @@ void iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) | |||
927 | IWL_ERR(mvm, "failed to set non-QoS seqno\n"); | 794 | IWL_ERR(mvm, "failed to set non-QoS seqno\n"); |
928 | } | 795 | } |
929 | 796 | ||
797 | static int | ||
798 | iwl_mvm_send_wowlan_config_cmd(struct iwl_mvm *mvm, | ||
799 | const struct iwl_wowlan_config_cmd_v3 *cmd) | ||
800 | { | ||
801 | /* start only with the v2 part of the command */ | ||
802 | u16 cmd_len = sizeof(cmd->common); | ||
803 | |||
804 | if (mvm->fw->ucode_capa.api[0] & IWL_UCODE_TLV_API_WOWLAN_CONFIG_TID) | ||
805 | cmd_len = sizeof(*cmd); | ||
806 | |||
807 | return iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, CMD_SYNC, | ||
808 | cmd_len, cmd); | ||
809 | } | ||
810 | |||
930 | static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | 811 | static int __iwl_mvm_suspend(struct ieee80211_hw *hw, |
931 | struct cfg80211_wowlan *wowlan, | 812 | struct cfg80211_wowlan *wowlan, |
932 | bool test) | 813 | bool test) |
@@ -939,7 +820,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
939 | struct iwl_mvm_vif *mvmvif; | 820 | struct iwl_mvm_vif *mvmvif; |
940 | struct ieee80211_sta *ap_sta; | 821 | struct ieee80211_sta *ap_sta; |
941 | struct iwl_mvm_sta *mvm_ap_sta; | 822 | struct iwl_mvm_sta *mvm_ap_sta; |
942 | struct iwl_wowlan_config_cmd wowlan_config_cmd = {}; | 823 | struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = {}; |
943 | struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; | 824 | struct iwl_wowlan_kek_kck_material_cmd kek_kck_cmd = {}; |
944 | struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; | 825 | struct iwl_wowlan_tkip_params_cmd tkip_cmd = {}; |
945 | struct iwl_d3_manager_config d3_cfg_cmd_data = { | 826 | struct iwl_d3_manager_config d3_cfg_cmd_data = { |
@@ -961,7 +842,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
961 | .tkip = &tkip_cmd, | 842 | .tkip = &tkip_cmd, |
962 | .use_tkip = false, | 843 | .use_tkip = false, |
963 | }; | 844 | }; |
964 | int ret, i; | 845 | int ret; |
965 | int len __maybe_unused; | 846 | int len __maybe_unused; |
966 | 847 | ||
967 | if (!wowlan) { | 848 | if (!wowlan) { |
@@ -1002,49 +883,41 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
1002 | 883 | ||
1003 | mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; | 884 | mvm_ap_sta = (struct iwl_mvm_sta *)ap_sta->drv_priv; |
1004 | 885 | ||
1005 | /* TODO: wowlan_config_cmd.wowlan_ba_teardown_tids */ | 886 | /* TODO: wowlan_config_cmd.common.wowlan_ba_teardown_tids */ |
1006 | 887 | ||
1007 | wowlan_config_cmd.is_11n_connection = ap_sta->ht_cap.ht_supported; | 888 | wowlan_config_cmd.common.is_11n_connection = |
889 | ap_sta->ht_cap.ht_supported; | ||
1008 | 890 | ||
1009 | /* Query the last used seqno and set it */ | 891 | /* Query the last used seqno and set it */ |
1010 | ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); | 892 | ret = iwl_mvm_get_last_nonqos_seq(mvm, vif); |
1011 | if (ret < 0) | 893 | if (ret < 0) |
1012 | goto out_noreset; | 894 | goto out_noreset; |
1013 | wowlan_config_cmd.non_qos_seq = cpu_to_le16(ret); | 895 | wowlan_config_cmd.common.non_qos_seq = cpu_to_le16(ret); |
1014 | 896 | ||
1015 | /* | 897 | iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &wowlan_config_cmd.common); |
1016 | * For QoS counters, we store the one to use next, so subtract 0x10 | ||
1017 | * since the uCode will add 0x10 *before* using the value while we | ||
1018 | * increment after using the value (i.e. store the next value to use). | ||
1019 | */ | ||
1020 | for (i = 0; i < IWL_MAX_TID_COUNT; i++) { | ||
1021 | u16 seq = mvm_ap_sta->tid_data[i].seq_number; | ||
1022 | seq -= 0x10; | ||
1023 | wowlan_config_cmd.qos_seq[i] = cpu_to_le16(seq); | ||
1024 | } | ||
1025 | 898 | ||
1026 | if (wowlan->disconnect) | 899 | if (wowlan->disconnect) |
1027 | wowlan_config_cmd.wakeup_filter |= | 900 | wowlan_config_cmd.common.wakeup_filter |= |
1028 | cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | | 901 | cpu_to_le32(IWL_WOWLAN_WAKEUP_BEACON_MISS | |
1029 | IWL_WOWLAN_WAKEUP_LINK_CHANGE); | 902 | IWL_WOWLAN_WAKEUP_LINK_CHANGE); |
1030 | if (wowlan->magic_pkt) | 903 | if (wowlan->magic_pkt) |
1031 | wowlan_config_cmd.wakeup_filter |= | 904 | wowlan_config_cmd.common.wakeup_filter |= |
1032 | cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); | 905 | cpu_to_le32(IWL_WOWLAN_WAKEUP_MAGIC_PACKET); |
1033 | if (wowlan->gtk_rekey_failure) | 906 | if (wowlan->gtk_rekey_failure) |
1034 | wowlan_config_cmd.wakeup_filter |= | 907 | wowlan_config_cmd.common.wakeup_filter |= |
1035 | cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); | 908 | cpu_to_le32(IWL_WOWLAN_WAKEUP_GTK_REKEY_FAIL); |
1036 | if (wowlan->eap_identity_req) | 909 | if (wowlan->eap_identity_req) |
1037 | wowlan_config_cmd.wakeup_filter |= | 910 | wowlan_config_cmd.common.wakeup_filter |= |
1038 | cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); | 911 | cpu_to_le32(IWL_WOWLAN_WAKEUP_EAP_IDENT_REQ); |
1039 | if (wowlan->four_way_handshake) | 912 | if (wowlan->four_way_handshake) |
1040 | wowlan_config_cmd.wakeup_filter |= | 913 | wowlan_config_cmd.common.wakeup_filter |= |
1041 | cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); | 914 | cpu_to_le32(IWL_WOWLAN_WAKEUP_4WAY_HANDSHAKE); |
1042 | if (wowlan->n_patterns) | 915 | if (wowlan->n_patterns) |
1043 | wowlan_config_cmd.wakeup_filter |= | 916 | wowlan_config_cmd.common.wakeup_filter |= |
1044 | cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); | 917 | cpu_to_le32(IWL_WOWLAN_WAKEUP_PATTERN_MATCH); |
1045 | 918 | ||
1046 | if (wowlan->rfkill_release) | 919 | if (wowlan->rfkill_release) |
1047 | wowlan_config_cmd.wakeup_filter |= | 920 | wowlan_config_cmd.common.wakeup_filter |= |
1048 | cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); | 921 | cpu_to_le32(IWL_WOWLAN_WAKEUP_RF_KILL_DEASSERT); |
1049 | 922 | ||
1050 | if (wowlan->tcp) { | 923 | if (wowlan->tcp) { |
@@ -1052,7 +925,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
1052 | * Set the "link change" (really "link lost") flag as well | 925 | * Set the "link change" (really "link lost") flag as well |
1053 | * since that implies losing the TCP connection. | 926 | * since that implies losing the TCP connection. |
1054 | */ | 927 | */ |
1055 | wowlan_config_cmd.wakeup_filter |= | 928 | wowlan_config_cmd.common.wakeup_filter |= |
1056 | cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS | | 929 | cpu_to_le32(IWL_WOWLAN_WAKEUP_REMOTE_LINK_LOSS | |
1057 | IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE | | 930 | IWL_WOWLAN_WAKEUP_REMOTE_SIGNATURE_TABLE | |
1058 | IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET | | 931 | IWL_WOWLAN_WAKEUP_REMOTE_WAKEUP_PACKET | |
@@ -1150,9 +1023,7 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw, | |||
1150 | } | 1023 | } |
1151 | } | 1024 | } |
1152 | 1025 | ||
1153 | ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, | 1026 | ret = iwl_mvm_send_wowlan_config_cmd(mvm, &wowlan_config_cmd); |
1154 | CMD_SYNC, sizeof(wowlan_config_cmd), | ||
1155 | &wowlan_config_cmd); | ||
1156 | if (ret) | 1027 | if (ret) |
1157 | goto out; | 1028 | goto out; |
1158 | 1029 | ||
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index 521997669c99..10fcc1a79ebd 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h | |||
@@ -239,7 +239,7 @@ enum iwl_wowlan_wakeup_filters { | |||
239 | IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), | 239 | IWL_WOWLAN_WAKEUP_BCN_FILTERING = BIT(16), |
240 | }; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ | 240 | }; /* WOWLAN_WAKEUP_FILTER_API_E_VER_4 */ |
241 | 241 | ||
242 | struct iwl_wowlan_config_cmd { | 242 | struct iwl_wowlan_config_cmd_v2 { |
243 | __le32 wakeup_filter; | 243 | __le32 wakeup_filter; |
244 | __le16 non_qos_seq; | 244 | __le16 non_qos_seq; |
245 | __le16 qos_seq[8]; | 245 | __le16 qos_seq[8]; |
@@ -247,6 +247,12 @@ struct iwl_wowlan_config_cmd { | |||
247 | u8 is_11n_connection; | 247 | u8 is_11n_connection; |
248 | } __packed; /* WOWLAN_CONFIG_API_S_VER_2 */ | 248 | } __packed; /* WOWLAN_CONFIG_API_S_VER_2 */ |
249 | 249 | ||
250 | struct iwl_wowlan_config_cmd_v3 { | ||
251 | struct iwl_wowlan_config_cmd_v2 common; | ||
252 | u8 offloading_tid; | ||
253 | u8 reserved[3]; | ||
254 | } __packed; /* WOWLAN_CONFIG_API_S_VER_3 */ | ||
255 | |||
250 | /* | 256 | /* |
251 | * WOWLAN_TSC_RSC_PARAMS | 257 | * WOWLAN_TSC_RSC_PARAMS |
252 | */ | 258 | */ |
diff --git a/drivers/net/wireless/iwlwifi/mvm/mvm.h b/drivers/net/wireless/iwlwifi/mvm/mvm.h index f77be762ebd9..095bc2669610 100644 --- a/drivers/net/wireless/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/iwlwifi/mvm/mvm.h | |||
@@ -317,13 +317,13 @@ struct iwl_mvm_vif { | |||
317 | 317 | ||
318 | bool seqno_valid; | 318 | bool seqno_valid; |
319 | u16 seqno; | 319 | u16 seqno; |
320 | #endif | ||
320 | 321 | ||
321 | #if IS_ENABLED(CONFIG_IPV6) | 322 | #if IS_ENABLED(CONFIG_IPV6) |
322 | /* IPv6 addresses for WoWLAN */ | 323 | /* IPv6 addresses for WoWLAN */ |
323 | struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; | 324 | struct in6_addr target_ipv6_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX]; |
324 | int num_target_ipv6_addrs; | 325 | int num_target_ipv6_addrs; |
325 | #endif | 326 | #endif |
326 | #endif | ||
327 | 327 | ||
328 | #ifdef CONFIG_IWLWIFI_DEBUGFS | 328 | #ifdef CONFIG_IWLWIFI_DEBUGFS |
329 | struct iwl_mvm *mvm; | 329 | struct iwl_mvm *mvm; |
@@ -896,6 +896,9 @@ iwl_mvm_set_last_nonqos_seq(struct iwl_mvm *mvm, struct ieee80211_vif *vif) | |||
896 | { | 896 | { |
897 | } | 897 | } |
898 | #endif | 898 | #endif |
899 | void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, | ||
900 | struct iwl_wowlan_config_cmd_v2 *cmd); | ||
901 | int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct ieee80211_vif *vif); | ||
899 | 902 | ||
900 | /* D0i3 */ | 903 | /* D0i3 */ |
901 | void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); | 904 | void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); |
diff --git a/drivers/net/wireless/iwlwifi/mvm/offloading.c b/drivers/net/wireless/iwlwifi/mvm/offloading.c new file mode 100644 index 000000000000..9ec5a5991e3a --- /dev/null +++ b/drivers/net/wireless/iwlwifi/mvm/offloading.c | |||
@@ -0,0 +1,214 @@ | |||
1 | /****************************************************************************** | ||
2 | * | ||
3 | * This file is provided under a dual BSD/GPLv2 license. When using or | ||
4 | * redistributing this file, you may do so under either license. | ||
5 | * | ||
6 | * GPL LICENSE SUMMARY | ||
7 | * | ||
8 | * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of version 2 of the GNU General Public License as | ||
12 | * published by the Free Software Foundation. | ||
13 | * | ||
14 | * This program is distributed in the hope that it will be useful, but | ||
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | ||
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
17 | * General Public License for more details. | ||
18 | * | ||
19 | * You should have received a copy of the GNU General Public License | ||
20 | * along with this program; if not, write to the Free Software | ||
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, | ||
22 | * USA | ||
23 | * | ||
24 | * The full GNU General Public License is included in this distribution | ||
25 | * in the file called COPYING. | ||
26 | * | ||
27 | * Contact Information: | ||
28 | * Intel Linux Wireless <ilw@linux.intel.com> | ||
29 | * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 | ||
30 | * | ||
31 | * BSD LICENSE | ||
32 | * | ||
33 | * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved. | ||
34 | * All rights reserved. | ||
35 | * | ||
36 | * Redistribution and use in source and binary forms, with or without | ||
37 | * modification, are permitted provided that the following conditions | ||
38 | * are met: | ||
39 | * | ||
40 | * * Redistributions of source code must retain the above copyright | ||
41 | * notice, this list of conditions and the following disclaimer. | ||
42 | * * Redistributions in binary form must reproduce the above copyright | ||
43 | * notice, this list of conditions and the following disclaimer in | ||
44 | * the documentation and/or other materials provided with the | ||
45 | * distribution. | ||
46 | * * Neither the name Intel Corporation nor the names of its | ||
47 | * contributors may be used to endorse or promote products derived | ||
48 | * from this software without specific prior written permission. | ||
49 | * | ||
50 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | ||
51 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | ||
52 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | ||
53 | * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | ||
54 | * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | ||
55 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | ||
56 | * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | ||
57 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | ||
58 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
59 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | ||
60 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
61 | * | ||
62 | *****************************************************************************/ | ||
63 | #include <net/ipv6.h> | ||
64 | #include <net/addrconf.h> | ||
65 | #include "mvm.h" | ||
66 | |||
67 | void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta, | ||
68 | struct iwl_wowlan_config_cmd_v2 *cmd) | ||
69 | { | ||
70 | int i; | ||
71 | |||
72 | /* | ||
73 | * For QoS counters, we store the one to use next, so subtract 0x10 | ||
74 | * since the uCode will add 0x10 *before* using the value while we | ||
75 | * increment after using the value (i.e. store the next value to use). | ||
76 | */ | ||
77 | for (i = 0; i < IWL_MAX_TID_COUNT; i++) { | ||
78 | u16 seq = mvm_ap_sta->tid_data[i].seq_number; | ||
79 | seq -= 0x10; | ||
80 | cmd->qos_seq[i] = cpu_to_le16(seq); | ||
81 | } | ||
82 | } | ||
83 | |||
84 | int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct ieee80211_vif *vif) | ||
85 | { | ||
86 | union { | ||
87 | struct iwl_proto_offload_cmd_v1 v1; | ||
88 | struct iwl_proto_offload_cmd_v2 v2; | ||
89 | struct iwl_proto_offload_cmd_v3_small v3s; | ||
90 | struct iwl_proto_offload_cmd_v3_large v3l; | ||
91 | } cmd = {}; | ||
92 | struct iwl_host_cmd hcmd = { | ||
93 | .id = PROT_OFFLOAD_CONFIG_CMD, | ||
94 | .flags = CMD_SYNC, | ||
95 | .data[0] = &cmd, | ||
96 | .dataflags[0] = IWL_HCMD_DFL_DUP, | ||
97 | }; | ||
98 | struct iwl_proto_offload_cmd_common *common; | ||
99 | u32 enabled = 0, size; | ||
100 | u32 capa_flags = mvm->fw->ucode_capa.flags; | ||
101 | #if IS_ENABLED(CONFIG_IPV6) | ||
102 | struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif); | ||
103 | int i; | ||
104 | |||
105 | if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL || | ||
106 | capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { | ||
107 | struct iwl_ns_config *nsc; | ||
108 | struct iwl_targ_addr *addrs; | ||
109 | int n_nsc, n_addrs; | ||
110 | int c; | ||
111 | |||
112 | if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { | ||
113 | nsc = cmd.v3s.ns_config; | ||
114 | n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S; | ||
115 | addrs = cmd.v3s.targ_addrs; | ||
116 | n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S; | ||
117 | } else { | ||
118 | nsc = cmd.v3l.ns_config; | ||
119 | n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L; | ||
120 | addrs = cmd.v3l.targ_addrs; | ||
121 | n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L; | ||
122 | } | ||
123 | |||
124 | if (mvmvif->num_target_ipv6_addrs) | ||
125 | enabled |= IWL_D3_PROTO_OFFLOAD_NS; | ||
126 | |||
127 | /* | ||
128 | * For each address we have (and that will fit) fill a target | ||
129 | * address struct and combine for NS offload structs with the | ||
130 | * solicited node addresses. | ||
131 | */ | ||
132 | for (i = 0, c = 0; | ||
133 | i < mvmvif->num_target_ipv6_addrs && | ||
134 | i < n_addrs && c < n_nsc; i++) { | ||
135 | struct in6_addr solicited_addr; | ||
136 | int j; | ||
137 | |||
138 | addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i], | ||
139 | &solicited_addr); | ||
140 | for (j = 0; j < c; j++) | ||
141 | if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr, | ||
142 | &solicited_addr) == 0) | ||
143 | break; | ||
144 | if (j == c) | ||
145 | c++; | ||
146 | addrs[i].addr = mvmvif->target_ipv6_addrs[i]; | ||
147 | addrs[i].config_num = cpu_to_le32(j); | ||
148 | nsc[j].dest_ipv6_addr = solicited_addr; | ||
149 | memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN); | ||
150 | } | ||
151 | |||
152 | if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) | ||
153 | cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i); | ||
154 | else | ||
155 | cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i); | ||
156 | } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { | ||
157 | if (mvmvif->num_target_ipv6_addrs) { | ||
158 | enabled |= IWL_D3_PROTO_OFFLOAD_NS; | ||
159 | memcpy(cmd.v2.ndp_mac_addr, vif->addr, ETH_ALEN); | ||
160 | } | ||
161 | |||
162 | BUILD_BUG_ON(sizeof(cmd.v2.target_ipv6_addr[0]) != | ||
163 | sizeof(mvmvif->target_ipv6_addrs[0])); | ||
164 | |||
165 | for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, | ||
166 | IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2); i++) | ||
167 | memcpy(cmd.v2.target_ipv6_addr[i], | ||
168 | &mvmvif->target_ipv6_addrs[i], | ||
169 | sizeof(cmd.v2.target_ipv6_addr[i])); | ||
170 | } else { | ||
171 | if (mvmvif->num_target_ipv6_addrs) { | ||
172 | enabled |= IWL_D3_PROTO_OFFLOAD_NS; | ||
173 | memcpy(cmd.v1.ndp_mac_addr, vif->addr, ETH_ALEN); | ||
174 | } | ||
175 | |||
176 | BUILD_BUG_ON(sizeof(cmd.v1.target_ipv6_addr[0]) != | ||
177 | sizeof(mvmvif->target_ipv6_addrs[0])); | ||
178 | |||
179 | for (i = 0; i < min(mvmvif->num_target_ipv6_addrs, | ||
180 | IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1); i++) | ||
181 | memcpy(cmd.v1.target_ipv6_addr[i], | ||
182 | &mvmvif->target_ipv6_addrs[i], | ||
183 | sizeof(cmd.v1.target_ipv6_addr[i])); | ||
184 | } | ||
185 | #endif | ||
186 | |||
187 | if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) { | ||
188 | common = &cmd.v3s.common; | ||
189 | size = sizeof(cmd.v3s); | ||
190 | } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) { | ||
191 | common = &cmd.v3l.common; | ||
192 | size = sizeof(cmd.v3l); | ||
193 | } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) { | ||
194 | common = &cmd.v2.common; | ||
195 | size = sizeof(cmd.v2); | ||
196 | } else { | ||
197 | common = &cmd.v1.common; | ||
198 | size = sizeof(cmd.v1); | ||
199 | } | ||
200 | |||
201 | if (vif->bss_conf.arp_addr_cnt) { | ||
202 | enabled |= IWL_D3_PROTO_OFFLOAD_ARP; | ||
203 | common->host_ipv4_addr = vif->bss_conf.arp_addr_list[0]; | ||
204 | memcpy(common->arp_mac_addr, vif->addr, ETH_ALEN); | ||
205 | } | ||
206 | |||
207 | if (!enabled) | ||
208 | return 0; | ||
209 | |||
210 | common->enabled = cpu_to_le32(enabled); | ||
211 | |||
212 | hcmd.len[0] = size; | ||
213 | return iwl_mvm_send_cmd(mvm, &hcmd); | ||
214 | } | ||
diff --git a/drivers/net/wireless/iwlwifi/mvm/ops.c b/drivers/net/wireless/iwlwifi/mvm/ops.c index 75fbc4054173..87c32e80904c 100644 --- a/drivers/net/wireless/iwlwifi/mvm/ops.c +++ b/drivers/net/wireless/iwlwifi/mvm/ops.c | |||
@@ -850,6 +850,33 @@ static void iwl_mvm_enter_d0i3_iterator(void *_data, u8 *mac, | |||
850 | data->vif_count++; | 850 | data->vif_count++; |
851 | } | 851 | } |
852 | 852 | ||
853 | static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm, | ||
854 | struct iwl_wowlan_config_cmd_v3 *cmd, | ||
855 | struct iwl_d0i3_iter_data *iter_data) | ||
856 | { | ||
857 | struct ieee80211_sta *ap_sta; | ||
858 | struct iwl_mvm_sta *mvm_ap_sta; | ||
859 | |||
860 | if (iter_data->ap_sta_id == IWL_MVM_STATION_COUNT) | ||
861 | return; | ||
862 | |||
863 | rcu_read_lock(); | ||
864 | |||
865 | ap_sta = rcu_dereference(mvm->fw_id_to_mac_id[iter_data->ap_sta_id]); | ||
866 | if (IS_ERR_OR_NULL(ap_sta)) | ||
867 | goto out; | ||
868 | |||
869 | mvm_ap_sta = iwl_mvm_sta_from_mac80211(ap_sta); | ||
870 | cmd->common.is_11n_connection = ap_sta->ht_cap.ht_supported; | ||
871 | |||
872 | /* | ||
873 | * The d0i3 uCode takes care of the nonqos counters, | ||
874 | * so configure only the qos seq ones. | ||
875 | */ | ||
876 | iwl_mvm_set_wowlan_qos_seq(mvm_ap_sta, &cmd->common); | ||
877 | out: | ||
878 | rcu_read_unlock(); | ||
879 | } | ||
853 | static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) | 880 | static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) |
854 | { | 881 | { |
855 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); | 882 | struct iwl_mvm *mvm = IWL_OP_MODE_GET_MVM(op_mode); |
@@ -858,11 +885,14 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) | |||
858 | struct iwl_d0i3_iter_data d0i3_iter_data = { | 885 | struct iwl_d0i3_iter_data d0i3_iter_data = { |
859 | .mvm = mvm, | 886 | .mvm = mvm, |
860 | }; | 887 | }; |
861 | struct iwl_wowlan_config_cmd wowlan_config_cmd = { | 888 | struct iwl_wowlan_config_cmd_v3 wowlan_config_cmd = { |
862 | .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | | 889 | .common = { |
863 | IWL_WOWLAN_WAKEUP_BEACON_MISS | | 890 | .wakeup_filter = |
864 | IWL_WOWLAN_WAKEUP_LINK_CHANGE | | 891 | cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME | |
865 | IWL_WOWLAN_WAKEUP_BCN_FILTERING), | 892 | IWL_WOWLAN_WAKEUP_BEACON_MISS | |
893 | IWL_WOWLAN_WAKEUP_LINK_CHANGE | | ||
894 | IWL_WOWLAN_WAKEUP_BCN_FILTERING), | ||
895 | }, | ||
866 | }; | 896 | }; |
867 | struct iwl_d3_manager_config d3_cfg_cmd = { | 897 | struct iwl_d3_manager_config d3_cfg_cmd = { |
868 | .min_sleep_time = cpu_to_le32(1000), | 898 | .min_sleep_time = cpu_to_le32(1000), |
@@ -881,6 +911,7 @@ static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) | |||
881 | mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; | 911 | mvm->d0i3_ap_sta_id = IWL_MVM_STATION_COUNT; |
882 | } | 912 | } |
883 | 913 | ||
914 | iwl_mvm_set_wowlan_data(mvm, &wowlan_config_cmd, &d0i3_iter_data); | ||
884 | ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, | 915 | ret = iwl_mvm_send_cmd_pdu(mvm, WOWLAN_CONFIGURATION, flags, |
885 | sizeof(wowlan_config_cmd), | 916 | sizeof(wowlan_config_cmd), |
886 | &wowlan_config_cmd); | 917 | &wowlan_config_cmd); |