diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 334 |
1 files changed, 289 insertions, 45 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 030cf153bea2..db71150b8040 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -150,6 +150,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
150 | .len = IEEE80211_MAX_DATA_LEN }, | 150 | .len = IEEE80211_MAX_DATA_LEN }, |
151 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, | 151 | [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, }, |
152 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, | 152 | [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 }, |
153 | [NL80211_ATTR_CQM] = { .type = NLA_NESTED, }, | ||
154 | [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG }, | ||
155 | [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 }, | ||
153 | }; | 156 | }; |
154 | 157 | ||
155 | /* policy for the attributes */ | 158 | /* policy for the attributes */ |
@@ -586,6 +589,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
586 | i++; | 589 | i++; |
587 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 590 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
588 | } | 591 | } |
592 | CMD(set_channel, SET_CHANNEL); | ||
589 | 593 | ||
590 | #undef CMD | 594 | #undef CMD |
591 | 595 | ||
@@ -686,10 +690,90 @@ static int parse_txq_params(struct nlattr *tb[], | |||
686 | return 0; | 690 | return 0; |
687 | } | 691 | } |
688 | 692 | ||
693 | static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev) | ||
694 | { | ||
695 | /* | ||
696 | * You can only set the channel explicitly for AP, mesh | ||
697 | * and WDS type interfaces; all others have their channel | ||
698 | * managed via their respective "establish a connection" | ||
699 | * command (connect, join, ...) | ||
700 | * | ||
701 | * Monitors are special as they are normally slaved to | ||
702 | * whatever else is going on, so they behave as though | ||
703 | * you tried setting the wiphy channel itself. | ||
704 | */ | ||
705 | return !wdev || | ||
706 | wdev->iftype == NL80211_IFTYPE_AP || | ||
707 | wdev->iftype == NL80211_IFTYPE_WDS || | ||
708 | wdev->iftype == NL80211_IFTYPE_MESH_POINT || | ||
709 | wdev->iftype == NL80211_IFTYPE_MONITOR; | ||
710 | } | ||
711 | |||
712 | static int __nl80211_set_channel(struct cfg80211_registered_device *rdev, | ||
713 | struct wireless_dev *wdev, | ||
714 | struct genl_info *info) | ||
715 | { | ||
716 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
717 | u32 freq; | ||
718 | int result; | ||
719 | |||
720 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ]) | ||
721 | return -EINVAL; | ||
722 | |||
723 | if (!nl80211_can_set_dev_channel(wdev)) | ||
724 | return -EOPNOTSUPP; | ||
725 | |||
726 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
727 | channel_type = nla_get_u32(info->attrs[ | ||
728 | NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
729 | if (channel_type != NL80211_CHAN_NO_HT && | ||
730 | channel_type != NL80211_CHAN_HT20 && | ||
731 | channel_type != NL80211_CHAN_HT40PLUS && | ||
732 | channel_type != NL80211_CHAN_HT40MINUS) | ||
733 | return -EINVAL; | ||
734 | } | ||
735 | |||
736 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
737 | |||
738 | mutex_lock(&rdev->devlist_mtx); | ||
739 | if (wdev) { | ||
740 | wdev_lock(wdev); | ||
741 | result = cfg80211_set_freq(rdev, wdev, freq, channel_type); | ||
742 | wdev_unlock(wdev); | ||
743 | } else { | ||
744 | result = cfg80211_set_freq(rdev, NULL, freq, channel_type); | ||
745 | } | ||
746 | mutex_unlock(&rdev->devlist_mtx); | ||
747 | |||
748 | return result; | ||
749 | } | ||
750 | |||
751 | static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info) | ||
752 | { | ||
753 | struct cfg80211_registered_device *rdev; | ||
754 | struct net_device *netdev; | ||
755 | int result; | ||
756 | |||
757 | rtnl_lock(); | ||
758 | |||
759 | result = get_rdev_dev_by_info_ifindex(info, &rdev, &netdev); | ||
760 | if (result) | ||
761 | goto unlock; | ||
762 | |||
763 | result = __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info); | ||
764 | |||
765 | unlock: | ||
766 | rtnl_unlock(); | ||
767 | |||
768 | return result; | ||
769 | } | ||
770 | |||
689 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | 771 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) |
690 | { | 772 | { |
691 | struct cfg80211_registered_device *rdev; | 773 | struct cfg80211_registered_device *rdev; |
692 | int result = 0, rem_txq_params = 0; | 774 | struct net_device *netdev = NULL; |
775 | struct wireless_dev *wdev; | ||
776 | int result, rem_txq_params = 0; | ||
693 | struct nlattr *nl_txq_params; | 777 | struct nlattr *nl_txq_params; |
694 | u32 changed; | 778 | u32 changed; |
695 | u8 retry_short = 0, retry_long = 0; | 779 | u8 retry_short = 0, retry_long = 0; |
@@ -698,16 +782,50 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
698 | 782 | ||
699 | rtnl_lock(); | 783 | rtnl_lock(); |
700 | 784 | ||
785 | /* | ||
786 | * Try to find the wiphy and netdev. Normally this | ||
787 | * function shouldn't need the netdev, but this is | ||
788 | * done for backward compatibility -- previously | ||
789 | * setting the channel was done per wiphy, but now | ||
790 | * it is per netdev. Previous userland like hostapd | ||
791 | * also passed a netdev to set_wiphy, so that it is | ||
792 | * possible to let that go to the right netdev! | ||
793 | */ | ||
701 | mutex_lock(&cfg80211_mutex); | 794 | mutex_lock(&cfg80211_mutex); |
702 | 795 | ||
703 | rdev = __cfg80211_rdev_from_info(info); | 796 | if (info->attrs[NL80211_ATTR_IFINDEX]) { |
704 | if (IS_ERR(rdev)) { | 797 | int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]); |
705 | mutex_unlock(&cfg80211_mutex); | 798 | |
706 | result = PTR_ERR(rdev); | 799 | netdev = dev_get_by_index(genl_info_net(info), ifindex); |
707 | goto unlock; | 800 | if (netdev && netdev->ieee80211_ptr) { |
801 | rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy); | ||
802 | mutex_lock(&rdev->mtx); | ||
803 | } else | ||
804 | netdev = NULL; | ||
708 | } | 805 | } |
709 | 806 | ||
710 | mutex_lock(&rdev->mtx); | 807 | if (!netdev) { |
808 | rdev = __cfg80211_rdev_from_info(info); | ||
809 | if (IS_ERR(rdev)) { | ||
810 | mutex_unlock(&cfg80211_mutex); | ||
811 | result = PTR_ERR(rdev); | ||
812 | goto unlock; | ||
813 | } | ||
814 | wdev = NULL; | ||
815 | netdev = NULL; | ||
816 | result = 0; | ||
817 | |||
818 | mutex_lock(&rdev->mtx); | ||
819 | } else if (netif_running(netdev) && | ||
820 | nl80211_can_set_dev_channel(netdev->ieee80211_ptr)) | ||
821 | wdev = netdev->ieee80211_ptr; | ||
822 | else | ||
823 | wdev = NULL; | ||
824 | |||
825 | /* | ||
826 | * end workaround code, by now the rdev is available | ||
827 | * and locked, and wdev may or may not be NULL. | ||
828 | */ | ||
711 | 829 | ||
712 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) | 830 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) |
713 | result = cfg80211_dev_rename( | 831 | result = cfg80211_dev_rename( |
@@ -746,26 +864,7 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
746 | } | 864 | } |
747 | 865 | ||
748 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | 866 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { |
749 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | 867 | result = __nl80211_set_channel(rdev, wdev, info); |
750 | u32 freq; | ||
751 | |||
752 | result = -EINVAL; | ||
753 | |||
754 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
755 | channel_type = nla_get_u32(info->attrs[ | ||
756 | NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
757 | if (channel_type != NL80211_CHAN_NO_HT && | ||
758 | channel_type != NL80211_CHAN_HT20 && | ||
759 | channel_type != NL80211_CHAN_HT40PLUS && | ||
760 | channel_type != NL80211_CHAN_HT40MINUS) | ||
761 | goto bad_res; | ||
762 | } | ||
763 | |||
764 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
765 | |||
766 | mutex_lock(&rdev->devlist_mtx); | ||
767 | result = rdev_set_freq(rdev, NULL, freq, channel_type); | ||
768 | mutex_unlock(&rdev->devlist_mtx); | ||
769 | if (result) | 868 | if (result) |
770 | goto bad_res; | 869 | goto bad_res; |
771 | } | 870 | } |
@@ -862,6 +961,8 @@ static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
862 | 961 | ||
863 | bad_res: | 962 | bad_res: |
864 | mutex_unlock(&rdev->mtx); | 963 | mutex_unlock(&rdev->mtx); |
964 | if (netdev) | ||
965 | dev_put(netdev); | ||
865 | unlock: | 966 | unlock: |
866 | rtnl_unlock(); | 967 | rtnl_unlock(); |
867 | return result; | 968 | return result; |
@@ -2096,7 +2197,8 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
2096 | goto out_rtnl; | 2197 | goto out_rtnl; |
2097 | 2198 | ||
2098 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && | 2199 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP && |
2099 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN) { | 2200 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN && |
2201 | dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) { | ||
2100 | err = -EINVAL; | 2202 | err = -EINVAL; |
2101 | goto out; | 2203 | goto out; |
2102 | } | 2204 | } |
@@ -2439,6 +2541,7 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
2439 | params.use_cts_prot = -1; | 2541 | params.use_cts_prot = -1; |
2440 | params.use_short_preamble = -1; | 2542 | params.use_short_preamble = -1; |
2441 | params.use_short_slot_time = -1; | 2543 | params.use_short_slot_time = -1; |
2544 | params.ap_isolate = -1; | ||
2442 | 2545 | ||
2443 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) | 2546 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) |
2444 | params.use_cts_prot = | 2547 | params.use_cts_prot = |
@@ -2455,6 +2558,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
2455 | params.basic_rates_len = | 2558 | params.basic_rates_len = |
2456 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | 2559 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); |
2457 | } | 2560 | } |
2561 | if (info->attrs[NL80211_ATTR_AP_ISOLATE]) | ||
2562 | params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]); | ||
2458 | 2563 | ||
2459 | rtnl_lock(); | 2564 | rtnl_lock(); |
2460 | 2565 | ||
@@ -3392,6 +3497,7 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | |||
3392 | int err, ssid_len, ie_len = 0; | 3497 | int err, ssid_len, ie_len = 0; |
3393 | enum nl80211_auth_type auth_type; | 3498 | enum nl80211_auth_type auth_type; |
3394 | struct key_parse key; | 3499 | struct key_parse key; |
3500 | bool local_state_change; | ||
3395 | 3501 | ||
3396 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | 3502 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) |
3397 | return -EINVAL; | 3503 | return -EINVAL; |
@@ -3470,9 +3576,12 @@ static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | |||
3470 | goto out; | 3576 | goto out; |
3471 | } | 3577 | } |
3472 | 3578 | ||
3579 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; | ||
3580 | |||
3473 | err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, | 3581 | err = cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid, |
3474 | ssid, ssid_len, ie, ie_len, | 3582 | ssid, ssid_len, ie, ie_len, |
3475 | key.p.key, key.p.key_len, key.idx); | 3583 | key.p.key, key.p.key_len, key.idx, |
3584 | local_state_change); | ||
3476 | 3585 | ||
3477 | out: | 3586 | out: |
3478 | cfg80211_unlock_rdev(rdev); | 3587 | cfg80211_unlock_rdev(rdev); |
@@ -3551,9 +3660,8 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3551 | { | 3660 | { |
3552 | struct cfg80211_registered_device *rdev; | 3661 | struct cfg80211_registered_device *rdev; |
3553 | struct net_device *dev; | 3662 | struct net_device *dev; |
3554 | struct wireless_dev *wdev; | ||
3555 | struct cfg80211_crypto_settings crypto; | 3663 | struct cfg80211_crypto_settings crypto; |
3556 | struct ieee80211_channel *chan, *fixedchan; | 3664 | struct ieee80211_channel *chan; |
3557 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; | 3665 | const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL; |
3558 | int err, ssid_len, ie_len = 0; | 3666 | int err, ssid_len, ie_len = 0; |
3559 | bool use_mfp = false; | 3667 | bool use_mfp = false; |
@@ -3596,16 +3704,6 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3596 | goto out; | 3704 | goto out; |
3597 | } | 3705 | } |
3598 | 3706 | ||
3599 | mutex_lock(&rdev->devlist_mtx); | ||
3600 | wdev = dev->ieee80211_ptr; | ||
3601 | fixedchan = rdev_fixed_channel(rdev, wdev); | ||
3602 | if (fixedchan && chan != fixedchan) { | ||
3603 | err = -EBUSY; | ||
3604 | mutex_unlock(&rdev->devlist_mtx); | ||
3605 | goto out; | ||
3606 | } | ||
3607 | mutex_unlock(&rdev->devlist_mtx); | ||
3608 | |||
3609 | ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); | 3707 | ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); |
3610 | ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); | 3708 | ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); |
3611 | 3709 | ||
@@ -3649,6 +3747,7 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) | |||
3649 | const u8 *ie = NULL, *bssid; | 3747 | const u8 *ie = NULL, *bssid; |
3650 | int err, ie_len = 0; | 3748 | int err, ie_len = 0; |
3651 | u16 reason_code; | 3749 | u16 reason_code; |
3750 | bool local_state_change; | ||
3652 | 3751 | ||
3653 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | 3752 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) |
3654 | return -EINVAL; | 3753 | return -EINVAL; |
@@ -3694,7 +3793,10 @@ static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info) | |||
3694 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | 3793 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); |
3695 | } | 3794 | } |
3696 | 3795 | ||
3697 | err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code); | 3796 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; |
3797 | |||
3798 | err = cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code, | ||
3799 | local_state_change); | ||
3698 | 3800 | ||
3699 | out: | 3801 | out: |
3700 | cfg80211_unlock_rdev(rdev); | 3802 | cfg80211_unlock_rdev(rdev); |
@@ -3711,6 +3813,7 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) | |||
3711 | const u8 *ie = NULL, *bssid; | 3813 | const u8 *ie = NULL, *bssid; |
3712 | int err, ie_len = 0; | 3814 | int err, ie_len = 0; |
3713 | u16 reason_code; | 3815 | u16 reason_code; |
3816 | bool local_state_change; | ||
3714 | 3817 | ||
3715 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | 3818 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) |
3716 | return -EINVAL; | 3819 | return -EINVAL; |
@@ -3756,7 +3859,10 @@ static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info) | |||
3756 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | 3859 | ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); |
3757 | } | 3860 | } |
3758 | 3861 | ||
3759 | err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code); | 3862 | local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE]; |
3863 | |||
3864 | err = cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code, | ||
3865 | local_state_change); | ||
3760 | 3866 | ||
3761 | out: | 3867 | out: |
3762 | cfg80211_unlock_rdev(rdev); | 3868 | cfg80211_unlock_rdev(rdev); |
@@ -4337,9 +4443,10 @@ static int nl80211_remain_on_channel(struct sk_buff *skb, | |||
4337 | if (channel_type != NL80211_CHAN_NO_HT && | 4443 | if (channel_type != NL80211_CHAN_NO_HT && |
4338 | channel_type != NL80211_CHAN_HT20 && | 4444 | channel_type != NL80211_CHAN_HT20 && |
4339 | channel_type != NL80211_CHAN_HT40PLUS && | 4445 | channel_type != NL80211_CHAN_HT40PLUS && |
4340 | channel_type != NL80211_CHAN_HT40MINUS) | 4446 | channel_type != NL80211_CHAN_HT40MINUS) { |
4341 | err = -EINVAL; | 4447 | err = -EINVAL; |
4342 | goto out; | 4448 | goto out; |
4449 | } | ||
4343 | } | 4450 | } |
4344 | 4451 | ||
4345 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | 4452 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); |
@@ -4611,9 +4718,10 @@ static int nl80211_action(struct sk_buff *skb, struct genl_info *info) | |||
4611 | if (channel_type != NL80211_CHAN_NO_HT && | 4718 | if (channel_type != NL80211_CHAN_NO_HT && |
4612 | channel_type != NL80211_CHAN_HT20 && | 4719 | channel_type != NL80211_CHAN_HT20 && |
4613 | channel_type != NL80211_CHAN_HT40PLUS && | 4720 | channel_type != NL80211_CHAN_HT40PLUS && |
4614 | channel_type != NL80211_CHAN_HT40MINUS) | 4721 | channel_type != NL80211_CHAN_HT40MINUS) { |
4615 | err = -EINVAL; | 4722 | err = -EINVAL; |
4616 | goto out; | 4723 | goto out; |
4724 | } | ||
4617 | } | 4725 | } |
4618 | 4726 | ||
4619 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | 4727 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); |
@@ -4779,6 +4887,84 @@ unlock_rtnl: | |||
4779 | return err; | 4887 | return err; |
4780 | } | 4888 | } |
4781 | 4889 | ||
4890 | static struct nla_policy | ||
4891 | nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = { | ||
4892 | [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 }, | ||
4893 | [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 }, | ||
4894 | [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 }, | ||
4895 | }; | ||
4896 | |||
4897 | static int nl80211_set_cqm_rssi(struct genl_info *info, | ||
4898 | s32 threshold, u32 hysteresis) | ||
4899 | { | ||
4900 | struct cfg80211_registered_device *rdev; | ||
4901 | struct wireless_dev *wdev; | ||
4902 | struct net_device *dev; | ||
4903 | int err; | ||
4904 | |||
4905 | if (threshold > 0) | ||
4906 | return -EINVAL; | ||
4907 | |||
4908 | rtnl_lock(); | ||
4909 | |||
4910 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4911 | if (err) | ||
4912 | goto unlock_rdev; | ||
4913 | |||
4914 | wdev = dev->ieee80211_ptr; | ||
4915 | |||
4916 | if (!rdev->ops->set_cqm_rssi_config) { | ||
4917 | err = -EOPNOTSUPP; | ||
4918 | goto unlock_rdev; | ||
4919 | } | ||
4920 | |||
4921 | if (wdev->iftype != NL80211_IFTYPE_STATION) { | ||
4922 | err = -EOPNOTSUPP; | ||
4923 | goto unlock_rdev; | ||
4924 | } | ||
4925 | |||
4926 | err = rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev, | ||
4927 | threshold, hysteresis); | ||
4928 | |||
4929 | unlock_rdev: | ||
4930 | cfg80211_unlock_rdev(rdev); | ||
4931 | dev_put(dev); | ||
4932 | rtnl_unlock(); | ||
4933 | |||
4934 | return err; | ||
4935 | } | ||
4936 | |||
4937 | static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info) | ||
4938 | { | ||
4939 | struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1]; | ||
4940 | struct nlattr *cqm; | ||
4941 | int err; | ||
4942 | |||
4943 | cqm = info->attrs[NL80211_ATTR_CQM]; | ||
4944 | if (!cqm) { | ||
4945 | err = -EINVAL; | ||
4946 | goto out; | ||
4947 | } | ||
4948 | |||
4949 | err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm, | ||
4950 | nl80211_attr_cqm_policy); | ||
4951 | if (err) | ||
4952 | goto out; | ||
4953 | |||
4954 | if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] && | ||
4955 | attrs[NL80211_ATTR_CQM_RSSI_HYST]) { | ||
4956 | s32 threshold; | ||
4957 | u32 hysteresis; | ||
4958 | threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]); | ||
4959 | hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]); | ||
4960 | err = nl80211_set_cqm_rssi(info, threshold, hysteresis); | ||
4961 | } else | ||
4962 | err = -EINVAL; | ||
4963 | |||
4964 | out: | ||
4965 | return err; | ||
4966 | } | ||
4967 | |||
4782 | static struct genl_ops nl80211_ops[] = { | 4968 | static struct genl_ops nl80211_ops[] = { |
4783 | { | 4969 | { |
4784 | .cmd = NL80211_CMD_GET_WIPHY, | 4970 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -5083,6 +5269,18 @@ static struct genl_ops nl80211_ops[] = { | |||
5083 | .policy = nl80211_policy, | 5269 | .policy = nl80211_policy, |
5084 | /* can be retrieved by unprivileged users */ | 5270 | /* can be retrieved by unprivileged users */ |
5085 | }, | 5271 | }, |
5272 | { | ||
5273 | .cmd = NL80211_CMD_SET_CQM, | ||
5274 | .doit = nl80211_set_cqm, | ||
5275 | .policy = nl80211_policy, | ||
5276 | .flags = GENL_ADMIN_PERM, | ||
5277 | }, | ||
5278 | { | ||
5279 | .cmd = NL80211_CMD_SET_CHANNEL, | ||
5280 | .doit = nl80211_set_channel, | ||
5281 | .policy = nl80211_policy, | ||
5282 | .flags = GENL_ADMIN_PERM, | ||
5283 | }, | ||
5086 | }; | 5284 | }; |
5087 | 5285 | ||
5088 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 5286 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
@@ -5833,6 +6031,52 @@ void nl80211_send_action_tx_status(struct cfg80211_registered_device *rdev, | |||
5833 | nlmsg_free(msg); | 6031 | nlmsg_free(msg); |
5834 | } | 6032 | } |
5835 | 6033 | ||
6034 | void | ||
6035 | nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev, | ||
6036 | struct net_device *netdev, | ||
6037 | enum nl80211_cqm_rssi_threshold_event rssi_event, | ||
6038 | gfp_t gfp) | ||
6039 | { | ||
6040 | struct sk_buff *msg; | ||
6041 | struct nlattr *pinfoattr; | ||
6042 | void *hdr; | ||
6043 | |||
6044 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
6045 | if (!msg) | ||
6046 | return; | ||
6047 | |||
6048 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM); | ||
6049 | if (!hdr) { | ||
6050 | nlmsg_free(msg); | ||
6051 | return; | ||
6052 | } | ||
6053 | |||
6054 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
6055 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
6056 | |||
6057 | pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM); | ||
6058 | if (!pinfoattr) | ||
6059 | goto nla_put_failure; | ||
6060 | |||
6061 | NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, | ||
6062 | rssi_event); | ||
6063 | |||
6064 | nla_nest_end(msg, pinfoattr); | ||
6065 | |||
6066 | if (genlmsg_end(msg, hdr) < 0) { | ||
6067 | nlmsg_free(msg); | ||
6068 | return; | ||
6069 | } | ||
6070 | |||
6071 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
6072 | nl80211_mlme_mcgrp.id, gfp); | ||
6073 | return; | ||
6074 | |||
6075 | nla_put_failure: | ||
6076 | genlmsg_cancel(msg, hdr); | ||
6077 | nlmsg_free(msg); | ||
6078 | } | ||
6079 | |||
5836 | static int nl80211_netlink_notify(struct notifier_block * nb, | 6080 | static int nl80211_netlink_notify(struct notifier_block * nb, |
5837 | unsigned long state, | 6081 | unsigned long state, |
5838 | void *_notify) | 6082 | void *_notify) |