185732ac8SCy Schubert /* 285732ac8SCy Schubert * Operating classes 385732ac8SCy Schubert * Copyright(c) 2015 Intel Deutschland GmbH 485732ac8SCy Schubert * Contact Information: 585732ac8SCy Schubert * Intel Linux Wireless <ilw@linux.intel.com> 685732ac8SCy Schubert * Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497 785732ac8SCy Schubert * 885732ac8SCy Schubert * This software may be distributed under the terms of the BSD license. 985732ac8SCy Schubert * See README for more details. 1085732ac8SCy Schubert */ 1185732ac8SCy Schubert 1285732ac8SCy Schubert #include "utils/includes.h" 1385732ac8SCy Schubert 1485732ac8SCy Schubert #include "utils/common.h" 1585732ac8SCy Schubert #include "common/ieee802_11_common.h" 1685732ac8SCy Schubert #include "wpa_supplicant_i.h" 17c1d255d3SCy Schubert #include "bss.h" 1885732ac8SCy Schubert 1985732ac8SCy Schubert 20c1d255d3SCy Schubert static enum chan_allowed allow_channel(struct hostapd_hw_modes *mode, 21c1d255d3SCy Schubert u8 op_class, u8 chan, 2285732ac8SCy Schubert unsigned int *flags) 2385732ac8SCy Schubert { 2485732ac8SCy Schubert int i; 25*a90b9d01SCy Schubert bool is_6ghz = is_6ghz_op_class(op_class); 2685732ac8SCy Schubert 2785732ac8SCy Schubert for (i = 0; i < mode->num_channels; i++) { 28c1d255d3SCy Schubert bool chan_is_6ghz; 29c1d255d3SCy Schubert 30*a90b9d01SCy Schubert chan_is_6ghz = is_6ghz_freq(mode->channels[i].freq); 31c1d255d3SCy Schubert if (is_6ghz == chan_is_6ghz && mode->channels[i].chan == chan) 3285732ac8SCy Schubert break; 3385732ac8SCy Schubert } 3485732ac8SCy Schubert 3585732ac8SCy Schubert if (i == mode->num_channels || 3685732ac8SCy Schubert (mode->channels[i].flag & HOSTAPD_CHAN_DISABLED)) 3785732ac8SCy Schubert return NOT_ALLOWED; 3885732ac8SCy Schubert 3985732ac8SCy Schubert if (flags) 4085732ac8SCy Schubert *flags = mode->channels[i].flag; 4185732ac8SCy Schubert 4285732ac8SCy Schubert if (mode->channels[i].flag & HOSTAPD_CHAN_NO_IR) 4385732ac8SCy Schubert return NO_IR; 4485732ac8SCy Schubert 4585732ac8SCy Schubert return ALLOWED; 4685732ac8SCy Schubert } 4785732ac8SCy Schubert 4885732ac8SCy Schubert 49c1d255d3SCy Schubert static int get_center_80mhz(struct hostapd_hw_modes *mode, u8 channel, 50c1d255d3SCy Schubert const u8 *center_channels, size_t num_chan) 5185732ac8SCy Schubert { 5285732ac8SCy Schubert size_t i; 5385732ac8SCy Schubert 5485732ac8SCy Schubert if (mode->mode != HOSTAPD_MODE_IEEE80211A) 5585732ac8SCy Schubert return 0; 5685732ac8SCy Schubert 57c1d255d3SCy Schubert for (i = 0; i < num_chan; i++) { 5885732ac8SCy Schubert /* 5985732ac8SCy Schubert * In 80 MHz, the bandwidth "spans" 12 channels (e.g., 36-48), 6085732ac8SCy Schubert * so the center channel is 6 channels away from the start/end. 6185732ac8SCy Schubert */ 6285732ac8SCy Schubert if (channel >= center_channels[i] - 6 && 6385732ac8SCy Schubert channel <= center_channels[i] + 6) 6485732ac8SCy Schubert return center_channels[i]; 6585732ac8SCy Schubert } 6685732ac8SCy Schubert 6785732ac8SCy Schubert return 0; 6885732ac8SCy Schubert } 6985732ac8SCy Schubert 7085732ac8SCy Schubert 71c1d255d3SCy Schubert static enum chan_allowed verify_80mhz(struct hostapd_hw_modes *mode, 72c1d255d3SCy Schubert u8 op_class, u8 channel) 7385732ac8SCy Schubert { 7485732ac8SCy Schubert u8 center_chan; 7585732ac8SCy Schubert unsigned int i; 7685732ac8SCy Schubert unsigned int no_ir = 0; 77c1d255d3SCy Schubert const u8 *center_channels; 78c1d255d3SCy Schubert size_t num_chan; 79c1d255d3SCy Schubert const u8 center_channels_5ghz[] = { 42, 58, 106, 122, 138, 155, 171 }; 80c1d255d3SCy Schubert const u8 center_channels_6ghz[] = { 7, 23, 39, 55, 71, 87, 103, 119, 81c1d255d3SCy Schubert 135, 151, 167, 183, 199, 215 }; 8285732ac8SCy Schubert 83c1d255d3SCy Schubert if (is_6ghz_op_class(op_class)) { 84c1d255d3SCy Schubert center_channels = center_channels_6ghz; 85c1d255d3SCy Schubert num_chan = ARRAY_SIZE(center_channels_6ghz); 86c1d255d3SCy Schubert } else { 87c1d255d3SCy Schubert center_channels = center_channels_5ghz; 88c1d255d3SCy Schubert num_chan = ARRAY_SIZE(center_channels_5ghz); 89c1d255d3SCy Schubert } 90c1d255d3SCy Schubert 91c1d255d3SCy Schubert center_chan = get_center_80mhz(mode, channel, center_channels, 92c1d255d3SCy Schubert num_chan); 9385732ac8SCy Schubert if (!center_chan) 9485732ac8SCy Schubert return NOT_ALLOWED; 9585732ac8SCy Schubert 9685732ac8SCy Schubert /* check all the channels are available */ 9785732ac8SCy Schubert for (i = 0; i < 4; i++) { 9885732ac8SCy Schubert unsigned int flags; 9985732ac8SCy Schubert u8 adj_chan = center_chan - 6 + i * 4; 10085732ac8SCy Schubert 101c1d255d3SCy Schubert if (allow_channel(mode, op_class, adj_chan, &flags) == 102c1d255d3SCy Schubert NOT_ALLOWED) 10385732ac8SCy Schubert return NOT_ALLOWED; 10485732ac8SCy Schubert 105*a90b9d01SCy Schubert if (!(flags & HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL)) 10685732ac8SCy Schubert return NOT_ALLOWED; 10785732ac8SCy Schubert 10885732ac8SCy Schubert if (flags & HOSTAPD_CHAN_NO_IR) 10985732ac8SCy Schubert no_ir = 1; 11085732ac8SCy Schubert } 11185732ac8SCy Schubert 11285732ac8SCy Schubert if (no_ir) 11385732ac8SCy Schubert return NO_IR; 11485732ac8SCy Schubert 11585732ac8SCy Schubert return ALLOWED; 11685732ac8SCy Schubert } 11785732ac8SCy Schubert 11885732ac8SCy Schubert 119c1d255d3SCy Schubert static int get_center_160mhz(struct hostapd_hw_modes *mode, u8 channel, 120c1d255d3SCy Schubert const u8 *center_channels, size_t num_chan) 12185732ac8SCy Schubert { 12285732ac8SCy Schubert unsigned int i; 12385732ac8SCy Schubert 12485732ac8SCy Schubert if (mode->mode != HOSTAPD_MODE_IEEE80211A) 12585732ac8SCy Schubert return 0; 12685732ac8SCy Schubert 127c1d255d3SCy Schubert for (i = 0; i < num_chan; i++) { 12885732ac8SCy Schubert /* 12985732ac8SCy Schubert * In 160 MHz, the bandwidth "spans" 28 channels (e.g., 36-64), 13085732ac8SCy Schubert * so the center channel is 14 channels away from the start/end. 13185732ac8SCy Schubert */ 13285732ac8SCy Schubert if (channel >= center_channels[i] - 14 && 13385732ac8SCy Schubert channel <= center_channels[i] + 14) 13485732ac8SCy Schubert return center_channels[i]; 13585732ac8SCy Schubert } 13685732ac8SCy Schubert 13785732ac8SCy Schubert return 0; 13885732ac8SCy Schubert } 13985732ac8SCy Schubert 14085732ac8SCy Schubert 14185732ac8SCy Schubert static enum chan_allowed verify_160mhz(struct hostapd_hw_modes *mode, 142c1d255d3SCy Schubert u8 op_class, u8 channel) 14385732ac8SCy Schubert { 14485732ac8SCy Schubert u8 center_chan; 14585732ac8SCy Schubert unsigned int i; 14685732ac8SCy Schubert unsigned int no_ir = 0; 147c1d255d3SCy Schubert const u8 *center_channels; 148c1d255d3SCy Schubert size_t num_chan; 149c1d255d3SCy Schubert const u8 center_channels_5ghz[] = { 50, 114, 163 }; 150c1d255d3SCy Schubert const u8 center_channels_6ghz[] = { 15, 47, 79, 111, 143, 175, 207 }; 15185732ac8SCy Schubert 152c1d255d3SCy Schubert if (is_6ghz_op_class(op_class)) { 153c1d255d3SCy Schubert center_channels = center_channels_6ghz; 154c1d255d3SCy Schubert num_chan = ARRAY_SIZE(center_channels_6ghz); 155c1d255d3SCy Schubert } else { 156c1d255d3SCy Schubert center_channels = center_channels_5ghz; 157c1d255d3SCy Schubert num_chan = ARRAY_SIZE(center_channels_5ghz); 158c1d255d3SCy Schubert } 159c1d255d3SCy Schubert 160c1d255d3SCy Schubert center_chan = get_center_160mhz(mode, channel, center_channels, 161c1d255d3SCy Schubert num_chan); 16285732ac8SCy Schubert if (!center_chan) 16385732ac8SCy Schubert return NOT_ALLOWED; 16485732ac8SCy Schubert 16585732ac8SCy Schubert /* Check all the channels are available */ 16685732ac8SCy Schubert for (i = 0; i < 8; i++) { 16785732ac8SCy Schubert unsigned int flags; 16885732ac8SCy Schubert u8 adj_chan = center_chan - 14 + i * 4; 16985732ac8SCy Schubert 170c1d255d3SCy Schubert if (allow_channel(mode, op_class, adj_chan, &flags) == 171c1d255d3SCy Schubert NOT_ALLOWED) 17285732ac8SCy Schubert return NOT_ALLOWED; 17385732ac8SCy Schubert 174*a90b9d01SCy Schubert if (!(flags & HOSTAPD_CHAN_VHT_80MHZ_SUBCHANNEL) || 175*a90b9d01SCy Schubert !(flags & HOSTAPD_CHAN_VHT_160MHZ_SUBCHANNEL)) 17685732ac8SCy Schubert return NOT_ALLOWED; 17785732ac8SCy Schubert 17885732ac8SCy Schubert if (flags & HOSTAPD_CHAN_NO_IR) 17985732ac8SCy Schubert no_ir = 1; 18085732ac8SCy Schubert } 18185732ac8SCy Schubert 18285732ac8SCy Schubert if (no_ir) 18385732ac8SCy Schubert return NO_IR; 18485732ac8SCy Schubert 18585732ac8SCy Schubert return ALLOWED; 18685732ac8SCy Schubert } 18785732ac8SCy Schubert 18885732ac8SCy Schubert 189*a90b9d01SCy Schubert static int get_center_320mhz(struct hostapd_hw_modes *mode, u8 channel, 190*a90b9d01SCy Schubert const u8 *center_channels, size_t num_chan) 191*a90b9d01SCy Schubert { 192*a90b9d01SCy Schubert unsigned int i; 193*a90b9d01SCy Schubert 194*a90b9d01SCy Schubert if (mode->mode != HOSTAPD_MODE_IEEE80211A || !mode->is_6ghz) 195*a90b9d01SCy Schubert return 0; 196*a90b9d01SCy Schubert 197*a90b9d01SCy Schubert for (i = 0; i < num_chan; i++) { 198*a90b9d01SCy Schubert /* 199*a90b9d01SCy Schubert * In 320 MHz, the bandwidth "spans" 60 channels (e.g., 65-125), 200*a90b9d01SCy Schubert * so the center channel is 30 channels away from the start/end. 201*a90b9d01SCy Schubert */ 202*a90b9d01SCy Schubert if (channel >= center_channels[i] - 30 && 203*a90b9d01SCy Schubert channel <= center_channels[i] + 30) 204*a90b9d01SCy Schubert return center_channels[i]; 205*a90b9d01SCy Schubert } 206*a90b9d01SCy Schubert 207*a90b9d01SCy Schubert return 0; 208*a90b9d01SCy Schubert } 209*a90b9d01SCy Schubert 210*a90b9d01SCy Schubert 211*a90b9d01SCy Schubert static enum chan_allowed verify_320mhz(struct hostapd_hw_modes *mode, 212*a90b9d01SCy Schubert u8 op_class, u8 channel) 213*a90b9d01SCy Schubert { 214*a90b9d01SCy Schubert u8 center_chan; 215*a90b9d01SCy Schubert unsigned int i; 216*a90b9d01SCy Schubert bool no_ir = false; 217*a90b9d01SCy Schubert const u8 *center_channels; 218*a90b9d01SCy Schubert size_t num_chan; 219*a90b9d01SCy Schubert const u8 center_channels_6ghz[] = { 31, 63, 95, 127, 159, 191 }; 220*a90b9d01SCy Schubert 221*a90b9d01SCy Schubert center_channels = center_channels_6ghz; 222*a90b9d01SCy Schubert num_chan = ARRAY_SIZE(center_channels_6ghz); 223*a90b9d01SCy Schubert 224*a90b9d01SCy Schubert center_chan = get_center_320mhz(mode, channel, center_channels, 225*a90b9d01SCy Schubert num_chan); 226*a90b9d01SCy Schubert if (!center_chan) 227*a90b9d01SCy Schubert return NOT_ALLOWED; 228*a90b9d01SCy Schubert 229*a90b9d01SCy Schubert /* Check all the channels are available */ 230*a90b9d01SCy Schubert for (i = 0; i < 16; i++) { 231*a90b9d01SCy Schubert unsigned int flags; 232*a90b9d01SCy Schubert u8 adj_chan = center_chan - 30 + i * 4; 233*a90b9d01SCy Schubert 234*a90b9d01SCy Schubert if (allow_channel(mode, op_class, adj_chan, &flags) == 235*a90b9d01SCy Schubert NOT_ALLOWED) 236*a90b9d01SCy Schubert return NOT_ALLOWED; 237*a90b9d01SCy Schubert 238*a90b9d01SCy Schubert if (!(flags & HOSTAPD_CHAN_EHT_320MHZ_SUBCHANNEL)) 239*a90b9d01SCy Schubert return NOT_ALLOWED; 240*a90b9d01SCy Schubert 241*a90b9d01SCy Schubert if (flags & HOSTAPD_CHAN_NO_IR) 242*a90b9d01SCy Schubert no_ir = true; 243*a90b9d01SCy Schubert } 244*a90b9d01SCy Schubert 245*a90b9d01SCy Schubert if (no_ir) 246*a90b9d01SCy Schubert return NO_IR; 247*a90b9d01SCy Schubert 248*a90b9d01SCy Schubert return ALLOWED; 249*a90b9d01SCy Schubert } 250*a90b9d01SCy Schubert 251*a90b9d01SCy Schubert 252c1d255d3SCy Schubert enum chan_allowed verify_channel(struct hostapd_hw_modes *mode, u8 op_class, 253c1d255d3SCy Schubert u8 channel, u8 bw) 25485732ac8SCy Schubert { 25585732ac8SCy Schubert unsigned int flag = 0; 25685732ac8SCy Schubert enum chan_allowed res, res2; 25785732ac8SCy Schubert 258c1d255d3SCy Schubert res2 = res = allow_channel(mode, op_class, channel, &flag); 259c1d255d3SCy Schubert if (bw == BW40MINUS || (bw == BW40 && (((channel - 1) / 4) % 2))) { 26085732ac8SCy Schubert if (!(flag & HOSTAPD_CHAN_HT40MINUS)) 26185732ac8SCy Schubert return NOT_ALLOWED; 262c1d255d3SCy Schubert res2 = allow_channel(mode, op_class, channel - 4, NULL); 2634b72b91aSCy Schubert } else if (bw == BW40PLUS) { 26485732ac8SCy Schubert if (!(flag & HOSTAPD_CHAN_HT40PLUS)) 26585732ac8SCy Schubert return NOT_ALLOWED; 266c1d255d3SCy Schubert res2 = allow_channel(mode, op_class, channel + 4, NULL); 2674b72b91aSCy Schubert } else if (is_6ghz_op_class(op_class) && bw == BW40) { 2684b72b91aSCy Schubert if (get_6ghz_sec_channel(channel) < 0) 2694b72b91aSCy Schubert res2 = allow_channel(mode, op_class, channel - 4, NULL); 2704b72b91aSCy Schubert else 2714b72b91aSCy Schubert res2 = allow_channel(mode, op_class, channel + 4, NULL); 27285732ac8SCy Schubert } else if (bw == BW80) { 27385732ac8SCy Schubert /* 27485732ac8SCy Schubert * channel is a center channel and as such, not necessarily a 27585732ac8SCy Schubert * valid 20 MHz channels. Override earlier allow_channel() 27685732ac8SCy Schubert * result and use only the 80 MHz specific version. 27785732ac8SCy Schubert */ 278c1d255d3SCy Schubert res2 = res = verify_80mhz(mode, op_class, channel); 27985732ac8SCy Schubert } else if (bw == BW160) { 28085732ac8SCy Schubert /* 28185732ac8SCy Schubert * channel is a center channel and as such, not necessarily a 28285732ac8SCy Schubert * valid 20 MHz channels. Override earlier allow_channel() 28385732ac8SCy Schubert * result and use only the 160 MHz specific version. 28485732ac8SCy Schubert */ 285c1d255d3SCy Schubert res2 = res = verify_160mhz(mode, op_class, channel); 28685732ac8SCy Schubert } else if (bw == BW80P80) { 28785732ac8SCy Schubert /* 28885732ac8SCy Schubert * channel is a center channel and as such, not necessarily a 28985732ac8SCy Schubert * valid 20 MHz channels. Override earlier allow_channel() 29085732ac8SCy Schubert * result and use only the 80 MHz specific version. 29185732ac8SCy Schubert */ 292c1d255d3SCy Schubert res2 = res = verify_80mhz(mode, op_class, channel); 293*a90b9d01SCy Schubert } else if (bw == BW320) { 294*a90b9d01SCy Schubert /* 295*a90b9d01SCy Schubert * channel is a center channel and as such, not necessarily a 296*a90b9d01SCy Schubert * valid 20 MHz channels. Override earlier allow_channel() 297*a90b9d01SCy Schubert * result and use only the 320 MHz specific version. 298*a90b9d01SCy Schubert */ 299*a90b9d01SCy Schubert res2= res = verify_320mhz(mode, op_class, channel); 30085732ac8SCy Schubert } 30185732ac8SCy Schubert 30285732ac8SCy Schubert if (res == NOT_ALLOWED || res2 == NOT_ALLOWED) 30385732ac8SCy Schubert return NOT_ALLOWED; 30485732ac8SCy Schubert 30585732ac8SCy Schubert if (res == NO_IR || res2 == NO_IR) 30685732ac8SCy Schubert return NO_IR; 30785732ac8SCy Schubert 30885732ac8SCy Schubert return ALLOWED; 30985732ac8SCy Schubert } 31085732ac8SCy Schubert 31185732ac8SCy Schubert 31285732ac8SCy Schubert static int wpas_op_class_supported(struct wpa_supplicant *wpa_s, 3134bc52338SCy Schubert struct wpa_ssid *ssid, 31485732ac8SCy Schubert const struct oper_class_map *op_class) 31585732ac8SCy Schubert { 31685732ac8SCy Schubert int chan; 31785732ac8SCy Schubert size_t i; 31885732ac8SCy Schubert struct hostapd_hw_modes *mode; 31985732ac8SCy Schubert int found; 3204bc52338SCy Schubert int z; 3214bc52338SCy Schubert int freq2 = 0; 3224bc52338SCy Schubert int freq5 = 0; 323*a90b9d01SCy Schubert bool freq6 = false; 32485732ac8SCy Schubert 325c1d255d3SCy Schubert mode = get_mode(wpa_s->hw.modes, wpa_s->hw.num_modes, op_class->mode, 326c1d255d3SCy Schubert is_6ghz_op_class(op_class->op_class)); 32785732ac8SCy Schubert if (!mode) 32885732ac8SCy Schubert return 0; 32985732ac8SCy Schubert 3304bc52338SCy Schubert /* If we are configured to disable certain things, take that into 3314bc52338SCy Schubert * account here. */ 332c1d255d3SCy Schubert if (ssid && ssid->freq_list && ssid->freq_list[0]) { 3334bc52338SCy Schubert for (z = 0; ; z++) { 3344bc52338SCy Schubert int f = ssid->freq_list[z]; 3354bc52338SCy Schubert 3364bc52338SCy Schubert if (f == 0) 3374bc52338SCy Schubert break; /* end of list */ 338*a90b9d01SCy Schubert if (is_6ghz_freq(f)) 339*a90b9d01SCy Schubert freq6 = true; 340*a90b9d01SCy Schubert else if (f > 4000 && f < 6000) 3414bc52338SCy Schubert freq5 = 1; 3424bc52338SCy Schubert else if (f > 2400 && f < 2500) 3434bc52338SCy Schubert freq2 = 1; 3444bc52338SCy Schubert } 3454bc52338SCy Schubert } else { 3464bc52338SCy Schubert /* No frequencies specified, can use anything hardware supports. 3474bc52338SCy Schubert */ 3484bc52338SCy Schubert freq2 = freq5 = 1; 349*a90b9d01SCy Schubert freq6 = true; 3504bc52338SCy Schubert } 3514bc52338SCy Schubert 352*a90b9d01SCy Schubert if (is_6ghz_op_class(op_class->op_class) && !freq6) 353*a90b9d01SCy Schubert return 0; 3544bc52338SCy Schubert if (op_class->op_class >= 115 && op_class->op_class <= 130 && !freq5) 3554bc52338SCy Schubert return 0; 3564bc52338SCy Schubert if (op_class->op_class >= 81 && op_class->op_class <= 84 && !freq2) 3574bc52338SCy Schubert return 0; 3584bc52338SCy Schubert 3594bc52338SCy Schubert #ifdef CONFIG_HT_OVERRIDES 360c1d255d3SCy Schubert if (ssid && ssid->disable_ht) { 3614bc52338SCy Schubert switch (op_class->op_class) { 3624bc52338SCy Schubert case 83: 3634bc52338SCy Schubert case 84: 3644bc52338SCy Schubert case 104: 3654bc52338SCy Schubert case 105: 3664bc52338SCy Schubert case 116: 3674bc52338SCy Schubert case 117: 3684bc52338SCy Schubert case 119: 3694bc52338SCy Schubert case 120: 3704bc52338SCy Schubert case 122: 3714bc52338SCy Schubert case 123: 3724bc52338SCy Schubert case 126: 3734bc52338SCy Schubert case 127: 3744bc52338SCy Schubert case 128: 3754bc52338SCy Schubert case 129: 3764bc52338SCy Schubert case 130: 3774bc52338SCy Schubert /* Disable >= 40 MHz channels if HT is disabled */ 3784bc52338SCy Schubert return 0; 3794bc52338SCy Schubert } 3804bc52338SCy Schubert } 3814bc52338SCy Schubert #endif /* CONFIG_HT_OVERRIDES */ 3824bc52338SCy Schubert 3834bc52338SCy Schubert #ifdef CONFIG_VHT_OVERRIDES 384c1d255d3SCy Schubert if (ssid && ssid->disable_vht) { 3854bc52338SCy Schubert if (op_class->op_class >= 128 && op_class->op_class <= 130) { 3864bc52338SCy Schubert /* Disable >= 80 MHz channels if VHT is disabled */ 3874bc52338SCy Schubert return 0; 3884bc52338SCy Schubert } 3894bc52338SCy Schubert } 3904bc52338SCy Schubert #endif /* CONFIG_VHT_OVERRIDES */ 3914bc52338SCy Schubert 39285732ac8SCy Schubert if (op_class->op_class == 128) { 393c1d255d3SCy Schubert u8 channels[] = { 42, 58, 106, 122, 138, 155, 171 }; 39485732ac8SCy Schubert 39585732ac8SCy Schubert for (i = 0; i < ARRAY_SIZE(channels); i++) { 396c1d255d3SCy Schubert if (verify_channel(mode, op_class->op_class, 397c1d255d3SCy Schubert channels[i], op_class->bw) != 39885732ac8SCy Schubert NOT_ALLOWED) 39985732ac8SCy Schubert return 1; 40085732ac8SCy Schubert } 40185732ac8SCy Schubert 40285732ac8SCy Schubert return 0; 40385732ac8SCy Schubert } 40485732ac8SCy Schubert 40585732ac8SCy Schubert if (op_class->op_class == 129) { 40685732ac8SCy Schubert /* Check if either 160 MHz channels is allowed */ 407c1d255d3SCy Schubert return verify_channel(mode, op_class->op_class, 50, 408c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED || 409c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 114, 410c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED || 411c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 163, 412c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED; 41385732ac8SCy Schubert } 41485732ac8SCy Schubert 41585732ac8SCy Schubert if (op_class->op_class == 130) { 41685732ac8SCy Schubert /* Need at least two non-contiguous 80 MHz segments */ 41785732ac8SCy Schubert found = 0; 41885732ac8SCy Schubert 419c1d255d3SCy Schubert if (verify_channel(mode, op_class->op_class, 42, 420c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED || 421c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 58, 422c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED) 42385732ac8SCy Schubert found++; 424c1d255d3SCy Schubert if (verify_channel(mode, op_class->op_class, 106, 425c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED || 426c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 122, 427c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED || 428c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 138, 429c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED || 430c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 155, 431c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED || 432c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 171, 433c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED) 43485732ac8SCy Schubert found++; 435c1d255d3SCy Schubert if (verify_channel(mode, op_class->op_class, 106, 436c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED && 437c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 138, 438c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED) 43985732ac8SCy Schubert found++; 440c1d255d3SCy Schubert if (verify_channel(mode, op_class->op_class, 122, 441c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED && 442c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 155, 443c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED) 444c1d255d3SCy Schubert found++; 445c1d255d3SCy Schubert if (verify_channel(mode, op_class->op_class, 138, 446c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED && 447c1d255d3SCy Schubert verify_channel(mode, op_class->op_class, 171, 448c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED) 44985732ac8SCy Schubert found++; 45085732ac8SCy Schubert 45185732ac8SCy Schubert if (found >= 2) 45285732ac8SCy Schubert return 1; 45385732ac8SCy Schubert 45485732ac8SCy Schubert return 0; 45585732ac8SCy Schubert } 45685732ac8SCy Schubert 457c1d255d3SCy Schubert if (op_class->op_class == 135) { 458c1d255d3SCy Schubert /* Need at least two 80 MHz segments which do not fall under the 459c1d255d3SCy Schubert * same 160 MHz segment to support 80+80 in 6 GHz. 460c1d255d3SCy Schubert */ 461c1d255d3SCy Schubert int first_seg = 0; 462c1d255d3SCy Schubert int curr_seg = 0; 463c1d255d3SCy Schubert 464c1d255d3SCy Schubert for (chan = op_class->min_chan; chan <= op_class->max_chan; 465c1d255d3SCy Schubert chan += op_class->inc) { 466c1d255d3SCy Schubert curr_seg++; 467c1d255d3SCy Schubert if (verify_channel(mode, op_class->op_class, chan, 468c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED) { 469c1d255d3SCy Schubert if (!first_seg) { 470c1d255d3SCy Schubert first_seg = curr_seg; 471c1d255d3SCy Schubert continue; 472c1d255d3SCy Schubert } 473c1d255d3SCy Schubert 474c1d255d3SCy Schubert /* Supported if at least two non-consecutive 80 475c1d255d3SCy Schubert * MHz segments allowed. 476c1d255d3SCy Schubert */ 477c1d255d3SCy Schubert if ((curr_seg - first_seg) > 1) 478c1d255d3SCy Schubert return 1; 479c1d255d3SCy Schubert 480c1d255d3SCy Schubert /* Supported even if the 80 MHz segments are 481c1d255d3SCy Schubert * consecutive when they do not fall under the 482c1d255d3SCy Schubert * same 160 MHz segment. 483c1d255d3SCy Schubert */ 484c1d255d3SCy Schubert if ((first_seg % 2) == 0) 485c1d255d3SCy Schubert return 1; 486c1d255d3SCy Schubert } 487c1d255d3SCy Schubert } 488c1d255d3SCy Schubert 489c1d255d3SCy Schubert return 0; 490c1d255d3SCy Schubert } 491c1d255d3SCy Schubert 49285732ac8SCy Schubert found = 0; 49385732ac8SCy Schubert for (chan = op_class->min_chan; chan <= op_class->max_chan; 49485732ac8SCy Schubert chan += op_class->inc) { 495c1d255d3SCy Schubert if (verify_channel(mode, op_class->op_class, chan, 496c1d255d3SCy Schubert op_class->bw) != NOT_ALLOWED) { 49785732ac8SCy Schubert found = 1; 49885732ac8SCy Schubert break; 49985732ac8SCy Schubert } 50085732ac8SCy Schubert } 50185732ac8SCy Schubert 50285732ac8SCy Schubert return found; 50385732ac8SCy Schubert } 50485732ac8SCy Schubert 50585732ac8SCy Schubert 506c1d255d3SCy Schubert static int wpas_sta_secondary_channel_offset(struct wpa_bss *bss, u8 *current, 507c1d255d3SCy Schubert u8 *channel) 508c1d255d3SCy Schubert { 509c1d255d3SCy Schubert 510c1d255d3SCy Schubert const u8 *ies; 511c1d255d3SCy Schubert u8 phy_type; 512c1d255d3SCy Schubert size_t ies_len; 513c1d255d3SCy Schubert 514c1d255d3SCy Schubert if (!bss) 515c1d255d3SCy Schubert return -1; 516c1d255d3SCy Schubert ies = wpa_bss_ie_ptr(bss); 517c1d255d3SCy Schubert ies_len = bss->ie_len ? bss->ie_len : bss->beacon_ie_len; 518c1d255d3SCy Schubert return wpas_get_op_chan_phy(bss->freq, ies, ies_len, current, 519c1d255d3SCy Schubert channel, &phy_type); 520c1d255d3SCy Schubert } 521c1d255d3SCy Schubert 522c1d255d3SCy Schubert 5234bc52338SCy Schubert size_t wpas_supp_op_class_ie(struct wpa_supplicant *wpa_s, 5244bc52338SCy Schubert struct wpa_ssid *ssid, 525c1d255d3SCy Schubert struct wpa_bss *bss, u8 *pos, size_t len) 52685732ac8SCy Schubert { 52785732ac8SCy Schubert struct wpabuf *buf; 52885732ac8SCy Schubert u8 op, current, chan; 52985732ac8SCy Schubert u8 *ie_len; 53085732ac8SCy Schubert size_t res; 531*a90b9d01SCy Schubert bool op128 = false, op130 = false, op133 = false, op135 = false; 53285732ac8SCy Schubert 53385732ac8SCy Schubert /* 534c1d255d3SCy Schubert * Determine the current operating class correct mode based on 535c1d255d3SCy Schubert * advertised BSS capabilities, if available. Fall back to a less 536c1d255d3SCy Schubert * accurate guess based on frequency if the needed IEs are not available 537c1d255d3SCy Schubert * or used. 53885732ac8SCy Schubert */ 539c1d255d3SCy Schubert if (wpas_sta_secondary_channel_offset(bss, ¤t, &chan) < 0 && 540*a90b9d01SCy Schubert ieee80211_freq_to_channel_ext(bss->freq, 0, 541*a90b9d01SCy Schubert CONF_OPER_CHWIDTH_USE_HT, ¤t, 542*a90b9d01SCy Schubert &chan) == NUM_HOSTAPD_MODES) 54385732ac8SCy Schubert return 0; 54485732ac8SCy Schubert 54585732ac8SCy Schubert /* 54685732ac8SCy Schubert * Need 3 bytes for EID, length, and current operating class, plus 54785732ac8SCy Schubert * 1 byte for every other supported operating class. 54885732ac8SCy Schubert */ 54985732ac8SCy Schubert buf = wpabuf_alloc(global_op_class_size + 3); 55085732ac8SCy Schubert if (!buf) 55185732ac8SCy Schubert return 0; 55285732ac8SCy Schubert 55385732ac8SCy Schubert wpabuf_put_u8(buf, WLAN_EID_SUPPORTED_OPERATING_CLASSES); 55485732ac8SCy Schubert /* Will set the length later, putting a placeholder */ 55585732ac8SCy Schubert ie_len = wpabuf_put(buf, 1); 55685732ac8SCy Schubert wpabuf_put_u8(buf, current); 55785732ac8SCy Schubert 55885732ac8SCy Schubert for (op = 0; global_op_class[op].op_class; op++) { 559*a90b9d01SCy Schubert bool supp; 560*a90b9d01SCy Schubert u8 op_class = global_op_class[op].op_class; 561*a90b9d01SCy Schubert 562*a90b9d01SCy Schubert supp = wpas_op_class_supported(wpa_s, ssid, 563*a90b9d01SCy Schubert &global_op_class[op]); 564*a90b9d01SCy Schubert if (!supp) 565*a90b9d01SCy Schubert continue; 566*a90b9d01SCy Schubert switch (op_class) { 567*a90b9d01SCy Schubert case 128: 568*a90b9d01SCy Schubert op128 = true; 569*a90b9d01SCy Schubert break; 570*a90b9d01SCy Schubert case 130: 571*a90b9d01SCy Schubert op130 = true; 572*a90b9d01SCy Schubert break; 573*a90b9d01SCy Schubert case 133: 574*a90b9d01SCy Schubert op133 = true; 575*a90b9d01SCy Schubert break; 576*a90b9d01SCy Schubert case 135: 577*a90b9d01SCy Schubert op135 = true; 578*a90b9d01SCy Schubert break; 579*a90b9d01SCy Schubert } 580*a90b9d01SCy Schubert if (is_80plus_op_class(op_class)) 581*a90b9d01SCy Schubert continue; 582*a90b9d01SCy Schubert 583*a90b9d01SCy Schubert /* Add a 1-octet operating class to the Operating Class field */ 58485732ac8SCy Schubert wpabuf_put_u8(buf, global_op_class[op].op_class); 58585732ac8SCy Schubert } 58685732ac8SCy Schubert 587*a90b9d01SCy Schubert /* Add the 2-octet operating classes (i.e., 80+80 MHz cases), if any */ 588*a90b9d01SCy Schubert if ((op128 && op130) || (op133 && op135)) { 589*a90b9d01SCy Schubert /* Operating Class Duple Sequence field */ 590*a90b9d01SCy Schubert 591*a90b9d01SCy Schubert /* Zero Delimiter */ 592*a90b9d01SCy Schubert wpabuf_put_u8(buf, 0); 593*a90b9d01SCy Schubert 594*a90b9d01SCy Schubert /* Operating Class Duple List */ 595*a90b9d01SCy Schubert if (op128 && op130) { 596*a90b9d01SCy Schubert wpabuf_put_u8(buf, 130); 597*a90b9d01SCy Schubert wpabuf_put_u8(buf, 128); 598*a90b9d01SCy Schubert } 599*a90b9d01SCy Schubert if (op133 && op135) { 600*a90b9d01SCy Schubert wpabuf_put_u8(buf, 135); 601*a90b9d01SCy Schubert wpabuf_put_u8(buf, 133); 602*a90b9d01SCy Schubert } 603*a90b9d01SCy Schubert } 604*a90b9d01SCy Schubert 60585732ac8SCy Schubert *ie_len = wpabuf_len(buf) - 2; 606c1d255d3SCy Schubert if (*ie_len < 2) { 607c1d255d3SCy Schubert wpa_printf(MSG_DEBUG, 608c1d255d3SCy Schubert "No supported operating classes IE to add"); 609c1d255d3SCy Schubert res = 0; 610c1d255d3SCy Schubert } else if (wpabuf_len(buf) > len) { 61185732ac8SCy Schubert wpa_printf(MSG_ERROR, 612c1d255d3SCy Schubert "Supported operating classes IE exceeds maximum buffer length"); 61385732ac8SCy Schubert res = 0; 61485732ac8SCy Schubert } else { 61585732ac8SCy Schubert os_memcpy(pos, wpabuf_head(buf), wpabuf_len(buf)); 61685732ac8SCy Schubert res = wpabuf_len(buf); 61785732ac8SCy Schubert wpa_hexdump_buf(MSG_DEBUG, 61885732ac8SCy Schubert "Added supported operating classes IE", buf); 61985732ac8SCy Schubert } 62085732ac8SCy Schubert 62185732ac8SCy Schubert wpabuf_free(buf); 62285732ac8SCy Schubert return res; 62385732ac8SCy Schubert } 624c1d255d3SCy Schubert 625c1d255d3SCy Schubert 626c1d255d3SCy Schubert int * wpas_supp_op_classes(struct wpa_supplicant *wpa_s) 627c1d255d3SCy Schubert { 628c1d255d3SCy Schubert int op; 629c1d255d3SCy Schubert unsigned int pos, max_num = 0; 630c1d255d3SCy Schubert int *classes; 631c1d255d3SCy Schubert 632c1d255d3SCy Schubert for (op = 0; global_op_class[op].op_class; op++) 633c1d255d3SCy Schubert max_num++; 634c1d255d3SCy Schubert classes = os_zalloc((max_num + 1) * sizeof(int)); 635c1d255d3SCy Schubert if (!classes) 636c1d255d3SCy Schubert return NULL; 637c1d255d3SCy Schubert 638c1d255d3SCy Schubert for (op = 0, pos = 0; global_op_class[op].op_class; op++) { 639c1d255d3SCy Schubert if (wpas_op_class_supported(wpa_s, NULL, &global_op_class[op])) 640c1d255d3SCy Schubert classes[pos++] = global_op_class[op].op_class; 641c1d255d3SCy Schubert } 642c1d255d3SCy Schubert 643c1d255d3SCy Schubert return classes; 644c1d255d3SCy Schubert } 645