+void parse_tid_stats(struct nlattr *tid_stats_attr)
+{
+ struct nlattr *stats_info[NL80211_TID_STATS_MAX + 1], *tidattr, *info;
+ static struct nla_policy stats_policy[NL80211_TID_STATS_MAX + 1] = {
+ [NL80211_TID_STATS_RX_MSDU] = { .type = NLA_U64 },
+ [NL80211_TID_STATS_TX_MSDU] = { .type = NLA_U64 },
+ [NL80211_TID_STATS_TX_MSDU_RETRIES] = { .type = NLA_U64 },
+ [NL80211_TID_STATS_TX_MSDU_FAILED] = { .type = NLA_U64 },
+ };
+ int rem, i = 0;
+
+ printf("\n\tMSDU:\n\t\tTID\trx\ttx\ttx retries\ttx failed");
+ nla_for_each_nested(tidattr, tid_stats_attr, rem) {
+ if (nla_parse_nested(stats_info, NL80211_TID_STATS_MAX,
+ tidattr, stats_policy)) {
+ printf("failed to parse nested stats attributes!");
+ return;
+ }
+ printf("\n\t\t%d", i++);
+ info = stats_info[NL80211_TID_STATS_RX_MSDU];
+ if (info)
+ printf("\t%llu", (unsigned long long)nla_get_u64(info));
+ info = stats_info[NL80211_TID_STATS_TX_MSDU];
+ if (info)
+ printf("\t%llu", (unsigned long long)nla_get_u64(info));
+ info = stats_info[NL80211_TID_STATS_TX_MSDU_RETRIES];
+ if (info)
+ printf("\t%llu", (unsigned long long)nla_get_u64(info));
+ info = stats_info[NL80211_TID_STATS_TX_MSDU_FAILED];
+ if (info)
+ printf("\t\t%llu", (unsigned long long)nla_get_u64(info));
+ }
+}
+
+void parse_bss_param(struct nlattr *bss_param_attr)
+{
+ struct nlattr *bss_param_info[NL80211_STA_BSS_PARAM_MAX + 1], *info;
+ static struct nla_policy bss_poilcy[NL80211_STA_BSS_PARAM_MAX + 1] = {
+ [NL80211_STA_BSS_PARAM_CTS_PROT] = { .type = NLA_FLAG },
+ [NL80211_STA_BSS_PARAM_SHORT_PREAMBLE] = { .type = NLA_FLAG },
+ [NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME] = { .type = NLA_FLAG },
+ [NL80211_STA_BSS_PARAM_DTIM_PERIOD] = { .type = NLA_U8 },
+ [NL80211_STA_BSS_PARAM_BEACON_INTERVAL] = { .type = NLA_U16 },
+ };
+
+ if (nla_parse_nested(bss_param_info, NL80211_STA_BSS_PARAM_MAX,
+ bss_param_attr, bss_poilcy)) {
+ printf("failed to parse nested bss param attributes!");
+ }
+
+ info = bss_param_info[NL80211_STA_BSS_PARAM_DTIM_PERIOD];
+ if (info)
+ printf("\n\tDTIM period:\t%u", nla_get_u8(info));
+ info = bss_param_info[NL80211_STA_BSS_PARAM_BEACON_INTERVAL];
+ if (info)
+ printf("\n\tbeacon interval:%u", nla_get_u16(info));
+ info = bss_param_info[NL80211_STA_BSS_PARAM_CTS_PROT];
+ if (info) {
+ printf("\n\tCTS protection:");
+ if (nla_get_u16(info))
+ printf("\tyes");
+ else
+ printf("\tno");
+ }
+ info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_PREAMBLE];
+ if (info) {
+ printf("\n\tshort preamble:");
+ if (nla_get_u16(info))
+ printf("\tyes");
+ else
+ printf("\tno");
+ }
+ info = bss_param_info[NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME];
+ if (info) {
+ printf("\n\tshort slot time:");
+ if (nla_get_u16(info))
+ printf("yes");
+ else
+ printf("no");
+ }
+}
+
+void parse_bitrate(struct nlattr *bitrate_attr, char *buf, int buflen)
+{
+ int rate = 0;
+ char *pos = buf;
+ struct nlattr *rinfo[NL80211_RATE_INFO_MAX + 1];
+ static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
+ [NL80211_RATE_INFO_BITRATE] = { .type = NLA_U16 },
+ [NL80211_RATE_INFO_BITRATE32] = { .type = NLA_U32 },
+ [NL80211_RATE_INFO_MCS] = { .type = NLA_U8 },
+ [NL80211_RATE_INFO_40_MHZ_WIDTH] = { .type = NLA_FLAG },
+ [NL80211_RATE_INFO_SHORT_GI] = { .type = NLA_FLAG },
+ };
+
+ if (nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
+ bitrate_attr, rate_policy)) {
+ snprintf(buf, buflen, "failed to parse nested rate attributes!");
+ return;
+ }
+
+ if (rinfo[NL80211_RATE_INFO_BITRATE32])
+ rate = nla_get_u32(rinfo[NL80211_RATE_INFO_BITRATE32]);
+ else if (rinfo[NL80211_RATE_INFO_BITRATE])
+ rate = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
+ if (rate > 0)
+ pos += snprintf(pos, buflen - (pos - buf),
+ "%d.%d MBit/s", rate / 10, rate % 10);
+
+ if (rinfo[NL80211_RATE_INFO_MCS])
+ pos += snprintf(pos, buflen - (pos - buf),
+ " MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]));
+ if (rinfo[NL80211_RATE_INFO_VHT_MCS])
+ pos += snprintf(pos, buflen - (pos - buf),
+ " VHT-MCS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_MCS]));
+ if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
+ pos += snprintf(pos, buflen - (pos - buf), " 40MHz");
+ if (rinfo[NL80211_RATE_INFO_80_MHZ_WIDTH])
+ pos += snprintf(pos, buflen - (pos - buf), " 80MHz");
+ if (rinfo[NL80211_RATE_INFO_80P80_MHZ_WIDTH])
+ pos += snprintf(pos, buflen - (pos - buf), " 80P80MHz");
+ if (rinfo[NL80211_RATE_INFO_160_MHZ_WIDTH])
+ pos += snprintf(pos, buflen - (pos - buf), " 160MHz");
+ if (rinfo[NL80211_RATE_INFO_SHORT_GI])
+ pos += snprintf(pos, buflen - (pos - buf), " short GI");
+ if (rinfo[NL80211_RATE_INFO_VHT_NSS])
+ pos += snprintf(pos, buflen - (pos - buf),
+ " VHT-NSS %d", nla_get_u8(rinfo[NL80211_RATE_INFO_VHT_NSS]));
+}
+
+static char *get_chain_signal(struct nlattr *attr_list)
+{
+ struct nlattr *attr;
+ static char buf[64];
+ char *cur = buf;
+ int i = 0, rem;
+ const char *prefix;
+
+ if (!attr_list)
+ return "";
+
+ nla_for_each_nested(attr, attr_list, rem) {
+ if (i++ > 0)
+ prefix = ", ";
+ else
+ prefix = "[";
+
+ cur += snprintf(cur, sizeof(buf) - (cur - buf), "%s%d", prefix,
+ (int8_t) nla_get_u8(attr));
+ }
+
+ if (i)
+ snprintf(cur, sizeof(buf) - (cur - buf), "] ");
+
+ return buf;
+}
+