genetlink: allow making ops const
[pandora-kernel.git] / net / wireless / nl80211.c
index 626dc3b..3fef559 100644 (file)
@@ -30,9 +30,9 @@ static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
                                   struct cfg80211_crypto_settings *settings,
                                   int cipher_limit);
 
-static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
+static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                            struct genl_info *info);
-static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
+static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info);
 
 /* the netlink family */
@@ -354,6 +354,9 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
        [NL80211_ATTR_CSA_IES] = { .type = NLA_NESTED },
        [NL80211_ATTR_CSA_C_OFF_BEACON] = { .type = NLA_U16 },
        [NL80211_ATTR_CSA_C_OFF_PRESP] = { .type = NLA_U16 },
+       [NL80211_ATTR_STA_SUPPORTED_CHANNELS] = { .type = NLA_BINARY },
+       [NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES] = { .type = NLA_BINARY },
+       [NL80211_ATTR_HANDLE_DFS] = { .type = NLA_FLAG },
 };
 
 /* policy for the key attributes */
@@ -3896,9 +3899,45 @@ static int nl80211_parse_sta_wme(struct genl_info *info,
        return 0;
 }
 
+static int nl80211_parse_sta_channel_info(struct genl_info *info,
+                                     struct station_parameters *params)
+{
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]) {
+               params->supported_channels =
+                    nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               params->supported_channels_len =
+                    nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_CHANNELS]);
+               /*
+                * Need to include at least one (first channel, number of
+                * channels) tuple for each subband, and must have proper
+                * tuples for the rest of the data as well.
+                */
+               if (params->supported_channels_len < 2)
+                       return -EINVAL;
+               if (params->supported_channels_len % 2)
+                       return -EINVAL;
+       }
+
+       if (info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]) {
+               params->supported_oper_classes =
+                nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               params->supported_oper_classes_len =
+                 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_OPER_CLASSES]);
+               /*
+                * The value of the Length field of the Supported Operating
+                * Classes element is between 2 and 253.
+                */
+               if (params->supported_oper_classes_len < 2 ||
+                   params->supported_oper_classes_len > 253)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
 static int nl80211_set_station_tdls(struct genl_info *info,
                                    struct station_parameters *params)
 {
+       int err;
        /* Dummy STA entry gets updated once the peer capabilities are known */
        if (info->attrs[NL80211_ATTR_PEER_AID])
                params->aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
@@ -3909,6 +3948,10 @@ static int nl80211_set_station_tdls(struct genl_info *info,
                params->vht_capa =
                        nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
 
+       err = nl80211_parse_sta_channel_info(info, params);
+       if (err)
+               return err;
+
        return nl80211_parse_sta_wme(info, params);
 }
 
@@ -4089,6 +4132,10 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+       err = nl80211_parse_sta_channel_info(info, &params);
+       if (err)
+               return err;
+
        err = nl80211_parse_sta_wme(info, &params);
        if (err)
                return err;
@@ -5591,6 +5638,9 @@ static int nl80211_start_radar_detection(struct sk_buff *skb,
        if (err)
                return err;
 
+       if (netif_carrier_ok(dev))
+               return -EBUSY;
+
        if (wdev->cac_started)
                return -EBUSY;
 
@@ -5634,15 +5684,27 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        static struct nlattr *csa_attrs[NL80211_ATTR_MAX+1];
        u8 radar_detect_width = 0;
        int err;
+       bool need_new_beacon = false;
 
        if (!rdev->ops->channel_switch ||
            !(rdev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH))
                return -EOPNOTSUPP;
 
-       /* may add IBSS support later */
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_P2P_GO:
+               need_new_beacon = true;
+
+               /* useless if AP is not running */
+               if (!wdev->beacon_interval)
+                       return -EINVAL;
+               break;
+       case NL80211_IFTYPE_ADHOC:
+       case NL80211_IFTYPE_MESH_POINT:
+               break;
+       default:
                return -EOPNOTSUPP;
+       }
 
        memset(&params, 0, sizeof(params));
 
@@ -5651,15 +5713,14 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                return -EINVAL;
 
        /* only important for AP, IBSS and mesh create IEs internally */
-       if (!info->attrs[NL80211_ATTR_CSA_IES])
-               return -EINVAL;
-
-       /* useless if AP is not running */
-       if (!wdev->beacon_interval)
+       if (need_new_beacon && !info->attrs[NL80211_ATTR_CSA_IES])
                return -EINVAL;
 
        params.count = nla_get_u32(info->attrs[NL80211_ATTR_CH_SWITCH_COUNT]);
 
+       if (!need_new_beacon)
+               goto skip_beacons;
+
        err = nl80211_parse_beacon(info->attrs, &params.beacon_after);
        if (err)
                return err;
@@ -5699,6 +5760,7 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
                        return -EINVAL;
        }
 
+skip_beacons:
        err = nl80211_parse_chandef(rdev, info, &params.chandef);
        if (err)
                return err;
@@ -5706,12 +5768,17 @@ static int nl80211_channel_switch(struct sk_buff *skb, struct genl_info *info)
        if (!cfg80211_reg_can_beacon(&rdev->wiphy, &params.chandef))
                return -EINVAL;
 
-       err = cfg80211_chandef_dfs_required(wdev->wiphy, &params.chandef);
-       if (err < 0) {
-               return err;
-       } else if (err) {
-               radar_detect_width = BIT(params.chandef.width);
-               params.radar_required = true;
+       if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO ||
+           dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC) {
+               err = cfg80211_chandef_dfs_required(wdev->wiphy,
+                                                   &params.chandef);
+               if (err < 0) {
+                       return err;
+               } else if (err) {
+                       radar_detect_width = BIT(params.chandef.width);
+                       params.radar_required = true;
+               }
        }
 
        err = cfg80211_can_use_iftype_chan(rdev, wdev, wdev->iftype,
@@ -6535,6 +6602,9 @@ static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
        ibss.control_port =
                nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
 
+       ibss.userspace_handles_dfs =
+               nla_get_flag(info->attrs[NL80211_ATTR_HANDLE_DFS]);
+
        err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
        if (err)
                kfree(connkeys);
@@ -8781,7 +8851,7 @@ static int nl80211_crit_protocol_stop(struct sk_buff *skb,
 #define NL80211_FLAG_NEED_WDEV_UP      (NL80211_FLAG_NEED_WDEV |\
                                         NL80211_FLAG_CHECK_NETDEV_UP)
 
-static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
+static int nl80211_pre_doit(const struct genl_ops *ops, struct sk_buff *skb,
                            struct genl_info *info)
 {
        struct cfg80211_registered_device *rdev;
@@ -8850,7 +8920,7 @@ static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
        return 0;
 }
 
-static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
+static void nl80211_post_doit(const struct genl_ops *ops, struct sk_buff *skb,
                              struct genl_info *info)
 {
        if (info->user_ptr[1]) {
@@ -10740,7 +10810,9 @@ void cfg80211_ch_switch_notify(struct net_device *dev,
        wdev_lock(wdev);
 
        if (WARN_ON(wdev->iftype != NL80211_IFTYPE_AP &&
-                   wdev->iftype != NL80211_IFTYPE_P2P_GO))
+                   wdev->iftype != NL80211_IFTYPE_P2P_GO &&
+                   wdev->iftype != NL80211_IFTYPE_ADHOC &&
+                   wdev->iftype != NL80211_IFTYPE_MESH_POINT))
                goto out;
 
        wdev->channel = chandef->chan;