v5 of these patches attempt to underline the extendability of the interface
via naming of data types and slight changes in the docs.
Note, this concept is implemented in the N900 already, and proven there to work.
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.
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 | 45 +++++++++++++++++
include/net/cfg80211.h | 18 +++++++
include/net/mac80211.h | 30 ++++++++++++
net/mac80211/cfg.c | 27 ++++++++++
net/mac80211/mlme.c | 10 ++++
net/wireless/mlme.c | 13 +++++
net/wireless/nl80211.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.h | 5 ++
8 files changed, 270 insertions(+), 0 deletions(-)
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 | 45 +++++++++++++++++
include/net/cfg80211.h | 18 +++++++
net/wireless/mlme.c | 13 +++++
net/wireless/nl80211.c | 122 +++++++++++++++++++++++++++++++++++++++++++++++
net/wireless/nl80211.h | 5 ++
5 files changed, 203 insertions(+), 0 deletions(-)
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 28ba20f..b436e4e 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.) Setting zero threshold disables the feature.
*
* @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,38 @@ 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_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_NONE - No RSSI threshold event occurred
+ * @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_NONE,
+ 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..6763630 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 connection quality monitor parameters.
*
*/
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,
+ s32 rssi_thold, u8 rssi_hyst);
};
/*
@@ -2337,4 +2341,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_notify - notification of a connection quality monitoring event
+ * @dev: network device
+ * @rssi_event: the triggered RSSI event, if there is one
+ * @gfp: context flags
+ *
+ * This function is called, when a configured connection quality monitoring
+ * event occurs. Currently only RSSI events are supported.
+ */
+void cfg80211_cqm_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..0792242 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_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_notify(rdev, dev, rssi_event, gfp);
+}
+EXPORT_SYMBOL(cfg80211_cqm_notify);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e447db0..fc817cf 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,75 @@ 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_U8 },
+ [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .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;
+ s32 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_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
+ if (rssi_thold > 0) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ rssi_hyst = nla_get_u8(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
+
+ 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 +5152,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 +5908,52 @@ 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_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_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;
+
+ if (rssi_event != NL80211_CQM_RSSI_THRESHOLD_EVENT_NONE)
+ 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..729d6a6 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_rssi_threshold_event rssi_event,
+ gfp_t gfp);
+
#endif /* __NET_WIRELESS_NL80211_H */
--
1.6.3.3
On Sat, 2010-03-20 at 17:50 +0100, ext Johannes Berg wrote:
> On Fri, 2010-03-19 at 10:03 +0200, Juuso Oikarinen wrote:
>
> > + * @IEEE80211_HW_SUPPORTS_CQM:
> > + * 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.
>
> It doesn't really matter at this point, before we have any other CQM
> data point implementations, but I would expect this to be CQM_RSSI,
> CQM_BEACON_LOSS etc. in different flags eventually since not all might
> be supported and one might want to allow some w/o the others?
I agree. I will change this.
> > +static int ieee80211_set_cqm_config(struct wiphy *wiphy,
> > + struct net_device *dev,
> > + s32 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_vif *vif = &sdata->vif;
> > + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
> > +
> > + if (vif->type != NL80211_IFTYPE_STATION)
> > + return -EOPNOTSUPP;
>
> I think this check should probably be in cfg80211, it really doesn't
> make sense in any other mode.
Agreed.
> I'm sorry for making you go through iterations over and over again!
Really, no problem. It's also in my/our benefit the best possible code
out. Thanks for your time!
-Juuso
> johannes
>
On Fri, 2010-03-19 at 10:03 +0200, Juuso Oikarinen wrote:
> + * @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.) Setting zero threshold disables the feature.
I've been thinking about this for a while, and I think I finally decided
that I'd prefer another command number for the event. SET doesn't seem
to match the event very well at all.
> +/**
> + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
> + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_NONE - No RSSI threshold event occurred
What is none intended for? If it's for any of the other possible future
enhancements, I'd think in that case we just leave out the attribute?
> +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_U8 },
Now I'm confused. Not that I think we'll ever need a hysteresis value
larger than that, but isn't that a little inconsistent?
johannes
On Fri, 2010-03-19 at 10:03 +0200, Juuso Oikarinen wrote:
> + * @IEEE80211_HW_SUPPORTS_CQM:
> + * 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.
It doesn't really matter at this point, before we have any other CQM
data point implementations, but I would expect this to be CQM_RSSI,
CQM_BEACON_LOSS etc. in different flags eventually since not all might
be supported and one might want to allow some w/o the others?
> +static int ieee80211_set_cqm_config(struct wiphy *wiphy,
> + struct net_device *dev,
> + s32 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_vif *vif = &sdata->vif;
> + struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
> +
> + if (vif->type != NL80211_IFTYPE_STATION)
> + return -EOPNOTSUPP;
I think this check should probably be in cfg80211, it really doesn't
make sense in any other mode.
I'm sorry for making you go through iterations over and over again!
johannes
On Sat, 2010-03-20 at 17:48 +0100, ext Johannes Berg wrote:
> On Fri, 2010-03-19 at 10:03 +0200, Juuso Oikarinen wrote:
>
> > + * @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.) Setting zero threshold disables the feature.
>
> I've been thinking about this for a while, and I think I finally decided
> that I'd prefer another command number for the event. SET doesn't seem
> to match the event very well at all.
I'll change this. The reason I have it this way now is just that I
noticed that sharing the ID's is a common way to do it in existing code.
> > +/**
> > + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
> > + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_NONE - No RSSI threshold event occurred
>
> What is none intended for? If it's for any of the other possible future
> enhancements, I'd think in that case we just leave out the attribute?
Yes, thinking back, this was a bad idea. I'll take it out ;)
> > +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_U8 },
>
> Now I'm confused. Not that I think we'll ever need a hysteresis value
> larger than that, but isn't that a little inconsistent?
As per your preference, I'll change this. I personally don't seem to get
over my old habit of always feeling the urge to save some bits ;)
Thanks for your comments.
-Juuso
> 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 | 30 ++++++++++++++++++++++++++++++
net/mac80211/cfg.c | 27 +++++++++++++++++++++++++++
net/mac80211/mlme.c | 10 ++++++++++
3 files changed, 67 insertions(+), 0 deletions(-)
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 936bc41..5af4365 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;
+ u8 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:
+ * 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 = 1<<19,
};
/**
@@ -2370,6 +2383,23 @@ void ieee80211_sta_block_awake(struct ieee80211_hw *hw,
*/
void ieee80211_beacon_loss(struct ieee80211_vif *vif);
+/**
+ * ieee80211_cqm_notify - inform a configured connection quality monitoring
+ * threshold triggered
+ *
+ * @vif: &struct ieee80211_vif pointer from the add_interface callback.
+ * @rssi_event: the RSSI trigger event type, if any
+ * @gfp: context flags
+ *
+ * When the IEEE80211_HW_SUPPORTS_CQM is set, and a connection quality
+ * monitoring is configured for specific quality related parameters,
+ * the driver will inform whenever any monitored parameter reaches its
+ * threshold. Currently only RSSI events are supported.
+ */
+void ieee80211_cqm_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..95f422c 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_config(struct wiphy *wiphy,
+ struct net_device *dev,
+ s32 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_vif *vif = &sdata->vif;
+ struct ieee80211_bss_conf *bss_conf = &vif->bss_conf;
+
+ if (vif->type != NL80211_IFTYPE_STATION)
+ return -EOPNOTSUPP;
+
+ if (!(local->hw.flags & IEEE80211_HW_SUPPORTS_CQM))
+ 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;
+
+ 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_config = ieee80211_set_cqm_config,
};
diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c
index be5f723..68ba5d9 100644
--- a/net/mac80211/mlme.c
+++ b/net/mac80211/mlme.c
@@ -2135,3 +2135,13 @@ 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_rssi_threshold_event rssi_event,
+ gfp_t gfp)
+{
+ struct ieee80211_sub_if_data *sdata = vif_to_sdata(vif);
+
+ cfg80211_cqm_notify(sdata->dev, rssi_event, gfp);
+}
+EXPORT_SYMBOL(ieee80211_cqm_notify);
--
1.6.3.3