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 are currently still under development, and still mostly untested.
This v2 of the patches have seen rework based on Johannes Berg's comments.
Thanks in advance for your comments and suggestions.
Juuso Oikarinen (2):
cfg80211: Add connection quality monitoring support to nl80211
mac80211: Add support for connection quality monitoring
include/linux/nl80211.h | 36 ++++++++++++++
include/net/cfg80211.h | 17 +++++++
include/net/mac80211.h | 26 ++++++++++
net/mac80211/cfg.c | 26 ++++++++++
net/mac80211/mlme.c | 9 ++++
net/wireless/mlme.c | 12 +++++
net/wireless/nl80211.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.h | 5 ++
8 files changed, 254 insertions(+), 0 deletions(-)
On Tue, 2010-03-16 at 22:51 +0100, ext Johannes Berg wrote:
> On Mon, 2010-03-15 at 14:55 +0200, Juuso Oikarinen wrote:
>
> > + * @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 +851,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 +1594,29 @@ 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
> > + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm
>
> You really like abbreviations? I think it wouldn't hurt to make these
> more verbose :)
You know, I used to always write things fully open, which I always got
complaints for. It seems I'm now closing on the opposite ;)
To me CQM sounds like a nice snappy name for the feature ;)
"CONNECTION_QUALITY_MONITOR" is obviously too long, so I quess a
compromise option would be to use "CONN_QUAL_MON" or something.
If there is no hard opposition, I would like to stick with CQM.
Obviously, I'll revisit those docs for v3.
> > + * @NL80211_ATTR_CQM_RSSI_LEVEL: Current level in relation to threshold
> > + * @__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_LEVEL,
> > +
> > + /* keep last */
> > + __NL80211_ATTR_CQM_AFTER_LAST,
> > + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
> > +};
> > +
> > +enum nl80211_cqm_level {
>
> Docs? But then again, see further down.
>
> > diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> > index 3d134a1..5f48472 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_config: Configure cqm RSSI notification threshold.
>
> abbreviations in the method are one thing, but in the docs?
I'll need to revisit all these docs for v3.
> > +/**
> > + * cfg80211_cqm_notify - notification of a connection quality monitoring event
> > + * @dev: network device
> > + * @rssi_level: current level of the RSSI
> > + * @gfp: context flags
> > + *
> > + * This function is called, when a configured connection quality monitoring
> > + * event occurs.
> > + */
> > +void cfg80211_cqm_notify(struct net_device *dev,
> > + enum nl80211_cqm_level rssi_level, gfp_t gfp);
>
> I think you should just pass the RSSI value instead of this odd level.
> If you can't do that, then at least rename cqm_level to
> cqm_rssi_threshold_reached or something?
I'll rename the enum for v3.
> > +static struct nla_policy
> > +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX+1] __read_mostly = {
> > + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U8 },
> > + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
> > + [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_U32 },
> > +};
>
> Using a u8 for RSSI isn't really a great thing to do, I think. Can't we
> just use s32 (aka u32)?
I get these sensations every now and then that I should for some reason
save lots of memory, for no good reason, and then I end up using awkward
data types for some values. I'll change to s32.
Thanks for the review! I'm still trying to adapt to the ways and
conventions of these modules, but I'll get there.
> johannes
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-wireless" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
On Mon, 2010-03-15 at 14:55 +0200, Juuso Oikarinen wrote:
> + * @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 +851,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 +1594,29 @@ 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
> + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm
You really like abbreviations? I think it wouldn't hurt to make these
more verbose :)
> + * @NL80211_ATTR_CQM_RSSI_LEVEL: Current level in relation to threshold
> + * @__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_LEVEL,
> +
> + /* keep last */
> + __NL80211_ATTR_CQM_AFTER_LAST,
> + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
> +};
> +
> +enum nl80211_cqm_level {
Docs? But then again, see further down.
> diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
> index 3d134a1..5f48472 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_config: Configure cqm RSSI notification threshold.
abbreviations in the method are one thing, but in the docs?
> +/**
> + * cfg80211_cqm_notify - notification of a connection quality monitoring event
> + * @dev: network device
> + * @rssi_level: current level of the RSSI
> + * @gfp: context flags
> + *
> + * This function is called, when a configured connection quality monitoring
> + * event occurs.
> + */
> +void cfg80211_cqm_notify(struct net_device *dev,
> + enum nl80211_cqm_level rssi_level, gfp_t gfp);
I think you should just pass the RSSI value instead of this odd level.
If you can't do that, then at least rename cqm_level to
cqm_rssi_threshold_reached or something?
> +static struct nla_policy
> +nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX+1] __read_mostly = {
> + [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U8 },
> + [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
> + [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_U32 },
> +};
Using a u8 for RSSI isn't really a great thing to do, I think. Can't we
just use s32 (aka u32)?
johannes
On Wed, 2010-03-17 at 08:02 +0200, Juuso Oikarinen wrote:
> > I'm curious. How does the HW/firmware do this?
>
> > In my testing on some hw, the beacon RSSI fluctuated wildly, so you'd
> > have to set the hysteresis pretty high.
>
> The HW/firmware can be configured with parameters related to averaging,
> which will smoothen the worst of the fluctuation at least between single
> frames.
Ok, cool. Thanks for taking the time to answer :)
> Still, there is the WLAN tendency of a very steep RSSI curve, so we'll
> probably end up with a relatively high hysteresis. Figuring out good
> values for the config is still to-be-done.
Right. And it will be a tradeoff between roaming decision speed and
battery life.
johannes
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]>
---
include/net/mac80211.h | 26 ++++++++++++++++++++++++++
net/mac80211/cfg.c | 26 ++++++++++++++++++++++++++
net/mac80211/mlme.c | 9 +++++++++
3 files changed, 61 insertions(+), 0 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 936bc41..391c063 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -599,6 +599,7 @@ enum ieee80211_conf_flags {
* @IEEE80211_CONF_CHANGE_RETRY_LIMITS: retry limits changed
* @IEEE80211_CONF_CHANGE_IDLE: Idle flag changed
* @IEEE80211_CONF_CHANGE_SMPS: Spatial multiplexing powersave mode changed
+ * @IEEE80211_CONF_CHANGE_CQM: Connection quality monitor config changed
*/
enum ieee80211_conf_changed {
IEEE80211_CONF_CHANGE_SMPS = BIT(1),
@@ -609,6 +610,7 @@ enum ieee80211_conf_changed {
IEEE80211_CONF_CHANGE_CHANNEL = BIT(6),
IEEE80211_CONF_CHANGE_RETRY_LIMITS = BIT(7),
IEEE80211_CONF_CHANGE_IDLE = BIT(8),
+ IEEE80211_CONF_CHANGE_CQM = BIT(9),
};
/**
@@ -662,6 +664,9 @@ enum ieee80211_smps_mode {
* frame, called "dot11ShortRetryLimit" in 802.11, but actually means the
* number of transmissions not the number of retries
*
+ * @cqm_rssi_thold: Connection quality monitor RSSI threshold
+ * @cqm_rssi_hyst: Connection quality monitor RSSI hysteresis
+ *
* @smps_mode: spatial multiplexing powersave mode; note that
* %IEEE80211_SMPS_STATIC is used when the device is not
* configured for an HT channel
@@ -676,6 +681,9 @@ struct ieee80211_conf {
u8 long_frame_max_tx_count, short_frame_max_tx_count;
+ s8 cqm_rssi_thold;
+ u8 cqm_rssi_hyst;
+
struct ieee80211_channel *channel;
enum nl80211_channel_type channel_type;
enum ieee80211_smps_mode smps_mode;
@@ -954,6 +962,10 @@ enum ieee80211_tkip_key_type {
* Hardware can provide ack status reports of Tx frames to
* the stack.
*
+ * @IEEE80211_HW_SUPPORTS_CQM:
+ * Hardware can do connection quality monitoring - i.e. it can monitor
+ * RSSI levels and notify on changes based on a threshold and hysteresis.
+ *
*/
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 = 1<<19,
};
/**
@@ -2370,6 +2383,19 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
*/
void ieee80211_beacon_loss(struct ieee80211_vif *vif);
+/**
+ * ieee80211_cqm_notify - inform cqm RSSI threshold triggered
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @rssi_level: the current level of the RSSI value
+ *
+ * When the IEEE80211_HW_SUPPORTS_CQM is set, and a RSSI threshold has been
+ * configured, the driver will inform whenever RSSI goes above or below
+ * the threshold with this function.
+ */
+void ieee80211_cqm_notify(struct ieee80211_vif *vif,
+ enum nl80211_cqm_level rssi_level);
+
/* Rate control API */
/**
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index b7116ef..fbd39ee 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1402,6 +1402,31 @@ static int ieee80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
return 0;
}
+static int ieee80211_set_cqm_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s8 rssi_thold, u8 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_conf *conf = &local->hw.conf;
+
+ if (sdata->vif.type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM))
+ return -EOPNOTSUPP;
+
+ if (rssi_thold == conf->cqm_rssi_thold &&
+ rssi_hyst == conf->cqm_rssi_hyst)
+ return 0;
+
+ conf->cqm_rssi_thold = rssi_thold;
+ conf->cqm_rssi_hyst = rssi_hyst;
+
+ ieee80211_hw_config(local, IEEE80211_CONF_CHANGE_CQM);
+ return 0;
+}
+
static int ieee80211_set_bitrate_mask(struct wiphy *wiphy,
struct net_device *dev,
const u8 *addr,
@@ -1506,4 +1531,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_config = ieee80211_set_cqm_config
};
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index be5f723..baff89d 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2135,3 +2135,12 @@ int ieee80211_mgd_action(struct ieee80211_sub_if_data *sdata,
*cookie = (unsigned long) skb;
return 0;
}
+
+void ieee80211_cqm_notify(struct ieee80211_vif *vif,
+ enum nl80211_cqm_level rssi_level)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ cfg80211_cqm_notify(sdata->dev, rssi_level, GFP_KERNEL);
+}
+EXPORT_SYMBOL(ieee80211_cqm_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]>
---
include/linux/nl80211.h | 36 ++++++++++++++
include/net/cfg80211.h | 17 +++++++
net/wireless/mlme.c | 12 +++++
net/wireless/nl80211.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.h | 5 ++
5 files changed, 193 insertions(+), 0 deletions(-)
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 28ba20f..322776c 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -323,6 +323,10 @@
* 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 and
+ * notification. This command is used both as a command (to configure
+ * a trigger level) and as an event (to indicate the configured level was
+ * reached.)
*
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
@@ -419,6 +423,8 @@ enum nl80211_commands {
NL80211_CMD_SET_POWER_SAVE,
NL80211_CMD_GET_POWER_SAVE,
+ NL80211_CMD_SET_CQM,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -691,6 +697,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 +851,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 +1594,29 @@ 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
+ * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm
+ * @NL80211_ATTR_CQM_RSSI_LEVEL: Current level in relation to threshold
+ * @__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_LEVEL,
+
+ /* keep last */
+ __NL80211_ATTR_CQM_AFTER_LAST,
+ NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
+};
+
+enum nl80211_cqm_level {
+ NL80211_CQM_LEVEL_HIGH,
+ NL80211_CQM_LEVEL_LOW,
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 3d134a1..5f48472 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_config: Configure cqm RSSI notification threshold.
*
*/
struct cfg80211_ops {
@@ -1152,6 +1153,9 @@ struct cfg80211_ops {
int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
bool enabled, int timeout);
+
+ int (*set_cqm_config)(struct wiphy *wiphy, struct net_device *dev,
+ s8 rssi_thold, u8 rssi_hyst);
};
/*
@@ -2337,4 +2341,17 @@ 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_notify - notification of a connection quality monitoring event
+ * @dev: network device
+ * @rssi_level: current level of the RSSI
+ * @gfp: context flags
+ *
+ * This function is called, when a configured connection quality monitoring
+ * event occurs.
+ */
+void cfg80211_cqm_notify(struct net_device *dev,
+ enum nl80211_cqm_level rssi_level, gfp_t gfp);
+
#endif /* __NET_CFG80211_H */
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 62bc885..9d7edc8 100644
--- a/net/wireless/mlme.c
+++ b/net/wireless/mlme.c
@@ -894,3 +894,15 @@ 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_notify(struct net_device *dev,
+ enum nl80211_cqm_level rssi_level, 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_notify(rdev, dev, rssi_level, gfp);
+}
+EXPORT_SYMBOL(cfg80211_cqm_notify);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e447db0..d0f4bd4 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,79 @@ 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_U8 },
+ [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U8 },
+ [NL80211_ATTR_CQM_RSSI_LEVEL] = { .type = NLA_U32 },
+};
+
+static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
+{
+ struct cfg80211_registered_device *rdev;
+ struct wireless_dev *wdev;
+ struct net_device *dev;
+ struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
+ struct nlattr *cqm;
+ u8 rssi_thold;
+ u8 rssi_hyst;
+ 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]) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ rssi_thold = nla_get_u8(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ if (rssi_thold > 100) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ rssi_hyst = nla_get_u8(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
+ if (rssi_hyst > 10) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ 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_config) {
+ err = -EOPNOTSUPP;
+ goto out;
+ }
+
+ err = rdev->ops->set_cqm_config(wdev->wiphy, dev,
+ rssi_thold, -rssi_hyst);
+
+unlock_rdev:
+ cfg80211_unlock_rdev(rdev);
+ dev_put(dev);
+ rtnl_unlock();
+
+out:
+ return err;
+}
+
static struct genl_ops nl80211_ops[] = {
{
.cmd = NL80211_CMD_GET_WIPHY,
@@ -5082,6 +5156,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 +5912,49 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
nlmsg_free(msg);
}
+void nl80211_send_cqm_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ enum nl80211_cqm_level rssi_level,
+ 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_SET_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_LEVEL, rssi_level);
+ 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..93a17c5 100644
--- a/net/wireless/nl80211.h
+++ b/net/wireless/nl80211.h
@@ -82,4 +82,9 @@ 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_notify(struct cfg80211_registered_device *rdev,
+ struct net_device *netdev,
+ enum nl80211_cqm_level rssi_level,
+ gfp_t gfp);
+
#endif /* __NET_WIRELESS_NL80211_H */
--
1.6.3.3
> I'm curious. How does the HW/firmware do this?
Libertas firmware supports this, for example. There's a
CMD_802_11_SUBSCRIBE_EVENT where I can ask the firmware to send me an event if
one or more of the following things happen:
SNR_HIGH averaged SNR (db) above threshold
RSSI_HIGH averaged RSSI (dBm) above threshold
LINK_LOSS consecutive no of beacons missed
MAX_FAIL consecutive tx retries above threshold
SNR_LOW averaged SNR below threshold
RSSI_LOW averaged RSSI below threshold
--
http://www.holgerschurig.de
On Mon, 2010-03-15 at 14:55 +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.
>
> 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.
I'm curious. How does the HW/firmware do this?
In my testing on some hw, the beacon RSSI fluctuated wildly, so you'd
have to set the hysteresis pretty high.
johannes
On Tue, 2010-03-16 at 22:52 +0100, ext Johannes Berg wrote:
> On Mon, 2010-03-15 at 14:55 +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.
> >
> > 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.
>
> I'm curious. How does the HW/firmware do this?
> In my testing on some hw, the beacon RSSI fluctuated wildly, so you'd
> have to set the hysteresis pretty high.
The HW/firmware can be configured with parameters related to averaging,
which will smoothen the worst of the fluctuation at least between single
frames.
Still, there is the WLAN tendency of a very steep RSSI curve, so we'll
probably end up with a relatively high hysteresis. Figuring out good
values for the config is still to-be-done.
-Juuso
> johannes
>