iwlwifi: mvm: implement new IPv6 offload API
authorJohannes Berg <johannes.berg@intel.com>
Tue, 20 Aug 2013 11:04:10 +0000 (13:04 +0200)
committerJohannes Berg <johannes.berg@intel.com>
Wed, 2 Oct 2013 16:00:45 +0000 (18:00 +0200)
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>
drivers/net/wireless/iwlwifi/iwl-fw.h
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/fw-api-d3.h

index 8c2473e..761794a 100644 (file)
@@ -84,6 +84,8 @@
  * @IWL_UCODE_TLV_FLAGS_BF_UPDATED: new beacon filtering API
  * @IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID: not sending a probe with the SSID element
  *     from the probe request template.
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL: new NS offload (small version)
+ * @IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE: new NS offload (large version)
  * @IWL_UCODE_TLV_FLAGS_SCHED_SCAN: this uCode image supports scheduled scan.
  * @IWL_UCODE_TLV_FLAGS_STA_KEY_CMD: new ADD_STA and ADD_STA_KEY command API
  */
@@ -101,6 +103,8 @@ enum iwl_ucode_tlv_flag {
        IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS     = BIT(10),
        IWL_UCODE_TLV_FLAGS_BF_UPDATED          = BIT(11),
        IWL_UCODE_TLV_FLAGS_NO_BASIC_SSID       = BIT(12),
+       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL    = BIT(15),
+       IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE    = BIT(16),
        IWL_UCODE_TLV_FLAGS_SCHED_SCAN          = BIT(17),
        IWL_UCODE_TLV_FLAGS_STA_KEY_CMD         = BIT(19),
 };
index 123a44f..d08c12b 100644 (file)
@@ -67,6 +67,7 @@
 #include <net/cfg80211.h>
 #include <net/ipv6.h>
 #include <net/tcp.h>
+#include <net/addrconf.h>
 #include "iwl-modparams.h"
 #include "fw-api.h"
 #include "mvm.h"
@@ -381,14 +382,74 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
        union {
                struct iwl_proto_offload_cmd_v1 v1;
                struct iwl_proto_offload_cmd_v2 v2;
+               struct iwl_proto_offload_cmd_v3_small v3s;
+               struct iwl_proto_offload_cmd_v3_large v3l;
        } cmd = {};
+       struct iwl_host_cmd hcmd = {
+               .id = PROT_OFFLOAD_CONFIG_CMD,
+               .flags = CMD_SYNC,
+               .data[0] = &cmd,
+               .dataflags[0] = IWL_HCMD_DFL_DUP,
+       };
        struct iwl_proto_offload_cmd_common *common;
        u32 enabled = 0, size;
+       u32 capa_flags = mvm->fw->ucode_capa.flags;
 #if IS_ENABLED(CONFIG_IPV6)
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
        int i;
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+       if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL ||
+           capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+               struct iwl_ns_config *nsc;
+               struct iwl_targ_addr *addrs;
+               int n_nsc, n_addrs;
+               int c;
+
+               if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+                       nsc = cmd.v3s.ns_config;
+                       n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S;
+                       addrs = cmd.v3s.targ_addrs;
+                       n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S;
+               } else {
+                       nsc = cmd.v3l.ns_config;
+                       n_nsc = IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L;
+                       addrs = cmd.v3l.targ_addrs;
+                       n_addrs = IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L;
+               }
+
+               if (mvmvif->num_target_ipv6_addrs)
+                       enabled |= IWL_D3_PROTO_OFFLOAD_NS;
+
+               /*
+                * For each address we have (and that will fit) fill a target
+                * address struct and combine for NS offload structs with the
+                * solicited node addresses.
+                */
+               for (i = 0, c = 0;
+                    i < mvmvif->num_target_ipv6_addrs &&
+                    i < n_addrs && c < n_nsc; i++) {
+                       struct in6_addr solicited_addr;
+                       int j;
+
+                       addrconf_addr_solict_mult(&mvmvif->target_ipv6_addrs[i],
+                                                 &solicited_addr);
+                       for (j = 0; j < c; j++)
+                               if (ipv6_addr_cmp(&nsc[j].dest_ipv6_addr,
+                                                 &solicited_addr) == 0)
+                                       break;
+                       if (j == c)
+                               c++;
+                       addrs[i].addr = mvmvif->target_ipv6_addrs[i];
+                       addrs[i].config_num = cpu_to_le32(j);
+                       nsc[j].dest_ipv6_addr = solicited_addr;
+                       memcpy(nsc[j].target_mac_addr, vif->addr, ETH_ALEN);
+               }
+
+               if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL)
+                       cmd.v3s.num_valid_ipv6_addrs = cpu_to_le32(i);
+               else
+                       cmd.v3l.num_valid_ipv6_addrs = cpu_to_le32(i);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
                if (mvmvif->num_target_ipv6_addrs) {
                        enabled |= IWL_D3_PROTO_OFFLOAD_NS;
                        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,
        }
 #endif
 
-       if (mvm->fw->ucode_capa.flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
+       if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_SMALL) {
+               common = &cmd.v3s.common;
+               size = sizeof(cmd.v3s);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_NEW_NSOFFL_LARGE) {
+               common = &cmd.v3l.common;
+               size = sizeof(cmd.v3l);
+       } else if (capa_flags & IWL_UCODE_TLV_FLAGS_D3_6_IPV6_ADDRS) {
                common = &cmd.v2.common;
                size = sizeof(cmd.v2);
        } else {
@@ -438,8 +505,8 @@ static int iwl_mvm_send_proto_offload(struct iwl_mvm *mvm,
 
        common->enabled = cpu_to_le32(enabled);
 
-       return iwl_mvm_send_cmd_pdu(mvm, PROT_OFFLOAD_CONFIG_CMD, CMD_SYNC,
-                                   size, &cmd);
+       hcmd.len[0] = size;
+       return iwl_mvm_send_cmd(mvm, &hcmd);
 }
 
 enum iwl_mvm_tcp_packet_type {
index df72fcd..1f7d65a 100644 (file)
@@ -100,7 +100,12 @@ enum iwl_proto_offloads {
 
 #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V1    2
 #define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V2    6
-#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX   6
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L   12
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S   4
+#define IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_MAX   12
+
+#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L    4
+#define IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S    2
 
 /**
  * struct iwl_proto_offload_cmd_common - ARP/NS offload common part
@@ -155,6 +160,43 @@ struct iwl_proto_offload_cmd_v2 {
        u8 reserved2[3];
 } __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_2 */
 
+struct iwl_ns_config {
+       struct in6_addr source_ipv6_addr;
+       struct in6_addr dest_ipv6_addr;
+       u8 target_mac_addr[ETH_ALEN];
+       __le16 reserved;
+} __packed; /* NS_OFFLOAD_CONFIG */
+
+struct iwl_targ_addr {
+       struct in6_addr addr;
+       __le32 config_num;
+} __packed; /* TARGET_IPV6_ADDRESS */
+
+/**
+ * struct iwl_proto_offload_cmd_v3_small - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @target_ipv6_addr: target IPv6 addresses
+ * @ns_config: NS offload configurations
+ */
+struct iwl_proto_offload_cmd_v3_small {
+       struct iwl_proto_offload_cmd_common common;
+       __le32 num_valid_ipv6_addrs;
+       struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3S];
+       struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3S];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
+
+/**
+ * struct iwl_proto_offload_cmd_v3_large - ARP/NS offload configuration
+ * @common: common/IPv4 configuration
+ * @target_ipv6_addr: target IPv6 addresses
+ * @ns_config: NS offload configurations
+ */
+struct iwl_proto_offload_cmd_v3_large {
+       struct iwl_proto_offload_cmd_common common;
+       __le32 num_valid_ipv6_addrs;
+       struct iwl_targ_addr targ_addrs[IWL_PROTO_OFFLOAD_NUM_IPV6_ADDRS_V3L];
+       struct iwl_ns_config ns_config[IWL_PROTO_OFFLOAD_NUM_NS_CONFIG_V3L];
+} __packed; /* PROT_OFFLOAD_CONFIG_CMD_DB_S_VER_3 */
 
 /*
  * WOWLAN_PATTERNS