diff options
-rw-r--r-- | include/linux/nl80211.h | 46 | ||||
-rw-r--r-- | include/net/cfg80211.h | 19 | ||||
-rw-r--r-- | net/wireless/mlme.c | 13 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 131 | ||||
-rw-r--r-- | 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 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 | */ | ||
1609 | enum 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 | */ | ||
1627 | enum 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 | */ |
1012 | struct cfg80211_ops { | 1013 | struct 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, | |||
2337 | void cfg80211_action_tx_status(struct net_device *dev, u64 cookie, | 2342 | void 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 | */ | ||
2355 | void 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 | } |
896 | EXPORT_SYMBOL(cfg80211_action_tx_status); | 896 | EXPORT_SYMBOL(cfg80211_action_tx_status); |
897 | |||
898 | void 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 | } | ||
909 | EXPORT_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 | ||
4782 | static struct nla_policy | ||
4783 | nl80211_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 | |||
4789 | static 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 | |||
4821 | unlock_rdev: | ||
4822 | cfg80211_unlock_rdev(rdev); | ||
4823 | dev_put(dev); | ||
4824 | rtnl_unlock(); | ||
4825 | |||
4826 | return err; | ||
4827 | } | ||
4828 | |||
4829 | static 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 | |||
4856 | out: | ||
4857 | return err; | ||
4858 | } | ||
4859 | |||
4781 | static struct genl_ops nl80211_ops[] = { | 4860 | static 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 | ||
5087 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5172 | static 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 | ||
5920 | void | ||
5921 | nl80211_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 | |||
5835 | static int nl80211_netlink_notify(struct notifier_block * nb, | 5966 | static 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 | ||
85 | void | ||
86 | nl80211_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 */ |