aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJuuso Oikarinen <juuso.oikarinen@nokia.com>2010-03-23 03:02:33 -0400
committerJohn W. Linville <linville@tuxdriver.com>2010-03-24 16:02:37 -0400
commitd6dc1a386358979e12366d1f35eeb68fc181e101 (patch)
tree4b65ba21328d6984d008b62795dd60d99c350632
parent921ca03c8f8e982f27fc406bc301caf2196b99f7 (diff)
cfg80211: Add connection quality monitoring support to nl80211
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 <juuso.oikarinen@nokia.com> Reviewed-by: Johannes Berg <johannes@sipsolutions.net> Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r--include/linux/nl80211.h46
-rw-r--r--include/net/cfg80211.h19
-rw-r--r--net/wireless/mlme.c13
-rw-r--r--net/wireless/nl80211.c131
-rw-r--r--net/wireless/nl80211.h6
5 files changed, 215 insertions, 0 deletions
diff --git a/include/linux/nl80211.h b/include/linux/nl80211.h
index 28ba20fda3e2..89947597b9ce 100644
--- a/include/linux/nl80211.h
+++ b/include/linux/nl80211.h
@@ -323,6 +323,12 @@
323 * the TX command and %NL80211_ATTR_FRAME includes the contents of the 323 * the TX command and %NL80211_ATTR_FRAME includes the contents of the
324 * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged 324 * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged
325 * the frame. 325 * the frame.
326 * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command
327 * is used to configure connection quality monitoring notification trigger
328 * levels.
329 * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This
330 * command is used as an event to indicate the that a trigger level was
331 * reached.
326 * 332 *
327 * @NL80211_CMD_MAX: highest used command number 333 * @NL80211_CMD_MAX: highest used command number
328 * @__NL80211_CMD_AFTER_LAST: internal use 334 * @__NL80211_CMD_AFTER_LAST: internal use
@@ -419,6 +425,9 @@ enum nl80211_commands {
419 NL80211_CMD_SET_POWER_SAVE, 425 NL80211_CMD_SET_POWER_SAVE,
420 NL80211_CMD_GET_POWER_SAVE, 426 NL80211_CMD_GET_POWER_SAVE,
421 427
428 NL80211_CMD_SET_CQM,
429 NL80211_CMD_NOTIFY_CQM,
430
422 /* add new commands above here */ 431 /* add new commands above here */
423 432
424 /* used to define NL80211_CMD_MAX below */ 433 /* used to define NL80211_CMD_MAX below */
@@ -691,6 +700,9 @@ enum nl80211_commands {
691 * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was 700 * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was
692 * acknowledged by the recipient. 701 * acknowledged by the recipient.
693 * 702 *
703 * @NL80211_ATTR_CQM: connection quality monitor configuration in a
704 * nested attribute with %NL80211_ATTR_CQM_* sub-attributes.
705 *
694 * @NL80211_ATTR_MAX: highest attribute number currently defined 706 * @NL80211_ATTR_MAX: highest attribute number currently defined
695 * @__NL80211_ATTR_AFTER_LAST: internal use 707 * @__NL80211_ATTR_AFTER_LAST: internal use
696 */ 708 */
@@ -842,6 +854,8 @@ enum nl80211_attrs {
842 854
843 NL80211_ATTR_PS_STATE, 855 NL80211_ATTR_PS_STATE,
844 856
857 NL80211_ATTR_CQM,
858
845 /* add attributes here, update the policy in nl80211.c */ 859 /* add attributes here, update the policy in nl80211.c */
846 860
847 __NL80211_ATTR_AFTER_LAST, 861 __NL80211_ATTR_AFTER_LAST,
@@ -1583,4 +1597,36 @@ enum nl80211_ps_state {
1583 NL80211_PS_ENABLED, 1597 NL80211_PS_ENABLED,
1584}; 1598};
1585 1599
1600/**
1601 * enum nl80211_attr_cqm - connection quality monitor attributes
1602 * @__NL80211_ATTR_CQM_INVALID: invalid
1603 * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm (zero to disable)
1604 * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm
1605 * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event
1606 * @__NL80211_ATTR_CQM_AFTER_LAST: internal
1607 * @NL80211_ATTR_CQM_MAX: highest key attribute
1608 */
1609enum nl80211_attr_cqm {
1610 __NL80211_ATTR_CQM_INVALID,
1611 NL80211_ATTR_CQM_RSSI_THOLD,
1612 NL80211_ATTR_CQM_RSSI_HYST,
1613 NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
1614
1615 /* keep last */
1616 __NL80211_ATTR_CQM_AFTER_LAST,
1617 NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1
1618};
1619
1620/**
1621 * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event
1622 * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the
1623 * configured threshold
1624 * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the
1625 * configured threshold
1626 */
1627enum nl80211_cqm_rssi_threshold_event {
1628 NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
1629 NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
1630};
1631
1586#endif /* __LINUX_NL80211_H */ 1632#endif /* __LINUX_NL80211_H */
diff --git a/include/net/cfg80211.h b/include/net/cfg80211.h
index 3d134a1fb96b..868cfd3b9724 100644
--- a/include/net/cfg80211.h
+++ b/include/net/cfg80211.h
@@ -1007,6 +1007,7 @@ struct cfg80211_pmksa {
1007 * RSN IE. It allows for faster roaming between WPA2 BSSIDs. 1007 * RSN IE. It allows for faster roaming between WPA2 BSSIDs.
1008 * @del_pmksa: Delete a cached PMKID. 1008 * @del_pmksa: Delete a cached PMKID.
1009 * @flush_pmksa: Flush all cached PMKIDs. 1009 * @flush_pmksa: Flush all cached PMKIDs.
1010 * @set_cqm_rssi_config: Configure connection quality monitor RSSI threshold.
1010 * 1011 *
1011 */ 1012 */
1012struct cfg80211_ops { 1013struct cfg80211_ops {
@@ -1152,6 +1153,10 @@ struct cfg80211_ops {
1152 1153
1153 int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev, 1154 int (*set_power_mgmt)(struct wiphy *wiphy, struct net_device *dev,
1154 bool enabled, int timeout); 1155 bool enabled, int timeout);
1156
1157 int (*set_cqm_rssi_config)(struct wiphy *wiphy,
1158 struct net_device *dev,
1159 s32 rssi_thold, u32 rssi_hyst);
1155}; 1160};
1156 1161
1157/* 1162/*
@@ -2337,4 +2342,18 @@ bool cfg80211_rx_action(struct net_device *dev, int freq, const u8 *buf,
2337void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, 2342void cfg80211_action_tx_status(struct net_device *dev, u64 cookie,
2338 const u8 *buf, size_t len, bool ack, gfp_t gfp); 2343 const u8 *buf, size_t len, bool ack, gfp_t gfp);
2339 2344
2345
2346/**
2347 * cfg80211_cqm_rssi_notify - connection quality monitoring rssi event
2348 * @dev: network device
2349 * @rssi_event: the triggered RSSI event
2350 * @gfp: context flags
2351 *
2352 * This function is called when a configured connection quality monitoring
2353 * rssi threshold reached event occurs.
2354 */
2355void cfg80211_cqm_rssi_notify(struct net_device *dev,
2356 enum nl80211_cqm_rssi_threshold_event rssi_event,
2357 gfp_t gfp);
2358
2340#endif /* __NET_CFG80211_H */ 2359#endif /* __NET_CFG80211_H */
diff --git a/net/wireless/mlme.c b/net/wireless/mlme.c
index 62bc8855e123..0855f0d32349 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,
894 nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp); 894 nl80211_send_action_tx_status(rdev, dev, cookie, buf, len, ack, gfp);
895} 895}
896EXPORT_SYMBOL(cfg80211_action_tx_status); 896EXPORT_SYMBOL(cfg80211_action_tx_status);
897
898void cfg80211_cqm_rssi_notify(struct net_device *dev,
899 enum nl80211_cqm_rssi_threshold_event rssi_event,
900 gfp_t gfp)
901{
902 struct wireless_dev *wdev = dev->ieee80211_ptr;
903 struct wiphy *wiphy = wdev->wiphy;
904 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
905
906 /* Indicate roaming trigger event to user space */
907 nl80211_send_cqm_rssi_notify(rdev, dev, rssi_event, gfp);
908}
909EXPORT_SYMBOL(cfg80211_cqm_rssi_notify);
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index e447db04cf76..a7fc3d83f5f6 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] = {
149 .len = IEEE80211_MAX_DATA_LEN }, 149 .len = IEEE80211_MAX_DATA_LEN },
150 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, 150 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
151 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, 151 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
152 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
152}; 153};
153 154
154/* policy for the attributes */ 155/* policy for the attributes */
@@ -4778,6 +4779,84 @@ unlock_rtnl:
4778 return err; 4779 return err;
4779} 4780}
4780 4781
4782static struct nla_policy
4783nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
4784 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
4785 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
4786 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
4787};
4788
4789static int nl80211_set_cqm_rssi(struct genl_info *info,
4790 s32 threshold, u32 hysteresis)
4791{
4792 struct cfg80211_registered_device *rdev;
4793 struct wireless_dev *wdev;
4794 struct net_device *dev;
4795 int err;
4796
4797 if (threshold > 0)
4798 return -EINVAL;
4799
4800 rtnl_lock();
4801
4802 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
4803 if (err)
4804 goto unlock_rdev;
4805
4806 wdev = dev->ieee80211_ptr;
4807
4808 if (!rdev->ops->set_cqm_rssi_config) {
4809 err = -EOPNOTSUPP;
4810 goto unlock_rdev;
4811 }
4812
4813 if (wdev->iftype != NL80211_IFTYPE_STATION) {
4814 err = -EOPNOTSUPP;
4815 goto unlock_rdev;
4816 }
4817
4818 err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
4819 threshold, hysteresis);
4820
4821unlock_rdev:
4822 cfg80211_unlock_rdev(rdev);
4823 dev_put(dev);
4824 rtnl_unlock();
4825
4826 return err;
4827}
4828
4829static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
4830{
4831 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
4832 struct nlattr *cqm;
4833 int err;
4834
4835 cqm = info->attrs[NL80211_ATTR_CQM];
4836 if (!cqm) {
4837 err = -EINVAL;
4838 goto out;
4839 }
4840
4841 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
4842 nl80211_attr_cqm_policy);
4843 if (err)
4844 goto out;
4845
4846 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
4847 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
4848 s32 threshold;
4849 u32 hysteresis;
4850 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
4851 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
4852 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
4853 } else
4854 err = -EINVAL;
4855
4856out:
4857 return err;
4858}
4859
4781static struct genl_ops nl80211_ops[] = { 4860static struct genl_ops nl80211_ops[] = {
4782 { 4861 {
4783 .cmd = NL80211_CMD_GET_WIPHY, 4862 .cmd = NL80211_CMD_GET_WIPHY,
@@ -5082,6 +5161,12 @@ static struct genl_ops nl80211_ops[] = {
5082 .policy = nl80211_policy, 5161 .policy = nl80211_policy,
5083 /* can be retrieved by unprivileged users */ 5162 /* can be retrieved by unprivileged users */
5084 }, 5163 },
5164 {
5165 .cmd = NL80211_CMD_SET_CQM,
5166 .doit = nl80211_set_cqm,
5167 .policy = nl80211_policy,
5168 .flags = GENL_ADMIN_PERM,
5169 },
5085}; 5170};
5086 5171
5087static struct genl_multicast_group nl80211_mlme_mcgrp = { 5172static struct genl_multicast_group nl80211_mlme_mcgrp = {
@@ -5832,6 +5917,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev,
5832 nlmsg_free(msg); 5917 nlmsg_free(msg);
5833} 5918}
5834 5919
5920void
5921nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
5922 struct net_device *netdev,
5923 enum nl80211_cqm_rssi_threshold_event rssi_event,
5924 gfp_t gfp)
5925{
5926 struct sk_buff *msg;
5927 struct nlattr *pinfoattr;
5928 void *hdr;
5929
5930 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
5931 if (!msg)
5932 return;
5933
5934 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
5935 if (!hdr) {
5936 nlmsg_free(msg);
5937 return;
5938 }
5939
5940 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5941 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
5942
5943 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
5944 if (!pinfoattr)
5945 goto nla_put_failure;
5946
5947 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
5948 rssi_event);
5949
5950 nla_nest_end(msg, pinfoattr);
5951
5952 if (genlmsg_end(msg, hdr) < 0) {
5953 nlmsg_free(msg);
5954 return;
5955 }
5956
5957 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5958 nl80211_mlme_mcgrp.id, gfp);
5959 return;
5960
5961 nla_put_failure:
5962 genlmsg_cancel(msg, hdr);
5963 nlmsg_free(msg);
5964}
5965
5835static int nl80211_netlink_notify(struct notifier_block * nb, 5966static int nl80211_netlink_notify(struct notifier_block * nb,
5836 unsigned long state, 5967 unsigned long state,
5837 void *_notify) 5968 void *_notify)
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h
index 4ca511102c6c..2ad7fbc7d9f1 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,
82 const u8 *buf, size_t len, bool ack, 82 const u8 *buf, size_t len, bool ack,
83 gfp_t gfp); 83 gfp_t gfp);
84 84
85void
86nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
87 struct net_device *netdev,
88 enum nl80211_cqm_rssi_threshold_event rssi_event,
89 gfp_t gfp);
90
85#endif /* __NET_WIRELESS_NL80211_H */ 91#endif /* __NET_WIRELESS_NL80211_H */