add support for expected throughput
[project/iwinfo.git] / iwinfo_nl80211.c
index a65ed1e..738e2f5 100644 (file)
@@ -218,9 +218,6 @@ static struct nl80211_msg_conveyor * nl80211_new(struct genl_family *family,
        return &cv;
 
 err:
-       if (cb)
-               nl_cb_put(cb);
-
        if (req)
                nlmsg_free(req);
 
@@ -248,6 +245,9 @@ static int nl80211_phy_idx_from_uci_path(struct uci_section *s)
 
        snprintf(buf, sizeof(buf), "/sys/devices/%s/ieee80211/*/index", opt);  /**/
        if (glob(buf, 0, NULL, &gl))
+               snprintf(buf, sizeof(buf), "/sys/devices/platform/%s/ieee80211/*/index", opt);  /**/
+
+       if (glob(buf, 0, NULL, &gl))
                return -1;
 
        if (gl.gl_pathc > 0)
@@ -369,20 +369,24 @@ nla_put_failure:
        return NULL;
 }
 
-static struct nl80211_msg_conveyor * nl80211_send(
-       struct nl80211_msg_conveyor *cv,
-       int (*cb_func)(struct nl_msg *, void *), void *cb_arg
-{
+static int nl80211_send(struct nl80211_msg_conveyor *cv,
+                        int (*cb_func)(struct nl_msg *, void *),
+                        void *cb_arg)
+{
        static struct nl80211_msg_conveyor rcv;
-       int err = 1;
+       int err;
 
        if (cb_func)
                nl_cb_set(cv->cb, NL_CB_VALID, NL_CB_CUSTOM, cb_func, cb_arg);
        else
                nl_cb_set(cv->cb, NL_CB_VALID, NL_CB_CUSTOM, nl80211_msg_response, &rcv);
 
-       if (nl_send_auto_complete(nls->nl_sock, cv->msg) < 0)
-               goto err;
+       err = nl_send_auto_complete(nls->nl_sock, cv->msg);
+
+       if (err < 0)
+               goto out;
+
+       err = 1;
 
        nl_cb_err(cv->cb,               NL_CB_CUSTOM, nl80211_msg_error,  &err);
        nl_cb_set(cv->cb, NL_CB_FINISH, NL_CB_CUSTOM, nl80211_msg_finish, &err);
@@ -391,13 +395,23 @@ static struct nl80211_msg_conveyor * nl80211_send(
        while (err > 0)
                nl_recvmsgs(nls->nl_sock, cv->cb);
 
-       return &rcv;
+out:
+       nl80211_free(cv);
+       return err;
+}
 
-err:
-       nl_cb_put(cv->cb);
-       nlmsg_free(cv->msg);
+static int nl80211_request(const char *ifname, int cmd, int flags,
+                           int (*cb_func)(struct nl_msg *, void *),
+                           void *cb_arg)
+{
+       struct nl80211_msg_conveyor *cv;
 
-       return NULL;
+       cv = nl80211_msg(ifname, cmd, flags);
+
+       if (!cv)
+               return -ENOMEM;
+
+       return nl80211_send(cv, cb_func, cb_arg);
 }
 
 static struct nlattr ** nl80211_parse(struct nl_msg *msg)
@@ -411,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)
 {
@@ -446,18 +484,24 @@ static int nl80211_subscribe(const char *family, const char *group)
 {
        struct nl80211_group_conveyor cv = { .name = group, .id = -ENOENT };
        struct nl80211_msg_conveyor *req;
+       int err;
 
        req = nl80211_ctl(CTRL_CMD_GETFAMILY, 0);
        if (req)
        {
                NLA_PUT_STRING(req->msg, CTRL_ATTR_FAMILY_NAME, family);
-               nl80211_send(req, nl80211_subscribe_cb, &cv);
+               err = nl80211_send(req, nl80211_subscribe_cb, &cv);
+
+               if (err)
+                       return err;
+
+               return nl_socket_add_membership(nls->nl_sock, cv.id);
 
 nla_put_failure:
                nl80211_free(req);
        }
 
-       return nl_socket_add_membership(nls->nl_sock, cv.id);
+       return -ENOMEM;
 }
 
 
@@ -466,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;
@@ -477,10 +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;
@@ -490,17 +537,28 @@ static int nl80211_wait(const char *family, const char *group, int cmd)
        if (!cb)
                return -ENOMEM;
 
+       nl_cb_err(cb,                  NL_CB_CUSTOM, nl80211_msg_error,      &err);
        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 );
 
-       while (!cv.recv)
+       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);
 
        nl_cb_put(cb);
 
-       return 0;
+       return err;
 }
 
+#define nl80211_wait(family, group, ...) \
+       __nl80211_wait(family, group, __VA_ARGS__, 0)
+
 
 static int nl80211_freq2channel(int freq)
 {
@@ -551,16 +609,11 @@ static int nl80211_ifname2phy_cb(struct nl_msg *msg, void *arg)
 static char * nl80211_ifname2phy(const char *ifname)
 {
        static char phy[32] = { 0 };
-       struct nl80211_msg_conveyor *req;
 
        memset(phy, 0, sizeof(phy));
 
-       req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
-       if (req)
-       {
-               nl80211_send(req, nl80211_ifname2phy_cb, phy);
-               nl80211_free(req);
-       }
+       nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
+                       nl80211_ifname2phy_cb, phy);
 
        return phy[0] ? phy : NULL;
 }
@@ -604,7 +657,7 @@ static char * nl80211_phy2ifname(const char *ifname)
                                            ((ifidx < 0) || (cifidx < ifidx)))
                                        {
                                                ifidx = cifidx;
-                                               strncpy(nif, e->d_name, sizeof(nif));
+                                               strncpy(nif, e->d_name, sizeof(nif) - 1);
                                        }
                                }
                        }
@@ -643,17 +696,13 @@ static int nl80211_get_mode_cb(struct nl_msg *msg, void *arg)
 static int nl80211_get_mode(const char *ifname, int *buf)
 {
        char *res;
-       struct nl80211_msg_conveyor *req;
 
-       res = nl80211_phy2ifname(ifname);
-       req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0);
        *buf = IWINFO_OPMODE_UNKNOWN;
 
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_mode_cb, buf);
-               nl80211_free(req);
-       }
+       res = nl80211_phy2ifname(ifname);
+
+       nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
+                       nl80211_get_mode_cb, buf);
 
        return (*buf == IWINFO_OPMODE_UNKNOWN) ? -1 : 0;
 }
@@ -893,7 +942,7 @@ static int __nl80211_wpactl_query(const char *ifname, ...)
 
 static char * nl80211_ifadd(const char *ifname)
 {
-       char *rv = NULL, path[PATH_MAX];
+       char path[PATH_MAX];
        static char nif[IFNAMSIZ] = { 0 };
        struct nl80211_msg_conveyor *req;
        FILE *sysfs;
@@ -917,18 +966,19 @@ static char * nl80211_ifadd(const char *ifname)
                        fclose(sysfs);
                }
 
-               rv = nif;
+               return nif;
 
        nla_put_failure:
                nl80211_free(req);
        }
 
-       return rv;
+       return NULL;
 }
 
 static void nl80211_ifdel(const char *ifname)
 {
        struct nl80211_msg_conveyor *req;
+       int err;
 
        req = nl80211_msg(ifname, NL80211_CMD_DEL_INTERFACE, 0);
        if (req)
@@ -936,6 +986,7 @@ static void nl80211_ifdel(const char *ifname)
                NLA_PUT_STRING(req->msg, NL80211_ATTR_IFNAME, ifname);
 
                nl80211_send(req, NULL, NULL);
+               return;
 
        nla_put_failure:
                nl80211_free(req);
@@ -951,7 +1002,7 @@ static void nl80211_hostapd_hup(const char *ifname)
        if (phy)
        {
                snprintf(buf, sizeof(buf), "/var/run/wifi-%s.pid", phy);
-               if ((fd = open(buf, O_RDONLY)) > 0)
+               if ((fd = open(buf, O_RDONLY)) >= 0)
                {
                        if (read(fd, buf, sizeof(buf)) > 0)
                                pid = atoi(buf);
@@ -1036,47 +1087,33 @@ static int nl80211_get_ssid_bssid_cb(struct nl_msg *msg, void *arg)
 static int nl80211_get_ssid(const char *ifname, char *buf)
 {
        char *res;
-       struct nl80211_msg_conveyor *req;
-       struct nl80211_ssid_bssid sb;
+       struct nl80211_ssid_bssid sb = { .ssid = (unsigned char *)buf };
 
        /* try to find ssid from scan dump results */
        res = nl80211_phy2ifname(ifname);
-       req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
-
-       sb.ssid = (unsigned char *)buf;
-       *buf = 0;
+       sb.ssid[0] = 0;
 
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_ssid_bssid_cb, &sb);
-               nl80211_free(req);
-       }
+       nl80211_request(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
+                       nl80211_get_ssid_bssid_cb, &sb);
 
        /* failed, try to find from hostapd info */
-       if (*buf == 0)
-               nl80211_hostapd_query(ifname, "ssid", buf, IWINFO_ESSID_MAX_SIZE + 1);
+       if (sb.ssid[0] == 0)
+               nl80211_hostapd_query(ifname, "ssid", sb.ssid,
+                                     IWINFO_ESSID_MAX_SIZE + 1);
 
-       return (*buf == 0) ? -1 : 0;
+       return (sb.ssid[0] == 0) ? -1 : 0;
 }
 
 static int nl80211_get_bssid(const char *ifname, char *buf)
 {
        char *res, bssid[sizeof("FF:FF:FF:FF:FF:FF\0")];
-       struct nl80211_msg_conveyor *req;
-       struct nl80211_ssid_bssid sb;
+       struct nl80211_ssid_bssid sb = { };
 
        /* try to find bssid from scan dump results */
        res = nl80211_phy2ifname(ifname);
-       req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
 
-       sb.ssid = NULL;
-       sb.bssid[0] = 0;
-
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_ssid_bssid_cb, &sb);
-               nl80211_free(req);
-       }
+       nl80211_request(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
+                       nl80211_get_ssid_bssid_cb, &sb);
 
        /* failed, try to find mac from hostapd info */
        if ((sb.bssid[0] == 0) &&
@@ -1140,18 +1177,13 @@ static int nl80211_get_frequency_info_cb(struct nl_msg *msg, void *arg)
 static int nl80211_get_frequency(const char *ifname, int *buf)
 {
        char *res, channel[4], hwmode[2];
-       struct nl80211_msg_conveyor *req;
 
        /* try to find frequency from interface info */
        res = nl80211_phy2ifname(ifname);
-       req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0);
        *buf = 0;
 
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_frequency_info_cb, buf);
-               nl80211_free(req);
-       }
+       nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
+                       nl80211_get_frequency_info_cb, buf);
 
        /* failed, try to find frequency from hostapd info */
        if ((*buf == 0) &&
@@ -1165,13 +1197,9 @@ static int nl80211_get_frequency(const char *ifname, int *buf)
        if (*buf == 0)
        {
                res = nl80211_phy2ifname(ifname);
-               req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
 
-               if (req)
-               {
-                       nl80211_send(req, nl80211_get_frequency_scan_cb, buf);
-                       nl80211_free(req);
-               }
+               nl80211_request(res ? res : ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
+                               nl80211_get_frequency_scan_cb, buf);
        }
 
        return (*buf == 0) ? -1 : 0;
@@ -1202,21 +1230,15 @@ static int nl80211_get_txpower_cb(struct nl_msg *msg, void *arg)
 static int nl80211_get_txpower(const char *ifname, int *buf)
 {
        char *res;
-       struct nl80211_msg_conveyor *req;
 
        res = nl80211_phy2ifname(ifname);
-       req = nl80211_msg(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0);
+       *buf = 0;
 
-       if (req)
-       {
-               *buf = 0;
-               nl80211_send(req, nl80211_get_txpower_cb, buf);
-               nl80211_free(req);
-               if (*buf)
-                       return 0;
-       }
+       if (nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
+                           nl80211_get_txpower_cb, buf))
+               return -1;
 
-       return -1;
+       return 0;
 }
 
 
@@ -1284,7 +1306,6 @@ static void nl80211_fill_signal(const char *ifname, struct nl80211_rssi_rate *r)
 {
        DIR *d;
        struct dirent *de;
-       struct nl80211_msg_conveyor *req;
 
        r->rssi = 0;
        r->rate = 0;
@@ -1297,14 +1318,8 @@ static void nl80211_fill_signal(const char *ifname, struct nl80211_rssi_rate *r)
                            (!de->d_name[strlen(ifname)] ||
                             !strncmp(&de->d_name[strlen(ifname)], ".sta", 4)))
                        {
-                               req = nl80211_msg(de->d_name, NL80211_CMD_GET_STATION,
-                                                 NLM_F_DUMP);
-
-                               if (req)
-                               {
-                                       nl80211_send(req, nl80211_fill_signal_cb, r);
-                                       nl80211_free(req);
-                               }
+                               nl80211_request(de->d_name, NL80211_CMD_GET_STATION,
+                                               NLM_F_DUMP, nl80211_fill_signal_cb, r);
                        }
                }
 
@@ -1372,24 +1387,17 @@ static int nl80211_get_noise_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_noise(const char *ifname, int *buf)
 {
-       int8_t noise;
-       struct nl80211_msg_conveyor *req;
+       int8_t noise = 0;
 
-       req = nl80211_msg(ifname, NL80211_CMD_GET_SURVEY, NLM_F_DUMP);
-       if (req)
-       {
-               noise = 0;
-
-               nl80211_send(req, nl80211_get_noise_cb, &noise);
-               nl80211_free(req);
+       if (nl80211_request(ifname, NL80211_CMD_GET_SURVEY, NLM_F_DUMP,
+                           nl80211_get_noise_cb, &noise))
+               goto out;
 
-               if (noise)
-               {
-                       *buf = noise;
-                       return 0;
-               }
-       }
+       *buf = noise;
+       return 0;
 
+out:
+       *buf = 0;
        return -1;
 }
 
@@ -1628,6 +1636,48 @@ static int nl80211_get_phyname(const char *ifname, char *buf)
 }
 
 
+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_assoclist_cb(struct nl_msg *msg, void *arg)
 {
        struct nl80211_array_buf *arr = arg;
@@ -1651,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] = {
@@ -1686,38 +1737,12 @@ static int nl80211_get_assoclist_cb(struct nl_msg *msg, void *arg)
                if (sinfo[NL80211_STA_INFO_RX_BITRATE] &&
                    !nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
                                      sinfo[NL80211_STA_INFO_RX_BITRATE], rate_policy))
-               {
-                       if (rinfo[NL80211_RATE_INFO_BITRATE])
-                               e->rx_rate.rate =
-                                       nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100;
-
-                       if (rinfo[NL80211_RATE_INFO_MCS])
-                               e->rx_rate.mcs = nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]);
-
-                       if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
-                               e->rx_rate.is_40mhz = 1;
-
-                       if (rinfo[NL80211_RATE_INFO_SHORT_GI])
-                               e->rx_rate.is_short_gi = 1;
-               }
+                       nl80211_parse_rateinfo(rinfo, &e->rx_rate);
 
                if (sinfo[NL80211_STA_INFO_TX_BITRATE] &&
                    !nla_parse_nested(rinfo, NL80211_RATE_INFO_MAX,
                                      sinfo[NL80211_STA_INFO_TX_BITRATE], rate_policy))
-               {
-                       if (rinfo[NL80211_RATE_INFO_BITRATE])
-                               e->tx_rate.rate =
-                                       nla_get_u16(rinfo[NL80211_RATE_INFO_BITRATE]) * 100;
-
-                       if (rinfo[NL80211_RATE_INFO_MCS])
-                               e->tx_rate.mcs = nla_get_u8(rinfo[NL80211_RATE_INFO_MCS]);
-
-                       if (rinfo[NL80211_RATE_INFO_40_MHZ_WIDTH])
-                               e->tx_rate.is_40mhz = 1;
-
-                       if (rinfo[NL80211_RATE_INFO_SHORT_GI])
-                               e->tx_rate.is_short_gi = 1;
-               }
+                       nl80211_parse_rateinfo(rinfo, &e->tx_rate);
 
                if (sinfo[NL80211_STA_INFO_RX_BYTES])
                        e->rx_bytes = nla_get_u32(sinfo[NL80211_STA_INFO_RX_BYTES]);
@@ -1734,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])
                {
@@ -1777,7 +1805,6 @@ static int nl80211_get_assoclist(const char *ifname, char *buf, int *len)
        DIR *d;
        int i, noise = 0;
        struct dirent *de;
-       struct nl80211_msg_conveyor *req;
        struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
        struct iwinfo_assoclist_entry *e;
 
@@ -1789,14 +1816,8 @@ static int nl80211_get_assoclist(const char *ifname, char *buf, int *len)
                            (!de->d_name[strlen(ifname)] ||
                             !strncmp(&de->d_name[strlen(ifname)], ".sta", 4)))
                        {
-                               req = nl80211_msg(de->d_name, NL80211_CMD_GET_STATION,
-                                                 NLM_F_DUMP);
-
-                               if (req)
-                               {
-                                       nl80211_send(req, nl80211_get_assoclist_cb, &arr);
-                                       nl80211_free(req);
-                               }
+                               nl80211_request(de->d_name, NL80211_CMD_GET_STATION,
+                                               NLM_F_DUMP, nl80211_get_assoclist_cb, &arr);
                        }
                }
 
@@ -1864,7 +1885,7 @@ static int nl80211_get_txpwrlist_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_txpwrlist(const char *ifname, char *buf, int *len)
 {
-       int ch_cur;
+       int err, ch_cur;
        int dbm_max = -1, dbm_cur, dbm_cnt;
        struct nl80211_msg_conveyor *req;
        struct iwinfo_txpwrlist_entry entry;
@@ -1872,17 +1893,13 @@ static int nl80211_get_txpwrlist(const char *ifname, char *buf, int *len)
        if (nl80211_get_channel(ifname, &ch_cur))
                ch_cur = 0;
 
-       req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
-       if (req)
-       {
-               /* initialize the value pointer with channel for callback */
-               dbm_max = ch_cur;
+       /* initialize the value pointer with channel for callback */
+       dbm_max = ch_cur;
 
-               nl80211_send(req, nl80211_get_txpwrlist_cb, &dbm_max);
-               nl80211_free(req);
-       }
+       err = nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
+                             nl80211_get_txpwrlist_cb, &dbm_max);
 
-       if (dbm_max > 0)
+       if (!err)
        {
                for (dbm_cur = 0, dbm_cnt = 0;
                     dbm_cur < dbm_max;
@@ -2085,27 +2102,25 @@ static int nl80211_get_scanlist_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_scanlist_nl(const char *ifname, char *buf, int *len)
 {
-       struct nl80211_msg_conveyor *req;
        struct nl80211_scanlist sl = { .e = (struct iwinfo_scanlist_entry *)buf };
 
-       req = nl80211_msg(ifname, NL80211_CMD_TRIGGER_SCAN, 0);
-       if (req)
-       {
-               nl80211_send(req, NULL, NULL);
-               nl80211_free(req);
-       }
+       if (nl80211_request(ifname, NL80211_CMD_TRIGGER_SCAN, 0, NULL, NULL))
+               goto out;
 
-       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;
 
-       req = nl80211_msg(ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP);
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_scanlist_cb, &sl);
-               nl80211_free(req);
-       }
+       if (nl80211_request(ifname, NL80211_CMD_GET_SCAN, NLM_F_DUMP,
+                           nl80211_get_scanlist_cb, &sl))
+               goto out;
 
        *len = sl.len * sizeof(struct iwinfo_scanlist_entry);
-       return *len ? 0 : -1;
+       return 0;
+
+out:
+       *len = 0;
+       return -1;
 }
 
 static int wpasupp_ssid_decode(const char *in, char *out, int outlen)
@@ -2211,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);
@@ -2290,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);
@@ -2393,37 +2413,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++;
+                               }
+                       }
                }
        }
 
@@ -2432,22 +2472,27 @@ 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 *req;
+       struct nl80211_msg_conveyor *cv;
        struct nl80211_array_buf arr = { .buf = buf, .count = 0 };
+       uint32_t features = nl80211_get_protocol_features(ifname);
+       int flags;
 
-       req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_freqlist_cb, &arr);
-               nl80211_free(req);
-       }
+       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;
 
-       if (arr.count > 0)
-       {
-               *len = arr.count * sizeof(struct iwinfo_freqlist_entry);
-               return 0;
-       }
+       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;
 }
 
@@ -2466,20 +2511,11 @@ static int nl80211_get_country_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_country(const char *ifname, char *buf)
 {
-       int rv = -1;
-       struct nl80211_msg_conveyor *req;
-
-       req = nl80211_msg(ifname, NL80211_CMD_GET_REG, 0);
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_country_cb, buf);
-               nl80211_free(req);
-
-               if (buf[0])
-                       rv = 0;
-       }
+       if (nl80211_request(ifname, NL80211_CMD_GET_REG, 0,
+                           nl80211_get_country_cb, buf))
+               return -1;
 
-       return rv;
+       return 0;
 }
 
 static int nl80211_get_countrylist(const char *ifname, char *buf, int *len)
@@ -2589,43 +2625,39 @@ static int nl80211_get_modelist_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_hwmodelist(const char *ifname, int *buf)
 {
-       struct nl80211_msg_conveyor *req;
        struct nl80211_modes m = { 0 };
 
-       req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_modelist_cb, &m);
-               nl80211_free(req);
-       }
+       if (nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
+                           nl80211_get_modelist_cb, &m))
+               goto out;
 
-       if (m.ok)
-       {
-               *buf = m.hw;
-               return 0;
-       }
+       if (!m.ok)
+               goto out;
+
+       *buf = m.hw;
+       return 0;
 
+out:
+       *buf = 0;
        return -1;
 }
 
 static int nl80211_get_htmodelist(const char *ifname, int *buf)
 {
-       struct nl80211_msg_conveyor *req;
        struct nl80211_modes m = { 0 };
 
-       req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
-       if (req)
-       {
-               nl80211_send(req, nl80211_get_modelist_cb, &m);
-               nl80211_free(req);
-       }
+       if (nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
+                           nl80211_get_modelist_cb, &m))
+               goto out;
 
-       if (m.ok)
-       {
-               *buf = m.ht;
-               return 0;
-       }
+       if (!m.ok)
+               goto out;
 
+       *buf = m.ht;
+       return 0;
+
+out:
+       *buf = 0;
        return -1;
 }
 
@@ -2685,20 +2717,16 @@ static int nl80211_get_ifcomb_cb(struct nl_msg *msg, void *arg)
 
 static int nl80211_get_mbssid_support(const char *ifname, int *buf)
 {
-       struct nl80211_msg_conveyor *req;
-
-       req = nl80211_msg(ifname, NL80211_CMD_GET_WIPHY, 0);
-       if (!req)
+       if (nl80211_request(ifname, NL80211_CMD_GET_WIPHY, 0,
+                           nl80211_get_ifcomb_cb, buf))
                return -1;
 
-       nl80211_send(req, nl80211_get_ifcomb_cb, buf);
-       nl80211_free(req);
        return 0;
 }
 
 static int nl80211_get_hardware_id(const char *ifname, char *buf)
 {
-       int rv;
+       int rv = -1;
        char *res;
 
        /* Got a radioX pseudo interface, find some interface on it or create one */