+static void nl80211_parse_rateinfo(struct nlattr **ri,
+ struct iwinfo_rate_entry *re)
+{
+ if (ri[NL80211_RATE_INFO_BITRATE32])
+ re->rate = nla_get_u32(ri[NL80211_RATE_INFO_BITRATE32]) * 100;
+ else if (ri[NL80211_RATE_INFO_BITRATE])
+ re->rate = nla_get_u16(ri[NL80211_RATE_INFO_BITRATE]) * 100;
+
+ if (ri[NL80211_RATE_INFO_VHT_MCS])
+ {
+ re->is_vht = 1;
+ re->mcs = nla_get_u8(ri[NL80211_RATE_INFO_VHT_MCS]);
+
+ if (ri[NL80211_RATE_INFO_VHT_NSS])
+ re->nss = nla_get_u8(ri[NL80211_RATE_INFO_VHT_NSS]);
+ }
+ else if (ri[NL80211_RATE_INFO_MCS])
+ {
+ re->is_ht = 1;
+ re->mcs = nla_get_u8(ri[NL80211_RATE_INFO_MCS]);
+ }
+
+ if (ri[NL80211_RATE_INFO_5_MHZ_WIDTH])
+ re->mhz = 5;
+ else if (ri[NL80211_RATE_INFO_10_MHZ_WIDTH])
+ re->mhz = 10;
+ else if (ri[NL80211_RATE_INFO_40_MHZ_WIDTH])
+ re->mhz = 40;
+ else if (ri[NL80211_RATE_INFO_80_MHZ_WIDTH])
+ re->mhz = 80;
+ else if (ri[NL80211_RATE_INFO_80P80_MHZ_WIDTH] ||
+ ri[NL80211_RATE_INFO_160_MHZ_WIDTH])
+ re->mhz = 160;
+ else
+ re->mhz = 20;
+
+ if (ri[NL80211_RATE_INFO_SHORT_GI])
+ re->is_short_gi = 1;
+
+ re->is_40mhz = (re->mhz == 40);
+}
+
+static int nl80211_get_survey_cb(struct nl_msg *msg, void *arg)
+{
+ struct nl80211_array_buf *arr = arg;
+ struct iwinfo_survey_entry *e = arr->buf;
+ struct nlattr **attr = nl80211_parse(msg);
+ struct nlattr *sinfo[NL80211_SURVEY_INFO_MAX + 1];
+ int rc;
+
+ static struct nla_policy survey_policy[NL80211_SURVEY_INFO_MAX + 1] = {
+ [NL80211_SURVEY_INFO_FREQUENCY] = { .type = NLA_U32 },
+ [NL80211_SURVEY_INFO_NOISE] = { .type = NLA_U8 },
+ [NL80211_SURVEY_INFO_TIME] = { .type = NLA_U64 },
+ [NL80211_SURVEY_INFO_TIME_BUSY] = { .type = NLA_U64 },
+ [NL80211_SURVEY_INFO_TIME_EXT_BUSY] = { .type = NLA_U64 },
+ [NL80211_SURVEY_INFO_TIME_RX] = { .type = NLA_U64 },
+ [NL80211_SURVEY_INFO_TIME_TX] = { .type = NLA_U64 },
+ };
+
+ rc = nla_parse_nested(sinfo, NL80211_SURVEY_INFO_MAX,
+ attr[NL80211_ATTR_SURVEY_INFO],
+ survey_policy);
+ if (rc)
+ return NL_SKIP;
+
+ /* advance to end of array */
+ e += arr->count;
+ memset(e, 0, sizeof(*e));
+
+ if (sinfo[NL80211_SURVEY_INFO_FREQUENCY])
+ e->mhz = nla_get_u32(sinfo[NL80211_SURVEY_INFO_FREQUENCY]);
+
+ if (sinfo[NL80211_SURVEY_INFO_NOISE])
+ e->noise = nla_get_u8(sinfo[NL80211_SURVEY_INFO_NOISE]);
+
+ if (sinfo[NL80211_SURVEY_INFO_TIME])
+ e->active_time = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME]);
+
+ if (sinfo[NL80211_SURVEY_INFO_TIME_BUSY])
+ e->busy_time = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_BUSY]);
+
+ if (sinfo[NL80211_SURVEY_INFO_TIME_EXT_BUSY])
+ e->busy_time_ext = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_EXT_BUSY]);
+
+ if (sinfo[NL80211_SURVEY_INFO_TIME_RX])
+ e->rxtime = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_RX]);
+
+ if (sinfo[NL80211_SURVEY_INFO_TIME_TX])
+ e->txtime = nla_get_u64(sinfo[NL80211_SURVEY_INFO_TIME_TX]);
+
+ arr->count++;
+ return NL_SKIP;
+}
+
+
+static void plink_state_to_str(char *dst, unsigned state)
+{
+ switch (state) {
+ case NL80211_PLINK_LISTEN:
+ strcpy(dst, "LISTEN");
+ break;
+ case NL80211_PLINK_OPN_SNT:
+ strcpy(dst, "OPN_SNT");
+ break;
+ case NL80211_PLINK_OPN_RCVD:
+ strcpy(dst, "OPN_RCVD");
+ break;
+ case NL80211_PLINK_CNF_RCVD:
+ strcpy(dst, "CNF_RCVD");
+ break;
+ case NL80211_PLINK_ESTAB:
+ strcpy(dst, "ESTAB");
+ break;
+ case NL80211_PLINK_HOLDING:
+ strcpy(dst, "HOLDING");
+ break;
+ case NL80211_PLINK_BLOCKED:
+ strcpy(dst, "BLOCKED");
+ break;
+ default:
+ strcpy(dst, "UNKNOWN");
+ break;
+ }
+}
+
+static void power_mode_to_str(char *dst, struct nlattr *a)
+{
+ enum nl80211_mesh_power_mode pm = nla_get_u32(a);
+
+ switch (pm) {
+ case NL80211_MESH_POWER_ACTIVE:
+ strcpy(dst, "ACTIVE");
+ break;
+ case NL80211_MESH_POWER_LIGHT_SLEEP:
+ strcpy(dst, "LIGHT SLEEP");
+ break;
+ case NL80211_MESH_POWER_DEEP_SLEEP:
+ strcpy(dst, "DEEP SLEEP");
+ break;
+ default:
+ strcpy(dst, "UNKNOWN");
+ break;
+ }
+}
+