/* * * Wireless daemon for Linux * * Copyright (C) 2021 Intel Corporation. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * */ #include #include #include #include #include "ell/useful.h" #include "src/band.h" #include "src/netdev.h" void band_free(struct band *band) { if (band->he_capabilities) l_queue_destroy(band->he_capabilities, l_free); l_free(band->freq_attrs); l_free(band); } /* * Rates are stored as they are encoded in the Supported Rates IE. * This data was taken from 802.11 Section 17.3.10.2 Table 17-18 and * Table 17-4. Together we have minimum RSSI required for a given data rate. */ static const struct { int32_t rssi; uint8_t rate; } rate_rssi_map[] = { { -90, 2 }, /* Make something up for 11b rates */ { -88, 4 }, { -86, 11 }, { -84, 22 }, { -82, 12 }, { -81, 18 }, { -79, 24 }, { -77, 36 }, { -74, 48 }, { -70, 72 }, { -66, 96 }, { -65, 108 }, }; static bool peer_supports_rate(const uint8_t *rates, uint8_t rate) { int i; if (rates && rates[1]) { for (i = 0; i < rates[1]; i++) { uint8_t r = rates[i + 2] & 0x7f; if (r == rate) return true; } } return false; } int band_estimate_nonht_rate(const struct band *band, const uint8_t *supported_rates, const uint8_t *ext_supported_rates, int32_t rssi, uint64_t *out_data_rate) { int nrates = L_ARRAY_SIZE(rate_rssi_map); uint8_t max_rate = 0; int i; if (!supported_rates && !ext_supported_rates) return -EINVAL; /* * Start at the back of the array. Rates are generally given in * ascending order, starting at 11b rates, then 11g rates. More often * than not we'll pick the highest rate and avoid unneeded processing */ for (i = band->supported_rates_len - 1; i >= 0; i--) { uint8_t rate = band->supported_rates[i]; int j; if (max_rate >= rate) continue; /* Can this rate be used at the peer's RSSI? */ for (j = 0; j < nrates; j++) if (rate_rssi_map[j].rate == rate) break; if (j == nrates) continue; if (rssi < rate_rssi_map[j].rssi) continue; if (peer_supports_rate(supported_rates, rate) || peer_supports_rate(ext_supported_rates, rate)) max_rate = rate; } if (!max_rate) return -ENETUNREACH; *out_data_rate = max_rate * 500000; return 0; } /* * Base RSSI values for 20MHz (HT, VHT and HE) channel. These values can be * used to calculate the minimum RSSI values for all other channel widths. HT * MCS indexes are grouped into ranges of 8 (per spatial stream), VHT in groups * of 10 and HE in groups of 12. This just means HT will not use the last four * index's of this array, and VHT won't use the last two. * * Note: The values here are not based on anything from 802.11 but data * found elsewhere online (presumably from testing, we hope). The two * indexes for HE (MCS 11/12) are not based on any data, but just * increased by 3dB compared to the previous value. We consider this good * enough for its purpose to estimate the date rate for network/BSS * preference. */ static const int32_t ht_vht_he_base_rssi[] = { -82, -79, -77, -74, -70, -66, -65, -64, -59, -57, -54, -51 }; /* * Data Rate for HT/VHT is obtained according to this formula: * Nsd * Nbpscs * R * Nss / (Tdft + Tgi) * * Where Nsd is [52, 108, 234, 468] for 20/40/80/160 Mhz respectively * Nbpscs is [1, 2, 4, 6, 8] for BPSK/QPSK/16QAM/64QAM/256QAM * R is [1/2, 2/3, 3/4, 5/6] depending on the MCS index * Nss is the number of spatial streams * Tdft = 3.2 us * Tgi = Long/Short GI of 0.8/0.4 us * * Short GI rate can be easily obtained by multiplying by (10 / 9) * * The table was pre-computed using the following python snippet: * rfactors = [ 1/2, 1/2, 3/4, 1/2, 3/4, 2/3, 3/4, 5/6, 3/4, 5/6 ] * nbpscs = [1, 2, 2, 4, 4, 6, 6, 6, 8, 8 ] * nsds = [52, 108, 234, 468] * * for nsd in nsds: * rates = [] * for i in xrange(0, 10): * data_rate = (nsd * rfactors[i] * nbpscs[i]) / 0.004 * rates.append(int(data_rate) * 1000) * print('rates for nsd: ' + nsd + ': ' + rates) */ static const uint64_t ht_vht_rates[4][10] = { [OFDM_CHANNEL_WIDTH_20MHZ] = { 6500000ULL, 13000000ULL, 19500000ULL, 26000000ULL, 39000000ULL, 52000000ULL, 58500000ULL, 65000000ULL, 78000000ULL, 86666000ULL }, [OFDM_CHANNEL_WIDTH_40MHZ] = { 13500000ULL, 27000000ULL, 40500000ULL, 54000000ULL, 81000000ULL, 108000000ULL, 121500000ULL, 135000000ULL, 162000000ULL, 180000000ULL, }, [OFDM_CHANNEL_WIDTH_80MHZ] = { 29250000ULL, 58500000ULL, 87750000ULL, 117000000ULL, 175500000ULL, 234000000ULL, 263250000ULL, 292500000ULL, 351000000ULL, 390000000ULL, }, [OFDM_CHANNEL_WIDTH_160MHZ] = { 58500000ULL, 117000000ULL, 175500000ULL, 234000000ULL, 351000000ULL, 468000000ULL, 526500000ULL, 585000000ULL, 702000000ULL, 780000000ULL, } }; /* * Both HT and VHT rates are calculated in the same fashion. The only difference * is a relative MCS index is used for HT since, for each NSS, the formula * is the same with relative index's. This is why this is called with index % 8 * for HT, but not VHT. */ bool band_ofdm_rate(uint8_t index, enum ofdm_channel_width width, int32_t rssi, uint8_t nss, bool sgi, uint64_t *data_rate) { uint64_t rate; int32_t width_adjust = width * 3; if (rssi < ht_vht_he_base_rssi[index] + width_adjust) return false; rate = ht_vht_rates[width][index]; if (sgi) rate = rate / 9 * 10; rate *= nss; *data_rate = rate; return true; } static bool find_best_mcs_ht(const struct band *band, const uint8_t *tx_mcs_set, uint8_t max_mcs, enum ofdm_channel_width width, int32_t rssi, bool sgi, uint64_t *out_data_rate) { int i; /* * TODO: Support MCS values 32 - 76 * * The MCS values > 31 use an unequal modulation, and the number of * supported MCS indexes per NSS differs. We do not consider them * here for now to keep things simple(r). */ for (i = max_mcs; i >= 0; i--) { if (!test_bit(band->ht_mcs_set, i)) continue; if (!test_bit(tx_mcs_set, i)) continue; if (band_ofdm_rate(i % 8, width, rssi, (i / 8) + 1, sgi, out_data_rate)) return true; } return false; } int band_estimate_ht_rx_rate(const struct band *band, const uint8_t *htc, const uint8_t *hto, int32_t rssi, uint64_t *out_data_rate) { uint8_t channel_offset; int max_mcs = 31; bool sgi; uint8_t unequal_tx_mcs_set[16]; const uint8_t *tx_mcs_set; if (!band->ht_supported) return -ENOTSUP; if (!htc || !hto) return -ENOTSUP; memset(unequal_tx_mcs_set, 0, sizeof(unequal_tx_mcs_set)); tx_mcs_set = htc + 5; /* * Check 'Tx MCS Set Defined' at bit 96 and 'Tx MCS Set Unequal' at * bit 97 of the Supported MCS Set field. Also extract 'Tx Maximum * Number of Spatial Streams Supported' field at bits 98 and 99. * * Note 44 on page 1662 of 802.11-2016 states: * "How a non-AP STA determines an AP's HT MCS transmission support, * if the Tx MCS Set subfield in the HT Capabilities element * advertised by the AP is equal to 0 or if he Tx Rx MCS Set Not Equal * subfield in that element is equal to 1, is implementation dependent. * The non-AP STA might conservatively use the basic HT-MCS set, or it * might use knowledge of past transmissions by the AP, or it might * use other means. */ if (test_bit(tx_mcs_set, 96)) { if (test_bit(tx_mcs_set, 97)) { uint8_t max_nss = bit_field(tx_mcs_set[12], 2, 2); max_mcs = max_nss * 4 + 7; /* * For purposes of finding the best MCS below, assume * the AP can send any MCS up to max_nss (i.e 0-7 for * 1 nss, 0-15 for 2 nss, 0-23 for 3 nss, 0-31 for 4 */ memset(unequal_tx_mcs_set, 0xff, max_nss + 1); tx_mcs_set = unequal_tx_mcs_set; } } else max_mcs = 7; /* Test for 40 Mhz operation */ channel_offset = bit_field(hto[3], 0, 2); if (test_bit(hto + 3, 2) && (channel_offset == 1 || channel_offset == 3)) { sgi = test_bit(band->ht_capabilities, 6) && test_bit(htc + 2, 6); if (find_best_mcs_ht(band, tx_mcs_set, max_mcs, OFDM_CHANNEL_WIDTH_40MHZ, rssi, sgi, out_data_rate)) return 0; } sgi = test_bit(band->ht_capabilities, 5) && test_bit(htc + 2, 5); if (find_best_mcs_ht(band, tx_mcs_set, max_mcs, OFDM_CHANNEL_WIDTH_20MHZ, rssi, sgi, out_data_rate)) return 0; return -ENETUNREACH; } static bool find_best_mcs_vht(uint8_t max_index, enum ofdm_channel_width width, int32_t rssi, uint8_t nss, bool sgi, uint64_t *out_data_rate) { int i; /* * Iterate over all available MCS indexes to find the best one * we can use. Note that band_ofdm_rate() will return false if a * given combination cannot be used due to rssi being too low. * * Also, Certain MCS/Width/NSS combinations are not valid, * refer to IEEE 802.11-2016 Section 21.5 for more details */ for (i = max_index; i >= 0; i--) if (band_ofdm_rate(i, width, rssi, nss, sgi, out_data_rate)) return true; return false; } static bool find_best_mcs_nss(const uint8_t *rx_map, const uint8_t *tx_map, uint8_t value0, uint8_t value1, uint8_t value2, uint32_t *mcs_out, uint32_t *nss_out) { uint32_t nss = 0; uint32_t max_mcs = 0; int bitoffset; for (bitoffset = 14; bitoffset >= 0; bitoffset -= 2) { uint8_t rx_val = bit_field(rx_map[bitoffset / 8], bitoffset % 8, 2); uint8_t tx_val = bit_field(tx_map[bitoffset / 8], bitoffset % 8, 2); /* * 0 indicates support for MCS 0 - value0 * 1 indicates support for MCS 0 - value1 * 2 indicates support for MCS 0 - value2 * 3 indicates no support */ if (rx_val == 3 || tx_val == 3) continue; /* rx_val/tx_val tells us which value# to use */ max_mcs = minsize(rx_val, tx_val); switch (max_mcs) { case 0: max_mcs = value0; break; case 1: max_mcs = value1; break; case 2: max_mcs = value2; break; } nss = bitoffset / 2 + 1; break; } if (!nss) return false; *nss_out = nss; *mcs_out = max_mcs; return true; } /* * IEEE 802.11 - Table 9-250 * * For simplicity, we are ignoring the Extended BSS BW support, per NOTE 11: * * NOTE 11-A receiving STA in which dot11VHTExtendedNSSCapable is false will * ignore the Extended NSS BW Support subfield and effectively evaluate this * table only at the entries where Extended NSS BW Support is 0. * * This also allows us to group the 160/80+80 widths together, since they are * the same when Extended NSS BW is zero. */ int band_estimate_vht_rx_rate(const struct band *band, const uint8_t *vhtc, const uint8_t *vhto, const uint8_t *htc, const uint8_t *hto, int32_t rssi, uint64_t *out_data_rate) { uint32_t nss = 0; uint32_t max_mcs = 7; /* MCS 0-7 for NSS:1 is always supported */ const uint8_t *rx_mcs_map; const uint8_t *tx_mcs_map; uint8_t chan_width; uint8_t channel_offset; bool sgi; if (!band->vht_supported || !band->ht_supported) return -ENOTSUP; if (!vhtc || !vhto || !htc || !hto) return -ENOTSUP; if (vhto[2] > 3) return -EBADMSG; /* * Find the highest NSS/MCS index combination. Since this is used by * STAs, we try to estimate our 'download' speed from the AP/peer. * Hence we look at the TX MCS map of the peer and our own RX MCS map * to find an overlapping combination that works */ rx_mcs_map = band->vht_mcs_set; tx_mcs_map = vhtc + 2 + 8; if (!find_best_mcs_nss(rx_mcs_map, tx_mcs_map, 7, 8, 9, &max_mcs, &nss)) return -EBADMSG; /* * There is no way to know whether a peer would send us packets using * the short guard interval (SGI.) SGI capability is only used to * indicate whether the peer can accept packets that we send this way. * Here we make the assumption that if the peer has the capability to * accept packets using SGI and we have the capability to do so, then * SGI will be used * * Also, we assume that the highest bandwidth will result in the * highest rate for any given rssi. Even accounting for invalid * MCS/Width/NSS combinations, the higher channel width results * in better data rate at [mcs index - 2] compared to [mcs index] of * a next lower bandwidth. */ /* See if 160 Mhz operation is available */ chan_width = bit_field(band->vht_capabilities[0], 2, 2); if (chan_width != 1 && chan_width != 2) goto try_vht80; /* * Channel Width is set to 2 or 3, or 1 and * channel center frequency segment 1 is non-zero */ if (vhto[2] == 2 || vhto[2] == 3 || (vhto[2] == 1 && vhto[4])) { sgi = test_bit(band->vht_capabilities, 6) && test_bit(vhtc + 2, 6); if (find_best_mcs_vht(max_mcs, OFDM_CHANNEL_WIDTH_160MHZ, rssi, nss, sgi, out_data_rate)) return 0; } try_vht80: if (vhto[2] == 1) { sgi = test_bit(band->vht_capabilities, 5) && test_bit(vhtc + 2, 5); if (find_best_mcs_vht(max_mcs, OFDM_CHANNEL_WIDTH_80MHZ, rssi, nss, sgi, out_data_rate)) return 0; } /* Otherwise, assume 20/40 Operation */ channel_offset = bit_field(hto[3], 0, 2); /* Test for 40 Mhz operation */ if (test_bit(hto + 3, 2) && (channel_offset == 1 || channel_offset == 3)) { sgi = test_bit(band->ht_capabilities, 6) && test_bit(htc + 2, 6); if (find_best_mcs_vht(max_mcs, OFDM_CHANNEL_WIDTH_40MHZ, rssi, nss, sgi, out_data_rate)) return 0; } sgi = test_bit(band->ht_capabilities, 5) && test_bit(htc + 2, 5); if (find_best_mcs_vht(max_mcs, OFDM_CHANNEL_WIDTH_20MHZ, rssi, nss, sgi, out_data_rate)) return 0; return -ENETUNREACH; } /* * Data Rate for HE is much the same as HT/VHT but some additional MCS indexes * were added. This mean rfactors, and nbpscs will contain two additional * values: * * rfactors.extend([3/4, 5/6]) * nbpscs.extend([10, 10]) * * The guard interval also differs: * * Tdft = 12.8us * Tgi = 0.8, 1.6 or 2.3us * * The Nsd values for HE are: * * Nsd = [234, 468, 980, 1960] * * The formula is identical to HT/VHT: * * Nsd * Nbpscs * R * Nss / (Tdft + Tgi) * * Note: The table below assumes a 0.8us GI. There isn't any way to know what * GI will be used for an actual connection, so assume the best. */ static uint64_t he_rates[4][12] = { [OFDM_CHANNEL_WIDTH_20MHZ] = { 8600000ULL, 17200000ULL, 25800000ULL, 34400000ULL, 51600000ULL, 68800000ULL, 77400000ULL, 86000000ULL, 103200000ULL, 114700000ULL, 129000000ULL, 143300000ULL, }, [OFDM_CHANNEL_WIDTH_40MHZ] = { 17200000ULL, 34400000ULL, 51600000ULL, 68800000ULL, 103200000ULL, 137600000ULL, 154900000ULL, 172000000ULL, 206500000ULL, 229400000ULL, 258000000ULL, 286800000ULL, }, [OFDM_CHANNEL_WIDTH_80MHZ] = { 36000000ULL, 72000000ULL, 108000000ULL, 144100000ULL, 216200000ULL, 288200000ULL, 324300000ULL, 360300000ULL, 432400000ULL, 480400000ULL, 540400000ULL, 600500000ULL, }, [OFDM_CHANNEL_WIDTH_160MHZ] = { 72000000ULL, 144100000ULL, 216200000ULL, 288200000ULL, 432400000ULL, 576500000ULL, 648500000ULL, 720600000ULL, 864700000ULL, 960800000ULL, 1080900000ULL, 1201000000ULL, }, }; static bool band_he_rate(uint8_t index, enum ofdm_channel_width width, int32_t rssi, uint8_t nss, uint64_t *data_rate) { uint64_t rate; int32_t width_adjust; width_adjust = width * 3; if (rssi < ht_vht_he_base_rssi[index] + width_adjust) return false; rate = he_rates[width][index]; rate *= nss; *data_rate = rate; return true; } static bool find_rate_he(const uint8_t *rx_map, const uint8_t *tx_map, enum ofdm_channel_width width, int32_t rssi, uint64_t *out_data_rate) { uint32_t nss; uint32_t max_mcs; int i; if (!find_best_mcs_nss(rx_map, tx_map, 7, 9, 11, &max_mcs, &nss)) return false; for (i = max_mcs; i >= 0; i--) if (band_he_rate(i, width, rssi, nss, out_data_rate)) return true; return false; } /* * HE data rate is calculated based on 802.11ax - Section 27.5 */ int band_estimate_he_rx_rate(const struct band *band, const uint8_t *hec, int32_t rssi, uint64_t *out_data_rate) { enum ofdm_channel_width width = OFDM_CHANNEL_WIDTH_20MHZ; int i; const struct band_he_capabilities *he_cap = NULL; const struct l_queue_entry *entry; const uint8_t *rx_map; const uint8_t *tx_map; uint64_t rate = 0; uint64_t new_rate = 0; uint8_t width_set; if (!hec || !band->he_capabilities) return -EBADMSG; for (entry = l_queue_get_entries(band->he_capabilities); entry; entry = entry->next) { const struct band_he_capabilities *cap = entry->data; /* * TODO: Station type is assumed here since it is the only * consumer of these data rate estimation APIs. If this * changes the iftype would need to be passed in. */ if (cap->iftypes & (1 << NETDEV_IFTYPE_STATION)) { he_cap = cap; break; } } if (!he_cap) return -ENOTSUP; /* AND the width sets, giving the widths supported by both */ width_set = bit_field(he_cap->he_phy_capa[0], 1, 7) & bit_field((hec + 6)[0], 1, 7); /* * The HE-MCS maps are 17 bytes into the HE Capabilities IE, and * alternate RX/TX every 2 bytes. Start the TX map 17 + 2 bytes * into the MCS set. For each MCS set find the best data rate. */ rx_map = he_cap->he_mcs_set; tx_map = hec + 19; /* * 802.11ax Table 9-322b * * B3 indicates support for 80+80MHz MCS set */ if (test_bit(&width_set, 3)) { if (find_rate_he(rx_map + 8, tx_map + 8, OFDM_CHANNEL_WIDTH_160MHZ, rssi, &new_rate)) rate = new_rate; } /* B2 indicates support for 160MHz MCS set */ if (test_bit(&width_set, 2)) { if (find_rate_he(rx_map + 4, tx_map + 4, OFDM_CHANNEL_WIDTH_160MHZ, rssi, &new_rate) && new_rate > rate) rate = new_rate; } /* B1 indicates support for 80MHz */ if (test_bit(&width_set, 1)) width = OFDM_CHANNEL_WIDTH_80MHZ; /* B0 indicates support for 40MHz */ if (test_bit(&width_set, 0)) width = OFDM_CHANNEL_WIDTH_40MHZ; /* <= 80MHz MCS set */ for (i = width; i >= OFDM_CHANNEL_WIDTH_20MHZ; i--) { if (find_rate_he(rx_map, tx_map, i, rssi, &new_rate)) { if (new_rate > rate) rate = new_rate; break; } } if (!rate) return -EBADMSG; *out_data_rate = rate; return 0; } static int band_channel_info_get_bandwidth(const struct band_chandef *info) { switch (info->channel_width) { case BAND_CHANDEF_WIDTH_20NOHT: case BAND_CHANDEF_WIDTH_20: return 20; case BAND_CHANDEF_WIDTH_40: return 40; case BAND_CHANDEF_WIDTH_80: return 80; case BAND_CHANDEF_WIDTH_80P80: case BAND_CHANDEF_WIDTH_160: return 160; default: break; } return -ENOTSUP; } struct operating_class_info { uint32_t starting_frequency; uint32_t flags; uint8_t channel_set[60]; uint8_t center_frequencies[30]; uint16_t channel_spacing; uint8_t operating_class; }; enum operating_class_flags { PRIMARY_CHANNEL_UPPER = 0x1, PRIMARY_CHANNEL_LOWER = 0x2, PLUS80 = 0x4, }; static const struct operating_class_info e4_operating_classes[] = { { .operating_class = 81, .starting_frequency = 2407, .channel_set = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }, .channel_spacing = 20, }, { .operating_class = 82, .starting_frequency = 2414, .channel_set = { 14 }, .channel_spacing = 20, }, { .operating_class = 83, .starting_frequency = 2407, .channel_set = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_LOWER, }, { .operating_class = 84, .starting_frequency = 2407, .channel_set = { 5, 6, 7, 8, 9, 10, 11, 12, 13 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_UPPER, }, { .operating_class = 115, .starting_frequency = 5000, .channel_set = { 36, 40, 44, 48}, .channel_spacing = 20, }, { .operating_class = 116, .starting_frequency = 5000, .channel_set = { 36, 44 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_LOWER, }, { .operating_class = 117, .starting_frequency = 5000, .channel_set = { 40, 48 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_UPPER, }, { .operating_class = 118, .starting_frequency = 5000, .channel_set = { 52, 56, 60, 64}, .channel_spacing = 20, }, { .operating_class = 119, .starting_frequency = 5000, .channel_set = { 52, 60 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_LOWER, }, { .operating_class = 120, .starting_frequency = 5000, .channel_set = { 56, 64 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_UPPER, }, { .operating_class = 121, .starting_frequency = 5000, .channel_set = { 100, 104, 108, 112, 116, 120, 124, 128, 132, 136, 140, 144 }, .channel_spacing = 20, }, { .operating_class = 122, .starting_frequency = 5000, .channel_set = { 100, 108, 116, 124, 132, 140}, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_LOWER, }, { .operating_class = 123, .starting_frequency = 5000, .channel_set = { 104, 112, 120, 128, 136, 144 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_UPPER, }, { .operating_class = 124, .starting_frequency = 5000, .channel_set = { 149, 153, 157, 161 }, .channel_spacing = 20, }, { .operating_class = 125, .starting_frequency = 5000, .channel_set = { 149, 153, 157, 161, 165, 169, 173, 177 }, .channel_spacing = 20, }, { .operating_class = 126, .starting_frequency = 5000, .channel_set = { 149, 157, 165, 173 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_LOWER, }, { .operating_class = 127, .starting_frequency = 5000, .channel_set = { 153, 161, 169, 177 }, .channel_spacing = 40, .flags = PRIMARY_CHANNEL_UPPER, }, { .operating_class = 128, .starting_frequency = 5000, .channel_spacing = 80, .center_frequencies = { 42, 58, 106, 122, 138, 155, 171 }, }, { .operating_class = 129, .starting_frequency = 5000, .channel_spacing = 160, .center_frequencies = { 50, 114, 163 }, }, { .operating_class = 130, .starting_frequency = 5000, .channel_spacing = 80, .center_frequencies = { 42, 58, 106, 122, 138, 155, 171 }, .flags = PLUS80, }, { .operating_class = 131, .starting_frequency = 5950, .channel_spacing = 20, .channel_set = { 1, 5, 9, 13, 17, 21, 25, 29, 33, 37, 41, 45, 49, 53, 57, 61, 65, 69, 73, 77, 81, 85, 89, 93, 97, 101, 105, 109, 113, 117, 121, 125, 129, 133, 137, 141, 145, 149, 153, 157, 161, 165, 169, 173, 177, 181, 185, 189, 193, 197, 201, 205, 209, 213, 217, 221, 225, 229, 233 }, }, { .operating_class = 132, .starting_frequency = 5950, .channel_spacing = 40, .center_frequencies = { 3, 11, 19, 27, 35, 43, 51, 59, 67, 75, 83, 91, 99, 107, 115, 123, 131, 139, 147, 155, 163, 171, 179, 187, 195, 203, 211, 219, 227 }, }, { .operating_class = 133, .starting_frequency = 5950, .channel_spacing = 80, .center_frequencies = { 7, 23, 39, 55, 71, 87, 103, 119, 135, 151, 167, 183, 199, 215 }, }, { .operating_class = 134, .starting_frequency = 5950, .channel_spacing = 160, .center_frequencies = { 15, 47, 79, 111, 143, 175, 207 }, }, { .operating_class = 135, .starting_frequency = 5950, .channel_spacing = 80, .center_frequencies = { 7, 23, 39, 55, 71, 87, 102, 119, 135, 151, 167, 183, 199, 215 }, .flags = PLUS80, }, { .operating_class = 136, .starting_frequency = 5950, .channel_spacing = 20, .center_frequencies = { 2 }, } }; static const struct operating_class_info *e4_find_opclass(uint32_t opclass) { unsigned int i; for (i = 0; i < L_ARRAY_SIZE(e4_operating_classes); i++) { if (e4_operating_classes[i].operating_class != opclass) continue; return &e4_operating_classes[i]; } return NULL; } static int e4_channel_to_frequency(const struct operating_class_info *info, uint32_t channel) { unsigned int i; unsigned int offset; for (i = 0; info->channel_set[i] && i < L_ARRAY_SIZE(info->channel_set); i++) { if (info->channel_set[i] != channel) continue; return channel * 5 + info->starting_frequency; } /* * Only classes in Table E4 with center frequencies are 128-130, * which use 20 Mhz wide channels. Since E4 gives a center frequency, * calculate the channel offset based on channel spacing. * * Typically +/- 6 for 80 Mhz channels and +/- 14 for 160 Mhz channels */ offset = (info->channel_spacing - 20) / 5 / 2; /* * Check that the channel is in the frequency range given by one of * the center frequencies listed for the operating class. The channel * must be a valid channel for a lower operating class and spaced * 20 mhz apart */ for (i = 0; info->center_frequencies[i] && i < L_ARRAY_SIZE(info->center_frequencies); i++) { unsigned int upper = info->center_frequencies[i] + offset; unsigned int j = info->center_frequencies[i] - offset; while (j <= upper && channel >= j) { if (channel == j) return channel * 5 + info->starting_frequency; j += 4; } } return -EINVAL; } static int e4_frequency_to_channel(const struct operating_class_info *info, uint32_t frequency) { return (frequency - info->starting_frequency) / 5; } static int e4_has_frequency(const struct operating_class_info *info, uint32_t frequency) { unsigned int i; unsigned int channel = e4_frequency_to_channel(info, frequency); for (i = 0; info->channel_set[i] && i < L_ARRAY_SIZE(info->channel_set); i++) { if (info->channel_set[i] != channel) continue; return 0; } return -ENOENT; } static int e4_has_ccfi(const struct operating_class_info *info, uint32_t center_frequency) { unsigned int i; unsigned int ccfi = e4_frequency_to_channel(info, center_frequency); for (i = 0; info->center_frequencies[i] && i < L_ARRAY_SIZE(info->center_frequencies); i++) { if (info->center_frequencies[i] != ccfi) continue; return 0; } return -ENOENT; } static int e4_class_matches(const struct operating_class_info *info, const struct band_chandef *chandef) { int own_bandwidth = band_channel_info_get_bandwidth(chandef); int r; if (own_bandwidth < 0) return own_bandwidth; switch (chandef->channel_width) { case BAND_CHANDEF_WIDTH_20NOHT: case BAND_CHANDEF_WIDTH_20: case BAND_CHANDEF_WIDTH_40: if (own_bandwidth != info->channel_spacing) return -ENOENT; if (own_bandwidth == 40) { uint32_t behavior; if (chandef->center1_frequency > chandef->frequency) behavior = PRIMARY_CHANNEL_LOWER; else behavior = PRIMARY_CHANNEL_UPPER; if ((info->flags & behavior) != behavior) return -ENOENT; } return e4_has_frequency(info, chandef->frequency); case BAND_CHANDEF_WIDTH_80: case BAND_CHANDEF_WIDTH_160: if (info->flags & PLUS80) return -ENOENT; if (own_bandwidth != info->channel_spacing) return -ENOENT; return e4_has_ccfi(info, chandef->center1_frequency); case BAND_CHANDEF_WIDTH_80P80: if (!(info->flags & PLUS80)) return -ENOENT; r = e4_has_ccfi(info, chandef->center1_frequency); if (r < 0) return r; return e4_has_ccfi(info, chandef->center2_frequency); default: break; } return -ENOTSUP; } int oci_to_frequency(uint32_t operating_class, uint32_t channel) { const struct operating_class_info *info; info = e4_find_opclass(operating_class); if (!info) return -ENOENT; return e4_channel_to_frequency(info, channel); } int oci_verify(const uint8_t oci[static 3], const struct band_chandef *own) { const struct operating_class_info *info; int oci_frequency; int own_bandwidth; int oci_bandwidth; info = e4_find_opclass(oci[0]); if (!info) return -ENOENT; /* * 802.11-2020, 12.2.9: * Verifying that the maximum bandwidth used by the STA to transmit or * receive PPDUs to/from the peer STA from which the OCI was received * is no greater than the bandwidth of the operating class specified * in the Operating Class field of the received OCI */ own_bandwidth = band_channel_info_get_bandwidth(own); if (own_bandwidth < 0) return own_bandwidth; oci_bandwidth = info->channel_spacing; if (info->flags & PLUS80) oci_bandwidth *= 2; if (own_bandwidth > oci_bandwidth) return -EPERM; /* * 802.11-2020, 12.2.9: * Verifying that the primary channel used by the STA to transmit or * receive PPDUs to/from the peer STA from which the OCI was received * is equal to the Primary Channel Number field (for the corresponding * operating class) */ oci_frequency = e4_channel_to_frequency(info, oci[1]); if (oci_frequency < 0) return oci_frequency; if (oci_frequency != (int) own->frequency) return -EPERM; /* * 802.11-2020, 12.2.9: * Verifying that, when 40 MHz bandwidth is used by the STA to transmit * or receive PPDUs to/from the peer STA from which the OCI was * received, the nonprimary 20 MHz used matches the operating class * (i.e., upper/lower behavior) specified in the Operating Class field * of the received OCI * * NOTE: For now we only check this if the STA and peer are operating * on 40 Mhz channels. If the STA is operating on 40 Mhz while the * peer is operating on 80 or 160 Mhz wide channels, then only the * primary channel validation is performed * * With 6GHz operating classes there is no concept of upper/lower 40mhz * channels, therefore this special handling list not needed. */ if (own_bandwidth == 40 && oci_bandwidth == 40 && info->operating_class < 131) { uint32_t behavior; /* * - Primary Channel Upper Behavior -> Secondary channel below * primary channel. Or HT40MINUS. * - Primary Channel Lower Behavior -> Secondary channel above * primary channel. Or HT40PLUS. */ if (own->center1_frequency > own->frequency) behavior = PRIMARY_CHANNEL_LOWER; else behavior = PRIMARY_CHANNEL_UPPER; if ((info->flags & behavior) != behavior) return -EPERM; } /* * 802.11-2020, 12.2.9: * Verifying that, if operating an 80+80 MHz operating class, the * frequency segment 1 channel number used by the STA to transmit or * receive PPDUs to/from the peer STA from which the OCI was received * is equal to the Frequency Segment 1 Channel Number field of the OCI. */ if (own->channel_width == BAND_CHANDEF_WIDTH_80P80) { uint32_t freq_segment1_chan_num; if (!(info->flags & PLUS80)) return -EPERM; freq_segment1_chan_num = e4_frequency_to_channel(info, own->center2_frequency); if (freq_segment1_chan_num != oci[2]) return -EPERM; } return 0; } int oci_from_chandef(const struct band_chandef *own, uint8_t oci[static 3]) { unsigned int i; for (i = 0; i < L_ARRAY_SIZE(e4_operating_classes); i++) { const struct operating_class_info *info = &e4_operating_classes[i]; if (e4_class_matches(info, own) < 0) continue; oci[0] = info->operating_class; oci[1] = e4_frequency_to_channel(info, own->frequency); if (own->center2_frequency) oci[2] = e4_frequency_to_channel(info, own->center2_frequency); else oci[2] = 0; return 0; } return -ENOENT; } /* Find an HT chandef for the frequency */ int band_freq_to_ht_chandef(uint32_t freq, const struct band_freq_attrs *attr, struct band_chandef *chandef) { enum band_freq band; enum band_chandef_width width; unsigned int i; const struct operating_class_info *best = NULL; if (attr->disabled || !attr->supported) return -EINVAL; if (!band_freq_to_channel(freq, &band)) return -EINVAL; for (i = 0; i < L_ARRAY_SIZE(e4_operating_classes); i++) { const struct operating_class_info *info = &e4_operating_classes[i]; enum band_chandef_width w; if (e4_has_frequency(info, freq) < 0) continue; /* Any restrictions for this channel width? */ switch (info->channel_spacing) { case 20: w = BAND_CHANDEF_WIDTH_20; break; case 40: w = BAND_CHANDEF_WIDTH_40; /* 6GHz remove the upper/lower 40mhz channel concept */ if (band == BAND_FREQ_6_GHZ) break; if (info->flags & PRIMARY_CHANNEL_LOWER && attr->no_ht40_plus) continue; if (info->flags & PRIMARY_CHANNEL_UPPER && attr->no_ht40_minus) continue; break; default: continue; } if (!best || best->channel_spacing < info->channel_spacing) { best = info; width = w; } } if (!best) return -ENOENT; chandef->frequency = freq; chandef->channel_width = width; /* * Choose a secondary channel frequency: * - 20mhz no secondary * - 40mhz we can base the selection off the channel flags, either * higher or lower. */ _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"") switch (width) { _Pragma("GCC diagnostic pop") case BAND_CHANDEF_WIDTH_20: return 0; case BAND_CHANDEF_WIDTH_40: if (band == BAND_FREQ_6_GHZ) return 0; if (best->flags & PRIMARY_CHANNEL_UPPER) chandef->center1_frequency = freq - 10; else chandef->center1_frequency = freq + 10; return 0; default: /* Should never happen */ return -EINVAL; } return 0; } uint8_t band_freq_to_channel(uint32_t freq, enum band_freq *out_band) { unsigned int i; enum band_freq band = 0; uint32_t channel = 0; if (freq >= 2412 && freq <= 2484) { if (freq == 2484) channel = 14; else { channel = freq - 2407; if (channel % 5) return 0; channel /= 5; } band = BAND_FREQ_2_4_GHZ; goto check_e4; } if (freq >= 5005 && freq < 5900) { if (freq % 5) return 0; channel = (freq - 5000) / 5; band = BAND_FREQ_5_GHZ; goto check_e4; } if (freq >= 4905 && freq < 5000) { if (freq % 5) return 0; channel = (freq - 4000) / 5; band = BAND_FREQ_5_GHZ; goto check_e4; } if (freq > 5950 && freq <= 7115) { if (freq % 5) return 0; channel = (freq - 5950) / 5; band = BAND_FREQ_6_GHZ; goto check_e4; } if (freq == 5935) { band = BAND_FREQ_6_GHZ; channel = 2; } if (!band || !channel) return 0; check_e4: for (i = 0; i < L_ARRAY_SIZE(e4_operating_classes); i++) { const struct operating_class_info *info = &e4_operating_classes[i]; if (e4_has_frequency(info, freq) == 0 || e4_has_ccfi(info, freq) == 0) { if (out_band) *out_band = band; return channel; } } return 0; } uint32_t band_channel_to_freq(uint8_t channel, enum band_freq band) { unsigned int i; uint32_t freq = 0; if (band == BAND_FREQ_2_4_GHZ) { if (channel >= 1 && channel <= 13) freq = 2407 + 5 * channel; else if (channel == 14) freq = 2484; goto check_e4; } if (band == BAND_FREQ_5_GHZ) { if (channel >= 1 && channel <= 179) freq = 5000 + 5 * channel; else if (channel >= 181 && channel <= 199) freq = 4000 + 5 * channel; goto check_e4; } if (band == BAND_FREQ_6_GHZ) { /* operating class 136 */ if (channel == 2) { freq = 5935; goto check_e4; } /* Channels increment by 4, starting with 1 */ if (channel % 4 != 1) return 0; if (channel < 1 || channel > 233) return 0; /* operating classes 131, 132, 133, 134, 135 */ freq = 5950 + 5 * channel; } check_e4: for (i = 0; i < L_ARRAY_SIZE(e4_operating_classes); i++) { const struct operating_class_info *info = &e4_operating_classes[i]; if (e4_has_frequency(info, freq) == 0 || e4_has_ccfi(info, freq) == 0) return freq; } return 0; } static const char *const oper_class_us_codes[] = { "US", "CA" }; static const char *const oper_class_eu_codes[] = { "AL", "AM", "AT", "AZ", "BA", "BE", "BG", "BY", "CH", "CY", "CZ", "DE", "DK", "EE", "EL", "ES", "FI", "FR", "GE", "HR", "HU", "IE", "IS", "IT", "LI", "LT", "LU", "LV", "MD", "ME", "MK", "MT", "NL", "NO", "PL", "PT", "RO", "RS", "RU", "SE", "SI", "SK", "TR", "UA", "UK" }; /* Annex E, table E-1 */ static const uint8_t oper_class_us_to_global[] = { [1] = 115, [2] = 118, [3] = 124, [4] = 121, [5] = 125, [6] = 103, [7] = 103, [8] = 102, [9] = 102, [10] = 101, [11] = 101, [12] = 81, [13] = 94, [14] = 95, [15] = 96, [22] = 116, [23] = 119, [24] = 122, [25] = 126, [26] = 126, [27] = 117, [28] = 120, [29] = 123, [30] = 127, [31] = 127, [32] = 83, [33] = 84, [34] = 180, /* 128 - 130 is a 1 to 1 mapping */ }; /* Annex E, table E-2 */ static const uint8_t oper_class_eu_to_global[] = { [1] = 115, [2] = 118, [3] = 121, [4] = 81, [5] = 116, [6] = 119, [7] = 122, [8] = 117, [9] = 120, [10] = 123, [11] = 83, [12] = 84, [17] = 125, [18] = 130, /* 128 - 130 is a 1 to 1 mapping */ }; /* Annex E, table E-3 */ static const uint8_t oper_class_jp_to_global[] = { [1] = 115, [2] = 112, [3] = 112, [4] = 112, [5] = 112, [6] = 112, [7] = 109, [8] = 109, [9] = 109, [10] = 109, [11] = 109, [12] = 113, [13] = 113, [14] = 113, [15] = 113, [16] = 110, [17] = 110, [18] = 110, [19] = 110, [20] = 110, [21] = 114, [22] = 114, [23] = 114, [24] = 114, [25] = 111, [26] = 111, [27] = 111, [28] = 111, [29] = 111, [30] = 81, [31] = 82, [32] = 118, [33] = 118, [34] = 121, [35] = 121, [36] = 116, [37] = 119, [38] = 119, [39] = 122, [40] = 122, [41] = 117, [42] = 120, [43] = 120, [44] = 123, [45] = 123, [46] = 104, [47] = 104, [48] = 104, [49] = 104, [50] = 104, [51] = 105, [52] = 105, [53] = 105, [54] = 105, [55] = 105, [56] = 83, [57] = 84, [58] = 121, [59] = 180, /* 128 - 130 is a 1 to 1 mapping */ }; /* Annex E, table E-4 (only 2.4GHz, 4.9 / 5GHz, and 6GHz bands) */ static const enum band_freq oper_class_to_band_global[] = { [81 ... 84] = BAND_FREQ_2_4_GHZ, [104 ... 130] = BAND_FREQ_5_GHZ, [131 ... 136] = BAND_FREQ_6_GHZ, }; /* Annex E, table E-5 */ static const uint8_t oper_class_cn_to_global[] = { [1] = 115, [2] = 118, [3] = 125, [4] = 116, [5] = 119, [6] = 126, [7] = 81, [8] = 83, [9] = 84, /* 128 - 130 is a 1 to 1 mapping */ }; enum band_freq band_oper_class_to_band(const uint8_t *country, uint8_t oper_class) { unsigned int i; int table = 0; if (country && country[2] >= 1 && country[2] <= 5) table = country[2]; else if (country) { for (i = 0; i < L_ARRAY_SIZE(oper_class_us_codes); i++) if (!memcmp(oper_class_us_codes[i], country, 2)) { /* Use table E-1 */ table = 1; break; } for (i = 0; i < L_ARRAY_SIZE(oper_class_eu_codes); i++) if (!memcmp(oper_class_eu_codes[i], country, 2)) { /* Use table E-2 */ table = 2; break; } if (!memcmp("JP", country, 2)) /* Use table E-3 */ table = 3; if (!memcmp("CN", country, 2)) /* Use table E-5 */ table = 5; } switch (table) { case 1: if (oper_class < L_ARRAY_SIZE(oper_class_us_to_global)) oper_class = oper_class_us_to_global[oper_class]; break; case 2: if (oper_class < L_ARRAY_SIZE(oper_class_eu_to_global)) oper_class = oper_class_eu_to_global[oper_class]; break; case 3: if (oper_class < L_ARRAY_SIZE(oper_class_jp_to_global)) oper_class = oper_class_jp_to_global[oper_class]; break; case 5: if (oper_class < L_ARRAY_SIZE(oper_class_cn_to_global)) oper_class = oper_class_cn_to_global[oper_class]; break; } if (oper_class < L_ARRAY_SIZE(oper_class_to_band_global)) return oper_class_to_band_global[oper_class]; else return 0; } const char *band_chandef_width_to_string(enum band_chandef_width width) { switch (width) { case BAND_CHANDEF_WIDTH_20NOHT: return "20MHz (no-HT)"; case BAND_CHANDEF_WIDTH_20: return "20MHz"; case BAND_CHANDEF_WIDTH_40: return "40MHz"; case BAND_CHANDEF_WIDTH_80: return "80MHz"; case BAND_CHANDEF_WIDTH_80P80: return "80+80MHz"; case BAND_CHANDEF_WIDTH_160: return "160MHz"; } return NULL; }