public inbox for linux-wireless@vger.kernel.org
 help / color / mirror / Atom feed
From: Johannes Berg <johannes@sipsolutions.net>
To: Alexander Wilhelm <alexander.wilhelm@westermo.com>
Cc: Jeff Johnson <jjohnson@kernel.org>,
	ath12k@lists.infradead.org,  linux-wireless@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: ath12k: handling of HE and EHT capabilities
Date: Thu, 12 Mar 2026 12:05:22 +0100	[thread overview]
Message-ID: <40b4b959b2ea5afd55381e6ae2d0c1908456734c.camel@sipsolutions.net> (raw)
In-Reply-To: <abKbLNK0YrT6dr96@FUE-ALEWI-WINX>

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

  reply	other threads:[~2026-03-12 11:05 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 [this message]
2026-03-12 12:10       ` Johannes Berg
2026-03-12 13:00         ` Alexander Wilhelm
2026-03-13  7:45           ` Alexander Wilhelm

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=40b4b959b2ea5afd55381e6ae2d0c1908456734c.camel@sipsolutions.net \
    --to=johannes@sipsolutions.net \
    --cc=alexander.wilhelm@westermo.com \
    --cc=ath12k@lists.infradead.org \
    --cc=jjohnson@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-wireless@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox