wil6210: support FTM/AOA while unassociated

Prepare driver for FTM/AOA while unassociated.
1. Added attributes allowing user space to specify the frequency
where FTM/AOA peer is listening. When specified it will override
the entry in the kernel scan results cache. If not specified,
the kernel scan results cache will be used like today.
This gives user space location framework more flexibility - it
can maintain its own cache of peers, or perform scans less
often (since entries in the kernel scan results cache expire
quickly)
2. Remove associated check when starting FTM session, so session
can start even when unassociated.

Change-Id: Ib0ba52c506e1d8d59ab8c12043acd792405d69a0
CRs-Fixed: 1094147
Signed-off-by: Lior David <liord@codeaurora.org>
Signed-off-by: Maya Erez <merez@codeaurora.org>
diff --git a/drivers/net/wireless/ath/wil6210/ftm.c b/drivers/net/wireless/ath/wil6210/ftm.c
index 1d4dcd7..f94c894 100644
--- a/drivers/net/wireless/ath/wil6210/ftm.c
+++ b/drivers/net/wireless/ath/wil6210/ftm.c
@@ -52,6 +52,7 @@
 	[QCA_WLAN_VENDOR_ATTR_FTM_INITIAL_TOKEN] = { .type = NLA_U8 },
 	[QCA_WLAN_VENDOR_ATTR_AOA_TYPE] = { .type = NLA_U32 },
 	[QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK] = { .type = NLA_U32 },
+	[QCA_WLAN_VENDOR_ATTR_FREQ] = { .type = NLA_U32 },
 };
 
 static const struct
@@ -61,6 +62,7 @@
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS] = { .type = NLA_U32 },
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS] = { .type = NLA_NESTED },
 	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID] = { .type = NLA_U8 },
+	[QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ] = { .type = NLA_U32 },
 };
 
 static const struct
@@ -72,6 +74,37 @@
 	[QCA_WLAN_VENDOR_ATTR_FTM_PARAM_BURST_PERIOD] = { .type = NLA_U16 },
 };
 
+static u8 wil_ftm_get_channel(struct wil6210_priv *wil,
+			      const u8 *mac_addr, u32 freq)
+{
+	struct wiphy *wiphy = wil_to_wiphy(wil);
+	struct cfg80211_bss *bss;
+	struct ieee80211_channel *chan;
+	u8 channel;
+
+	if (freq) {
+		chan = ieee80211_get_channel(wiphy, freq);
+		if (!chan) {
+			wil_err(wil, "invalid freq: %d\n", freq);
+			return 0;
+		}
+		channel = chan->hw_value;
+	} else {
+		bss = cfg80211_get_bss(wiphy, NULL, mac_addr,
+				       NULL, 0, IEEE80211_BSS_TYPE_ANY,
+				       IEEE80211_PRIVACY_ANY);
+		if (!bss) {
+			wil_err(wil, "Unable to find BSS\n");
+			return 0;
+		}
+		channel = bss->channel->hw_value;
+		cfg80211_put_bss(wiphy, bss);
+	}
+
+	wil_dbg_misc(wil, "target %pM at channel %d\n", mac_addr, channel);
+	return channel;
+}
+
 static int wil_ftm_parse_meas_params(struct wil6210_priv *wil,
 				     struct nlattr *attr,
 				     struct wil_ftm_meas_params *params)
@@ -277,7 +310,7 @@
 {
 	int rc = 0;
 	bool has_lci = false, has_lcr = false;
-	u8 max_meas = 0, *ptr;
+	u8 max_meas = 0, channel, *ptr;
 	u32 i, cmd_len;
 	struct wmi_tof_session_start_cmd *cmd;
 
@@ -287,12 +320,6 @@
 		rc = -EAGAIN;
 		goto out;
 	}
-	/* for now allow measurement to associated AP only */
-	if (!test_bit(wil_status_fwconnected, wil->status)) {
-		wil_err(wil, "must be associated\n");
-		rc = -ENOTSUPP;
-		goto out;
-	}
 
 	for (i = 0; i < request->n_peers; i++) {
 		if (request->peers[i].flags &
@@ -337,7 +364,14 @@
 	for (i = 0; i < request->n_peers; i++) {
 		ether_addr_copy(cmd->ftm_dest_info[i].dst_mac,
 				request->peers[i].mac_addr);
-		cmd->ftm_dest_info[i].channel = request->peers[i].channel;
+		channel = wil_ftm_get_channel(wil, request->peers[i].mac_addr,
+					      request->peers[i].freq);
+		if (!channel) {
+			wil_err(wil, "can't find FTM target at index %d\n", i);
+			rc = -EINVAL;
+			goto out_cmd;
+		}
+		cmd->ftm_dest_info[i].channel = channel - 1;
 		if (request->peers[i].flags &
 		    QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAG_SECURE) {
 			cmd->ftm_dest_info[i].flags |=
@@ -371,14 +405,13 @@
 	}
 
 	rc = wmi_send(wil, WMI_TOF_SESSION_START_CMDID, cmd, cmd_len);
+
+	if (!rc) {
+		wil->ftm.session_cookie = request->session_cookie;
+		wil->ftm.session_started = 1;
+	}
+out_cmd:
 	kfree(cmd);
-
-	if (rc)
-		goto out_ftm_res;
-
-	wil->ftm.session_cookie = request->session_cookie;
-	wil->ftm.session_started = 1;
-
 out_ftm_res:
 	if (rc) {
 		kfree(wil->ftm.ftm_res);
@@ -449,8 +482,8 @@
 				   struct wil_aoa_meas_request *request)
 {
 	int rc = 0;
-	struct cfg80211_bss *bss;
 	struct wmi_aoa_meas_cmd cmd;
+	u8 channel;
 
 	mutex_lock(&wil->ftm.lock);
 
@@ -465,30 +498,25 @@
 		goto out;
 	}
 
-	bss = cfg80211_get_bss(wil_to_wiphy(wil), NULL, request->mac_addr,
-			       NULL, 0,
-			       IEEE80211_BSS_TYPE_ANY, IEEE80211_PRIVACY_ANY);
-	if (!bss) {
-		wil_err(wil, "Unable to find BSS\n");
-		rc = -ENOENT;
+	channel = wil_ftm_get_channel(wil, request->mac_addr, request->freq);
+	if (!channel) {
+		rc = -EINVAL;
 		goto out;
 	}
 
 	memset(&cmd, 0, sizeof(cmd));
 	ether_addr_copy(cmd.mac_addr, request->mac_addr);
-	cmd.channel = bss->channel->hw_value - 1;
+	cmd.channel = channel - 1;
 	cmd.aoa_meas_type = request->type;
 
 	rc = wmi_send(wil, WMI_AOA_MEAS_CMDID, &cmd, sizeof(cmd));
 	if (rc)
-		goto out_bss;
+		goto out;
 
 	ether_addr_copy(wil->ftm.aoa_peer_mac_addr, request->mac_addr);
 	mod_timer(&wil->ftm.aoa_timer,
 		  jiffies + msecs_to_jiffies(WIL_AOA_MEASUREMENT_TIMEOUT));
 	wil->ftm.aoa_started = 1;
-out_bss:
-	cfg80211_put_bss(wil_to_wiphy(wil), bss);
 out:
 	mutex_unlock(&wil->ftm.lock);
 	return rc;
@@ -726,7 +754,6 @@
 	struct nlattr *tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX + 1];
 	struct nlattr *peer;
 	int rc, n_peers = 0, index = 0, tmp;
-	struct cfg80211_bss *bss;
 
 	if (!test_bit(WMI_FW_CAPABILITY_FTM, wil->fw_capabilities))
 		return -ENOTSUPP;
@@ -790,17 +817,9 @@
 		memcpy(request->peers[index].mac_addr,
 		       nla_data(tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAC_ADDR]),
 		       ETH_ALEN);
-		bss = cfg80211_get_bss(wiphy, NULL,
-				       request->peers[index].mac_addr, NULL, 0,
-				       IEEE80211_BSS_TYPE_ANY,
-				       IEEE80211_PRIVACY_ANY);
-		if (!bss) {
-			wil_err(wil, "invalid bss at index %d\n", index);
-			rc = -ENOENT;
-			goto out;
-		}
-		request->peers[index].channel = bss->channel->hw_value - 1;
-		cfg80211_put_bss(wiphy, bss);
+		if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ])
+			request->peers[index].freq = nla_get_u32(
+				tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ]);
 		if (tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS])
 			request->peers[index].flags = nla_get_u32(
 				tb2[QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_FLAGS]);
@@ -873,6 +892,8 @@
 	ether_addr_copy(request.mac_addr,
 			nla_data(tb[QCA_WLAN_VENDOR_ATTR_MAC_ADDR]));
 	request.type = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_AOA_TYPE]);
+	if (tb[QCA_WLAN_VENDOR_ATTR_FREQ])
+		request.freq = nla_get_u32(tb[QCA_WLAN_VENDOR_ATTR_FREQ]);
 
 	rc = wil_aoa_cfg80211_start_measurement(wil, &request);
 	return rc;
diff --git a/drivers/net/wireless/ath/wil6210/ftm.h b/drivers/net/wireless/ath/wil6210/ftm.h
index 9721344..46936f7 100644
--- a/drivers/net/wireless/ath/wil6210/ftm.h
+++ b/drivers/net/wireless/ath/wil6210/ftm.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -77,6 +77,8 @@
  *  %QCA_WLAN_VENDOR_ATTR_AOA_TYPE_TOP_CIR_PHASE_AMP: array of 2 U16
  *  values, phase and amplitude of the strongest CIR path for each
  *  antenna in the measured array(s)
+ * @QCA_WLAN_VENDOR_ATTR_FREQ: Frequency where peer is listening, in MHz.
+ *  Unsigned 32 bit value.
  */
 enum qca_wlan_vendor_attr_loc {
 	/* we reuse these attributes */
@@ -94,6 +96,7 @@
 	QCA_WLAN_VENDOR_ATTR_AOA_TYPE = 23,
 	QCA_WLAN_VENDOR_ATTR_LOC_ANTENNA_ARRAY_MASK = 24,
 	QCA_WLAN_VENDOR_ATTR_AOA_MEAS_RESULT = 25,
+	QCA_WLAN_VENDOR_ATTR_FREQ = 26,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_LOC_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_LOC_MAX = QCA_WLAN_VENDOR_ATTR_LOC_AFTER_LAST - 1,
@@ -176,6 +179,9 @@
  * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD: Request AOA
  *  measurement every _value_ bursts. If 0 or not specified,
  *  AOA measurements will be disabled for this peer.
+ * @QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ: Frequency in MHz where
+ *  peer is listening. Optional; if not specified, use the
+ *  entry from the kernel scan results cache.
  */
 enum qca_wlan_vendor_attr_ftm_peer_info {
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_INVALID,
@@ -184,6 +190,7 @@
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MEAS_PARAMS,
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_SECURE_TOKEN_ID,
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_AOA_BURST_PERIOD,
+	QCA_WLAN_VENDOR_ATTR_FTM_PEER_FREQ,
 	/* keep last */
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_AFTER_LAST,
 	QCA_WLAN_VENDOR_ATTR_FTM_PEER_MAX =
@@ -426,7 +433,7 @@
 /* measurement request for a single peer */
 struct wil_ftm_meas_peer_info {
 	u8 mac_addr[ETH_ALEN];
-	u8 channel;
+	u32 freq;
 	u32 flags; /* enum qca_wlan_vendor_attr_ftm_peer_meas_flags */
 	struct wil_ftm_meas_params params;
 	u8 secure_token_id;
@@ -465,6 +472,7 @@
 /* standalone AOA measurement request */
 struct wil_aoa_meas_request {
 	u8 mac_addr[ETH_ALEN];
+	u32 freq;
 	u32 type;
 };