iwlwifi: mvm: add SSID match information for net-detect
authorLuciano Coelho <luciano.coelho@intel.com>
Fri, 21 Nov 2014 07:32:23 +0000 (09:32 +0200)
committerEmmanuel Grumbach <emmanuel.grumbach@intel.com>
Mon, 1 Dec 2014 10:04:42 +0000 (12:04 +0200)
Add SSID information to the net-detect indication to userspace.  Fetch
the matched profiles from the firmware in order to find the correct
SSIDs in the profiles that matched.

Since the net-detect information is stored in cfg80211, and in theory
it could change while we are waking up and reading the matched
profiles, we need to save it when we enter D3 so it is available
during resume.

Signed-off-by: Luciano Coelho <luciano.coelho@intel.com>
Reviewed-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: Emmanuel Grumbach <emmanuel.grumbach@intel.com>
drivers/net/wireless/iwlwifi/mvm/d3.c
drivers/net/wireless/iwlwifi/mvm/mvm.h

index c7297c581488ccf651aaaf8c3e751f577f61c41a..c20d8de40fcded422b9e55e415042e72f2a6ccd1 100644 (file)
@@ -979,8 +979,30 @@ iwl_mvm_netdetect_config(struct iwl_mvm *mvm,
                return ret;
 
        ret = iwl_mvm_scan_offload_start(mvm, vif, nd_config, &mvm->nd_ies);
+       if (ret)
+               return ret;
 
-       return ret;
+       if (WARN_ON(mvm->nd_match_sets))
+               return -EBUSY;
+
+       /* save the sched scan matchsets for later reporting */
+       if (nd_config->n_match_sets) {
+               mvm->nd_match_sets = kmemdup(nd_config->match_sets,
+                                            sizeof(*nd_config->match_sets) *
+                                            nd_config->n_match_sets,
+                                            GFP_KERNEL);
+               if (mvm->nd_match_sets)
+                       mvm->n_nd_match_sets = nd_config->n_match_sets;
+       }
+
+       return 0;
+}
+
+static void iwl_mvm_free_nd(struct iwl_mvm *mvm)
+{
+       kfree(mvm->nd_match_sets);
+       mvm->nd_match_sets = NULL;
+       mvm->n_nd_match_sets = 0;
 }
 
 static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
@@ -1094,8 +1116,10 @@ static int __iwl_mvm_suspend(struct ieee80211_hw *hw,
 
        iwl_trans_d3_suspend(mvm->trans, test);
  out:
-       if (ret < 0)
+       if (ret < 0) {
                ieee80211_restart_hw(mvm->hw);
+               iwl_mvm_free_nd(mvm);
+       }
  out_noreset:
        mutex_unlock(&mvm->mutex);
 
@@ -1653,14 +1677,15 @@ out_free_resp:
 static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
                                            struct ieee80211_vif *vif)
 {
-       struct cfg80211_wowlan_nd_info net_detect = {};
+       struct cfg80211_wowlan_nd_info *net_detect = NULL;
        struct cfg80211_wowlan_wakeup wakeup = {
                .pattern_idx = -1,
        };
        struct cfg80211_wowlan_wakeup *wakeup_report = &wakeup;
        struct iwl_wowlan_status *fw_status;
-       u32 matched_profiles;
+       unsigned long matched_profiles;
        u32 reasons = 0;
+       int i, n_matches;
 
        fw_status = iwl_mvm_get_wakeup_status(mvm, vif);
        if (!IS_ERR_OR_NULL(fw_status))
@@ -1678,10 +1703,46 @@ static void iwl_mvm_query_netdetect_reasons(struct iwl_mvm *mvm,
                goto out;
        }
 
-       wakeup.net_detect = &net_detect;
+       if (mvm->n_nd_match_sets) {
+               n_matches = hweight_long(matched_profiles);
+       } else {
+               IWL_ERR(mvm, "no net detect match information available\n");
+               n_matches = 0;
+       }
+
+       net_detect = kzalloc(sizeof(*net_detect) +
+                            (n_matches * sizeof(net_detect->matches[0])),
+                            GFP_KERNEL);
+       if (!net_detect || !n_matches)
+               goto out_report_nd;
+
+       for_each_set_bit(i, &matched_profiles, mvm->n_nd_match_sets) {
+               struct cfg80211_wowlan_nd_match *match;
+
+               match = kzalloc(sizeof(*match), GFP_KERNEL);
+               if (!match)
+                       goto out_report_nd;
+
+               match->ssid.ssid_len = mvm->nd_match_sets[i].ssid.ssid_len;
+               memcpy(match->ssid.ssid, mvm->nd_match_sets[i].ssid.ssid,
+                      match->ssid.ssid_len);
+
+               net_detect->matches[net_detect->n_matches++] = match;
+       }
+
+out_report_nd:
+       wakeup.net_detect = net_detect;
 out:
+       iwl_mvm_free_nd(mvm);
+
        mutex_unlock(&mvm->mutex);
        ieee80211_report_wowlan_wakeup(vif, wakeup_report, GFP_KERNEL);
+
+       if (net_detect) {
+               for (i = 0; i < net_detect->n_matches; i++)
+                       kfree(net_detect->matches[i]);
+               kfree(net_detect);
+       }
 }
 
 static void iwl_mvm_read_d3_sram(struct iwl_mvm *mvm)
index 09d48333d35aa7bb41325b641c244dcbeba749ba..d69cc862234a158c5a963847743b1b585041601c 100644 (file)
@@ -685,6 +685,8 @@ struct iwl_mvm {
        /* sched scan settings for net detect */
        struct cfg80211_sched_scan_request *nd_config;
        struct ieee80211_scan_ies nd_ies;
+       struct cfg80211_match_set *nd_match_sets;
+       int n_nd_match_sets;
        bool net_detect;
 #ifdef CONFIG_IWLWIFI_DEBUGFS
        u32 d3_wake_sysassert; /* must be u32 for debugfs_create_bool */