diff options
author | Johannes Berg <johannes.berg@intel.com> | 2013-08-20 07:04:10 -0400 |
---|---|---|
committer | Johannes Berg <johannes.berg@intel.com> | 2013-10-02 12:00:45 -0400 |
commit | f7fc598931766a0609f33de249c17c067737425e (patch) | |
tree | 65e668f7e82131fe7a629c09c2e15c0ff238c5a0 | |
parent | f6fc57756bd8e89687b165280a9bdee290a7850a (diff) |
iwlwifi: mvm: implement new IPv6 offload API
The firmware API for IPv6 NDP/NS offload has changed again.
Implement support for the new API; this requires calculating
the solicited node address for each "target" address as it's
no longer ignored by the firmware.
Reviewed-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
-rw-r--r-- | drivers/net/wireless/iwlwifi/iwl-fw.h | 4 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/d3.c | 75 | ||||
-rw-r--r-- | drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h | 44 |
3 files changed, 118 insertions, 5 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-fw.h b/drivers/net/wireless/iwlwifi/iwl-fw.h index 8c2473e212d3..761794a5e916 100644 --- a/drivers/net/wireless/iwlwifi/iwl-fw.h +++ b/drivers/net/wireless/iwlwifi/iwl-fw.h | |||
@@ -84,6 +84,8 @@ | |||
84 | * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API | 84 | * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API |
85 | * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element | 85 | * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element |
86 | * from the probe request template. | 86 | * from the probe request template. |
87 | * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version) | ||
88 | * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version) | ||
87 | * @IWL_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan. | 89 | * @IWL_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan. |
88 | * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API | 90 | * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API |
89 | */ | 91 | */ |
@@ -101,6 +103,8 @@ enum iwl_ucode_tlv_flag { | |||
101 | IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10), | 103 | IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS = BIT(10), |
102 | IWL_UCODE_TLV_FLAGS_BF_UPDATED = BIT(11), | 104 | IWL_UCODE_TLV_FLAGS_BF_UPDATED = BIT(11), |
103 | IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12), | 105 | IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID = BIT(12), |
106 | IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL = BIT(15), | ||
107 | IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE = BIT(16), | ||
104 | IWL_UCODE_TLV_FLAGS_SCHED_SCAN = BIT(17), | 108 | IWL_UCODE_TLV_FLAGS_SCHED_SCAN = BIT(17), |
105 | IWL_UCODE_TLV_FLAGS_STA_KEY_CMD = BIT(19), | 109 | IWL_UCODE_TLV_FLAGS_STA_KEY_CMD = BIT(19), |
106 | }; | 110 | }; |
diff --git a/drivers/net/wireless/iwlwifi/mvm/d3.c b/drivers/net/wireless/iwlwifi/mvm/d3.c index 123a44f031a7..d08c12b930e5 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 { |
diff --git a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h index df72fcdf8170..1f7d65aaa87a 100644 --- a/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h +++ b/drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h | |||
@@ -100,7 +100,12 @@ enum iwl_proto_offloads { | |||
100 | 100 | ||
101 | #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 | 101 | #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1 2 |
102 | #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 | 102 | #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2 6 |
103 | #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 6 | 103 | #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L 12 |
104 | #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S 4 | ||
105 | #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX 12 | ||
106 | |||
107 | #define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L 4 | ||
108 | #define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S 2 | ||
104 | 109 | ||
105 | /** | 110 | /** |
106 | * struct iwl_proto_offload_cmd_common - ARP/NS offload common part | 111 | * struct iwl_proto_offload_cmd_common - ARP/NS offload common part |
@@ -155,6 +160,43 @@ struct iwl_proto_offload_cmd_v2 { | |||
155 | u8 reserved2[3]; | 160 | u8 reserved2[3]; |
156 | } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ | 161 | } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */ |
157 | 162 | ||
163 | struct iwl_ns_config { | ||
164 | struct in6_addr source_ipv6_addr; | ||
165 | struct in6_addr dest_ipv6_addr; | ||
166 | u8 target_mac_addr[ETH_ALEN]; | ||
167 | __le16 reserved; | ||
168 | } __packed; /* NS_OFFLOAD_CONFIG */ | ||
169 | |||
170 | struct iwl_targ_addr { | ||
171 | struct in6_addr addr; | ||
172 | __le32 config_num; | ||
173 | } __packed; /* TARGET_IPV6_ADDRESS */ | ||
174 | |||
175 | /** | ||
176 | * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration | ||
177 | * @common: common/IPv4 configuration | ||
178 | * @target_ipv6_addr: target IPv6 addresses | ||
179 | * @ns_config: NS offload configurations | ||
180 | */ | ||
181 | struct iwl_proto_offload_cmd_v3_small { | ||
182 | struct iwl_proto_offload_cmd_common common; | ||
183 | __le32 num_valid_ipv6_addrs; | ||
184 | struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S]; | ||
185 | struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S]; | ||
186 | } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ | ||
187 | |||
188 | /** | ||
189 | * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration | ||
190 | * @common: common/IPv4 configuration | ||
191 | * @target_ipv6_addr: target IPv6 addresses | ||
192 | * @ns_config: NS offload configurations | ||
193 | */ | ||
194 | struct iwl_proto_offload_cmd_v3_large { | ||
195 | struct iwl_proto_offload_cmd_common common; | ||
196 | __le32 num_valid_ipv6_addrs; | ||
197 | struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L]; | ||
198 | struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L]; | ||
199 | } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */ | ||
158 | 200 | ||
159 | /* | 201 | /* |
160 | * WOWLAN_PATTERNS | 202 | * WOWLAN_PATTERNS |