When multiple hardwares are grouped under a wiphy, the information about it is advertised via NL80211_ATTR_WIPHY_RADIOS and NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS attributes. Add support to parse and display the information in info command. Sample output with hwsim radio (insmod mac80211_hwsim.ko radios=1 mlo=1 multi_radio=1): $ iw phy0 info .... valid interface combinations: * #{ IBSS } <= 1, #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 2048, #{ P2P-device } <= 1, total <= 2050, #channels <= 1, radar detect widths: { 20 MHz (no HT), 20 MHz, 40 MHz, 80 MHz, 160 MHz, 5 MHz, 10 MHz } .... Supported wiphy radios: * Idx 0: Frequency Range: 2402 MHz - 2494 MHz Radio's valid interface combinations: * #{ IBSS } <= 1, #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 2048, #{ P2P-device } <= 1, total <= 2050, #channels <= 1, radar detect widths: { 20 MHz (no HT), 20 MHz, 40 MHz, 80 MHz, 160 MHz, 5 MHz, 10 MHz } * Idx 1: Frequency Range: 5170 MHz - 5935 MHz Radio's valid interface combinations: * #{ IBSS } <= 1, #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 2048, #{ P2P-device } <= 1, total <= 2050, #channels <= 1, radar detect widths: { 20 MHz (no HT), 20 MHz, 40 MHz, 80 MHz, 160 MHz, 5 MHz, 10 MHz } * Idx 2: Frequency Range: 5945 MHz - 7125 MHz Radio's valid interface combinations: * #{ IBSS } <= 1, #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 2048, #{ P2P-device } <= 1, total <= 2050, #channels <= 1, radar detect widths: { 20 MHz (no HT), 20 MHz, 40 MHz, 80 MHz, 160 MHz, 5 MHz, 10 MHz } * Idx 3: Frequency Range: 892 MHz - 937 MHz Radio's valid interface combinations: * #{ IBSS } <= 1, #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 2048, #{ P2P-device } <= 1, total <= 2050, #channels <= 1, radar detect widths: { 20 MHz (no HT), 20 MHz, 40 MHz, 80 MHz, 160 MHz, 5 MHz, 10 MHz } Globally valid interface combinations: * #{ IBSS } <= 1, #{ managed, AP, mesh point, P2P-client, P2P-GO } <= 2048, #{ P2P-device } <= 1, total <= 2050, #channels <= 4 Signed-off-by: Aditya Kumar Singh <quic_adisi@xxxxxxxxxxx> --- info.c | 219 ++++++++++++++++++++++++++++++++++++--------------------- iw.h | 7 ++ reg.c | 7 -- 3 files changed, 147 insertions(+), 86 deletions(-) diff --git a/info.c b/info.c index c5e863f1d6b8..986eaa66b3b7 100644 --- a/info.c +++ b/info.c @@ -295,6 +295,85 @@ static void print_pmsr_capabilities(struct nlattr *pmsr_capa) } } +static void print_interface_combination(struct nlattr *nla, bool *have_combinations, + const char *indent, const char *tag) +{ + static struct nla_policy iface_combination_policy[NUM_NL80211_IFACE_COMB] = { + [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, + [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, + [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, + [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, + }; + struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; + static struct nla_policy iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { + [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, + [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, + }; + struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; + struct nlattr *nl_limit; + int err, rem_limit; + bool comma = false; + + if (!(*have_combinations)) { + printf("%s%svalid interface combinations:\n", indent, tag); + *have_combinations = true; + } + + printf("%s\t * ", indent); + + err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, + nla, iface_combination_policy); + if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || + !tb_comb[NL80211_IFACE_COMB_MAXNUM] || + !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) { + printf(" <failed to parse>\n"); + return; + } + + nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], rem_limit) { + err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, + nl_limit, iface_limit_policy); + if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) { + printf("<failed to parse>\n"); + return; + } + + if (comma) + printf(", "); + comma = true; + printf("#{ "); + print_iftype_line(tb_limit[NL80211_IFACE_LIMIT_TYPES]); + printf(" } <= %u", nla_get_u32(tb_limit[NL80211_IFACE_LIMIT_MAX])); + } + printf(",\n%s\t ", indent); + + printf("total <= %d, #channels <= %d%s", + nla_get_u32(tb_comb[NL80211_IFACE_COMB_MAXNUM]), + nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]), + tb_comb[NL80211_IFACE_COMB_STA_AP_BI_MATCH] ? + ", STA/AP BI must match" : ""); + if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) { + unsigned long widths = nla_get_u32(tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]); + + if (widths) { + int width; + bool first = true; + + printf(", radar detect widths: {"); + for (width = 0; width < 32; width++) + if (widths & (1 << width)) { + printf("%s %s", + first ? "":",", + channel_width_name(width)); + first = false; + } + printf(" }\n"); + } + } + printf("\n"); +} + static int print_phy_handler(struct nl_msg *msg, void *arg) { struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; @@ -560,88 +639,12 @@ next: "\t\t", tb_msg[NL80211_ATTR_SOFTWARE_IFTYPES]); if (tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS]) { + bool have_combinations = false; struct nlattr *nl_combi; int rem_combi; - bool have_combinations = false; - - nla_for_each_nested(nl_combi, tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS], rem_combi) { - static struct nla_policy iface_combination_policy[NUM_NL80211_IFACE_COMB] = { - [NL80211_IFACE_COMB_LIMITS] = { .type = NLA_NESTED }, - [NL80211_IFACE_COMB_MAXNUM] = { .type = NLA_U32 }, - [NL80211_IFACE_COMB_STA_AP_BI_MATCH] = { .type = NLA_FLAG }, - [NL80211_IFACE_COMB_NUM_CHANNELS] = { .type = NLA_U32 }, - [NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS] = { .type = NLA_U32 }, - }; - struct nlattr *tb_comb[NUM_NL80211_IFACE_COMB]; - static struct nla_policy iface_limit_policy[NUM_NL80211_IFACE_LIMIT] = { - [NL80211_IFACE_LIMIT_TYPES] = { .type = NLA_NESTED }, - [NL80211_IFACE_LIMIT_MAX] = { .type = NLA_U32 }, - }; - struct nlattr *tb_limit[NUM_NL80211_IFACE_LIMIT]; - struct nlattr *nl_limit; - int err, rem_limit; - bool comma = false; - - if (!have_combinations) { - printf("\tvalid interface combinations:\n"); - have_combinations = true; - } - printf("\t\t * "); - - err = nla_parse_nested(tb_comb, MAX_NL80211_IFACE_COMB, - nl_combi, iface_combination_policy); - if (err || !tb_comb[NL80211_IFACE_COMB_LIMITS] || - !tb_comb[NL80211_IFACE_COMB_MAXNUM] || - !tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]) { - printf(" <failed to parse>\n"); - goto broken_combination; - } - - nla_for_each_nested(nl_limit, tb_comb[NL80211_IFACE_COMB_LIMITS], rem_limit) { - err = nla_parse_nested(tb_limit, MAX_NL80211_IFACE_LIMIT, - nl_limit, iface_limit_policy); - if (err || !tb_limit[NL80211_IFACE_LIMIT_TYPES]) { - printf("<failed to parse>\n"); - goto broken_combination; - } - - if (comma) - printf(", "); - comma = true; - printf("#{ "); - print_iftype_line(tb_limit[NL80211_IFACE_LIMIT_TYPES]); - printf(" } <= %u", nla_get_u32(tb_limit[NL80211_IFACE_LIMIT_MAX])); - } - printf(",\n\t\t "); - - printf("total <= %d, #channels <= %d%s", - nla_get_u32(tb_comb[NL80211_IFACE_COMB_MAXNUM]), - nla_get_u32(tb_comb[NL80211_IFACE_COMB_NUM_CHANNELS]), - tb_comb[NL80211_IFACE_COMB_STA_AP_BI_MATCH] ? - ", STA/AP BI must match" : ""); - if (tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]) { - unsigned long widths = nla_get_u32(tb_comb[NL80211_IFACE_COMB_RADAR_DETECT_WIDTHS]); - - if (widths) { - int width; - bool first = true; - - printf(", radar detect widths: {"); - for (width = 0; width < 32; width++) - if (widths & (1 << width)) { - printf("%s %s", - first ? "":",", - channel_width_name(width)); - first = false; - } - printf(" }\n"); - } - } - printf("\n"); -broken_combination: - ; - } + nla_for_each_nested(nl_combi, tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS], rem_combi) + print_interface_combination(nl_combi, &have_combinations, "\t", ""); if (!have_combinations) printf("\tinterface combinations are not supported\n"); @@ -885,6 +888,64 @@ broken_combination: printf("\tMaximum associated stations in AP mode: %u\n", nla_get_u32(tb_msg[NL80211_ATTR_MAX_AP_ASSOC_STA])); + if (tb_msg[NL80211_ATTR_WIPHY_RADIOS]) { + struct nlattr *radio; + int rem_radios; + + printf("\tSupported wiphy radios:\n"); + nla_for_each_nested(radio, tb_msg[NL80211_ATTR_WIPHY_RADIOS], rem_radios) { + bool have_combinations = false; + struct nlattr *radio_prop; + int rem_radio_prop; + + nla_for_each_nested(radio_prop, radio, rem_radio_prop) { + struct nlattr *tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_MAX + 1]; + + switch (nla_type(radio_prop)) { + case NL80211_WIPHY_RADIO_ATTR_INDEX: + printf("\t\t* Idx %u:\n", nla_get_u32(radio_prop)); + break; + case NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE: + printf("\t\t\tFrequency Range: "); + + nla_parse_nested(tb_freq, NL80211_WIPHY_RADIO_FREQ_ATTR_MAX, radio_prop, + NULL); + if (!tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_START] || + !tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_END]) { + printf("<failed to parse>"); + } else { + printf("%u MHz - %u MHz", + KHZ_TO_MHZ(nla_get_u32(tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_START])), + KHZ_TO_MHZ(nla_get_u32(tb_freq[NL80211_WIPHY_RADIO_FREQ_ATTR_END]))); + } + printf("\n"); + break; + case NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION: + print_interface_combination(radio_prop, &have_combinations, "\t\t\t", + "Radio's "); + if (!have_combinations) + printf("\t\t\tRadio level interface combinations are not supported\n"); + break; + default: + printf("\t\t\t* <failed to parse>\n"); + } + } + } + } + + if (tb_msg[NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS]) { + bool have_combinations = false; + struct nlattr *nl_combi; + int rem_combi; + + nla_for_each_nested(nl_combi, tb_msg[NL80211_ATTR_WIPHY_INTERFACE_COMBINATIONS], rem_combi) + print_interface_combination(nl_combi, &have_combinations, "\t", + "Globally "); + + if (!have_combinations) + printf("\tGlobal interface combinations are not supported\n"); + } + return NL_SKIP; } diff --git a/iw.h b/iw.h index f416d6df1843..b45e54a8832b 100644 --- a/iw.h +++ b/iw.h @@ -47,6 +47,13 @@ enum nlmsgerr_attrs { # define nl_sock nl_handle #endif +#define MHZ_TO_KHZ(freq) ((freq) * 1000) +#define KHZ_TO_MHZ(freq) ((freq) / 1000) +#define DBI_TO_MBI(gain) ((gain) * 100) +#define MBI_TO_DBI(gain) ((gain) / 100) +#define DBM_TO_MBM(gain) ((gain) * 100) +#define MBM_TO_DBM(gain) ((gain) / 100) + struct nl80211_state { struct nl_sock *nl_sock; int nl80211_id; diff --git a/reg.c b/reg.c index 857c995cf635..27899ce859ea 100644 --- a/reg.c +++ b/reg.c @@ -13,13 +13,6 @@ SECTION(reg); -#define MHZ_TO_KHZ(freq) ((freq) * 1000) -#define KHZ_TO_MHZ(freq) ((freq) / 1000) -#define DBI_TO_MBI(gain) ((gain) * 100) -#define MBI_TO_DBI(gain) ((gain) / 100) -#define DBM_TO_MBM(gain) ((gain) * 100) -#define MBM_TO_DBM(gain) ((gain) / 100) - static bool isalpha_upper(char letter) { if (letter >= 65 && letter <= 90) -- 2.34.1