linux-wireless.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/2] Add support for ACS offload for AP
@ 2018-12-16 13:58 Ahmad Masri
  2018-12-16 13:58 ` [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) " Ahmad Masri
  2018-12-16 13:58 ` [PATCH 2/2] wil6210: add implementation of acs cfg80211_ops Ahmad Masri
  0 siblings, 2 replies; 6+ messages in thread
From: Ahmad Masri @ 2018-12-16 13:58 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Ahmad Masri, linux-wireless, wil6210

The following set of patches add support for Automatic Channel Selection
(ACS) offload for AP in cfg80211 and an implementation in wil6210 driver.

Ahmad Masri (2):
  cfg80211: Add Automatic Channel Selection (ACS) offload for AP
  wil6210: add implementation of acs cfg80211_ops

 drivers/net/wireless/ath/wil6210/cfg80211.c | 140 ++++++++++++++++++++-
 drivers/net/wireless/ath/wil6210/debugfs.c  | 154 +++++++++++++++++++++++
 drivers/net/wireless/ath/wil6210/main.c     |   5 +
 drivers/net/wireless/ath/wil6210/wil6210.h  |  15 +++
 drivers/net/wireless/ath/wil6210/wmi.c      |  76 ++++++++++++
 drivers/net/wireless/ath/wil6210/wmi.h      |   3 +-
 include/net/cfg80211.h                      |  33 +++++
 include/uapi/linux/nl80211.h                |  65 ++++++++++
 net/wireless/mlme.c                         |  31 +++++
 net/wireless/nl80211.c                      | 182 ++++++++++++++++++++++++++++
 net/wireless/nl80211.h                      |   6 +
 net/wireless/rdev-ops.h                     |  12 ++
 net/wireless/trace.h                        |  23 ++++
 13 files changed, 742 insertions(+), 3 deletions(-)

-- 
1.9.1


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) offload for AP
  2018-12-16 13:58 [PATCH 0/2] Add support for ACS offload for AP Ahmad Masri
@ 2018-12-16 13:58 ` Ahmad Masri
  2018-12-17 11:03   ` Kalle Valo
  2018-12-17 13:41   ` Johannes Berg
  2018-12-16 13:58 ` [PATCH 2/2] wil6210: add implementation of acs cfg80211_ops Ahmad Masri
  1 sibling, 2 replies; 6+ messages in thread
From: Ahmad Masri @ 2018-12-16 13:58 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Ahmad Masri, linux-wireless, wil6210

Add ACS offload to let the device select a channel for AP from
a list of channels that the user-space provides. ACS can customize
the method of AP channel selection and add parameters like traffic
load, number of APs on channel and more.

Change-Id: I18cb8460b9418512ac7ac9f76fe8f7f379f2478b
Signed-off-by: Ahmad Masri <amasri@codeaurora.org>
---
 include/net/cfg80211.h       |  33 ++++++++
 include/uapi/linux/nl80211.h |  65 ++++++++++++++++
 net/wireless/mlme.c          |  31 ++++++++
 net/wireless/nl80211.c       | 182 +++++++++++++++++++++++++++++++++++++++++++
 net/wireless/nl80211.h       |   6 ++
 net/wireless/rdev-ops.h      |  12 +++
 net/wireless/trace.h         |  23 ++++++
 7 files changed, 352 insertions(+)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index ede7fcd..c3faa48 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -715,6 +715,17 @@ struct survey_info {
 	s8 noise;
 };
 
+/**
+ * struct cfg80211_acs_params - ACS parameters
+ * @n_channels: total number of channels for ACS
+ * @channels: list of chan_def to run ACS on
+ */
+struct cfg80211_acs_params {
+	u32 n_channels;
+	/* keep last */
+	struct cfg80211_chan_def channels[0];
+};
+
 #define CFG80211_MAX_WEP_KEYS	4
 
 /**
@@ -3377,6 +3388,11 @@ struct cfg80211_pmsr_request {
  *	Statistics should be cumulative, currently no way to reset is provided.
  * @start_pmsr: start peer measurement (e.g. FTM)
  * @abort_pmsr: abort peer measurement
+ * @acs: run automatic channel selection offload measurement to find the best
+ *	channel to start the AP on. Userspace provide a list of chan_defs, the
+ *	driver should perform ACS on the provided list and select best channel.
+ *	driver should call cfg80211_acs_result to complete this operation.
+ *	(invoked with the wireless_dev mutex held)
  */
 struct cfg80211_ops {
 	int	(*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -3691,6 +3707,8 @@ struct cfg80211_ops {
 			      struct cfg80211_pmsr_request *request);
 	void	(*abort_pmsr)(struct wiphy *wiphy, struct wireless_dev *wdev,
 			      struct cfg80211_pmsr_request *request);
+	int	(*acs)(struct wiphy *wiphy, struct net_device *dev,
+		       struct cfg80211_acs_params *params);
 };
 
 /*
@@ -4662,6 +4680,7 @@ struct wireless_dev {
 	unsigned long cac_start_time;
 	unsigned int cac_time_ms;
 
+	bool acs_started;
 #ifdef CONFIG_CFG80211_WEXT
 	/* wext data */
 	struct {
@@ -6362,6 +6381,20 @@ void cfg80211_cac_event(struct net_device *netdev,
 
 
 /**
+ * cfg80211_acs_result - ACS result notification
+ * @netdev: network device
+ * @chandef: chandef for the selected channel, NULL on unsuccessful operation
+ * @status: ACS status as specified in &enum nl80211_acs_status
+ * @gfp: context flags
+ *
+ * This function is called when ACS measurement is finished or aborted.
+ * This must be called to notify the completion of a ACS process.
+ */
+void cfg80211_acs_result(struct net_device *netdev,
+			 const struct cfg80211_chan_def *chandef,
+			 enum nl80211_acs_status status, gfp_t gfp);
+
+/**
  * cfg80211_gtk_rekey_notify - notify userspace about driver rekeying
  * @dev: network device
  * @bssid: BSSID of AP (to avoid races)
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 2b53c0e..b993b86f 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1032,6 +1032,13 @@
  *	ht opmode or vht opmode changes using any of %NL80211_ATTR_SMPS_MODE,
  *	%NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its
  *	address(specified in %NL80211_ATTR_MAC).
+ * @NL80211_CMD_ACS: For offloaded Automatic Channel Selection (ACS). Userspace
+ *	sends this command before starting an AP, the kernel indicates this
+ *	command after a channel was selected. When sent from userspace has
+ *	attribute NL80211_ATTR_CHAN_DEF for a list of channels (chan_def).
+ *	When sent from kernel has attributes NL80211_ATTR_ACS_STATUS which
+ *	provides the operation status and NL80211_ATTR_WIPHY_FREQ and other
+ *	chan_def attributes which describes the chosen channel.
  *
  * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in
  *	the %NL80211_ATTR_FTM_RESPONDER_STATS attribute.
@@ -1277,6 +1284,7 @@ enum nl80211_commands {
 	NL80211_CMD_PEER_MEASUREMENT_START,
 	NL80211_CMD_PEER_MEASUREMENT_RESULT,
 	NL80211_CMD_PEER_MEASUREMENT_COMPLETE,
+	NL80211_CMD_ACS,
 
 	/* add new commands above here */
 
@@ -2273,6 +2281,13 @@ enum nl80211_commands {
  * @NL80211_ATTR_HE_CAPABILITY: HE Capability information element (from
  *	association request when used with NL80211_CMD_NEW_STATION). Can be set
  *	only if %NL80211_STA_FLAG_WME is set.
+ * @NL80211_ATTR_ACS_STATUS: attribute in which kernel indicates ACS status
+ *	as defined in &enum nl80211_acs_status
+ * @NL80211_ATTR_CHAN_DEF: attribute for nesting chan_def parameters
+ *	as defined in &enum nl80211_ch_def_attr. The new attribute allows
+ *	userspace to send a list of struct cfg80211_chan_def. For example, ACS
+ *	command uses this attribute for sending a list of channels, for more
+ *	details see &NL80211_CMD_ACS.
  *
  * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include
  *	in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing
@@ -2741,6 +2756,9 @@ enum nl80211_attrs {
 
 	NL80211_ATTR_PEER_MEASUREMENTS,
 
+	NL80211_ATTR_ACS_STATUS,
+	NL80211_ATTR_CHAN_DEF,
+
 	/* add attributes here, update the policy in nl80211.c */
 
 	__NL80211_ATTR_AFTER_LAST,
@@ -4548,6 +4566,35 @@ enum nl80211_packet_pattern_attr {
 };
 
 /**
+ * enum nl80211_ch_def_attr - channel def attribute
+ * @__NL80211_ATTR_CH_DEF_INVALID: invalid number for nested attribute
+ * @NL80211_ATTR_CH_DEF_FREQ: frequency of the selected channel in MHz,
+ *	defines the channel together with the attributes
+ *	%NL80211_ATTR_CH_DEF_WIDTH and if needed %NL80211_ATTR_CH_DEF_FREQ1 and
+ *	%NL80211_ATTR_CH_DEF_FREQ2
+ * @NL80211_ATTR_CH_DEF_WIDTH: u32 attribute containing one of the values
+ *	of &enum nl80211_chan_width, describing the channel width. See the
+ *	documentation of the enum for more information.
+ * @NL80211_ATTR_CH_DEF_FREQ1: Center frequency of the first part of the
+ *	channel, used for anything but 20 MHz bandwidth
+ * @NL80211_ATTR_CH_DEF_FREQ2: Center frequency of the second part of the
+ *	channel, used only for 80+80 MHz bandwidth
+ * @__NL80211_ATTR_CH_DEF_AFTER_LAST: internal
+ * @NL80211_ATTR_CH_DEF_MAX: max attribute number
+ */
+enum nl80211_ch_def_attr {
+	__NL80211_ATTR_CH_DEF_INVALID,
+	NL80211_ATTR_CH_DEF_FREQ,
+	NL80211_ATTR_CH_DEF_WIDTH,
+	NL80211_ATTR_CH_DEF_FREQ1,
+	NL80211_ATTR_CH_DEF_FREQ2,
+
+	/* keep last */
+	__NL80211_ATTR_CH_DEF_AFTER_LAST,
+	NL80211_ATTR_CH_DEF_MAX = __NL80211_ATTR_CH_DEF_AFTER_LAST - 1
+};
+
+/**
  * struct nl80211_pattern_support - packet pattern support information
  * @max_patterns: maximum number of patterns supported
  * @min_pattern_len: minimum length of each pattern
@@ -5308,6 +5355,8 @@ enum nl80211_feature_flags {
  *      able to rekey an in-use key correctly. Userspace must not rekey PTK keys
  *      if this flag is not set. Ignoring this can leak clear text packets and/or
  *      freeze the connection.
+ * @NL80211_EXT_FEATURE_ACS_OFFLOAD: The driver supports offload of ACS from
+ *	a list of channels provided by the userspace.
  *
  * @NUM_NL80211_EXT_FEATURES: number of extended features.
  * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -5348,6 +5397,7 @@ enum nl80211_ext_feature_index {
 	NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
 	NL80211_EXT_FEATURE_CAN_REPLACE_PTK0,
 	NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
+	NL80211_EXT_FEATURE_ACS_OFFLOAD,
 
 	/* add new features before the definition below */
 	NUM_NL80211_EXT_FEATURES,
@@ -5561,6 +5611,21 @@ enum nl80211_dfs_state {
 };
 
 /**
+ * enum nl80211_acs_status - ACS status
+ *
+ * status to be used to inform userspace about the result of the ACS offloaded
+ * measurement.
+ *
+ * @NL80211_ACS_SUCCESS: The ACS operation finished successfully
+ * @NL80211_ACS_FAILED: Failed to run the ACS. Userspace should choose a channel
+ *	by itself.
+ */
+enum nl80211_acs_status {
+	NL80211_ACS_SUCCESS,
+	NL80211_ACS_FAILED,
+};
+
+/**
  * enum enum nl80211_protocol_features - nl80211 protocol features
  * @NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP: nl80211 supports splitting
  *	wiphy dumps (if requested by the application with the attribute
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 1615e50..ed36d91 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -903,3 +903,34 @@ void cfg80211_cac_event(struct net_device *netdev,
 	nl80211_radar_notify(rdev, chandef, event, netdev, gfp);
 }
 EXPORT_SYMBOL(cfg80211_cac_event);
+
+void cfg80211_acs_result(struct net_device *netdev,
+			 const struct cfg80211_chan_def *chandef,
+			 enum nl80211_acs_status status, gfp_t gfp)
+{
+	struct wireless_dev *wdev = netdev->ieee80211_ptr;
+	struct wiphy *wiphy = wdev->wiphy;
+	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
+
+	if (WARN_ON(!wdev->acs_started))
+		return;
+
+	switch (status) {
+	case NL80211_ACS_SUCCESS:
+		if (!chandef) {
+			WARN_ON(1);
+			goto out;
+		}
+	case NL80211_ACS_FAILED:
+		break;
+	default:
+		WARN_ON(1);
+		goto out;
+	}
+	nl80211_acs_notify(rdev, chandef, status, netdev, gfp);
+
+out:
+	wdev->acs_started = false;
+	dev_put(netdev);
+}
+EXPORT_SYMBOL(cfg80211_acs_result);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 5ec200e..bf25824 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -557,6 +557,8 @@ static int validate_ie_attr(const struct nlattr *attr,
 	[NL80211_ATTR_PEER_MEASUREMENTS] =
 		NLA_POLICY_NESTED(NL80211_PMSR_FTM_REQ_ATTR_MAX,
 				  nl80211_pmsr_attr_policy),
+
+	[NL80211_ATTR_CHAN_DEF] = { .type = NLA_NESTED },
 };
 
 /* policy for the key attributes */
@@ -697,6 +699,15 @@ static int validate_ie_attr(const struct nlattr *attr,
 	[NL80211_PKTPAT_OFFSET] = { .type = NLA_U32 },
 };
 
+/* policy for channel attributes */
+static const struct nla_policy
+nl80211_ch_def_policy[NL80211_ATTR_CH_DEF_MAX + 1] = {
+	[NL80211_ATTR_CH_DEF_FREQ] = { .type = NLA_U32 },
+	[NL80211_ATTR_CH_DEF_WIDTH] = { .type = NLA_U32 },
+	[NL80211_ATTR_CH_DEF_FREQ1] = { .type = NLA_U32 },
+	[NL80211_ATTR_CH_DEF_FREQ2] = { .type = NLA_U32 },
+};
+
 int nl80211_prepare_wdev_dump(struct netlink_callback *cb,
 			      struct cfg80211_registered_device **rdev,
 			      struct wireless_dev **wdev)
@@ -2561,6 +2572,66 @@ int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
 	return 0;
 }
 
+static int
+nl80211_parse_chandef_new(struct cfg80211_registered_device *rdev,
+			  struct genl_info *info, struct nlattr *channel,
+			  struct cfg80211_chan_def *chandef)
+{
+	u32 control_freq;
+	struct nlattr *attrs[NL80211_ATTR_CH_DEF_MAX + 1];
+	int err;
+
+	if (!channel)
+		return -EINVAL;
+
+	err = nla_parse_nested(attrs, NL80211_ATTR_CH_DEF_MAX, channel,
+			       nl80211_ch_def_policy, info->extack);
+	if (err)
+		return err;
+
+
+	if (!attrs[NL80211_ATTR_CH_DEF_FREQ])
+		return -EINVAL;
+
+	control_freq = nla_get_u32(attrs[NL80211_ATTR_CH_DEF_FREQ]);
+
+	chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
+	chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+	chandef->center_freq1 = control_freq;
+	chandef->center_freq2 = 0;
+
+	/* Primary channel not allowed */
+	if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
+		return -EINVAL;
+
+	if (attrs[NL80211_ATTR_CH_DEF_WIDTH]) {
+		chandef->width =
+			nla_get_u32(attrs[NL80211_ATTR_CH_DEF_WIDTH]);
+		if (attrs[NL80211_ATTR_CH_DEF_FREQ1])
+			chandef->center_freq1 =
+				nla_get_u32(
+					attrs[NL80211_ATTR_CH_DEF_FREQ1]);
+		if (attrs[NL80211_ATTR_CH_DEF_FREQ2])
+			chandef->center_freq2 =
+				nla_get_u32(
+					attrs[NL80211_ATTR_CH_DEF_FREQ2]);
+	}
+
+	if (!cfg80211_chandef_valid(chandef))
+		return -EINVAL;
+
+	if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
+				     IEEE80211_CHAN_DISABLED))
+		return -EINVAL;
+
+	if ((chandef->width == NL80211_CHAN_WIDTH_5 ||
+	     chandef->width == NL80211_CHAN_WIDTH_10) &&
+	    !(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_5_10_MHZ))
+		return -EINVAL;
+
+	return 0;
+}
+
 static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
 				 struct net_device *dev,
 				 struct genl_info *info)
@@ -13140,6 +13211,71 @@ static int nl80211_get_ftm_responder_stats(struct sk_buff *skb,
 	return -ENOBUFS;
 }
 
+static int nl80211_acs(struct sk_buff *skb, struct genl_info *info)
+{
+	struct cfg80211_registered_device *rdev = info->user_ptr[0];
+	struct net_device *dev = info->user_ptr[1];
+	struct wireless_dev *wdev = dev->ieee80211_ptr;
+	struct cfg80211_acs_params *request;
+	struct nlattr *attr;
+	struct wiphy *wiphy;
+	int ret, tmp, n_channels = 0, i = 0;
+
+	if (WARN_ON(wdev->acs_started))
+		return -EALREADY;
+
+	if (!rdev->ops->acs)
+		return -EOPNOTSUPP;
+
+	if (wdev->iftype != NL80211_IFTYPE_AP)
+		return -EOPNOTSUPP;
+
+	wiphy = &rdev->wiphy;
+
+	if (!info->attrs[NL80211_ATTR_CHAN_DEF])
+		return -EINVAL;
+
+	nla_for_each_nested(attr,
+			    info->attrs[NL80211_ATTR_CHAN_DEF], tmp)
+		n_channels++;
+
+	if (!n_channels)
+		return -EINVAL;
+
+	request = kzalloc(sizeof(*request) +
+			  sizeof(struct cfg80211_chan_def) * n_channels,
+			  GFP_KERNEL);
+	if (!request)
+		return -ENOMEM;
+
+	nla_for_each_nested(attr,
+			    info->attrs[NL80211_ATTR_CHAN_DEF], tmp) {
+		struct cfg80211_chan_def chandef;
+
+		ret = nl80211_parse_chandef_new(rdev, info, attr, &chandef);
+		if (ret)
+			goto out_free;
+
+		request->channels[i++] = chandef;
+	}
+	request->n_channels = i;
+
+	wdev->acs_started = true;
+	dev_hold(dev);
+
+	wdev_lock(wdev);
+	ret = rdev_acs(rdev, dev, request);
+	if (ret) {
+		wdev->acs_started = false;
+		dev_put(dev);
+	}
+	wdev_unlock(wdev);
+
+ out_free:
+	kfree(request);
+	return ret;
+}
+
 #define NL80211_FLAG_NEED_WIPHY		0x01
 #define NL80211_FLAG_NEED_NETDEV	0x02
 #define NL80211_FLAG_NEED_RTNL		0x04
@@ -14066,6 +14202,15 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
 		.internal_flags = NL80211_FLAG_NEED_WDEV_UP |
 				  NL80211_FLAG_NEED_RTNL,
 	},
+	{
+		.cmd = NL80211_CMD_ACS,
+		.doit = nl80211_acs,
+		.policy = nl80211_policy,
+		.flags = GENL_UNS_ADMIN_PERM,
+		.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
+				  NL80211_FLAG_NEED_RTNL,
+	},
+
 };
 
 static struct genl_family nl80211_fam __ro_after_init = {
@@ -15656,6 +15801,43 @@ void cfg80211_ch_switch_started_notify(struct net_device *dev,
 	nlmsg_free(msg);
 }
 
+void
+nl80211_acs_notify(struct cfg80211_registered_device *rdev,
+		   const struct cfg80211_chan_def *chandef,
+		   enum nl80211_acs_status status,
+		   struct net_device *netdev, gfp_t gfp)
+{
+	struct sk_buff *msg;
+	void *hdr;
+
+	msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
+	if (!msg)
+		return;
+
+	hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ACS);
+	if (!hdr) {
+		nlmsg_free(msg);
+		return;
+	}
+
+	if (nla_put_u32(msg, NL80211_ATTR_ACS_STATUS, status))
+		goto nla_put_failure;
+
+	if (nl80211_send_chandef(msg, chandef))
+		goto nla_put_failure;
+
+	genlmsg_end(msg, hdr);
+
+	genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), msg, 0,
+				NL80211_MCGRP_MLME, gfp);
+
+	return;
+
+nla_put_failure:
+	genlmsg_cancel(msg, hdr);
+	nlmsg_free(msg);
+}
+
 void cfg80211_sta_opmode_change_notify(struct net_device *dev, const u8 *mac,
 				       struct sta_opmode_info *sta_opmode,
 				       gfp_t gfp)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 531c82d..e406592 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -119,6 +119,12 @@ int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
 		     enum nl80211_radar_event event,
 		     struct net_device *netdev, gfp_t gfp);
 
+void
+nl80211_acs_notify(struct cfg80211_registered_device *rdev,
+		   const struct cfg80211_chan_def *chandef,
+		   enum nl80211_acs_status status,
+		   struct net_device *netdev, gfp_t gfp);
+
 void nl80211_send_ap_stopped(struct wireless_dev *wdev);
 
 void cfg80211_rdev_free_coalesce(struct cfg80211_registered_device *rdev);
diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 5cb48d1..ebd9576 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -1272,4 +1272,16 @@ static inline int rdev_del_pmk(struct cfg80211_registered_device *rdev,
 	trace_rdev_return_void(&rdev->wiphy);
 }
 
+static inline int rdev_acs(struct cfg80211_registered_device *rdev,
+			   struct net_device *dev,
+			   struct cfg80211_acs_params *params)
+{
+	int ret;
+
+	trace_rdev_acs(&rdev->wiphy, dev, params);
+	ret = rdev->ops->acs(&rdev->wiphy, dev, params);
+	trace_rdev_return_int(&rdev->wiphy, ret);
+	return ret;
+}
+
 #endif /* __CFG80211_RDEV_OPS */
diff --git a/net/wireless/trace.h b/net/wireless/trace.h
index 44b2ce1..c841873 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -2530,6 +2530,29 @@
 	TP_ARGS(wiphy, wdev, cookie)
 );
 
+TRACE_EVENT(rdev_acs,
+	TP_PROTO(struct wiphy *wiphy, struct net_device *netdev,
+		 struct cfg80211_acs_params *params),
+
+	TP_ARGS(wiphy, netdev, params),
+
+	TP_STRUCT__entry(
+		WIPHY_ENTRY
+		NETDEV_ENTRY
+		__field(u32, n_channels)
+	),
+
+	TP_fast_assign(
+		WIPHY_ASSIGN;
+		NETDEV_ASSIGN;
+		__entry->n_channels = params->n_channels;
+	),
+
+	TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
+		  ", num of channels: %u", WIPHY_PR_ARG, NETDEV_PR_ARG,
+		  __entry->n_channels)
+);
+
 /*************************************************************
  *	     cfg80211 exported functions traces		     *
  *************************************************************/
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/2] wil6210: add implementation of acs cfg80211_ops
  2018-12-16 13:58 [PATCH 0/2] Add support for ACS offload for AP Ahmad Masri
  2018-12-16 13:58 ` [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) " Ahmad Masri
@ 2018-12-16 13:58 ` Ahmad Masri
  2018-12-17 11:07   ` Kalle Valo
  1 sibling, 1 reply; 6+ messages in thread
From: Ahmad Masri @ 2018-12-16 13:58 UTC (permalink / raw)
  To: Johannes Berg; +Cc: Ahmad Masri, linux-wireless, wil6210

Add ACS to cfg80211 operations that the device handles and performs.
wil6210 reports NL80211_EXT_FEATURE_ACS_OFFLOAD indicating a support
for acs operation through cfg80211_ops.

ACS is performed by the driver and the FW. Once ACS operation is
called the driver builds a WMI command with the requested channels
and sends the command to the FW. The FW performs ACS scan on the
channels and reports for each channel the following:
1- Number of beacon received on channel
2- channel busy time
3- transmit time
4- receive time
5- noise level
The driver uses the above information to select the best channel
to start the AP, and reports it to the host.

User may use debugfs acs_ch_weight to set a different weights
for the channels.

Change-Id: I8e13296fad0c9fa8b15788fd37c23d825d41d8e5
Signed-off-by: Ahmad Masri <amasri@codeaurora.org>
---
 drivers/net/wireless/ath/wil6210/cfg80211.c | 140 ++++++++++++++++++++++++-
 drivers/net/wireless/ath/wil6210/debugfs.c  | 154 ++++++++++++++++++++++++++++
 drivers/net/wireless/ath/wil6210/main.c     |   5 +
 drivers/net/wireless/ath/wil6210/wil6210.h  |  15 +++
 drivers/net/wireless/ath/wil6210/wmi.c      |  76 ++++++++++++++
 drivers/net/wireless/ath/wil6210/wmi.h      |   3 +-
 6 files changed, 390 insertions(+), 3 deletions(-)

diff --git a/drivers/net/wireless/ath/wil6210/cfg80211.c b/drivers/net/wireless/ath/wil6210/cfg80211.c
index 9b2f9f5..62abfb5 100644
--- a/drivers/net/wireless/ath/wil6210/cfg80211.c
+++ b/drivers/net/wireless/ath/wil6210/cfg80211.c
@@ -35,6 +35,14 @@
 };
 #endif
 
+/* in case of channels' noise values all zero, applying weights will not work.
+ * to avoid such a case, we will add some small positive value to
+ * all channels' noise calculation
+ */
+#define ACS_CH_NOISE_INIT_VAL (100)
+
+#define ACS_DEFAULT_BEST_CHANNEL 2
+
 #define CHAN60G(_channel, _flags) {				\
 	.band			= NL80211_BAND_60GHZ,		\
 	.center_freq		= 56160 + (2160 * (_channel)),	\
@@ -44,7 +52,7 @@
 	.max_power		= 40,				\
 }
 
-static struct ieee80211_channel wil_60ghz_channels[] = {
+static struct ieee80211_channel wil_60ghz_channels[WIL_MAX_CHANNELS] = {
 	CHAN60G(1, 0),
 	CHAN60G(2, 0),
 	CHAN60G(3, 0),
@@ -64,7 +72,7 @@
 	}
 }
 
-static int wil_num_supported_channels(struct wil6210_priv *wil)
+int wil_num_supported_channels(struct wil6210_priv *wil)
 {
 	int num_channels = ARRAY_SIZE(wil_60ghz_channels);
 
@@ -2358,6 +2366,132 @@ static int wil_cfg80211_resume(struct wiphy *wiphy)
 	return rc;
 }
 
+static u8 wil_acs_calc_channel(struct wil6210_priv *wil)
+{
+	int i, best_channel = ACS_DEFAULT_BEST_CHANNEL - 1;
+	struct scan_acs_info *ch;
+	u64 dwell_time = le32_to_cpu(wil->survey_reply.evt.dwell_time);
+	u16 filled = le16_to_cpu(wil->survey_reply.evt.filled);
+	u8 num_channels = wil->survey_reply.evt.num_scanned_channels;
+	u64 busy_time, tx_time;
+	u64 min_i_ch = (u64)-1, cur_i_ch;
+	u8 p_min = 0, ch_noise;
+
+	wil_dbg_misc(wil,
+		     "acs_calc_channel: filled info: 0x%04X, for %u channels\n",
+		     filled, num_channels);
+
+	if (!num_channels) {
+		wil_err(wil, "received results with no channel info\n");
+		return 0;
+	}
+
+	/* find P_min */
+	if (filled & WMI_ACS_INFO_BITMASK_NOISE) {
+		p_min = wil->survey_reply.ch_info[0].noise;
+
+		for (i = 1; i < num_channels; i++)
+			p_min = min(p_min, wil->survey_reply.ch_info[i].noise);
+	}
+
+	wil_dbg_misc(wil, "acs_calc_channel: p_min is %u\n", p_min);
+
+	/* Choosing channel according to the following formula:
+	 * 16 bit fixed point math
+	 * I_ch = { [ (T_busy - T_tx) << 16 ] /
+	 *        (T_dwell - T_tx) } * 2^(P_rx - P_min)
+	 */
+	for (i = 0; i < num_channels; i++) {
+		ch = &wil->survey_reply.ch_info[i];
+
+		busy_time = filled & WMI_ACS_INFO_BITMASK_BUSY_TIME ?
+				le16_to_cpu(ch->busy_time) : 0;
+
+		tx_time = filled & WMI_ACS_INFO_BITMASK_TX_TIME ?
+				le16_to_cpu(ch->tx_time) : 0;
+
+		ch_noise = filled & WMI_ACS_INFO_BITMASK_NOISE ? ch->noise : 0;
+
+		wil_dbg_misc(wil,
+			     "acs_calc_channel: Ch[%d]: busy %llu, tx %llu, noise %u, dwell %llu\n",
+			     ch->channel + 1, busy_time, tx_time, ch_noise,
+			     dwell_time);
+
+		if (dwell_time == tx_time) {
+			wil_err(wil,
+				"Ch[%d] dwell_time == tx_time: %llu\n",
+				ch->channel + 1, dwell_time);
+			continue;
+		}
+
+		cur_i_ch = (busy_time - tx_time) << 16;
+		do_div(cur_i_ch,
+		       ((dwell_time - tx_time) << (ch_noise - p_min)));
+
+		/* Apply channel priority */
+		cur_i_ch = (cur_i_ch + ACS_CH_NOISE_INIT_VAL) *
+			   wil->acs_ch_weight[ch->channel];
+		do_div(cur_i_ch, 100);
+
+		wil_dbg_misc(wil, "acs_calc_channel: Ch[%d] w %u, I_ch %llu\n",
+			     ch->channel + 1, wil->acs_ch_weight[ch->channel],
+			     cur_i_ch);
+
+		if (i == 0 || cur_i_ch < min_i_ch) {
+			min_i_ch = cur_i_ch;
+			best_channel = ch->channel;
+		}
+	}
+
+	wil_dbg_misc(wil,
+		     "acs_calc_channel: best channel %d with I_ch of %llu\n",
+		     best_channel + 1, min_i_ch);
+
+	return best_channel;
+}
+
+static void wil_acs_notify(struct wiphy *wiphy, struct net_device *dev,
+			   u32 status)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+	u8 ch = wil_acs_calc_channel(wil);
+	u32 freq = ieee80211_channel_to_frequency(ch + 1, NL80211_BAND_60GHZ);
+	struct ieee80211_channel *channel = ieee80211_get_channel(wiphy, freq);
+	struct cfg80211_chan_def chandef = {0};
+
+	if (channel) {
+		cfg80211_chandef_create(&chandef, channel, NL80211_CHAN_NO_HT);
+	} else {
+		wil_err(wil, "Invalid freq %d\n", freq);
+		status = NL80211_ACS_FAILED;
+	}
+
+	cfg80211_acs_result(dev, &chandef, status, GFP_KERNEL);
+}
+
+static int
+wil_cfg80211_acs(struct wiphy *wiphy, struct net_device *dev,
+		 struct cfg80211_acs_params *params)
+{
+	struct wil6210_priv *wil = wiphy_to_wil(wiphy);
+	enum nl80211_acs_status status = NL80211_ACS_SUCCESS;
+	int rc;
+
+	if (params->n_channels == 0) {
+		wil_err(wil, "acs: No valid channels for ACS\n");
+		return -EINVAL;
+	}
+
+	rc = wil_start_acs_survey(wil, WMI_SCAN_DWELL_TIME_MS,
+				  params->channels, params->n_channels);
+	if (rc)
+		status = NL80211_ACS_FAILED;
+
+	wil_acs_notify(wiphy, dev, status);
+
+	return 0;
+}
+
 static const struct cfg80211_ops wil_cfg80211_ops = {
 	.add_virtual_intf = wil_cfg80211_add_iface,
 	.del_virtual_intf = wil_cfg80211_del_iface,
@@ -2394,6 +2528,7 @@ static int wil_cfg80211_resume(struct wiphy *wiphy)
 	.sched_scan_start = wil_cfg80211_sched_scan_start,
 	.sched_scan_stop = wil_cfg80211_sched_scan_stop,
 	.update_ft_ies = wil_cfg80211_update_ft_ies,
+	.acs = wil_cfg80211_acs,
 };
 
 static void wil_wiphy_init(struct wiphy *wiphy)
@@ -2436,6 +2571,7 @@ static void wil_wiphy_init(struct wiphy *wiphy)
 #ifdef CONFIG_PM
 	wiphy->wowlan = &wil_wowlan_support;
 #endif
+	wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_ACS_OFFLOAD);
 }
 
 int wil_cfg80211_iface_combinations_from_fw(
diff --git a/drivers/net/wireless/ath/wil6210/debugfs.c b/drivers/net/wireless/ath/wil6210/debugfs.c
index 20dd4d0..a376229 100644
--- a/drivers/net/wireless/ath/wil6210/debugfs.c
+++ b/drivers/net/wireless/ath/wil6210/debugfs.c
@@ -26,6 +26,8 @@
 #include "txrx.h"
 #include "pmc.h"
 
+#define WIL_DEBUGFS_BUF_SIZE 400
+
 /* Nasty hack. Better have per device instances */
 static u32 mem_addr;
 static u32 dbg_txdesc_index;
@@ -2250,6 +2252,98 @@ static ssize_t wil_read_led_blink_time(struct file *file, char __user *user_buf,
 	.open  = simple_open,
 };
 
+/*---------ACS channel weight------------*/
+static ssize_t wil_write_acs_ch_weight(struct file *file,
+				       const char __user *buf,
+				       size_t len, loff_t *ppos)
+{
+	struct wil6210_priv *wil = file->private_data;
+	int i, rc;
+	char *token, *dupbuf, *kbuf = kmalloc(len + 1, GFP_KERNEL);
+	unsigned short channel_weights[WIL_MAX_CHANNELS];
+
+	if (!kbuf)
+		return -ENOMEM;
+
+	rc = simple_write_to_buffer(kbuf, len, ppos, buf, len);
+	if (rc != len) {
+		kfree(kbuf);
+		return rc >= 0 ? -EIO : rc;
+	}
+
+	kbuf[len] = '\0';
+	dupbuf = kbuf;
+
+	/* Format for writing is num of channels unsigned short separated
+	 * by spaces:
+	 * <ch 1 weight> ... <channel max weight>
+	 */
+
+	/* set the channels weights */
+	for (i = 0; i < WIL_MAX_CHANNELS; ++i) {
+		token = strsep(&dupbuf, " ");
+		if (!token)
+			goto out;
+		if (kstrtou16(token, 0, &channel_weights[i]))
+			goto out;
+	}
+	memcpy(wil->acs_ch_weight, channel_weights, sizeof(wil->acs_ch_weight));
+
+out:
+	kfree(kbuf);
+	if (i != WIL_MAX_CHANNELS)
+		return -EINVAL;
+
+	return len;
+}
+
+static ssize_t wil_read_acs_ch_weight(struct file *file, char __user *user_buf,
+				      size_t count, loff_t *ppos)
+{
+	struct wil6210_priv *wil = file->private_data;
+	char *buf;
+	size_t buf_size = WIL_DEBUGFS_BUF_SIZE;
+	int i, bytes_used, offset, rc = -EINVAL;
+
+	buf = kmalloc(buf_size, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	bytes_used = snprintf(buf, buf_size,
+			      "To set acs channel weights write:\n"
+			      "<ch 1 weight> <ch 2 weight> ... <ch max weight>\n"
+			      "The current values are:\n");
+
+	if (bytes_used < 0 || bytes_used >= buf_size)
+		goto out;
+
+	buf_size -= bytes_used;
+	offset = bytes_used;
+
+	for (i = 0; i < WIL_MAX_CHANNELS; ++i) {
+		bytes_used = snprintf(buf + offset, buf_size, "%hu ",
+				      wil->acs_ch_weight[i]);
+		if (bytes_used < 0 || bytes_used >= buf_size)
+			goto out;
+
+		buf_size -= bytes_used;
+		offset += bytes_used;
+	}
+	strncat(buf, "\n", WIL_DEBUGFS_BUF_SIZE);
+	rc = simple_read_from_buffer(user_buf, count, ppos, buf, offset);
+
+out:
+	kfree(buf);
+
+	return rc;
+}
+
+static const struct file_operations fops_acs_ch_weight = {
+	.read = wil_read_acs_ch_weight,
+	.write = wil_write_acs_ch_weight,
+	.open  = simple_open,
+};
+
 /*---------FW capabilities------------*/
 static int wil_fw_capabilities_debugfs_show(struct seq_file *s, void *data)
 {
@@ -2414,6 +2508,65 @@ static ssize_t wil_compressed_rx_status_write(struct file *file,
 	.llseek	= seq_lseek,
 };
 
+/*---------Survey results------------*/
+static int wil_survey_debugfs_show(struct seq_file *s, void *data)
+{
+	struct wil6210_priv *wil = s->private;
+	int i, n_ch;
+	u16 filled;
+
+	if (!wil->survey_ready) {
+		seq_puts(s, "Survey not ready\n");
+		return 0;
+	}
+	seq_printf(s, "dwell_time : %d\n",
+		   le32_to_cpu(wil->survey_reply.evt.dwell_time));
+	filled = le16_to_cpu(wil->survey_reply.evt.filled);
+	n_ch = min_t(int, wil->survey_reply.evt.num_scanned_channels,
+		     ARRAY_SIZE(wil->survey_reply.ch_info));
+
+#define ACS_FILLED(x) (filled & WMI_ACS_INFO_BITMASK_ ## x) ? \
+	" " __stringify(x) : ""
+	seq_printf(s, "Filled : 0x%04x%s%s%s%s%s\n", filled,
+		   ACS_FILLED(BEACON_FOUND),
+		   ACS_FILLED(BUSY_TIME),
+		   ACS_FILLED(TX_TIME),
+		   ACS_FILLED(RX_TIME),
+		   ACS_FILLED(NOISE)
+		  );
+#undef ACS_FILLED
+	seq_printf(s, "Channels [%d] {\n", n_ch);
+	for (i = 0; i < n_ch; i++) {
+		struct scan_acs_info *ch = &wil->survey_reply.ch_info[i];
+
+		seq_printf(s, "  [%d]", ch->channel);
+#define ACS_PRINT(x, str, field) do { if (filled & WMI_ACS_INFO_BITMASK_ ## x) \
+		seq_printf(s, " %s : %d", str, field); \
+	} while (0)
+		ACS_PRINT(BEACON_FOUND, "bcon", ch->beacon_found);
+		ACS_PRINT(BUSY_TIME, "busy", le16_to_cpu(ch->busy_time));
+		ACS_PRINT(TX_TIME, "tx", le16_to_cpu(ch->tx_time));
+		ACS_PRINT(RX_TIME, "rx", le16_to_cpu(ch->rx_time));
+		ACS_PRINT(NOISE, "noise", ch->noise);
+#undef ACS_PRINT
+		seq_puts(s, "\n");
+	}
+	seq_puts(s, "}\n");
+	return 0;
+}
+
+static int wil_survey_seq_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, wil_survey_debugfs_show, inode->i_private);
+}
+
+static const struct file_operations fops_survey = {
+	.open		= wil_survey_seq_open,
+	.release	= single_release,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+};
+
 /*----------------*/
 static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
 				       struct dentry *dbg)
@@ -2473,6 +2626,7 @@ static void wil6210_debugfs_init_blobs(struct wil6210_priv *wil,
 	{"tx_latency",	0644,		&fops_tx_latency},
 	{"link_stats",	0644,		&fops_link_stats},
 	{"link_stats_global",	0644,	&fops_link_stats_global},
+	{"acs_ch_weight",	0644,	&fops_acs_ch_weight},
 };
 
 static void wil6210_debugfs_init_files(struct wil6210_priv *wil,
diff --git a/drivers/net/wireless/ath/wil6210/main.c b/drivers/net/wireless/ath/wil6210/main.c
index ba6a2ee..f20ced0 100644
--- a/drivers/net/wireless/ath/wil6210/main.c
+++ b/drivers/net/wireless/ath/wil6210/main.c
@@ -750,6 +750,11 @@ int wil_priv_init(struct wil6210_priv *wil)
 
 	wil->amsdu_en = 1;
 
+	/* ACS related */
+	wil->acs_ch_weight[0] = 120;
+	for (i = 1; i < WIL_MAX_CHANNELS; i++)
+		wil->acs_ch_weight[i] = 100;
+
 	return 0;
 
 out_wmi_wq:
diff --git a/drivers/net/wireless/ath/wil6210/wil6210.h b/drivers/net/wireless/ath/wil6210/wil6210.h
index 0f3be3ff..c286750 100644
--- a/drivers/net/wireless/ath/wil6210/wil6210.h
+++ b/drivers/net/wireless/ath/wil6210/wil6210.h
@@ -67,6 +67,8 @@
  */
 #define WIL_MAX_VIFS 4
 
+#define WIL_MAX_CHANNELS 4 /* max supported channels */
+
 /**
  * extract bits [@b0:@b1] (inclusive) from the value @x
  * it should be @b0 <= @b1, or result is incorrect
@@ -1045,6 +1047,15 @@ struct wil6210_priv {
 
 	u32 max_agg_wsize;
 	u32 max_ampdu_size;
+
+	/* ACS related */
+	unsigned short acs_ch_weight[WIL_MAX_CHANNELS];
+	bool survey_ready;
+	struct {
+		struct wmi_cmd_hdr wmi;
+		struct wmi_acs_passive_scan_complete_event evt;
+		struct scan_acs_info ch_info[WIL_MAX_CHANNELS];
+	} __packed survey_reply;
 };
 
 #define wil_to_wiphy(i) (i->wiphy)
@@ -1252,6 +1263,9 @@ int wil_addba_rx_request(struct wil6210_priv *wil, u8 mid,
 			 u8 cidxtid, u8 dialog_token, __le16 ba_param_set,
 			 __le16 ba_timeout, __le16 ba_seq_ctrl);
 int wil_addba_tx_request(struct wil6210_priv *wil, u8 ringid, u16 wsize);
+int wil_start_acs_survey(struct wil6210_priv *wil, uint dwell_time,
+			 struct cfg80211_chan_def channels[],
+			 u8 num_channels);
 
 void wil6210_clear_irq(struct wil6210_priv *wil);
 int wil6210_init_irq(struct wil6210_priv *wil, int irq);
@@ -1402,4 +1416,5 @@ int wmi_addba_rx_resp_edma(struct wil6210_priv *wil, u8 mid, u8 cid,
 
 void update_supported_bands(struct wil6210_priv *wil);
 
+int wil_num_supported_channels(struct wil6210_priv *wil);
 #endif /* __WIL6210_H__ */
diff --git a/drivers/net/wireless/ath/wil6210/wmi.c b/drivers/net/wireless/ath/wil6210/wmi.c
index 345f059..5ca0d88 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.c
+++ b/drivers/net/wireless/ath/wil6210/wmi.c
@@ -3811,3 +3811,79 @@ int wmi_link_stats_cfg(struct wil6210_vif *vif, u32 type, u8 cid, u32 interval)
 
 	return 0;
 }
+
+int wil_start_acs_survey(struct wil6210_priv *wil, uint dwell_time,
+			 struct cfg80211_chan_def channels[],
+			 u8 num_channels)
+{
+	struct wil6210_vif *vif = ndev_to_vif(wil->main_ndev);
+	int rc, i;
+	u8 ch;
+	struct wmi_acs_passive_scan_complete_event *reply;
+	u8 num_supported_channels = wil_num_supported_channels(wil);
+	struct {
+		struct wmi_start_scan_cmd cmd;
+		struct {
+			u8 channel;
+			u8 reserved;
+		} channel_list[WIL_MAX_CHANNELS];
+	} __packed scan_cmd = {
+		.cmd = {
+			.scan_type = WMI_PASSIVE_SCAN,
+			.dwell_time = cpu_to_le32(dwell_time),
+			.num_channels = min_t(u8, num_channels,
+					      num_supported_channels),
+		},
+	};
+
+	wil->survey_ready = false;
+	memset(&wil->survey_reply, 0, sizeof(wil->survey_reply));
+	reply = &wil->survey_reply.evt;
+	reply->status = WMI_SCAN_FAILED;
+
+	for (i = 0; i < scan_cmd.cmd.num_channels; i++) {
+		ch = channels[i].chan->hw_value;
+
+		if (ch == 0) {
+			wil_err(wil, "ACS requested for wrong channel\n");
+			return -EINVAL;
+		}
+		wil_dbg_misc(wil, "ACS channel %d : %d MHz\n",
+			     ch, channels[i].chan->center_freq);
+		scan_cmd.channel_list[i].channel = ch - 1;
+	}
+
+	/* send scan command with the requested channel and wait
+	 * for results
+	 */
+	rc = wmi_call(wil, WMI_START_SCAN_CMDID, vif->mid, &scan_cmd,
+		      sizeof(scan_cmd), WMI_ACS_PASSIVE_SCAN_COMPLETE_EVENTID,
+		      &wil->survey_reply, sizeof(wil->survey_reply),
+		      WMI_SURVEY_TIMEOUT_MS);
+	if (rc) {
+		wil_err(wil, "ACS passive Scan failed (0x%08x)\n", rc);
+		return rc;
+	}
+
+	if (reply->num_scanned_channels > num_supported_channels) {
+		wil_err(wil,
+			"Survey num of scanned channels %d exceeds num of supported channels %d\n",
+			reply->num_scanned_channels,
+			num_supported_channels);
+		reply->status = WMI_SCAN_FAILED;
+		return -EINVAL;
+	}
+
+	if (reply->status != WMI_SCAN_SUCCESS) {
+		wil_err(wil, "ACS survey failed, status (%d)\n",
+			wil->survey_reply.evt.status);
+		return -EINVAL;
+	}
+	wil->survey_ready = true;
+
+	/* The results in survey_reply */
+	wil_dbg_misc(wil, "ACS scan success, filled mask: 0x%08X\n",
+		     le16_to_cpu(reply->filled));
+
+	return 0;
+}
diff --git a/drivers/net/wireless/ath/wil6210/wmi.h b/drivers/net/wireless/ath/wil6210/wmi.h
index b668758..5a71e98 100644
--- a/drivers/net/wireless/ath/wil6210/wmi.h
+++ b/drivers/net/wireless/ath/wil6210/wmi.h
@@ -2444,7 +2444,8 @@ struct wmi_acs_passive_scan_complete_event {
 	 */
 	__le16 filled;
 	u8 num_scanned_channels;
-	u8 reserved;
+	/* enum scan_status */
+	u8 status;
 	struct scan_acs_info scan_info_list[0];
 } __packed;
 
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) offload for AP
  2018-12-16 13:58 ` [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) " Ahmad Masri
@ 2018-12-17 11:03   ` Kalle Valo
  2018-12-17 13:41   ` Johannes Berg
  1 sibling, 0 replies; 6+ messages in thread
From: Kalle Valo @ 2018-12-17 11:03 UTC (permalink / raw)
  To: Ahmad Masri; +Cc: Johannes Berg, linux-wireless, wil6210

Ahmad Masri <amasri@codeaurora.org> writes:

> Add ACS offload to let the device select a channel for AP from
> a list of channels that the user-space provides. ACS can customize
> the method of AP channel selection and add parameters like traffic
> load, number of APs on channel and more.
>
> Change-Id: I18cb8460b9418512ac7ac9f76fe8f7f379f2478b
> Signed-off-by: Ahmad Masri <amasri@codeaurora.org>

No Change-Id, please.

> @@ -4662,6 +4680,7 @@ struct wireless_dev {
>  	unsigned long cac_start_time;
>  	unsigned int cac_time_ms;
>  
> +	bool acs_started;
>  #ifdef CONFIG_CFG80211_WEXT

Empty line before ifdef? Just like it had before your patch.

> +void cfg80211_acs_result(struct net_device *netdev,
> +			 const struct cfg80211_chan_def *chandef,
> +			 enum nl80211_acs_status status, gfp_t gfp)
> +{
> +	struct wireless_dev *wdev = netdev->ieee80211_ptr;
> +	struct wiphy *wiphy = wdev->wiphy;
> +	struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
> +
> +	if (WARN_ON(!wdev->acs_started))
> +		return;
> +
> +	switch (status) {
> +	case NL80211_ACS_SUCCESS:
> +		if (!chandef) {
> +			WARN_ON(1);
> +			goto out;
> +		}

if (WARN_ON(!chandef))
	goto out;

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/2] wil6210: add implementation of acs cfg80211_ops
  2018-12-16 13:58 ` [PATCH 2/2] wil6210: add implementation of acs cfg80211_ops Ahmad Masri
@ 2018-12-17 11:07   ` Kalle Valo
  0 siblings, 0 replies; 6+ messages in thread
From: Kalle Valo @ 2018-12-17 11:07 UTC (permalink / raw)
  To: Ahmad Masri; +Cc: Johannes Berg, linux-wireless, wil6210

Ahmad Masri <amasri@codeaurora.org> writes:

> Add ACS to cfg80211 operations that the device handles and performs.
> wil6210 reports NL80211_EXT_FEATURE_ACS_OFFLOAD indicating a support
> for acs operation through cfg80211_ops.
>
> ACS is performed by the driver and the FW. Once ACS operation is
> called the driver builds a WMI command with the requested channels
> and sends the command to the FW. The FW performs ACS scan on the
> channels and reports for each channel the following:
> 1- Number of beacon received on channel
> 2- channel busy time
> 3- transmit time
> 4- receive time
> 5- noise level
> The driver uses the above information to select the best channel
> to start the AP, and reports it to the host.

What's the point? Why is it better that wil6210 makes the decision
instead of hostapd?

> User may use debugfs acs_ch_weight to set a different weights
> for the channels.

Debugfs does not sound like the best interface for that, shouldn't it go
via nl80211?

> Change-Id: I8e13296fad0c9fa8b15788fd37c23d825d41d8e5

No Change-Id, please.

-- 
Kalle Valo

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) offload for AP
  2018-12-16 13:58 ` [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) " Ahmad Masri
  2018-12-17 11:03   ` Kalle Valo
@ 2018-12-17 13:41   ` Johannes Berg
  1 sibling, 0 replies; 6+ messages in thread
From: Johannes Berg @ 2018-12-17 13:41 UTC (permalink / raw)
  To: Ahmad Masri; +Cc: linux-wireless, wil6210

Setting aside Kalle's question for a second (though you should address
them, probably before mine!)...


> + * @NL80211_ATTR_CHAN_DEF: attribute for nesting chan_def parameters
> + *	as defined in &enum nl80211_ch_def_attr. The new attribute allows
> + *	userspace to send a list of struct cfg80211_chan_def. For example, ACS
> + *	command uses this attribute for sending a list of channels, for more
> + *	details see &NL80211_CMD_ACS.

I don't think this makes sense. Just have an NL80211_ATTR_CHANNEL_LIST
or so, which is like a NLA_POLICY_NESTED_ARRAY, and then inside we can
reuse the existing top-level attributes for how to define a channel.

That'll save you most of the parsing code too.

>  /**
> + * enum nl80211_acs_status - ACS status
> + *
> + * status to be used to inform userspace about the result of the ACS offloaded
> + * measurement.
> + *
> + * @NL80211_ACS_SUCCESS: The ACS operation finished successfully
> + * @NL80211_ACS_FAILED: Failed to run the ACS. Userspace should choose a channel
> + *	by itself.
> + */
> +enum nl80211_acs_status {
> +	NL80211_ACS_SUCCESS,
> +	NL80211_ACS_FAILED,
> +};

It seems like a "successful" flag attribute would be sufficient.

> +	dev_put(netdev);

> +	dev_hold(dev);

Don't do that. Need to cancel the operation if the netdev is removed
instead.

johannes


^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2018-12-17 13:41 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-16 13:58 [PATCH 0/2] Add support for ACS offload for AP Ahmad Masri
2018-12-16 13:58 ` [PATCH 1/2] cfg80211: Add Automatic Channel Selection (ACS) " Ahmad Masri
2018-12-17 11:03   ` Kalle Valo
2018-12-17 13:41   ` Johannes Berg
2018-12-16 13:58 ` [PATCH 2/2] wil6210: add implementation of acs cfg80211_ops Ahmad Masri
2018-12-17 11:07   ` Kalle Valo

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).