static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
					 struct cfg80211_bitrate_mask *mask)
{
	struct nlattr *tb[NL80211_TXRATE_MAX + 1];
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
	int rem, i;
	struct nlattr *tx_rates;
	struct ieee80211_supported_band *sband;
	u16 vht_tx_mcs_map;

	memset(mask, 0, sizeof(*mask));
	/* Default to all rates enabled */
	for (i = 0; i < NUM_NL80211_BANDS; i++) {
		sband = rdev->wiphy.bands[i];

		if (!sband)
			continue;

		mask->control[i].legacy = (1 << sband->n_bitrates) - 1;
		memcpy(mask->control[i].ht_mcs,
		       sband->ht_cap.mcs.rx_mask,
		       sizeof(mask->control[i].ht_mcs));

		if (!sband->vht_cap.vht_supported)
			continue;

		vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
		vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
	}

	/* if no rates are given set it back to the defaults */
	if (!info->attrs[NL80211_ATTR_TX_RATES])
		goto out;

	/* The nested attribute uses enum nl80211_band as the index. This maps
	 * directly to the enum nl80211_band values used in cfg80211.
	 */
	BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
	nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
		enum nl80211_band band = nla_type(tx_rates);
		int err;

		if (band < 0 || band >= NUM_NL80211_BANDS)
			return -EINVAL;
		sband = rdev->wiphy.bands[band];
		if (sband == NULL)
			return -EINVAL;
		err = nla_parse_nested_deprecated(tb, NL80211_TXRATE_MAX,
						  tx_rates,
						  nl80211_txattr_policy,
						  info->extack);
		if (err)
			return err;
		if (tb[NL80211_TXRATE_LEGACY]) {
			mask->control[band].legacy = rateset_to_mask(
				sband,
				nla_data(tb[NL80211_TXRATE_LEGACY]),
				nla_len(tb[NL80211_TXRATE_LEGACY]));
			if ((mask->control[band].legacy == 0) &&
			    nla_len(tb[NL80211_TXRATE_LEGACY]))
				return -EINVAL;
		}
		if (tb[NL80211_TXRATE_HT]) {
			if (!ht_rateset_to_mask(
					sband,
					nla_data(tb[NL80211_TXRATE_HT]),
					nla_len(tb[NL80211_TXRATE_HT]),
					mask->control[band].ht_mcs))
				return -EINVAL;
		}
		if (tb[NL80211_TXRATE_VHT]) {
			if (!vht_set_mcs_mask(
					sband,
					nla_data(tb[NL80211_TXRATE_VHT]),
					mask->control[band].vht_mcs))
				return -EINVAL;
		}
		if (tb[NL80211_TXRATE_GI]) {
			mask->control[band].gi =
				nla_get_u8(tb[NL80211_TXRATE_GI]);
			if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI)
				return -EINVAL;
		}

		if (mask->control[band].legacy == 0) {
			/* don't allow empty legacy rates if HT or VHT
			 * are not even supported.
			 */
			if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
			      rdev->wiphy.bands[band]->vht_cap.vht_supported))
				return -EINVAL;

			for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
				if (mask->control[band].ht_mcs[i])
					goto out;

			for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
				if (mask->control[band].vht_mcs[i])
					goto out;

			/* legacy and mcs rates may not be both empty */
			return -EINVAL;
		}
	}

out:
	return 0;
}
static int nl80211_parse_tx_bitrate_mask(struct genl_info *info,
					 struct cfg80211_bitrate_mask *mask)
{
	struct nlattr *tb[NL80211_TXRATE_MAX + 1];
	struct cfg80211_registered_device *rdev = info->user_ptr[0];
	int rem, i;
	struct nlattr *tx_rates;
	struct ieee80211_supported_band *sband;
	u16 vht_tx_mcs_map;

	memset(mask, 0, sizeof(*mask));
	 
	for (i = 0; i < NUM_NL80211_BANDS; i++) {
		sband = rdev->wiphy.bands[i];

		if (!sband)
			continue;

		mask->control[i].legacy = (1 << sband->n_bitrates) - 1;
		memcpy(mask->control[i].ht_mcs,
		       sband->ht_cap.mcs.rx_mask,
		       sizeof(mask->control[i].ht_mcs));

		if (!sband->vht_cap.vht_supported)
			continue;

		vht_tx_mcs_map = le16_to_cpu(sband->vht_cap.vht_mcs.tx_mcs_map);
		vht_build_mcs_mask(vht_tx_mcs_map, mask->control[i].vht_mcs);
	}

	 
	if (!info->attrs[NL80211_ATTR_TX_RATES])
		goto out;

	 
	BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
	nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem) {
		enum nl80211_band band = nla_type(tx_rates);
		int err;

		if (band < 0 || band >= NUM_NL80211_BANDS)
			return -EINVAL;
		sband = rdev->wiphy.bands[band];
		if (sband == NULL)
			return -EINVAL;
		err = nla_parse_nested_deprecated(tb, NL80211_TXRATE_MAX,
						  tx_rates,
						  nl80211_txattr_policy,
						  info->extack);
		if (err)
			return err;
		if (tb[NL80211_TXRATE_LEGACY]) {
			mask->control[band].legacy = rateset_to_mask(
				sband,
				nla_data(tb[NL80211_TXRATE_LEGACY]),
				nla_len(tb[NL80211_TXRATE_LEGACY]));
			if ((mask->control[band].legacy == 0) &&
			    nla_len(tb[NL80211_TXRATE_LEGACY]))
				return -EINVAL;
		}
		if (tb[NL80211_TXRATE_HT]) {
			if (!ht_rateset_to_mask(
					sband,
					nla_data(tb[NL80211_TXRATE_HT]),
					nla_len(tb[NL80211_TXRATE_HT]),
					mask->control[band].ht_mcs))
				return -EINVAL;
		}
		if (tb[NL80211_TXRATE_VHT]) {
			if (!vht_set_mcs_mask(
					sband,
					nla_data(tb[NL80211_TXRATE_VHT]),
					mask->control[band].vht_mcs))
				return -EINVAL;
		}
		if (tb[NL80211_TXRATE_GI]) {
			mask->control[band].gi =
				nla_get_u8(tb[NL80211_TXRATE_GI]);
			if (mask->control[band].gi > NL80211_TXRATE_FORCE_LGI)
				return -EINVAL;
		}

		if (mask->control[band].legacy == 0) {
			 
			if (!(rdev->wiphy.bands[band]->ht_cap.ht_supported ||
			      rdev->wiphy.bands[band]->vht_cap.vht_supported))
				return -EINVAL;

			for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
				if (mask->control[band].ht_mcs[i])
					goto out;

			for (i = 0; i < NL80211_VHT_NSS_MAX; i++)
				if (mask->control[band].vht_mcs[i])
					goto out;

			 
			return -EINVAL;
		}
	}

out:
	return 0;
}