* ath12k: handling of HE and EHT capabilities
@ 2026-03-12 9:02 Alexander Wilhelm
2026-03-12 9:37 ` Johannes Berg
0 siblings, 1 reply; 7+ messages in thread
From: Alexander Wilhelm @ 2026-03-12 9:02 UTC (permalink / raw)
To: Jeff Johnson; +Cc: ath12k, linux-wireless, linux-kernel
Hello devs,
I’m currently trying to fix the HE and EHT capabilities handling on the
big‑endian platform. Unfortunately, I don’t fully understand how exactly
these capabilities are supposed to be defined.
For example, I use the `iw` tool to display the capabilities and their
descriptions. The code for that has the following function prototypes:
* void print_ht_capability(__u16 cap);
* void print_vht_info(__u32 capa, const __u8 *mcs);
* static void __print_he_capa(const __u16 *mac_cap,
const __u16 *phy_cap,
const __u16 *mcs_set, size_t mcs_len,
const __u8 *ppet, int ppet_len,
bool indent);
* static void __print_eht_capa(int band,
const __u8 *mac_cap,
const __u32 *phy_cap,
const __u8 *mcs_set, size_t mcs_len,
const __u8 *ppet, size_t ppet_len,
const __u16 *he_phy_cap,
bool indent);
For HE capabilites in 6 GHz band I couldn't find the respective function.
Then I looked into `include/net/cfg80211.h` and examined the structures
that define the capability data types.
struct ieee80211_sta_ht_cap {
u16 cap; /* use IEEE80211_HT_CAP_ */
bool ht_supported;
u8 ampdu_factor;
u8 ampdu_density;
struct ieee80211_mcs_info mcs;
};
struct ieee80211_sta_vht_cap {
bool vht_supported;
u32 cap; /* use IEEE80211_VHT_CAP_ */
struct ieee80211_vht_mcs_info vht_mcs;
};
The structs for HT and VHT use `u16` and `u32` data types for the `cap`
variable, matching what `iw` does. That part is consistent.
struct ieee80211_he_cap_elem {
u8 mac_cap_info[6];
u8 phy_cap_info[11];
} __packed;
struct ieee80211_he_6ghz_capa {
/* uses IEEE80211_HE_6GHZ_CAP_* below */
__le16 capa; }
__packed;
However, for HE the types differ from the `iw` implementation. Here, `u8`
arrays are used instead of `u16` for MAC and PHY capabilities. The 6 GHz
capabilities use `u16`, which is also different.
struct ieee80211_eht_cap_elem_fixed {
u8 mac_cap_info[2];
u8 phy_cap_info[9];
} __packed;
For EHT, `u8` arrays are also used for both MAC and PHY caps, instead of
`u32` for the PHY caps as in the `iw` implementation.
The current `ath12k` implementation always uses `u32` values, which does
not work on big‑endian platforms:
ath12k_pci 0001:01:00.0: BAR 0: assigned [mem 0xc00000000-0xc001fffff 64bit]
ath12k_pci 0001:01:00.0: MSI vectors: 1
ath12k_pci 0001:01:00.0: Hardware name: qcn9274 hw2.0
ath12k_pci 0001:01:00.0: qmi dma allocation failed (29360128 B type 1), will try later with small size
ath12k_pci 0001:01:00.0: memory type 10 not supported
ath12k_pci 0001:01:00.0: chip_id 0x0 chip_family 0xb board_id 0x1005 soc_id 0x401a2200
ath12k_pci 0001:01:00.0: fw_version 0x111300d6 fw_build_timestamp 2024-08-06 08:43 fw_build_id QC_IMAGE_VERSION_STRING=WLAN.WBE.1.1.1-00214-QCAHKSWPL_SILICONZ-1
ath12k_pci 0001:01:00.0: leaving PCI ASPM disabled to avoid MHI M2 problems
ath12k_pci 0001:01:00.0: Invalid module id 2
ath12k_pci 0001:01:00.0: failed to parse tlv -22
ath12k_pci 0001:01:00.0: ieee80211 registration failed: -22
ath12k_pci 0001:01:00.0: failed register the radio with mac80211: -22
ath12k_pci 0001:01:00.0: failed to create pdev core: -22
ath12k_pci 0001:01:00.0: qmi failed set mode request, mode: 4, err = -110
ath12k_pci 0001:01:00.0: qmi failed to send wlan mode off
I want to address and fix this issue. However, I cannot apply the “never
break the userspace” rule here, as it seems, it is already broken. Can
someone help clarify which datatypes are supposed to be used? Once that is
clear, I can fix the `ath12k` implementation.
Best regards
Alexander Wilhelm
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: ath12k: handling of HE and EHT capabilities 2026-03-12 9:02 ath12k: handling of HE and EHT capabilities Alexander Wilhelm @ 2026-03-12 9:37 ` Johannes Berg 2026-03-12 10:53 ` Alexander Wilhelm 0 siblings, 1 reply; 7+ messages in thread From: Johannes Berg @ 2026-03-12 9:37 UTC (permalink / raw) To: Alexander Wilhelm, Jeff Johnson; +Cc: ath12k, linux-wireless, linux-kernel Hi, > For example, I use the `iw` tool to display the capabilities and their > descriptions. The code for that has the following function prototypes: > > * void print_ht_capability(__u16 cap); > * void print_vht_info(__u32 capa, const __u8 *mcs); > * static void __print_he_capa(const __u16 *mac_cap, > const __u16 *phy_cap, > const __u16 *mcs_set, size_t mcs_len, > const __u8 *ppet, int ppet_len, > bool indent); > * static void __print_eht_capa(int band, > const __u8 *mac_cap, > const __u32 *phy_cap, > const __u8 *mcs_set, size_t mcs_len, > const __u8 *ppet, size_t ppet_len, > const __u16 *he_phy_cap, > bool indent); This is perhaps a bit unfortunate, but note that the HE and EHT __u16 and __u32 here are really little endian pointers, and the functions do byte-order conversion. > struct ieee80211_sta_ht_cap { > u16 cap; /* use IEEE80211_HT_CAP_ */ > bool ht_supported; > u8 ampdu_factor; > u8 ampdu_density; > struct ieee80211_mcs_info mcs; > }; > > struct ieee80211_sta_vht_cap { > bool vht_supported; > u32 cap; /* use IEEE80211_VHT_CAP_ */ > struct ieee80211_vht_mcs_info vht_mcs; > }; > > The structs for HT and VHT use `u16` and `u32` data types for the `cap` > variable, matching what `iw` does. That part is consistent. Careful. There are different structs used in different places, notably HT/VHT and HE/EHT differ. For HT and VHT, look at the start of nl80211_send_band_rateinfo(), which sends themas individual attributes, defined in enum nl80211_band_attr, and the values that are u16 (NL80211_BAND_ATTR_HT_CAPA) or u32 (NL80211_BAND_ATTR_VHT_CAPA) are in host byte order, though both are actually documented misleadingly ("as in [V]HT information IE" is just all around wrong.) For HE/EHT, you have it in nl80211_send_iftype_data() since it's per interface type, and all the individual values are just as they appear in the spec, regardless of their size. Note that spec is generally in little endian, but sometimes has strange field lengths like MAC capabilities being 6 bytes in HE: > struct ieee80211_he_cap_elem { > u8 mac_cap_info[6]; > u8 phy_cap_info[11]; > } __packed; > > struct ieee80211_he_6ghz_capa { > /* uses IEEE80211_HE_6GHZ_CAP_* below */ > __le16 capa; } > __packed; > > However, for HE the types differ from the `iw` implementation. Here, `u8` > arrays are used instead of `u16` for MAC and PHY capabilities. The 6 GHz > capabilities use `u16`, which is also different. That doesn't really matter, they're just a set of 6 or 11 bytes, and e.g. the HE MAC capabilities are treated by the kernel as a set of 6 bytes, but by iw as a set of 3 __le16, which results in the same interpretation, or at least should. > struct ieee80211_eht_cap_elem_fixed { > u8 mac_cap_info[2]; > u8 phy_cap_info[9]; > } __packed; > > For EHT, `u8` arrays are also used for both MAC and PHY caps, instead of > `u32` for the PHY caps as in the `iw` implementation. Same thing here. > The current `ath12k` implementation always uses `u32` values, which does > not work on big‑endian platforms: Yeah, that seems problematic and not really fitting for something that's 6, 11, 2 or 9 bytes long? > I want to address and fix this issue. However, I cannot apply the “never > break the userspace” rule here, as it seems, it is already broken. I don't think it's broken, why do you say so? What's (clearly) broken is how ath12k puts the data into the HE/EHT structs that the kernel expects, but per your dmesg: > ath12k_pci 0001:01:00.0: ieee80211 registration failed: -22 > ath12k_pci 0001:01:00.0: failed register the radio with mac80211: -22 it seems that even mac80211 doesn't like the capabilities, so the byte order issue already exists there. It seems to me the issue is that ath12k_band_cap is in u32, converted, but then memcpy()d. johannes ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: ath12k: handling of HE and EHT capabilities 2026-03-12 9:37 ` Johannes Berg @ 2026-03-12 10:53 ` Alexander Wilhelm 2026-03-12 11:05 ` Johannes Berg 0 siblings, 1 reply; 7+ messages in thread From: Alexander Wilhelm @ 2026-03-12 10:53 UTC (permalink / raw) To: Johannes Berg; +Cc: Jeff Johnson, ath12k, linux-wireless, linux-kernel On Thu, Mar 12, 2026 at 10:37:46AM +0100, Johannes Berg wrote: > Hi, > > For example, I use the `iw` tool to display the capabilities and their > > descriptions. The code for that has the following function prototypes: > > > > * void print_ht_capability(__u16 cap); > > * void print_vht_info(__u32 capa, const __u8 *mcs); > > * static void __print_he_capa(const __u16 *mac_cap, > > const __u16 *phy_cap, > > const __u16 *mcs_set, size_t mcs_len, > > const __u8 *ppet, int ppet_len, > > bool indent); > > * static void __print_eht_capa(int band, > > const __u8 *mac_cap, > > const __u32 *phy_cap, > > const __u8 *mcs_set, size_t mcs_len, > > const __u8 *ppet, size_t ppet_len, > > const __u16 *he_phy_cap, > > bool indent); > > This is perhaps a bit unfortunate, but note that the HE and EHT __u16 > and __u32 here are really little endian pointers, and the functions do > byte-order conversion. I don’t see this in the function. For example, the MAC capabilities are a `u16 *` in CPU endianness, which is simply memcpy’d from the parsed `NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC`. Later, they are treated as `u16 *`, as shown in the following code: printf("%s\t\tHE MAC Capabilities (0x", pre); for (i = 0; i < 3; i++) printf("%04x", mac_cap[i]); printf("):\n"); Here is the result on little‑ vs. big‑endian platforms: Little endian: HE MAC Capabilities (0x081a010d030f): Big endian: HE MAC Capabilities (0x0b00189a4010): For the PHY capabilities, they are also a `u16 *`, but they are treated as a `u8 *`. However, later in the description printing, `PRINT_HE_CAP` does not take endianness into account. This prints the correct hex values, but the interpretation/description is wrong: Little endian: HE PHY Capabilities: (0x1c604c89ffdb839c110c00): HE40/HE80/5GHz HE160/5GHz HE160/HE80+80/5GHz LDPC Coding in Payload HE SU PPDU with 1x HE-LTF and 0.8us GI STBC Tx <= 80MHz STBC Rx <= 80MHz Full Bandwidth UL MU-MIMO DCM Max Constellation: 1 DCM Max Constellation Rx: 1 SU Beamformer SU Beamformee MU Beamformer Beamformee STS <= 80Mhz: 7 Beamformee STS > 80Mhz: 7 Sounding Dimensions <= 80Mhz: 3 Sounding Dimensions > 80Mhz: 3 Ng = 16 SU Feedback Ng = 16 MU Feedback Codebook Size SU Feedback Codebook Size MU Feedback PPE Threshold Present HE SU PPDU & HE PPDU 4x HE-LTF 0.8us GI Max NC: 3 STBC Rx > 80MHz HE ER SU PPDU 4x HE-LTF 0.8us GI HE ER SU PPDU 1x HE-LTF 0.8us GI TX 1024-QAM RX 1024-QAM Big endian: HE PHY Capabilities: (0x1c604c89ffda839c110c00): Punctured Preamble RX: 12 HE SU PPDU with 1x HE-LTF and 0.8us GI Doppler Rx Full Bandwidth UL MU-MIMO DCM Max Constellation: 3 DCM Max NSS Tx: 1 DCM Max Constellation Rx: 3 DCM Max NSS Rx: 1 Rx HE MU PPDU from Non-AP STA SU Beamformer SU Beamformee Beamformee STS <= 80Mhz: 2 Beamformee STS > 80Mhz: 4 Sounding Dimensions <= 80Mhz: 3 Ng = 16 MU Feedback Codebook Size MU Feedback Triggered MU Beamforming Feedback Triggered CQI Feedback Partial Bandwidth DL MU-MIMO PPE Threshold Present SRP-based SR Max NC: 2 20MHz in 160/80+80MHz HE PPDU 80MHz in 160/80+80MHz HE PPDU HE ER SU PPDU 1x HE-LTF 0.8us GI DCM Max BW: 2 > > struct ieee80211_sta_ht_cap { > > u16 cap; /* use IEEE80211_HT_CAP_ */ > > bool ht_supported; > > u8 ampdu_factor; > > u8 ampdu_density; > > struct ieee80211_mcs_info mcs; > > }; > > > > struct ieee80211_sta_vht_cap { > > bool vht_supported; > > u32 cap; /* use IEEE80211_VHT_CAP_ */ > > struct ieee80211_vht_mcs_info vht_mcs; > > }; > > > > The structs for HT and VHT use `u16` and `u32` data types for the `cap` > > variable, matching what `iw` does. That part is consistent. > > Careful. There are different structs used in different places, notably > HT/VHT and HE/EHT differ. > > For HT and VHT, look at the start of nl80211_send_band_rateinfo(), which > sends themas individual attributes, defined in enum nl80211_band_attr, > and the values that are u16 (NL80211_BAND_ATTR_HT_CAPA) or u32 > (NL80211_BAND_ATTR_VHT_CAPA) are in host byte order, though both are > actually documented misleadingly ("as in [V]HT information IE" is just > all around wrong.) > > For HE/EHT, you have it in nl80211_send_iftype_data() since it's per > interface type, and all the individual values are just as they appear in > the spec, regardless of their size. Okay, I think I understand so far. I had also initially wondered why HE/EHT capabilities were treated separately from the HT/VHT capabilities. > Note that spec is generally in little endian, but sometimes has strange > field lengths like MAC capabilities being 6 bytes in HE: > > > struct ieee80211_he_cap_elem { > > u8 mac_cap_info[6]; > > u8 phy_cap_info[11]; > > } __packed; > > > > struct ieee80211_he_6ghz_capa { > > /* uses IEEE80211_HE_6GHZ_CAP_* below */ > > __le16 capa; } > > __packed; > > > > However, for HE the types differ from the `iw` implementation. Here, `u8` > > arrays are used instead of `u16` for MAC and PHY capabilities. The 6 GHz > > capabilities use `u16`, which is also different. > > That doesn't really matter, they're just a set of 6 or 11 bytes, and > e.g. the HE MAC capabilities are treated by the kernel as a set of 6 > bytes, but by iw as a set of 3 __le16, which results in the same > interpretation, or at least should. Sure. I agree with you, it makes no difference whether I use `u8[6]` or `__le16[3]`, as long as I use `memcpy` and don’t perform any CPU‑endian swapping at this point. > > struct ieee80211_eht_cap_elem_fixed { > > u8 mac_cap_info[2]; > > u8 phy_cap_info[9]; > > } __packed; > > > > For EHT, `u8` arrays are also used for both MAC and PHY caps, instead of > > `u32` for the PHY caps as in the `iw` implementation. > > Same thing here. > > > The current `ath12k` implementation always uses `u32` values, which does > > not work on big‑endian platforms: > > Yeah, that seems problematic and not really fitting for something that's > 6, 11, 2 or 9 bytes long? > > > I want to address and fix this issue. However, I cannot apply the “never > > break the userspace” rule here, as it seems, it is already broken. > > I don't think it's broken, why do you say so? Well, if `ath12k` uses `u32` in CPU‑native order, that’s a bug, and I can’t get `ieee80211_hw` registered. If I use `__le32` in little-endian order instead, I end up with incorrect capabilities and mismatched descriptions shown by the `iw` tool (but I can get the driver running). So neither approach seems to be a 100% solution at first glance. Did I misinterpret the rule? > What's (clearly) broken is how ath12k puts the data into the HE/EHT > structs that the kernel expects, but per your dmesg: > > > ath12k_pci 0001:01:00.0: ieee80211 registration failed: -22 > > ath12k_pci 0001:01:00.0: failed register the radio with mac80211: -22 > > it seems that even mac80211 doesn't like the capabilities, so the byte > order issue already exists there. > > It seems to me the issue is that ath12k_band_cap is in u32, converted, > but then memcpy()d. The `ath12k` driver uses `u32` arrays in CPU‑native order for this, so the swap is effectively happening. Later, in `ieee80211_register_hw`, the values are compared at the bit level, and that’s where it fails. I understand that technically `__le32` could be used in `ath12k`, meaning no swap, but since `u8` arrays are used in `cfg80211`, that might actually be the better approach. I just wanted to provide a solution that is clean and acceptable. The `iw` tool still needs to be updated accordingly. Best regards Alexander Wilhelm ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: ath12k: handling of HE and EHT capabilities 2026-03-12 10:53 ` Alexander Wilhelm @ 2026-03-12 11:05 ` Johannes Berg 2026-03-12 12:10 ` Johannes Berg 0 siblings, 1 reply; 7+ messages in thread From: Johannes Berg @ 2026-03-12 11:05 UTC (permalink / raw) To: Alexander Wilhelm; +Cc: Jeff Johnson, ath12k, linux-wireless, linux-kernel On Thu, 2026-03-12 at 11:53 +0100, Alexander Wilhelm wrote: > On Thu, Mar 12, 2026 at 10:37:46AM +0100, Johannes Berg wrote: > > Hi, > > > For example, I use the `iw` tool to display the capabilities and their > > > descriptions. The code for that has the following function prototypes: > > > > > > * void print_ht_capability(__u16 cap); > > > * void print_vht_info(__u32 capa, const __u8 *mcs); > > > * static void __print_he_capa(const __u16 *mac_cap, > > > const __u16 *phy_cap, > > > const __u16 *mcs_set, size_t mcs_len, > > > const __u8 *ppet, int ppet_len, > > > bool indent); > > > * static void __print_eht_capa(int band, > > > const __u8 *mac_cap, > > > const __u32 *phy_cap, > > > const __u8 *mcs_set, size_t mcs_len, > > > const __u8 *ppet, size_t ppet_len, > > > const __u16 *he_phy_cap, > > > bool indent); > > > > This is perhaps a bit unfortunate, but note that the HE and EHT __u16 > > and __u32 here are really little endian pointers, and the functions do > > byte-order conversion. > > I don’t see this in the function. For example, the MAC capabilities are a > `u16 *` in CPU endianness, which is simply memcpy’d from the parsed > `NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC`. Later, they are treated as `u16 *`, > as shown in the following code: > > printf("%s\t\tHE MAC Capabilities (0x", pre); > for (i = 0; i < 3; i++) > printf("%04x", mac_cap[i]); > printf("):\n"); > > Here is the result on little‑ vs. big‑endian platforms: > > Little endian: > HE MAC Capabilities (0x081a010d030f): > > Big endian: > HE MAC Capabilities (0x0b00189a4010): Oh, OK, so _that_ print is definitely wrong in iw. But the individual prints are converted: #define PRINT_HE_CAP(_var, _idx, _bit, _str) \ do { \ if (le16toh(_var[_idx]) & BIT(_bit)) \ printf("%s\t\t\t" _str "\n", pre); \ } while (0) > For the PHY capabilities, they are also a `u16 *`, but they are treated as > a `u8 *`. However, later in the description printing, `PRINT_HE_CAP` does > not take endianness into account. PRINT_HE_CAP *does* convert, there's le16toh() there. Same in PRINT_HE_CAP_MASK. It should convert, because it's from a u8[6] kernel API that just carries the values as they are in the spec. > > > I want to address and fix this issue. However, I cannot apply the “never > > > break the userspace” rule here, as it seems, it is already broken. > > > > I don't think it's broken, why do you say so? Well, I see now that I missed the printf("%s\t\tHE MAC Capabilities (0x", pre); for (i = 0; i < 3; i++) printf("%04x", le16toh(mac_cap[i])); and printf("%s\t\tEHT MAC Capabilities (0x", pre); for (i = 0; i < 2; i++) printf("%02x", mac_cap[i]); parts, those are definitely broken in iw on big endian platforms. We should fix those in iw. The actual individual prints seem fine though. > Well, if `ath12k` uses `u32` in CPU‑native order, that’s a bug, and I can’t > get `ieee80211_hw` registered. If I use `__le32` in little-endian order > instead, I end up with incorrect capabilities and mismatched descriptions > shown by the `iw` tool (but I can get the driver running). So neither > approach seems to be a 100% solution at first glance. Did I misinterpret > the rule? The *descriptions* should be fine I think? Just the first line with the hex would be messed up. > > What's (clearly) broken is how ath12k puts the data into the HE/EHT > > structs that the kernel expects, but per your dmesg: > > > > > ath12k_pci 0001:01:00.0: ieee80211 registration failed: -22 > > > ath12k_pci 0001:01:00.0: failed register the radio with mac80211: -22 > > > > it seems that even mac80211 doesn't like the capabilities, so the byte > > order issue already exists there. > > > > It seems to me the issue is that ath12k_band_cap is in u32, converted, > > but then memcpy()d. > > The `ath12k` driver uses `u32` arrays in CPU‑native order for this, so the > swap is effectively happening. Yeah but the swap is wrong, since HE/EHT capabilities are just byte arrays in spec byte order in cfg80211/nl80211. > Later, in `ieee80211_register_hw`, the > values are compared at the bit level, and that’s where it fails. I > understand that technically `__le32` could be used in `ath12k`, meaning no > swap, but since `u8` arrays are used in `cfg80211`, that might actually be > the better approach. Sure, could use u8 in ath12k too, dunno, up to the maintainer. At least if it was __le32 you could still memcpy() from it since no swap happened, and wouldn't change the code structure that much. johannes ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: ath12k: handling of HE and EHT capabilities 2026-03-12 11:05 ` Johannes Berg @ 2026-03-12 12:10 ` Johannes Berg 2026-03-12 13:00 ` Alexander Wilhelm 0 siblings, 1 reply; 7+ messages in thread From: Johannes Berg @ 2026-03-12 12:10 UTC (permalink / raw) To: Alexander Wilhelm; +Cc: Jeff Johnson, ath12k, linux-wireless, linux-kernel Wait ... > > I don’t see this in the function. For example, the MAC capabilities are a > > `u16 *` in CPU endianness, which is simply memcpy’d from the parsed > > `NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC`. Later, they are treated as `u16 *`, > > as shown in the following code: > > > > printf("%s\t\tHE MAC Capabilities (0x", pre); > > for (i = 0; i < 3; i++) > > printf("%04x", mac_cap[i]); > > printf("):\n"); That's incorrect for sure. But iw code now actually reads printf("%s\t\tHE MAC Capabilities (0x", pre); for (i = 0; i < 3; i++) printf("%04x", le16toh(mac_cap[i])); printf("):\n"); which is correct. HE PHY capabilities are printed as printf("%s\t\tHE PHY Capabilities: (0x", pre); for (i = 0; i < 11; i++) printf("%02x", ((__u8 *)phy_cap)[i + 1]); in my version of the code, and it seems to me the +1 is incorrect either way? > printf("%s\t\tEHT MAC Capabilities (0x", pre); > for (i = 0; i < 2; i++) > printf("%02x", mac_cap[i]); This was also correct, not incorrect as I stated, since mac_cap is u8 *, and EHT PHY capabilities are cast to u8 * first. Maybe your iw is just really old? johannes ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: ath12k: handling of HE and EHT capabilities 2026-03-12 12:10 ` Johannes Berg @ 2026-03-12 13:00 ` Alexander Wilhelm 2026-03-13 7:45 ` Alexander Wilhelm 0 siblings, 1 reply; 7+ messages in thread From: Alexander Wilhelm @ 2026-03-12 13:00 UTC (permalink / raw) To: Johannes Berg; +Cc: Jeff Johnson, ath12k, linux-wireless, linux-kernel On Thu, Mar 12, 2026 at 01:10:21PM +0100, Johannes Berg wrote: > Wait ... > > > > I don’t see this in the function. For example, the MAC capabilities are a > > > `u16 *` in CPU endianness, which is simply memcpy’d from the parsed > > > `NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC`. Later, they are treated as `u16 *`, > > > as shown in the following code: > > > > > > printf("%s\t\tHE MAC Capabilities (0x", pre); > > > for (i = 0; i < 3; i++) > > > printf("%04x", mac_cap[i]); > > > printf("):\n"); > > That's incorrect for sure. But iw code now actually reads > > printf("%s\t\tHE MAC Capabilities (0x", pre); > for (i = 0; i < 3; i++) > printf("%04x", le16toh(mac_cap[i])); > printf("):\n"); > > > which is correct. HE PHY capabilities are printed as > > printf("%s\t\tHE PHY Capabilities: (0x", pre); > for (i = 0; i < 11; i++) > printf("%02x", ((__u8 *)phy_cap)[i + 1]); > > in my version of the code, and it seems to me the +1 is incorrect either > way? > > > printf("%s\t\tEHT MAC Capabilities (0x", pre); > > for (i = 0; i < 2; i++) > > printf("%02x", mac_cap[i]); > > This was also correct, not incorrect as I stated, since mac_cap is u8 *, > and EHT PHY capabilities are cast to u8 * first. > > Maybe your iw is just really old? Sorry, my fault. I'm using `OpenWrt v24.10.5` with `iw` version 6.9. The latest master has the `le16toh` implemented. With my `ath12k` fix the PHY capabilities and the respecitve descriptions are fine now. But I still cannot get MAC capabilities correct. I'll analyze it further. Best regards Alexander wilhelm ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: ath12k: handling of HE and EHT capabilities 2026-03-12 13:00 ` Alexander Wilhelm @ 2026-03-13 7:45 ` Alexander Wilhelm 0 siblings, 0 replies; 7+ messages in thread From: Alexander Wilhelm @ 2026-03-13 7:45 UTC (permalink / raw) To: Johannes Berg; +Cc: Jeff Johnson, ath12k, linux-wireless, linux-kernel On Thu, Mar 12, 2026 at 02:00:21PM +0100, Alexander Wilhelm wrote: > On Thu, Mar 12, 2026 at 01:10:21PM +0100, Johannes Berg wrote: > > Wait ... > > > > > > I don’t see this in the function. For example, the MAC capabilities are a > > > > `u16 *` in CPU endianness, which is simply memcpy’d from the parsed > > > > `NL80211_BAND_IFTYPE_ATTR_HE_CAP_MAC`. Later, they are treated as `u16 *`, > > > > as shown in the following code: > > > > > > > > printf("%s\t\tHE MAC Capabilities (0x", pre); > > > > for (i = 0; i < 3; i++) > > > > printf("%04x", mac_cap[i]); > > > > printf("):\n"); > > > > That's incorrect for sure. But iw code now actually reads > > > > printf("%s\t\tHE MAC Capabilities (0x", pre); > > for (i = 0; i < 3; i++) > > printf("%04x", le16toh(mac_cap[i])); > > printf("):\n"); > > > > > > which is correct. HE PHY capabilities are printed as > > > > printf("%s\t\tHE PHY Capabilities: (0x", pre); > > for (i = 0; i < 11; i++) > > printf("%02x", ((__u8 *)phy_cap)[i + 1]); > > > > in my version of the code, and it seems to me the +1 is incorrect either > > way? > > > > > printf("%s\t\tEHT MAC Capabilities (0x", pre); > > > for (i = 0; i < 2; i++) > > > printf("%02x", mac_cap[i]); > > > > This was also correct, not incorrect as I stated, since mac_cap is u8 *, > > and EHT PHY capabilities are cast to u8 * first. > > > > Maybe your iw is just really old? > > Sorry, my fault. I'm using `OpenWrt v24.10.5` with `iw` version 6.9. The > latest master has the `le16toh` implemented. With my `ath12k` fix the PHY > capabilities and the respecitve descriptions are fine now. But I still > cannot get MAC capabilities correct. I'll analyze it further. Hi Johannes, I finally have `ath12k` running with the correct capabilities. The latest `iw` version also performs the byte swaps correctly, except for the HE MAC capabilities output. There, each 2‑byte pair is swapped between big‑endian and little‑endian platforms. I’m sending a patch to make this consistent across all architectures. Thank you for the support. Best regards Alexander Wilhelm ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-03-13 7:45 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-03-12 9:02 ath12k: handling of HE and EHT capabilities Alexander Wilhelm 2026-03-12 9:37 ` Johannes Berg 2026-03-12 10:53 ` Alexander Wilhelm 2026-03-12 11:05 ` Johannes Berg 2026-03-12 12:10 ` Johannes Berg 2026-03-12 13:00 ` Alexander Wilhelm 2026-03-13 7:45 ` Alexander Wilhelm
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox