hardware: add device ids for QCA9984, 88W8887 and 88W8964 radios
[project/iwinfo.git] / iwinfo_nl80211.c
index db0fc42..ecd2d6a 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)
 {
@@ -1243,7 +1279,8 @@ static int nl80211_fill_signal_cb(struct nl_msg *msg, void *arg)
                        if (sinfo[NL80211_STA_INFO_SIGNAL])
                        {
                                dbm = nla_get_u8(sinfo[NL80211_STA_INFO_SIGNAL]);
-                               rr->rssi = rr->rssi ? (int8_t)((rr->rssi + dbm) / 2) : dbm;
+                               rr->rssi = (rr->rssi * rr->rssi_samples + dbm) / (rr->rssi_samples + 1);
+                               rr->rssi_samples++;
                        }
 
                        if (sinfo[NL80211_STA_INFO_TX_BITRATE])
@@ -1255,8 +1292,8 @@ static int nl80211_fill_signal_cb(struct nl_msg *msg, void *arg)
                                        if (rinfo[NL80211_RATE_INFO_BITRATE])
                                        {
                                                mbit = nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]);
-                                               rr->rate = rr->rate
-                                                       ? (int16_t)((rr->rate + mbit) / 2) : mbit;
+                                               rr->rate = (rr->rate * rr->rate_samples + mbit) / (rr->rate_samples + 1);
+                                               rr->rate_samples++;
                                        }
                                }
                        }
@@ -1271,8 +1308,7 @@ static void nl80211_fill_signal(const char *ifname, struct nl80211_rssi_rate *r)
        DIR *d;
        struct dirent *de;
 
-       r->rssi = 0;
-       r->rate = 0;
+       memset(r, 0, sizeof(*r));
 
        if ((d = opendir("/sys/class/net")) != NULL)
        {
@@ -1297,7 +1333,7 @@ static int nl80211_get_bitrate(const char *ifname, int *buf)
 
        nl80211_fill_signal(ifname, &rr);
 
-       if (rr.rate)
+       if (rr.rate_samples)
        {
                *buf = (rr.rate * 100);
                return 0;
@@ -1312,7 +1348,7 @@ static int nl80211_get_signal(const char *ifname, int *buf)
 
        nl80211_fill_signal(ifname, &rr);
 
-       if (rr.rssi)
+       if (rr.rssi_samples)
        {
                *buf = rr.rssi;
                return 0;
@@ -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,
@@ -2182,11 +2223,21 @@ static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
                        /* is another unrelated event, retry */
                        tries--;
                }
+
+               /* got a failure reply */
+               else if (!strcmp(reply, "FAIL-BUSY\n"))
+               {
+                       break;
+               }
        }
 
        /* 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 +2315,7 @@ static int nl80211_get_scanlist_wpactl(const char *ifname, char *buf, int *len)
                }
 
                *len = count * sizeof(struct iwinfo_scanlist_entry);
+               break;
        }
 
        close(sock);
@@ -2367,37 +2419,57 @@ static int nl80211_get_freqlist_cb(struct nl_msg *msg, void *arg)
        int bands_remain, freqs_remain;
 
        struct nl80211_array_buf *arr = arg;
-       struct iwinfo_freqlist_entry *e = arr->buf;
+       struct iwinfo_freqlist_entry *e;
 
        struct nlattr **attr = nl80211_parse(msg);
        struct nlattr *bands[NL80211_BAND_ATTR_MAX + 1];
        struct nlattr *freqs[NL80211_FREQUENCY_ATTR_MAX + 1];
        struct nlattr *band, *freq;
 
-       nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
-       {
-               nla_parse(bands, NL80211_BAND_ATTR_MAX,
-                         nla_data(band), nla_len(band), NULL);
+       e = arr->buf;
+       e += arr->count;
 
-               nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
+       if (attr[NL80211_ATTR_WIPHY_BANDS]) {
+               nla_for_each_nested(band, attr[NL80211_ATTR_WIPHY_BANDS], bands_remain)
                {
-                       nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
-                                 nla_data(freq), nla_len(freq), NULL);
-
-                       if (!freqs[NL80211_FREQUENCY_ATTR_FREQ] ||
-                           freqs[NL80211_FREQUENCY_ATTR_DISABLED])
-                               continue;
-
-                       e->mhz = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]);
-                       e->channel = nl80211_freq2channel(e->mhz);
-
-                       e->restricted = (
-                               freqs[NL80211_FREQUENCY_ATTR_NO_IR] &&
-                               !freqs[NL80211_FREQUENCY_ATTR_RADAR]
-                       ) ? 1 : 0;
+                       nla_parse(bands, NL80211_BAND_ATTR_MAX,
+                                 nla_data(band), nla_len(band), NULL);
 
-                       e++;
-                       arr->count++;
+                       if (bands[NL80211_BAND_ATTR_FREQS]) {
+                               nla_for_each_nested(freq, bands[NL80211_BAND_ATTR_FREQS], freqs_remain)
+                               {
+                                       nla_parse(freqs, NL80211_FREQUENCY_ATTR_MAX,
+                                                 nla_data(freq), nla_len(freq), NULL);
+
+                                       if (!freqs[NL80211_FREQUENCY_ATTR_FREQ] ||
+                                           freqs[NL80211_FREQUENCY_ATTR_DISABLED])
+                                               continue;
+
+                                       e->mhz = nla_get_u32(freqs[NL80211_FREQUENCY_ATTR_FREQ]);
+                                       e->channel = nl80211_freq2channel(e->mhz);
+
+                                       e->restricted = (
+                                               freqs[NL80211_FREQUENCY_ATTR_NO_IR] &&
+                                               !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++;
+                               }
+                       }
                }
        }
 
@@ -2406,15 +2478,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;