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

Configure Feed

Select the types of activity you want to include in your feed.

wifi: mac80211: add initial UHR support

Add support for making UHR connections and accepting AP
stations with UHR support.

Link: https://patch.msgid.link/20260130164259.7185980484eb.Ieec940b58dbf8115dab7e1e24cb5513f52c8cb2f@changeid
Signed-off-by: Johannes Berg <johannes.berg@intel.com>

+370 -39
+33 -2
include/net/mac80211.h
··· 7 7 * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> 8 8 * Copyright 2013-2014 Intel Mobile Communications GmbH 9 9 * Copyright (C) 2015 - 2017 Intel Deutschland GmbH 10 - * Copyright (C) 2018 - 2025 Intel Corporation 10 + * Copyright (C) 2018 - 2026 Intel Corporation 11 11 */ 12 12 13 13 #ifndef MAC80211_H ··· 706 706 * @pwr_reduction: power constraint of BSS. 707 707 * @eht_support: does this BSS support EHT 708 708 * @epcs_support: does this BSS support EPCS 709 + * @uhr_support: does this BSS support UHR 709 710 * @csa_active: marks whether a channel switch is going on. 710 711 * @mu_mimo_owner: indicates interface owns MU-MIMO capability 711 712 * @chanctx_conf: The channel context this interface is assigned to, or %NULL ··· 833 832 u8 pwr_reduction; 834 833 bool eht_support; 835 834 bool epcs_support; 835 + bool uhr_support; 836 + 836 837 bool csa_active; 837 838 838 839 bool mu_mimo_owner; ··· 1601 1598 RX_ENC_VHT, 1602 1599 RX_ENC_HE, 1603 1600 RX_ENC_EHT, 1601 + RX_ENC_UHR, 1604 1602 }; 1605 1603 1606 1604 /** ··· 1635 1631 * @antenna: antenna used 1636 1632 * @rate_idx: index of data rate into band's supported rates or MCS index if 1637 1633 * HT or VHT is used (%RX_FLAG_HT/%RX_FLAG_VHT) 1638 - * @nss: number of streams (VHT, HE and EHT only) 1634 + * @nss: number of streams (VHT, HE, EHT and UHR only) 1639 1635 * @flag: %RX_FLAG_\* 1640 1636 * @encoding: &enum mac80211_rx_encoding 1641 1637 * @bw: &enum rate_info_bw ··· 1646 1642 * @eht: EHT specific rate information 1647 1643 * @eht.ru: EHT RU, from &enum nl80211_eht_ru_alloc 1648 1644 * @eht.gi: EHT GI, from &enum nl80211_eht_gi 1645 + * @uhr: UHR specific rate information 1646 + * @uhr.ru: UHR RU, from &enum nl80211_eht_ru_alloc 1647 + * @uhr.gi: UHR GI, from &enum nl80211_eht_gi 1648 + * @uhr.elr: UHR ELR MCS was used 1649 + * @uhr.im: UHR interference mitigation was used 1649 1650 * @rx_flags: internal RX flags for mac80211 1650 1651 * @ampdu_reference: A-MPDU reference number, must be a different value for 1651 1652 * each A-MPDU but the same for each subframe within one A-MPDU ··· 1682 1673 u8 ru:4; 1683 1674 u8 gi:2; 1684 1675 } eht; 1676 + struct { 1677 + u8 ru:4; 1678 + u8 gi:2; 1679 + u8 elr:1; 1680 + u8 im:1; 1681 + } uhr; 1685 1682 }; 1686 1683 u8 rate_idx; 1687 1684 u8 nss; ··· 2449 2434 * @he_cap: HE capabilities of this STA 2450 2435 * @he_6ghz_capa: on 6 GHz, holds the HE 6 GHz band capabilities 2451 2436 * @eht_cap: EHT capabilities of this STA 2437 + * @uhr_cap: UHR capabilities of this STA 2452 2438 * @s1g_cap: S1G capabilities of this STA 2453 2439 * @agg: per-link data for multi-link aggregation 2454 2440 * @bandwidth: current bandwidth the station can receive with ··· 2473 2457 struct ieee80211_sta_he_cap he_cap; 2474 2458 struct ieee80211_he_6ghz_capa he_6ghz_capa; 2475 2459 struct ieee80211_sta_eht_cap eht_cap; 2460 + struct ieee80211_sta_uhr_cap uhr_cap; 2476 2461 struct ieee80211_sta_s1g_cap s1g_cap; 2477 2462 2478 2463 struct ieee80211_sta_aggregates agg; ··· 7304 7287 struct ieee80211_vif *vif) 7305 7288 { 7306 7289 return ieee80211_get_eht_iftype_cap(sband, ieee80211_vif_type_p2p(vif)); 7290 + } 7291 + 7292 + /** 7293 + * ieee80211_get_uhr_iftype_cap_vif - return UHR capabilities for sband/vif 7294 + * @sband: the sband to search for the iftype on 7295 + * @vif: the vif to get the iftype from 7296 + * 7297 + * Return: pointer to the struct ieee80211_sta_uhr_cap, or %NULL is none found 7298 + */ 7299 + static inline const struct ieee80211_sta_uhr_cap * 7300 + ieee80211_get_uhr_iftype_cap_vif(const struct ieee80211_supported_band *sband, 7301 + struct ieee80211_vif *vif) 7302 + { 7303 + return ieee80211_get_uhr_iftype_cap(sband, ieee80211_vif_type_p2p(vif)); 7307 7304 } 7308 7305 7309 7306 /**
+1 -1
net/mac80211/Makefile
··· 36 36 tdls.o \ 37 37 ocb.o \ 38 38 airtime.o \ 39 - eht.o 39 + eht.o uhr.o 40 40 41 41 mac80211-$(CONFIG_MAC80211_LEDS) += led.o 42 42 mac80211-$(CONFIG_MAC80211_DEBUGFS) += \
+15 -1
net/mac80211/cfg.c
··· 5 5 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> 6 6 * Copyright 2013-2015 Intel Mobile Communications GmbH 7 7 * Copyright (C) 2015-2017 Intel Deutschland GmbH 8 - * Copyright (C) 2018-2025 Intel Corporation 8 + * Copyright (C) 2018-2026 Intel Corporation 9 9 */ 10 10 11 11 #include <linux/ieee80211.h> ··· 1608 1608 link_conf->eht_mu_beamformer = false; 1609 1609 } 1610 1610 1611 + if (params->uhr_oper) { 1612 + if (!link_conf->eht_support) 1613 + return -EOPNOTSUPP; 1614 + 1615 + link_conf->uhr_support = true; 1616 + } 1617 + 1611 1618 if (sdata->vif.type == NL80211_IFTYPE_AP && 1612 1619 params->mbssid_config.tx_wdev) { 1613 1620 err = ieee80211_set_ap_mbssid_options(sdata, ··· 2092 2085 params->vht_capa || 2093 2086 params->he_capa || 2094 2087 params->eht_capa || 2088 + params->uhr_capa || 2095 2089 params->s1g_capa || 2096 2090 params->opmode_notif_used; 2097 2091 ··· 2169 2161 params->he_capa_len, 2170 2162 params->eht_capa, 2171 2163 params->eht_capa_len, 2164 + link_sta); 2165 + 2166 + if (params->uhr_capa) 2167 + ieee80211_uhr_cap_ie_to_sta_uhr_cap(sdata, sband, 2168 + params->uhr_capa, 2169 + params->uhr_capa_len, 2172 2170 link_sta); 2173 2171 2174 2172 if (params->s1g_capa)
+17 -2
net/mac80211/ieee80211_i.h
··· 5 5 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 6 6 * Copyright 2007-2010 Johannes Berg <johannes@sipsolutions.net> 7 7 * Copyright 2013-2015 Intel Mobile Communications GmbH 8 - * Copyright (C) 2018-2025 Intel Corporation 8 + * Copyright (C) 2018-2026 Intel Corporation 9 9 */ 10 10 11 11 #ifndef IEEE80211_I_H ··· 394 394 IEEE80211_CONN_MODE_VHT, 395 395 IEEE80211_CONN_MODE_HE, 396 396 IEEE80211_CONN_MODE_EHT, 397 + IEEE80211_CONN_MODE_UHR, 397 398 }; 398 399 399 - #define IEEE80211_CONN_MODE_HIGHEST IEEE80211_CONN_MODE_EHT 400 + #define IEEE80211_CONN_MODE_HIGHEST IEEE80211_CONN_MODE_UHR 400 401 401 402 enum ieee80211_conn_bw_limit { 402 403 IEEE80211_CONN_BW_LIMIT_20, ··· 1825 1824 const struct ieee80211_multi_link_elem *ml_epcs; 1826 1825 const struct ieee80211_bandwidth_indication *bandwidth_indication; 1827 1826 const struct ieee80211_ttlm_elem *ttlm[IEEE80211_TTLM_MAX_CNT]; 1827 + const struct ieee80211_uhr_cap *uhr_cap; 1828 + const struct ieee80211_uhr_operation *uhr_operation; 1828 1829 1829 1830 /* not the order in the psd values is per element, not per chandef */ 1830 1831 struct ieee80211_parsed_tpe tpe; ··· 1851 1848 u8 country_elem_len; 1852 1849 u8 bssid_index_len; 1853 1850 u8 eht_cap_len; 1851 + u8 uhr_cap_len; 1852 + u8 uhr_operation_len; 1854 1853 1855 1854 /* mult-link element can be de-fragmented and thus u8 is not sufficient */ 1856 1855 size_t ml_basic_len; ··· 2696 2691 struct ieee80211_sub_if_data *sdata, 2697 2692 const struct ieee80211_supported_band *sband, 2698 2693 const struct ieee80211_conn_settings *conn); 2694 + int ieee80211_put_uhr_cap(struct sk_buff *skb, 2695 + struct ieee80211_sub_if_data *sdata, 2696 + const struct ieee80211_supported_band *sband); 2699 2697 int ieee80211_put_reg_conn(struct sk_buff *skb, 2700 2698 enum ieee80211_channel_flags flags); 2701 2699 ··· 2873 2865 void ieee80211_process_ml_reconf_resp(struct ieee80211_sub_if_data *sdata, 2874 2866 struct ieee80211_mgmt *mgmt, size_t len); 2875 2867 void ieee80211_stop_mbssid(struct ieee80211_sub_if_data *sdata); 2868 + 2869 + void 2870 + ieee80211_uhr_cap_ie_to_sta_uhr_cap(struct ieee80211_sub_if_data *sdata, 2871 + struct ieee80211_supported_band *sband, 2872 + const struct ieee80211_uhr_cap *uhr_cap, 2873 + u8 uhr_cap_len, 2874 + struct link_sta_info *link_sta); 2876 2875 2877 2876 #if IS_ENABLED(CONFIG_MAC80211_KUNIT_TEST) 2878 2877 #define EXPORT_SYMBOL_IF_MAC80211_KUNIT(sym) EXPORT_SYMBOL_IF_KUNIT(sym)
+13 -2
net/mac80211/main.c
··· 5 5 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 6 6 * Copyright 2013-2014 Intel Mobile Communications GmbH 7 7 * Copyright (C) 2017 Intel Deutschland GmbH 8 - * Copyright (C) 2018-2025 Intel Corporation 8 + * Copyright (C) 2018-2026 Intel Corporation 9 9 */ 10 10 11 11 #include <net/mac80211.h> ··· 1123 1123 int result, i; 1124 1124 enum nl80211_band band; 1125 1125 int channels, max_bitrates; 1126 - bool supp_ht, supp_vht, supp_he, supp_eht, supp_s1g; 1126 + bool supp_ht, supp_vht, supp_he, supp_eht, supp_s1g, supp_uhr; 1127 1127 struct cfg80211_chan_def dflt_chandef = {}; 1128 1128 1129 1129 if (ieee80211_hw_check(hw, QUEUE_CONTROL) && ··· 1237 1237 supp_he = false; 1238 1238 supp_eht = false; 1239 1239 supp_s1g = false; 1240 + supp_uhr = false; 1240 1241 for (band = 0; band < NUM_NL80211_BANDS; band++) { 1241 1242 const struct ieee80211_sband_iftype_data *iftd; 1242 1243 struct ieee80211_supported_band *sband; ··· 1294 1293 1295 1294 supp_he = supp_he || iftd->he_cap.has_he; 1296 1295 supp_eht = supp_eht || iftd->eht_cap.has_eht; 1296 + supp_uhr = supp_uhr || iftd->uhr_cap.has_uhr; 1297 1297 1298 1298 if (band == NL80211_BAND_2GHZ) 1299 1299 he_40_mhz_cap = ··· 1325 1323 1326 1324 /* EHT requires HE support */ 1327 1325 if (WARN_ON(supp_eht && !supp_he)) 1326 + return -EINVAL; 1327 + 1328 + /* UHR requires EHT support */ 1329 + if (WARN_ON(supp_uhr && !supp_eht)) 1328 1330 return -EINVAL; 1329 1331 1330 1332 if (!sband->ht_cap.ht_supported) ··· 1442 1436 sizeof(struct ieee80211_eht_mcs_nss_supp) + 1443 1437 IEEE80211_EHT_PPE_THRES_MAX_LEN; 1444 1438 } 1439 + 1440 + if (supp_uhr) 1441 + local->scan_ies_len += 1442 + 3 + sizeof(struct ieee80211_uhr_cap) + 1443 + sizeof(struct ieee80211_uhr_cap_phy); 1445 1444 1446 1445 if (!local->ops->hw_scan) { 1447 1446 /* For hw_scan, driver needs to set these up. */
+107 -8
net/mac80211/mlme.c
··· 162 162 const struct ieee80211_vht_operation *vht_oper = elems->vht_operation; 163 163 const struct ieee80211_he_operation *he_oper = elems->he_operation; 164 164 const struct ieee80211_eht_operation *eht_oper = elems->eht_operation; 165 + const struct ieee80211_uhr_operation *uhr_oper = elems->uhr_operation; 165 166 struct ieee80211_supported_band *sband = 166 167 sdata->local->hw.wiphy->bands[channel->band]; 167 168 struct cfg80211_chan_def vht_chandef; ··· 193 192 194 193 /* get special 6 GHz case out of the way */ 195 194 if (sband->band == NL80211_BAND_6GHZ) { 196 - enum ieee80211_conn_mode mode = IEEE80211_CONN_MODE_EHT; 195 + enum ieee80211_conn_mode mode = IEEE80211_CONN_MODE_HIGHEST; 197 196 198 197 /* this is an error */ 199 198 if (conn->mode < IEEE80211_CONN_MODE_HE) ··· 216 215 return IEEE80211_CONN_MODE_LEGACY; 217 216 } 218 217 219 - return mode; 218 + if (mode <= IEEE80211_CONN_MODE_EHT) 219 + return mode; 220 + goto check_uhr; 220 221 } 221 222 222 223 /* now we have the progression HT, VHT, ... */ ··· 343 340 *chandef = eht_chandef; 344 341 } 345 342 346 - return IEEE80211_CONN_MODE_EHT; 343 + check_uhr: 344 + if (conn->mode < IEEE80211_CONN_MODE_UHR || !uhr_oper) 345 + return IEEE80211_CONN_MODE_EHT; 346 + 347 + /* 348 + * In beacons we don't have all the data - but we know the size was OK, 349 + * so if the size is valid as a non-beacon case, we have more data and 350 + * can validate the NPCA parameters. 351 + */ 352 + if (ieee80211_uhr_oper_size_ok((const void *)uhr_oper, 353 + elems->uhr_operation_len, 354 + false)) { 355 + struct cfg80211_chan_def npca_chandef = *chandef; 356 + const struct ieee80211_uhr_npca_info *npca; 357 + const __le16 *dis_subch_bmap; 358 + u16 punct = chandef->punctured, npca_punct; 359 + 360 + npca = ieee80211_uhr_npca_info(uhr_oper); 361 + if (npca) { 362 + int width = cfg80211_chandef_get_width(chandef); 363 + u8 offs = le32_get_bits(npca->params, 364 + IEEE80211_UHR_NPCA_PARAMS_PRIMARY_CHAN_OFFS); 365 + u32 cf1 = chandef->center_freq1; 366 + bool pri_upper, npca_upper; 367 + 368 + pri_upper = chandef->chan->center_freq > cf1; 369 + npca_upper = 20 * offs >= width / 2; 370 + 371 + if (20 * offs >= cfg80211_chandef_get_width(chandef) || 372 + pri_upper == npca_upper) { 373 + sdata_info(sdata, 374 + "AP UHR NPCA primary channel invalid, disabling UHR\n"); 375 + return IEEE80211_CONN_MODE_EHT; 376 + } 377 + } 378 + 379 + dis_subch_bmap = ieee80211_uhr_npca_dis_subch_bitmap(uhr_oper); 380 + 381 + if (dis_subch_bmap) { 382 + npca_punct = get_unaligned_le16(dis_subch_bmap); 383 + npca_chandef.punctured = npca_punct; 384 + } 385 + 386 + /* 387 + * must be a valid puncturing pattern for this channel as 388 + * well as puncturing all subchannels that are already in 389 + * the disabled subchannel bitmap on the primary channel 390 + */ 391 + if (!cfg80211_chandef_valid(&npca_chandef) || 392 + ((punct & npca_punct) != punct)) { 393 + sdata_info(sdata, 394 + "AP UHR NPCA disabled subchannel bitmap invalid, disabling UHR\n"); 395 + return IEEE80211_CONN_MODE_EHT; 396 + } 397 + } 398 + 399 + return IEEE80211_CONN_MODE_UHR; 347 400 } 348 401 349 402 static bool ··· 1150 1091 IEEE80211_CONN_BW_LIMIT_160); 1151 1092 break; 1152 1093 case IEEE80211_CONN_MODE_EHT: 1094 + case IEEE80211_CONN_MODE_UHR: 1153 1095 conn->bw_limit = min_t(enum ieee80211_conn_bw_limit, 1154 1096 conn->bw_limit, 1155 1097 IEEE80211_CONN_BW_LIMIT_320); ··· 1168 1108 set_bit(BSS_MEMBERSHIP_SELECTOR_HE_PHY, sta_selectors); 1169 1109 if (conn->mode >= IEEE80211_CONN_MODE_EHT) 1170 1110 set_bit(BSS_MEMBERSHIP_SELECTOR_EHT_PHY, sta_selectors); 1111 + if (conn->mode >= IEEE80211_CONN_MODE_UHR) 1112 + set_bit(BSS_MEMBERSHIP_SELECTOR_UHR_PHY, sta_selectors); 1171 1113 1172 1114 /* 1173 1115 * We do not support EPD or GLK so never add them. ··· 1216 1154 conn->bw_limit, 1217 1155 IEEE80211_CONN_BW_LIMIT_160); 1218 1156 } 1157 + 1158 + if (conn->mode >= IEEE80211_CONN_MODE_UHR && 1159 + !cfg80211_chandef_usable(sdata->wdev.wiphy, &chanreq->oper, 1160 + IEEE80211_CHAN_NO_UHR)) 1161 + conn->mode = IEEE80211_CONN_MODE_EHT; 1219 1162 1220 1163 if (chanreq->oper.width != ap_chandef->width || ap_mode != conn->mode) 1221 1164 link_id_info(sdata, link_id, ··· 1951 1884 1952 1885 /* 1953 1886 * careful - need to know about all the present elems before 1954 - * calling ieee80211_assoc_add_ml_elem(), so add this one if 1955 - * we're going to put it after the ML element 1887 + * calling ieee80211_assoc_add_ml_elem(), so add these if 1888 + * we're going to put them after the ML element 1956 1889 */ 1957 1890 if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT) 1958 1891 ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_EHT_CAPABILITY); 1892 + if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR) 1893 + ADD_PRESENT_EXT_ELEM(WLAN_EID_EXT_UHR_CAPA); 1959 1894 1960 1895 if (link_id == assoc_data->assoc_link_id) 1961 1896 ieee80211_assoc_add_ml_elem(sdata, skb, orig_capab, ext_capa, ··· 1969 1900 if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_EHT) 1970 1901 ieee80211_put_eht_cap(skb, sdata, sband, 1971 1902 &assoc_data->link[link_id].conn); 1903 + 1904 + if (assoc_data->link[link_id].conn.mode >= IEEE80211_CONN_MODE_UHR) 1905 + ieee80211_put_uhr_cap(skb, sdata, sband); 1972 1906 1973 1907 if (sband->band == NL80211_BAND_S1GHZ) { 1974 1908 ieee80211_add_aid_request_ie(sdata, skb); ··· 2206 2134 size += 2 + 1 + sizeof(struct ieee80211_eht_cap_elem) + 2207 2135 sizeof(struct ieee80211_eht_mcs_nss_supp) + 2208 2136 IEEE80211_EHT_PPE_THRES_MAX_LEN; 2137 + 2138 + size += 2 + 1 + sizeof(struct ieee80211_uhr_cap) + 2139 + sizeof(struct ieee80211_uhr_cap_phy); 2209 2140 2210 2141 return size; 2211 2142 } ··· 5606 5531 bss_conf->epcs_support = false; 5607 5532 } 5608 5533 5534 + if (elems->uhr_operation && elems->uhr_cap && 5535 + link->u.mgd.conn.mode >= IEEE80211_CONN_MODE_UHR) { 5536 + ieee80211_uhr_cap_ie_to_sta_uhr_cap(sdata, sband, 5537 + elems->uhr_cap, 5538 + elems->uhr_cap_len, 5539 + link_sta); 5540 + 5541 + bss_conf->uhr_support = link_sta->pub->uhr_cap.has_uhr; 5542 + } else { 5543 + bss_conf->uhr_support = false; 5544 + } 5545 + 5609 5546 if (elems->s1g_oper && 5610 5547 link->u.mgd.conn.mode == IEEE80211_CONN_MODE_S1G && 5611 5548 elems->s1g_capab) ··· 5908 5821 bool is_6ghz = sband->band == NL80211_BAND_6GHZ; 5909 5822 const struct ieee80211_sta_he_cap *he_cap; 5910 5823 const struct ieee80211_sta_eht_cap *eht_cap; 5824 + const struct ieee80211_sta_uhr_cap *uhr_cap; 5911 5825 struct ieee80211_sta_vht_cap vht_cap; 5912 5826 5913 5827 if (sband->band == NL80211_BAND_S1GHZ) { ··· 6084 5996 "no EHT support, limiting to HE\n"); 6085 5997 goto out; 6086 5998 } 6087 - 6088 - /* we have EHT */ 6089 - 6090 5999 conn->mode = IEEE80211_CONN_MODE_EHT; 6091 6000 6092 6001 /* check bandwidth */ ··· 6093 6008 else if (is_6ghz) 6094 6009 mlme_link_id_dbg(sdata, link_id, 6095 6010 "no EHT 320 MHz cap in 6 GHz, limiting to 160 MHz\n"); 6011 + 6012 + if (req && req->flags & ASSOC_REQ_DISABLE_UHR) { 6013 + mlme_link_id_dbg(sdata, link_id, 6014 + "UHR disabled by flag, limiting to EHT\n"); 6015 + goto out; 6016 + } 6017 + 6018 + uhr_cap = ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif); 6019 + if (!uhr_cap) { 6020 + mlme_link_id_dbg(sdata, link_id, 6021 + "no UHR support, limiting to EHT\n"); 6022 + goto out; 6023 + } 6024 + conn->mode = IEEE80211_CONN_MODE_UHR; 6096 6025 6097 6026 out: 6098 6027 mlme_link_id_dbg(sdata, link_id,
+21 -1
net/mac80211/parse.c
··· 6 6 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 7 7 * Copyright 2013-2014 Intel Mobile Communications GmbH 8 8 * Copyright (C) 2015-2017 Intel Deutschland GmbH 9 - * Copyright (C) 2018-2025 Intel Corporation 9 + * Copyright (C) 2018-2026 Intel Corporation 10 10 * 11 11 * element parsing for mac80211 12 12 */ ··· 187 187 elems->ttlm_num < ARRAY_SIZE(elems->ttlm)) { 188 188 elems->ttlm[elems->ttlm_num] = (void *)data; 189 189 elems->ttlm_num++; 190 + } 191 + break; 192 + case WLAN_EID_EXT_UHR_OPER: 193 + if (params->mode < IEEE80211_CONN_MODE_UHR) 194 + break; 195 + calc_crc = true; 196 + if (ieee80211_uhr_oper_size_ok(data, len, 197 + params->type == (IEEE80211_FTYPE_MGMT | 198 + IEEE80211_STYPE_BEACON))) { 199 + elems->uhr_operation = data; 200 + elems->uhr_operation_len = len; 201 + } 202 + break; 203 + case WLAN_EID_EXT_UHR_CAPA: 204 + if (params->mode < IEEE80211_CONN_MODE_UHR) 205 + break; 206 + calc_crc = true; 207 + if (ieee80211_uhr_capa_size_ok(data, len, true)) { 208 + elems->uhr_cap = data; 209 + elems->uhr_cap_len = len; 190 210 } 191 211 break; 192 212 }
+26
net/mac80211/rx.c
··· 5518 5518 status->rate_idx, status->nss, status->eht.gi)) 5519 5519 goto drop; 5520 5520 break; 5521 + case RX_ENC_UHR: 5522 + if (WARN_ONCE(!(status->rate_idx <= 15 || 5523 + status->rate_idx == 17 || 5524 + status->rate_idx == 19 || 5525 + status->rate_idx == 20 || 5526 + status->rate_idx == 23) || 5527 + !status->nss || 5528 + status->nss > 8 || 5529 + status->uhr.gi > NL80211_RATE_INFO_EHT_GI_3_2, 5530 + "Rate marked as a UHR rate but data is invalid: MCS:%d, NSS:%d, GI:%d\n", 5531 + status->rate_idx, status->nss, status->uhr.gi)) 5532 + goto drop; 5533 + if (WARN_ONCE(status->uhr.elr && 5534 + (status->nss != 1 || status->rate_idx > 1 || 5535 + status->uhr.gi != NL80211_RATE_INFO_EHT_GI_1_6 || 5536 + status->bw != RATE_INFO_BW_20 || status->uhr.im), 5537 + "bad UHR ELR MCS MCS:%d, NSS:%d, GI:%d, BW:%d, IM:%d\n", 5538 + status->rate_idx, status->nss, status->uhr.gi, 5539 + status->bw, status->uhr.im)) 5540 + goto drop; 5541 + if (WARN_ONCE(status->uhr.im && 5542 + (status->nss != 1 || status->rate_idx == 15), 5543 + "bad UHR IM MCS MCS:%d, NSS:%d\n", 5544 + status->rate_idx, status->nss)) 5545 + goto drop; 5546 + break; 5521 5547 default: 5522 5548 WARN_ON_ONCE(1); 5523 5549 fallthrough;
+12 -1
net/mac80211/sta_info.c
··· 4 4 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz> 5 5 * Copyright 2013-2014 Intel Mobile Communications GmbH 6 6 * Copyright (C) 2015 - 2017 Intel Deutschland GmbH 7 - * Copyright (C) 2018-2025 Intel Corporation 7 + * Copyright (C) 2018-2026 Intel Corporation 8 8 */ 9 9 10 10 #include <linux/module.h> ··· 2566 2566 rinfo->nss = STA_STATS_GET(EHT_NSS, rate); 2567 2567 rinfo->eht_gi = STA_STATS_GET(EHT_GI, rate); 2568 2568 rinfo->eht_ru_alloc = STA_STATS_GET(EHT_RU, rate); 2569 + break; 2570 + case STA_STATS_RATE_TYPE_UHR: 2571 + rinfo->flags = RATE_INFO_FLAGS_UHR_MCS; 2572 + rinfo->mcs = STA_STATS_GET(UHR_MCS, rate); 2573 + rinfo->nss = STA_STATS_GET(UHR_NSS, rate); 2574 + rinfo->eht_gi = STA_STATS_GET(UHR_GI, rate); 2575 + rinfo->eht_ru_alloc = STA_STATS_GET(UHR_RU, rate); 2576 + if (STA_STATS_GET(UHR_ELR, rate)) 2577 + rinfo->flags |= RATE_INFO_FLAGS_UHR_ELR_MCS; 2578 + if (STA_STATS_GET(UHR_IM, rate)) 2579 + rinfo->flags |= RATE_INFO_FLAGS_UHR_IM; 2569 2580 break; 2570 2581 } 2571 2582 }
+60 -20
net/mac80211/sta_info.h
··· 3 3 * Copyright 2002-2005, Devicescape Software, Inc. 4 4 * Copyright 2013-2014 Intel Mobile Communications GmbH 5 5 * Copyright(c) 2015-2017 Intel Deutschland GmbH 6 - * Copyright(c) 2020-2024 Intel Corporation 6 + * Copyright(c) 2020-2026 Intel Corporation 7 7 */ 8 8 9 9 #ifndef STA_INFO_H ··· 1009 1009 STA_STATS_RATE_TYPE_HE, 1010 1010 STA_STATS_RATE_TYPE_S1G, 1011 1011 STA_STATS_RATE_TYPE_EHT, 1012 + STA_STATS_RATE_TYPE_UHR, 1012 1013 }; 1013 1014 1014 - #define STA_STATS_FIELD_HT_MCS GENMASK( 7, 0) 1015 - #define STA_STATS_FIELD_LEGACY_IDX GENMASK( 3, 0) 1016 - #define STA_STATS_FIELD_LEGACY_BAND GENMASK( 7, 4) 1017 - #define STA_STATS_FIELD_VHT_MCS GENMASK( 3, 0) 1018 - #define STA_STATS_FIELD_VHT_NSS GENMASK( 7, 4) 1019 - #define STA_STATS_FIELD_HE_MCS GENMASK( 3, 0) 1020 - #define STA_STATS_FIELD_HE_NSS GENMASK( 7, 4) 1021 - #define STA_STATS_FIELD_EHT_MCS GENMASK( 3, 0) 1022 - #define STA_STATS_FIELD_EHT_NSS GENMASK( 7, 4) 1023 - #define STA_STATS_FIELD_BW GENMASK(12, 8) 1024 - #define STA_STATS_FIELD_SGI GENMASK(13, 13) 1025 - #define STA_STATS_FIELD_TYPE GENMASK(16, 14) 1026 - #define STA_STATS_FIELD_HE_RU GENMASK(19, 17) 1027 - #define STA_STATS_FIELD_HE_GI GENMASK(21, 20) 1028 - #define STA_STATS_FIELD_HE_DCM GENMASK(22, 22) 1029 - #define STA_STATS_FIELD_EHT_RU GENMASK(20, 17) 1030 - #define STA_STATS_FIELD_EHT_GI GENMASK(22, 21) 1015 + /* common */ 1016 + #define STA_STATS_FIELD_TYPE 0x0000000F 1017 + #define STA_STATS_FIELD_BW 0x000001F0 1018 + #define STA_STATS_FIELD_RESERVED 0x00000E00 1019 + 1020 + /* STA_STATS_RATE_TYPE_LEGACY */ 1021 + #define STA_STATS_FIELD_LEGACY_IDX 0x0000F000 1022 + #define STA_STATS_FIELD_LEGACY_BAND 0x000F0000 1023 + 1024 + /* STA_STATS_RATE_TYPE_HT */ 1025 + #define STA_STATS_FIELD_HT_MCS 0x000FF000 1026 + 1027 + /* STA_STATS_RATE_TYPE_VHT */ 1028 + #define STA_STATS_FIELD_VHT_MCS 0x0000F000 1029 + #define STA_STATS_FIELD_VHT_NSS 0x000F0000 1030 + 1031 + /* HT & VHT */ 1032 + #define STA_STATS_FIELD_SGI 0x00100000 1033 + 1034 + /* STA_STATS_RATE_TYPE_HE */ 1035 + #define STA_STATS_FIELD_HE_MCS 0x0000F000 1036 + #define STA_STATS_FIELD_HE_NSS 0x000F0000 1037 + #define STA_STATS_FIELD_HE_RU 0x00700000 1038 + #define STA_STATS_FIELD_HE_GI 0x01800000 1039 + #define STA_STATS_FIELD_HE_DCM 0x02000000 1040 + 1041 + /* STA_STATS_RATE_TYPE_EHT */ 1042 + #define STA_STATS_FIELD_EHT_MCS 0x0000F000 1043 + #define STA_STATS_FIELD_EHT_NSS 0x000F0000 1044 + #define STA_STATS_FIELD_EHT_RU 0x00F00000 1045 + #define STA_STATS_FIELD_EHT_GI 0x03000000 1046 + 1047 + /* STA_STATS_RATE_TYPE_UHR */ 1048 + #define STA_STATS_FIELD_UHR_MCS 0x0001F000 1049 + #define STA_STATS_FIELD_UHR_NSS 0x001E0000 1050 + #define STA_STATS_FIELD_UHR_RU 0x01E00000 1051 + #define STA_STATS_FIELD_UHR_GI 0x06000000 1052 + #define STA_STATS_FIELD_UHR_ELR 0x08000000 1053 + #define STA_STATS_FIELD_UHR_IM 0x10000000 1054 + 1031 1055 1032 1056 #define STA_STATS_FIELD(_n, _v) FIELD_PREP(STA_STATS_FIELD_ ## _n, _v) 1033 1057 #define STA_STATS_GET(_n, _v) FIELD_GET(STA_STATS_FIELD_ ## _n, _v) ··· 1064 1040 1065 1041 r = STA_STATS_FIELD(BW, s->bw); 1066 1042 1067 - if (s->enc_flags & RX_ENC_FLAG_SHORT_GI) 1068 - r |= STA_STATS_FIELD(SGI, 1); 1043 + switch (s->encoding) { 1044 + case RX_ENC_HT: 1045 + case RX_ENC_VHT: 1046 + if (s->enc_flags & RX_ENC_FLAG_SHORT_GI) 1047 + r |= STA_STATS_FIELD(SGI, 1); 1048 + break; 1049 + default: 1050 + break; 1051 + } 1069 1052 1070 1053 switch (s->encoding) { 1071 1054 case RX_ENC_VHT: ··· 1103 1072 r |= STA_STATS_FIELD(EHT_MCS, s->rate_idx); 1104 1073 r |= STA_STATS_FIELD(EHT_GI, s->eht.gi); 1105 1074 r |= STA_STATS_FIELD(EHT_RU, s->eht.ru); 1075 + break; 1076 + case RX_ENC_UHR: 1077 + r |= STA_STATS_FIELD(TYPE, STA_STATS_RATE_TYPE_UHR); 1078 + r |= STA_STATS_FIELD(UHR_NSS, s->nss); 1079 + r |= STA_STATS_FIELD(UHR_MCS, s->rate_idx); 1080 + r |= STA_STATS_FIELD(UHR_GI, s->uhr.gi); 1081 + r |= STA_STATS_FIELD(UHR_RU, s->uhr.ru); 1082 + r |= STA_STATS_FIELD(UHR_ELR, s->uhr.elr); 1083 + r |= STA_STATS_FIELD(UHR_IM, s->uhr.im); 1106 1084 break; 1107 1085 default: 1108 1086 WARN_ON(1);
+30
net/mac80211/uhr.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * UHR handling 4 + * 5 + * Copyright(c) 2025-2026 Intel Corporation 6 + */ 7 + 8 + #include "ieee80211_i.h" 9 + 10 + void 11 + ieee80211_uhr_cap_ie_to_sta_uhr_cap(struct ieee80211_sub_if_data *sdata, 12 + struct ieee80211_supported_band *sband, 13 + const struct ieee80211_uhr_cap *uhr_cap, 14 + u8 uhr_cap_len, 15 + struct link_sta_info *link_sta) 16 + { 17 + struct ieee80211_sta_uhr_cap *sta_uhr_cap = &link_sta->pub->uhr_cap; 18 + bool from_ap; 19 + 20 + memset(sta_uhr_cap, 0, sizeof(*sta_uhr_cap)); 21 + 22 + if (!ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif)) 23 + return; 24 + 25 + sta_uhr_cap->has_uhr = true; 26 + 27 + sta_uhr_cap->mac = uhr_cap->mac; 28 + from_ap = sdata->vif.type == NL80211_IFTYPE_STATION; 29 + sta_uhr_cap->phy = *ieee80211_uhr_phy_cap(uhr_cap, from_ap); 30 + }
+35 -1
net/mac80211/util.c
··· 6 6 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net> 7 7 * Copyright 2013-2014 Intel Mobile Communications GmbH 8 8 * Copyright (C) 2015-2017 Intel Deutschland GmbH 9 - * Copyright (C) 2018-2025 Intel Corporation 9 + * Copyright (C) 2018-2026 Intel Corporation 10 10 * 11 11 * utilities for mac80211 12 12 */ ··· 1420 1420 err = ieee80211_put_he_6ghz_cap(skb, sdata, IEEE80211_SMPS_OFF); 1421 1421 if (err) 1422 1422 return err; 1423 + 1424 + if (cfg80211_any_usable_channels(local->hw.wiphy, BIT(sband->band), 1425 + IEEE80211_CHAN_NO_UHR)) { 1426 + err = ieee80211_put_uhr_cap(skb, sdata, sband); 1427 + if (err) 1428 + return err; 1429 + } 1423 1430 1424 1431 /* 1425 1432 * If adding more here, adjust code in main.c ··· 4534 4527 return 0; 4535 4528 } 4536 4529 4530 + int ieee80211_put_uhr_cap(struct sk_buff *skb, 4531 + struct ieee80211_sub_if_data *sdata, 4532 + const struct ieee80211_supported_band *sband) 4533 + { 4534 + const struct ieee80211_sta_uhr_cap *uhr_cap = 4535 + ieee80211_get_uhr_iftype_cap_vif(sband, &sdata->vif); 4536 + int len; 4537 + 4538 + if (!uhr_cap) 4539 + return 0; 4540 + 4541 + len = 2 + 1 + sizeof(struct ieee80211_uhr_cap) + 4542 + sizeof(struct ieee80211_uhr_cap_phy); 4543 + 4544 + if (skb_tailroom(skb) < len) 4545 + return -ENOBUFS; 4546 + 4547 + skb_put_u8(skb, WLAN_EID_EXTENSION); 4548 + skb_put_u8(skb, len - 2); 4549 + skb_put_u8(skb, WLAN_EID_EXT_UHR_CAPA); 4550 + skb_put_data(skb, &uhr_cap->mac, sizeof(uhr_cap->mac)); 4551 + skb_put_data(skb, &uhr_cap->phy, sizeof(uhr_cap->phy)); 4552 + 4553 + return 0; 4554 + } 4555 + 4537 4556 const char *ieee80211_conn_mode_str(enum ieee80211_conn_mode mode) 4538 4557 { 4539 4558 static const char * const modes[] = { ··· 4569 4536 [IEEE80211_CONN_MODE_VHT] = "VHT", 4570 4537 [IEEE80211_CONN_MODE_HE] = "HE", 4571 4538 [IEEE80211_CONN_MODE_EHT] = "EHT", 4539 + [IEEE80211_CONN_MODE_UHR] = "UHR", 4572 4540 }; 4573 4541 4574 4542 if (WARN_ON(mode >= ARRAY_SIZE(modes)))