To implement good performance WLAN roaming, it is not sufficient to start
scanning for other available AP's only after the currently serving association
is lost.
The entity controlling the roaming will need to get indication of a
deteriorating WLAN connection in order to start preparing for roaming already
before the serving association is lost. This way, it can roam to a better AP
perhaps even before the serving association becomes too bad in quality.
These patches propose an implementation facilitating this using a simple RSSI
threshold and hysteresis approach.
These patches add a nl80211 interface for simply configuring a rssi threshold
and hysteresis value to facilitate very basic connection quality monitoring.
For the triggering, these patches currently rely on HW support, host based
triggering is not implemented, but could be added later if needed.
These patches have been tested with the wl1271 driver.
Juuso Oikarinen (2):
cfg80211: Add connection quality monitoring support to nl80211
mac80211: Add support for connection quality monitoring
include/linux/nl80211.h | 46 ++++++++++++++++
include/net/cfg80211.h | 19 +++++++
include/net/mac80211.h | 29 ++++++++++
net/mac80211/cfg.c | 27 ++++++++++
net/mac80211/mlme.c | 15 +++++
net/wireless/mlme.c | 13 +++++
net/wireless/nl80211.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.h | 6 ++
8 files changed, 286 insertions(+), 0 deletions(-)
On Tue, Mar 23, 2010 at 05:14:53AM -0700, Marcel Holtmann wrote:
> Hi Juuso,
>
> > > > Add support for the set_cqm_config op. This op function configures the
> > > > requested connection quality monitor rssi threshold and rssi hysteresis
> > > > values to the hardware if the hardware supports
> > > > IEEE80211_HW_SUPPORTS_CQM.
> > > >
> > > > For unsupporting hardware, currently -EOPNOTSUPP is returned, so the mac80211
> > > > is currently not doing connection quality monitoring on the host. This could be
> > > > added later, if needed.
> > >
> > > I think you mean "For unsupported hardware, ..." ;)
> >
> > I guess you could phrase it like this as well. Do you think this is a
> > problem, i.e. do you want me to resubmit a fixed patch?
>
> you can leave this up to John actually. He could also fix this on the
> fly when it applies the patch. However if he wants you to re-submit you
> might have to do it ;)
I'll take care of it -- this is what I live to do. :-)
John
--
John W. Linville Someday the world will need a hero, and you
[email protected] might be all we have. Be ready.
On Tue, Mar 23, 2010 at 12:02 AM, Juuso Oikarinen
<[email protected]> wrote:
> + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm
I still don't get what this particular value is for, despite this kdoc entry.
Luis
On Sun, Mar 28, 2010 at 12:43:32PM -0700, Jouni Malinen wrote:
> On Wed, Mar 24, 2010 at 11:46:07PM -0700, Johannes Berg wrote:
> > So this is merged, anyone want to implement it in mac80211 so we can
> > start playing with it? I won't get around to it for quite a while.
>
> How about something like this? Based on a very brief test (walk 10
> meters toward the AP and then back ;-) this seems to be doing more or
> less what I expect the feature should do.
And for those interested in playing more with this, I added support for
using the new signal strength threshold with the bgscan_simple module in
wpa_supplicant. This seemed to provide reasonable behavior in an office
environment with multiple APs and some overlapping coverage area in a
test with ath9k. I used following wpa_supplicant configuration option
for this particular test:
bgscan="simple:30:-50:300"
This uses background scan interval of 30 seconds if signal strength is
below -50 dBm and 300 seconds if above. A new scan is triggered
immediately if the signal strength drops below the threshold (or if it
drops further 4 dB and it has been more than 10 seconds from the
previous scan). Furthermore, I had manually configured the list of known
channels that the APs here are using (scan_freq parameter).
The connection did not die at any point (based on a continuous ping) and
roaming behavior seemed reasonable, i.e., signal strength did not drop
below -65 dBm or so before the new AP was already found. Background
scans are also quite tolerable when the frequency list is limited and
even more so, when remaining in the same location where the signal
strength remains above the threshold. That 300 second interval could
also be made much higher if it is desirable to avoid more or less all
background scans since the immediate scan triggered by signal strength
drop by itself should cover most use cases.
--
Jouni Malinen PGP id EFC895FA
On Tue, 2010-03-23 at 17:23 +0100, ext Luis R. Rodriguez wrote:
> On Tue, Mar 23, 2010 at 12:02 AM, Juuso Oikarinen
> <[email protected]> wrote:
>
> > + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm
>
> I still don't get what this particular value is for, despite this kdoc entry.
>
> Luis
I will submit one more patch to elaborate this doc further.
In the meanwhile, to explain it here, this value defines the minimum
value the RSSI level must change before the RSSI event can be issued
again, to limit the effect of a RSSI value that is oscillating around
the configured threshold.
-Juuso
Add support for the set_cqm_config op. This op function configures the
requested connection quality monitor rssi threshold and rssi hysteresis
values to the hardware if the hardware supports
IEEE80211_HW_SUPPORTS_CQM.
For unsupporting hardware, currently -EOPNOTSUPP is returned, so the mac80211
is currently not doing connection quality monitoring on the host. This could be
added later, if needed.
Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
include/net/mac80211.h | 29 +++++++++++++++++++++++++++++
net/mac80211/cfg.c | 27 +++++++++++++++++++++++++++
net/mac80211/mlme.c | 15 +++++++++++++++
3 files changed, 71 insertions(+), 0 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 936bc41..40e23f3 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -144,6 +144,7 @@ struct ieee80211_low_level_stats {
* new beacon (beaconing modes)
* @BSS_CHANGED_BEACON_ENABLED: Beaconing should be
* enabled/disabled (beaconing modes)
+ * @BSS_CHANGED_CQM: Connection quality monitor config changed
*/
enum ieee80211_bss_change {
BSS_CHANGED_ASSOC = 1<<0,
@@ -156,6 +157,7 @@ enum ieee80211_bss_change {
BSS_CHANGED_BSSID = 1<<7,
BSS_CHANGED_BEACON = 1<<8,
BSS_CHANGED_BEACON_ENABLED = 1<<9,
+ BSS_CHANGED_CQM = 1<<10,
};
/**
@@ -185,6 +187,9 @@ enum ieee80211_bss_change {
* @enable_beacon: whether beaconing should be enabled or not
* @ht_operation_mode: HT operation mode (like in &struct ieee80211_ht_info).
* This field is only valid when the channel type is one of the HT types.
+ * @cqm_rssi_thold: Connection quality monitor RSSI threshold, a zero value
+ * implies disabled
+ * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
*/
struct ieee80211_bss_conf {
const u8 *bssid;
@@ -202,6 +207,8 @@ struct ieee80211_bss_conf {
u64 timestamp;
u32 basic_rates;
u16 ht_operation_mode;
+ s32 cqm_rssi_thold;
+ u32 cqm_rssi_hyst;
};
/**
@@ -954,6 +961,11 @@ enum ieee80211_tkip_key_type {
* Hardware can provide ack status reports of Tx frames to
* the stack.
*
+ * @IEEE80211_HW_SUPPORTS_CQM_RSSI:
+ * Hardware can do connection quality monitoring - i.e. it can monitor
+ * connection quality related parameters, such as the RSSI level and
+ * provide notifications if configured trigger levels are reached.
+ *
*/
enum ieee80211_hw_flags {
IEEE80211_HW_HAS_RATE_CONTROL = 1<<0,
@@ -975,6 +987,7 @@ enum ieee80211_hw_flags {
IEEE80211_HW_SUPPORTS_DYNAMIC_SMPS = 1<<16,
IEEE80211_HW_SUPPORTS_UAPSD = 1<<17,
IEEE80211_HW_REPORTS_TX_ACK_STATUS = 1<<18,
+ IEEE80211_HW_SUPPORTS_CQM_RSSI = 1<<19,
};
/**
@@ -2370,6 +2383,22 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
*/
void ieee80211_beacon_loss(struct ieee80211_vif *vif);
+/**
+ * ieee80211_cqm_rssi_notify - inform a configured connection quality monitoring
+ * rssi threshold triggered
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @rssi_event: the RSSI trigger event type
+ * @gfp: context flags
+ *
+ * When the %IEEE80211_HW_SUPPORTS_CQM_RSSI is set, and a connection quality
+ * monitoring is configured with an rssi threshold, the driver will inform
+ * whenever the rssi level reaches the threshold.
+ */
+void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp);
+
/* Rate control API */
/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b7116ef..c8f5205 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1402,6 +1402,32 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
+static int ieee80211_set_cqm_rssi_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst)
+{
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct ieee80211_vif *vif = &sdata->vif;
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI))
+ return -EOPNOTSUPP;
+
+ if (rssi_thold == bss_conf->cqm_rssi_thold &&
+ rssi_hyst == bss_conf->cqm_rssi_hyst)
+ return 0;
+
+ bss_conf->cqm_rssi_thold = rssi_thold;
+ bss_conf->cqm_rssi_hyst = rssi_hyst;
+
+ /* tell the driver upon association, unless already associated */
+ if (sdata->u.mgd.associated)
+ ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
+
+ return 0;
+}
+
static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr,
@@ -1506,4 +1532,5 @@ struct cfg80211_ops mac80211_config_ops = {
.remain_on_channel = ieee80211_remain_on_channel,
.cancel_remain_on_channel = ieee80211_cancel_remain_on_channel,
.action = ieee80211_action,
+ .set_cqm_rssi_config = ieee80211_set_cqm_rssi_config,
};
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index be5f723..5927077 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -753,6 +753,11 @@ static void ieee80211_set_associated(struct ieee80211_sub_if_data *sdata,
/* And the BSSID changed - we're associated now */
bss_info_changed |= BSS_CHANGED_BSSID;
+ /* Tell the driver to monitor connection quality (if supported) */
+ if ((local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI) &&
+ sdata->vif.bss_conf.cqm_rssi_thold)
+ bss_info_changed |= BSS_CHANGED_CQM;
+
ieee80211_bss_info_change_notify(sdata, bss_info_changed);
mutex_lock(&local->iflist_mtx);
@@ -2135,3 +2140,13 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
*cookie = (unsigned long) skb;
return 0;
}
+
+void ieee80211_cqm_rssi_notify(struct ieee80211_vif *vif,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ cfg80211_cqm_rssi_notify(sdata->dev, rssi_event, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_rssi_notify);
--
1.6.3.3
Add support for basic configuration of a connection quality monitoring to the
nl80211 interface, and basic support for notifying about triggered monitoring
events.
Via this interface a user-space connection manager may configure and receive
pre-warning events of deteriorating WLAN connection quality, and start
preparing for roaming in advance, before the connection is already lost.
An example usage of such a trigger is starting scanning for nearby AP's in
an attempt to find one with better connection quality, and associate to it
before the connection characteristics of the existing connection become too bad
or the association is even lost, leading in a prolonged delay in connectivity.
The interface currently supports only RSSI, but it could be later extended
to include other parameters, such as signal-to-noise ratio, if need for that
arises.
Signed-off-by: Juuso Oikarinen <[email protected]>
Reviewed-by: Johannes Berg <[email protected]>
---
include/linux/nl80211.h | 46 ++++++++++++++++
include/net/cfg80211.h | 19 +++++++
net/wireless/mlme.c | 13 +++++
net/wireless/nl80211.c | 131 +++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.h | 6 ++
5 files changed, 215 insertions(+), 0 deletions(-)
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 28ba20f..8994759 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -323,6 +323,12 @@
* the TX command and %NL80211_ATTR_FRAME includes the contents of the
* frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
* the frame.
+ * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
+ * is used to configure connection quality monitoring notification trigger
+ * levels.
+ * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
+ * command is used as an event to indicate the that a trigger level was
+ * reached.
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
@@ -419,6 +425,9 @@ enum nl80211_commands {
NL80211_CMD_SET_POWER_SAVE,
NL80211_CMD_GET_POWER_SAVE,
+ NL80211_CMD_SET_CQM,
+ NL80211_CMD_NOTIFY_CQM,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -691,6 +700,9 @@ enum nl80211_commands {
* @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
* acknowledged by the recipient.
*
+ * @NL80211_ATTR_CQM: connection quality monitor configuration in a
+ * nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
+ *
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
*/
@@ -842,6 +854,8 @@ enum nl80211_attrs {
NL80211_ATTR_PS_STATE,
+ NL80211_ATTR_CQM,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -1583,4 +1597,36 @@ enum nl80211_ps_state {
NL80211_PS_ENABLED,
};
+/**
+ * enum nl80211_attr_cqm - connection quality monitor attributes
+ * @__NL80211_ATTR_CQM_INVALID: invalid
+ * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm (zero to disable)
+ * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm
+ * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
+ * @__NL80211_ATTR_CQM_AFTER_LAST: internal
+ * @NL80211_ATTR_CQM_MAX: highest key attribute
+ */
+enum nl80211_attr_cqm {
+ __NL80211_ATTR_CQM_INVALID,
+ NL80211_ATTR_CQM_RSSI_THOLD,
+ NL80211_ATTR_CQM_RSSI_HYST,
+ NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+
+ /* keep last */
+ __NL80211_ATTR_CQM_AFTER_LAST,
+ NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the
+ * configured threshold
+ * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the
+ * configured threshold
+ */
+enum nl80211_cqm_rssi_threshold_event {
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 3d134a1..868cfd3 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1007,6 +1007,7 @@ struct cfg80211_pmksa {
* RSN IE. It allows for faster roaming between WPA2 BSSIDs.
* @del_pmksa: Delete a cached PMKID.
* @flush_pmksa: Flush all cached PMKIDs.
+ * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
*
*/
struct cfg80211_ops {
@@ -1152,6 +1153,10 @@ struct cfg80211_ops {
int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout);
+
+ int (*set_cqm_rssi_config)(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 rssi_thold, u32 rssi_hyst);
};
/*
@@ -2337,4 +2342,18 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf,
void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
const u8 *buf, size_t len, bool ack, gfp_t gfp);
+
+/**
+ * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
+ * @dev: network device
+ * @rssi_event: the triggered RSSI event
+ * @gfp: context flags
+ *
+ * This function is called when a configured connection quality monitoring
+ * rssi threshold reached event occurs.
+ */
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp);
+
#endif /* __NET_CFG80211_H */
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 62bc885..0855f0d 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -894,3 +894,16 @@ void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
}
EXPORT_SYMBOL(cfg80211_action_tx_status);
+
+void cfg80211_cqm_rssi_notify(struct net_device *dev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp)
+{
+ struct wireless_dev *wdev = dev->ieee80211_ptr;
+ struct wiphy *wiphy = wdev->wiphy;
+ struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
+
+ /* Indicate roaming trigger event to user space */
+ nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
+}
+EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e447db0..a7fc3d8 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -149,6 +149,7 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
.len = IEEE80211_MAX_DATA_LEN },
[NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
[NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
};
/* policy for the attributes */
@@ -4778,6 +4779,84 @@ unlock_rtnl:
return err;
}
+static struct nla_policy
+nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
+ [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
+ [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
+};
+
+static int nl80211_set_cqm_rssi(struct genl_info *info,
+ s32 threshold, u32 hysteresis)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ struct net_device *dev;
+ int err;
+
+ if (threshold > 0)
+ return -EINVAL;
+
+ rtnl_lock();
+
+ err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
+ if (err)
+ goto unlock_rdev;
+
+ wdev = dev->ieee80211_ptr;
+
+ if (!rdev->ops->set_cqm_rssi_config) {
+ err = -EOPNOTSUPP;
+ goto unlock_rdev;
+ }
+
+ if (wdev->iftype != NL80211_IFTYPE_STATION) {
+ err = -EOPNOTSUPP;
+ goto unlock_rdev;
+ }
+
+ err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
+ threshold, hysteresis);
+
+unlock_rdev:
+ cfg80211_unlock_rdev(rdev);
+ dev_put(dev);
+ rtnl_unlock();
+
+ return err;
+}
+
+static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
+{
+ struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
+ struct nlattr *cqm;
+ int err;
+
+ cqm = info->attrs[NL80211_ATTR_CQM];
+ if (!cqm) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
+ nl80211_attr_cqm_policy);
+ if (err)
+ goto out;
+
+ if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
+ attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
+ s32 threshold;
+ u32 hysteresis;
+ threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
+ err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
+ } else
+ err = -EINVAL;
+
+out:
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -5082,6 +5161,12 @@ static struct genl_ops nl80211_ops[] = {
.policy = nl80211_policy,
/* can be retrieved by unprivileged users */
},
+ {
+ .cmd = NL80211_CMD_SET_CQM,
+ .doit = nl80211_set_cqm,
+ .policy = nl80211_policy,
+ .flags = GENL_ADMIN_PERM,
+ },
};
static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -5832,6 +5917,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+void
+nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp)
+{
+ struct sk_buff *msg;
+ struct nlattr *pinfoattr;
+ void *hdr;
+
+ msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
+ if (!msg)
+ return;
+
+ hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
+ if (!hdr) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
+
+ pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
+ if (!pinfoattr)
+ goto nla_put_failure;
+
+ NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
+ rssi_event);
+
+ nla_nest_end(msg, pinfoattr);
+
+ if (genlmsg_end(msg, hdr) < 0) {
+ nlmsg_free(msg);
+ return;
+ }
+
+ genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
+ nl80211_mlme_mcgrp.id, gfp);
+ return;
+
+ nla_put_failure:
+ genlmsg_cancel(msg, hdr);
+ nlmsg_free(msg);
+}
+
static int nl80211_netlink_notify(struct notifier_block * nb,
unsigned long state,
void *_notify)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 4ca5111..2ad7fbc 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -82,4 +82,10 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
const u8 *buf, size_t len, bool ack,
gfp_t gfp);
+void
+nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ enum nl80211_cqm_rssi_threshold_event rssi_event,
+ gfp_t gfp);
+
#endif /* __NET_WIRELESS_NL80211_H */
--
1.6.3.3
Hi Juuso,
> > > Add support for the set_cqm_config op. This op function configures the
> > > requested connection quality monitor rssi threshold and rssi hysteresis
> > > values to the hardware if the hardware supports
> > > IEEE80211_HW_SUPPORTS_CQM.
> > >
> > > For unsupporting hardware, currently -EOPNOTSUPP is returned, so the mac80211
> > > is currently not doing connection quality monitoring on the host. This could be
> > > added later, if needed.
> >
> > I think you mean "For unsupported hardware, ..." ;)
>
> I guess you could phrase it like this as well. Do you think this is a
> problem, i.e. do you want me to resubmit a fixed patch?
you can leave this up to John actually. He could also fix this on the
fly when it applies the patch. However if he wants you to re-submit you
might have to do it ;)
Regards
Marcel
On Tue, 2010-03-23 at 08:13 +0100, ext Marcel Holtmann wrote:
> Hi Juuso,
>
> > Add support for the set_cqm_config op. This op function configures the
> > requested connection quality monitor rssi threshold and rssi hysteresis
> > values to the hardware if the hardware supports
> > IEEE80211_HW_SUPPORTS_CQM.
> >
> > For unsupporting hardware, currently -EOPNOTSUPP is returned, so the mac80211
> > is currently not doing connection quality monitoring on the host. This could be
> > added later, if needed.
>
> I think you mean "For unsupported hardware, ..." ;)
I guess you could phrase it like this as well. Do you think this is a
problem, i.e. do you want me to resubmit a fixed patch?
-Juuso
> Regards
>
> Marcel
>
>
On Wed, Mar 24, 2010 at 11:46:07PM -0700, Johannes Berg wrote:
> So this is merged, anyone want to implement it in mac80211 so we can
> start playing with it? I won't get around to it for quite a while.
How about something like this? Based on a very brief test (walk 10
meters toward the AP and then back ;-) this seems to be doing more or
less what I expect the feature should do. This was with ath9k, but the
code should work with any driver that indicates Beacon frames to
mac80211 (and uses a suitable unit for the signal strength).
mac80211: Track Beacon signal strength and implement cqm events
Calculate a running average of the signal strength reported for Beacon
frames from the current BSS and indicate cqm events if the average value
moves below or above the configured threshold value (and filter out
repetitive events by using the configured hysteresis).
Signed-off-by: Jouni Malinen <[email protected]>
---
net/mac80211/cfg.c | 9 +++++---
net/mac80211/debugfs_netdev.c | 12 +++++++++++
net/mac80211/ieee80211_i.h | 19 +++++++++++++++++
net/mac80211/mlme.c | 45 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 82 insertions(+), 3 deletions(-)
--- wireless-testing.orig/net/mac80211/debugfs_netdev.c 2010-03-28 11:28:46.000000000 -0700
+++ wireless-testing/net/mac80211/debugfs_netdev.c 2010-03-28 11:42:21.000000000 -0700
@@ -99,6 +99,14 @@ static ssize_t ieee80211_if_fmt_##name(
return scnprintf(buf, buflen, "%pM\n", sdata->field); \
}
+#define IEEE80211_IF_FMT_DEC_DIV_16(name, field) \
+static ssize_t ieee80211_if_fmt_##name( \
+ const struct ieee80211_sub_if_data *sdata, \
+ char *buf, int buflen) \
+{ \
+ return scnprintf(buf, buflen, "%d\n", sdata->field / 16); \
+}
+
#define __IEEE80211_IF_FILE(name, _write) \
static ssize_t ieee80211_if_read_##name(struct file *file, \
char __user *userbuf, \
@@ -139,6 +147,8 @@ IEEE80211_IF_FILE(rc_rateidx_mask_5ghz,
/* STA attributes */
IEEE80211_IF_FILE(bssid, u.mgd.bssid, MAC);
IEEE80211_IF_FILE(aid, u.mgd.aid, DEC);
+IEEE80211_IF_FILE(last_beacon, u.mgd.last_beacon_signal, DEC);
+IEEE80211_IF_FILE(ave_beacon, u.mgd.ave_beacon_signal, DEC_DIV_16);
static int ieee80211_set_smps(struct ieee80211_sub_if_data *sdata,
enum ieee80211_smps_mode smps_mode)
@@ -275,6 +285,8 @@ static void add_sta_files(struct ieee802
DEBUGFS_ADD(bssid);
DEBUGFS_ADD(aid);
+ DEBUGFS_ADD(last_beacon);
+ DEBUGFS_ADD(ave_beacon);
DEBUGFS_ADD_MODE(smps, 0600);
}
--- wireless-testing.orig/net/mac80211/ieee80211_i.h 2010-03-28 11:17:34.000000000 -0700
+++ wireless-testing/net/mac80211/ieee80211_i.h 2010-03-28 12:10:22.000000000 -0700
@@ -317,6 +317,7 @@ enum ieee80211_sta_flags {
IEEE80211_STA_MFP_ENABLED = BIT(6),
IEEE80211_STA_UAPSD_ENABLED = BIT(7),
IEEE80211_STA_NULLFUNC_ACKED = BIT(8),
+ IEEE80211_STA_RESET_SIGNAL_AVE = BIT(9),
};
struct ieee80211_if_managed {
@@ -359,6 +360,24 @@ struct ieee80211_if_managed {
int wmm_last_param_set;
u8 use_4addr;
+
+ /* Signal strength from the last Beacon frame in the current BSS. */
+ int last_beacon_signal;
+
+ /*
+ * Weighted average of the signal strength from Beacon frames in the
+ * current BSS. This is in units of 1/16 of the signal unit to maintain
+ * accuracy and to speed up calculations, i.e., the value need to be
+ * devided by 16 to get the actual value.
+ */
+ int ave_beacon_signal;
+
+ /*
+ * Last Beacon frame signal strength average (ave_beacon_signal / 16)
+ * that triggered a cqm event. 0 indicates that no event has been
+ * generated for the current association.
+ */
+ int last_cqm_event_signal;
};
enum ieee80211_ibss_request {
--- wireless-testing.orig/net/mac80211/mlme.c 2010-03-28 11:14:11.000000000 -0700
+++ wireless-testing/net/mac80211/mlme.c 2010-03-28 12:36:18.000000000 -0700
@@ -46,6 +46,13 @@
*/
#define IEEE80211_PROBE_WAIT (HZ / 2)
+/*
+ * Weight given to the latest Beacon frame when calculating average signal
+ * strength for Beacon frames received in the current BSS. This must be
+ * between 1 and 15.
+ */
+#define IEEE80211_SIGNAL_AVE_WEIGHT 3
+
#define TMR_RUNNING_TIMER 0
#define TMR_RUNNING_CHANSW 1
@@ -728,6 +735,8 @@ static void ieee80211_set_associated(str
sdata->u.mgd.associated = cbss;
memcpy(sdata->u.mgd.bssid, cbss->bssid, ETH_ALEN);
+ sdata->u.mgd.flags |= IEEE80211_STA_RESET_SIGNAL_AVE;
+
/* just to be sure */
sdata->u.mgd.flags &= ~(IEEE80211_STA_CONNECTION_POLL |
IEEE80211_STA_BEACON_POLL);
@@ -1343,6 +1352,7 @@ static void ieee80211_rx_mgmt_beacon(str
struct ieee80211_rx_status *rx_status)
{
struct ieee80211_if_managed *ifmgd = &sdata->u.mgd;
+ struct ieee80211_bss_conf *bss_conf = &sdata->vif.bss_conf;
size_t baselen;
struct ieee802_11_elems elems;
struct ieee80211_local *local = sdata->local;
@@ -1378,6 +1388,41 @@ static void ieee80211_rx_mgmt_beacon(str
if (memcmp(bssid, mgmt->bssid, ETH_ALEN) != 0)
return;
+ /* Track average RSSI from the Beacon frames of the current AP */
+ ifmgd->last_beacon_signal = rx_status->signal;
+ if (ifmgd->flags & IEEE80211_STA_RESET_SIGNAL_AVE) {
+ ifmgd->flags &= ~IEEE80211_STA_RESET_SIGNAL_AVE;
+ ifmgd->ave_beacon_signal = rx_status->signal;
+ ifmgd->last_cqm_event_signal = 0;
+ } else {
+ ifmgd->ave_beacon_signal =
+ (IEEE80211_SIGNAL_AVE_WEIGHT * rx_status->signal * 16 +
+ (16 - IEEE80211_SIGNAL_AVE_WEIGHT) *
+ ifmgd->ave_beacon_signal) / 16;
+ }
+ if (bss_conf->cqm_rssi_thold &&
+ !(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
+ int sig = ifmgd->ave_beacon_signal / 16;
+ int last_event = ifmgd->last_cqm_event_signal;
+ int thold = bss_conf->cqm_rssi_thold;
+ int hyst = bss_conf->cqm_rssi_hyst;
+ if (sig < thold &&
+ (last_event == 0 || sig < last_event - hyst)) {
+ ifmgd->last_cqm_event_signal = sig;
+ ieee80211_cqm_rssi_notify(
+ &sdata->vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
+ GFP_ATOMIC);
+ } else if (sig > thold &&
+ (last_event == 0 || sig > last_event + hyst)) {
+ ifmgd->last_cqm_event_signal = sig;
+ ieee80211_cqm_rssi_notify(
+ &sdata->vif,
+ NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
+ GFP_ATOMIC);
+ }
+ }
+
if (ifmgd->flags & IEEE80211_STA_BEACON_POLL) {
#ifdef CONFIG_MAC80211_VERBOSE_DEBUG
if (net_ratelimit()) {
--- wireless-testing.orig/net/mac80211/cfg.c 2010-03-28 12:05:02.000000000 -0700
+++ wireless-testing/net/mac80211/cfg.c 2010-03-28 12:06:47.000000000 -0700
@@ -1411,9 +1411,6 @@ static int ieee80211_set_cqm_rssi_config
struct ieee80211_vif *vif = &sdata->vif;
struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
- if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI))
- return -EOPNOTSUPP;
-
if (rssi_thold == bss_conf->cqm_rssi_thold &&
rssi_hyst == bss_conf->cqm_rssi_hyst)
return 0;
@@ -1421,6 +1418,12 @@ static int ieee80211_set_cqm_rssi_config
bss_conf->cqm_rssi_thold = rssi_thold;
bss_conf->cqm_rssi_hyst = rssi_hyst;
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM_RSSI)) {
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+ return 0;
+ }
+
/* tell the driver upon association, unless already associated */
if (sdata->u.mgd.associated)
ieee80211_bss_info_change_notify(sdata, BSS_CHANGED_CQM);
--
Jouni Malinen PGP id EFC895FA
Hi Juuso,
> Add support for the set_cqm_config op. This op function configures the
> requested connection quality monitor rssi threshold and rssi hysteresis
> values to the hardware if the hardware supports
> IEEE80211_HW_SUPPORTS_CQM.
>
> For unsupporting hardware, currently -EOPNOTSUPP is returned, so the mac80211
> is currently not doing connection quality monitoring on the host. This could be
> added later, if needed.
I think you mean "For unsupported hardware, ..." ;)
Regards
Marcel
On Sun, 2010-03-28 at 12:43 -0700, Jouni Malinen wrote:
> + /*
> + * Weighted average of the signal strength from Beacon frames in the
> + * current BSS. This is in units of 1/16 of the signal unit to maintain
> + * accuracy and to speed up calculations, i.e., the value need to be
> + * devided by 16 to get the actual value.
> + */
divided ;)
Ok I wouldn't have noticed if evolution didn't have spell checking...
> + if (sig < thold &&
> + (last_event == 0 || sig < last_event - hyst)) {
> + ifmgd->last_cqm_event_signal = sig;
> + ieee80211_cqm_rssi_notify(
> + &sdata->vif,
> + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
> + GFP_ATOMIC);
> + } else if (sig > thold &&
> + (last_event == 0 || sig > last_event + hyst)) {
> + ifmgd->last_cqm_event_signal = sig;
> + ieee80211_cqm_rssi_notify(
> + &sdata->vif,
> + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
> + GFP_ATOMIC);
No need for GFP_ATOMIC, GFP_KERNEL should be fine, I think.
johannes
On Tue, 2010-03-23 at 09:02 +0200, Juuso Oikarinen wrote:
> To implement good performance WLAN roaming, it is not sufficient to start
> scanning for other available AP's only after the currently serving association
> is lost.
>
> The entity controlling the roaming will need to get indication of a
> deteriorating WLAN connection in order to start preparing for roaming already
> before the serving association is lost. This way, it can roam to a better AP
> perhaps even before the serving association becomes too bad in quality.
>
> These patches propose an implementation facilitating this using a simple RSSI
> threshold and hysteresis approach.
So this is merged, anyone want to implement it in mac80211 so we can
start playing with it? I won't get around to it for quite a while.
johannes
On Tue, Mar 23, 2010 at 12:02 AM, Juuso Oikarinen
<[email protected]> wrote:
> Add support for the set_cqm_config op. This op function configures the
> requested connection quality monitor rssi threshold and rssi hysteresis
> values to the hardware if the hardware supports
> IEEE80211_HW_SUPPORTS_CQM.
>
> For unsupporting hardware, currently -EOPNOTSUPP is returned, so the mac80211
> is currently not doing connection quality monitoring on the host. This could be
> added later, if needed.
>
> Signed-off-by: Juuso Oikarinen <[email protected]>
> Reviewed-by: Johannes Berg <[email protected]>
Thanks for the work Juuso, looks good. I'm so pleased at how changes
like these can be made so easily *and* are extremely easy to read
thanks to the good effort put into cfg80211/nl80211/mac80211. Night
and day compared to the old wext days.
Luis
On Tue, Mar 23, 2010 at 11:14 PM, Juuso Oikarinen
<[email protected]> wrote:
> On Tue, 2010-03-23 at 17:23 +0100, ext Luis R. Rodriguez wrote:
>> On Tue, Mar 23, 2010 at 12:02 AM, Juuso Oikarinen
>> <[email protected]> wrote:
>>
>> > + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm
>>
>> I still don't get what this particular value is for, despite this kdoc entry.
>>
>> Luis
>
> I will submit one more patch to elaborate this doc further.
>
> In the meanwhile, to explain it here, this value defines the minimum
> value the RSSI level must change before the RSSI event can be issued
> again, to limit the effect of a RSSI value that is oscillating around
> the configured threshold.
Thanks Jusso, that makes it crystal clear.
Luis