add support for expected throughput
[project/iwinfo.git] / iwinfo_nl80211.c
index 0f9800b..738e2f5 100644 (file)
@@ -425,6 +425,30 @@ static struct nlattr ** nl80211_parse(struct nl_msg *msg)
        return attr;
 }
 
+static int nl80211_get_protocol_features_cb(struct nl_msg *msg, void *arg)
+{
+       uint32_t *features = arg;
+       struct nlattr **attr = nl80211_parse(msg);
+
+       if (attr[NL80211_ATTR_PROTOCOL_FEATURES])
+               *features = nla_get_u32(attr[NL80211_ATTR_PROTOCOL_FEATURES]);
+
+       return NL_SKIP;
+}
+
+static int nl80211_get_protocol_features(const char *ifname)
+{
+       struct nl80211_msg_conveyor *req;
+       uint32_t features = 0;
+
+       req = nl80211_msg(ifname, NL80211_CMD_GET_PROTOCOL_FEATURES, 0);
+       if (req) {
+               nl80211_send(req, nl80211_get_protocol_features_cb, &features);
+               nl80211_free(req);
+       }
+
+       return features;
+}
 
 static int nl80211_subscribe_cb(struct nl_msg *msg, void *arg)
 {
@@ -486,7 +510,7 @@ static int nl80211_wait_cb(struct nl_msg *msg, void *arg)
        struct nl80211_event_conveyor *cv = arg;
        struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
 
-       if (gnlh->cmd == cv->wait)
+       if (cv->wait[gnlh->cmd / 32] & (1 << (gnlh->cmd % 32)))
                cv->recv = gnlh->cmd;
 
        return NL_SKIP;
@@ -497,11 +521,13 @@ static int nl80211_wait_seq_check(struct nl_msg *msg, void *arg)
        return NL_OK;
 }
 
-static int nl80211_wait(const char *family, const char *group, int cmd)
+static int __nl80211_wait(const char *family, const char *group, ...)
 {
-       struct nl80211_event_conveyor cv = { .wait = cmd };
+       struct nl80211_event_conveyor cv = { };
        struct nl_cb *cb;
        int err = 0;
+       int cmd;
+       va_list ap;
 
        if (nl80211_subscribe(family, group))
                return -ENOENT;
@@ -515,6 +541,13 @@ static int nl80211_wait(const char *family, const char *group, int cmd)
        nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, nl80211_wait_seq_check, NULL);
        nl_cb_set(cb, NL_CB_VALID,     NL_CB_CUSTOM, nl80211_wait_cb,        &cv );
 
+       va_start(ap, group);
+
+       for (cmd = va_arg(ap, int); cmd != 0; cmd = va_arg(ap, int))
+               cv.wait[cmd / 32] |= (1 << (cmd % 32));
+
+       va_end(ap);
+
        while (!cv.recv && !err)
                nl_recvmsgs(nls->nl_sock, cb);
 
@@ -523,6 +556,9 @@ static int nl80211_wait(const char *family, const char *group, int cmd)
        return err;
 }
 
+#define nl80211_wait(family, group, ...) \
+       __nl80211_wait(family, group, __VA_ARGS__, 0)
+
 
 static int nl80211_freq2channel(int freq)
 {
@@ -1665,6 +1701,7 @@ static int nl80211_get_assoclist_cb(struct nl_msg *msg, void *arg)
                [NL80211_STA_INFO_T_OFFSET]      = { .type = NLA_U64    },
                [NL80211_STA_INFO_STA_FLAGS] =
                        { .minlen = sizeof(struct nl80211_sta_flag_update) },
+               [NL80211_STA_INFO_EXPECTED_THROUGHPUT]   = { .type = NLA_U32    },
        };
 
        static struct nla_policy rate_policy[NL80211_RATE_INFO_MAX + 1] = {
@@ -1722,6 +1759,9 @@ static int nl80211_get_assoclist_cb(struct nl_msg *msg, void *arg)
                if (sinfo[NL80211_STA_INFO_T_OFFSET])
                        e->t_offset = nla_get_u64(sinfo[NL80211_STA_INFO_T_OFFSET]);
 
+               if (sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT])
+                       e->thr = nla_get_u32(sinfo[NL80211_STA_INFO_EXPECTED_THROUGHPUT]);
+
                /* Station flags */
                if (sinfo[NL80211_STA_INFO_STA_FLAGS])
                {
@@ -2067,7 +2107,8 @@ static int nl80211_get_scanlist_nl(const char *ifname, char *buf, int *len)
        if (nl80211_request(ifname, NL80211_CMD_TRIGGER_SCAN, 0, NULL, NULL))
                goto out;
 
-       if (nl80211_wait("nl80211", "scan", NL80211_CMD_NEW_SCAN_RESULTS))
+       if (nl80211_wait("nl80211", "scan",
+                        NL80211_CMD_NEW_SCAN_RESULTS, NL80211_CMD_SCAN_ABORTED))
                goto out;
 
        if (nl80211_request(ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
@@ -2185,8 +2226,12 @@ static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
        }
 
        /* receive and parse scan results if the wait above didn't time out */
-       if (ready && nl80211_wpactl_recv(sock, reply, sizeof(reply)) > 0)
+       while (ready && nl80211_wpactl_recv(sock, reply, sizeof(reply)) > 0)
        {
+               /* received an event notification, receive again */
+               if (reply[0] == '<')
+                       continue;
+
                nl80211_get_quality_max(ifname, &qmax);
 
                for (line = strtok_r(reply, "\n", &pos);
@@ -2264,6 +2309,7 @@ static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
                }
 
                *len = count * sizeof(struct iwinfo_scanlist_entry);
+               break;
        }
 
        close(sock);
@@ -2401,6 +2447,19 @@ static int nl80211_get_freqlist_cb(struct nl_msg *msg, void *arg)
                                                !freqs[NL80211_FREQUENCY_ATTR_RADAR]
                                        ) ? 1 : 0;
 
+                                       if (freqs[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
+                                               e->flags |= IWINFO_FREQ_NO_HT40MINUS;
+                                       if (freqs[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
+                                               e->flags |= IWINFO_FREQ_NO_HT40PLUS;
+                                       if (freqs[NL80211_FREQUENCY_ATTR_NO_80MHZ])
+                                               e->flags |= IWINFO_FREQ_NO_80MHZ;
+                                       if (freqs[NL80211_FREQUENCY_ATTR_NO_160MHZ])
+                                               e->flags |= IWINFO_FREQ_NO_160MHZ;
+                                       if (freqs[NL80211_FREQUENCY_ATTR_NO_20MHZ])
+                                               e->flags |= IWINFO_FREQ_NO_20MHZ;
+                                       if (freqs[NL80211_FREQUENCY_ATTR_NO_10MHZ])
+                                               e->flags |= IWINFO_FREQ_NO_10MHZ;
+
                                        e++;
                                        arr->count++;
                                }
@@ -2413,15 +2472,25 @@ static int nl80211_get_freqlist_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_freqlist(const char *ifname, char *buf, int *len)
 {
+       struct nl80211_msg_conveyor *cv;
        struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
+       uint32_t features = nl80211_get_protocol_features(ifname);
+       int flags;
 
-       if (nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
-                           nl80211_get_freqlist_cb, &arr))
+       flags = features & NL80211_PROTOCOL_FEATURE_SPLIT_WIPHY_DUMP ? NLM_F_DUMP : 0;
+       cv = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, flags);
+       if (!cv)
+               goto out;
+
+       NLA_PUT_FLAG(cv->msg, NL80211_ATTR_SPLIT_WIPHY_DUMP);
+       if (nl80211_send(cv, nl80211_get_freqlist_cb, &arr))
                goto out;
 
        *len = arr.count * sizeof(struct iwinfo_freqlist_entry);
        return 0;
 
+nla_put_failure:
+       nl80211_free(cv);
 out:
        *len = 0;
        return -1;