static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
	int err;
	struct net_device *dev = info->user_ptr[1];
	struct station_parameters params;
	u8 *mac_addr = NULL;
	u32 auth_assoc = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
			 BIT(NL80211_STA_FLAG_ASSOCIATED);

	memset(&params, 0, sizeof(params));

	if (!rdev->ops->add_station)
		return -EOPNOTSUPP;

	if (!info->attrs[NL80211_ATTR_MAC])
		return -EINVAL;

	if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
		return -EINVAL;

	if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
		return -EINVAL;

	if (!info->attrs[NL80211_ATTR_STA_AID] &&
	    !info->attrs[NL80211_ATTR_PEER_AID])
		return -EINVAL;

	mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
	params.supported_rates =
		nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
	params.supported_rates_len =
		nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
	params.listen_interval =
		nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);

	if (info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]) {
		params.support_p2p_ps =
			nla_get_u8(info->attrs[NL80211_ATTR_STA_SUPPORT_P2P_PS]);
	} else {
		/*
		 * if not specified, assume it's supported for P2P GO interface,
		 * and is NOT supported for AP interface
		 */
		params.support_p2p_ps =
			dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO;
	}

	if (info->attrs[NL80211_ATTR_PEER_AID])
		params.aid = nla_get_u16(info->attrs[NL80211_ATTR_PEER_AID]);
	else
		params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);

	if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
		params.capability =
			nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
		params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
	}

	if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
		params.ext_capab =
			nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
		params.ext_capab_len =
			nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
	}

	if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
		params.ht_capa =
			nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);

	if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
		params.vht_capa =
			nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);

	if (info->attrs[NL80211_ATTR_HE_CAPABILITY]) {
		params.he_capa =
			nla_data(info->attrs[NL80211_ATTR_HE_CAPABILITY]);
		params.he_capa_len =
			nla_len(info->attrs[NL80211_ATTR_HE_CAPABILITY]);

		/* max len is validated in nla policy */
		if (params.he_capa_len < NL80211_HE_MIN_CAPABILITY_LEN)
			return -EINVAL;
	}

	if (info->attrs[NL80211_ATTR_OPMODE_NOTIF]) {
		params.opmode_notif_used = true;
		params.opmode_notif =
			nla_get_u8(info->attrs[NL80211_ATTR_OPMODE_NOTIF]);
	}

	if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
		params.plink_action =
			nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);

	if (info->attrs[NL80211_ATTR_AIRTIME_WEIGHT])
		params.airtime_weight =
			nla_get_u16(info->attrs[NL80211_ATTR_AIRTIME_WEIGHT]);

	if (params.airtime_weight &&
	    !wiphy_ext_feature_isset(&rdev->wiphy,
				     NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
		return -EOPNOTSUPP;

	err = nl80211_parse_sta_txpower_setting(info, &params);
	if (err)
		return err;

	err = nl80211_parse_sta_channel_info(info, &params);
	if (err)
		return err;

	err = nl80211_parse_sta_wme(info, &params);
	if (err)
		return err;

	if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
		return -EINVAL;

	/* HT/VHT requires QoS, but if we don't have that just ignore HT/VHT
	 * as userspace might just pass through the capabilities from the IEs
	 * directly, rather than enforcing this restriction and returning an
	 * error in this case.
	 */
	if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME))) {
		params.ht_capa = NULL;
		params.vht_capa = NULL;

		/* HE requires WME */
		if (params.he_capa_len)
			return -EINVAL;
	}

	/* When you run into this, adjust the code below for the new flag */
	BUILD_BUG_ON(NL80211_STA_FLAG_MAX != 7);

	switch (dev->ieee80211_ptr->iftype) {
	case NL80211_IFTYPE_AP:
	case NL80211_IFTYPE_AP_VLAN:
	case NL80211_IFTYPE_P2P_GO:
		/* ignore WME attributes if iface/sta is not capable */
		if (!(rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) ||
		    !(params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)))
			params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;

		/* TDLS peers cannot be added */
		if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
		    info->attrs[NL80211_ATTR_PEER_AID])
			return -EINVAL;
		/* but don't bother the driver with it */
		params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);

		/* allow authenticated/associated only if driver handles it */
		if (!(rdev->wiphy.features &
				NL80211_FEATURE_FULL_AP_CLIENT_STATE) &&
		    params.sta_flags_mask & auth_assoc)
			return -EINVAL;

		/* Older userspace, or userspace wanting to be compatible with
		 * !NL80211_FEATURE_FULL_AP_CLIENT_STATE, will not set the auth
		 * and assoc flags in the mask, but assumes the station will be
		 * added as associated anyway since this was the required driver
		 * behaviour before NL80211_FEATURE_FULL_AP_CLIENT_STATE was
		 * introduced.
		 * In order to not bother drivers with this quirk in the API
		 * set the flags in both the mask and set for new stations in
		 * this case.
		 */
		if (!(params.sta_flags_mask & auth_assoc)) {
			params.sta_flags_mask |= auth_assoc;
			params.sta_flags_set |= auth_assoc;
		}

		/* 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:
		/* ignore uAPSD data */
		params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;

		/* associated is disallowed */
		if (params.sta_flags_mask & BIT(NL80211_STA_FLAG_ASSOCIATED))
			return -EINVAL;
		/* TDLS peers cannot be added */
		if ((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) ||
		    info->attrs[NL80211_ATTR_PEER_AID])
			return -EINVAL;
		break;
	case NL80211_IFTYPE_STATION:
	case NL80211_IFTYPE_P2P_CLIENT:
		/* ignore uAPSD data */
		params.sta_modify_mask &= ~STATION_PARAM_APPLY_UAPSD;

		/* these are disallowed */
		if (params.sta_flags_mask &
				(BIT(NL80211_STA_FLAG_ASSOCIATED) |
				 BIT(NL80211_STA_FLAG_AUTHENTICATED)))
			return -EINVAL;
		/* 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;
		/*
		 * Older wpa_supplicant versions always mark the TDLS peer
		 * as authorized, but it shouldn't yet be.
		 */
		params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_AUTHORIZED);
		break;
	default:
		return -EOPNOTSUPP;
	}

	/* be aware of params.vlan when changing code here */

	err = rdev_add_station(rdev, dev, mac_addr, &params);

	if (params.vlan)
		dev_put(params.vlan);
	return err;
}