iwlwifi: mvm: support beacon storing
authorSara Sharon <sara.sharon@intel.com>
Tue, 29 Dec 2015 09:07:15 +0000 (11:07 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Mon, 1 Feb 2016 14:40:22 +0000 (16:40 +0200)
Currently firmware is configured to filter out beacons. In case
a beacon was changed - it is waking the host.
However, some vendors change their IEs frequently without any
significant change, and redundant wakeups are triggered as a
result.
As a solution disable beacon filtering when entering d0i3.
Instead, firmware will store the latest beacon and upon exiting
d0i3 it will send it up to the host, so the host can act upon
changes (if there were any).
This beacon will arrive as a dedicated notification - support it
as well.

Signed-off-by: Sara Sharon <sara.sharon@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/intel/iwlwifi/iwl-fw-file.h
drivers/net/wireless/intel/iwlwifi/mvm/d3.c
drivers/net/wireless/intel/iwlwifi/mvm/fw-api-d3.h
drivers/net/wireless/intel/iwlwifi/mvm/fw-api.h
drivers/net/wireless/intel/iwlwifi/mvm/mac-ctxt.c
drivers/net/wireless/intel/iwlwifi/mvm/mvm.h
drivers/net/wireless/intel/iwlwifi/mvm/ops.c
drivers/net/wireless/intel/iwlwifi/mvm/power.c

index dbf08c4936544fade60b9d650b89e9e59c22dcc8..5152987c8b8d895d29073300dc8a94436858a451 100644 (file)
@@ -315,6 +315,8 @@ typedef unsigned int __bitwise__ iwl_ucode_tlv_capa_t;
  * @IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT: supports bt-coex Multi-priority LUT
  * @IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION: firmware will decide on what
  *     antenna the beacon should be transmitted
+ * @IWL_UCODE_TLV_CAPA_BEACON_STORING: firmware will store the latest beacon
+ *     from AP and will send it upon d0i3 exit.
  * @IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2: support LAR API V2
  *
  * @NUM_IWL_UCODE_TLV_CAPA: number of bits used
@@ -346,6 +348,7 @@ enum iwl_ucode_tlv_capa {
        IWL_UCODE_TLV_CAPA_SHORT_PM_TIMEOUTS            = (__force iwl_ucode_tlv_capa_t)65,
        IWL_UCODE_TLV_CAPA_BT_MPLUT_SUPPORT             = (__force iwl_ucode_tlv_capa_t)67,
        IWL_UCODE_TLV_CAPA_BEACON_ANT_SELECTION         = (__force iwl_ucode_tlv_capa_t)71,
+       IWL_UCODE_TLV_CAPA_BEACON_STORING               = (__force iwl_ucode_tlv_capa_t)72,
        IWL_UCODE_TLV_CAPA_LAR_SUPPORT_V2               = (__force iwl_ucode_tlv_capa_t)73,
 
        NUM_IWL_UCODE_TLV_CAPA
index 78572ef89b26676e119175d766fdfcf5f0396e98..346376187ef849455ae1b60da0039cc2e48fd633 100644 (file)
@@ -853,7 +853,8 @@ iwl_mvm_get_wowlan_config(struct iwl_mvm *mvm,
        wowlan_config_cmd->is_11n_connection =
                                        ap_sta->ht_cap.ht_supported;
        wowlan_config_cmd->flags = ENABLE_L3_FILTERING |
-               ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING;
+               ENABLE_NBNS_FILTERING | ENABLE_DHCP_FILTERING |
+               ENABLE_STORE_BEACON;
 
        /* Query the last used seqno and set it */
        ret = iwl_mvm_get_last_nonqos_seq(mvm, vif);
index 62b9a0a96700738ad5df1c15a8d02305804fbb0d..eec52c57f71831014d550c917f5452015373d5fd 100644 (file)
@@ -251,6 +251,7 @@ enum iwl_wowlan_flags {
        ENABLE_L3_FILTERING     = BIT(1),
        ENABLE_NBNS_FILTERING   = BIT(2),
        ENABLE_DHCP_FILTERING   = BIT(3),
+       ENABLE_STORE_BEACON     = BIT(4),
 };
 
 struct iwl_wowlan_config_cmd {
index 82049bb139c26b2ae68f3d8a8ce191bde5f97de5..b6b57273b8ba53c127e382d68e9cf9220c922e2b 100644 (file)
@@ -280,11 +280,16 @@ enum iwl_phy_ops_subcmd_ids {
        DTS_MEASUREMENT_NOTIF_WIDE = 0xFF,
 };
 
+enum iwl_prot_offload_subcmd_ids {
+       STORED_BEACON_NTF = 0xFF,
+};
+
 /* command groups */
 enum {
        LEGACY_GROUP = 0x0,
        LONG_GROUP = 0x1,
        PHY_OPS_GROUP = 0x4,
+       PROT_OFFLOAD_GROUP = 0xb,
 };
 
 /**
@@ -1851,4 +1856,28 @@ struct iwl_shared_mem_cfg {
        __le32 page_buff_size;
 } __packed; /* SHARED_MEM_ALLOC_API_S_VER_1 */
 
+#define MAX_STORED_BEACON_SIZE 600
+
+/**
+ * Stored beacon notification
+ *
+ * @system_time: system time on air rise
+ * @tsf: TSF on air rise
+ * @beacon_timestamp: beacon on air rise
+ * @phy_flags: general phy flags: band, modulation, etc.
+ * @channel: channel this beacon was received on
+ * @rates: rate in ucode internal format
+ * @byte_count: frame's byte count
+ */
+struct iwl_stored_beacon_notif {
+       __le32 system_time;
+       __le64 tsf;
+       __le32 beacon_timestamp;
+       __le16 phy_flags;
+       __le16 channel;
+       __le32 rates;
+       __le32 byte_count;
+       u8 data[MAX_STORED_BEACON_SIZE];
+} __packed; /* WOWLAN_STROED_BEACON_INFO_S_VER_1 */
+
 #endif /* __fw_api_h__ */
index bf1e5eb5dbdba3657071ae1492e52b7073e6a9cd..62927f567afc7bc2e9827ab95d38cc2dabfaa50e 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -34,7 +34,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015 Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -1462,3 +1462,40 @@ void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
                                                   iwl_mvm_beacon_loss_iterator,
                                                   mb);
 }
+
+void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb)
+{
+       struct iwl_rx_packet *pkt = rxb_addr(rxb);
+       struct iwl_stored_beacon_notif *sb = (void *)pkt->data;
+       struct ieee80211_rx_status rx_status;
+       struct sk_buff *skb;
+       u32 size = le32_to_cpu(sb->byte_count);
+
+       if (size == 0)
+               return;
+
+       skb = alloc_skb(size, GFP_ATOMIC);
+       if (!skb) {
+               IWL_ERR(mvm, "alloc_skb failed\n");
+               return;
+       }
+
+       /* update rx_status according to the notification's metadata */
+       memset(&rx_status, 0, sizeof(rx_status));
+       rx_status.mactime = le64_to_cpu(sb->tsf);
+       rx_status.device_timestamp = le32_to_cpu(sb->system_time);
+       rx_status.band =
+               (sb->phy_flags & cpu_to_le16(RX_RES_PHY_FLAGS_BAND_24)) ?
+                               IEEE80211_BAND_2GHZ : IEEE80211_BAND_5GHZ;
+       rx_status.freq =
+               ieee80211_channel_to_frequency(le16_to_cpu(sb->channel),
+                                              rx_status.band);
+
+       /* copy the data */
+       memcpy(skb_put(skb, size), sb->data, size);
+       memcpy(IEEE80211_SKB_RXCB(skb), &rx_status, sizeof(rx_status));
+
+       /* pass it as regular rx to mac80211 */
+       ieee80211_rx_napi(mvm->hw, skb, NULL);
+}
index 216c8d6275a17cf11c0fe5fa14cf50787ec2ea17..b45cf4f63781a3f04216fd832126c888d1523e87 100644 (file)
@@ -1192,6 +1192,8 @@ void iwl_mvm_rx_beacon_notif(struct iwl_mvm *mvm,
                             struct iwl_rx_cmd_buffer *rxb);
 void iwl_mvm_rx_missed_beacons_notif(struct iwl_mvm *mvm,
                                     struct iwl_rx_cmd_buffer *rxb);
+void iwl_mvm_rx_stored_beacon_notif(struct iwl_mvm *mvm,
+                                   struct iwl_rx_cmd_buffer *rxb);
 void iwl_mvm_mac_ctxt_recalc_tsf_id(struct iwl_mvm *mvm,
                                    struct ieee80211_vif *vif);
 unsigned long iwl_mvm_get_used_hw_queues(struct iwl_mvm *mvm,
index 6bf2bde239a0bd9c40e01f448bc80986346eae3c..3c869ad6aaecff2c7ceb0c62e433ab1b3f982fa2 100644 (file)
@@ -33,6 +33,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2015 Intel Mobile Communications GmbH
+ * Copyright(c) 2016 Intel Deutschland GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -267,6 +268,8 @@ static const struct iwl_rx_handlers iwl_mvm_rx_handlers[] = {
                   true),
        RX_HANDLER(MFUART_LOAD_NOTIFICATION, iwl_mvm_rx_mfuart_notif, false),
        RX_HANDLER(TOF_NOTIFICATION, iwl_mvm_tof_resp_handler, true),
+       RX_HANDLER_GRP(PROT_OFFLOAD_GROUP, STORED_BEACON_NTF,
+                      iwl_mvm_rx_stored_beacon_notif, false),
 
 };
 #undef RX_HANDLER
@@ -386,10 +389,18 @@ static const struct iwl_hcmd_names iwl_mvm_phy_names[] = {
        HCMD_NAME(DTS_MEASUREMENT_NOTIF_WIDE),
 };
 
+/* Please keep this array *SORTED* by hex value.
+ * Access is done through binary search
+ */
+static const struct iwl_hcmd_names iwl_mvm_prot_offload_names[] = {
+       HCMD_NAME(STORED_BEACON_NTF),
+};
+
 static const struct iwl_hcmd_arr iwl_mvm_groups[] = {
        [LEGACY_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
        [LONG_GROUP] = HCMD_ARR(iwl_mvm_legacy_names),
        [PHY_OPS_GROUP] = HCMD_ARR(iwl_mvm_phy_names),
+       [PROT_OFFLOAD_GROUP] = HCMD_ARR(iwl_mvm_prot_offload_names),
 };
 
 /* this forward declaration can avoid to export the function */
@@ -1195,7 +1206,7 @@ static void iwl_mvm_set_wowlan_data(struct iwl_mvm *mvm,
        cmd->is_11n_connection = ap_sta->ht_cap.ht_supported;
        cmd->offloading_tid = iter_data->offloading_tid;
        cmd->flags = ENABLE_L3_FILTERING | ENABLE_NBNS_FILTERING |
-               ENABLE_DHCP_FILTERING;
+               ENABLE_DHCP_FILTERING | ENABLE_STORE_BEACON;
        /*
         * The d0i3 uCode takes care of the nonqos counters,
         * so configure only the qos seq ones.
@@ -1216,8 +1227,7 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
        struct iwl_wowlan_config_cmd wowlan_config_cmd = {
                .wakeup_filter = cpu_to_le32(IWL_WOWLAN_WAKEUP_RX_FRAME |
                                             IWL_WOWLAN_WAKEUP_BEACON_MISS |
-                                            IWL_WOWLAN_WAKEUP_LINK_CHANGE |
-                                            IWL_WOWLAN_WAKEUP_BCN_FILTERING),
+                                            IWL_WOWLAN_WAKEUP_LINK_CHANGE),
        };
        struct iwl_d3_manager_config d3_cfg_cmd = {
                .min_sleep_time = cpu_to_le32(1000),
@@ -1267,6 +1277,12 @@ int iwl_mvm_enter_d0i3(struct iwl_op_mode *op_mode)
 
        /* configure wowlan configuration only if needed */
        if (mvm->d0i3_ap_sta_id != IWL_MVM_STATION_COUNT) {
+               /* wake on beacons only if beacon storing isn't supported */
+               if (!fw_has_capa(&mvm->fw->ucode_capa,
+                                IWL_UCODE_TLV_CAPA_BEACON_STORING))
+                       wowlan_config_cmd.wakeup_filter |=
+                               cpu_to_le32(IWL_WOWLAN_WAKEUP_BCN_FILTERING);
+
                iwl_mvm_wowlan_config_key_params(mvm,
                                                 d0i3_iter_data.connected_vif,
                                                 true, flags);
index 16b3e36fb1ba7cd9ad6d7a108c996d4a15b266bf..0d03b3e3cf4e08f78ec8ba2cb25ef103d1ff94c6 100644 (file)
@@ -7,7 +7,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015        Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of version 2 of the GNU General Public License as
@@ -34,7 +34,7 @@
  *
  * Copyright(c) 2012 - 2014 Intel Corporation. All rights reserved.
  * Copyright(c) 2013 - 2014 Intel Mobile Communications GmbH
- * Copyright(c) 2015        Intel Deutschland GmbH
+ * Copyright(c) 2015 - 2016 Intel Deutschland GmbH
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -903,9 +903,9 @@ static int iwl_mvm_update_beacon_abort(struct iwl_mvm *mvm,
        return _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd, 0, false);
 }
 
-int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
-                                 struct ieee80211_vif *vif,
-                                 u32 flags)
+static int _iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
+                                         struct ieee80211_vif *vif,
+                                         u32 flags, bool d0i3)
 {
        struct iwl_beacon_filter_cmd cmd = {};
        struct iwl_mvm_vif *mvmvif = iwl_mvm_vif_from_mac80211(vif);
@@ -916,12 +916,20 @@ int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
 
        ret = iwl_mvm_beacon_filter_send_cmd(mvm, &cmd, flags);
 
-       if (!ret)
+       /* don't change bf_enabled in case of temporary d0i3 configuration */
+       if (!ret && !d0i3)
                mvmvif->bf_data.bf_enabled = false;
 
        return ret;
 }
 
+int iwl_mvm_disable_beacon_filter(struct iwl_mvm *mvm,
+                                 struct ieee80211_vif *vif,
+                                 u32 flags)
+{
+       return _iwl_mvm_disable_beacon_filter(mvm, vif, flags, false);
+}
+
 static int iwl_mvm_power_set_ps(struct iwl_mvm *mvm)
 {
        bool disable_ps;
@@ -1058,8 +1066,17 @@ int iwl_mvm_update_d0i3_power_mode(struct iwl_mvm *mvm,
                        IWL_BF_CMD_CONFIG_D0I3,
                        .bf_enable_beacon_filter = cpu_to_le32(1),
                };
-               ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf,
-                                                   flags, true);
+               /*
+                * When beacon storing is supported - disable beacon filtering
+                * altogether - the latest beacon will be sent when exiting d0i3
+                */
+               if (fw_has_capa(&mvm->fw->ucode_capa,
+                               IWL_UCODE_TLV_CAPA_BEACON_STORING))
+                       ret = _iwl_mvm_disable_beacon_filter(mvm, vif, flags,
+                                                            true);
+               else
+                       ret = _iwl_mvm_enable_beacon_filter(mvm, vif, &cmd_bf,
+                                                           flags, true);
        } else {
                if (mvmvif->bf_data.bf_enabled)
                        ret = iwl_mvm_enable_beacon_filter(mvm, vif, flags);