aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net
diff options
context:
space:
mode:
authorEliad Peller <eliad@wizery.com>2013-11-21 12:19:52 -0500
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>2014-03-16 07:45:23 -0400
commit1a95c8df7ed1ddf5e1d732a594f5a1b09da9a8c5 (patch)
treec4a8c745468f89ed75f32ea47673064f2b04c3d5 /drivers/net
parentc63722cfd441bd3a99c65fa4c40bc65d7776e772 (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.h8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/Makefile2
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/d3.c189
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h8
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/mvm.h5
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/offloading.c214
-rw-r--r--drivers/net/wireless/iwlwifi/mvm/ops.c41
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 */
132enum 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
3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o 3iwlmvm-y += utils.o rx.o tx.o binding.o quota.o sta.o sf.o
4iwlmvm-y += scan.o time-event.o rs.o 4iwlmvm-y += scan.o time-event.o rs.o
5iwlmvm-y += power.o coex.o 5iwlmvm-y += power.o coex.o
6iwlmvm-y += led.o tt.o 6iwlmvm-y += led.o tt.o offloading.o
7iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o 7iwlmvm-$(CONFIG_IWLWIFI_DEBUGFS) += debugfs.o debugfs-vif.o
8iwlmvm-$(CONFIG_PM_SLEEP) += d3.o 8iwlmvm-$(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
379static 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
512enum iwl_mvm_tcp_packet_type { 379enum 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
797static int
798iwl_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
930static int __iwl_mvm_suspend(struct ieee80211_hw *hw, 811static 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
242struct iwl_wowlan_config_cmd { 242struct 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
250struct 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
899void iwl_mvm_set_wowlan_qos_seq(struct iwl_mvm_sta *mvm_ap_sta,
900 struct iwl_wowlan_config_cmd_v2 *cmd);
901int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm, struct ieee80211_vif *vif);
899 902
900/* D0i3 */ 903/* D0i3 */
901void iwl_mvm_ref(struct iwl_mvm *mvm, enum iwl_mvm_ref_type ref_type); 904void 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
67void 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
84int 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
853static 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);
877out:
878 rcu_read_unlock();
879}
853static int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode) 880static 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);