static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
{
	struct cfg80211_registered_device *rdev;
	struct net_device *netdev = NULL;
	struct wireless_dev *wdev;
	int result = 0, rem_txq_params = 0;
	struct nlattr *nl_txq_params;
	u32 changed;
	u8 retry_short = 0, retry_long = 0;
	u32 frag_threshold = 0, rts_threshold = 0;
	u8 coverage_class = 0;
	u32 txq_limit = 0, txq_memory_limit = 0, txq_quantum = 0;

	ASSERT_RTNL();

	/*
	 * Try to find the wiphy and netdev. Normally this
	 * function shouldn't need the netdev, but this is
	 * done for backward compatibility -- previously
	 * setting the channel was done per wiphy, but now
	 * it is per netdev. Previous userland like hostapd
	 * also passed a netdev to set_wiphy, so that it is
	 * possible to let that go to the right netdev!
	 */

	if (info->attrs[NL80211_ATTR_IFINDEX]) {
		int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);

		netdev = __dev_get_by_index(genl_info_net(info), ifindex);
		if (netdev && netdev->ieee80211_ptr)
			rdev = wiphy_to_rdev(netdev->ieee80211_ptr->wiphy);
		else
			netdev = NULL;
	}

	if (!netdev) {
		rdev = __cfg80211_rdev_from_attrs(genl_info_net(info),
						  info->attrs);
		if (IS_ERR(rdev))
			return PTR_ERR(rdev);
		wdev = NULL;
		netdev = NULL;
		result = 0;
	} else
		wdev = netdev->ieee80211_ptr;

	/*
	 * end workaround code, by now the rdev is available
	 * and locked, and wdev may or may not be NULL.
	 */

	if (info->attrs[NL80211_ATTR_WIPHY_NAME])
		result = cfg80211_dev_rename(
			rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));

	if (result)
		return result;

	if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
		struct ieee80211_txq_params txq_params;
		struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];

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

		if (!netdev)
			return -EINVAL;

		if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
		    netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
			return -EINVAL;

		if (!netif_running(netdev))
			return -ENETDOWN;

		nla_for_each_nested(nl_txq_params,
				    info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
				    rem_txq_params) {
			result = nla_parse_nested_deprecated(tb,
							     NL80211_TXQ_ATTR_MAX,
							     nl_txq_params,
							     txq_params_policy,
							     info->extack);
			if (result)
				return result;
			result = parse_txq_params(tb, &txq_params);
			if (result)
				return result;

			result = rdev_set_txq_params(rdev, netdev,
						     &txq_params);
			if (result)
				return result;
		}
	}

	if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
		result = __nl80211_set_channel(
			rdev,
			nl80211_can_set_dev_channel(wdev) ? netdev : NULL,
			info);
		if (result)
			return result;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
		struct wireless_dev *txp_wdev = wdev;
		enum nl80211_tx_power_setting type;
		int idx, mbm = 0;

		if (!(rdev->wiphy.features & NL80211_FEATURE_VIF_TXPOWER))
			txp_wdev = NULL;

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

		idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
		type = nla_get_u32(info->attrs[idx]);

		if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
		    (type != NL80211_TX_POWER_AUTOMATIC))
			return -EINVAL;

		if (type != NL80211_TX_POWER_AUTOMATIC) {
			idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
			mbm = nla_get_u32(info->attrs[idx]);
		}

		result = rdev_set_tx_power(rdev, txp_wdev, type, mbm);
		if (result)
			return result;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
	    info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
		u32 tx_ant, rx_ant;

		if ((!rdev->wiphy.available_antennas_tx &&
		     !rdev->wiphy.available_antennas_rx) ||
		    !rdev->ops->set_antenna)
			return -EOPNOTSUPP;

		tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
		rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);

		/* reject antenna configurations which don't match the
		 * available antenna masks, except for the "all" mask */
		if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
		    (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx)))
			return -EINVAL;

		tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
		rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;

		result = rdev_set_antenna(rdev, tx_ant, rx_ant);
		if (result)
			return result;
	}

	changed = 0;

	if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
		retry_short = nla_get_u8(
			info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);

		changed |= WIPHY_PARAM_RETRY_SHORT;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
		retry_long = nla_get_u8(
			info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);

		changed |= WIPHY_PARAM_RETRY_LONG;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
		frag_threshold = nla_get_u32(
			info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
		if (frag_threshold < 256)
			return -EINVAL;

		if (frag_threshold != (u32) -1) {
			/*
			 * Fragments (apart from the last one) are required to
			 * have even length. Make the fragmentation code
			 * simpler by stripping LSB should someone try to use
			 * odd threshold value.
			 */
			frag_threshold &= ~0x1;
		}
		changed |= WIPHY_PARAM_FRAG_THRESHOLD;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
		rts_threshold = nla_get_u32(
			info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
		changed |= WIPHY_PARAM_RTS_THRESHOLD;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
		if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK])
			return -EINVAL;

		coverage_class = nla_get_u8(
			info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
		changed |= WIPHY_PARAM_COVERAGE_CLASS;
	}

	if (info->attrs[NL80211_ATTR_WIPHY_DYN_ACK]) {
		if (!(rdev->wiphy.features & NL80211_FEATURE_ACKTO_ESTIMATION))
			return -EOPNOTSUPP;

		changed |= WIPHY_PARAM_DYN_ACK;
	}

	if (info->attrs[NL80211_ATTR_TXQ_LIMIT]) {
		if (!wiphy_ext_feature_isset(&rdev->wiphy,
					     NL80211_EXT_FEATURE_TXQS))
			return -EOPNOTSUPP;
		txq_limit = nla_get_u32(
			info->attrs[NL80211_ATTR_TXQ_LIMIT]);
		changed |= WIPHY_PARAM_TXQ_LIMIT;
	}

	if (info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]) {
		if (!wiphy_ext_feature_isset(&rdev->wiphy,
					     NL80211_EXT_FEATURE_TXQS))
			return -EOPNOTSUPP;
		txq_memory_limit = nla_get_u32(
			info->attrs[NL80211_ATTR_TXQ_MEMORY_LIMIT]);
		changed |= WIPHY_PARAM_TXQ_MEMORY_LIMIT;
	}

	if (info->attrs[NL80211_ATTR_TXQ_QUANTUM]) {
		if (!wiphy_ext_feature_isset(&rdev->wiphy,
					     NL80211_EXT_FEATURE_TXQS))
			return -EOPNOTSUPP;
		txq_quantum = nla_get_u32(
			info->attrs[NL80211_ATTR_TXQ_QUANTUM]);
		changed |= WIPHY_PARAM_TXQ_QUANTUM;
	}

	if (changed) {
		u8 old_retry_short, old_retry_long;
		u32 old_frag_threshold, old_rts_threshold;
		u8 old_coverage_class;
		u32 old_txq_limit, old_txq_memory_limit, old_txq_quantum;

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

		old_retry_short = rdev->wiphy.retry_short;
		old_retry_long = rdev->wiphy.retry_long;
		old_frag_threshold = rdev->wiphy.frag_threshold;
		old_rts_threshold = rdev->wiphy.rts_threshold;
		old_coverage_class = rdev->wiphy.coverage_class;
		old_txq_limit = rdev->wiphy.txq_limit;
		old_txq_memory_limit = rdev->wiphy.txq_memory_limit;
		old_txq_quantum = rdev->wiphy.txq_quantum;

		if (changed & WIPHY_PARAM_RETRY_SHORT)
			rdev->wiphy.retry_short = retry_short;
		if (changed & WIPHY_PARAM_RETRY_LONG)
			rdev->wiphy.retry_long = retry_long;
		if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
			rdev->wiphy.frag_threshold = frag_threshold;
		if (changed & WIPHY_PARAM_RTS_THRESHOLD)
			rdev->wiphy.rts_threshold = rts_threshold;
		if (changed & WIPHY_PARAM_COVERAGE_CLASS)
			rdev->wiphy.coverage_class = coverage_class;
		if (changed & WIPHY_PARAM_TXQ_LIMIT)
			rdev->wiphy.txq_limit = txq_limit;
		if (changed & WIPHY_PARAM_TXQ_MEMORY_LIMIT)
			rdev->wiphy.txq_memory_limit = txq_memory_limit;
		if (changed & WIPHY_PARAM_TXQ_QUANTUM)
			rdev->wiphy.txq_quantum = txq_quantum;

		result = rdev_set_wiphy_params(rdev, changed);
		if (result) {
			rdev->wiphy.retry_short = old_retry_short;
			rdev->wiphy.retry_long = old_retry_long;
			rdev->wiphy.frag_threshold = old_frag_threshold;
			rdev->wiphy.rts_threshold = old_rts_threshold;
			rdev->wiphy.coverage_class = old_coverage_class;
			rdev->wiphy.txq_limit = old_txq_limit;
			rdev->wiphy.txq_memory_limit = old_txq_memory_limit;
			rdev->wiphy.txq_quantum = old_txq_quantum;
			return result;
		}
	}
	return 0;
}