]> asedeno.scripts.mit.edu Git - linux.git/blobdiff - net/wireless/util.c
Merge tag 'drm-next-2018-10-24' of git://anongit.freedesktop.org/drm/drm
[linux.git] / net / wireless / util.c
index 959ed3acd2407c8c4beb4ce91b625315bf926109..ef14d80ca03ee22c5568933e1ff3d970557e59ea 100644 (file)
@@ -5,17 +5,20 @@
  * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
  * Copyright 2013-2014  Intel Mobile Communications GmbH
  * Copyright 2017      Intel Deutschland GmbH
+ * Copyright (C) 2018 Intel Corporation
  */
 #include <linux/export.h>
 #include <linux/bitops.h>
 #include <linux/etherdevice.h>
 #include <linux/slab.h>
+#include <linux/ieee80211.h>
 #include <net/cfg80211.h>
 #include <net/ip.h>
 #include <net/dsfield.h>
 #include <linux/if_vlan.h>
 #include <linux/mpls.h>
 #include <linux/gcd.h>
+#include <linux/bitfield.h>
 #include "core.h"
 #include "rdev-ops.h"
 
@@ -88,7 +91,7 @@ int ieee80211_channel_to_frequency(int chan, enum nl80211_band band)
                        return 5000 + chan * 5;
                break;
        case NL80211_BAND_60GHZ:
-               if (chan < 5)
+               if (chan < 7)
                        return 56160 + chan * 2160;
                break;
        default:
@@ -109,7 +112,7 @@ int ieee80211_frequency_to_channel(int freq)
                return (freq - 4000) / 5;
        else if (freq <= 45000) /* DMG band lower limit */
                return (freq - 5000) / 5;
-       else if (freq >= 58320 && freq <= 64800)
+       else if (freq >= 58320 && freq <= 70200)
                return (freq - 56160) / 2160;
        else
                return 0;
@@ -1568,7 +1571,7 @@ bool ieee80211_chandef_to_operating_class(struct cfg80211_chan_def *chandef,
        }
 
        /* 56.16 GHz, channel 1..4 */
-       if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 4) {
+       if (freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6) {
                if (chandef->width >= NL80211_CHAN_WIDTH_40)
                        return false;
 
@@ -1893,3 +1896,154 @@ EXPORT_SYMBOL(rfc1042_header);
 const unsigned char bridge_tunnel_header[] __aligned(2) =
        { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 };
 EXPORT_SYMBOL(bridge_tunnel_header);
+
+/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */
+struct iapp_layer2_update {
+       u8 da[ETH_ALEN];        /* broadcast */
+       u8 sa[ETH_ALEN];        /* STA addr */
+       __be16 len;             /* 6 */
+       u8 dsap;                /* 0 */
+       u8 ssap;                /* 0 */
+       u8 control;
+       u8 xid_info[3];
+} __packed;
+
+void cfg80211_send_layer2_update(struct net_device *dev, const u8 *addr)
+{
+       struct iapp_layer2_update *msg;
+       struct sk_buff *skb;
+
+       /* Send Level 2 Update Frame to update forwarding tables in layer 2
+        * bridge devices */
+
+       skb = dev_alloc_skb(sizeof(*msg));
+       if (!skb)
+               return;
+       msg = skb_put(skb, sizeof(*msg));
+
+       /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID)
+        * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */
+
+       eth_broadcast_addr(msg->da);
+       ether_addr_copy(msg->sa, addr);
+       msg->len = htons(6);
+       msg->dsap = 0;
+       msg->ssap = 0x01;       /* NULL LSAP, CR Bit: Response */
+       msg->control = 0xaf;    /* XID response lsb.1111F101.
+                                * F=0 (no poll command; unsolicited frame) */
+       msg->xid_info[0] = 0x81;        /* XID format identifier */
+       msg->xid_info[1] = 1;   /* LLC types/classes: Type 1 LLC */
+       msg->xid_info[2] = 0;   /* XID sender's receive window size (RW) */
+
+       skb->dev = dev;
+       skb->protocol = eth_type_trans(skb, dev);
+       memset(skb->cb, 0, sizeof(skb->cb));
+       netif_rx_ni(skb);
+}
+EXPORT_SYMBOL(cfg80211_send_layer2_update);
+
+int ieee80211_get_vht_max_nss(struct ieee80211_vht_cap *cap,
+                             enum ieee80211_vht_chanwidth bw,
+                             int mcs, bool ext_nss_bw_capable)
+{
+       u16 map = le16_to_cpu(cap->supp_mcs.rx_mcs_map);
+       int max_vht_nss = 0;
+       int ext_nss_bw;
+       int supp_width;
+       int i, mcs_encoding;
+
+       if (map == 0xffff)
+               return 0;
+
+       if (WARN_ON(mcs > 9))
+               return 0;
+       if (mcs <= 7)
+               mcs_encoding = 0;
+       else if (mcs == 8)
+               mcs_encoding = 1;
+       else
+               mcs_encoding = 2;
+
+       /* find max_vht_nss for the given MCS */
+       for (i = 7; i >= 0; i--) {
+               int supp = (map >> (2 * i)) & 3;
+
+               if (supp == 3)
+                       continue;
+
+               if (supp >= mcs_encoding) {
+                       max_vht_nss = i;
+                       break;
+               }
+       }
+
+       if (!(cap->supp_mcs.tx_mcs_map &
+                       cpu_to_le16(IEEE80211_VHT_EXT_NSS_BW_CAPABLE)))
+               return max_vht_nss;
+
+       ext_nss_bw = le32_get_bits(cap->vht_cap_info,
+                                  IEEE80211_VHT_CAP_EXT_NSS_BW_MASK);
+       supp_width = le32_get_bits(cap->vht_cap_info,
+                                  IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_MASK);
+
+       /* if not capable, treat ext_nss_bw as 0 */
+       if (!ext_nss_bw_capable)
+               ext_nss_bw = 0;
+
+       /* This is invalid */
+       if (supp_width == 3)
+               return 0;
+
+       /* This is an invalid combination so pretend nothing is supported */
+       if (supp_width == 2 && (ext_nss_bw == 1 || ext_nss_bw == 2))
+               return 0;
+
+       /*
+        * Cover all the special cases according to IEEE 802.11-2016
+        * Table 9-250. All other cases are either factor of 1 or not
+        * valid/supported.
+        */
+       switch (bw) {
+       case IEEE80211_VHT_CHANWIDTH_USE_HT:
+       case IEEE80211_VHT_CHANWIDTH_80MHZ:
+               if ((supp_width == 1 || supp_width == 2) &&
+                   ext_nss_bw == 3)
+                       return 2 * max_vht_nss;
+               break;
+       case IEEE80211_VHT_CHANWIDTH_160MHZ:
+               if (supp_width == 0 &&
+                   (ext_nss_bw == 1 || ext_nss_bw == 2))
+                       return DIV_ROUND_UP(max_vht_nss, 2);
+               if (supp_width == 0 &&
+                   ext_nss_bw == 3)
+                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+               if (supp_width == 1 &&
+                   ext_nss_bw == 3)
+                       return 2 * max_vht_nss;
+               break;
+       case IEEE80211_VHT_CHANWIDTH_80P80MHZ:
+               if (supp_width == 0 &&
+                   (ext_nss_bw == 1 || ext_nss_bw == 2))
+                       return 0; /* not possible */
+               if (supp_width == 0 &&
+                   ext_nss_bw == 2)
+                       return DIV_ROUND_UP(max_vht_nss, 2);
+               if (supp_width == 0 &&
+                   ext_nss_bw == 3)
+                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+               if (supp_width == 1 &&
+                   ext_nss_bw == 0)
+                       return 0; /* not possible */
+               if (supp_width == 1 &&
+                   ext_nss_bw == 1)
+                       return DIV_ROUND_UP(max_vht_nss, 2);
+               if (supp_width == 1 &&
+                   ext_nss_bw == 2)
+                       return DIV_ROUND_UP(3 * max_vht_nss, 4);
+               break;
+       }
+
+       /* not covered or invalid combination received */
+       return max_vht_nss;
+}
+EXPORT_SYMBOL(ieee80211_get_vht_max_nss);