diff options
Diffstat (limited to 'net/wireless/util.c')
-rw-r--r-- | net/wireless/util.c | 126 |
1 files changed, 118 insertions, 8 deletions
diff --git a/net/wireless/util.c b/net/wireless/util.c index 6a750bc6bcfe..f0536d44d43c 100644 --- a/net/wireless/util.c +++ b/net/wireless/util.c | |||
@@ -544,7 +544,8 @@ EXPORT_SYMBOL(ieee80211_data_from_8023); | |||
544 | 544 | ||
545 | void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, | 545 | void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, |
546 | const u8 *addr, enum nl80211_iftype iftype, | 546 | const u8 *addr, enum nl80211_iftype iftype, |
547 | const unsigned int extra_headroom) | 547 | const unsigned int extra_headroom, |
548 | bool has_80211_header) | ||
548 | { | 549 | { |
549 | struct sk_buff *frame = NULL; | 550 | struct sk_buff *frame = NULL; |
550 | u16 ethertype; | 551 | u16 ethertype; |
@@ -553,14 +554,18 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list, | |||
553 | int remaining, err; | 554 | int remaining, err; |
554 | u8 dst[ETH_ALEN], src[ETH_ALEN]; | 555 | u8 dst[ETH_ALEN], src[ETH_ALEN]; |
555 | 556 | ||
556 | err = ieee80211_data_to_8023(skb, addr, iftype); | 557 | if (has_80211_header) { |
557 | if (err) | 558 | err = ieee80211_data_to_8023(skb, addr, iftype); |
558 | goto out; | 559 | if (err) |
560 | goto out; | ||
559 | 561 | ||
560 | /* skip the wrapping header */ | 562 | /* skip the wrapping header */ |
561 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); | 563 | eth = (struct ethhdr *) skb_pull(skb, sizeof(struct ethhdr)); |
562 | if (!eth) | 564 | if (!eth) |
563 | goto out; | 565 | goto out; |
566 | } else { | ||
567 | eth = (struct ethhdr *) skb->data; | ||
568 | } | ||
564 | 569 | ||
565 | while (skb != frame) { | 570 | while (skb != frame) { |
566 | u8 padding; | 571 | u8 padding; |
@@ -803,6 +808,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev, | |||
803 | return -EBUSY; | 808 | return -EBUSY; |
804 | 809 | ||
805 | if (ntype != otype) { | 810 | if (ntype != otype) { |
811 | err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr, | ||
812 | ntype); | ||
813 | if (err) | ||
814 | return err; | ||
815 | |||
806 | dev->ieee80211_ptr->use_4addr = false; | 816 | dev->ieee80211_ptr->use_4addr = false; |
807 | dev->ieee80211_ptr->mesh_id_up_len = 0; | 817 | dev->ieee80211_ptr->mesh_id_up_len = 0; |
808 | 818 | ||
@@ -896,3 +906,103 @@ u16 cfg80211_calculate_bitrate(struct rate_info *rate) | |||
896 | /* do NOT round down here */ | 906 | /* do NOT round down here */ |
897 | return (bitrate + 50000) / 100000; | 907 | return (bitrate + 50000) / 100000; |
898 | } | 908 | } |
909 | |||
910 | int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev, | ||
911 | u32 beacon_int) | ||
912 | { | ||
913 | struct wireless_dev *wdev; | ||
914 | int res = 0; | ||
915 | |||
916 | if (!beacon_int) | ||
917 | return -EINVAL; | ||
918 | |||
919 | mutex_lock(&rdev->devlist_mtx); | ||
920 | |||
921 | list_for_each_entry(wdev, &rdev->netdev_list, list) { | ||
922 | if (!wdev->beacon_interval) | ||
923 | continue; | ||
924 | if (wdev->beacon_interval != beacon_int) { | ||
925 | res = -EINVAL; | ||
926 | break; | ||
927 | } | ||
928 | } | ||
929 | |||
930 | mutex_unlock(&rdev->devlist_mtx); | ||
931 | |||
932 | return res; | ||
933 | } | ||
934 | |||
935 | int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev, | ||
936 | struct wireless_dev *wdev, | ||
937 | enum nl80211_iftype iftype) | ||
938 | { | ||
939 | struct wireless_dev *wdev_iter; | ||
940 | int num[NUM_NL80211_IFTYPES]; | ||
941 | int total = 1; | ||
942 | int i, j; | ||
943 | |||
944 | ASSERT_RTNL(); | ||
945 | |||
946 | /* Always allow software iftypes */ | ||
947 | if (rdev->wiphy.software_iftypes & BIT(iftype)) | ||
948 | return 0; | ||
949 | |||
950 | /* | ||
951 | * Drivers will gradually all set this flag, until all | ||
952 | * have it we only enforce for those that set it. | ||
953 | */ | ||
954 | if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS)) | ||
955 | return 0; | ||
956 | |||
957 | memset(num, 0, sizeof(num)); | ||
958 | |||
959 | num[iftype] = 1; | ||
960 | |||
961 | mutex_lock(&rdev->devlist_mtx); | ||
962 | list_for_each_entry(wdev_iter, &rdev->netdev_list, list) { | ||
963 | if (wdev_iter == wdev) | ||
964 | continue; | ||
965 | if (!netif_running(wdev_iter->netdev)) | ||
966 | continue; | ||
967 | |||
968 | if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype)) | ||
969 | continue; | ||
970 | |||
971 | num[wdev_iter->iftype]++; | ||
972 | total++; | ||
973 | } | ||
974 | mutex_unlock(&rdev->devlist_mtx); | ||
975 | |||
976 | for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) { | ||
977 | const struct ieee80211_iface_combination *c; | ||
978 | struct ieee80211_iface_limit *limits; | ||
979 | |||
980 | c = &rdev->wiphy.iface_combinations[i]; | ||
981 | |||
982 | limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits, | ||
983 | GFP_KERNEL); | ||
984 | if (!limits) | ||
985 | return -ENOMEM; | ||
986 | if (total > c->max_interfaces) | ||
987 | goto cont; | ||
988 | |||
989 | for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) { | ||
990 | if (rdev->wiphy.software_iftypes & BIT(iftype)) | ||
991 | continue; | ||
992 | for (j = 0; j < c->n_limits; j++) { | ||
993 | if (!(limits[j].types & iftype)) | ||
994 | continue; | ||
995 | if (limits[j].max < num[iftype]) | ||
996 | goto cont; | ||
997 | limits[j].max -= num[iftype]; | ||
998 | } | ||
999 | } | ||
1000 | /* yay, it fits */ | ||
1001 | kfree(limits); | ||
1002 | return 0; | ||
1003 | cont: | ||
1004 | kfree(limits); | ||
1005 | } | ||
1006 | |||
1007 | return -EBUSY; | ||
1008 | } | ||