iwinfo: add basic IEEE 802.11be support
authorKoral Ilgun <koral.ilgun@smartrg.com>
Sat, 9 Mar 2024 03:33:58 +0000 (19:33 -0800)
committerDaniel Golle <daniel@makrotopia.org>
Tue, 17 Sep 2024 10:12:51 +0000 (11:12 +0100)
Add support for IEEE 802.11be via HW and HT modelist as well as
EHT specific rate information for associated STAs.

Signed-off-by: Koral Ilgun <koral.ilgun@smartrg.com>
Signed-off-by: Chad Monroe <chad@monroe.io>
include/iwinfo.h
include/iwinfo/utils.h
iwinfo_cli.c
iwinfo_lib.c
iwinfo_lua.c
iwinfo_nl80211.c
iwinfo_utils.c

index b50de69f49caf962edc3dbf2008397cfdc2e99ba..2a841be12f93a4d7e3bb17547a9e7c230a4ee748 100644 (file)
@@ -31,6 +31,7 @@ enum iwinfo_80211 {
        IWINFO_80211_AC,
        IWINFO_80211_AD,
        IWINFO_80211_AX,
+       IWINFO_80211_BE,
 
        /* keep last */
        IWINFO_80211_COUNT
@@ -43,6 +44,7 @@ enum iwinfo_80211 {
 #define IWINFO_80211_AC      (1 << IWINFO_80211_AC)
 #define IWINFO_80211_AD      (1 << IWINFO_80211_AD)
 #define IWINFO_80211_AX      (1 << IWINFO_80211_AX)
+#define IWINFO_80211_BE      (1 << IWINFO_80211_BE)
 
 extern const char * const IWINFO_80211_NAMES[IWINFO_80211_COUNT];
 
@@ -192,6 +194,12 @@ enum iwinfo_htmode {
        IWINFO_HTMODE_HE80,
        IWINFO_HTMODE_HE80_80,
        IWINFO_HTMODE_HE160,
+       IWINFO_HTMODE_EHT20,
+       IWINFO_HTMODE_EHT40,
+       IWINFO_HTMODE_EHT80,
+       IWINFO_HTMODE_EHT80_80,
+       IWINFO_HTMODE_EHT160,
+       IWINFO_HTMODE_EHT320,
 
        /* keep last */
        IWINFO_HTMODE_COUNT
@@ -210,6 +218,12 @@ enum iwinfo_htmode {
 #define IWINFO_HTMODE_HE80       (1 << IWINFO_HTMODE_HE80)
 #define IWINFO_HTMODE_HE80_80    (1 << IWINFO_HTMODE_HE80_80)
 #define IWINFO_HTMODE_HE160      (1 << IWINFO_HTMODE_HE160)
+#define IWINFO_HTMODE_EHT20      (1 << IWINFO_HTMODE_EHT20)
+#define IWINFO_HTMODE_EHT40      (1 << IWINFO_HTMODE_EHT40)
+#define IWINFO_HTMODE_EHT80      (1 << IWINFO_HTMODE_EHT80)
+#define IWINFO_HTMODE_EHT80_80   (1 << IWINFO_HTMODE_EHT80_80)
+#define IWINFO_HTMODE_EHT160     (1 << IWINFO_HTMODE_EHT160)
+#define IWINFO_HTMODE_EHT320     (1 << IWINFO_HTMODE_EHT320)
 
 extern const char * const IWINFO_HTMODE_NAMES[IWINFO_HTMODE_COUNT];
 
@@ -222,10 +236,13 @@ struct iwinfo_rate_entry {
        uint8_t is_ht:1;
        uint8_t is_vht:1;
        uint8_t is_he:1;
+       uint8_t is_eht:1;
        uint8_t he_gi;
        uint8_t he_dcm;
        uint8_t mhz;
        uint8_t nss;
+       uint8_t mhz_hi;
+       uint8_t eht_gi;
 };
 
 struct iwinfo_assoclist_entry {
index 7b8ceea12439b6652f0d311d280c8da7709976d1..fbd7d6c295b624bed70d8d1a555b38218655f202 100644 (file)
@@ -53,6 +53,7 @@ size_t iwinfo_format_hwmodes(int modes, char *buf, size_t len);
 int iwinfo_htmode_is_ht(int htmode);
 int iwinfo_htmode_is_vht(int htmode);
 int iwinfo_htmode_is_he(int htmode);
+int iwinfo_htmode_is_eht(int htmode);
 
 int iwinfo_ifup(const char *ifname);
 int iwinfo_ifdown(const char *ifname);
index 5dcee9ad023f4311fe75a7283d7c0bb788b6f976..6f87d98b4f25596f3d286f3c43bd99af4054871d 100644 (file)
@@ -327,6 +327,17 @@ static char * format_assocrate(struct iwinfo_rate_entry *r)
                        p += snprintf(p, l, ", HE-DCM %d", r->he_dcm);
                        l = sizeof(buf) - (p - buf);
                }
+               else if (r->is_eht)
+               {
+                       p += snprintf(p, l, ", EHT-MCS %d, %dMHz", r->mcs, r->mhz_hi * 256 + r->mhz);
+                       l = sizeof(buf) - (p - buf);
+
+                       p += snprintf(p, l, ", EHT-NSS %d", r->nss);
+                       l = sizeof(buf) - (p - buf);
+
+                       p += snprintf(p, l, ", EHT-GI %d", r->eht_gi);
+                       l = sizeof(buf) - (p - buf);
+               }
        }
 
        return buf;
index 579efc4e8792921c3d94606635ea79967cb05b40..f9f6569f2edba83712af8ccaa48f5069ecf57afc 100644 (file)
@@ -30,6 +30,7 @@ const char * const IWINFO_80211_NAMES[IWINFO_80211_COUNT] = {
        "ac",
        "ad",
        "ax",
+       "be",
 };
 
 const char * const IWINFO_BAND_NAMES[IWINFO_BAND_COUNT] = {
@@ -92,7 +93,13 @@ const char * const IWINFO_HTMODE_NAMES[IWINFO_HTMODE_COUNT] = {
        "HE40",
        "HE80",
        "HE80+80",
-       "HE160"
+       "HE160",
+       "EHT20",
+       "EHT40",
+       "EHT80",
+       "EHT80+80",
+       "EHT160",
+       "EHT320",
 };
 
 const char * const IWINFO_FREQ_FLAG_NAMES[IWINFO_FREQ_FLAG_COUNT] = {
index ecf257d67678331425bbc2a8a496d20ba038a060..fbe8c7c9870588043d2f1b04a6f4bb6fcd615e09 100644 (file)
@@ -279,7 +279,10 @@ static void set_rateinfo(lua_State *L, struct iwinfo_rate_entry *r, bool rx)
        lua_pushboolean(L, r->is_he);
        lua_setfield(L, -2, rx ? "rx_he" : "tx_he");
 
-       lua_pushnumber(L, r->mhz);
+       lua_pushboolean(L, r->is_eht);
+       lua_setfield(L, -2, rx ? "rx_eht" : "tx_eht");
+
+       lua_pushnumber(L, r->mhz_hi * 256 + r->mhz);
        lua_setfield(L, -2, rx ? "rx_mhz" : "tx_mhz");
 
        if (r->is_ht)
@@ -293,7 +296,7 @@ static void set_rateinfo(lua_State *L, struct iwinfo_rate_entry *r, bool rx)
                lua_pushboolean(L, r->is_short_gi);
                lua_setfield(L, -2, rx ? "rx_short_gi" : "tx_short_gi");
        }
-       else if (r->is_vht || r->is_he)
+       else if (r->is_vht || r->is_he | r->is_eht)
        {
                lua_pushnumber(L, r->mcs);
                lua_setfield(L, -2, rx ? "rx_mcs" : "tx_mcs");
@@ -309,6 +312,11 @@ static void set_rateinfo(lua_State *L, struct iwinfo_rate_entry *r, bool rx)
                        lua_setfield(L, -2, rx ? "rx_he_dcm" : "tx_he_dcm");
                }
 
+               if (r->is_eht) {
+                       lua_pushnumber(L, r->eht_gi);
+                       lua_setfield(L, -2, rx ? "rx_eht_gi" : "tx_eht_gi");
+               }
+
                if (r->is_vht) {
                        lua_pushboolean(L, r->is_short_gi);
                        lua_setfield(L, -2, rx ? "rx_short_gi" : "tx_short_gi");
@@ -554,6 +562,9 @@ static int iwinfo_L_hwmodelist(lua_State *L, int (*func)(const char *, int *))
                lua_pushboolean(L, hwmodes & IWINFO_80211_AX);
                lua_setfield(L, -2, "ax");
 
+               lua_pushboolean(L, hwmodes & IWINFO_80211_BE);
+               lua_setfield(L, -2, "be");
+
                return 1;
        }
 
index 220024955acce647410a8992f7fa19ae183c6b86..c352c9f454c777f4718d218de98cc1a427594492 100644 (file)
@@ -2047,7 +2047,17 @@ static void nl80211_parse_rateinfo(struct nlattr **ri,
        else if (ri[NL80211_RATE_INFO_BITRATE])
                re->rate = nla_get_u16(ri[NL80211_RATE_INFO_BITRATE]) * 100;
 
-       if (ri[NL80211_RATE_INFO_HE_MCS])
+       if (ri[NL80211_RATE_INFO_EHT_MCS])
+       {
+               re->is_eht = 1;
+               re->mcs = nla_get_u8(ri[NL80211_RATE_INFO_EHT_MCS]);
+
+               if (ri[NL80211_RATE_INFO_EHT_NSS])
+                       re->nss = nla_get_u8(ri[NL80211_RATE_INFO_EHT_NSS]);
+               if (ri[NL80211_RATE_INFO_EHT_GI])
+                       re->eht_gi = nla_get_u8(ri[NL80211_RATE_INFO_EHT_GI]);
+       }
+       else if (ri[NL80211_RATE_INFO_HE_MCS])
        {
                re->is_he = 1;
                re->mcs = nla_get_u8(ri[NL80211_RATE_INFO_HE_MCS]);
@@ -2084,6 +2094,8 @@ static void nl80211_parse_rateinfo(struct nlattr **ri,
        else if (ri[NL80211_RATE_INFO_80P80_MHZ_WIDTH] ||
                 ri[NL80211_RATE_INFO_160_MHZ_WIDTH])
                re->mhz = 160;
+       else if (ri[NL80211_RATE_INFO_320_MHZ_WIDTH])
+               re->mhz_hi = 320 / 256, re->mhz = 320 % 256;
        else
                re->mhz = 20;
 
@@ -3155,6 +3167,7 @@ struct nl80211_modes
        uint16_t nl_ht;
        uint32_t nl_vht;
        uint16_t he_phy_cap[6];
+       uint16_t eht_phy_cap[9];
 };
 
 static void nl80211_eval_modelist(struct nl80211_modes *m)
@@ -3183,6 +3196,22 @@ static void nl80211_eval_modelist(struct nl80211_modes *m)
                        m->ht |= IWINFO_HTMODE_HE160 | IWINFO_HTMODE_HE80_80;
        }
 
+       if (m->eht_phy_cap[0] != 0) {
+               m->hw |= IWINFO_80211_BE;
+               m->ht |= IWINFO_HTMODE_EHT20;
+
+               if (m->he_phy_cap[0] & BIT(9))
+                       m->ht |= IWINFO_HTMODE_EHT40;
+               if (m->he_phy_cap[0] & BIT(10))
+                       m->ht |= IWINFO_HTMODE_EHT40 | IWINFO_HTMODE_EHT80;
+               if (m->he_phy_cap[0] & BIT(11))
+                       m->ht |= IWINFO_HTMODE_EHT160;
+               if (m->he_phy_cap[0] & BIT(12))
+                       m->ht |= IWINFO_HTMODE_EHT160 | IWINFO_HTMODE_EHT80_80;
+               if ((m->eht_phy_cap[0] & BIT(9)) && (m->bands & IWINFO_BAND_6))
+                       m->ht |= IWINFO_HTMODE_EHT320;
+       }
+
        if (m->bands & IWINFO_BAND_24)
        {
                m->hw |= IWINFO_80211_B;
@@ -3252,6 +3281,8 @@ static int nl80211_get_modelist_cb(struct nl_msg *msg, void *arg)
                                nla_for_each_nested(nl_iftype, bands[NL80211_BAND_ATTR_IFTYPE_DATA], rem_band) {
                                        nla_parse(tb, NL80211_BAND_IFTYPE_ATTR_MAX,
                                                  nla_data(nl_iftype), nla_len(nl_iftype), NULL);
+
+                                       // HE
                                        if (tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]) {
                                                len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]);
 
@@ -3261,6 +3292,17 @@ static int nl80211_get_modelist_cb(struct nl_msg *msg, void *arg)
                                                        nla_data(tb[NL80211_BAND_IFTYPE_ATTR_HE_CAP_PHY]),
                                                        len);
                                        }
+
+                                       // EHT
+                                       if (tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]) {
+                                               len = nla_len(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]);
+
+                                               if (len > sizeof(m->eht_phy_cap) - 1)
+                                                       len = sizeof(m->eht_phy_cap) - 1;
+                                               memcpy(&((uint8_t *)m->eht_phy_cap)[1],
+                                                       nla_data(tb[NL80211_BAND_IFTYPE_ATTR_EHT_CAP_PHY]),
+                                                       len);
+                                       }
                                }
                        }
                }
@@ -3325,6 +3367,7 @@ static int nl80211_get_htmode(const char *ifname, int *buf)
        char *res, b[2] = { 0 };
        int err;
        bool he = false;
+       bool eht = false;
 
        res = nl80211_phy2ifname(ifname);
        *buf = 0;
@@ -3335,6 +3378,9 @@ static int nl80211_get_htmode(const char *ifname, int *buf)
        if (err)
                return -1;
 
+       if (nl80211_hostapd_query(res ? res : ifname, "ieee80211be", b, sizeof(b)))
+               eht = b[0] == '1';
+
        if (nl80211_hostapd_query(res ? res : ifname, "ieee80211ax", b, sizeof(b)))
                he = b[0] == '1';
        else if (nl80211_wpactl_query(res ? res : ifname, "wifi_generation", b, sizeof(b)))
@@ -3343,7 +3389,7 @@ static int nl80211_get_htmode(const char *ifname, int *buf)
        switch (chn.width) {
        case NL80211_CHAN_WIDTH_20:
                if (he)
-                       *buf = IWINFO_HTMODE_HE20;
+                       *buf = (eht == true) ? IWINFO_HTMODE_EHT20 : IWINFO_HTMODE_HE20;
                else if (chn.mode == -1)
                        *buf = IWINFO_HTMODE_VHT20;
                else
@@ -3351,7 +3397,7 @@ static int nl80211_get_htmode(const char *ifname, int *buf)
                break;
        case NL80211_CHAN_WIDTH_40:
                if (he)
-                       *buf = IWINFO_HTMODE_HE40;
+                       *buf = (eht == true) ? IWINFO_HTMODE_EHT40 : IWINFO_HTMODE_HE40;
                else if (chn.mode == -1)
                        *buf = IWINFO_HTMODE_VHT40;
                else
@@ -3359,22 +3405,25 @@ static int nl80211_get_htmode(const char *ifname, int *buf)
                break;
        case NL80211_CHAN_WIDTH_80:
                if (he)
-                       *buf = IWINFO_HTMODE_HE80;
+                       *buf = (eht == true) ? IWINFO_HTMODE_EHT80 : IWINFO_HTMODE_HE80;
                else
                        *buf = IWINFO_HTMODE_VHT80;
                break;
        case NL80211_CHAN_WIDTH_80P80:
                if (he)
-                       *buf = IWINFO_HTMODE_HE80_80;
+                       *buf = (eht == true) ? IWINFO_HTMODE_EHT80_80 : IWINFO_HTMODE_HE80_80;
                else
                        *buf = IWINFO_HTMODE_VHT80_80;
                break;
        case NL80211_CHAN_WIDTH_160:
                if (he)
-                       *buf = IWINFO_HTMODE_HE160;
+                       *buf = (eht == true) ? IWINFO_HTMODE_EHT160 : IWINFO_HTMODE_HE160;
                else
                        *buf = IWINFO_HTMODE_VHT160;
                break;
+       case NL80211_CHAN_WIDTH_320:
+               *buf = IWINFO_HTMODE_EHT320;
+               break;
        case NL80211_CHAN_WIDTH_5:
        case NL80211_CHAN_WIDTH_10:
        case NL80211_CHAN_WIDTH_20_NOHT:
index d96cbb345e68fb9b9a4d6edcc3067b5a192afd23..279c30771fb801df26955b230f95a4523c7fdbe0 100644 (file)
@@ -151,8 +151,8 @@ uint8_t iwinfo_ghz2band(uint32_t ghz)
 
 size_t iwinfo_format_hwmodes(int modes, char *buf, size_t len)
 {
-       // bit numbers as per IWINFO_80211_*:  ad ac ax  a  b  g  n
-       const int order[IWINFO_80211_COUNT] = { 5, 4, 6, 0, 1, 2, 3 };
+       // bit numbers as per IWINFO_80211_*:  ad ac ax  a  b  be g  n
+       const int order[IWINFO_80211_COUNT] = { 5, 4, 6, 0, 1, 7, 2, 3 };
        size_t res = 0;
        int i;
 
@@ -216,6 +216,22 @@ int iwinfo_htmode_is_he(int htmode)
        return 0;
 }
 
+int iwinfo_htmode_is_eht(int htmode)
+{
+       switch (htmode)
+       {
+       case IWINFO_HTMODE_EHT20:
+       case IWINFO_HTMODE_EHT40:
+       case IWINFO_HTMODE_EHT80:
+       case IWINFO_HTMODE_EHT80_80:
+       case IWINFO_HTMODE_EHT160:
+       case IWINFO_HTMODE_EHT320:
+               return 1;
+       }
+
+       return 0;
+}
+
 int iwinfo_ifup(const char *ifname)
 {
        struct ifreq ifr;