| --- a/info.c |
| +++ b/info.c |
| @@ -295,6 +295,151 @@ static void print_pmsr_capabilities(stru |
| } |
| } |
| |
| +static void print_interface_combinations(struct nlattr *ifcomb, bool radio) |
| +{ |
| + const char *indent = radio ? "\t" : ""; |
| + struct nlattr *nl_combi; |
| + bool have_combinations = false; |
| + int rem; |
| + |
| + nla_for_each_nested(nl_combi, ifcomb, rem) { |
| + 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 (radio && nla_type(nl_combi) != |
| + NL80211_WIPHY_RADIO_ATTR_INTERFACE_COMBINATION) |
| + continue; |
| + |
| + if (!have_combinations) { |
| + printf("\t%svalid interface combinations:\n", indent); |
| + have_combinations = true; |
| + } |
| + |
| + printf("\t\t%s * ", indent); |
| + |
| + 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%s ", 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"); |
| +broken_combination: |
| + ; |
| + } |
| + |
| + if (!have_combinations) |
| + printf("\t%sinterface combinations are not supported\n", indent); |
| +} |
| + |
| +static void print_radio_freq(struct nlattr *freqs) |
| +{ |
| + struct nlattr *freq; |
| + int rem; |
| + |
| + nla_for_each_nested(freq, freqs, rem) { |
| + static struct nla_policy freq_policy[NL80211_WIPHY_RADIO_FREQ_ATTR_MAX + 1] = { |
| + [NL80211_WIPHY_RADIO_FREQ_ATTR_START] = { .type = NLA_U32 }, |
| + [NL80211_WIPHY_RADIO_FREQ_ATTR_END] = { .type = NLA_U32 }, |
| + }; |
| + struct nlattr *tb[NL80211_WIPHY_RADIO_FREQ_ATTR_MAX + 1]; |
| + uint32_t start, end; |
| + |
| + if (nla_type(freq) != NL80211_WIPHY_RADIO_ATTR_FREQ_RANGE) |
| + continue; |
| + |
| + if (nla_parse_nested(tb, NL80211_WIPHY_RADIO_ATTR_MAX + 1, |
| + freq, freq_policy) || |
| + !tb[NL80211_WIPHY_RADIO_FREQ_ATTR_START] || |
| + !tb[NL80211_WIPHY_RADIO_FREQ_ATTR_END]) |
| + continue; |
| + |
| + start = nla_get_u32(tb[NL80211_WIPHY_RADIO_FREQ_ATTR_START]); |
| + end = nla_get_u32(tb[NL80211_WIPHY_RADIO_FREQ_ATTR_END]); |
| + |
| + printf("\t\tfreq range: %.1f MHz - %.1f MHz\n", (float)start / 1000, (float)end / 1000); |
| + } |
| +} |
| + |
| +static void print_radios(struct nlattr *radios) |
| +{ |
| + struct nlattr *radio; |
| + int rem, idx = 0; |
| + |
| + nla_for_each_nested(radio, radios, rem) { |
| + static struct nla_policy radio_policy[NL80211_WIPHY_RADIO_ATTR_MAX + 1] = { |
| + [NL80211_WIPHY_RADIO_ATTR_INDEX] = { .type = NLA_U32 }, |
| + }; |
| + struct nlattr *tb[NL80211_WIPHY_RADIO_ATTR_MAX + 1]; |
| + |
| + if (nla_parse_nested(tb, NL80211_WIPHY_RADIO_ATTR_MAX + 1, |
| + radio, radio_policy) || |
| + !tb[NL80211_WIPHY_RADIO_ATTR_INDEX]) |
| + continue; |
| + |
| + printf("\twiphy radio %d:\n", nla_get_u32(tb[NL80211_WIPHY_RADIO_ATTR_INDEX])); |
| + print_radio_freq(radio); |
| + print_interface_combinations(radio, true); |
| + } |
| +} |
| + |
| static int print_phy_handler(struct nl_msg *msg, void *arg) |
| { |
| struct nlattr *tb_msg[NL80211_ATTR_MAX + 1]; |
| @@ -565,93 +710,11 @@ next: |
| "\t\t", tb_msg[NL80211_ATTR_SOFTWARE_IFTYPES]); |
| #endif |
| |
| - if (tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS]) { |
| - 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 * "); |
| + if (tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS]) |
| + print_interface_combinations(tb_msg[NL80211_ATTR_INTERFACE_COMBINATIONS], false); |
| |
| - 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: |
| - ; |
| - } |
| - |
| - if (!have_combinations) |
| - printf("\tinterface combinations are not supported\n"); |
| - } |
| + if (tb_msg[NL80211_ATTR_WIPHY_RADIOS]) |
| + print_radios(tb_msg[NL80211_ATTR_WIPHY_RADIOS]); |
| |
| #ifdef IW_FULL |
| if (tb_msg[NL80211_ATTR_SUPPORTED_COMMANDS]) { |