Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

wifi: cfg80211: correctly implement and validate S1G chandef

Currently, the S1G channelisation implementation differs from that of
VHT, which is the PHY that S1G is based on. The major difference between
the clock rate is 1/10th of VHT. However how their channelisation is
represented within cfg80211 and mac80211 vastly differ.

To rectify this, remove the use of IEEE80211_CHAN_1/2/4.. flags that were
previously used to indicate the control channel width, however it should be
implied that the control channels are 1MHz in the case of S1G. Additionally,
introduce the invert - being IEEE80211_CHAN_NO_4/8/16MHz - that imply
the control channel may not be used for a certain bandwidth. With these
new flags, we can perform regulatory and chandef validation just as we would
for VHT.

To deal with the notion that S1G PHYs may contain a 2MHz primary channel,
introduce a new variable, s1g_primary_2mhz, which indicates whether we are
operating on a 2MHz primary channel. In this case, the chandef::chan points to
the 1MHz primary channel pointed to by the primary channel location. Alongside
this, introduce some new helper routines that can extract the sibling 1MHz
channel. The sibling being the alternate 1MHz primary subchannel within the
2MHz primary channel that is not pointed to by chandef::chan.

Furthermore, due to unique restrictions imposed on S1G PHYs, introduce
a new flag, IEEE80211_CHAN_S1G_NO_PRIMARY, which states that the 1MHz channel
cannot be used as a primary channel. This is assumed to be set by vendors
as it is hardware and regdom specific, When we validate a 2MHz primary channel,
we need to ensure both 1MHz subchannels do not contain this flag. If one or
both of the 1MHz subchannels contain this flag then the 2MHz primary is not
permitted for use as a primary channel.

Properly integrate S1G channel validation such that it is implemented
according with other PHY types such as VHT. Additionally, implement a new
S1G-specific regulatory flag to allow cfg80211 to understand specific
vendor requirements for S1G PHYs.

Signed-off-by: Arien Judge <arien.judge@morsemicro.com>
Signed-off-by: Andrew Pope <andrew.pope@morsemicro.com>
Signed-off-by: Lachlan Hodges <lachlan.hodges@morsemicro.com>
Link: https://patch.msgid.link/20250918051913.500781-2-lachlan.hodges@morsemicro.com
[remove redundant NL80211_ATTR_S1G_PRIMARY_2MHZ check]
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

authored by

Lachlan Hodges and committed by
Johannes Berg
d0688dc2 ccdc96fa

+224 -100
+95
include/net/cfg80211.h
··· 129 129 * with very low power (VLP), even if otherwise set to NO_IR. 130 130 * @IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY: Allow activity on a 20 MHz channel, 131 131 * even if otherwise set to NO_IR. 132 + * @IEEE80211_CHAN_S1G_NO_PRIMARY: Prevents the channel for use as an S1G 133 + * primary channel. Does not prevent the wider operating channel 134 + * described by the chandef from being used. In order for a 2MHz primary 135 + * to be used, both 1MHz subchannels shall not contain this flag. 136 + * @IEEE80211_CHAN_NO_4MHZ: 4 MHz bandwidth is not permitted on this channel. 137 + * @IEEE80211_CHAN_NO_8MHZ: 8 MHz bandwidth is not permitted on this channel. 138 + * @IEEE80211_CHAN_NO_16MHZ: 16 MHz bandwidth is not permitted on this channel. 132 139 */ 133 140 enum ieee80211_channel_flags { 134 141 IEEE80211_CHAN_DISABLED = BIT(0), ··· 165 158 IEEE80211_CHAN_CAN_MONITOR = BIT(24), 166 159 IEEE80211_CHAN_ALLOW_6GHZ_VLP_AP = BIT(25), 167 160 IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY = BIT(26), 161 + IEEE80211_CHAN_S1G_NO_PRIMARY = BIT(27), 162 + IEEE80211_CHAN_NO_4MHZ = BIT(28), 163 + IEEE80211_CHAN_NO_8MHZ = BIT(29), 164 + IEEE80211_CHAN_NO_16MHZ = BIT(30), 168 165 }; 169 166 170 167 #define IEEE80211_CHAN_NO_HT40 \ ··· 832 821 * @punctured: mask of the punctured 20 MHz subchannels, with 833 822 * bits turned on being disabled (punctured); numbered 834 823 * from lower to higher frequency (like in the spec) 824 + * @s1g_primary_2mhz: Indicates if the control channel pointed to 825 + * by 'chan' exists as a 1MHz primary subchannel within an 826 + * S1G 2MHz primary channel. 835 827 */ 836 828 struct cfg80211_chan_def { 837 829 struct ieee80211_channel *chan; ··· 844 830 struct ieee80211_edmg edmg; 845 831 u16 freq1_offset; 846 832 u16 punctured; 833 + bool s1g_primary_2mhz; 847 834 }; 848 835 849 836 /* ··· 1003 988 cfg80211_chandef_is_edmg(const struct cfg80211_chan_def *chandef) 1004 989 { 1005 990 return chandef->edmg.channels || chandef->edmg.bw_config; 991 + } 992 + 993 + /** 994 + * cfg80211_chandef_is_s1g - check if chandef represents an S1G channel 995 + * @chandef: the channel definition 996 + * 997 + * Return: %true if S1G. 998 + */ 999 + static inline bool 1000 + cfg80211_chandef_is_s1g(const struct cfg80211_chan_def *chandef) 1001 + { 1002 + return chandef->chan->band == NL80211_BAND_S1GHZ; 1006 1003 } 1007 1004 1008 1005 /** ··· 10205 10178 void *data), 10206 10179 void *data); 10207 10180 #endif 10181 + 10182 + /** 10183 + * cfg80211_s1g_get_start_freq_khz - get S1G chandef start frequency 10184 + * @chandef: the chandef to use 10185 + * 10186 + * Return: the chandefs starting frequency in KHz 10187 + */ 10188 + static inline u32 10189 + cfg80211_s1g_get_start_freq_khz(const struct cfg80211_chan_def *chandef) 10190 + { 10191 + u32 bw_mhz = cfg80211_chandef_get_width(chandef); 10192 + u32 center_khz = 10193 + MHZ_TO_KHZ(chandef->center_freq1) + chandef->freq1_offset; 10194 + return center_khz - bw_mhz * 500 + 500; 10195 + } 10196 + 10197 + /** 10198 + * cfg80211_s1g_get_end_freq_khz - get S1G chandef end frequency 10199 + * @chandef: the chandef to use 10200 + * 10201 + * Return: the chandefs ending frequency in KHz 10202 + */ 10203 + static inline u32 10204 + cfg80211_s1g_get_end_freq_khz(const struct cfg80211_chan_def *chandef) 10205 + { 10206 + u32 bw_mhz = cfg80211_chandef_get_width(chandef); 10207 + u32 center_khz = 10208 + MHZ_TO_KHZ(chandef->center_freq1) + chandef->freq1_offset; 10209 + return center_khz + bw_mhz * 500 - 500; 10210 + } 10211 + 10212 + /** 10213 + * cfg80211_s1g_get_primary_sibling - retrieve the sibling 1MHz subchannel 10214 + * for an S1G chandef using a 2MHz primary channel. 10215 + * @wiphy: wiphy the channel belongs to 10216 + * @chandef: the chandef to use 10217 + * 10218 + * When chandef::s1g_primary_2mhz is set to true, we are operating on a 2MHz 10219 + * primary channel. The 1MHz subchannel designated by the primary channel 10220 + * location exists within chandef::chan, whilst the 'sibling' is denoted as 10221 + * being the other 1MHz subchannel that make up the 2MHz primary channel. 10222 + * 10223 + * Returns: the sibling 1MHz &struct ieee80211_channel, or %NULL on failure. 10224 + */ 10225 + static inline struct ieee80211_channel * 10226 + cfg80211_s1g_get_primary_sibling(struct wiphy *wiphy, 10227 + const struct cfg80211_chan_def *chandef) 10228 + { 10229 + int width_mhz = cfg80211_chandef_get_width(chandef); 10230 + u32 pri_1mhz_khz, sibling_1mhz_khz, op_low_1mhz_khz, pri_index; 10231 + 10232 + if (!chandef->s1g_primary_2mhz || width_mhz < 2) 10233 + return NULL; 10234 + 10235 + pri_1mhz_khz = ieee80211_channel_to_khz(chandef->chan); 10236 + op_low_1mhz_khz = cfg80211_s1g_get_start_freq_khz(chandef); 10237 + 10238 + /* 10239 + * Compute the index of the primary 1 MHz subchannel within the 10240 + * operating channel, relative to the lowest 1 MHz center frequency. 10241 + * Flip the least significant bit to select the even/odd sibling, 10242 + * then translate that index back into a channel frequency. 10243 + */ 10244 + pri_index = (pri_1mhz_khz - op_low_1mhz_khz) / 1000; 10245 + sibling_1mhz_khz = op_low_1mhz_khz + ((pri_index ^ 1) * 1000); 10246 + 10247 + return ieee80211_get_channel_khz(wiphy, sibling_1mhz_khz); 10248 + } 10208 10249 10209 10250 #endif /* __NET_CFG80211_H */
+15
include/uapi/linux/nl80211.h
··· 2969 2969 * capabilities supported by the driver. See &enum nl80211_nan_capabilities 2970 2970 * for details. 2971 2971 * 2972 + * @NL80211_ATTR_S1G_PRIMARY_2MHZ: flag attribute indicating that the S1G 2973 + * primary channel is 2 MHz wide, and the control channel designates 2974 + * the 1 MHz primary subchannel within that 2 MHz primary. 2975 + * 2972 2976 * @NUM_NL80211_ATTR: total number of nl80211_attrs available 2973 2977 * @NL80211_ATTR_MAX: highest attribute number currently defined 2974 2978 * @__NL80211_ATTR_AFTER_LAST: internal use ··· 3538 3534 NL80211_ATTR_NAN_CONFIG, 3539 3535 NL80211_ATTR_NAN_NEW_CLUSTER, 3540 3536 NL80211_ATTR_NAN_CAPABILITIES, 3537 + 3538 + NL80211_ATTR_S1G_PRIMARY_2MHZ, 3541 3539 3542 3540 /* add attributes here, update the policy in nl80211.c */ 3543 3541 ··· 4438 4432 * very low power (VLP) AP, despite being NO_IR. 4439 4433 * @NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY: This channel can be active in 4440 4434 * 20 MHz bandwidth, despite being NO_IR. 4435 + * @NL80211_FREQUENCY_ATTR_NO_4MHZ: 4 MHz operation is not allowed on this 4436 + * channel in current regulatory domain. 4437 + * @NL80211_FREQUENCY_ATTR_NO_8MHZ: 8 MHz operation is not allowed on this 4438 + * channel in current regulatory domain. 4439 + * @NL80211_FREQUENCY_ATTR_NO_16MHZ: 16 MHz operation is not allowed on this 4440 + * channel in current regulatory domain. 4441 4441 * @NL80211_FREQUENCY_ATTR_MAX: highest frequency attribute number 4442 4442 * currently defined 4443 4443 * @__NL80211_FREQUENCY_ATTR_AFTER_LAST: internal use ··· 4489 4477 NL80211_FREQUENCY_ATTR_CAN_MONITOR, 4490 4478 NL80211_FREQUENCY_ATTR_ALLOW_6GHZ_VLP_AP, 4491 4479 NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY, 4480 + NL80211_FREQUENCY_ATTR_NO_4MHZ, 4481 + NL80211_FREQUENCY_ATTR_NO_8MHZ, 4482 + NL80211_FREQUENCY_ATTR_NO_16MHZ, 4492 4483 4493 4484 /* keep last */ 4494 4485 __NL80211_FREQUENCY_ATTR_AFTER_LAST,
+70 -33
net/wireless/chan.c
··· 100 100 punctured = 0) : (punctured >>= 1))) \ 101 101 if (!(punctured & 1)) 102 102 103 + #define for_each_s1g_subchan(chandef, freq_khz) \ 104 + for (freq_khz = cfg80211_s1g_get_start_freq_khz(chandef); \ 105 + freq_khz <= cfg80211_s1g_get_end_freq_khz(chandef); \ 106 + freq_khz += MHZ_TO_KHZ(1)) 107 + 103 108 struct cfg80211_per_bw_puncturing_values { 104 109 u8 len; 105 110 const u16 *valid_values; ··· 341 336 342 337 bool cfg80211_chandef_valid(const struct cfg80211_chan_def *chandef) 343 338 { 344 - u32 control_freq, oper_freq; 345 - int oper_width, control_width; 339 + u32 control_freq, control_freq_khz, start_khz, end_khz; 346 340 347 341 if (!chandef->chan) 348 342 return false; ··· 367 363 case NL80211_CHAN_WIDTH_4: 368 364 case NL80211_CHAN_WIDTH_8: 369 365 case NL80211_CHAN_WIDTH_16: 370 - if (chandef->chan->band != NL80211_BAND_S1GHZ) 371 - return false; 372 - 373 - control_freq = ieee80211_channel_to_khz(chandef->chan); 374 - oper_freq = ieee80211_chandef_to_khz(chandef); 375 - control_width = nl80211_chan_width_to_mhz( 376 - ieee80211_s1g_channel_width( 377 - chandef->chan)); 378 - oper_width = cfg80211_chandef_get_width(chandef); 379 - 380 - if (oper_width < 0 || control_width < 0) 366 + if (!cfg80211_chandef_is_s1g(chandef)) 381 367 return false; 382 368 if (chandef->center_freq2) 383 369 return false; 384 370 385 - if (control_freq + MHZ_TO_KHZ(control_width) / 2 > 386 - oper_freq + MHZ_TO_KHZ(oper_width) / 2) 387 - return false; 371 + control_freq_khz = ieee80211_channel_to_khz(chandef->chan); 372 + start_khz = cfg80211_s1g_get_start_freq_khz(chandef); 373 + end_khz = cfg80211_s1g_get_end_freq_khz(chandef); 388 374 389 - if (control_freq - MHZ_TO_KHZ(control_width) / 2 < 390 - oper_freq - MHZ_TO_KHZ(oper_width) / 2) 375 + if (control_freq_khz < start_khz || control_freq_khz > end_khz) 391 376 return false; 392 377 break; 393 378 case NL80211_CHAN_WIDTH_80P80: ··· 452 459 453 460 if (cfg80211_chandef_is_edmg(chandef) && 454 461 !cfg80211_edmg_chandef_valid(chandef)) 462 + return false; 463 + 464 + if (!cfg80211_chandef_is_s1g(chandef) && chandef->s1g_primary_2mhz) 455 465 return false; 456 466 457 467 return valid_puncturing_bitmap(chandef); ··· 720 724 enum nl80211_iftype iftype) 721 725 { 722 726 struct ieee80211_channel *c; 727 + 728 + /* DFS is not required for S1G */ 729 + if (cfg80211_chandef_is_s1g(chandef)) 730 + return 0; 723 731 724 732 for_each_subchan(chandef, freq, cf) { 725 733 c = ieee80211_get_channel_khz(wiphy, freq); ··· 1130 1130 return true; 1131 1131 } 1132 1132 1133 + static bool cfg80211_s1g_usable(struct wiphy *wiphy, 1134 + const struct cfg80211_chan_def *chandef) 1135 + { 1136 + u32 freq_khz; 1137 + const struct ieee80211_channel *chan; 1138 + u32 pri_khz = ieee80211_channel_to_khz(chandef->chan); 1139 + u32 end_khz = cfg80211_s1g_get_end_freq_khz(chandef); 1140 + u32 start_khz = cfg80211_s1g_get_start_freq_khz(chandef); 1141 + int width_mhz = cfg80211_chandef_get_width(chandef); 1142 + u32 prohibited_flags = IEEE80211_CHAN_DISABLED; 1143 + 1144 + if (width_mhz >= 16) 1145 + prohibited_flags |= IEEE80211_CHAN_NO_16MHZ; 1146 + if (width_mhz >= 8) 1147 + prohibited_flags |= IEEE80211_CHAN_NO_8MHZ; 1148 + if (width_mhz >= 4) 1149 + prohibited_flags |= IEEE80211_CHAN_NO_4MHZ; 1150 + 1151 + if (chandef->chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY) 1152 + return false; 1153 + 1154 + if (pri_khz < start_khz || pri_khz > end_khz) 1155 + return false; 1156 + 1157 + for_each_s1g_subchan(chandef, freq_khz) { 1158 + chan = ieee80211_get_channel_khz(wiphy, freq_khz); 1159 + if (!chan || (chan->flags & prohibited_flags)) 1160 + return false; 1161 + } 1162 + 1163 + if (chandef->s1g_primary_2mhz) { 1164 + u32 sib_khz; 1165 + const struct ieee80211_channel *sibling; 1166 + 1167 + sibling = cfg80211_s1g_get_primary_sibling(wiphy, chandef); 1168 + if (!sibling) 1169 + return false; 1170 + 1171 + if (sibling->flags & IEEE80211_CHAN_S1G_NO_PRIMARY) 1172 + return false; 1173 + 1174 + sib_khz = ieee80211_channel_to_khz(sibling); 1175 + if (sib_khz < start_khz || sib_khz > end_khz) 1176 + return false; 1177 + } 1178 + 1179 + return true; 1180 + } 1181 + 1133 1182 bool _cfg80211_chandef_usable(struct wiphy *wiphy, 1134 1183 const struct cfg80211_chan_def *chandef, 1135 1184 u32 prohibited_flags, ··· 1203 1154 ext_nss_cap = __le16_to_cpu(vht_cap->vht_mcs.tx_highest) & 1204 1155 IEEE80211_VHT_EXT_NSS_BW_CAPABLE; 1205 1156 1157 + if (cfg80211_chandef_is_s1g(chandef)) 1158 + return cfg80211_s1g_usable(wiphy, chandef); 1159 + 1206 1160 if (edmg_cap->channels && 1207 1161 !cfg80211_edmg_usable(wiphy, 1208 1162 chandef->edmg.channels, ··· 1217 1165 control_freq = chandef->chan->center_freq; 1218 1166 1219 1167 switch (chandef->width) { 1220 - case NL80211_CHAN_WIDTH_1: 1221 - width = 1; 1222 - break; 1223 - case NL80211_CHAN_WIDTH_2: 1224 - width = 2; 1225 - break; 1226 - case NL80211_CHAN_WIDTH_4: 1227 - width = 4; 1228 - break; 1229 - case NL80211_CHAN_WIDTH_8: 1230 - width = 8; 1231 - break; 1232 - case NL80211_CHAN_WIDTH_16: 1233 - width = 16; 1234 - break; 1235 1168 case NL80211_CHAN_WIDTH_5: 1236 1169 width = 5; 1237 1170 break;
+22 -15
net/wireless/nl80211.c
··· 931 931 [NL80211_ATTR_S1G_SHORT_BEACON] = 932 932 NLA_POLICY_NESTED(nl80211_s1g_short_beacon), 933 933 [NL80211_ATTR_BSS_PARAM] = { .type = NLA_FLAG }, 934 + [NL80211_ATTR_S1G_PRIMARY_2MHZ] = { .type = NLA_FLAG }, 934 935 }; 935 936 936 937 /* policy for the key attributes */ ··· 1319 1318 if ((chan->flags & IEEE80211_CHAN_ALLOW_20MHZ_ACTIVITY) && 1320 1319 nla_put_flag(msg, 1321 1320 NL80211_FREQUENCY_ATTR_ALLOW_20MHZ_ACTIVITY)) 1321 + goto nla_put_failure; 1322 + if ((chan->flags & IEEE80211_CHAN_NO_4MHZ) && 1323 + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_4MHZ)) 1324 + goto nla_put_failure; 1325 + if ((chan->flags & IEEE80211_CHAN_NO_8MHZ) && 1326 + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_8MHZ)) 1327 + goto nla_put_failure; 1328 + if ((chan->flags & IEEE80211_CHAN_NO_16MHZ) && 1329 + nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_16MHZ)) 1322 1330 goto nla_put_failure; 1323 1331 } 1324 1332 ··· 3551 3541 chandef->center_freq1 = KHZ_TO_MHZ(control_freq); 3552 3542 chandef->freq1_offset = control_freq % 1000; 3553 3543 chandef->center_freq2 = 0; 3544 + chandef->s1g_primary_2mhz = false; 3554 3545 3555 3546 if (!chandef->chan) { 3556 3547 NL_SET_ERR_MSG_ATTR(extack, attrs[NL80211_ATTR_WIPHY_FREQ], ··· 3595 3584 return -EINVAL; 3596 3585 } 3597 3586 } else if (attrs[NL80211_ATTR_CHANNEL_WIDTH]) { 3598 - chandef->width = 3599 - nla_get_u32(attrs[NL80211_ATTR_CHANNEL_WIDTH]); 3600 - if (chandef->chan->band == NL80211_BAND_S1GHZ) { 3601 - /* User input error for channel width doesn't match channel */ 3602 - if (chandef->width != ieee80211_s1g_channel_width(chandef->chan)) { 3603 - NL_SET_ERR_MSG_ATTR(extack, 3604 - attrs[NL80211_ATTR_CHANNEL_WIDTH], 3605 - "bad channel width"); 3606 - return -EINVAL; 3607 - } 3608 - } 3587 + chandef->width = nla_get_u32(attrs[NL80211_ATTR_CHANNEL_WIDTH]); 3609 3588 if (attrs[NL80211_ATTR_CENTER_FREQ1]) { 3610 3589 chandef->center_freq1 = 3611 3590 nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ1]); 3612 - chandef->freq1_offset = 3613 - nla_get_u32_default(attrs[NL80211_ATTR_CENTER_FREQ1_OFFSET], 3614 - 0); 3591 + chandef->freq1_offset = nla_get_u32_default( 3592 + attrs[NL80211_ATTR_CENTER_FREQ1_OFFSET], 0); 3615 3593 } 3594 + 3616 3595 if (attrs[NL80211_ATTR_CENTER_FREQ2]) 3617 3596 chandef->center_freq2 = 3618 3597 nla_get_u32(attrs[NL80211_ATTR_CENTER_FREQ2]); 3598 + 3599 + chandef->s1g_primary_2mhz = nla_get_flag( 3600 + attrs[NL80211_ATTR_S1G_PRIMARY_2MHZ]); 3619 3601 } 3620 3602 3621 3603 if (info->attrs[NL80211_ATTR_WIPHY_EDMG_CHANNELS]) { ··· 10459 10455 goto out_free; 10460 10456 } 10461 10457 10462 - /* ignore disabled channels */ 10458 + /* Ignore disabled / no primary channels */ 10463 10459 if (chan->flags & IEEE80211_CHAN_DISABLED || 10460 + chan->flags & IEEE80211_CHAN_S1G_NO_PRIMARY || 10464 10461 !cfg80211_wdev_channel_allowed(wdev, chan)) 10465 10462 continue; 10466 10463 ··· 10483 10478 chan = &wiphy->bands[band]->channels[j]; 10484 10479 10485 10480 if (chan->flags & IEEE80211_CHAN_DISABLED || 10481 + chan->flags & 10482 + IEEE80211_CHAN_S1G_NO_PRIMARY || 10486 10483 !cfg80211_wdev_channel_allowed(wdev, chan)) 10487 10484 continue; 10488 10485
+22 -52
net/wireless/reg.c
··· 1707 1707 if (reg_rule->flags & NL80211_RRF_AUTO_BW) 1708 1708 max_bandwidth_khz = reg_get_max_bandwidth(regd, reg_rule); 1709 1709 1710 + if (is_s1g) { 1711 + if (max_bandwidth_khz < MHZ_TO_KHZ(16)) 1712 + bw_flags |= IEEE80211_CHAN_NO_16MHZ; 1713 + if (max_bandwidth_khz < MHZ_TO_KHZ(8)) 1714 + bw_flags |= IEEE80211_CHAN_NO_8MHZ; 1715 + if (max_bandwidth_khz < MHZ_TO_KHZ(4)) 1716 + bw_flags |= IEEE80211_CHAN_NO_4MHZ; 1717 + return bw_flags; 1718 + } 1719 + 1710 1720 /* If we get a reg_rule we can assume that at least 5Mhz fit */ 1711 1721 if (!cfg80211_does_bw_fit_range(freq_range, 1712 1722 center_freq_khz, ··· 1727 1717 MHZ_TO_KHZ(20))) 1728 1718 bw_flags |= IEEE80211_CHAN_NO_20MHZ; 1729 1719 1730 - if (is_s1g) { 1731 - /* S1G is strict about non overlapping channels. We can 1732 - * calculate which bandwidth is allowed per channel by finding 1733 - * the largest bandwidth which cleanly divides the freq_range. 1734 - */ 1735 - int edge_offset; 1736 - int ch_bw = max_bandwidth_khz; 1720 + if (max_bandwidth_khz < MHZ_TO_KHZ(10)) 1721 + bw_flags |= IEEE80211_CHAN_NO_10MHZ; 1722 + if (max_bandwidth_khz < MHZ_TO_KHZ(20)) 1723 + bw_flags |= IEEE80211_CHAN_NO_20MHZ; 1724 + if (max_bandwidth_khz < MHZ_TO_KHZ(40)) 1725 + bw_flags |= IEEE80211_CHAN_NO_HT40; 1726 + if (max_bandwidth_khz < MHZ_TO_KHZ(80)) 1727 + bw_flags |= IEEE80211_CHAN_NO_80MHZ; 1728 + if (max_bandwidth_khz < MHZ_TO_KHZ(160)) 1729 + bw_flags |= IEEE80211_CHAN_NO_160MHZ; 1730 + if (max_bandwidth_khz < MHZ_TO_KHZ(320)) 1731 + bw_flags |= IEEE80211_CHAN_NO_320MHZ; 1737 1732 1738 - while (ch_bw) { 1739 - edge_offset = (center_freq_khz - ch_bw / 2) - 1740 - freq_range->start_freq_khz; 1741 - if (edge_offset % ch_bw == 0) { 1742 - switch (KHZ_TO_MHZ(ch_bw)) { 1743 - case 1: 1744 - bw_flags |= IEEE80211_CHAN_1MHZ; 1745 - break; 1746 - case 2: 1747 - bw_flags |= IEEE80211_CHAN_2MHZ; 1748 - break; 1749 - case 4: 1750 - bw_flags |= IEEE80211_CHAN_4MHZ; 1751 - break; 1752 - case 8: 1753 - bw_flags |= IEEE80211_CHAN_8MHZ; 1754 - break; 1755 - case 16: 1756 - bw_flags |= IEEE80211_CHAN_16MHZ; 1757 - break; 1758 - default: 1759 - /* If we got here, no bandwidths fit on 1760 - * this frequency, ie. band edge. 1761 - */ 1762 - bw_flags |= IEEE80211_CHAN_DISABLED; 1763 - break; 1764 - } 1765 - break; 1766 - } 1767 - ch_bw /= 2; 1768 - } 1769 - } else { 1770 - if (max_bandwidth_khz < MHZ_TO_KHZ(10)) 1771 - bw_flags |= IEEE80211_CHAN_NO_10MHZ; 1772 - if (max_bandwidth_khz < MHZ_TO_KHZ(20)) 1773 - bw_flags |= IEEE80211_CHAN_NO_20MHZ; 1774 - if (max_bandwidth_khz < MHZ_TO_KHZ(40)) 1775 - bw_flags |= IEEE80211_CHAN_NO_HT40; 1776 - if (max_bandwidth_khz < MHZ_TO_KHZ(80)) 1777 - bw_flags |= IEEE80211_CHAN_NO_80MHZ; 1778 - if (max_bandwidth_khz < MHZ_TO_KHZ(160)) 1779 - bw_flags |= IEEE80211_CHAN_NO_160MHZ; 1780 - if (max_bandwidth_khz < MHZ_TO_KHZ(320)) 1781 - bw_flags |= IEEE80211_CHAN_NO_320MHZ; 1782 - } 1783 1733 return bw_flags; 1784 1734 } 1785 1735