utils: add iwinfo_htmode_is_{ht|vht|he} helpers
[project/iwinfo.git] / iwinfo_nl80211.c
index 545c56c5322d92317aa0ade3e70740f71608618a..a78343fde43ee93bbe0c427c7bb28c1a04e8a72e 100644 (file)
@@ -153,6 +153,23 @@ static int nl80211_readstr(const char *path, char *buffer, int length)
        return rv;
 }
 
+static int nl80211_get_band(int nla_type)
+{
+       switch (nla_type)
+       {
+       case NL80211_BAND_2GHZ:
+               return IWINFO_BAND_24;
+       case NL80211_BAND_5GHZ:
+               return IWINFO_BAND_5;
+       case NL80211_BAND_6GHZ:
+               return IWINFO_BAND_6;
+       case NL80211_BAND_60GHZ:
+               return IWINFO_BAND_60;
+       }
+
+       return 0;
+}
+
 
 static int nl80211_msg_error(struct sockaddr_nl *nla,
        struct nlmsgerr *err, void *arg)
@@ -655,7 +672,13 @@ static int __nl80211_wait(const char *family, const char *group, ...)
 #define nl80211_wait(family, group, ...) \
        __nl80211_wait(family, group, __VA_ARGS__, 0)
 
-
+/* This is linux's ieee80211_freq_khz_to_channel() which is:
+ * SPDX-License-Identifier: GPL-2.0
+ * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018-2022 Intel Corporation
+ */
 static int nl80211_freq2channel(int freq)
 {
        if (freq == 2484)
@@ -664,14 +687,31 @@ static int nl80211_freq2channel(int freq)
                return (freq - 2407) / 5;
        else if (freq >= 4910 && freq <= 4980)
                return (freq - 4000) / 5;
-       else if(freq >= 56160 + 2160 * 1 && freq <= 56160 + 2160 * 6)
+       else if (freq < 5925)
+               return (freq - 5000) / 5;
+       else if (freq == 5935)
+               return 2;
+       else if (freq <= 45000) /* DMG band lower limit */
+               /* see 802.11ax D6.1 27.3.22.2 */
+               return (freq - 5950) / 5;
+       else if (freq >= 58320 && freq <= 70200)
                return (freq - 56160) / 2160;
        else
-               return (freq - 5000) / 5;
+               return 0;
 }
 
-static int nl80211_channel2freq(int channel, const char *band)
+/* This is linux's ieee80211_channel_to_freq_khz() which is:
+ * SPDX-License-Identifier: GPL-2.0
+ * Copyright 2007-2009 Johannes Berg <johannes@sipsolutions.net>
+ * Copyright 2013-2014 Intel Mobile Communications GmbH
+ * Copyright 2017 Intel Deutschland GmbH
+ * Copyright (C) 2018-2022 Intel Corporation
+ */
+static int nl80211_channel2freq(int channel, const char *band, bool ax)
 {
+       if (channel < 1)
+               return 0;
+
        if (!band || band[0] != 'a')
        {
                if (channel == 14)
@@ -679,9 +719,17 @@ static int nl80211_channel2freq(int channel, const char *band)
                else if (channel < 14)
                        return (channel * 5) + 2407;
        }
-       else if ( strcmp(band, "ad") == 0)
+       else if (strcmp(band, "ad")  == 0)
+       {
+               if (channel < 7)
+                       return 56160 + 2160 * channel;
+       }
+       else if (ax)
        {
-               return 56160 + 2160 * channel;
+               if (channel == 2)
+                       return 5935;
+               if (channel < 233)
+                       return (channel * 5) + 5950;
        }
        else
        {
@@ -1336,7 +1384,7 @@ static int nl80211_get_frequency_info_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_frequency(const char *ifname, int *buf)
 {
-       char *res, channel[4], hwmode[3];
+       char *res, channel[4] = { 0 }, hwmode[3] = { 0 }, ax[2] = { 0 };
 
        /* try to find frequency from interface info */
        res = nl80211_phy2ifname(ifname);
@@ -1348,9 +1396,10 @@ static int nl80211_get_frequency(const char *ifname, int *buf)
        /* failed, try to find frequency from hostapd info */
        if ((*buf == 0) &&
            nl80211_hostapd_query(ifname, "hw_mode", hwmode, sizeof(hwmode),
-                                         "channel", channel, sizeof(channel)) == 2)
+                                         "channel", channel, sizeof(channel),
+                                         "ieee80211ax", ax, sizeof(ax)) >= 2)
        {
-               *buf = nl80211_channel2freq(atoi(channel), hwmode);
+               *buf = nl80211_channel2freq(atoi(channel), hwmode, ax[0] == '1');
        }
 
        /* failed, try to find frequency from scan results */
@@ -2980,6 +3029,8 @@ static int nl80211_get_freqlist_cb(struct nl_msg *msg, void *arg)
                                                e->flags |= IWINFO_FREQ_NO_20MHZ;
                                        if (freqs[NL80211_FREQUENCY_ATTR_NO_10MHZ])
                                                e->flags |= IWINFO_FREQ_NO_10MHZ;
+                                       if (freqs[NL80211_FREQUENCY_ATTR_NO_HE])
+                                               e->flags |= IWINFO_FREQ_NO_HE;
 
                                        e++;
                                        arr->count++;
@@ -3137,17 +3188,17 @@ static void nl80211_eval_modelist(struct nl80211_modes *m)
 static int nl80211_get_modelist_cb(struct nl_msg *msg, void *arg)
 {
        struct nl80211_modes *m = arg;
-       int bands_remain, freqs_remain;
+       int bands_remain;
        struct nlattr **attr = nl80211_parse(msg);
        struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
-       struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
-       struct nlattr *band, *freq;
-       uint32_t freq_mhz;
+       struct nlattr *band;
 
        if (attr[NL80211_ATTR_WIPHY_BANDS])
        {
                nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
                {
+                       m->bands |= nl80211_get_band(band->nla_type);
+
                        nla_parse(bands, NL80211_BAND_ATTR_MAX,
                                  nla_data(band), nla_len(band), NULL);
 
@@ -3177,37 +3228,6 @@ static int nl80211_get_modelist_cb(struct nl_msg *msg, void *arg)
                                        }
                                }
                        }
-
-                       if (bands[NL80211_BAND_ATTR_FREQS]) {
-                               nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS],
-                                                   freqs_remain)
-                               {
-                                       nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
-                                               nla_data(freq), nla_len(freq), NULL);
-
-                                       if (!freqs[NL80211_FREQUENCY_ATTR_FREQ])
-                                               continue;
-
-                                       freq_mhz = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]);
-
-                                       if (freq_mhz > 2400 && freq_mhz < 2485)
-                                       {
-                                               m->bands |= IWINFO_BAND_24;
-                                       }
-                                       else if (freq_mhz > 5000 && freq_mhz < 5850)
-                                       {
-                                               m->bands |= IWINFO_BAND_5;
-                                       }
-                                       else if (freq_mhz > 6000 && freq_mhz < 7120)
-                                       {
-                                               m->bands |= IWINFO_BAND_6;
-                                       }
-                                       else if (freq_mhz >= 56160)
-                                       {
-                                               m->bands |= IWINFO_BAND_60;
-                                       }
-                               }
-                       }
                }
 
                m->ok = 1;
@@ -3266,9 +3286,10 @@ static int nl80211_get_htmode_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_htmode(const char *ifname, int *buf)
 {
-       struct chan_info chn = { .width = 0, .mode = 0 };
-       char *res;
+       struct chan_info chn = { 0 };
+       char *res, b[2] = { 0 };
        int err;
+       bool he = false;
 
        res = nl80211_phy2ifname(ifname);
        *buf = 0;
@@ -3279,27 +3300,45 @@ static int nl80211_get_htmode(const char *ifname, int *buf)
        if (err)
                return -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)))
+               he = b[0] == '6';
+
        switch (chn.width) {
        case NL80211_CHAN_WIDTH_20:
-               if (chn.mode == -1)
+               if (he)
+                       *buf = IWINFO_HTMODE_HE20;
+               else if (chn.mode == -1)
                        *buf = IWINFO_HTMODE_VHT20;
                else
                        *buf = IWINFO_HTMODE_HT20;
                break;
        case NL80211_CHAN_WIDTH_40:
-               if (chn.mode == -1)
+               if (he)
+                       *buf = IWINFO_HTMODE_HE40;
+               else if (chn.mode == -1)
                        *buf = IWINFO_HTMODE_VHT40;
                else
                        *buf = IWINFO_HTMODE_HT40;
                break;
        case NL80211_CHAN_WIDTH_80:
-               *buf = IWINFO_HTMODE_VHT80;
+               if (he)
+                       *buf = IWINFO_HTMODE_HE80;
+               else
+                       *buf = IWINFO_HTMODE_VHT80;
                break;
        case NL80211_CHAN_WIDTH_80P80:
-               *buf = IWINFO_HTMODE_VHT80_80;
+               if (he)
+                       *buf = IWINFO_HTMODE_HE80_80;
+               else
+                       *buf = IWINFO_HTMODE_VHT80_80;
                break;
        case NL80211_CHAN_WIDTH_160:
-               *buf = IWINFO_HTMODE_VHT160;
+               if (he)
+                       *buf = IWINFO_HTMODE_HE160;
+               else
+                       *buf = IWINFO_HTMODE_VHT160;
                break;
        case NL80211_CHAN_WIDTH_5:
        case NL80211_CHAN_WIDTH_10: