brcmfmac: add wowl gtk rekeying offload support
authorHante Meuleman <meuleman@broadcom.com>
Wed, 17 Feb 2016 10:27:03 +0000 (11:27 +0100)
committerKalle Valo <kvalo@codeaurora.org>
Mon, 7 Mar 2016 12:14:57 +0000 (14:14 +0200)
This patch adds support for gtk rekeying offload and for gtk
rekeying failure during wowl mode.

Reviewed-by: Arend Van Spriel <arend@broadcom.com>
Reviewed-by: Franky (Zhenhui) Lin <frankyl@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Hante Meuleman <meuleman@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/cfg80211.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.c
drivers/net/wireless/broadcom/brcm80211/brcmfmac/feature.h
drivers/net/wireless/broadcom/brcm80211/brcmfmac/fwil_types.h

index 71f1fdf2e7e816957fca08910c8fe09adc8e9080..6d849bb2e27c9b21029f9185424762d2e41b21a4 100644 (file)
@@ -3526,6 +3526,10 @@ static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
                        else
                                wakeup_data.net_detect = cfg->wowl.nd_info;
                }
+               if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
+                       brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
+                       wakeup_data.gtk_rekey_failure = true;
+               }
        } else {
                wakeup = NULL;
        }
@@ -3607,6 +3611,8 @@ static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
                brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
                                    brcmf_wowl_nd_results);
        }
+       if (wowl->gtk_rekey_failure)
+               wowl_config |= BRCMF_WOWL_GTK_FAILURE;
        if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
                wowl_config |= BRCMF_WOWL_UNASSOC;
 
@@ -4874,7 +4880,32 @@ static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
        return ret;
 }
 
-static struct cfg80211_ops wl_cfg80211_ops = {
+#ifdef CONFIG_PM
+static int
+brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
+                             struct cfg80211_gtk_rekey_data *gtk)
+{
+       struct brcmf_if *ifp = netdev_priv(ndev);
+       struct brcmf_gtk_keyinfo_le gtk_le;
+       int ret;
+
+       brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
+
+       memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
+       memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
+       memcpy(gtk_le.replay_counter, gtk->replay_ctr,
+              sizeof(gtk_le.replay_counter));
+
+       ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", &gtk_le,
+                                      sizeof(gtk_le));
+       if (ret < 0)
+               brcmf_err("gtk_key_info iovar failed: ret=%d\n", ret);
+
+       return ret;
+}
+#endif
+
+static struct cfg80211_ops brcmf_cfg80211_ops = {
        .add_virtual_intf = brcmf_cfg80211_add_iface,
        .del_virtual_intf = brcmf_cfg80211_del_iface,
        .change_virtual_intf = brcmf_cfg80211_change_iface,
@@ -6139,19 +6170,18 @@ 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);
-       s32 err;
-       u32 wowl_cap;
 
        if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
-               err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
-               if (!err) {
-                       if (wowl_cap & BRCMF_WOWL_PFN_FOUND) {
-                               brcmf_wowlan_support.flags |=
-                                                       WIPHY_WOWLAN_NET_DETECT;
-                               init_waitqueue_head(&cfg->wowl.nd_data_wait);
-                       }
+               if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
+                       brcmf_wowlan_support.flags |= WIPHY_WOWLAN_NET_DETECT;
+                       init_waitqueue_head(&cfg->wowl.nd_data_wait);
                }
        }
+       if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
+               brcmf_wowlan_support.flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
+               brcmf_wowlan_support.flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
+       }
+
        wiphy->wowlan = &brcmf_wowlan_support;
 #endif
 }
@@ -6538,6 +6568,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
        struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
        struct brcmf_cfg80211_info *cfg;
        struct wiphy *wiphy;
+       struct cfg80211_ops *ops;
        struct brcmf_cfg80211_vif *vif;
        struct brcmf_if *ifp;
        s32 err = 0;
@@ -6549,8 +6580,17 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
                return NULL;
        }
 
+       ops = kzalloc(sizeof(*ops), GFP_KERNEL);
+       if (!ops)
+               return NULL;
+
+       memcpy(ops, &brcmf_cfg80211_ops, sizeof(*ops));
        ifp = netdev_priv(ndev);
-       wiphy = wiphy_new(&wl_cfg80211_ops, sizeof(struct brcmf_cfg80211_info));
+#ifdef CONFIG_PM
+       if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
+               ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
+#endif
+       wiphy = wiphy_new(ops, sizeof(struct brcmf_cfg80211_info));
        if (!wiphy) {
                brcmf_err("Could not allocate wiphy device\n");
                return NULL;
@@ -6560,6 +6600,7 @@ struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
 
        cfg = wiphy_priv(wiphy);
        cfg->wiphy = wiphy;
+       cfg->ops = ops;
        cfg->pub = drvr;
        init_vif_event(&cfg->vif_event);
        INIT_LIST_HEAD(&cfg->vif_list);
@@ -6686,6 +6727,7 @@ priv_out:
        ifp->vif = NULL;
 wiphy_out:
        brcmf_free_wiphy(wiphy);
+       kfree(ops);
        return NULL;
 }
 
@@ -6696,6 +6738,7 @@ void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
 
        brcmf_btcoex_detach(cfg);
        wiphy_unregister(cfg->wiphy);
+       kfree(cfg->ops);
        wl_deinit_priv(cfg);
        brcmf_free_wiphy(cfg->wiphy);
 }
index 01f096f7a08941f049e0804b96becfc08a8b24a6..e4c1d436468461e61b853f7739fb089a007fd6a3 100644 (file)
@@ -256,6 +256,7 @@ struct brcmf_cfg80211_wowl {
  * struct brcmf_cfg80211_info - dongle private data of cfg80211 interface
  *
  * @wiphy: wiphy object for cfg80211 interface.
+ * @ops: pointer to copy of ops as registered with wiphy object.
  * @conf: dongle configuration.
  * @p2p: peer-to-peer specific information.
  * @btcoex: Bluetooth coexistence information.
@@ -288,6 +289,7 @@ struct brcmf_cfg80211_wowl {
  */
 struct brcmf_cfg80211_info {
        struct wiphy *wiphy;
+       struct cfg80211_ops *ops;
        struct brcmf_cfg80211_conf *conf;
        struct brcmf_p2p_info p2p;
        struct brcmf_btcoex_info *btcoex;
index 1ffa95f1b8d2b325df79f79e655b777df49162e3..098732a0c91b48264a56c1eb019b1f592d570761 100644 (file)
@@ -136,6 +136,7 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
 {
        struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
        struct brcmf_pno_macaddr_le pfn_mac;
+       u32 wowl_cap;
        s32 err;
 
        brcmf_feat_firmware_capabilities(ifp);
@@ -143,6 +144,17 @@ void brcmf_feat_attach(struct brcmf_pub *drvr)
        brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_PNO, "pfn");
        if (drvr->bus_if->wowl_supported)
                brcmf_feat_iovar_int_get(ifp, BRCMF_FEAT_WOWL, "wowl");
+       if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) {
+               err = brcmf_fil_iovar_int_get(ifp, "wowl_cap", &wowl_cap);
+               if (!err) {
+                       if (wowl_cap & BRCMF_WOWL_PFN_FOUND)
+                               ifp->drvr->feat_flags |=
+                                       BIT(BRCMF_FEAT_WOWL_ND);
+                       if (wowl_cap & BRCMF_WOWL_GTK_FAILURE)
+                               ifp->drvr->feat_flags |=
+                                       BIT(BRCMF_FEAT_WOWL_GTK);
+               }
+       }
        /* MBSS does not work for 43362 */
        if (drvr->bus_if->chip == BRCM_CC_43362_CHIP_ID)
                ifp->drvr->feat_flags &= ~BIT(BRCMF_FEAT_MBSS);
index 2e2479d413379b0b980433fd27b1a9b43950b503..f940c29bcfafb361b2c38319ee6d3c0ae1e43dde 100644 (file)
@@ -27,6 +27,8 @@
  * RSDB: Real Simultaneous Dual Band
  * TDLS: Tunneled Direct Link Setup
  * SCAN_RANDOM_MAC: Random MAC during (net detect) scheduled scan.
+ * WOWL_ND: WOWL net detect (PNO)
+ * WOWL_GTK: (WOWL) GTK rekeying offload
  */
 #define BRCMF_FEAT_LIST \
        BRCMF_FEAT_DEF(MBSS) \
@@ -36,7 +38,9 @@
        BRCMF_FEAT_DEF(P2P) \
        BRCMF_FEAT_DEF(RSDB) \
        BRCMF_FEAT_DEF(TDLS) \
-       BRCMF_FEAT_DEF(SCAN_RANDOM_MAC)
+       BRCMF_FEAT_DEF(SCAN_RANDOM_MAC) \
+       BRCMF_FEAT_DEF(WOWL_ND) \
+       BRCMF_FEAT_DEF(WOWL_GTK)
 
 /*
  * Quirks:
index e9e177dad1a6a66ff03d98a25c738fc325dc0b2f..6d41ae392a8fc815ade7592ede54afa4c96f12b5 100644 (file)
 /* Wakeup if received matched secured pattern: */
 #define BRCMF_WOWL_SECURE              (1 << 25)
 /* Wakeup on finding preferred network */
-#define BRCMF_WOWL_PFN_FOUND           (1 << 26)
+#define BRCMF_WOWL_PFN_FOUND           (1 << 27)
+/* Wakeup on receiving pairwise key EAP packets: */
+#define WIPHY_WOWL_EAP_PK              (1 << 28)
 /* Link Down indication in WoWL mode: */
 #define BRCMF_WOWL_LINKDOWN            (1 << 31)
 
 
 #define BRCMF_MCSSET_LEN               16
 
+#define BRCMF_RSN_KCK_LENGTH           16
+#define BRCMF_RSN_KEK_LENGTH           16
+#define BRCMF_RSN_REPLAY_LEN           8
+
 /* join preference types for join_pref iovar */
 enum brcmf_join_pref_types {
        BRCMF_JOIN_PREF_RSSI = 1,
@@ -789,4 +795,17 @@ struct brcmf_pktcnt_le {
        __le32 rx_ocast_good_pkt;
 };
 
+/**
+ * struct brcmf_gtk_keyinfo_le - GTP rekey data
+ *
+ * @kck: key confirmation key.
+ * @kek: key encryption key.
+ * @replay_counter: replay counter.
+ */
+struct brcmf_gtk_keyinfo_le {
+       u8 kck[BRCMF_RSN_KCK_LENGTH];
+       u8 kek[BRCMF_RSN_KEK_LENGTH];
+       u8 replay_counter[BRCMF_RSN_REPLAY_LEN];
+};
+
 #endif /* FWIL_TYPES_H_ */