Merge branch 'x86-pmem-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[firefly-linux-kernel-4.4.55.git] / net / wireless / nl80211.c
index b6f84f6a2a095ef0c94891f796ef6cf7cf9af65f..dd78445c7d50630524b7d33b96b73a2e416c662c 100644 (file)
@@ -399,6 +399,7 @@ static const struct nla_policy nl80211_policy[NUM_NL80211_ATTR] = {
        [NL80211_ATTR_WIPHY_SELF_MANAGED_REG] = { .type = NLA_FLAG },
        [NL80211_ATTR_NETNS_FD] = { .type = NLA_U32 },
        [NL80211_ATTR_SCHED_SCAN_DELAY] = { .type = NLA_U32 },
+       [NL80211_ATTR_REG_INDOOR] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -1098,8 +1099,6 @@ static int nl80211_send_wowlan(struct sk_buff *msg,
        if (large && nl80211_send_wowlan_tcp_caps(rdev, msg))
                return -ENOBUFS;
 
-       /* TODO: send wowlan net detect */
-
        nla_nest_end(msg, nl_wowlan);
 
        return 0;
@@ -2668,7 +2667,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
 
        wdev = rdev_add_virtual_intf(rdev,
                                nla_data(info->attrs[NL80211_ATTR_IFNAME]),
-                               type, err ? NULL : &flags, &params);
+                               NET_NAME_USER, type, err ? NULL : &flags,
+                               &params);
        if (WARN_ON(!wdev)) {
                nlmsg_free(msg);
                return -EPROTO;
@@ -4968,7 +4968,10 @@ static int parse_reg_rule(struct nlattr *tb[],
 static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
 {
        char *data = NULL;
+       bool is_indoor;
        enum nl80211_user_reg_hint_type user_reg_hint_type;
+       u32 owner_nlportid;
+
 
        /*
         * You should only get this when cfg80211 hasn't yet initialized
@@ -4994,7 +4997,15 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
                data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
                return regulatory_hint_user(data, user_reg_hint_type);
        case NL80211_USER_REG_HINT_INDOOR:
-               return regulatory_hint_indoor_user();
+               if (info->attrs[NL80211_ATTR_SOCKET_OWNER]) {
+                       owner_nlportid = info->snd_portid;
+                       is_indoor = !!info->attrs[NL80211_ATTR_REG_INDOOR];
+               } else {
+                       owner_nlportid = 0;
+                       is_indoor = true;
+               }
+
+               return regulatory_hint_indoor(is_indoor, owner_nlportid);
        default:
                return -EINVAL;
        }
@@ -5275,7 +5286,7 @@ do {                                                                          \
        FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshAwakeWindowDuration,
                                  0, 65535, mask,
                                  NL80211_MESHCONF_AWAKE_WINDOW, nla_get_u16);
-       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 1, 0xffffffff,
+       FILL_IN_MESH_PARAM_IF_SET(tb, cfg, plink_timeout, 0, 0xffffffff,
                                  mask, NL80211_MESHCONF_PLINK_TIMEOUT,
                                  nla_get_u32);
        if (mask_out)
@@ -5653,7 +5664,7 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
                }
        }
 
-       r = set_regdom(rd);
+       r = set_regdom(rd, REGD_SOURCE_CRDA);
        /* set_regdom took ownership */
        rd = NULL;
 
@@ -5693,8 +5704,8 @@ static int nl80211_parse_random_mac(struct nlattr **attrs,
        int i;
 
        if (!attrs[NL80211_ATTR_MAC] && !attrs[NL80211_ATTR_MAC_MASK]) {
-               memset(mac_addr, 0, ETH_ALEN);
-               memset(mac_addr_mask, 0, ETH_ALEN);
+               eth_zero_addr(mac_addr);
+               eth_zero_addr(mac_addr_mask);
                mac_addr[0] = 0x2;
                mac_addr_mask[0] = 0x3;
 
@@ -7275,8 +7286,18 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
                break;
        case NL80211_CHAN_WIDTH_20:
        case NL80211_CHAN_WIDTH_40:
-               if (rdev->wiphy.features & NL80211_FEATURE_HT_IBSS)
-                       break;
+               if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+                       return -EINVAL;
+               break;
+       case NL80211_CHAN_WIDTH_80:
+       case NL80211_CHAN_WIDTH_80P80:
+       case NL80211_CHAN_WIDTH_160:
+               if (!(rdev->wiphy.features & NL80211_FEATURE_HT_IBSS))
+                       return -EINVAL;
+               if (!wiphy_ext_feature_isset(&rdev->wiphy,
+                                            NL80211_EXT_FEATURE_VHT_IBSS))
+                       return -EINVAL;
+               break;
        default:
                return -EINVAL;
        }
@@ -7389,8 +7410,8 @@ static int nl80211_set_mcast_rate(struct sk_buff *skb, struct genl_info *info)
 
 static struct sk_buff *
 __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
-                           int approxlen, u32 portid, u32 seq,
-                           enum nl80211_commands cmd,
+                           struct wireless_dev *wdev, int approxlen,
+                           u32 portid, u32 seq, enum nl80211_commands cmd,
                            enum nl80211_attrs attr,
                            const struct nl80211_vendor_cmd_info *info,
                            gfp_t gfp)
@@ -7421,6 +7442,16 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
                        goto nla_put_failure;
        }
 
+       if (wdev) {
+               if (nla_put_u64(skb, NL80211_ATTR_WDEV,
+                               wdev_id(wdev)))
+                       goto nla_put_failure;
+               if (wdev->netdev &&
+                   nla_put_u32(skb, NL80211_ATTR_IFINDEX,
+                               wdev->netdev->ifindex))
+                       goto nla_put_failure;
+       }
+
        data = nla_nest_start(skb, attr);
 
        ((void **)skb->cb)[0] = rdev;
@@ -7435,6 +7466,7 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev,
 }
 
 struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
+                                          struct wireless_dev *wdev,
                                           enum nl80211_commands cmd,
                                           enum nl80211_attrs attr,
                                           int vendor_event_idx,
@@ -7460,7 +7492,7 @@ struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy,
                return NULL;
        }
 
-       return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0,
+       return __cfg80211_alloc_vendor_skb(rdev, wdev, approxlen, 0, 0,
                                           cmd, attr, info, gfp);
 }
 EXPORT_SYMBOL(__cfg80211_alloc_event_skb);
@@ -8761,8 +8793,8 @@ static int nl80211_send_wowlan_tcp(struct sk_buff *msg,
        if (!nl_tcp)
                return -ENOBUFS;
 
-       if (nla_put_be32(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
-           nla_put_be32(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
+       if (nla_put_in_addr(msg, NL80211_WOWLAN_TCP_SRC_IPV4, tcp->src) ||
+           nla_put_in_addr(msg, NL80211_WOWLAN_TCP_DST_IPV4, tcp->dst) ||
            nla_put(msg, NL80211_WOWLAN_TCP_DST_MAC, ETH_ALEN, tcp->dst_mac) ||
            nla_put_u16(msg, NL80211_WOWLAN_TCP_SRC_PORT, tcp->src_port) ||
            nla_put_u16(msg, NL80211_WOWLAN_TCP_DST_PORT, tcp->dst_port) ||
@@ -8808,6 +8840,9 @@ static int nl80211_send_wowlan_nd(struct sk_buff *msg,
        if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_INTERVAL, req->interval))
                return -ENOBUFS;
 
+       if (nla_put_u32(msg, NL80211_ATTR_SCHED_SCAN_DELAY, req->delay))
+               return -ENOBUFS;
+
        freqs = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
        if (!freqs)
                return -ENOBUFS;
@@ -8993,8 +9028,8 @@ static int nl80211_parse_wowlan_tcp(struct cfg80211_registered_device *rdev,
        cfg = kzalloc(size, GFP_KERNEL);
        if (!cfg)
                return -ENOMEM;
-       cfg->src = nla_get_be32(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
-       cfg->dst = nla_get_be32(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
+       cfg->src = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_SRC_IPV4]);
+       cfg->dst = nla_get_in_addr(tb[NL80211_WOWLAN_TCP_DST_IPV4]);
        memcpy(cfg->dst_mac, nla_data(tb[NL80211_WOWLAN_TCP_DST_MAC]),
               ETH_ALEN);
        if (tb[NL80211_WOWLAN_TCP_SRC_PORT])
@@ -9094,6 +9129,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        const struct wiphy_wowlan_support *wowlan = rdev->wiphy.wowlan;
        int err, i;
        bool prev_enabled = rdev->wiphy.wowlan_config;
+       bool regular = false;
 
        if (!wowlan)
                return -EOPNOTSUPP;
@@ -9121,12 +9157,14 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
                        return -EINVAL;
                new_triggers.disconnect = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
                if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
                        return -EINVAL;
                new_triggers.magic_pkt = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
@@ -9136,24 +9174,28 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
                        return -EINVAL;
                new_triggers.gtk_rekey_failure = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
                if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
                        return -EINVAL;
                new_triggers.eap_identity_req = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
                if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
                        return -EINVAL;
                new_triggers.four_way_handshake = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
                if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
                        return -EINVAL;
                new_triggers.rfkill_release = true;
+               regular = true;
        }
 
        if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
@@ -9162,6 +9204,8 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                int rem, pat_len, mask_len, pkt_offset;
                struct nlattr *pat_tb[NUM_NL80211_PKTPAT];
 
+               regular = true;
+
                nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
                                    rem)
                        n_patterns++;
@@ -9223,6 +9267,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION]) {
+               regular = true;
                err = nl80211_parse_wowlan_tcp(
                        rdev, tb[NL80211_WOWLAN_TRIG_TCP_CONNECTION],
                        &new_triggers);
@@ -9231,6 +9276,7 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
        }
 
        if (tb[NL80211_WOWLAN_TRIG_NET_DETECT]) {
+               regular = true;
                err = nl80211_parse_wowlan_nd(
                        rdev, wowlan, tb[NL80211_WOWLAN_TRIG_NET_DETECT],
                        &new_triggers);
@@ -9238,6 +9284,17 @@ static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
                        goto error;
        }
 
+       /* The 'any' trigger means the device continues operating more or less
+        * as in its normal operation mode and wakes up the host on most of the
+        * normal interrupts (like packet RX, ...)
+        * It therefore makes little sense to combine with the more constrained
+        * wakeup trigger modes.
+        */
+       if (new_triggers.any && regular) {
+               err = -EINVAL;
+               goto error;
+       }
+
        ntrig = kmemdup(&new_triggers, sizeof(new_triggers), GFP_KERNEL);
        if (!ntrig) {
                err = -ENOMEM;
@@ -9906,7 +9963,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy,
        if (WARN_ON(!rdev->cur_cmd_info))
                return NULL;
 
-       return __cfg80211_alloc_vendor_skb(rdev, approxlen,
+       return __cfg80211_alloc_vendor_skb(rdev, NULL, approxlen,
                                           rdev->cur_cmd_info->snd_portid,
                                           rdev->cur_cmd_info->snd_seq,
                                           cmd, attr, NULL, GFP_KERNEL);
@@ -12775,6 +12832,11 @@ static int nl80211_netlink_notify(struct notifier_block * nb,
 
        rcu_read_unlock();
 
+       /*
+        * It is possible that the user space process that is controlling the
+        * indoor setting disappeared, so notify the regulatory core.
+        */
+       regulatory_netlink_notify(notify->portid);
        return NOTIFY_OK;
 }