mwifiex: Add support for auto ARP in mwifiex.
authorMaithili Hinge <maithili@marvell.com>
Thu, 12 Mar 2015 07:38:39 +0000 (00:38 -0700)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 16 Mar 2015 16:04:59 +0000 (18:04 +0200)
This patch adds support for auto ARP feature in mwifiex.
The device will respond to ARP requests from the network
with ARP response in suspended state without waking up the host.
This feature is enabled in the driver by default.

Signed-off-by: Maithili Hinge <maithili@marvell.com>
Signed-off-by: Amitkumar Karwar <akarwar@marvell.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/mwifiex/cfg80211.c
drivers/net/wireless/mwifiex/fw.h
drivers/net/wireless/mwifiex/main.h
drivers/net/wireless/mwifiex/sta_cmd.c

index 8e1f681f960b73e02385d74a48b9e0b4efde16b3..b0778a699bbc4472096d90a769a4c5d08e292457 100644 (file)
@@ -2732,24 +2732,71 @@ mwifiex_is_pattern_supported(struct cfg80211_pkt_pattern *pat, s8 *byte_seq,
 }
 
 #ifdef CONFIG_PM
-static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
-                                 struct cfg80211_wowlan *wowlan)
+static void mwifiex_set_auto_arp_mef_entry(struct mwifiex_private *priv,
+                                          struct mwifiex_mef_entry *mef_entry)
+{
+       int i, filt_num = 0, num_ipv4 = 0;
+       struct in_device *in_dev;
+       struct in_ifaddr *ifa;
+       __be32 ips[MWIFIEX_MAX_SUPPORTED_IPADDR];
+       struct mwifiex_adapter *adapter = priv->adapter;
+
+       mef_entry->mode = MEF_MODE_HOST_SLEEP;
+       mef_entry->action = MEF_ACTION_AUTO_ARP;
+
+       /* Enable ARP offload feature */
+       memset(ips, 0, sizeof(ips));
+       for (i = 0; i < MWIFIEX_MAX_BSS_NUM; i++) {
+               if (adapter->priv[i]->netdev) {
+                       in_dev = __in_dev_get_rtnl(adapter->priv[i]->netdev);
+                       if (!in_dev)
+                               continue;
+                       ifa = in_dev->ifa_list;
+                       if (!ifa || !ifa->ifa_local)
+                               continue;
+                       ips[i] = ifa->ifa_local;
+                       num_ipv4++;
+               }
+       }
+
+       for (i = 0; i < num_ipv4; i++) {
+               if (!ips[i])
+                       continue;
+               mef_entry->filter[filt_num].repeat = 1;
+               memcpy(mef_entry->filter[filt_num].byte_seq,
+                      (u8 *)&ips[i], sizeof(ips[i]));
+               mef_entry->filter[filt_num].
+                       byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] =
+                       sizeof(ips[i]);
+               mef_entry->filter[filt_num].offset = 46;
+               mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+               if (filt_num) {
+                       mef_entry->filter[filt_num].filt_action =
+                               TYPE_OR;
+               }
+               filt_num++;
+       }
+
+       mef_entry->filter[filt_num].repeat = 1;
+       mef_entry->filter[filt_num].byte_seq[0] = 0x08;
+       mef_entry->filter[filt_num].byte_seq[1] = 0x06;
+       mef_entry->filter[filt_num].byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] = 2;
+       mef_entry->filter[filt_num].offset = 20;
+       mef_entry->filter[filt_num].filt_type = TYPE_EQ;
+       mef_entry->filter[filt_num].filt_action = TYPE_AND;
+}
+
+static int mwifiex_set_wowlan_mef_entry(struct mwifiex_private *priv,
+                                       struct mwifiex_ds_mef_cfg *mef_cfg,
+                                       struct mwifiex_mef_entry *mef_entry,
+                                       struct cfg80211_wowlan *wowlan)
 {
        int i, filt_num = 0, ret = 0;
        bool first_pat = true;
        u8 byte_seq[MWIFIEX_MEF_MAX_BYTESEQ + 1];
        const u8 ipv4_mc_mac[] = {0x33, 0x33};
        const u8 ipv6_mc_mac[] = {0x01, 0x00, 0x5e};
-       struct mwifiex_ds_mef_cfg mef_cfg;
-       struct mwifiex_mef_entry *mef_entry;
-
-       mef_entry = kzalloc(sizeof(*mef_entry), GFP_KERNEL);
-       if (!mef_entry)
-               return -ENOMEM;
 
-       memset(&mef_cfg, 0, sizeof(mef_cfg));
-       mef_cfg.num_entries = 1;
-       mef_cfg.mef_entry = mef_entry;
        mef_entry->mode = MEF_MODE_HOST_SLEEP;
        mef_entry->action = MEF_ACTION_ALLOW_AND_WAKEUP_HOST;
 
@@ -2766,20 +2813,19 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
                if (!wowlan->patterns[i].pkt_offset) {
                        if (!(byte_seq[0] & 0x01) &&
                            (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 1)) {
-                               mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+                               mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
                                continue;
                        } else if (is_broadcast_ether_addr(byte_seq)) {
-                               mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST;
+                               mef_cfg->criteria |= MWIFIEX_CRITERIA_BROADCAST;
                                continue;
                        } else if ((!memcmp(byte_seq, ipv4_mc_mac, 2) &&
                                    (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 2)) ||
                                   (!memcmp(byte_seq, ipv6_mc_mac, 3) &&
                                    (byte_seq[MWIFIEX_MEF_MAX_BYTESEQ] == 3))) {
-                               mef_cfg.criteria |= MWIFIEX_CRITERIA_MULTICAST;
+                               mef_cfg->criteria |= MWIFIEX_CRITERIA_MULTICAST;
                                continue;
                        }
                }
-
                mef_entry->filter[filt_num].repeat = 1;
                mef_entry->filter[filt_num].offset =
                        wowlan->patterns[i].pkt_offset;
@@ -2796,7 +2842,7 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
        }
 
        if (wowlan->magic_pkt) {
-               mef_cfg.criteria |= MWIFIEX_CRITERIA_UNICAST;
+               mef_cfg->criteria |= MWIFIEX_CRITERIA_UNICAST;
                mef_entry->filter[filt_num].repeat = 16;
                memcpy(mef_entry->filter[filt_num].byte_seq, priv->curr_addr,
                                ETH_ALEN);
@@ -2817,6 +2863,34 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
                mef_entry->filter[filt_num].filt_type = TYPE_EQ;
                mef_entry->filter[filt_num].filt_action = TYPE_OR;
        }
+       return ret;
+}
+
+static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
+                                 struct cfg80211_wowlan *wowlan)
+{
+       int ret = 0, num_entries = 1;
+       struct mwifiex_ds_mef_cfg mef_cfg;
+       struct mwifiex_mef_entry *mef_entry;
+
+       if (wowlan->n_patterns || wowlan->magic_pkt)
+               num_entries++;
+
+       mef_entry = kcalloc(num_entries, sizeof(*mef_entry), GFP_KERNEL);
+       if (!mef_entry)
+               return -ENOMEM;
+
+       memset(&mef_cfg, 0, sizeof(mef_cfg));
+       mef_cfg.criteria |= MWIFIEX_CRITERIA_BROADCAST |
+               MWIFIEX_CRITERIA_UNICAST;
+       mef_cfg.num_entries = num_entries;
+       mef_cfg.mef_entry = mef_entry;
+
+       mwifiex_set_auto_arp_mef_entry(priv, &mef_entry[0]);
+
+       if (wowlan->n_patterns || wowlan->magic_pkt)
+               ret = mwifiex_set_wowlan_mef_entry(priv, &mef_cfg,
+                                                  &mef_entry[1], wowlan);
 
        if (!mef_cfg.criteria)
                mef_cfg.criteria = MWIFIEX_CRITERIA_BROADCAST |
@@ -2824,8 +2898,8 @@ static int mwifiex_set_mef_filter(struct mwifiex_private *priv,
                        MWIFIEX_CRITERIA_MULTICAST;
 
        ret = mwifiex_send_cmd(priv, HostCmd_CMD_MEF_CFG,
-                       HostCmd_ACT_GEN_SET, 0, &mef_cfg, true);
-
+                       HostCmd_ACT_GEN_SET, 0,
+                       &mef_cfg, true);
        kfree(mef_entry);
        return ret;
 }
@@ -2850,12 +2924,10 @@ static int mwifiex_cfg80211_suspend(struct wiphy *wiphy,
                return 0;
        }
 
-       if (wowlan->n_patterns || wowlan->magic_pkt) {
-               ret = mwifiex_set_mef_filter(priv, wowlan);
-               if (ret) {
-                       dev_err(adapter->dev, "Failed to set MEF filter\n");
-                       return ret;
-               }
+       ret = mwifiex_set_mef_filter(priv, wowlan);
+       if (ret) {
+               dev_err(adapter->dev, "Failed to set MEF filter\n");
+               return ret;
        }
 
        if (wowlan->disconnect) {
index df553e86a0ad3bea74fc4ccee87db7c6e3d77e53..21a942fd2226ad4c41dea0400cd8e53291ae782c 100644 (file)
@@ -523,9 +523,11 @@ enum P2P_MODES {
 #define TYPE_OR                                (MAX_OPERAND+5)
 #define MEF_MODE_HOST_SLEEP                    1
 #define MEF_ACTION_ALLOW_AND_WAKEUP_HOST       3
+#define MEF_ACTION_AUTO_ARP                    0x10
 #define MWIFIEX_CRITERIA_BROADCAST     BIT(0)
 #define MWIFIEX_CRITERIA_UNICAST       BIT(1)
 #define MWIFIEX_CRITERIA_MULTICAST     BIT(3)
+#define MWIFIEX_MAX_SUPPORTED_IPADDR              4
 
 #define ACT_TDLS_DELETE            0x00
 #define ACT_TDLS_CREATE            0x01
index 16be45e9a66acb2d853b5d26c133411ddaf3c378..a0908c64103a4ae422cdbe4b6480051ff6e631a0 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/ctype.h>
 #include <linux/of.h>
 #include <linux/idr.h>
+#include <linux/inetdevice.h>
 
 #include "decl.h"
 #include "ioctl.h"
index f7d204ffd6e97c2444129252535e9fd3580959cc..b23eaed847016f92fbcab6fa4bd7e1caeff4b7c0 100644 (file)
@@ -1370,22 +1370,29 @@ mwifiex_cmd_mef_cfg(struct mwifiex_private *priv,
                    struct mwifiex_ds_mef_cfg *mef)
 {
        struct host_cmd_ds_mef_cfg *mef_cfg = &cmd->params.mef_cfg;
+       struct mwifiex_fw_mef_entry *mef_entry = NULL;
        u8 *pos = (u8 *)mef_cfg;
+       u16 i;
 
        cmd->command = cpu_to_le16(HostCmd_CMD_MEF_CFG);
 
        mef_cfg->criteria = cpu_to_le32(mef->criteria);
        mef_cfg->num_entries = cpu_to_le16(mef->num_entries);
        pos += sizeof(*mef_cfg);
-       mef_cfg->mef_entry->mode = mef->mef_entry->mode;
-       mef_cfg->mef_entry->action = mef->mef_entry->action;
-       pos += sizeof(*(mef_cfg->mef_entry));
 
-       if (mwifiex_cmd_append_rpn_expression(priv, mef->mef_entry, &pos))
-               return -1;
+       for (i = 0; i < mef->num_entries; i++) {
+               mef_entry = (struct mwifiex_fw_mef_entry *)pos;
+               mef_entry->mode = mef->mef_entry[i].mode;
+               mef_entry->action = mef->mef_entry[i].action;
+               pos += sizeof(*mef_cfg->mef_entry);
+
+               if (mwifiex_cmd_append_rpn_expression(priv,
+                                                     &mef->mef_entry[i], &pos))
+                       return -1;
 
-       mef_cfg->mef_entry->exprsize =
-                       cpu_to_le16(pos - mef_cfg->mef_entry->expr);
+               mef_entry->exprsize =
+                       cpu_to_le16(pos - mef_entry->expr);
+       }
        cmd->size = cpu_to_le16((u16) (pos - (u8 *)mef_cfg) + S_DS_GEN);
 
        return 0;