From: Lo-Hsiang Lo <[email protected]>

Set up wiphy->wowlan_config and a dummy wowlan filter so brcmfmac can
remain connected during suspend. Enable an unicast packet filter during
suspend so ping packets can wake up the system.

Signed-off-by: Lo-Hsiang Lo <[email protected]>
Signed-off-by: Chi-Hsien Lin <[email protected]>
---
 .../broadcom/brcm80211/brcmfmac/cfg80211.c         |  36 ++++-
 .../broadcom/brcm80211/brcmfmac/cfg80211.h         |   7 +
 .../wireless/broadcom/brcm80211/brcmfmac/common.c  |  15 +-
 .../wireless/broadcom/brcm80211/brcmfmac/core.c    | 175 +++++++++++++++++++++
 .../wireless/broadcom/brcm80211/brcmfmac/core.h    |   9 +-
 .../broadcom/brcm80211/brcmfmac/fwil_types.h       |  13 ++
 6 files changed, 247 insertions(+), 8 deletions(-)

diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
index 35301237d435..80ff43554cc3 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
@@ -3593,6 +3593,9 @@ static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
                                            brcmf_notify_sched_scan_results);
                        cfg->wowl.nd_enabled = false;
                }
+
+               /* disable packet filters */
+               brcmf_pktfilter_enable(ifp->ndev, false);
        }
        return 0;
 }
@@ -3651,6 +3654,9 @@ static void brcmf_configure_wowl(struct 
brcmf_cfg80211_info *cfg,
        brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
        brcmf_bus_wowl_config(cfg->pub->bus_if, true);
        cfg->wowl.active = true;
+
+       /* enable packet filters */
+       brcmf_pktfilter_enable(ifp->ndev, true);
 }
 
 static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
@@ -3677,7 +3683,8 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
        if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
                brcmf_abort_scanning(cfg);
 
-       if (wowl == NULL) {
+       if (!wowl || !test_bit(BRCMF_VIF_STATUS_CONNECTED,
+                              &ifp->vif->sme_state)) {
                brcmf_bus_wowl_config(cfg->pub->bus_if, false);
                list_for_each_entry(vif, &cfg->vif_list, list) {
                        if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
@@ -3697,8 +3704,9 @@ static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
                brcmf_set_mpc(ifp, 1);
 
        } else {
-               /* Configure WOWL paramaters */
-               brcmf_configure_wowl(cfg, ifp, wowl);
+               if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
+                       /* Configure WOWL parameters */
+                       brcmf_configure_wowl(cfg, ifp, wowl);
        }
 
 exit:
@@ -6471,6 +6479,7 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, 
struct brcmf_if *ifp)
 #ifdef CONFIG_PM
        struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
        struct wiphy_wowlan_support *wowl;
+       struct cfg80211_wowlan *brcmf_wowlan_config = NULL;
 
        wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),
                       GFP_KERNEL);
@@ -6493,6 +6502,27 @@ static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, 
struct brcmf_if *ifp)
        }
 
        wiphy->wowlan = wowl;
+
+       /* wowlan_config structure report for kernels */
+       brcmf_wowlan_config = kzalloc(sizeof(*brcmf_wowlan_config),
+                                     GFP_KERNEL);
+       if (brcmf_wowlan_config) {
+               brcmf_wowlan_config->any = false;
+               brcmf_wowlan_config->disconnect = true;
+               brcmf_wowlan_config->eap_identity_req = true;
+               brcmf_wowlan_config->four_way_handshake = true;
+               brcmf_wowlan_config->rfkill_release = false;
+               brcmf_wowlan_config->patterns = NULL;
+               brcmf_wowlan_config->n_patterns = 0;
+               brcmf_wowlan_config->tcp = NULL;
+               if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
+                       brcmf_wowlan_config->gtk_rekey_failure = true;
+               else
+                       brcmf_wowlan_config->gtk_rekey_failure = false;
+       } else {
+               brcmf_err("Can not allocate memory for brcm_wowlan_config\n");
+       }
+       wiphy->wowlan_config = brcmf_wowlan_config;
 #endif
 }
 
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
index 9a6287f084a9..d3ec684ddb0d 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
@@ -86,6 +86,13 @@
 
 #define BRCMF_VIF_EVENT_TIMEOUT                msecs_to_jiffies(1500)
 
+/* cfg80211 wowlan definitions */
+#define WL_WOWLAN_MAX_PATTERNS                 8
+#define WL_WOWLAN_MIN_PATTERN_LEN              1
+#define WL_WOWLAN_MAX_PATTERN_LEN              255
+#define WL_WOWLAN_PKT_FILTER_ID_FIRST  201
+#define WL_WOWLAN_PKT_FILTER_ID_LAST   (WL_WOWLAN_PKT_FILTER_ID_FIRST + \
+                                       WL_WOWLAN_MAX_PATTERNS - 1)
 /**
  * enum brcmf_scan_status - scan engine status
  *
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
index 1f1e95a15a17..33b9f36e91f4 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/common.c
@@ -210,7 +210,7 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
        char *ptr;
        s32 err;
 
-       /* retreive mac address */
+       /* retrieve mac addresses */
        err = brcmf_fil_iovar_data_get(ifp, "cur_etheraddr", ifp->mac_addr,
                                       sizeof(ifp->mac_addr));
        if (err < 0) {
@@ -345,6 +345,15 @@ int brcmf_c_preinit_dcmds(struct brcmf_if *ifp)
 
        /* Enable tx beamforming, errors can be ignored (not supported) */
        (void)brcmf_fil_iovar_int_set(ifp, "txbf", 1);
+
+       /* add unicast packet filter */
+       err = brcmf_pktfilter_add_remove(ifp->ndev,
+                                        BRCMF_UNICAST_FILTER_NUM, true);
+       if (err)
+               brcmf_info("Add unicast filter error (%d)\n", err);
+
+       /* do bus specific preinit here */
+       err = brcmf_bus_preinit(ifp->drvr->bus_if);
 done:
        return err;
 }
@@ -413,7 +422,7 @@ struct brcmf_mp_device *brcmf_get_module_param(struct 
device *dev,
        if (!settings)
                return NULL;
 
-       /* start by using the module paramaters */
+       /* start by using the module parameters */
        settings->p2p_enable = !!brcmf_p2p_enable;
        settings->feature_disable = brcmf_feature_disable;
        settings->fcmode = brcmf_fcmode;
@@ -498,7 +507,7 @@ static int __init brcmfmac_module_init(void)
        if (err == -ENODEV)
                brcmf_dbg(INFO, "No platform data available.\n");
 
-       /* Initialize global module paramaters */
+       /* Initialize global module parameters */
        brcmf_mp_attach();
 
        /* Continue the initialization by registering the different busses */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
index 860a4372cb56..fa7bcac16fe4 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.c
@@ -1345,3 +1345,178 @@ void __exit brcmf_core_exit(void)
 #endif
 }
 
+int
+brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num, bool add)
+{
+       struct brcmf_if *ifp =  netdev_priv(ndev);
+       struct brcmf_pub *drvr = ifp->drvr;
+       struct brcmf_pkt_filter_le *pkt_filter;
+       int filter_fixed_len = offsetof(struct brcmf_pkt_filter_le, u);
+       int pattern_fixed_len = offsetof(struct brcmf_pkt_filter_pattern_le,
+                                 mask_and_pattern);
+       u16 mask_and_pattern[MAX_PKTFILTER_PATTERN_SIZE];
+       int buflen = 0;
+       int ret = 0;
+
+       brcmf_dbg(INFO, "%s packet filter number %d\n",
+                 (add ? "add" : "remove"), filter_num);
+
+       pkt_filter = kzalloc(sizeof(*pkt_filter) +
+                       (MAX_PKTFILTER_PATTERN_SIZE * 2), GFP_ATOMIC);
+       if (!pkt_filter)
+               return -ENOMEM;
+
+       switch (filter_num) {
+       case BRCMF_UNICAST_FILTER_NUM:
+               pkt_filter->id = 100;
+               pkt_filter->type = 0;
+               pkt_filter->negate_match = 0;
+               pkt_filter->u.pattern.offset = 0;
+               pkt_filter->u.pattern.size_bytes = 1;
+               mask_and_pattern[0] = 0x0001;
+               break;
+       case BRCMF_BROADCAST_FILTER_NUM:
+               //filter_pattern = "101 0 0 0 0xFFFFFFFFFFFF 0xFFFFFFFFFFFF";
+               pkt_filter->id = 101;
+               pkt_filter->type = 0;
+               pkt_filter->negate_match = 0;
+               pkt_filter->u.pattern.offset = 0;
+               pkt_filter->u.pattern.size_bytes = 6;
+               mask_and_pattern[0] = 0xFFFF;
+               mask_and_pattern[1] = 0xFFFF;
+               mask_and_pattern[2] = 0xFFFF;
+               mask_and_pattern[3] = 0xFFFF;
+               mask_and_pattern[4] = 0xFFFF;
+               mask_and_pattern[5] = 0xFFFF;
+               break;
+       case BRCMF_MULTICAST4_FILTER_NUM:
+               //filter_pattern = "102 0 0 0 0xFFFFFF 0x01005E";
+               pkt_filter->id = 102;
+               pkt_filter->type = 0;
+               pkt_filter->negate_match = 0;
+               pkt_filter->u.pattern.offset = 0;
+               pkt_filter->u.pattern.size_bytes = 3;
+               mask_and_pattern[0] = 0xFFFF;
+               mask_and_pattern[1] = 0x01FF;
+               mask_and_pattern[2] = 0x5E00;
+               break;
+       case BRCMF_MULTICAST6_FILTER_NUM:
+               //filter_pattern = "103 0 0 0 0xFFFF 0x3333";
+               pkt_filter->id = 103;
+               pkt_filter->type = 0;
+               pkt_filter->negate_match = 0;
+               pkt_filter->u.pattern.offset = 0;
+               pkt_filter->u.pattern.size_bytes = 2;
+               mask_and_pattern[0] = 0xFFFF;
+               mask_and_pattern[1] = 0x3333;
+               break;
+       case BRCMF_MDNS_FILTER_NUM:
+               //filter_pattern = "104 0 0 0 0xFFFFFFFFFFFF 0x01005E0000FB";
+               pkt_filter->id = 104;
+               pkt_filter->type = 0;
+               pkt_filter->negate_match = 0;
+               pkt_filter->u.pattern.offset = 0;
+               pkt_filter->u.pattern.size_bytes = 6;
+               mask_and_pattern[0] = 0xFFFF;
+               mask_and_pattern[1] = 0xFFFF;
+               mask_and_pattern[2] = 0xFFFF;
+               mask_and_pattern[3] = 0x0001;
+               mask_and_pattern[4] = 0x005E;
+               mask_and_pattern[5] = 0xFB00;
+               break;
+       case BRCMF_ARP_FILTER_NUM:
+               //filter_pattern = "105 0 0 12 0xFFFF 0x0806";
+               pkt_filter->id = 105;
+               pkt_filter->type = 0;
+               pkt_filter->negate_match = 0;
+               pkt_filter->u.pattern.offset = 12;
+               pkt_filter->u.pattern.size_bytes = 2;
+               mask_and_pattern[0] = 0xFFFF;
+               mask_and_pattern[1] = 0x0608;
+               break;
+       case BRCMF_BROADCAST_ARP_FILTER_NUM:
+               //filter_pattern = "106 0 0 0
+               //0xFFFFFFFFFFFF0000000000000806
+               //0xFFFFFFFFFFFF0000000000000806";
+               pkt_filter->id = 106;
+               pkt_filter->type = 0;
+               pkt_filter->negate_match = 0;
+               pkt_filter->u.pattern.offset = 0;
+               pkt_filter->u.pattern.size_bytes = 14;
+               mask_and_pattern[0] = 0xFFFF;
+               mask_and_pattern[1] = 0xFFFF;
+               mask_and_pattern[2] = 0xFFFF;
+               mask_and_pattern[3] = 0x0000;
+               mask_and_pattern[4] = 0x0000;
+               mask_and_pattern[5] = 0x0000;
+               mask_and_pattern[6] = 0x0608;
+               mask_and_pattern[7] = 0xFFFF;
+               mask_and_pattern[8] = 0xFFFF;
+               mask_and_pattern[9] = 0xFFFF;
+               mask_and_pattern[10] = 0x0000;
+               mask_and_pattern[11] = 0x0000;
+               mask_and_pattern[12] = 0x0000;
+               mask_and_pattern[13] = 0x0608;
+               break;
+       default:
+               ret = -EINVAL;
+               goto failed;
+       }
+       memcpy(pkt_filter->u.pattern.mask_and_pattern, mask_and_pattern,
+              pkt_filter->u.pattern.size_bytes * 2);
+       buflen = filter_fixed_len + pattern_fixed_len +
+                 pkt_filter->u.pattern.size_bytes * 2;
+
+       if (add) {
+               /* Add filter */
+               ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_add",
+                                              pkt_filter, buflen);
+               if (ret)
+                       goto failed;
+               drvr->pkt_filter[filter_num].id = pkt_filter->id;
+               drvr->pkt_filter[filter_num].enable  = 0;
+
+       } else {
+               /* Delete filter */
+               ret = brcmf_fil_iovar_int_set(ifp, "pkt_filter_delete",
+                                             pkt_filter->id);
+               if (ret == -ENOENT)
+                       ret = 0;
+               if (ret)
+                       goto failed;
+
+               drvr->pkt_filter[filter_num].id = 0;
+               drvr->pkt_filter[filter_num].enable  = 0;
+       }
+
+failed:
+       if (ret)
+               brcmf_err("%s packet filter failed, ret=%d\n",
+                         (add ? "add" : "remove"), ret);
+
+       kfree(pkt_filter);
+       return ret;
+}
+
+int brcmf_pktfilter_enable(struct net_device *ndev, bool enable)
+{
+       struct brcmf_if *ifp =  netdev_priv(ndev);
+       struct brcmf_pub *drvr = ifp->drvr;
+       int ret = 0;
+       int idx = 0;
+
+       for (idx = 0; idx < MAX_PKT_FILTER_COUNT; ++idx) {
+               if (drvr->pkt_filter[idx].id != 0) {
+                       drvr->pkt_filter[idx].enable = enable;
+                       ret = brcmf_fil_iovar_data_set(ifp, "pkt_filter_enable",
+                                                      &drvr->pkt_filter[idx],
+                               sizeof(struct brcmf_pkt_filter_enable_le));
+                       if (ret) {
+                               brcmf_err("%s packet filter id(%d) failed, 
ret=%d\n",
+                                         (enable ? "enable" : "disable"),
+                                         drvr->pkt_filter[idx].id, ret);
+                       }
+               }
+       }
+       return ret;
+}
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
index dcf6e27cc16f..96c58261a5d8 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/core.h
@@ -23,6 +23,7 @@
 
 #include <net/cfg80211.h>
 #include "fweh.h"
+#include "fwil_types.h"
 
 #define TOE_TX_CSUM_OL         0x00000001
 #define TOE_RX_CSUM_OL         0x00000002
@@ -36,7 +37,7 @@
 #define BRCMF_DCMD_MEDLEN      1536
 #define BRCMF_DCMD_MAXLEN      8192
 
-/* IOCTL from host to device are limited in lenght. A device can only handle
+/* IOCTL from host to device are limited in length. A device can only handle
  * ethernet frame size. This limitation is to be applied by protocol layer.
  */
 #define BRCMF_TX_IOCTL_MAX_MSG_SIZE    (ETH_FRAME_LEN+ETH_FCS_LEN)
@@ -144,6 +145,8 @@ struct brcmf_pub {
        struct brcmf_mp_device *settings;
 
        u8 clmver[BRCMF_DCMD_SMLEN];
+       struct brcmf_pkt_filter_enable_le pkt_filter[MAX_PKT_FILTER_COUNT];
+
 };
 
 /* forward declarations */
@@ -221,5 +224,7 @@ void brcmf_netif_mon_rx(struct brcmf_if *ifp, struct 
sk_buff *skb);
 void brcmf_net_setcarrier(struct brcmf_if *ifp, bool on);
 int __init brcmf_core_init(void);
 void __exit brcmf_core_exit(void);
-
+int brcmf_pktfilter_add_remove(struct net_device *ndev, int filter_num,
+                              bool add);
+int brcmf_pktfilter_enable(struct net_device *ndev, bool enable);
 #endif /* BRCMFMAC_CORE_H */
diff --git a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h 
b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
index 39ac1bbb6cc0..a1303efcbb3c 100644
--- a/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
+++ b/drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h
@@ -147,6 +147,19 @@
 #define BRCMF_WOWL_MAXPATTERNS         8
 #define BRCMF_WOWL_MAXPATTERNSIZE      128
 
+enum {
+       BRCMF_UNICAST_FILTER_NUM = 0,
+       BRCMF_BROADCAST_FILTER_NUM,
+       BRCMF_MULTICAST4_FILTER_NUM,
+       BRCMF_MULTICAST6_FILTER_NUM,
+       BRCMF_MDNS_FILTER_NUM,
+       BRCMF_ARP_FILTER_NUM,
+       BRCMF_BROADCAST_ARP_FILTER_NUM,
+       MAX_PKT_FILTER_COUNT
+};
+
+#define MAX_PKTFILTER_PATTERN_SIZE             16
+
 #define BRCMF_COUNTRY_BUF_SZ           4
 #define BRCMF_ANT_MAX                  4
 
-- 
2.1.0

Reply via email to