forked from mirrors/linux
		
	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: Thomas Pedersen <thomas@adapt-ip.com> Link: https://lore.kernel.org/r/20200908190323.15814-6-thomas@adapt-ip.com Signed-off-by: Johannes Berg <johannes.berg@intel.com>
This commit is contained in:
		
							parent
							
								
									1d47f1198d
								
							
						
					
					
						commit
						11b34737b1
					
				
					 3 changed files with 115 additions and 52 deletions
				
			
		| 
						 | 
					@ -5294,6 +5294,16 @@ ieee80211_channel_to_khz(const struct ieee80211_channel *chan)
 | 
				
			||||||
	return MHZ_TO_KHZ(chan->center_freq) + chan->freq_offset;
 | 
						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
 | 
					 * ieee80211_channel_to_freq_khz - convert channel number to frequency
 | 
				
			||||||
 * @chan: channel number
 | 
					 * @chan: channel number
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -141,9 +141,62 @@ static bool cfg80211_edmg_chandef_valid(const struct cfg80211_chan_def *chandef)
 | 
				
			||||||
	return true;
 | 
						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)
 | 
					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)
 | 
						if (!chandef->chan)
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
| 
						 | 
					@ -155,10 +208,6 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (chandef->width) {
 | 
						switch (chandef->width) {
 | 
				
			||||||
	case NL80211_CHAN_WIDTH_1:
 | 
						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_5:
 | 
				
			||||||
	case NL80211_CHAN_WIDTH_10:
 | 
						case NL80211_CHAN_WIDTH_10:
 | 
				
			||||||
	case NL80211_CHAN_WIDTH_20:
 | 
						case NL80211_CHAN_WIDTH_20:
 | 
				
			||||||
| 
						 | 
					@ -169,6 +218,30 @@ bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef)
 | 
				
			||||||
		if (chandef->center_freq2)
 | 
							if (chandef->center_freq2)
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		break;
 | 
							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:
 | 
						case NL80211_CHAN_WIDTH_40:
 | 
				
			||||||
		if (chandef->center_freq1 != control_freq + 10 &&
 | 
							if (chandef->center_freq1 != control_freq + 10 &&
 | 
				
			||||||
		    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 *
 | 
					const struct cfg80211_chan_def *
 | 
				
			||||||
cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
 | 
					cfg80211_chandef_compatible(const struct cfg80211_chan_def *c1,
 | 
				
			||||||
			    const struct cfg80211_chan_def *c2)
 | 
								    const struct cfg80211_chan_def *c2)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -111,6 +111,33 @@ u32 ieee80211_channel_to_freq_khz(int chan, enum nl80211_band band)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
EXPORT_SYMBOL(ieee80211_channel_to_freq_khz);
 | 
					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)
 | 
					int ieee80211_freq_khz_to_channel(u32 freq)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	/* TODO: just handle MHz for now */
 | 
						/* TODO: just handle MHz for now */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue