Commit 11b34737 authored by Thomas Pedersen's avatar Thomas Pedersen Committed by Johannes Berg
Browse files

nl80211: support setting S1G channels



S1G channels have a single width defined per frequency, so
derive it from the channel flags with
ieee80211_s1g_channel_width().

Also support setting an S1G channel where control frequency may
differ from operating, and add some basic validation to
ensure the control channel is with the operating.

Signed-off-by: default avatarThomas Pedersen <thomas@adapt-ip.com>
Link: https://lore.kernel.org/r/20200908190323.15814-6-thomas@adapt-ip.com


Signed-off-by: default avatarJohannes Berg <johannes.berg@intel.com>
parent 1d47f119
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -5294,6 +5294,16 @@ ieee80211_channel_to_khz(const struct ieee80211_channel *chan)
	return MHZ_TO_KHZ(chan->center_freq) + chan->freq_offset;
}

/**
 * ieee80211_s1g_channel_width - get allowed channel width from @chan
 *
 * Only allowed for band NL80211_BAND_S1GHZ
 * @chan: channel
 * Return: The allowed channel width for this center_freq
 */
enum nl80211_chan_width
ieee80211_s1g_channel_width(const struct ieee80211_channel *chan);

/**
 * ieee80211_channel_to_freq_khz - convert channel number to frequency
 * @chan: channel number
+78 −52
Original line number Diff line number Diff line
@@ -141,9 +141,62 @@ static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
	return true;
}

static int nl80211_chan_width_to_mhz(enum nl80211_chan_width chan_width)
{
	int mhz;

	switch (chan_width) {
	case NL80211_CHAN_WIDTH_1:
		mhz = 1;
		break;
	case NL80211_CHAN_WIDTH_2:
		mhz = 2;
		break;
	case NL80211_CHAN_WIDTH_4:
		mhz = 4;
		break;
	case NL80211_CHAN_WIDTH_8:
		mhz = 8;
		break;
	case NL80211_CHAN_WIDTH_16:
		mhz = 16;
		break;
	case NL80211_CHAN_WIDTH_5:
		mhz = 5;
		break;
	case NL80211_CHAN_WIDTH_10:
		mhz = 10;
		break;
	case NL80211_CHAN_WIDTH_20:
	case NL80211_CHAN_WIDTH_20_NOHT:
		mhz = 20;
		break;
	case NL80211_CHAN_WIDTH_40:
		mhz = 40;
		break;
	case NL80211_CHAN_WIDTH_80P80:
	case NL80211_CHAN_WIDTH_80:
		mhz = 80;
		break;
	case NL80211_CHAN_WIDTH_160:
		mhz = 160;
		break;
	default:
		WARN_ON_ONCE(1);
		return -1;
	}
	return mhz;
}

static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
{
	return nl80211_chan_width_to_mhz(c->width);
}

bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
{
	u32 control_freq;
	u32 control_freq, oper_freq;
	int oper_width, control_width;

	if (!chandef->chan)
		return false;
@@ -155,10 +208,6 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)

	switch (chandef->width) {
	case NL80211_CHAN_WIDTH_1:
	case NL80211_CHAN_WIDTH_2:
	case NL80211_CHAN_WIDTH_4:
	case NL80211_CHAN_WIDTH_8:
	case NL80211_CHAN_WIDTH_16:
	case NL80211_CHAN_WIDTH_5:
	case NL80211_CHAN_WIDTH_10:
	case NL80211_CHAN_WIDTH_20:
@@ -169,6 +218,30 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
		if (chandef->center_freq2)
			return false;
		break;
	case NL80211_CHAN_WIDTH_2:
	case NL80211_CHAN_WIDTH_4:
	case NL80211_CHAN_WIDTH_8:
	case NL80211_CHAN_WIDTH_16:
		control_freq = ieee80211_channel_to_khz(chandef->chan);
		oper_freq = ieee80211_chandef_to_khz(chandef);
		control_width = nl80211_chan_width_to_mhz(
					ieee80211_s1g_channel_width(
								chandef->chan));
		oper_width = cfg80211_chandef_get_width(chandef);

		if (oper_width < 0 || control_width < 0)
			return false;
		if (chandef->center_freq2)
			return false;

		if (control_freq + MHZ_TO_KHZ(control_width) / 2 >
		    oper_freq + MHZ_TO_KHZ(oper_width) / 2)
			return false;

		if (control_freq - MHZ_TO_KHZ(control_width) / 2 <
		    oper_freq - MHZ_TO_KHZ(oper_width) / 2)
			return false;
		break;
	case NL80211_CHAN_WIDTH_40:
		if (chandef->center_freq1 != control_freq + 10 &&
		    chandef->center_freq1 != control_freq - 10)
@@ -264,53 +337,6 @@ static void chandef_primary_freqs(const struct cfg80211_chan_def *c,
	}
}

static int cfg80211_chandef_get_width(const struct cfg80211_chan_def *c)
{
	int width;

	switch (c->width) {
	case NL80211_CHAN_WIDTH_1:
		width = 1;
		break;
	case NL80211_CHAN_WIDTH_2:
		width = 2;
		break;
	case NL80211_CHAN_WIDTH_4:
		width = 4;
		break;
	case NL80211_CHAN_WIDTH_8:
		width = 8;
		break;
	case NL80211_CHAN_WIDTH_16:
		width = 16;
		break;
	case NL80211_CHAN_WIDTH_5:
		width = 5;
		break;
	case NL80211_CHAN_WIDTH_10:
		width = 10;
		break;
	case NL80211_CHAN_WIDTH_20:
	case NL80211_CHAN_WIDTH_20_NOHT:
		width = 20;
		break;
	case NL80211_CHAN_WIDTH_40:
		width = 40;
		break;
	case NL80211_CHAN_WIDTH_80P80:
	case NL80211_CHAN_WIDTH_80:
		width = 80;
		break;
	case NL80211_CHAN_WIDTH_160:
		width = 160;
		break;
	default:
		WARN_ON_ONCE(1);
		return -1;
	}
	return width;
}

const struct cfg80211_chan_def *
cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
			    const struct cfg80211_chan_def *c2)
+27 −0
Original line number Diff line number Diff line
@@ -111,6 +111,33 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
}
EXPORT_SYMBOL(ieee80211_channel_to_freq_khz);

enum nl80211_chan_width
ieee80211_s1g_channel_width(const struct ieee80211_channel *chan)
{
	if (WARN_ON(!chan || chan->band != NL80211_BAND_S1GHZ))
		return NL80211_CHAN_WIDTH_20_NOHT;

	/*S1G defines a single allowed channel width per channel.
	 * Extract that width here.
	 */
	if (chan->flags & IEEE80211_CHAN_1MHZ)
		return NL80211_CHAN_WIDTH_1;
	else if (chan->flags & IEEE80211_CHAN_2MHZ)
		return NL80211_CHAN_WIDTH_2;
	else if (chan->flags & IEEE80211_CHAN_4MHZ)
		return NL80211_CHAN_WIDTH_4;
	else if (chan->flags & IEEE80211_CHAN_8MHZ)
		return NL80211_CHAN_WIDTH_8;
	else if (chan->flags & IEEE80211_CHAN_16MHZ)
		return NL80211_CHAN_WIDTH_16;

	pr_err("unknown channel width for channel at %dKHz?\n",
	       ieee80211_channel_to_khz(chan));

	return NL80211_CHAN_WIDTH_1;
}
EXPORT_SYMBOL(ieee80211_s1g_channel_width);

int ieee80211_freq_khz_to_channel(u32 freq)
{
	/* TODO: just handle MHz for now */