Skip to content

Commit 4a4b816

Browse files
balrog-kunjmberg-intel
authored andcommitted
cfg80211: Accept multiple RSSI thresholds for CQM
Change the SET CQM command's RSSI threshold attribute to accept any number of thresholds as a sorted array. The API should be backwards compatible so that if one s32 threshold value is passed, the old mechanism is enabled. The netlink event generated is the same in both cases. cfg80211 handles an arbitrary number of RSSI thresholds but drivers have to provide a method (set_cqm_rssi_range_config) that configures a range set by a high and a low value. Drivers have to call back when the RSSI goes out of that range and there's no additional event for each time the range is reconfigured as there was with the current one-threshold API. This method doesn't have a hysteresis parameter because there's no benefit to the cfg80211 code from having the hysteresis be handled by hardware/driver in terms of the number of wakeups. At the same time it would likely be less consistent between drivers if offloaded or done in the drivers. Signed-off-by: Andrew Zaborowski <andrew.zaborowski@intel.com> Signed-off-by: Johannes Berg <johannes.berg@intel.com>
1 parent 3eb0928 commit 4a4b816

File tree

7 files changed

+198
-14
lines changed

7 files changed

+198
-14
lines changed

include/net/cfg80211.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2712,6 +2712,11 @@ struct cfg80211_nan_func {
27122712
* the current level is above/below the configured threshold; this may
27132713
* need some care when the configuration is changed (without first being
27142714
* disabled.)
2715+
* @set_cqm_rssi_range_config: Configure two RSSI thresholds in the
2716+
* connection quality monitor. An event is to be sent only when the
2717+
* signal level is found to be outside the two values. The driver should
2718+
* set %NL80211_EXT_FEATURE_CQM_RSSI_LIST if this method is implemented.
2719+
* If it is provided then there's no point providing @set_cqm_rssi_config.
27152720
* @set_cqm_txe_config: Configure connection quality monitor TX error
27162721
* thresholds.
27172722
* @sched_scan_start: Tell the driver to start a scheduled scan.
@@ -3001,6 +3006,10 @@ struct cfg80211_ops {
30013006
struct net_device *dev,
30023007
s32 rssi_thold, u32 rssi_hyst);
30033008

3009+
int (*set_cqm_rssi_range_config)(struct wiphy *wiphy,
3010+
struct net_device *dev,
3011+
s32 rssi_low, s32 rssi_high);
3012+
30043013
int (*set_cqm_txe_config)(struct wiphy *wiphy,
30053014
struct net_device *dev,
30063015
u32 rate, u32 pkts, u32 intvl);
@@ -3871,6 +3880,7 @@ void wiphy_free(struct wiphy *wiphy);
38713880
struct cfg80211_conn;
38723881
struct cfg80211_internal_bss;
38733882
struct cfg80211_cached_keys;
3883+
struct cfg80211_cqm_config;
38743884

38753885
/**
38763886
* struct wireless_dev - wireless device state
@@ -3934,6 +3944,7 @@ struct cfg80211_cached_keys;
39343944
* @event_list: (private) list for internal event processing
39353945
* @event_lock: (private) lock for event list
39363946
* @owner_nlportid: (private) owner socket port ID
3947+
* @cqm_config: (private) nl80211 RSSI monitor state
39373948
*/
39383949
struct wireless_dev {
39393950
struct wiphy *wiphy;
@@ -4002,6 +4013,8 @@ struct wireless_dev {
40024013
bool prev_bssid_valid;
40034014
} wext;
40044015
#endif
4016+
4017+
struct cfg80211_cqm_config *cqm_config;
40054018
};
40064019

40074020
static inline u8 *wdev_address(struct wireless_dev *wdev)

include/uapi/linux/nl80211.h

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3942,7 +3942,10 @@ enum nl80211_ps_state {
39423942
* @__NL80211_ATTR_CQM_INVALID: invalid
39433943
* @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies
39443944
* the threshold for the RSSI level at which an event will be sent. Zero
3945-
* to disable.
3945+
* to disable. Alternatively, if %NL80211_EXT_FEATURE_CQM_RSSI_LIST is
3946+
* set, multiple values can be supplied as a low-to-high sorted array of
3947+
* threshold values in dBm. Events will be sent when the RSSI value
3948+
* crosses any of the thresholds.
39463949
* @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies
39473950
* the minimum amount the RSSI level must change after an event before a
39483951
* new event may be issued (to reduce effects of RSSI oscillation).
@@ -4753,6 +4756,9 @@ enum nl80211_feature_flags {
47534756
* @NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI: The driver supports sched_scan
47544757
* for reporting BSSs with better RSSI than the current connected BSS
47554758
* (%NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI).
4759+
* @NL80211_EXT_FEATURE_CQM_RSSI_LIST: With this driver the
4760+
* %NL80211_ATTR_CQM_RSSI_THOLD attribute accepts a list of zero or more
4761+
* RSSI threshold values to monitor rather than exactly one threshold.
47564762
*
47574763
* @NUM_NL80211_EXT_FEATURES: number of extended features.
47584764
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -4771,6 +4777,7 @@ enum nl80211_ext_feature_index {
47714777
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA,
47724778
NL80211_EXT_FEATURE_MGMT_TX_RANDOM_TA_CONNECTED,
47734779
NL80211_EXT_FEATURE_SCHED_SCAN_RELATIVE_RSSI,
4780+
NL80211_EXT_FEATURE_CQM_RSSI_LIST,
47744781

47754782
/* add new features before the definition below */
47764783
NUM_NL80211_EXT_FEATURES,

net/wireless/core.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,12 @@ void wiphy_rfkill_set_hw_state(struct wiphy *wiphy, bool blocked)
954954
}
955955
EXPORT_SYMBOL(wiphy_rfkill_set_hw_state);
956956

957+
void cfg80211_cqm_config_free(struct wireless_dev *wdev)
958+
{
959+
kfree(wdev->cqm_config);
960+
wdev->cqm_config = NULL;
961+
}
962+
957963
void cfg80211_unregister_wdev(struct wireless_dev *wdev)
958964
{
959965
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
@@ -980,6 +986,8 @@ void cfg80211_unregister_wdev(struct wireless_dev *wdev)
980986
WARN_ON_ONCE(1);
981987
break;
982988
}
989+
990+
cfg80211_cqm_config_free(wdev);
983991
}
984992
EXPORT_SYMBOL(cfg80211_unregister_wdev);
985993

@@ -1234,6 +1242,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block *nb,
12341242
kzfree(wdev->wext.keys);
12351243
#endif
12361244
flush_work(&wdev->disconnect_wk);
1245+
cfg80211_cqm_config_free(wdev);
12371246
}
12381247
/*
12391248
* synchronise (so that we won't find this netdev

net/wireless/core.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,13 @@ struct cfg80211_iface_destroy {
272272
u32 nlportid;
273273
};
274274

275+
struct cfg80211_cqm_config {
276+
u32 rssi_hyst;
277+
s32 last_rssi_event_value;
278+
int n_rssi_thresholds;
279+
s32 rssi_thresholds[0];
280+
};
281+
275282
void cfg80211_destroy_ifaces(struct cfg80211_registered_device *rdev);
276283

277284
/* free object */
@@ -512,4 +519,6 @@ void cfg80211_stop_nan(struct cfg80211_registered_device *rdev,
512519
#define CFG80211_DEV_WARN_ON(cond) ({bool __r = (cond); __r; })
513520
#endif
514521

522+
void cfg80211_cqm_config_free(struct wireless_dev *wdev);
523+
515524
#endif /* __NET_WIRELESS_CORE_H */

net/wireless/nl80211.c

Lines changed: 125 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9473,7 +9473,7 @@ static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
94739473

94749474
static const struct nla_policy
94759475
nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] = {
9476-
[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
9476+
[NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_BINARY },
94779477
[NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
94789478
[NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
94799479
[NL80211_ATTR_CQM_TXE_RATE] = { .type = NLA_U32 },
@@ -9502,28 +9502,123 @@ static int nl80211_set_cqm_txe(struct genl_info *info,
95029502
return rdev_set_cqm_txe_config(rdev, dev, rate, pkts, intvl);
95039503
}
95049504

9505+
static int cfg80211_cqm_rssi_update(struct cfg80211_registered_device *rdev,
9506+
struct net_device *dev)
9507+
{
9508+
struct wireless_dev *wdev = dev->ieee80211_ptr;
9509+
s32 last, low, high;
9510+
u32 hyst;
9511+
int i, n;
9512+
int err;
9513+
9514+
/* RSSI reporting disabled? */
9515+
if (!wdev->cqm_config)
9516+
return rdev_set_cqm_rssi_range_config(rdev, dev, 0, 0);
9517+
9518+
/*
9519+
* Obtain current RSSI value if possible, if not and no RSSI threshold
9520+
* event has been received yet, we should receive an event after a
9521+
* connection is established and enough beacons received to calculate
9522+
* the average.
9523+
*/
9524+
if (!wdev->cqm_config->last_rssi_event_value && wdev->current_bss &&
9525+
rdev->ops->get_station) {
9526+
struct station_info sinfo;
9527+
u8 *mac_addr;
9528+
9529+
mac_addr = wdev->current_bss->pub.bssid;
9530+
9531+
err = rdev_get_station(rdev, dev, mac_addr, &sinfo);
9532+
if (err)
9533+
return err;
9534+
9535+
if (sinfo.filled & BIT(NL80211_STA_INFO_BEACON_SIGNAL_AVG))
9536+
wdev->cqm_config->last_rssi_event_value =
9537+
(s8) sinfo.rx_beacon_signal_avg;
9538+
}
9539+
9540+
last = wdev->cqm_config->last_rssi_event_value;
9541+
hyst = wdev->cqm_config->rssi_hyst;
9542+
n = wdev->cqm_config->n_rssi_thresholds;
9543+
9544+
for (i = 0; i < n; i++)
9545+
if (last < wdev->cqm_config->rssi_thresholds[i])
9546+
break;
9547+
9548+
low = i > 0 ?
9549+
(wdev->cqm_config->rssi_thresholds[i - 1] - hyst) : S32_MIN;
9550+
high = i < n ?
9551+
(wdev->cqm_config->rssi_thresholds[i] + hyst - 1) : S32_MAX;
9552+
9553+
return rdev_set_cqm_rssi_range_config(rdev, dev, low, high);
9554+
}
9555+
95059556
static int nl80211_set_cqm_rssi(struct genl_info *info,
9506-
s32 threshold, u32 hysteresis)
9557+
const s32 *thresholds, int n_thresholds,
9558+
u32 hysteresis)
95079559
{
95089560
struct cfg80211_registered_device *rdev = info->user_ptr[0];
95099561
struct net_device *dev = info->user_ptr[1];
95109562
struct wireless_dev *wdev = dev->ieee80211_ptr;
9563+
int i, err;
9564+
s32 prev = S32_MIN;
95119565

9512-
if (threshold > 0)
9513-
return -EINVAL;
9514-
9515-
/* disabling - hysteresis should also be zero then */
9516-
if (threshold == 0)
9517-
hysteresis = 0;
9566+
/* Check all values negative and sorted */
9567+
for (i = 0; i < n_thresholds; i++) {
9568+
if (thresholds[i] > 0 || thresholds[i] <= prev)
9569+
return -EINVAL;
95189570

9519-
if (!rdev->ops->set_cqm_rssi_config)
9520-
return -EOPNOTSUPP;
9571+
prev = thresholds[i];
9572+
}
95219573

95229574
if (wdev->iftype != NL80211_IFTYPE_STATION &&
95239575
wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
95249576
return -EOPNOTSUPP;
95259577

9526-
return rdev_set_cqm_rssi_config(rdev, dev, threshold, hysteresis);
9578+
wdev_lock(wdev);
9579+
cfg80211_cqm_config_free(wdev);
9580+
wdev_unlock(wdev);
9581+
9582+
if (n_thresholds <= 1 && rdev->ops->set_cqm_rssi_config) {
9583+
if (n_thresholds == 0 || thresholds[0] == 0) /* Disabling */
9584+
return rdev_set_cqm_rssi_config(rdev, dev, 0, 0);
9585+
9586+
return rdev_set_cqm_rssi_config(rdev, dev,
9587+
thresholds[0], hysteresis);
9588+
}
9589+
9590+
if (!wiphy_ext_feature_isset(&rdev->wiphy,
9591+
NL80211_EXT_FEATURE_CQM_RSSI_LIST))
9592+
return -EOPNOTSUPP;
9593+
9594+
if (n_thresholds == 1 && thresholds[0] == 0) /* Disabling */
9595+
n_thresholds = 0;
9596+
9597+
wdev_lock(wdev);
9598+
if (n_thresholds) {
9599+
struct cfg80211_cqm_config *cqm_config;
9600+
9601+
cqm_config = kzalloc(sizeof(struct cfg80211_cqm_config) +
9602+
n_thresholds * sizeof(s32), GFP_KERNEL);
9603+
if (!cqm_config) {
9604+
err = -ENOMEM;
9605+
goto unlock;
9606+
}
9607+
9608+
cqm_config->rssi_hyst = hysteresis;
9609+
cqm_config->n_rssi_thresholds = n_thresholds;
9610+
memcpy(cqm_config->rssi_thresholds, thresholds,
9611+
n_thresholds * sizeof(s32));
9612+
9613+
wdev->cqm_config = cqm_config;
9614+
}
9615+
9616+
err = cfg80211_cqm_rssi_update(rdev, dev);
9617+
9618+
unlock:
9619+
wdev_unlock(wdev);
9620+
9621+
return err;
95279622
}
95289623

95299624
static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
@@ -9543,10 +9638,16 @@ static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
95439638

95449639
if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
95459640
attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
9546-
s32 threshold = nla_get_s32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
9641+
const s32 *thresholds =
9642+
nla_data(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
9643+
int len = nla_len(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
95479644
u32 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
95489645

9549-
return nl80211_set_cqm_rssi(info, threshold, hysteresis);
9646+
if (len % 4)
9647+
return -EINVAL;
9648+
9649+
return nl80211_set_cqm_rssi(info, thresholds, len / 4,
9650+
hysteresis);
95509651
}
95519652

95529653
if (attrs[NL80211_ATTR_CQM_TXE_RATE] &&
@@ -13983,13 +14084,24 @@ void cfg80211_cqm_rssi_notify(struct net_device *dev,
1398314084
s32 rssi_level, gfp_t gfp)
1398414085
{
1398514086
struct sk_buff *msg;
14087+
struct wireless_dev *wdev = dev->ieee80211_ptr;
14088+
struct cfg80211_registered_device *rdev = wiphy_to_rdev(wdev->wiphy);
1398614089

1398714090
trace_cfg80211_cqm_rssi_notify(dev, rssi_event, rssi_level);
1398814091

1398914092
if (WARN_ON(rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW &&
1399014093
rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH))
1399114094
return;
1399214095

14096+
if (wdev->cqm_config) {
14097+
wdev->cqm_config->last_rssi_event_value = rssi_level;
14098+
14099+
cfg80211_cqm_rssi_update(rdev, dev);
14100+
14101+
if (rssi_level == 0)
14102+
rssi_level = wdev->cqm_config->last_rssi_event_value;
14103+
}
14104+
1399314105
msg = cfg80211_prepare_cqm(dev, NULL, gfp);
1399414106
if (!msg)
1399514107
return;

net/wireless/rdev-ops.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -749,6 +749,18 @@ rdev_set_cqm_rssi_config(struct cfg80211_registered_device *rdev,
749749
return ret;
750750
}
751751

752+
static inline int
753+
rdev_set_cqm_rssi_range_config(struct cfg80211_registered_device *rdev,
754+
struct net_device *dev, s32 low, s32 high)
755+
{
756+
int ret;
757+
trace_rdev_set_cqm_rssi_range_config(&rdev->wiphy, dev, low, high);
758+
ret = rdev->ops->set_cqm_rssi_range_config(&rdev->wiphy, dev,
759+
low, high);
760+
trace_rdev_return_int(&rdev->wiphy, ret);
761+
return ret;
762+
}
763+
752764
static inline int
753765
rdev_set_cqm_txe_config(struct cfg80211_registered_device *rdev,
754766
struct net_device *dev, u32 rate, u32 pkts, u32 intvl)

net/wireless/trace.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1322,6 +1322,28 @@ TRACE_EVENT(rdev_set_cqm_rssi_config,
13221322
__entry->rssi_thold, __entry->rssi_hyst)
13231323
);
13241324

1325+
TRACE_EVENT(rdev_set_cqm_rssi_range_config,
1326+
TP_PROTO(struct wiphy *wiphy,
1327+
struct net_device *netdev, s32 low, s32 high),
1328+
TP_ARGS(wiphy, netdev, low, high),
1329+
TP_STRUCT__entry(
1330+
WIPHY_ENTRY
1331+
NETDEV_ENTRY
1332+
__field(s32, rssi_low)
1333+
__field(s32, rssi_high)
1334+
),
1335+
TP_fast_assign(
1336+
WIPHY_ASSIGN;
1337+
NETDEV_ASSIGN;
1338+
__entry->rssi_low = low;
1339+
__entry->rssi_high = high;
1340+
),
1341+
TP_printk(WIPHY_PR_FMT ", " NETDEV_PR_FMT
1342+
", range: %d - %d ",
1343+
WIPHY_PR_ARG, NETDEV_PR_ARG,
1344+
__entry->rssi_low, __entry->rssi_high)
1345+
);
1346+
13251347
TRACE_EVENT(rdev_set_cqm_txe_config,
13261348
TP_PROTO(struct wiphy *wiphy, struct net_device *netdev, u32 rate,
13271349
u32 pkts, u32 intvl),

0 commit comments

Comments
 (0)