]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/wireless/nl80211.c
Merge tag 'spi-for-linus' of git://git.secretlab.ca/git/linux-2.6
[karo-tx-linux.git] / net / wireless / nl80211.c
index 999108cd947c5991facde7a5494ee3e0389ffe22..f45706adaf3411813133704d41a12aa9d1d59712 100644 (file)
@@ -223,8 +223,13 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
                                      .len = 20-1 },
        [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
+
        [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
        [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
+       [NL80211_ATTR_CHANNEL_WIDTH] = { .type = NLA_U32 },
+       [NL80211_ATTR_CENTER_FREQ1] = { .type = NLA_U32 },
+       [NL80211_ATTR_CENTER_FREQ2] = { .type = NLA_U32 },
+
        [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
        [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
        [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
@@ -358,6 +363,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
        [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
        [NL80211_ATTR_SCAN_FLAGS] = { .type = NLA_U32 },
+       [NL80211_ATTR_P2P_CTWINDOW] = { .type = NLA_U8 },
+       [NL80211_ATTR_P2P_OPPPS] = { .type = NLA_U8 },
 };
 
 /* policy for the key attributes */
@@ -1360,35 +1367,11 @@ static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
                wdev->iftype == NL80211_IFTYPE_P2P_GO;
 }
 
-static bool nl80211_valid_channel_type(struct genl_info *info,
-                                      enum nl80211_channel_type *channel_type)
-{
-       enum nl80211_channel_type tmp;
-
-       if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
-               return false;
-
-       tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
-       if (tmp != NL80211_CHAN_NO_HT &&
-           tmp != NL80211_CHAN_HT20 &&
-           tmp != NL80211_CHAN_HT40PLUS &&
-           tmp != NL80211_CHAN_HT40MINUS)
-               return false;
-
-       if (channel_type)
-               *channel_type = tmp;
-
-       return true;
-}
-
 static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
                                 struct genl_info *info,
                                 struct cfg80211_chan_def *chandef)
 {
-       struct ieee80211_sta_ht_cap *ht_cap;
-       struct ieee80211_channel *sc;
        u32 control_freq;
-       int offs;
 
        if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
                return -EINVAL;
@@ -1396,46 +1379,50 @@ static int nl80211_parse_chandef(struct cfg80211_registered_device *rdev,
        control_freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
 
        chandef->chan = ieee80211_get_channel(&rdev->wiphy, control_freq);
-       chandef->_type = NL80211_CHAN_NO_HT;
-
-       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
-           !nl80211_valid_channel_type(info, &chandef->_type))
-               return -EINVAL;
+       chandef->width = NL80211_CHAN_WIDTH_20_NOHT;
+       chandef->center_freq1 = control_freq;
+       chandef->center_freq2 = 0;
 
        /* Primary channel not allowed */
        if (!chandef->chan || chandef->chan->flags & IEEE80211_CHAN_DISABLED)
                return -EINVAL;
 
-       ht_cap = &rdev->wiphy.bands[chandef->chan->band]->ht_cap;
+       if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+               enum nl80211_channel_type chantype;
 
-       switch (chandef->_type) {
-       case NL80211_CHAN_NO_HT:
-               break;
-       case NL80211_CHAN_HT40MINUS:
-               if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40MINUS)
+               chantype = nla_get_u32(
+                               info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+               switch (chantype) {
+               case NL80211_CHAN_NO_HT:
+               case NL80211_CHAN_HT20:
+               case NL80211_CHAN_HT40PLUS:
+               case NL80211_CHAN_HT40MINUS:
+                       cfg80211_chandef_create(chandef, chandef->chan,
+                                               chantype);
+                       break;
+               default:
                        return -EINVAL;
-               offs = -20;
-               /* fall through */
-       case NL80211_CHAN_HT40PLUS:
-               if (chandef->_type == NL80211_CHAN_HT40PLUS) {
-                       if (chandef->chan->flags & IEEE80211_CHAN_NO_HT40PLUS)
-                               return -EINVAL;
-                       offs = 20;
                }
-               if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) ||
-                   ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)
-                       return -EINVAL;
+       } else if (info->attrs[NL80211_ATTR_CHANNEL_WIDTH]) {
+               chandef->width =
+                       nla_get_u32(info->attrs[NL80211_ATTR_CHANNEL_WIDTH]);
+               if (info->attrs[NL80211_ATTR_CENTER_FREQ1])
+                       chandef->center_freq1 =
+                               nla_get_u32(
+                                       info->attrs[NL80211_ATTR_CENTER_FREQ1]);
+               if (info->attrs[NL80211_ATTR_CENTER_FREQ2])
+                       chandef->center_freq2 =
+                               nla_get_u32(
+                                       info->attrs[NL80211_ATTR_CENTER_FREQ2]);
+       }
+
+       if (!cfg80211_chandef_valid(chandef))
+               return -EINVAL;
 
-               sc = ieee80211_get_channel(&rdev->wiphy,
-                                          chandef->chan->center_freq + offs);
-               if (!sc || sc->flags & IEEE80211_CHAN_DISABLED)
-                       return -EINVAL;
-               /* fall through */
-       case NL80211_CHAN_HT20:
-               if (!ht_cap->ht_supported)
-                       return -EINVAL;
-               break;
-       }
+       if (!cfg80211_chandef_usable(&rdev->wiphy, chandef,
+                                    IEEE80211_CHAN_DISABLED))
+               return -EINVAL;
 
        return 0;
 }
@@ -1800,10 +1787,28 @@ static inline u64 wdev_id(struct wireless_dev *wdev)
 static int nl80211_send_chandef(struct sk_buff *msg,
                                 struct cfg80211_chan_def *chandef)
 {
+       WARN_ON(!cfg80211_chandef_valid(chandef));
+
        if (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
                        chandef->chan->center_freq))
                return -ENOBUFS;
-       if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, chandef->_type))
+       switch (chandef->width) {
+       case NL80211_CHAN_WIDTH_20_NOHT:
+       case NL80211_CHAN_WIDTH_20:
+       case NL80211_CHAN_WIDTH_40:
+               if (nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
+                               cfg80211_get_chandef_type(chandef)))
+                       return -ENOBUFS;
+               break;
+       default:
+               break;
+       }
+       if (nla_put_u32(msg, NL80211_ATTR_CHANNEL_WIDTH, chandef->width))
+               return -ENOBUFS;
+       if (nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ1, chandef->center_freq1))
+               return -ENOBUFS;
+       if (chandef->center_freq2 &&
+           nla_put_u32(msg, NL80211_ATTR_CENTER_FREQ2, chandef->center_freq2))
                return -ENOBUFS;
        return 0;
 }
@@ -2673,6 +2678,32 @@ static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
                        info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
        }
 
+       if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
+               if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+                       return -EINVAL;
+               params.p2p_ctwindow =
+                       nla_get_u8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
+               if (params.p2p_ctwindow > 127)
+                       return -EINVAL;
+               if (params.p2p_ctwindow != 0 &&
+                   !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
+               u8 tmp;
+
+               if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+                       return -EINVAL;
+               tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+               if (tmp > 1)
+                       return -EINVAL;
+               params.p2p_opp_ps = tmp;
+               if (params.p2p_opp_ps != 0 &&
+                   !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+                       return -EINVAL;
+       }
+
        if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
                err = nl80211_parse_chandef(rdev, info, &params.chandef);
                if (err)
@@ -2831,29 +2862,52 @@ static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
 
        rate = nla_nest_start(msg, attr);
        if (!rate)
-               goto nla_put_failure;
+               return false;
 
        /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
        bitrate = cfg80211_calculate_bitrate(info);
        /* report 16-bit bitrate only if we can */
        bitrate_compat = bitrate < (1UL << 16) ? bitrate : 0;
-       if ((bitrate > 0 &&
-            nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate)) ||
-           (bitrate_compat > 0 &&
-            nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat)) ||
-           ((info->flags & RATE_INFO_FLAGS_MCS) &&
-            nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) ||
-           ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) &&
-            nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) ||
-           ((info->flags & RATE_INFO_FLAGS_SHORT_GI) &&
-            nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)))
-               goto nla_put_failure;
+       if (bitrate > 0 &&
+           nla_put_u32(msg, NL80211_RATE_INFO_BITRATE32, bitrate))
+               return false;
+       if (bitrate_compat > 0 &&
+           nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate_compat))
+               return false;
+
+       if (info->flags & RATE_INFO_FLAGS_MCS) {
+               if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+                       return false;
+       } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
+               if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
+                       return false;
+               if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
+                       return false;
+               if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
+                   nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
+                       return false;
+       }
 
        nla_nest_end(msg, rate);
        return true;
-
-nla_put_failure:
-       return false;
 }
 
 static int nl80211_send_station(struct sk_buff *msg, u32 portid, u32 seq,
@@ -3616,6 +3670,8 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
        params.use_short_slot_time = -1;
        params.ap_isolate = -1;
        params.ht_opmode = -1;
+       params.p2p_ctwindow = -1;
+       params.p2p_opp_ps = -1;
 
        if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
                params.use_cts_prot =
@@ -3638,6 +3694,32 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
                params.ht_opmode =
                        nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
 
+       if (info->attrs[NL80211_ATTR_P2P_CTWINDOW]) {
+               if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+                       return -EINVAL;
+               params.p2p_ctwindow =
+                       nla_get_s8(info->attrs[NL80211_ATTR_P2P_CTWINDOW]);
+               if (params.p2p_ctwindow < 0)
+                       return -EINVAL;
+               if (params.p2p_ctwindow != 0 &&
+                   !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_CTWIN))
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_P2P_OPPPS]) {
+               u8 tmp;
+
+               if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+                       return -EINVAL;
+               tmp = nla_get_u8(info->attrs[NL80211_ATTR_P2P_OPPPS]);
+               if (tmp > 1)
+                       return -EINVAL;
+               params.p2p_opp_ps = tmp;
+               if (params.p2p_opp_ps &&
+                   !(rdev->wiphy.features & NL80211_FEATURE_P2P_GO_OPPPS))
+                       return -EINVAL;
+       }
+
        if (!rdev->ops->change_bss)
                return -EOPNOTSUPP;
 
@@ -4726,6 +4808,7 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
                            struct cfg80211_internal_bss *intbss)
 {
        struct cfg80211_bss *res = &intbss->pub;
+       const struct cfg80211_bss_ies *ies;
        void *hdr;
        struct nlattr *bss;
 
@@ -4746,16 +4829,24 @@ static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
        if (!bss)
                goto nla_put_failure;
        if ((!is_zero_ether_addr(res->bssid) &&
-            nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) ||
-           (res->information_elements && res->len_information_elements &&
-            nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
-                    res->len_information_elements,
-                    res->information_elements)) ||
-           (res->beacon_ies && res->len_beacon_ies &&
-            res->beacon_ies != res->information_elements &&
-            nla_put(msg, NL80211_BSS_BEACON_IES,
-                    res->len_beacon_ies, res->beacon_ies)))
+            nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)))
                goto nla_put_failure;
+
+       rcu_read_lock();
+       ies = rcu_dereference(res->ies);
+       if (ies && ies->len && nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
+                                      ies->len, ies->data)) {
+               rcu_read_unlock();
+               goto nla_put_failure;
+       }
+       ies = rcu_dereference(res->beacon_ies);
+       if (ies && ies->len && nla_put(msg, NL80211_BSS_BEACON_IES,
+                                      ies->len, ies->data)) {
+               rcu_read_unlock();
+               goto nla_put_failure;
+       }
+       rcu_read_unlock();
+
        if (res->tsf &&
            nla_put_u64(msg, NL80211_BSS_TSF, res->tsf))
                goto nla_put_failure;
@@ -5416,6 +5507,12 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &ibss.chandef))
                return -EINVAL;
 
+       if (ibss.chandef.width > NL80211_CHAN_WIDTH_40)
+               return -EINVAL;
+       if (ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT &&
+           !(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+               return -EINVAL;
+
        ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
        ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
 
@@ -5447,7 +5544,8 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                if (IS_ERR(connkeys))
                        return PTR_ERR(connkeys);
 
-               if ((ibss.chandef._type != NL80211_CHAN_NO_HT) && no_ht) {
+               if ((ibss.chandef.width != NL80211_CHAN_WIDTH_20_NOHT) &&
+                   no_ht) {
                        kfree(connkeys);
                        return -EINVAL;
                }
@@ -6441,14 +6539,13 @@ nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
 };
 
 static int nl80211_set_cqm_txe(struct genl_info *info,
-                               u32 rate, u32 pkts, u32 intvl)
+                              u32 rate, u32 pkts, u32 intvl)
 {
        struct cfg80211_registered_device *rdev = info->user_ptr[0];
        struct wireless_dev *wdev;
        struct net_device *dev = info->user_ptr[1];
 
-       if ((rate < 0 || rate > 100) ||
-           (intvl < 0 || intvl > NL80211_CQM_TXE_MAX_INTVL))
+       if (rate > 100 || intvl > NL80211_CQM_TXE_MAX_INTVL)
                return -EINVAL;
 
        wdev = dev->ieee80211_ptr;