params.ht_capa =
                        nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
 
+       if (!rdev->ops->change_station)
+               return -EOPNOTSUPP;
+
        if (parse_station_flags(info, ¶ms))
                return -EINVAL;
 
                params.plink_state =
                    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
 
-       params.vlan = get_vlan(info, rdev);
-       if (IS_ERR(params.vlan))
-               return PTR_ERR(params.vlan);
-
-       /* validate settings */
-       err = 0;
-
        switch (dev->ieee80211_ptr->iftype) {
        case NL80211_IFTYPE_AP:
        case NL80211_IFTYPE_AP_VLAN:
        case NL80211_IFTYPE_P2P_GO:
                /* disallow mesh-specific things */
                if (params.plink_action)
-                       err = -EINVAL;
+                       return -EINVAL;
+
+               /* TDLS can't be set, ... */
+               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+                       return -EINVAL;
+               /*
+                * ... but don't bother the driver with it. This works around
+                * a hostapd/wpa_supplicant issue -- it always includes the
+                * TLDS_PEER flag in the mask even for AP mode.
+                */
+               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+               /* accept only the listed bits */
+               if (params.sta_flags_mask &
+                               ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
+                                 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
+                                 BIT(NL80211_STA_FLAG_WME) |
+                                 BIT(NL80211_STA_FLAG_MFP)))
+                       return -EINVAL;
+
+               /* must be last in here for error handling */
+               params.vlan = get_vlan(info, rdev);
+               if (IS_ERR(params.vlan))
+                       return PTR_ERR(params.vlan);
                break;
        case NL80211_IFTYPE_P2P_CLIENT:
        case NL80211_IFTYPE_STATION:
                /* disallow things sta doesn't support */
                if (params.plink_action)
-                       err = -EINVAL;
-               if (params.vlan)
-                       err = -EINVAL;
-               if (params.supported_rates &&
-                   !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
-                       err = -EINVAL;
+                       return -EINVAL;
                if (params.ht_capa)
-                       err = -EINVAL;
+                       return -EINVAL;
                if (params.listen_interval >= 0)
-                       err = -EINVAL;
-               if (params.sta_flags_mask &
-                               ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
-                                 BIT(NL80211_STA_FLAG_TDLS_PEER)))
-                       err = -EINVAL;
-               /* can't change the TDLS bit */
-               if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
-                   (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)))
-                       err = -EINVAL;
+                       return -EINVAL;
+               /*
+                * Don't allow userspace to change the TDLS_PEER flag,
+                * but silently ignore attempts to change it since we
+                * don't have state here to verify that it doesn't try
+                * to change the flag.
+                */
+               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
+
+               /* reject any changes other than AUTHORIZED */
+               if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
+                       return -EINVAL;
                break;
        case NL80211_IFTYPE_MESH_POINT:
                /* disallow things mesh doesn't support */
                if (params.vlan)
-                       err = -EINVAL;
+                       return -EINVAL;
                if (params.ht_capa)
-                       err = -EINVAL;
+                       return -EINVAL;
                if (params.listen_interval >= 0)
-                       err = -EINVAL;
+                       return -EINVAL;
+               /*
+                * No special handling for TDLS here -- the userspace
+                * mesh code doesn't have this bug.
+                */
                if (params.sta_flags_mask &
                                ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
                                  BIT(NL80211_STA_FLAG_MFP) |
                                  BIT(NL80211_STA_FLAG_AUTHORIZED)))
-                       err = -EINVAL;
+                       return -EINVAL;
                break;
        default:
-               err = -EINVAL;
+               return -EOPNOTSUPP;
        }
 
-       if (err)
-               goto out;
-
-       if (!rdev->ops->change_station) {
-               err = -EOPNOTSUPP;
-               goto out;
-       }
+       /* be aware of params.vlan when changing code here */
 
        err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, ¶ms);
 
- out:
        if (params.vlan)
                dev_put(params.vlan);
 
                params.plink_action =
                    nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
 
+       if (!rdev->ops->add_station)
+               return -EOPNOTSUPP;
+
        if (parse_station_flags(info, ¶ms))
                return -EINVAL;
 
-       /* parse WME attributes if sta is WME capable */
-       if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
-           (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
-           info->attrs[NL80211_ATTR_STA_WME]) {
-               struct nlattr *tb[NL80211_STA_WME_MAX + 1];
-               struct nlattr *nla;
+       switch (dev->ieee80211_ptr->iftype) {
+       case NL80211_IFTYPE_AP:
+       case NL80211_IFTYPE_AP_VLAN:
+       case NL80211_IFTYPE_P2P_GO:
+               /* parse WME attributes if sta is WME capable */
+               if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
+                   (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
+                   info->attrs[NL80211_ATTR_STA_WME]) {
+                       struct nlattr *tb[NL80211_STA_WME_MAX + 1];
+                       struct nlattr *nla;
+
+                       nla = info->attrs[NL80211_ATTR_STA_WME];
+                       err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
+                                              nl80211_sta_wme_policy);
+                       if (err)
+                               return err;
 
-               nla = info->attrs[NL80211_ATTR_STA_WME];
-               err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
-                                      nl80211_sta_wme_policy);
-               if (err)
-                       return err;
+                       if (tb[NL80211_STA_WME_UAPSD_QUEUES])
+                               params.uapsd_queues =
+                                    nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
+                       if (params.uapsd_queues &
+                                       ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
+                               return -EINVAL;
 
-               if (tb[NL80211_STA_WME_UAPSD_QUEUES])
-                       params.uapsd_queues =
-                            nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
-               if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
-                       return -EINVAL;
+                       if (tb[NL80211_STA_WME_MAX_SP])
+                               params.max_sp =
+                                    nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
 
-               if (tb[NL80211_STA_WME_MAX_SP])
-                       params.max_sp =
-                            nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
+                       if (params.max_sp &
+                                       ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
+                               return -EINVAL;
 
-               if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
+                       params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
+               }
+               /* TDLS peers cannot be added */
+               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
                        return -EINVAL;
+               /* but don't bother the driver with it */
+               params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
 
-               params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
+               /* must be last in here for error handling */
+               params.vlan = get_vlan(info, rdev);
+               if (IS_ERR(params.vlan))
+                       return PTR_ERR(params.vlan);
+               break;
+       case NL80211_IFTYPE_MESH_POINT:
+               /* TDLS peers cannot be added */
+               if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
+                       return -EINVAL;
+               break;
+       case NL80211_IFTYPE_STATION:
+               /* Only TDLS peers can be added */
+               if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
+                       return -EINVAL;
+               /* Can only add if TDLS ... */
+               if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
+                       return -EOPNOTSUPP;
+               /* ... with external setup is supported */
+               if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
+                       return -EOPNOTSUPP;
+               break;
+       default:
+               return -EOPNOTSUPP;
        }
 
-       if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO &&
-           dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)
-               return -EINVAL;
-
-       /*
-        * Only managed stations can add TDLS peers, and only when the
-        * wiphy supports external TDLS setup.
-        */
-       if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION &&
-           !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
-             (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
-             (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)))
-               return -EINVAL;
-
-       params.vlan = get_vlan(info, rdev);
-       if (IS_ERR(params.vlan))
-               return PTR_ERR(params.vlan);
-
-       /* validate settings */
-       err = 0;
-
-       if (!rdev->ops->add_station) {
-               err = -EOPNOTSUPP;
-               goto out;
-       }
+       /* be aware of params.vlan when changing code here */
 
        err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, ¶ms);
 
- out:
        if (params.vlan)
                dev_put(params.vlan);
        return err;