2019-04-17 09:22:09

by Tamizh chelvam

[permalink] [raw]
Subject: [PATCHv4 0/2] cfg80211/mac80211: Add support to configure and monitor station's rssi threshold

This patchsets introduced new NL command and api to support
configuring rssi for the connected stations and api to notify
userspace application upon crossing the configured threshold.
This will be useful for the application which requires
station's current signal strength change information.
Monitoring station's signal strength through station dump command
will unnecessarily increase the system overhead. This event based
mechanism will reduce the system overhead and helps application to
take a decision for the station for which event received.

Tamizh chelvam (2):
cfg80211: Add support to configure station specific RSSI threshold
for AP mode
mac80211: Implement API to configure station specific rssi threshold

v4:
* Fixed sparse warning

v3:
* Address Johannes comments

v2:
* Combined patchset 2 and 3 as single patch and addressed Johannes comments.

include/net/cfg80211.h | 39 +++++++++
include/net/mac80211.h | 7 ++
include/uapi/linux/nl80211.h | 21 +++++
net/mac80211/cfg.c | 108 +++++++++++++++++++++++++
net/mac80211/rx.c | 56 ++++++++++++-
net/mac80211/sta_info.c | 1 +
net/mac80211/sta_info.h | 31 ++++++++
net/wireless/nl80211.c | 179 ++++++++++++++++++++++++++++++++++++------
net/wireless/rdev-ops.h | 13 +++
net/wireless/trace.h | 21 +++++
10 files changed, 449 insertions(+), 27 deletions(-)

--
1.7.9.5



2019-04-17 09:22:23

by Tamizh chelvam

[permalink] [raw]
Subject: [PATCHv4 2/2] mac80211: Implement API to configure station specific rssi threshold

Implement set_sta_mon_rssi_config API to configure station
specific rssi threshold value to monitor change in connected
station's signal strength. RSSI low and high threshold values
will be calculated as per the station's current signal strength,
if the configuration needs to be a fixed threshold then the user
given value will be chosen as low and high threshold.

And this patch triggers cfg80211_sta_mon_rssi_notify with the
corresponding event when station signal goes out of configured threshold.
It uses rx data signal to check against rssi threshold configured by the user.
And update the lower and upper RSSI threshold for the station if it is not
configured as a fixed threshold.

This event will be useful for the application like steering to take
decision on any station depends on its current link quality.

Signed-off-by: Tamizh chelvam <[email protected]>
---
include/net/mac80211.h | 7 +++
net/mac80211/cfg.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 56 +++++++++++++++++++++++-
net/mac80211/sta_info.c | 1 +
net/mac80211/sta_info.h | 31 ++++++++++++++
5 files changed, 202 insertions(+), 1 deletion(-)

diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index d66fbfe..6893cb9 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -873,6 +873,13 @@ enum mac80211_rate_control_flags {
};


+/*
+ * How many frames need to have been used in average station's
+ * signal strength before checking against the threshold
+ */
+#define IEEE80211_STA_SIGNAL_AVE_MIN_COUNT 4
+
+
/* there are 40 bytes if you don't need the rateset to be kept */
#define IEEE80211_TX_INFO_DRIVER_DATA_SIZE 40

diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index ba6e408..b460051 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -3938,6 +3938,113 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy,
return drv_abort_pmsr(local, sdata, request);
}

+void sta_mon_rssi_config_free(struct sta_info *sta)
+{
+ struct sta_mon_rssi_config *old_rssi_config;
+
+ if (sta->rssi_config) {
+ old_rssi_config = sta->rssi_config;
+ RCU_INIT_POINTER(sta->rssi_config, NULL);
+ kfree_rcu(old_rssi_config, rcu_head);
+ }
+}
+
+void ieee80211_update_rssi_config(struct sta_info *sta)
+{
+ s32 last;
+ u32 hyst;
+ int i, n;
+
+ if (!sta->rssi_config || sta->rssi_config->fixed_thold)
+ return;
+
+ if (!sta->rssi_config->last_value)
+ sta->rssi_config->last_value =
+ -ewma_signal_read(&sta->rx_stats_avg.signal);
+
+ last = sta->rssi_config->last_value;
+ hyst = sta->rssi_config->hyst;
+ n = sta->rssi_config->n_thresholds;
+
+ for (i = 0; i < n; i++)
+ if (last < sta->rssi_config->thresholds[i])
+ break;
+
+ sta->rssi_config->low =
+ i > 0 ? (sta->rssi_config->thresholds[i - 1] - hyst) : S32_MIN;
+ sta->rssi_config->high =
+ i < n ? (sta->rssi_config->thresholds[i] + hyst - 1) : S32_MAX;
+}
+
+static int
+ieee80211_set_sta_mon_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ const u8 *mac_addr,
+ const struct cfg80211_sta_mon *sta_mon_cfg)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = sdata->local;
+ struct sta_mon_rssi_config *rssi_config;
+ struct sta_info *sta;
+ int ret = 0;
+
+ mutex_lock(&local->sta_mtx);
+ rcu_read_lock();
+
+ if (mac_addr) {
+ sta = sta_info_get_bss(sdata, mac_addr);
+ if (!sta) {
+ ret = -ENOENT;
+ goto out;
+ }
+
+ if (sta_mon_cfg->fixed_thold &&
+ sta_mon_cfg->n_tholds > 2) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ rssi_config = kzalloc(sizeof(*rssi_config) +
+ sta_mon_cfg->n_tholds * sizeof(s32),
+ GFP_KERNEL);
+ if (!rssi_config) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ rssi_config->hyst = sta_mon_cfg->rssi_hyst;
+ if (sta_mon_cfg->fixed_thold) {
+ sta_mon_rssi_config_free(sta);
+ if (sta_mon_cfg->n_tholds == 1) {
+ rssi_config->low = sta_mon_cfg->rssi_tholds[0];
+ rssi_config->high = sta_mon_cfg->rssi_tholds[0];
+ } else {
+ rssi_config->low = sta_mon_cfg->rssi_tholds[0];
+ rssi_config->high = sta_mon_cfg->rssi_tholds[1];
+ }
+ rssi_config->fixed_thold = sta_mon_cfg->fixed_thold;
+ rssi_config->hyst = sta_mon_cfg->rssi_hyst;
+ rssi_config->n_thresholds = sta_mon_cfg->n_tholds;
+ sta->rssi_config = rssi_config;
+ } else {
+ sta_mon_rssi_config_free(sta);
+ rssi_config->n_thresholds = sta_mon_cfg->n_tholds;
+ memcpy(rssi_config->thresholds,
+ sta_mon_cfg->rssi_tholds,
+ rssi_config->n_thresholds * sizeof(s32));
+ rssi_config->hyst = sta_mon_cfg->rssi_hyst;
+ sta->rssi_config = rssi_config;
+ /* Calculate low and high RSSI thresholds */
+ ieee80211_update_rssi_config(sta);
+ }
+ }
+
+out:
+ rcu_read_unlock();
+ mutex_unlock(&local->sta_mtx);
+ return ret;
+}
+
const struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
@@ -4035,4 +4142,5 @@ static int ieee80211_get_txq_stats(struct wiphy *wiphy,
.get_ftm_responder_stats = ieee80211_get_ftm_responder_stats,
.start_pmsr = ieee80211_start_pmsr,
.abort_pmsr = ieee80211_abort_pmsr,
+ .set_sta_mon_rssi_config = ieee80211_set_sta_mon_rssi_config,
};
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 4a03c18..de27a6f 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -1732,6 +1732,57 @@ void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *pubsta, u8 tid)
return RX_CONTINUE;
}

+static void ieee80211_sta_rx_signal_thold_check(struct ieee80211_rx_data *rx)
+{
+ struct sta_info *sta = rx->sta;
+ struct ieee80211_bss_conf *bss_conf = &rx->sdata->vif.bss_conf;
+ bool rssi_cross = false;
+
+ if (!wiphy_ext_feature_isset(rx->local->hw.wiphy,
+ NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG))
+ return;
+
+ if (!sta->rssi_config)
+ return;
+
+ rcu_read_lock();
+ sta->rssi_config->count_rx_signal++;
+ if (sta->rssi_config->count_rx_signal <
+ IEEE80211_STA_SIGNAL_AVE_MIN_COUNT)
+ return;
+
+ if (sta->rssi_config->low && bss_conf->enable_beacon) {
+ int last_event = sta->rssi_config->last_value;
+ int sig = -ewma_signal_read(&sta->rx_stats_avg.signal);
+ int low = sta->rssi_config->low;
+ int high = sta->rssi_config->high;
+
+ if (sig < low &&
+ (last_event == 0 || last_event >= low)) {
+ sta->rssi_config->last_value = sig;
+ cfg80211_sta_mon_rssi_notify(
+ rx->sdata->dev, sta->addr,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ sig, GFP_ATOMIC);
+ rssi_cross = true;
+ } else if (sig > high &&
+ (last_event == 0 || last_event <= high)) {
+ sta->rssi_config->last_value = sig;
+ cfg80211_sta_mon_rssi_notify(
+ rx->sdata->dev, sta->addr,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ sig, GFP_ATOMIC);
+ rssi_cross = true;
+ }
+ }
+
+ if (rssi_cross) {
+ ieee80211_update_rssi_config(sta);
+ rssi_cross = false;
+ }
+ rcu_read_unlock();
+}
+
static ieee80211_rx_result debug_noinline
ieee80211_rx_h_sta_process(struct ieee80211_rx_data *rx)
{
@@ -1787,6 +1838,7 @@ void ieee80211_sta_uapsd_trigger(struct ieee80211_sta *pubsta, u8 tid)
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
sta->rx_stats.last_signal = status->signal;
ewma_signal_add(&sta->rx_stats_avg.signal, -status->signal);
+ ieee80211_sta_rx_signal_thold_check(rx);
}

if (status->chains) {
@@ -4219,9 +4271,11 @@ static bool ieee80211_invoke_fast_rx(struct ieee80211_rx_data *rx,
/* statistics part of ieee80211_rx_h_sta_process() */
if (!(status->flag & RX_FLAG_NO_SIGNAL_VAL)) {
stats->last_signal = status->signal;
- if (!fast_rx->uses_rss)
+ if (!fast_rx->uses_rss) {
ewma_signal_add(&sta->rx_stats_avg.signal,
-status->signal);
+ ieee80211_sta_rx_signal_thold_check(rx);
+ }
}

if (status->chains) {
diff --git a/net/mac80211/sta_info.c b/net/mac80211/sta_info.c
index a4932ee..ea22c2d 100644
--- a/net/mac80211/sta_info.c
+++ b/net/mac80211/sta_info.c
@@ -1029,6 +1029,7 @@ static void __sta_info_destroy_part2(struct sta_info *sta)

rate_control_remove_sta_debugfs(sta);
ieee80211_sta_debugfs_remove(sta);
+ sta_mon_rssi_config_free(sta);

cleanup_single_sta(sta);
}
diff --git a/net/mac80211/sta_info.h b/net/mac80211/sta_info.h
index 71f7e49..17b3726 100644
--- a/net/mac80211/sta_info.h
+++ b/net/mac80211/sta_info.h
@@ -427,6 +427,33 @@ struct ieee80211_sta_rx_stats {
u64 msdu[IEEE80211_NUM_TIDS + 1];
};

+/**
+ * struct sta_mon_rssi_config - Monitor station's signal strength
+ * @rcu_head: rcu head for freeing structure
+ * @n_rssi_tholds: Number of thresholds passed by user
+ * @low: RSSI lower threshold for this station, a zero value implies
+ * disabled
+ * @high: RSSI upper threshold for this station
+ * @hyst: RSSI hysteresis for this station
+ * @last_value: Last RSSI value for this station triggered the
+ * RSSI cross event
+ * @fixed_thold - Indicate whether to use fixed thresholds limit or not
+ * @threholds: RSSI threshold limit passed by the user
+ * @count_rx_signal: Number of data frames used in averaging station signal.
+ * This can be used to avoid generating less reliable station rssi cross
+ * events that would be based only on couple of received frames
+ */
+struct sta_mon_rssi_config {
+ struct rcu_head rcu_head;
+ int n_thresholds;
+ s32 low, high;
+ u32 hyst;
+ s32 last_value;
+ u8 fixed_thold;
+ unsigned int count_rx_signal;
+ s32 thresholds[0];
+};
+
/*
* The bandwidth threshold below which the per-station CoDel parameters will be
* scaled to be more lenient (to prevent starvation of slow stations). This
@@ -496,6 +523,7 @@ struct ieee80211_sta_rx_stats {
* @fast_rx: RX fastpath information
* @tdls_chandef: a TDLS peer can have a wider chandef that is compatible to
* the BSS one.
+ * @rssi_config: RSSI threshold config parameters for this station
* @tx_stats: TX statistics
* @tx_stats.packets: # of packets transmitted
* @tx_stats.bytes: # of bytes in all packets transmitted
@@ -623,6 +651,7 @@ struct sta_info {

struct cfg80211_chan_def tdls_chandef;

+ struct sta_mon_rssi_config __rcu *rssi_config;
/* keep last! */
struct ieee80211_sta sta;
};
@@ -760,6 +789,7 @@ int sta_info_destroy_addr(struct ieee80211_sub_if_data *sdata,
const u8 *addr);
int sta_info_destroy_addr_bss(struct ieee80211_sub_if_data *sdata,
const u8 *addr);
+void sta_mon_rssi_config_free(struct sta_info *sta);

void sta_info_recalc_tim(struct sta_info *sta);

@@ -792,6 +822,7 @@ void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo,
void ieee80211_sta_expire(struct ieee80211_sub_if_data *sdata,
unsigned long exp_time);
u8 sta_info_tx_streams(struct sta_info *sta);
+void ieee80211_update_rssi_config(struct sta_info *sta);

void ieee80211_sta_ps_deliver_wakeup(struct sta_info *sta);
void ieee80211_sta_ps_deliver_poll_response(struct sta_info *sta);
--
1.7.9.5


2019-04-17 09:22:24

by Tamizh chelvam

[permalink] [raw]
Subject: [PATCHv4 1/2] cfg80211: Add support to configure station specific RSSI threshold for AP mode

Add infrastructure to configure station specific RSSI threshold
configuration to monitor station's signal strength variation.
This configuration will be useful for the application like
steering which requires change in the station's current signal
strength.

New NL80211_CMD_STA_MON introduced to configure the RSSI threshold
using NL80211_ATTR_CQM nested attributes. The MAC address of
a station is passed in NL80211_ATTR_MAC. And NL80211_ATTR_STA_MON_FIXED_THOLD
introduced to have this RSSI threshold as a fixed(not modifying the
low and high threshold upon crossing the threshold) or moving(modifying
low and high threshold upon crossing the threshold) thresholds.
Depends on the application's use case user can have either fixed or
moving RSSI range thresholds.

cfg80211_sta_mon_rssi_notify introduced to notify change in the
station's signal stregnth cross event using NL80211_CMD_NOTIFY_STA_MON.
Driver supporting this feature should advertise
NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG feature flag.

Signed-off-by: Tamizh chelvam <[email protected]>
---
include/net/cfg80211.h | 39 +++++++++
include/uapi/linux/nl80211.h | 21 +++++
net/wireless/nl80211.c | 179 ++++++++++++++++++++++++++++++++++++------
net/wireless/rdev-ops.h | 13 +++
net/wireless/trace.h | 21 +++++
5 files changed, 247 insertions(+), 26 deletions(-)

diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 2ea04e9..cb8ba5a 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -3161,6 +3161,20 @@ struct cfg80211_update_owe_info {
};

/**
+ * struct cfg80211_sta_mon - Configure station monitor parameters
+ * @rssi_tholds - array of RSSI thresholds for the station
+ * @rssi_hyst - RSSI hysterisis
+ * @n_tholds - Number of configuration thresholds
+ * @fixed_thold - Fixed thresholds limit used for the station
+ */
+struct cfg80211_sta_mon {
+ s32 *rssi_tholds;
+ u32 rssi_hyst;
+ int n_tholds;
+ u8 fixed_thold;
+};
+
+/**
* struct cfg80211_ops - backend description for wireless configuration
*
* This struct is registered by fullmac card drivers and/or wireless stacks
@@ -3501,6 +3515,12 @@ struct cfg80211_update_owe_info {
* @update_owe_info: Provide updated OWE info to driver. Driver implementing SME
* but offloading OWE processing to the user space will get the updated
* DH IE through this interface.
+ *
+ * @set_sta_mon_rssi_config: Configure RSSI threshold for a station.
+ * After configuration, the driver should (soon) send an event indicating
+ * the current level of a station is above/below the configured threshold;
+ * this may need some care when the configuration is changed
+ * (without first being disabled.)
*/
struct cfg80211_ops {
int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
@@ -3817,6 +3837,9 @@ struct cfg80211_ops {
struct cfg80211_pmsr_request *request);
int (*update_owe_info)(struct wiphy *wiphy, struct net_device *dev,
struct cfg80211_update_owe_info *owe_info);
+ int (*set_sta_mon_rssi_config)(struct wiphy *wiphy,
+ struct net_device *dev, const u8 *addr,
+ const struct cfg80211_sta_mon *sta_mon_cfg);
};

/*
@@ -6620,6 +6643,22 @@ bool cfg80211_rx_control_port(struct net_device *dev,
struct sk_buff *skb, bool unencrypted);

/**
+ * cfg80211_sta_mon_rssi_notify - Station's rssi out of range event
+ * @dev: network device
+ * @peer: Station's mac address
+ * @rssi_event: the triggered RSSI event
+ * @rssi_level: new RSSI level value or 0 if not available
+ * @gfp: context flags
+ *
+ * This function is called when a configured rssi threshold reached event
+ * occurs for a station.
+ */
+void
+cfg80211_sta_mon_rssi_notify(struct net_device *dev, const u8 *peer,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level, gfp_t gfp);
+
+/**
* cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
* @dev: network device
* @rssi_event: the triggered RSSI event
diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
index 25f70dd..cbe5866 100644
--- a/include/uapi/linux/nl80211.h
+++ b/include/uapi/linux/nl80211.h
@@ -1070,6 +1070,12 @@
* OWE AKM by the host drivers that implement SME but rely
* on the user space for the cryptographic/DH IE processing in AP mode.
*
+ * @NL80211_CMD_SET_STA_MON: This command is used to configure station's
+ * connection monitoring notification trigger levels. This uses nested
+ * attribute %NL80211_ATTR_CQM with %NL80211_ATTR_CQM_* sub-attributes.
+ * @NL80211_CMD_NOTIFY_STA_MON: This is used as an event to notify
+ * the user space that a trigger level was reached for a station.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1292,6 +1298,9 @@ enum nl80211_commands {

NL80211_CMD_UPDATE_OWE_INFO,

+ NL80211_CMD_SET_STA_MON,
+ NL80211_CMD_NOTIFY_STA_MON,
+
/* add new commands above here */

/* used to define NL80211_CMD_MAX below */
@@ -2324,6 +2333,12 @@ enum nl80211_commands {
* should be picking up the lowest tx power, either tx power per-interface
* or per-station.
*
+ * @NL80211_ATTR_STA_MON_FIXED_THOLD: This u8 attribute is used with
+ * %NL80211_CMD_SET_STA_MON to indicate driver that the monitoring
+ * threshold is fixed(not modifying the low and high threshold upon
+ * crossing the threshold) or moving(modifying low and high threshold
+ * upon crossing the threshold) thresholds.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2777,6 +2792,8 @@ enum nl80211_attrs {
NL80211_ATTR_STA_TX_POWER_SETTING,
NL80211_ATTR_STA_TX_POWER,

+ NL80211_ATTR_STA_MON_FIXED_THOLD,
+
/* add attributes here, update the policy in nl80211.c */

__NL80211_ATTR_AFTER_LAST,
@@ -5405,6 +5422,9 @@ enum nl80211_feature_flags {
* @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
* to a station.
*
+ * @NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG: Driver supports monitoring
+ * station's RSSI threshold value should adveritise this flag.
+ *
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
*/
@@ -5449,6 +5469,7 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
NL80211_EXT_FEATURE_EXT_KEY_ID,
NL80211_EXT_FEATURE_STA_TX_PWR,
+ NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG,

/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 846d25d..78657c0 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -280,6 +280,17 @@ static int validate_ie_attr(const struct nlattr *attr,
NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
};

+static const struct nla_policy
+nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
+ [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
+ [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 },
+};
+
const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
[NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
[NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
@@ -395,7 +406,8 @@ static int validate_ie_attr(const struct nlattr *attr,
[NL80211_ATTR_PS_STATE] = NLA_POLICY_RANGE(NLA_U32,
NL80211_PS_DISABLED,
NL80211_PS_ENABLED),
- [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
+ [NL80211_ATTR_CQM] =
+ NLA_POLICY_NESTED(nl80211_attr_cqm_policy),
[NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
[NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
[NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
@@ -546,6 +558,7 @@ static int validate_ie_attr(const struct nlattr *attr,
[NL80211_ATTR_PEER_MEASUREMENTS] =
NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
[NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
+ [NL80211_ATTR_STA_MON_FIXED_THOLD] = { .type = NLA_FLAG },
};

/* policy for the key attributes */
@@ -10498,17 +10511,6 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
return err;
}

-static const struct nla_policy
-nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
- [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
- [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
- [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
- [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
- [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
- [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
- [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 },
-};
-
static int nl80211_set_cqm_txe(struct genl_info *info,
u32 rate, u32 pkts, u32 intvl)
{
@@ -10589,14 +10591,9 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
return rdev_set_cqm_rssi_range_config(rdev, dev, low, high);
}

-static int nl80211_set_cqm_rssi(struct genl_info *info,
- const s32 *thresholds, int n_thresholds,
- u32 hysteresis)
+static int nl80211_validate_rssi_tholds(const s32 *thresholds, int n_thresholds)
{
- struct cfg80211_registered_device *rdev = info->user_ptr[0];
- struct net_device *dev = info->user_ptr[1];
- struct wireless_dev *wdev = dev->ieee80211_ptr;
- int i, err;
+ int i;
s32 prev = S32_MIN;

/* Check all values negative and sorted */
@@ -10606,6 +10603,21 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,

prev = thresholds[i];
}
+ return 0;
+}
+
+static int nl80211_set_cqm_rssi(struct genl_info *info,
+ const s32 *thresholds, int n_thresholds,
+ u32 hysteresis)
+{
+ struct cfg80211_registered_device *rdev = info->user_ptr[0];
+ struct net_device *dev = info->user_ptr[1];
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ int err;
+
+ err = nl80211_validate_rssi_tholds(thresholds, n_thresholds);
+ if (err)
+ return err;

if (wdev->iftype != NL80211_IFTYPE_STATION &&
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
@@ -10668,7 +10680,7 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
return -EINVAL;

err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
- nl80211_attr_cqm_policy, info->extack);
+ NULL, NULL);
if (err)
return err;

@@ -13405,6 +13417,78 @@ static int nl80211_update_owe_info(struct sk_buff *skb, struct genl_info *info)
return rdev_update_owe_info(rdev, dev, &owe_info);
}

+static int nl80211_set_sta_mon(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 nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
+ struct cfg80211_sta_mon sta_mon_config = {};
+ struct nlattr *sta_mon;
+ u8 *addr = NULL;
+ int err;
+
+ if (wdev->iftype != NL80211_IFTYPE_AP &&
+ wdev->iftype != NL80211_IFTYPE_P2P_GO)
+ return -ENOTSUPP;
+
+ wdev_lock(wdev);
+ sta_mon = info->attrs[NL80211_ATTR_CQM];
+ if (!sta_mon || !info->attrs[NL80211_ATTR_MAC]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, sta_mon,
+ NULL, NULL);
+ if (err) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
+ sta_mon_config.fixed_thold =
+ nla_get_flag(info->attrs[NL80211_ATTR_STA_MON_FIXED_THOLD]);
+
+ if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
+ attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
+ int len = nla_len(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+
+ sta_mon_config.rssi_tholds =
+ nla_data(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ sta_mon_config.rssi_hyst =
+ nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
+
+ if (!rdev->ops->set_sta_mon_rssi_config ||
+ !wiphy_ext_feature_isset(&rdev->wiphy,
+ NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG)) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ if (len % 4) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ sta_mon_config.n_tholds = len / 4;
+ err = nl80211_validate_rssi_tholds(sta_mon_config.rssi_tholds,
+ sta_mon_config.n_tholds);
+ if (err) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = rdev_set_sta_mon_rssi_config(rdev, dev, addr,
+ &sta_mon_config);
+ goto out;
+ }
+
+out:
+ wdev_unlock(wdev);
+ return err;
+}
+
#define NL80211_FLAG_NEED_WIPHY 0x01
#define NL80211_FLAG_NEED_NETDEV 0x02
#define NL80211_FLAG_NEED_RTNL 0x04
@@ -14242,6 +14326,13 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
.internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
NL80211_FLAG_NEED_RTNL,
},
+ {
+ .cmd = NL80211_CMD_SET_STA_MON,
+ .doit = nl80211_set_sta_mon,
+ .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 = {
@@ -15443,7 +15534,8 @@ bool cfg80211_rx_control_port(struct net_device *dev,
EXPORT_SYMBOL(cfg80211_rx_control_port);

static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
- const char *mac, gfp_t gfp)
+ const char *mac, gfp_t gfp,
+ enum nl80211_commands cmd)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@@ -15455,7 +15547,7 @@ static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,

cb = (void **)msg->cb;

- cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+ cb[0] = nl80211hdr_put(msg, 0, 0, 0, cmd);
if (!cb[0]) {
nlmsg_free(msg);
return NULL;
@@ -15517,7 +15609,7 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
rssi_level = wdev->cqm_config->last_rssi_event_value;
}

- msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp, NL80211_CMD_NOTIFY_CQM);
if (!msg)
return;

@@ -15538,13 +15630,48 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
}
EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);

+void
+cfg80211_sta_mon_rssi_notify(struct net_device *dev, const u8 *peer,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level, gfp_t gfp)
+{
+ struct sk_buff *msg;
+
+ trace_cfg80211_sta_mon_rssi_notify(dev, peer, rssi_event, rssi_level);
+ if (WARN_ON(!peer))
+ return;
+
+ if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
+ rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
+ return;
+
+ msg = cfg80211_prepare_cqm(dev, peer, gfp, NL80211_CMD_NOTIFY_STA_MON);
+ if (!msg)
+ return;
+
+ if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+ rssi_event))
+ goto nla_put_failure;
+
+ if (rssi_level && nla_put_s32(msg, NL80211_ATTR_CQM_RSSI_LEVEL,
+ rssi_level))
+ goto nla_put_failure;
+
+ cfg80211_send_cqm(msg, gfp);
+ return;
+
+ nla_put_failure:
+ nlmsg_free(msg);
+}
+EXPORT_SYMBOL(cfg80211_sta_mon_rssi_notify);
+
void cfg80211_cqm_txe_notify(struct net_device *dev,
const u8 *peer, u32 num_packets,
u32 rate, u32 intvl, gfp_t gfp)
{
struct sk_buff *msg;

- msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ msg = cfg80211_prepare_cqm(dev, peer, gfp, NL80211_CMD_NOTIFY_CQM);
if (!msg)
return;

@@ -15572,7 +15699,7 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,

trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);

- msg = cfg80211_prepare_cqm(dev, peer, gfp);
+ msg = cfg80211_prepare_cqm(dev, peer, gfp, NL80211_CMD_NOTIFY_CQM);
if (!msg)
return;

@@ -15591,7 +15718,7 @@ void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
{
struct sk_buff *msg;

- msg = cfg80211_prepare_cqm(dev, NULL, gfp);
+ msg = cfg80211_prepare_cqm(dev, NULL, gfp, NL80211_CMD_NOTIFY_CQM);
if (!msg)
return;

diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
index 18437a9..8d36745 100644
--- a/net/wireless/rdev-ops.h
+++ b/net/wireless/rdev-ops.h
@@ -1286,4 +1286,17 @@ static inline int rdev_update_owe_info(struct cfg80211_registered_device *rdev,
return ret;
}

+static inline int
+rdev_set_sta_mon_rssi_config(struct cfg80211_registered_device *rdev,
+ struct net_device *dev, const u8 *addr,
+ const struct cfg80211_sta_mon *sta_mon_config)
+{
+ int ret = -EOPNOTSUPP;
+
+ ret = rdev->ops->set_sta_mon_rssi_config(&rdev->wiphy, dev,
+ addr, sta_mon_config);
+ 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 488ef2ce..113cd45 100644
--- a/net/wireless/trace.h
+++ b/net/wireless/trace.h
@@ -3421,6 +3421,27 @@
WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
);

+TRACE_EVENT(cfg80211_sta_mon_rssi_notify,
+ TP_PROTO(struct net_device *netdev, const u8 *addr,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ s32 rssi_level),
+ TP_ARGS(netdev, addr, rssi_event, rssi_level),
+ TP_STRUCT__entry(
+ NETDEV_ENTRY
+ MAC_ENTRY(addr)
+ __field(enum nl80211_cqm_rssi_threshold_event, rssi_event)
+ __field(s32, rssi_level)
+ ),
+ TP_fast_assign(
+ NETDEV_ASSIGN;
+ MAC_ASSIGN(addr, addr);
+ __entry->rssi_event = rssi_event;
+ __entry->rssi_level = rssi_level;
+ ),
+ TP_printk(NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
+ ", rssi event: %d, level: %d", NETDEV_PR_ARG,
+ MAC_PR_ARG(addr), __entry->rssi_event, __entry->rssi_level)
+);
#endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */

#undef TRACE_INCLUDE_PATH
--
1.7.9.5


2019-04-26 10:03:41

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCHv4 0/2] cfg80211/mac80211: Add support to configure and monitor station's rssi threshold

On Wed, 2019-04-17 at 14:51 +0530, Tamizh chelvam wrote:
> This patchsets introduced new NL command and api to support
> configuring rssi for the connected stations and api to notify
> userspace application upon crossing the configured threshold.
> This will be useful for the application which requires
> station's current signal strength change information.

What is "the application"?

Given all our discussions at the summit and my thinking on all of this
recently, I'm _very_ tempted to just reject this patchset after all, and
ask you to (help) implement appropriate eBPF hooks in the stack that can
be used to implement it instead.

Why is this better than programmable infrastructure for it?

johannes


2019-04-26 10:05:55

by Johannes Berg

[permalink] [raw]
Subject: Re: [PATCHv4 2/2] mac80211: Implement API to configure station specific rssi threshold


> +void ieee80211_update_rssi_config(struct sta_info *sta)
> +{
> + s32 last;
> + u32 hyst;
> + int i, n;
> +
> + if (!sta->rssi_config || sta->rssi_config->fixed_thold)
> + return;

sparse is also giving various complaints about the RCU usage here.

johannes


2020-03-18 19:36:19

by Janusz Dziedzic

[permalink] [raw]
Subject: Re: [PATCHv4 1/2] cfg80211: Add support to configure station specific RSSI threshold for AP mode

śr., 17 kwi 2019 o 11:24 Tamizh chelvam <[email protected]> napisał(a):
>
> Add infrastructure to configure station specific RSSI threshold
> configuration to monitor station's signal strength variation.
> This configuration will be useful for the application like
> steering which requires change in the station's current signal
> strength.
>
> New NL80211_CMD_STA_MON introduced to configure the RSSI threshold
> using NL80211_ATTR_CQM nested attributes. The MAC address of
> a station is passed in NL80211_ATTR_MAC. And NL80211_ATTR_STA_MON_FIXED_THOLD
> introduced to have this RSSI threshold as a fixed(not modifying the
> low and high threshold upon crossing the threshold) or moving(modifying
> low and high threshold upon crossing the threshold) thresholds.
> Depends on the application's use case user can have either fixed or
> moving RSSI range thresholds.
>
> cfg80211_sta_mon_rssi_notify introduced to notify change in the
> station's signal stregnth cross event using NL80211_CMD_NOTIFY_STA_MON.
> Driver supporting this feature should advertise
> NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG feature flag.
>
> Signed-off-by: Tamizh chelvam <[email protected]>
> ---
> include/net/cfg80211.h | 39 +++++++++
> include/uapi/linux/nl80211.h | 21 +++++
> net/wireless/nl80211.c | 179 ++++++++++++++++++++++++++++++++++++------
> net/wireless/rdev-ops.h | 13 +++
> net/wireless/trace.h | 21 +++++
> 5 files changed, 247 insertions(+), 26 deletions(-)
>
> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> index 2ea04e9..cb8ba5a 100644
> --- a/include/net/cfg80211.h
> +++ b/include/net/cfg80211.h
> @@ -3161,6 +3161,20 @@ struct cfg80211_update_owe_info {
> };
>
> /**
> + * struct cfg80211_sta_mon - Configure station monitor parameters
> + * @rssi_tholds - array of RSSI thresholds for the station
> + * @rssi_hyst - RSSI hysterisis
> + * @n_tholds - Number of configuration thresholds
> + * @fixed_thold - Fixed thresholds limit used for the station
> + */
> +struct cfg80211_sta_mon {
> + s32 *rssi_tholds;
> + u32 rssi_hyst;
> + int n_tholds;
> + u8 fixed_thold;
> +};
> +
> +/**
> * struct cfg80211_ops - backend description for wireless configuration
> *
> * This struct is registered by fullmac card drivers and/or wireless stacks
> @@ -3501,6 +3515,12 @@ struct cfg80211_update_owe_info {
> * @update_owe_info: Provide updated OWE info to driver. Driver implementing SME
> * but offloading OWE processing to the user space will get the updated
> * DH IE through this interface.
> + *
> + * @set_sta_mon_rssi_config: Configure RSSI threshold for a station.
> + * After configuration, the driver should (soon) send an event indicating
> + * the current level of a station is above/below the configured threshold;
> + * this may need some care when the configuration is changed
> + * (without first being disabled.)
> */
> struct cfg80211_ops {
> int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan *wow);
> @@ -3817,6 +3837,9 @@ struct cfg80211_ops {
> struct cfg80211_pmsr_request *request);
> int (*update_owe_info)(struct wiphy *wiphy, struct net_device *dev,
> struct cfg80211_update_owe_info *owe_info);
> + int (*set_sta_mon_rssi_config)(struct wiphy *wiphy,
> + struct net_device *dev, const u8 *addr,
> + const struct cfg80211_sta_mon *sta_mon_cfg);
> };
>
> /*
> @@ -6620,6 +6643,22 @@ bool cfg80211_rx_control_port(struct net_device *dev,
> struct sk_buff *skb, bool unencrypted);
>
> /**
> + * cfg80211_sta_mon_rssi_notify - Station's rssi out of range event
> + * @dev: network device
> + * @peer: Station's mac address
> + * @rssi_event: the triggered RSSI event
> + * @rssi_level: new RSSI level value or 0 if not available
> + * @gfp: context flags
> + *
> + * This function is called when a configured rssi threshold reached event
> + * occurs for a station.
> + */
> +void
> +cfg80211_sta_mon_rssi_notify(struct net_device *dev, const u8 *peer,
> + enum nl80211_cqm_rssi_threshold_event rssi_event,
> + s32 rssi_level, gfp_t gfp);
> +
> +/**
> * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
> * @dev: network device
> * @rssi_event: the triggered RSSI event
> diff --git a/include/uapi/linux/nl80211.h b/include/uapi/linux/nl80211.h
> index 25f70dd..cbe5866 100644
> --- a/include/uapi/linux/nl80211.h
> +++ b/include/uapi/linux/nl80211.h
> @@ -1070,6 +1070,12 @@
> * OWE AKM by the host drivers that implement SME but rely
> * on the user space for the cryptographic/DH IE processing in AP mode.
> *
> + * @NL80211_CMD_SET_STA_MON: This command is used to configure station's
> + * connection monitoring notification trigger levels. This uses nested
> + * attribute %NL80211_ATTR_CQM with %NL80211_ATTR_CQM_* sub-attributes.
> + * @NL80211_CMD_NOTIFY_STA_MON: This is used as an event to notify
> + * the user space that a trigger level was reached for a station.
> + *
> * @NL80211_CMD_MAX: highest used command number
> * @__NL80211_CMD_AFTER_LAST: internal use
> */
> @@ -1292,6 +1298,9 @@ enum nl80211_commands {
>
> NL80211_CMD_UPDATE_OWE_INFO,
>
> + NL80211_CMD_SET_STA_MON,
> + NL80211_CMD_NOTIFY_STA_MON,
> +
> /* add new commands above here */
>
> /* used to define NL80211_CMD_MAX below */
> @@ -2324,6 +2333,12 @@ enum nl80211_commands {
> * should be picking up the lowest tx power, either tx power per-interface
> * or per-station.
> *
> + * @NL80211_ATTR_STA_MON_FIXED_THOLD: This u8 attribute is used with
> + * %NL80211_CMD_SET_STA_MON to indicate driver that the monitoring
> + * threshold is fixed(not modifying the low and high threshold upon
> + * crossing the threshold) or moving(modifying low and high threshold
> + * upon crossing the threshold) thresholds.
> + *
> * @NUM_NL80211_ATTR: total number of nl80211_attrs available
> * @NL80211_ATTR_MAX: highest attribute number currently defined
> * @__NL80211_ATTR_AFTER_LAST: internal use
> @@ -2777,6 +2792,8 @@ enum nl80211_attrs {
> NL80211_ATTR_STA_TX_POWER_SETTING,
> NL80211_ATTR_STA_TX_POWER,
>
> + NL80211_ATTR_STA_MON_FIXED_THOLD,
> +
> /* add attributes here, update the policy in nl80211.c */
>
> __NL80211_ATTR_AFTER_LAST,
> @@ -5405,6 +5422,9 @@ enum nl80211_feature_flags {
> * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling tx power
> * to a station.
> *
> + * @NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG: Driver supports monitoring
> + * station's RSSI threshold value should adveritise this flag.
> + *
> * @NUM_NL80211_EXT_FEATURES: number of extended features.
> * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
> */
> @@ -5449,6 +5469,7 @@ enum nl80211_ext_feature_index {
> NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
> NL80211_EXT_FEATURE_EXT_KEY_ID,
> NL80211_EXT_FEATURE_STA_TX_PWR,
> + NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG,
>
> /* add new features before the definition below */
> NUM_NL80211_EXT_FEATURES,
> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
> index 846d25d..78657c0 100644
> --- a/net/wireless/nl80211.c
> +++ b/net/wireless/nl80211.c
> @@ -280,6 +280,17 @@ static int validate_ie_attr(const struct nlattr *attr,
> NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
> };
>
> +static const struct nla_policy
> +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
> + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
> + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
> + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
> + [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
> + [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
> + [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
> + [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 },
> +};
> +
> const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
> [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
> [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
> @@ -395,7 +406,8 @@ static int validate_ie_attr(const struct nlattr *attr,
> [NL80211_ATTR_PS_STATE] = NLA_POLICY_RANGE(NLA_U32,
> NL80211_PS_DISABLED,
> NL80211_PS_ENABLED),
> - [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
> + [NL80211_ATTR_CQM] =
> + NLA_POLICY_NESTED(nl80211_attr_cqm_policy),
> [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
> [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
> [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
> @@ -546,6 +558,7 @@ static int validate_ie_attr(const struct nlattr *attr,
> [NL80211_ATTR_PEER_MEASUREMENTS] =
> NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
> [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
> + [NL80211_ATTR_STA_MON_FIXED_THOLD] = { .type = NLA_FLAG },
> };
>
> /* policy for the key attributes */
> @@ -10498,17 +10511,6 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
> return err;
> }
>
> -static const struct nla_policy
> -nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
> - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
> - [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
> - [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
> - [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
> - [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
> - [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
> - [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 },
> -};
> -
> static int nl80211_set_cqm_txe(struct genl_info *info,
> u32 rate, u32 pkts, u32 intvl)
> {
> @@ -10589,14 +10591,9 @@ static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
> return rdev_set_cqm_rssi_range_config(rdev, dev, low, high);
> }
>
> -static int nl80211_set_cqm_rssi(struct genl_info *info,
> - const s32 *thresholds, int n_thresholds,
> - u32 hysteresis)
> +static int nl80211_validate_rssi_tholds(const s32 *thresholds, int n_thresholds)
> {
> - struct cfg80211_registered_device *rdev = info->user_ptr[0];
> - struct net_device *dev = info->user_ptr[1];
> - struct wireless_dev *wdev = dev->ieee80211_ptr;
> - int i, err;
> + int i;
> s32 prev = S32_MIN;
>
> /* Check all values negative and sorted */
> @@ -10606,6 +10603,21 @@ static int nl80211_set_cqm_rssi(struct genl_info *info,
>
> prev = thresholds[i];
> }
> + return 0;
> +}
> +
> +static int nl80211_set_cqm_rssi(struct genl_info *info,
> + const s32 *thresholds, int n_thresholds,
> + u32 hysteresis)
> +{
> + struct cfg80211_registered_device *rdev = info->user_ptr[0];
> + struct net_device *dev = info->user_ptr[1];
> + struct wireless_dev *wdev = dev->ieee80211_ptr;
> + int err;
> +
> + err = nl80211_validate_rssi_tholds(thresholds, n_thresholds);
> + if (err)
> + return err;
>
> if (wdev->iftype != NL80211_IFTYPE_STATION &&
> wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
> @@ -10668,7 +10680,7 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
> return -EINVAL;
>
> err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
> - nl80211_attr_cqm_policy, info->extack);
> + NULL, NULL);
> if (err)
> return err;
>
> @@ -13405,6 +13417,78 @@ static int nl80211_update_owe_info(struct sk_buff *skb, struct genl_info *info)
> return rdev_update_owe_info(rdev, dev, &owe_info);
> }
>
> +static int nl80211_set_sta_mon(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 nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
> + struct cfg80211_sta_mon sta_mon_config = {};
> + struct nlattr *sta_mon;
> + u8 *addr = NULL;
> + int err;
> +
> + if (wdev->iftype != NL80211_IFTYPE_AP &&
> + wdev->iftype != NL80211_IFTYPE_P2P_GO)
> + return -ENOTSUPP;
> +
> + wdev_lock(wdev);
> + sta_mon = info->attrs[NL80211_ATTR_CQM];
> + if (!sta_mon || !info->attrs[NL80211_ATTR_MAC]) {
> + err = -EINVAL;
> + goto out;
> + }
> +
> + err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, sta_mon,
> + NULL, NULL);
> + if (err) {
> + err = -EINVAL;
> + goto out;
> + }
> +
> + addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
> + sta_mon_config.fixed_thold =
> + nla_get_flag(info->attrs[NL80211_ATTR_STA_MON_FIXED_THOLD]);
> +
> + if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
> + attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
> + int len = nla_len(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
> +
> + sta_mon_config.rssi_tholds =
> + nla_data(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
> + sta_mon_config.rssi_hyst =
> + nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
> +
> + if (!rdev->ops->set_sta_mon_rssi_config ||
> + !wiphy_ext_feature_isset(&rdev->wiphy,
> + NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG)) {
> + err = -EOPNOTSUPP;
> + goto out;
> + }
> +
> + if (len % 4) {
> + err = -EINVAL;
> + goto out;
> + }
> +
> + sta_mon_config.n_tholds = len / 4;
> + err = nl80211_validate_rssi_tholds(sta_mon_config.rssi_tholds,
> + sta_mon_config.n_tholds);
> + if (err) {
> + err = -EINVAL;
> + goto out;
> + }
> +
> + err = rdev_set_sta_mon_rssi_config(rdev, dev, addr,
> + &sta_mon_config);
> + goto out;
> + }
> +
> +out:
> + wdev_unlock(wdev);
> + return err;
> +}
> +
> #define NL80211_FLAG_NEED_WIPHY 0x01
> #define NL80211_FLAG_NEED_NETDEV 0x02
> #define NL80211_FLAG_NEED_RTNL 0x04
> @@ -14242,6 +14326,13 @@ static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
> .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
> NL80211_FLAG_NEED_RTNL,
> },
> + {
> + .cmd = NL80211_CMD_SET_STA_MON,
> + .doit = nl80211_set_sta_mon,
> + .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 = {
> @@ -15443,7 +15534,8 @@ bool cfg80211_rx_control_port(struct net_device *dev,
> EXPORT_SYMBOL(cfg80211_rx_control_port);
>
> static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
> - const char *mac, gfp_t gfp)
> + const char *mac, gfp_t gfp,
> + enum nl80211_commands cmd)
> {
> struct wireless_dev *wdev = dev->ieee80211_ptr;
> struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
> @@ -15455,7 +15547,7 @@ static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
>
> cb = (void **)msg->cb;
>
> - cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
> + cb[0] = nl80211hdr_put(msg, 0, 0, 0, cmd);
> if (!cb[0]) {
> nlmsg_free(msg);
> return NULL;
> @@ -15517,7 +15609,7 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
> rssi_level = wdev->cqm_config->last_rssi_event_value;
> }
>
> - msg = cfg80211_prepare_cqm(dev, NULL, gfp);
> + msg = cfg80211_prepare_cqm(dev, NULL, gfp, NL80211_CMD_NOTIFY_CQM);
> if (!msg)
> return;
>
> @@ -15538,13 +15630,48 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
> }
> EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
>
> +void
> +cfg80211_sta_mon_rssi_notify(struct net_device *dev, const u8 *peer,
> + enum nl80211_cqm_rssi_threshold_event rssi_event,
> + s32 rssi_level, gfp_t gfp)
> +{
> + struct sk_buff *msg;
> +
> + trace_cfg80211_sta_mon_rssi_notify(dev, peer, rssi_event, rssi_level);
> + if (WARN_ON(!peer))
> + return;
> +
> + if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
> + rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
> + return;
> +
> + msg = cfg80211_prepare_cqm(dev, peer, gfp, NL80211_CMD_NOTIFY_STA_MON);
> + if (!msg)
> + return;
> +
> + if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
> + rssi_event))
> + goto nla_put_failure;
> +
> + if (rssi_level && nla_put_s32(msg, NL80211_ATTR_CQM_RSSI_LEVEL,
> + rssi_level))
> + goto nla_put_failure;
> +
> + cfg80211_send_cqm(msg, gfp);
> + return;
> +
> + nla_put_failure:
> + nlmsg_free(msg);
> +}
> +EXPORT_SYMBOL(cfg80211_sta_mon_rssi_notify);
> +
> void cfg80211_cqm_txe_notify(struct net_device *dev,
> const u8 *peer, u32 num_packets,
> u32 rate, u32 intvl, gfp_t gfp)
> {
> struct sk_buff *msg;
>
> - msg = cfg80211_prepare_cqm(dev, peer, gfp);
> + msg = cfg80211_prepare_cqm(dev, peer, gfp, NL80211_CMD_NOTIFY_CQM);
> if (!msg)
> return;
>
> @@ -15572,7 +15699,7 @@ void cfg80211_cqm_pktloss_notify(struct net_device *dev,
>
> trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
>
> - msg = cfg80211_prepare_cqm(dev, peer, gfp);
> + msg = cfg80211_prepare_cqm(dev, peer, gfp, NL80211_CMD_NOTIFY_CQM);
> if (!msg)
> return;
>
> @@ -15591,7 +15718,7 @@ void cfg80211_cqm_beacon_loss_notify(struct net_device *dev, gfp_t gfp)
> {
> struct sk_buff *msg;
>
> - msg = cfg80211_prepare_cqm(dev, NULL, gfp);
> + msg = cfg80211_prepare_cqm(dev, NULL, gfp, NL80211_CMD_NOTIFY_CQM);
> if (!msg)
> return;
>
> diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
> index 18437a9..8d36745 100644
> --- a/net/wireless/rdev-ops.h
> +++ b/net/wireless/rdev-ops.h
> @@ -1286,4 +1286,17 @@ static inline int rdev_update_owe_info(struct cfg80211_registered_device *rdev,
> return ret;
> }
>
> +static inline int
> +rdev_set_sta_mon_rssi_config(struct cfg80211_registered_device *rdev,
> + struct net_device *dev, const u8 *addr,
> + const struct cfg80211_sta_mon *sta_mon_config)
> +{
> + int ret = -EOPNOTSUPP;
> +
> + ret = rdev->ops->set_sta_mon_rssi_config(&rdev->wiphy, dev,
> + addr, sta_mon_config);
> + 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 488ef2ce..113cd45 100644
> --- a/net/wireless/trace.h
> +++ b/net/wireless/trace.h
> @@ -3421,6 +3421,27 @@
> WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
> );
>
> +TRACE_EVENT(cfg80211_sta_mon_rssi_notify,
> + TP_PROTO(struct net_device *netdev, const u8 *addr,
> + enum nl80211_cqm_rssi_threshold_event rssi_event,
> + s32 rssi_level),
> + TP_ARGS(netdev, addr, rssi_event, rssi_level),
> + TP_STRUCT__entry(
> + NETDEV_ENTRY
> + MAC_ENTRY(addr)
> + __field(enum nl80211_cqm_rssi_threshold_event, rssi_event)
> + __field(s32, rssi_level)
> + ),
> + TP_fast_assign(
> + NETDEV_ASSIGN;
> + MAC_ASSIGN(addr, addr);
> + __entry->rssi_event = rssi_event;
> + __entry->rssi_level = rssi_level;
> + ),
> + TP_printk(NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
> + ", rssi event: %d, level: %d", NETDEV_PR_ARG,
> + MAC_PR_ARG(addr), __entry->rssi_event, __entry->rssi_level)
> +);
> #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
>
> #undef TRACE_INCLUDE_PATH
> --
> 1.7.9.5
>

Will you continue with that?

Maybe simple use:
- NL80211_CMD_SET_CQM - switch base on info->attrs[NL80211_ATTR_MAC],
if set assume this is for IFTYPE_AP in other case for IFTYPE_STA
- use n_thresolds - and only that on begining
- use/extend - rdev_set_cqm_rssi_range_config (... const u8 *addr ..)
- this is what we need rssi_low, rssi_high for AP and sta

Unfortunately for IFTYPE_STATION we are using: wdev->cqm_config (not
sure this is really required). Still even that required - can split to
sta/ap functions ...

This could be really useful for me. Don't know yet if will handle that
directly in hostapd or simple will forward this as an event to
hostapd_cli listeners.

BR
Janusz

2020-03-19 06:49:16

by Tamizh chelvam

[permalink] [raw]
Subject: Re: [PATCHv4 1/2] cfg80211: Add support to configure station specific RSSI threshold for AP mode

On 2020-03-19 01:04, Janusz Dziedzic wrote:
> śr., 17 kwi 2019 o 11:24 Tamizh chelvam <[email protected]>
> napisał(a):
>>
>> Add infrastructure to configure station specific RSSI threshold
>> configuration to monitor station's signal strength variation.
>> This configuration will be useful for the application like
>> steering which requires change in the station's current signal
>> strength.
>>
>> New NL80211_CMD_STA_MON introduced to configure the RSSI threshold
>> using NL80211_ATTR_CQM nested attributes. The MAC address of
>> a station is passed in NL80211_ATTR_MAC. And
>> NL80211_ATTR_STA_MON_FIXED_THOLD
>> introduced to have this RSSI threshold as a fixed(not modifying the
>> low and high threshold upon crossing the threshold) or
>> moving(modifying
>> low and high threshold upon crossing the threshold) thresholds.
>> Depends on the application's use case user can have either fixed or
>> moving RSSI range thresholds.
>>
>> cfg80211_sta_mon_rssi_notify introduced to notify change in the
>> station's signal stregnth cross event using
>> NL80211_CMD_NOTIFY_STA_MON.
>> Driver supporting this feature should advertise
>> NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG feature flag.
>>
>> Signed-off-by: Tamizh chelvam <[email protected]>
>> ---
>> include/net/cfg80211.h | 39 +++++++++
>> include/uapi/linux/nl80211.h | 21 +++++
>> net/wireless/nl80211.c | 179
>> ++++++++++++++++++++++++++++++++++++------
>> net/wireless/rdev-ops.h | 13 +++
>> net/wireless/trace.h | 21 +++++
>> 5 files changed, 247 insertions(+), 26 deletions(-)
>>
>> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
>> index 2ea04e9..cb8ba5a 100644
>> --- a/include/net/cfg80211.h
>> +++ b/include/net/cfg80211.h
>> @@ -3161,6 +3161,20 @@ struct cfg80211_update_owe_info {
>> };
>>
>> /**
>> + * struct cfg80211_sta_mon - Configure station monitor parameters
>> + * @rssi_tholds - array of RSSI thresholds for the station
>> + * @rssi_hyst - RSSI hysterisis
>> + * @n_tholds - Number of configuration thresholds
>> + * @fixed_thold - Fixed thresholds limit used for the station
>> + */
>> +struct cfg80211_sta_mon {
>> + s32 *rssi_tholds;
>> + u32 rssi_hyst;
>> + int n_tholds;
>> + u8 fixed_thold;
>> +};
>> +
>> +/**
>> * struct cfg80211_ops - backend description for wireless
>> configuration
>> *
>> * This struct is registered by fullmac card drivers and/or wireless
>> stacks
>> @@ -3501,6 +3515,12 @@ struct cfg80211_update_owe_info {
>> * @update_owe_info: Provide updated OWE info to driver. Driver
>> implementing SME
>> * but offloading OWE processing to the user space will get the
>> updated
>> * DH IE through this interface.
>> + *
>> + * @set_sta_mon_rssi_config: Configure RSSI threshold for a station.
>> + * After configuration, the driver should (soon) send an event
>> indicating
>> + * the current level of a station is above/below the configured
>> threshold;
>> + * this may need some care when the configuration is changed
>> + * (without first being disabled.)
>> */
>> struct cfg80211_ops {
>> int (*suspend)(struct wiphy *wiphy, struct cfg80211_wowlan
>> *wow);
>> @@ -3817,6 +3837,9 @@ struct cfg80211_ops {
>> struct cfg80211_pmsr_request *request);
>> int (*update_owe_info)(struct wiphy *wiphy, struct
>> net_device *dev,
>> struct cfg80211_update_owe_info
>> *owe_info);
>> + int (*set_sta_mon_rssi_config)(struct wiphy *wiphy,
>> + struct net_device *dev, const u8
>> *addr,
>> + const struct cfg80211_sta_mon
>> *sta_mon_cfg);
>> };
>>
>> /*
>> @@ -6620,6 +6643,22 @@ bool cfg80211_rx_control_port(struct net_device
>> *dev,
>> struct sk_buff *skb, bool unencrypted);
>>
>> /**
>> + * cfg80211_sta_mon_rssi_notify - Station's rssi out of range event
>> + * @dev: network device
>> + * @peer: Station's mac address
>> + * @rssi_event: the triggered RSSI event
>> + * @rssi_level: new RSSI level value or 0 if not available
>> + * @gfp: context flags
>> + *
>> + * This function is called when a configured rssi threshold reached
>> event
>> + * occurs for a station.
>> + */
>> +void
>> +cfg80211_sta_mon_rssi_notify(struct net_device *dev, const u8 *peer,
>> + enum nl80211_cqm_rssi_threshold_event
>> rssi_event,
>> + s32 rssi_level, gfp_t gfp);
>> +
>> +/**
>> * cfg80211_cqm_rssi_notify - connection quality monitoring rssi
>> event
>> * @dev: network device
>> * @rssi_event: the triggered RSSI event
>> diff --git a/include/uapi/linux/nl80211.h
>> b/include/uapi/linux/nl80211.h
>> index 25f70dd..cbe5866 100644
>> --- a/include/uapi/linux/nl80211.h
>> +++ b/include/uapi/linux/nl80211.h
>> @@ -1070,6 +1070,12 @@
>> * OWE AKM by the host drivers that implement SME but rely
>> * on the user space for the cryptographic/DH IE processing in AP
>> mode.
>> *
>> + * @NL80211_CMD_SET_STA_MON: This command is used to configure
>> station's
>> + * connection monitoring notification trigger levels. This uses
>> nested
>> + * attribute %NL80211_ATTR_CQM with %NL80211_ATTR_CQM_*
>> sub-attributes.
>> + * @NL80211_CMD_NOTIFY_STA_MON: This is used as an event to notify
>> + * the user space that a trigger level was reached for a station.
>> + *
>> * @NL80211_CMD_MAX: highest used command number
>> * @__NL80211_CMD_AFTER_LAST: internal use
>> */
>> @@ -1292,6 +1298,9 @@ enum nl80211_commands {
>>
>> NL80211_CMD_UPDATE_OWE_INFO,
>>
>> + NL80211_CMD_SET_STA_MON,
>> + NL80211_CMD_NOTIFY_STA_MON,
>> +
>> /* add new commands above here */
>>
>> /* used to define NL80211_CMD_MAX below */
>> @@ -2324,6 +2333,12 @@ enum nl80211_commands {
>> * should be picking up the lowest tx power, either tx power
>> per-interface
>> * or per-station.
>> *
>> + * @NL80211_ATTR_STA_MON_FIXED_THOLD: This u8 attribute is used with
>> + * %NL80211_CMD_SET_STA_MON to indicate driver that the
>> monitoring
>> + * threshold is fixed(not modifying the low and high threshold
>> upon
>> + * crossing the threshold) or moving(modifying low and high
>> threshold
>> + * upon crossing the threshold) thresholds.
>> + *
>> * @NUM_NL80211_ATTR: total number of nl80211_attrs available
>> * @NL80211_ATTR_MAX: highest attribute number currently defined
>> * @__NL80211_ATTR_AFTER_LAST: internal use
>> @@ -2777,6 +2792,8 @@ enum nl80211_attrs {
>> NL80211_ATTR_STA_TX_POWER_SETTING,
>> NL80211_ATTR_STA_TX_POWER,
>>
>> + NL80211_ATTR_STA_MON_FIXED_THOLD,
>> +
>> /* add attributes here, update the policy in nl80211.c */
>>
>> __NL80211_ATTR_AFTER_LAST,
>> @@ -5405,6 +5422,9 @@ enum nl80211_feature_flags {
>> * @NL80211_EXT_FEATURE_STA_TX_PWR: This driver supports controlling
>> tx power
>> * to a station.
>> *
>> + * @NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG: Driver supports
>> monitoring
>> + * station's RSSI threshold value should adveritise this flag.
>> + *
>> * @NUM_NL80211_EXT_FEATURES: number of extended features.
>> * @MAX_NL80211_EXT_FEATURES: highest extended feature index.
>> */
>> @@ -5449,6 +5469,7 @@ enum nl80211_ext_feature_index {
>> NL80211_EXT_FEATURE_SCHED_SCAN_BAND_SPECIFIC_RSSI_THOLD,
>> NL80211_EXT_FEATURE_EXT_KEY_ID,
>> NL80211_EXT_FEATURE_STA_TX_PWR,
>> + NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG,
>>
>> /* add new features before the definition below */
>> NUM_NL80211_EXT_FEATURES,
>> diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
>> index 846d25d..78657c0 100644
>> --- a/net/wireless/nl80211.c
>> +++ b/net/wireless/nl80211.c
>> @@ -280,6 +280,17 @@ static int validate_ie_attr(const struct nlattr
>> *attr,
>>
>> NLA_POLICY_NESTED_ARRAY(nl80211_psmr_peer_attr_policy),
>> };
>>
>> +static const struct nla_policy
>> +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
>> + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
>> + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
>> + [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
>> + [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
>> + [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
>> + [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
>> + [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 },
>> +};
>> +
>> const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
>> [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
>> [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
>> @@ -395,7 +406,8 @@ static int validate_ie_attr(const struct nlattr
>> *attr,
>> [NL80211_ATTR_PS_STATE] = NLA_POLICY_RANGE(NLA_U32,
>>
>> NL80211_PS_DISABLED,
>>
>> NL80211_PS_ENABLED),
>> - [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
>> + [NL80211_ATTR_CQM] =
>> + NLA_POLICY_NESTED(nl80211_attr_cqm_policy),
>> [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
>> [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
>> [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
>> @@ -546,6 +558,7 @@ static int validate_ie_attr(const struct nlattr
>> *attr,
>> [NL80211_ATTR_PEER_MEASUREMENTS] =
>> NLA_POLICY_NESTED(nl80211_pmsr_attr_policy),
>> [NL80211_ATTR_AIRTIME_WEIGHT] = NLA_POLICY_MIN(NLA_U16, 1),
>> + [NL80211_ATTR_STA_MON_FIXED_THOLD] = { .type = NLA_FLAG },
>> };
>>
>> /* policy for the key attributes */
>> @@ -10498,17 +10511,6 @@ static int nl80211_get_power_save(struct
>> sk_buff *skb, struct genl_info *info)
>> return err;
>> }
>>
>> -static const struct nla_policy
>> -nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
>> - [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
>> - [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
>> - [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
>> - [NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
>> - [NL80211_ATTR_CQM_TXE_PKTS] = { .type = NLA_U32 },
>> - [NL80211_ATTR_CQM_TXE_INTVL] = { .type = NLA_U32 },
>> - [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_S32 },
>> -};
>> -
>> static int nl80211_set_cqm_txe(struct genl_info *info,
>> u32 rate, u32 pkts, u32 intvl)
>> {
>> @@ -10589,14 +10591,9 @@ static int cfg80211_cqm_rssi_update(struct
>> cfg80211_registered_device *rdev,
>> return rdev_set_cqm_rssi_range_config(rdev, dev, low, high);
>> }
>>
>> -static int nl80211_set_cqm_rssi(struct genl_info *info,
>> - const s32 *thresholds, int
>> n_thresholds,
>> - u32 hysteresis)
>> +static int nl80211_validate_rssi_tholds(const s32 *thresholds, int
>> n_thresholds)
>> {
>> - struct cfg80211_registered_device *rdev = info->user_ptr[0];
>> - struct net_device *dev = info->user_ptr[1];
>> - struct wireless_dev *wdev = dev->ieee80211_ptr;
>> - int i, err;
>> + int i;
>> s32 prev = S32_MIN;
>>
>> /* Check all values negative and sorted */
>> @@ -10606,6 +10603,21 @@ static int nl80211_set_cqm_rssi(struct
>> genl_info *info,
>>
>> prev = thresholds[i];
>> }
>> + return 0;
>> +}
>> +
>> +static int nl80211_set_cqm_rssi(struct genl_info *info,
>> + const s32 *thresholds, int
>> n_thresholds,
>> + u32 hysteresis)
>> +{
>> + struct cfg80211_registered_device *rdev = info->user_ptr[0];
>> + struct net_device *dev = info->user_ptr[1];
>> + struct wireless_dev *wdev = dev->ieee80211_ptr;
>> + int err;
>> +
>> + err = nl80211_validate_rssi_tholds(thresholds, n_thresholds);
>> + if (err)
>> + return err;
>>
>> if (wdev->iftype != NL80211_IFTYPE_STATION &&
>> wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
>> @@ -10668,7 +10680,7 @@ static int nl80211_set_cqm(struct sk_buff
>> *skb, struct genl_info *info)
>> return -EINVAL;
>>
>> err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
>> - nl80211_attr_cqm_policy, info->extack);
>> + NULL, NULL);
>> if (err)
>> return err;
>>
>> @@ -13405,6 +13417,78 @@ static int nl80211_update_owe_info(struct
>> sk_buff *skb, struct genl_info *info)
>> return rdev_update_owe_info(rdev, dev, &owe_info);
>> }
>>
>> +static int nl80211_set_sta_mon(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 nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
>> + struct cfg80211_sta_mon sta_mon_config = {};
>> + struct nlattr *sta_mon;
>> + u8 *addr = NULL;
>> + int err;
>> +
>> + if (wdev->iftype != NL80211_IFTYPE_AP &&
>> + wdev->iftype != NL80211_IFTYPE_P2P_GO)
>> + return -ENOTSUPP;
>> +
>> + wdev_lock(wdev);
>> + sta_mon = info->attrs[NL80211_ATTR_CQM];
>> + if (!sta_mon || !info->attrs[NL80211_ATTR_MAC]) {
>> + err = -EINVAL;
>> + goto out;
>> + }
>> +
>> + err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, sta_mon,
>> + NULL, NULL);
>> + if (err) {
>> + err = -EINVAL;
>> + goto out;
>> + }
>> +
>> + addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
>> + sta_mon_config.fixed_thold =
>> +
>> nla_get_flag(info->attrs[NL80211_ATTR_STA_MON_FIXED_THOLD]);
>> +
>> + if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
>> + attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
>> + int len = nla_len(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
>> +
>> + sta_mon_config.rssi_tholds =
>> + nla_data(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
>> + sta_mon_config.rssi_hyst =
>> +
>> nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
>> +
>> + if (!rdev->ops->set_sta_mon_rssi_config ||
>> + !wiphy_ext_feature_isset(&rdev->wiphy,
>> +
>> NL80211_EXT_FEATURE_STA_MON_RSSI_CONFIG)) {
>> + err = -EOPNOTSUPP;
>> + goto out;
>> + }
>> +
>> + if (len % 4) {
>> + err = -EINVAL;
>> + goto out;
>> + }
>> +
>> + sta_mon_config.n_tholds = len / 4;
>> + err =
>> nl80211_validate_rssi_tholds(sta_mon_config.rssi_tholds,
>> +
>> sta_mon_config.n_tholds);
>> + if (err) {
>> + err = -EINVAL;
>> + goto out;
>> + }
>> +
>> + err = rdev_set_sta_mon_rssi_config(rdev, dev, addr,
>> + &sta_mon_config);
>> + goto out;
>> + }
>> +
>> +out:
>> + wdev_unlock(wdev);
>> + return err;
>> +}
>> +
>> #define NL80211_FLAG_NEED_WIPHY 0x01
>> #define NL80211_FLAG_NEED_NETDEV 0x02
>> #define NL80211_FLAG_NEED_RTNL 0x04
>> @@ -14242,6 +14326,13 @@ static void nl80211_post_doit(const struct
>> genl_ops *ops, struct sk_buff *skb,
>> .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
>> NL80211_FLAG_NEED_RTNL,
>> },
>> + {
>> + .cmd = NL80211_CMD_SET_STA_MON,
>> + .doit = nl80211_set_sta_mon,
>> + .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 = {
>> @@ -15443,7 +15534,8 @@ bool cfg80211_rx_control_port(struct
>> net_device *dev,
>> EXPORT_SYMBOL(cfg80211_rx_control_port);
>>
>> static struct sk_buff *cfg80211_prepare_cqm(struct net_device *dev,
>> - const char *mac, gfp_t
>> gfp)
>> + const char *mac, gfp_t
>> gfp,
>> + enum nl80211_commands cmd)
>> {
>> struct wireless_dev *wdev = dev->ieee80211_ptr;
>> struct cfg80211_registered_device *rdev =
>> wiphy_to_rdev(wdev->wiphy);
>> @@ -15455,7 +15547,7 @@ static struct sk_buff
>> *cfg80211_prepare_cqm(struct net_device *dev,
>>
>> cb = (void **)msg->cb;
>>
>> - cb[0] = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
>> + cb[0] = nl80211hdr_put(msg, 0, 0, 0, cmd);
>> if (!cb[0]) {
>> nlmsg_free(msg);
>> return NULL;
>> @@ -15517,7 +15609,7 @@ void cfg80211_cqm_rssi_notify(struct
>> net_device *dev,
>> rssi_level =
>> wdev->cqm_config->last_rssi_event_value;
>> }
>>
>> - msg = cfg80211_prepare_cqm(dev, NULL, gfp);
>> + msg = cfg80211_prepare_cqm(dev, NULL, gfp,
>> NL80211_CMD_NOTIFY_CQM);
>> if (!msg)
>> return;
>>
>> @@ -15538,13 +15630,48 @@ void cfg80211_cqm_rssi_notify(struct
>> net_device *dev,
>> }
>> EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
>>
>> +void
>> +cfg80211_sta_mon_rssi_notify(struct net_device *dev, const u8 *peer,
>> + enum nl80211_cqm_rssi_threshold_event
>> rssi_event,
>> + s32 rssi_level, gfp_t gfp)
>> +{
>> + struct sk_buff *msg;
>> +
>> + trace_cfg80211_sta_mon_rssi_notify(dev, peer, rssi_event,
>> rssi_level);
>> + if (WARN_ON(!peer))
>> + return;
>> +
>> + if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW
>> &&
>> + rssi_event !=
>> NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
>> + return;
>> +
>> + msg = cfg80211_prepare_cqm(dev, peer, gfp,
>> NL80211_CMD_NOTIFY_STA_MON);
>> + if (!msg)
>> + return;
>> +
>> + if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
>> + rssi_event))
>> + goto nla_put_failure;
>> +
>> + if (rssi_level && nla_put_s32(msg,
>> NL80211_ATTR_CQM_RSSI_LEVEL,
>> + rssi_level))
>> + goto nla_put_failure;
>> +
>> + cfg80211_send_cqm(msg, gfp);
>> + return;
>> +
>> + nla_put_failure:
>> + nlmsg_free(msg);
>> +}
>> +EXPORT_SYMBOL(cfg80211_sta_mon_rssi_notify);
>> +
>> void cfg80211_cqm_txe_notify(struct net_device *dev,
>> const u8 *peer, u32 num_packets,
>> u32 rate, u32 intvl, gfp_t gfp)
>> {
>> struct sk_buff *msg;
>>
>> - msg = cfg80211_prepare_cqm(dev, peer, gfp);
>> + msg = cfg80211_prepare_cqm(dev, peer, gfp,
>> NL80211_CMD_NOTIFY_CQM);
>> if (!msg)
>> return;
>>
>> @@ -15572,7 +15699,7 @@ void cfg80211_cqm_pktloss_notify(struct
>> net_device *dev,
>>
>> trace_cfg80211_cqm_pktloss_notify(dev, peer, num_packets);
>>
>> - msg = cfg80211_prepare_cqm(dev, peer, gfp);
>> + msg = cfg80211_prepare_cqm(dev, peer, gfp,
>> NL80211_CMD_NOTIFY_CQM);
>> if (!msg)
>> return;
>>
>> @@ -15591,7 +15718,7 @@ void cfg80211_cqm_beacon_loss_notify(struct
>> net_device *dev, gfp_t gfp)
>> {
>> struct sk_buff *msg;
>>
>> - msg = cfg80211_prepare_cqm(dev, NULL, gfp);
>> + msg = cfg80211_prepare_cqm(dev, NULL, gfp,
>> NL80211_CMD_NOTIFY_CQM);
>> if (!msg)
>> return;
>>
>> diff --git a/net/wireless/rdev-ops.h b/net/wireless/rdev-ops.h
>> index 18437a9..8d36745 100644
>> --- a/net/wireless/rdev-ops.h
>> +++ b/net/wireless/rdev-ops.h
>> @@ -1286,4 +1286,17 @@ static inline int rdev_update_owe_info(struct
>> cfg80211_registered_device *rdev,
>> return ret;
>> }
>>
>> +static inline int
>> +rdev_set_sta_mon_rssi_config(struct cfg80211_registered_device *rdev,
>> + struct net_device *dev, const u8 *addr,
>> + const struct cfg80211_sta_mon
>> *sta_mon_config)
>> +{
>> + int ret = -EOPNOTSUPP;
>> +
>> + ret = rdev->ops->set_sta_mon_rssi_config(&rdev->wiphy, dev,
>> + addr,
>> sta_mon_config);
>> + 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 488ef2ce..113cd45 100644
>> --- a/net/wireless/trace.h
>> +++ b/net/wireless/trace.h
>> @@ -3421,6 +3421,27 @@
>> WIPHY_PR_ARG, NETDEV_PR_ARG, MAC_PR_ARG(peer))
>> );
>>
>> +TRACE_EVENT(cfg80211_sta_mon_rssi_notify,
>> + TP_PROTO(struct net_device *netdev, const u8 *addr,
>> + enum nl80211_cqm_rssi_threshold_event rssi_event,
>> + s32 rssi_level),
>> + TP_ARGS(netdev, addr, rssi_event, rssi_level),
>> + TP_STRUCT__entry(
>> + NETDEV_ENTRY
>> + MAC_ENTRY(addr)
>> + __field(enum nl80211_cqm_rssi_threshold_event,
>> rssi_event)
>> + __field(s32, rssi_level)
>> + ),
>> + TP_fast_assign(
>> + NETDEV_ASSIGN;
>> + MAC_ASSIGN(addr, addr);
>> + __entry->rssi_event = rssi_event;
>> + __entry->rssi_level = rssi_level;
>> + ),
>> + TP_printk(NETDEV_PR_FMT ", station mac: " MAC_PR_FMT
>> + ", rssi event: %d, level: %d", NETDEV_PR_ARG,
>> + MAC_PR_ARG(addr), __entry->rssi_event,
>> __entry->rssi_level)
>> +);
>> #endif /* !__RDEV_OPS_TRACE || TRACE_HEADER_MULTI_READ */
>>
>> #undef TRACE_INCLUDE_PATH
>> --
>> 1.7.9.5
>>
>
> Will you continue with that?
>
Currently not working on this patchset.

> Maybe simple use:
> - NL80211_CMD_SET_CQM - switch base on info->attrs[NL80211_ATTR_MAC],
> if set assume this is for IFTYPE_AP in other case for IFTYPE_STA
> - use n_thresolds - and only that on begining
> - use/extend - rdev_set_cqm_rssi_range_config (... const u8 *addr ..)
> - this is what we need rssi_low, rssi_high for AP and sta
>

It was my first design using NL80211_CMD_SET_CQM and that was not
accepted by the community.
And I have moved to this design then got the suggestion to go with bpf
approach.

> Unfortunately for IFTYPE_STATION we are using: wdev->cqm_config (not
> sure this is really required). Still even that required - can split to
> sta/ap functions ...
>
> This could be really useful for me. Don't know yet if will handle that
> directly in hostapd or simple will forward this as an event to
> hostapd_cli listeners.
>

Thanks,
Tamizh.