// SPDX-License-Identifier: GPL-2.0 /* * This file contains helper code to handle channel * settings and keeping track of what is possible at * any point in time. * * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> * Copyright 2013-2014 Intel Mobile Communications GmbH * Copyright 2018-2025 Intel Corporation
*/
/* check if primary channel is punctured */ if (chandef->punctured & (u16)BIT((primary_center - start_freq) / 20)) returnfalse;
for (i = 0; i < per_bw_puncturing[idx].len; i++) { if (per_bw_puncturing[idx].valid_values[i] == chandef->punctured) returntrue;
}
returnfalse;
}
staticbool cfg80211_edmg_chandef_valid(conststruct cfg80211_chan_def *chandef)
{ int max_contiguous = 0; int num_of_enabled = 0; int contiguous = 0; int i;
if (!chandef->edmg.channels || !chandef->edmg.bw_config) returnfalse;
if (!cfg80211_valid_60g_freq(chandef->chan->center_freq)) returnfalse;
for (i = 0; i < 6; i++) { if (chandef->edmg.channels & BIT(i)) {
contiguous++;
num_of_enabled++;
} else {
contiguous = 0;
}
max_contiguous = max(contiguous, max_contiguous);
} /* basic verification of edmg configuration according to * IEEE P802.11ay/D4.0 section 9.4.2.251
*/ /* check bw_config against contiguous edmg channels */ switch (chandef->edmg.bw_config) { case IEEE80211_EDMG_BW_CONFIG_4: case IEEE80211_EDMG_BW_CONFIG_8: case IEEE80211_EDMG_BW_CONFIG_12: if (max_contiguous < 1) returnfalse; break; case IEEE80211_EDMG_BW_CONFIG_5: case IEEE80211_EDMG_BW_CONFIG_9: case IEEE80211_EDMG_BW_CONFIG_13: if (max_contiguous < 2) returnfalse; break; case IEEE80211_EDMG_BW_CONFIG_6: case IEEE80211_EDMG_BW_CONFIG_10: case IEEE80211_EDMG_BW_CONFIG_14: if (max_contiguous < 3) returnfalse; break; case IEEE80211_EDMG_BW_CONFIG_7: case IEEE80211_EDMG_BW_CONFIG_11: case IEEE80211_EDMG_BW_CONFIG_15: if (max_contiguous < 4) returnfalse; break;
default: returnfalse;
}
/* check bw_config against aggregated (non contiguous) edmg channels */ switch (chandef->edmg.bw_config) { case IEEE80211_EDMG_BW_CONFIG_4: case IEEE80211_EDMG_BW_CONFIG_5: case IEEE80211_EDMG_BW_CONFIG_6: case IEEE80211_EDMG_BW_CONFIG_7: break; case IEEE80211_EDMG_BW_CONFIG_8: case IEEE80211_EDMG_BW_CONFIG_9: case IEEE80211_EDMG_BW_CONFIG_10: case IEEE80211_EDMG_BW_CONFIG_11: if (num_of_enabled < 2) returnfalse; break; case IEEE80211_EDMG_BW_CONFIG_12: case IEEE80211_EDMG_BW_CONFIG_13: case IEEE80211_EDMG_BW_CONFIG_14: case IEEE80211_EDMG_BW_CONFIG_15: if (num_of_enabled < 4 || max_contiguous < 2) returnfalse; break; default: returnfalse;
}
returntrue;
}
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; case NL80211_CHAN_WIDTH_320:
mhz = 320; break; default:
WARN_ON_ONCE(1); return -1;
} return mhz;
}
EXPORT_SYMBOL(nl80211_chan_width_to_mhz);
staticbool cfg80211_valid_center_freq(u32 center, enum nl80211_chan_width width)
{ int bw; int step;
/* We only do strict verification on 6 GHz */ if (center < 5955 || center > 7115) returntrue;
bw = nl80211_chan_width_to_mhz(width); if (bw < 0) returnfalse;
/* Validate that the channels bw is entirely within the 6 GHz band */ if (center - bw / 2 < 5945 || center + bw / 2 > 7125) returnfalse;
/* With 320 MHz the permitted channels overlap */ if (bw == 320)
step = 160; else
step = bw;
/* * Valid channels are packed from lowest frequency towards higher ones. * So test that the lower frequency aligns with one of these steps.
*/ return (center - bw / 2 - 5945) % step == 0;
}
switch (chandef->width) { case NL80211_CHAN_WIDTH_5: case NL80211_CHAN_WIDTH_10: case NL80211_CHAN_WIDTH_20: case NL80211_CHAN_WIDTH_20_NOHT: if (ieee80211_chandef_to_khz(chandef) !=
ieee80211_channel_to_khz(chandef->chan)) returnfalse; if (chandef->center_freq2) returnfalse; break; 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: if (chandef->chan->band != NL80211_BAND_S1GHZ) returnfalse;
/* check primary is compatible -> error if not */ if (cfg80211_chandef_primary(c1, primary_chan_width, &punct_c1) !=
cfg80211_chandef_primary(c2, primary_chan_width, &punct_c2)) return ERR_PTR(-EINVAL);
if (punct_c1 != punct_c2) return ERR_PTR(-EINVAL);
/* assumes c1 is smaller width, if that was just checked -> done */ if (c1->width == primary_chan_width) return c2;
/* otherwise continue checking the next width */ return NULL;
}
/* If they are identical, return */ if (cfg80211_chandef_identical(c1, c2)) return c2;
/* otherwise, must have same control channel */ if (c1->chan != c2->chan) return NULL;
/* * If they have the same width, but aren't identical, * then they can't be compatible.
*/ if (c1->width == c2->width) return NULL;
/* * can't be compatible if one of them is 5/10 MHz or S1G * but they don't have the same width.
*/ #define NARROW_OR_S1G(width) ((width) == NL80211_CHAN_WIDTH_5 || \
(width) == NL80211_CHAN_WIDTH_10 || \
(width) == NL80211_CHAN_WIDTH_1 || \
(width) == NL80211_CHAN_WIDTH_2 || \
(width) == NL80211_CHAN_WIDTH_4 || \
(width) == NL80211_CHAN_WIDTH_8 || \
(width) == NL80211_CHAN_WIDTH_16)
if (NARROW_OR_S1G(c1->width) || NARROW_OR_S1G(c2->width)) return NULL;
/* * Make sure that c1 is always the narrower one, so that later * we either return NULL or c2 and don't have to check both * directions.
*/ if (c1->width > c2->width)
swap(c1, c2);
/* * No further checks needed if the "narrower" one is only 20 MHz. * Here "narrower" includes being a 20 MHz non-HT channel vs. a * 20 MHz HT (or later) one.
*/ if (c1->width <= NL80211_CHAN_WIDTH_20) return c2;
ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_40); if (ret) return ret;
ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_80); if (ret) return ret;
/* * If c1 is 80+80, then c2 is 160 or higher, but that cannot * match. If c2 was also 80+80 it was already either accepted * or rejected above (identical or not, respectively.)
*/ if (c1->width == NL80211_CHAN_WIDTH_80P80) return NULL;
ret = check_chandef_primary_compat(c1, c2, NL80211_CHAN_WIDTH_160); if (ret) return ret;
/* * Getting here would mean they're both wider than 160, have the * same primary 160, but are not identical - this cannot happen * since they must be 320 (no wider chandefs exist, at least yet.)
*/
WARN_ON_ONCE(1);
/* In order to avoid daisy chaining only allow BSS STA */ if (wdev->iftype != NL80211_IFTYPE_STATION ||
!wdev->links[link_id].client.current_bss) continue;
int cfg80211_chandef_dfs_required(struct wiphy *wiphy, conststruct cfg80211_chan_def *chandef, enum nl80211_iftype iftype)
{ int width; int ret;
if (WARN_ON(!cfg80211_chandef_valid(chandef))) return -EINVAL;
switch (iftype) { case NL80211_IFTYPE_ADHOC: case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO: case NL80211_IFTYPE_MESH_POINT:
width = cfg80211_chandef_get_width(chandef); if (width < 0) return -EINVAL;
ret = cfg80211_get_chans_dfs_required(wiphy, chandef, iftype);
return (ret > 0) ? BIT(chandef->width) : ret; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: case NL80211_IFTYPE_NAN: break; case NL80211_IFTYPE_WDS: case NL80211_IFTYPE_UNSPECIFIED: case NUM_NL80211_IFTYPES:
WARN_ON(1);
}
if (WARN_ON(!cfg80211_chandef_valid(chandef))) returnfalse;
width = cfg80211_chandef_get_width(chandef); if (width < 0) returnfalse;
/* * Check entire range of channels for the bandwidth. * Check all channels are DFS channels (DFS_USABLE or * DFS_AVAILABLE). Return number of usable channels * (require CAC). Allow DFS and non-DFS channel mix.
*/
for_each_subchan(chandef, freq, cf) {
c = ieee80211_get_channel_khz(wiphy, freq); if (!c) returnfalse;
if (c->flags & IEEE80211_CHAN_DISABLED) returnfalse;
if (c->flags & IEEE80211_CHAN_RADAR) { if (c->dfs_state == NL80211_DFS_UNAVAILABLE) returnfalse;
if (c->dfs_state == NL80211_DFS_USABLE)
count++;
}
}
/* * Checks if center frequency of chan falls with in the bandwidth * range of chandef.
*/ bool cfg80211_is_sub_chan(struct cfg80211_chan_def *chandef, struct ieee80211_channel *chan, bool primary_only)
{ int width;
u32 freq;
if (!chandef->chan) returnfalse;
if (chandef->chan->center_freq == chan->center_freq) returntrue;
if (primary_only) returnfalse;
width = cfg80211_chandef_get_width(chandef); if (width <= 20) returnfalse;
switch (wdev->iftype) { case NL80211_IFTYPE_AP: case NL80211_IFTYPE_P2P_GO:
for_each_valid_link(wdev, link) { if (wdev->links[link].ap.beacon_interval) returntrue;
} break; case NL80211_IFTYPE_ADHOC: if (wdev->u.ibss.ssid_len) returntrue; break; case NL80211_IFTYPE_MESH_POINT: if (wdev->u.mesh.id_len) returntrue; break; case NL80211_IFTYPE_STATION: case NL80211_IFTYPE_OCB: case NL80211_IFTYPE_P2P_CLIENT: case NL80211_IFTYPE_MONITOR: case NL80211_IFTYPE_AP_VLAN: case NL80211_IFTYPE_P2P_DEVICE: /* Can NAN type be considered as beaconing interface? */ case NL80211_IFTYPE_NAN: break; case NL80211_IFTYPE_UNSPECIFIED: case NL80211_IFTYPE_WDS: case NUM_NL80211_IFTYPES:
WARN_ON(1);
}
/* * Check entire range of channels for the bandwidth. * If any channel in between is disabled or has not * had gone through CAC return false
*/
for_each_subchan(chandef, freq, cf) {
c = ieee80211_get_channel_khz(wiphy, freq); if (!c) returnfalse;
if (c->flags & IEEE80211_CHAN_DISABLED) returnfalse;
/* check if the operating channels are valid and supported */ staticbool cfg80211_edmg_usable(struct wiphy *wiphy, u8 edmg_channels, enum ieee80211_edmg_bw_config edmg_bw_config, int primary_channel, struct ieee80211_edmg *edmg_cap)
{ struct ieee80211_channel *chan; int i, freq; int channels_counter = 0;
if (!edmg_channels && !edmg_bw_config) returntrue;
if ((!edmg_channels && edmg_bw_config) ||
(edmg_channels && !edmg_bw_config)) returnfalse;
if (!(edmg_channels & BIT(primary_channel - 1))) returnfalse;
/* 60GHz channels 1..6 */ for (i = 0; i < 6; i++) { if (!(edmg_channels & BIT(i))) continue;
/* IEEE802.11 allows max 4 channels */ if (channels_counter > 4) returnfalse;
/* check bw_config is a subset of what driver supports * (see IEEE P802.11ay/D4.0 section 9.4.2.251, Table 13)
*/ if ((edmg_bw_config % 4) > (edmg_cap->bw_config % 4)) returnfalse;
if (edmg_bw_config > edmg_cap->bw_config) returnfalse;
if (!support_320) returnfalse; break; default:
WARN_ON_ONCE(1); returnfalse;
}
/* * TODO: What if there are only certain 80/160/80+80 MHz channels * allowed by the driver, or only certain combinations? * For 40 MHz the driver can set the NO_HT40 flags, but for * 80/160 MHz and in particular 80+80 MHz this isn't really * feasible and we only have NO_80MHZ/NO_160MHZ so far but * no way to cover 80+80 MHz or more complex restrictions. * Note that such restrictions also need to be advertised to * userspace, for example for P2P channel selection.
*/
if (width > 20)
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
/* 5 and 10 MHz are only defined for the OFDM PHY */ if (width < 20)
prohibited_flags |= IEEE80211_CHAN_NO_OFDM;
for_each_subchan(chandef, freq, cf) {
c = ieee80211_get_channel_khz(wiphy, freq); if (!c) returnfalse; if (c->flags & permitting_flags) continue; if (c->flags & prohibited_flags) returnfalse;
}
/* * If a GO already operates on the same GO_CONCURRENT channel, * this one (maybe the same one) can beacon as well. We allow * the operation even if the station we relied on with * GO_CONCURRENT is disconnected now. But then we must make sure * we're not outdoor on an indoor-only channel.
*/ if (iftype == NL80211_IFTYPE_P2P_GO &&
wdev->iftype == NL80211_IFTYPE_P2P_GO &&
wdev->links[link_id].ap.beacon_interval &&
!(chan->flags & IEEE80211_CHAN_INDOOR_ONLY))
other_chan = wdev->links[link_id].ap.chandef.chan;
if (!other_chan) continue;
if (chan == other_chan) returntrue;
if (chan->band != NL80211_BAND_5GHZ &&
chan->band != NL80211_BAND_6GHZ) continue;
if (r1 != -EINVAL && r1 == r2) { /* * At some locations channels 149-165 are considered a * bundle, but at other locations, e.g., Indonesia, * channels 149-161 are considered a bundle while * channel 165 is left out and considered to be in a * different bundle. Thus, in case that there is a * station interface connected to an AP on channel 165, * it is assumed that channels 149-161 are allowed for * GO operations. However, having a station interface * connected to an AP on channels 149-161, does not * allow GO operation on channel 165.
*/ if (chan->center_freq == 5825 &&
other_chan->center_freq != 5825) continue; returntrue;
}
}
returnfalse;
}
/* * Check if the channel can be used under permissive conditions mandated by * some regulatory bodies, i.e., the channel is marked with * IEEE80211_CHAN_IR_CONCURRENT and there is an additional station interface * associated to an AP on the same channel or on the same UNII band * (assuming that the AP is an authorized master). * In addition allow operation on a channel on which indoor operation is * allowed, iff we are currently operating in an indoor environment.
*/ staticbool cfg80211_ir_permissive_chan(struct wiphy *wiphy, enum nl80211_iftype iftype, struct ieee80211_channel *chan)
{ struct wireless_dev *wdev; struct cfg80211_registered_device *rdev = wiphy_to_rdev(wiphy);
lockdep_assert_held(&rdev->wiphy.mtx);
if (!IS_ENABLED(CONFIG_CFG80211_REG_RELAX_NO_IR) ||
!(wiphy->regulatory_flags & REGULATORY_ENABLE_RELAX_NO_IR)) returnfalse;
/* only valid for GO and TDLS off-channel (station/p2p-CL) */ if (iftype != NL80211_IFTYPE_P2P_GO &&
iftype != NL80211_IFTYPE_STATION &&
iftype != NL80211_IFTYPE_P2P_CLIENT) returnfalse;
if (regulatory_indoor_allowed() &&
(chan->flags & IEEE80211_CHAN_INDOOR_ONLY)) returntrue;
if (!(chan->flags & IEEE80211_CHAN_IR_CONCURRENT)) returnfalse;
/* * Generally, it is possible to rely on another device/driver to allow * the IR concurrent relaxation, however, since the device can further * enforce the relaxation (by doing a similar verifications as this), * and thus fail the GO instantiation, consider only the interfaces of * the current registered device.
*/
list_for_each_entry(wdev, &rdev->wiphy.wdev_list, list) { bool ret;
ret = cfg80211_ir_permissive_check_wdev(iftype, wdev, chan); if (ret) return ret;
}
if (dfs_required > 0 &&
cfg80211_chandef_dfs_available(wiphy, chandef)) { /* We can skip IEEE80211_CHAN_NO_IR if chandef dfs available */
prohibited_flags &= ~IEEE80211_CHAN_NO_IR;
check_radar = false;
}
if (check_radar &&
!_cfg80211_chandef_usable(wiphy, chandef,
IEEE80211_CHAN_RADAR, 0)) returnfalse;
res = _cfg80211_chandef_usable(wiphy, chandef,
prohibited_flags,
permitting_flags);
/* * Under certain conditions suggested by some regulatory bodies a * GO/STA can IR on channels marked with IEEE80211_NO_IR. Set this flag * only if such relaxations are not enabled and the conditions are not * met.
*/ if (cfg->relax) {
lockdep_assert_held(&rdev->wiphy.mtx);
check_no_ir = !cfg80211_ir_permissive_chan(wiphy, cfg->iftype,
chandef->chan);
}
if (cfg->reg_power == IEEE80211_REG_VLP_AP)
permitting_flags |= IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.