cfg80211: add 802.11ad (60gHz band) support
[deliverable/linux.git] / net / wireless / util.c
index 957f2562161753fcec3b8789734bc0a36afebb84..0228c64e73d84a37a545d401d1bf5bb8e8b1f79c 100644 (file)
@@ -35,19 +35,29 @@ int ieee80211_channel_to_frequency(int chan, enum ieee80211_band band)
 {
        /* see 802.11 17.3.8.3.2 and Annex J
         * there are overlapping channel numbers in 5GHz and 2GHz bands */
-       if (band == IEEE80211_BAND_5GHZ) {
-               if (chan >= 182 && chan <= 196)
-                       return 4000 + chan * 5;
-               else
-                       return 5000 + chan * 5;
-       } else { /* IEEE80211_BAND_2GHZ */
+       if (chan <= 0)
+               return 0; /* not supported */
+       switch (band) {
+       case IEEE80211_BAND_2GHZ:
                if (chan == 14)
                        return 2484;
                else if (chan < 14)
                        return 2407 + chan * 5;
+               break;
+       case IEEE80211_BAND_5GHZ:
+               if (chan >= 182 && chan <= 196)
+                       return 4000 + chan * 5;
                else
-                       return 0; /* not supported */
+                       return 5000 + chan * 5;
+               break;
+       case IEEE80211_BAND_60GHZ:
+               if (chan < 5)
+                       return 56160 + chan * 2160;
+               break;
+       default:
+               ;
        }
+       return 0; /* not supported */
 }
 EXPORT_SYMBOL(ieee80211_channel_to_frequency);
 
@@ -60,8 +70,12 @@ int ieee80211_frequency_to_channel(int freq)
                return (freq - 2407) / 5;
        else if (freq >= 4910 && freq <= 4980)
                return (freq - 4000) / 5;
-       else
+       else if (freq <= 45000) /* DMG band lower limit */
                return (freq - 5000) / 5;
+       else if (freq >= 58320 && freq <= 64800)
+               return (freq - 56160) / 2160;
+       else
+               return 0;
 }
 EXPORT_SYMBOL(ieee80211_frequency_to_channel);
 
@@ -137,6 +151,11 @@ static void set_mandatory_flags_band(struct ieee80211_supported_band *sband,
                }
                WARN_ON(want != 0 && want != 3 && want != 6);
                break;
+       case IEEE80211_BAND_60GHZ:
+               /* check for mandatory HT MCS 1..4 */
+               WARN_ON(!sband->ht_cap.ht_supported);
+               WARN_ON((sband->ht_cap.mcs.rx_mask[0] & 0x1e) != 0x1e);
+               break;
        case IEEE80211_NUM_BANDS:
                WARN_ON(1);
                break;
@@ -370,7 +389,7 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
                     iftype != NL80211_IFTYPE_P2P_CLIENT &&
                     iftype != NL80211_IFTYPE_MESH_POINT) ||
                    (is_multicast_ether_addr(dst) &&
-                    !compare_ether_addr(src, addr)))
+                    ether_addr_equal(src, addr)))
                        return -1;
                if (iftype == NL80211_IFTYPE_MESH_POINT) {
                        struct ieee80211s_hdr *meshdr =
@@ -398,9 +417,9 @@ int ieee80211_data_to_8023(struct sk_buff *skb, const u8 *addr,
        payload = skb->data + hdrlen;
        ethertype = (payload[6] << 8) | payload[7];
 
-       if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
+       if (likely((ether_addr_equal(payload, rfc1042_header) &&
                    ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
-                  compare_ether_addr(payload, bridge_tunnel_header) == 0)) {
+                  ether_addr_equal(payload, bridge_tunnel_header))) {
                /* remove RFC1042 or Bridge-Tunnel encapsulation and
                 * replace EtherType */
                skb_pull(skb, hdrlen + 6);
@@ -609,10 +628,9 @@ void ieee80211_amsdu_to_8023s(struct sk_buff *skb, struct sk_buff_head *list,
                payload = frame->data;
                ethertype = (payload[6] << 8) | payload[7];
 
-               if (likely((compare_ether_addr(payload, rfc1042_header) == 0 &&
+               if (likely((ether_addr_equal(payload, rfc1042_header) &&
                            ethertype != ETH_P_AARP && ethertype != ETH_P_IPX) ||
-                          compare_ether_addr(payload,
-                                             bridge_tunnel_header) == 0)) {
+                          ether_addr_equal(payload, bridge_tunnel_header))) {
                        /* remove RFC1042 or Bridge-Tunnel
                         * encapsulation and replace EtherType */
                        skb_pull(frame, 6);
@@ -805,9 +823,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
             ntype == NL80211_IFTYPE_P2P_CLIENT))
                return -EBUSY;
 
-       if (ntype != otype) {
+       if (ntype != otype && netif_running(dev)) {
+               mutex_lock(&rdev->devlist_mtx);
                err = cfg80211_can_change_interface(rdev, dev->ieee80211_ptr,
                                                    ntype);
+               mutex_unlock(&rdev->devlist_mtx);
                if (err)
                        return err;
 
@@ -815,6 +835,9 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                dev->ieee80211_ptr->mesh_id_up_len = 0;
 
                switch (otype) {
+               case NL80211_IFTYPE_AP:
+                       cfg80211_stop_ap(rdev, dev);
+                       break;
                case NL80211_IFTYPE_ADHOC:
                        cfg80211_leave_ibss(rdev, dev, false);
                        break;
@@ -869,6 +892,11 @@ int cfg80211_change_iface(struct cfg80211_registered_device *rdev,
                }
        }
 
+       if (!err && ntype != otype && netif_running(dev)) {
+               cfg80211_update_iface_num(rdev, ntype, 1);
+               cfg80211_update_iface_num(rdev, otype, -1);
+       }
+
        return err;
 }
 
@@ -880,7 +908,7 @@ u16 cfg80211_calculate_bitrate(struct rate_info *rate)
                return rate->legacy;
 
        /* the formula below does only work for MCS values smaller than 32 */
-       if (rate->mcs >= 32)
+       if (WARN_ON_ONCE(rate->mcs >= 32))
                return 0;
 
        modulation = rate->mcs & 7;
@@ -931,33 +959,48 @@ int cfg80211_validate_beacon_int(struct cfg80211_registered_device *rdev,
        return res;
 }
 
-int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
-                                 struct wireless_dev *wdev,
-                                 enum nl80211_iftype iftype)
+int cfg80211_can_use_iftype_chan(struct cfg80211_registered_device *rdev,
+                                struct wireless_dev *wdev,
+                                enum nl80211_iftype iftype,
+                                struct ieee80211_channel *chan,
+                                enum cfg80211_chan_mode chanmode)
 {
        struct wireless_dev *wdev_iter;
+       u32 used_iftypes = BIT(iftype);
        int num[NUM_NL80211_IFTYPES];
+       struct ieee80211_channel
+                       *used_channels[CFG80211_MAX_NUM_DIFFERENT_CHANNELS];
+       struct ieee80211_channel *ch;
+       enum cfg80211_chan_mode chmode;
+       int num_different_channels = 0;
        int total = 1;
        int i, j;
 
        ASSERT_RTNL();
+       lockdep_assert_held(&rdev->devlist_mtx);
 
        /* Always allow software iftypes */
        if (rdev->wiphy.software_iftypes & BIT(iftype))
                return 0;
 
-       /*
-        * Drivers will gradually all set this flag, until all
-        * have it we only enforce for those that set it.
-        */
-       if (!(rdev->wiphy.flags & WIPHY_FLAG_ENFORCE_COMBINATIONS))
-               return 0;
-
        memset(num, 0, sizeof(num));
+       memset(used_channels, 0, sizeof(used_channels));
 
        num[iftype] = 1;
 
-       mutex_lock(&rdev->devlist_mtx);
+       switch (chanmode) {
+       case CHAN_MODE_UNDEFINED:
+               break;
+       case CHAN_MODE_SHARED:
+               WARN_ON(!chan);
+               used_channels[0] = chan;
+               num_different_channels++;
+               break;
+       case CHAN_MODE_EXCLUSIVE:
+               num_different_channels++;
+               break;
+       }
+
        list_for_each_entry(wdev_iter, &rdev->netdev_list, list) {
                if (wdev_iter == wdev)
                        continue;
@@ -967,28 +1010,59 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                if (rdev->wiphy.software_iftypes & BIT(wdev_iter->iftype))
                        continue;
 
+               cfg80211_get_chan_state(rdev, wdev_iter, &ch, &chmode);
+
+               switch (chmode) {
+               case CHAN_MODE_UNDEFINED:
+                       break;
+               case CHAN_MODE_SHARED:
+                       for (i = 0; i < CFG80211_MAX_NUM_DIFFERENT_CHANNELS; i++)
+                               if (!used_channels[i] || used_channels[i] == ch)
+                                       break;
+
+                       if (i == CFG80211_MAX_NUM_DIFFERENT_CHANNELS)
+                               return -EBUSY;
+
+                       if (used_channels[i] == NULL) {
+                               used_channels[i] = ch;
+                               num_different_channels++;
+                       }
+                       break;
+               case CHAN_MODE_EXCLUSIVE:
+                       num_different_channels++;
+                       break;
+               }
+
                num[wdev_iter->iftype]++;
                total++;
+               used_iftypes |= BIT(wdev_iter->iftype);
        }
-       mutex_unlock(&rdev->devlist_mtx);
+
+       if (total == 1)
+               return 0;
 
        for (i = 0; i < rdev->wiphy.n_iface_combinations; i++) {
                const struct ieee80211_iface_combination *c;
                struct ieee80211_iface_limit *limits;
+               u32 all_iftypes = 0;
 
                c = &rdev->wiphy.iface_combinations[i];
 
+               if (total > c->max_interfaces)
+                       continue;
+               if (num_different_channels > c->num_different_channels)
+                       continue;
+
                limits = kmemdup(c->limits, sizeof(limits[0]) * c->n_limits,
                                 GFP_KERNEL);
                if (!limits)
                        return -ENOMEM;
-               if (total > c->max_interfaces)
-                       goto cont;
 
                for (iftype = 0; iftype < NUM_NL80211_IFTYPES; iftype++) {
                        if (rdev->wiphy.software_iftypes & BIT(iftype))
                                continue;
                        for (j = 0; j < c->n_limits; j++) {
+                               all_iftypes |= limits[j].types;
                                if (!(limits[j].types & BIT(iftype)))
                                        continue;
                                if (limits[j].max < num[iftype])
@@ -996,7 +1070,20 @@ int cfg80211_can_change_interface(struct cfg80211_registered_device *rdev,
                                limits[j].max -= num[iftype];
                        }
                }
-               /* yay, it fits */
+
+               /*
+                * Finally check that all iftypes that we're currently
+                * using are actually part of this combination. If they
+                * aren't then we can't use this combination and have
+                * to continue to the next.
+                */
+               if ((all_iftypes & used_iftypes) != used_iftypes)
+                       goto cont;
+
+               /*
+                * This combination covered all interface types and
+                * supported the requested numbers, so we're good.
+                */
                kfree(limits);
                return 0;
  cont:
This page took 0.042514 seconds and 5 git commands to generate.