#include <glob.h>
#include <fnmatch.h>
#include <stdarg.h>
+#include <stdlib.h>
#include "iwinfo_nl80211.h"
static int nl80211_phy_idx_from_uci_path(struct uci_section *s)
{
- const char *opt;
- char buf[128];
+ size_t linklen, pathlen;
+ char buf[128], *link;
+ struct dirent *e;
+ const char *path;
int idx = -1;
- glob_t gl;
+ DIR *d;
- opt = uci_lookup_option_string(uci_ctx, s, "path");
- if (!opt)
+ path = uci_lookup_option_string(uci_ctx, s, "path");
+ if (!path)
return -1;
- 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 ((d = opendir("/sys/class/ieee80211")) != NULL)
+ {
+ while ((e = readdir(d)) != NULL)
+ {
+ snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/device", e->d_name);
- if (glob(buf, 0, NULL, &gl))
- return -1;
+ link = realpath(buf, NULL);
- if (gl.gl_pathc > 0)
- idx = nl80211_readint(gl.gl_pathv[0]);
+ if (link == NULL)
+ continue;
- globfree(&gl);
+ linklen = strlen(link);
+ pathlen = strlen(path);
+
+ if (pathlen >= linklen || strcmp(link + (linklen - pathlen), path))
+ linklen = 0;
+
+ free(link);
+
+ if (linklen == 0)
+ continue;
+
+ snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", e->d_name);
+
+ idx = nl80211_readint(buf);
+
+ if (idx >= 0)
+ break;
+ }
+
+ closedir(d);
+ }
return idx;
}
phyidx = atoi(&ifname[3]);
else if (!strncmp(ifname, "radio", 5))
phyidx = nl80211_phy_idx_from_uci(ifname);
- else if (!strncmp(ifname, "mon.", 4))
+
+ if (!strncmp(ifname, "mon.", 4))
ifidx = if_nametoindex(&ifname[4]);
else
ifidx = if_nametoindex(ifname);
if (!cv)
return NULL;
- if (ifidx > -1)
+ if (ifidx > 0)
NLA_PUT_U32(cv->msg, NL80211_ATTR_IFINDEX, ifidx);
-
- if (phyidx > -1)
+ else if (phyidx > -1)
NLA_PUT_U32(cv->msg, NL80211_ATTR_WIPHY, phyidx);
return cv;
if (nl80211_get_mode(ifname, &mode))
return 0;
- if (mode != IWINFO_OPMODE_CLIENT && mode != IWINFO_OPMODE_ADHOC)
+ if (mode != IWINFO_OPMODE_CLIENT &&
+ mode != IWINFO_OPMODE_ADHOC &&
+ mode != IWINFO_OPMODE_MESHPOINT)
return 0;
sock = nl80211_wpactl_connect(ifname, &local);
return (*buf == 0) ? -1 : 0;
}
+static int nl80211_get_center_freq1_cb(struct nl_msg *msg, void *arg)
+{
+ int *freq = arg;
+ struct nlattr **tb = nl80211_parse(msg);
+
+ if (tb[NL80211_ATTR_CENTER_FREQ1])
+ *freq = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+
+ return NL_SKIP;
+}
+
+static int nl80211_get_center_freq1(const char *ifname, int *buf)
+{
+ char *res;
+
+ /* try to find frequency from interface info */
+ res = nl80211_phy2ifname(ifname);
+ *buf = 0;
+
+ nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
+ nl80211_get_center_freq1_cb, buf);
+
+ return (*buf == 0) ? -1 : 0;
+}
+
+static int nl80211_get_center_freq2_cb(struct nl_msg *msg, void *arg)
+{
+ int *freq = arg;
+ struct nlattr **tb = nl80211_parse(msg);
+
+ if (tb[NL80211_ATTR_CENTER_FREQ2])
+ *freq = nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+ return NL_SKIP;
+}
+
+static int nl80211_get_center_freq2(const char *ifname, int *buf)
+{
+ char *res;
+
+ /* try to find frequency from interface info */
+ res = nl80211_phy2ifname(ifname);
+ *buf = 0;
+
+ nl80211_request(res ? res : ifname, NL80211_CMD_GET_INTERFACE, 0,
+ nl80211_get_center_freq2_cb, buf);
+
+ return (*buf == 0) ? -1 : 0;
+}
+
static int nl80211_get_channel(const char *ifname, int *buf)
{
if (!nl80211_get_frequency(ifname, buf))
return -1;
}
+static int nl80211_get_center_chan1(const char *ifname, int *buf)
+{
+ if (!nl80211_get_center_freq1(ifname, buf))
+ {
+ *buf = nl80211_freq2channel(*buf);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int nl80211_get_center_chan2(const char *ifname, int *buf)
+{
+ if (!nl80211_get_center_freq2(ifname, buf))
+ {
+ *buf = nl80211_freq2channel(*buf);
+ return 0;
+ }
+
+ return -1;
+}
+
static int nl80211_get_txpower_cb(struct nl_msg *msg, void *arg)
{
int *buf = arg;
static int nl80211_get_encryption(const char *ifname, char *buf)
{
char *p;
+ int opmode;
uint8_t wpa_version = 0;
char wpa[2], wpa_key_mgmt[64], wpa_pairwise[16], wpa_groupwise[16];
char auth_algs[2], wep_key0[27], wep_key1[27], wep_key2[27], wep_key3[27];
+ char mode[16];
struct iwinfo_crypto_entry *c = (struct iwinfo_crypto_entry *)buf;
if (nl80211_wpactl_query(ifname,
"pairwise_cipher", wpa_pairwise, sizeof(wpa_pairwise),
"group_cipher", wpa_groupwise, sizeof(wpa_groupwise),
- "key_mgmt", wpa_key_mgmt, sizeof(wpa_key_mgmt)))
+ "key_mgmt", wpa_key_mgmt, sizeof(wpa_key_mgmt),
+ "mode", mode, sizeof(mode)))
{
/* WEP or Open */
if (!strcmp(wpa_key_mgmt, "NONE"))
}
}
+ /* MESH with SAE */
+ else if (!strcmp(mode, "mesh") && !strcmp(wpa_key_mgmt, "UNKNOWN"))
+ {
+ c->enabled = 1;
+ c->wpa_version = 4;
+ c->auth_suites = IWINFO_KMGMT_SAE;
+ c->pair_ciphers = IWINFO_CIPHER_CCMP;
+ c->group_ciphers = IWINFO_CIPHER_CCMP;
+ }
+
/* WPA */
else
{
return 0;
}
+ /* Ad-Hoc or Mesh interfaces without wpa_supplicant are open */
+ else if (!nl80211_get_mode(ifname, &opmode) &&
+ (opmode == IWINFO_OPMODE_ADHOC ||
+ opmode == IWINFO_OPMODE_MESHPOINT))
+ {
+ c->enabled = 0;
+
+ return 0;
+ }
+
+
return -1;
}
static void nl80211_get_scancrypto(char *spec, struct iwinfo_crypto_entry *c)
{
int wpa_version = 0;
- char *p, *proto, *suites;
+ char *p, *q, *proto, *suites;
c->enabled = 0;
- for (p = strtok(spec, "[]"); p != NULL; p = strtok(NULL, "[]")) {
+ for (p = strtok_r(spec, "[]", &q); p; p = strtok_r(NULL, "[]", &q)) {
if (!strcmp(p, "WEP")) {
c->enabled = 1;
c->auth_suites = IWINFO_KMGMT_NONE;
iwinfo_parse_rsn(&e->crypto, ie + 6, ie[1] - 4,
IWINFO_CIPHER_TKIP, IWINFO_KMGMT_PSK);
break;
+ case 61: /* HT oeration */
+ e->ht_chan_info.primary_chan = ie[2];
+ e->ht_chan_info.secondary_chan_off = ie[3] & 0x3;
+ e->ht_chan_info.chan_width = (ie[4] & 0x4)>>2;
+ break;
+ case 192: /* VHT operation */
+ e->vht_chan_info.chan_width = ie[2];
+ e->vht_chan_info.center_chan_1 = ie[3];
+ e->vht_chan_info.center_chan_2 = ie[4];
+ break;
}
ielen -= ie[1] + 2;
tries--;
}
- /* got a failure reply */
+ /* scanning already in progress, keep awaiting results */
else if (!strcmp(reply, "FAIL-BUSY\n"))
+ {
+ tries--;
+ }
+
+ /* another failure, abort */
+ else if (!strncmp(reply, "FAIL-", 5))
{
break;
}
flags = strtok(NULL, "\t");
ssid = strtok(NULL, "\n");
- if (!bssid || !freq || !signal || !flags || !ssid)
+ if (!bssid || !freq || !signal || !flags)
continue;
/* BSSID */
e->mac[5] = strtol(&bssid[15], NULL, 16);
/* SSID */
- wpasupp_ssid_decode(ssid, e->ssid, sizeof(e->ssid));
+ if (ssid)
+ wpasupp_ssid_decode(ssid, e->ssid, sizeof(e->ssid));
+ else
+ e->ssid[0] = 0;
/* Mode */
if (strstr(flags, "[MESH]"))
return -1;
}
+struct chan_info {
+ int width;
+ int mode;
+};
+
+static int nl80211_get_htmode_cb(struct nl_msg *msg, void *arg)
+{
+ struct nlattr **tb = nl80211_parse(msg);
+ struct nlattr *cur;
+ struct chan_info *chn = arg;
+
+ if ((cur = tb[NL80211_ATTR_CHANNEL_WIDTH]))
+ chn->width = nla_get_u32(cur);
+
+ if ((cur = tb[NL80211_ATTR_BSS_HT_OPMODE]))
+ chn->mode = nla_get_u32(cur);
+
+ return NL_SKIP;
+}
+
+static int nl80211_get_htmode(const char *ifname, int *buf)
+{
+ struct chan_info chn = { .width = 0, .mode = 0 };
+ char *res;
+ int err;
+
+ res = nl80211_phy2ifname(ifname);
+ *buf = 0;
+
+ err = nl80211_request(res ? res : ifname,
+ NL80211_CMD_GET_INTERFACE, 0,
+ nl80211_get_htmode_cb, &chn);
+ if (err)
+ return -1;
+
+ switch (chn.width) {
+ case NL80211_CHAN_WIDTH_20:
+ if (chn.mode == -1)
+ *buf = IWINFO_HTMODE_VHT20;
+ else
+ *buf = IWINFO_HTMODE_HT20;
+ break;
+ case NL80211_CHAN_WIDTH_40:
+ if (chn.mode == -1)
+ *buf = IWINFO_HTMODE_VHT40;
+ else
+ *buf = IWINFO_HTMODE_HT40;
+ break;
+ case NL80211_CHAN_WIDTH_80:
+ *buf = IWINFO_HTMODE_VHT80;
+ break;
+ case NL80211_CHAN_WIDTH_80P80:
+ *buf = IWINFO_HTMODE_VHT80_80;
+ break;
+ case NL80211_CHAN_WIDTH_160:
+ *buf = IWINFO_HTMODE_VHT160;
+ break;
+ case NL80211_CHAN_WIDTH_5:
+ case NL80211_CHAN_WIDTH_10:
+ case NL80211_CHAN_WIDTH_20_NOHT:
+ *buf = IWINFO_HTMODE_NOHT;
+ break;
+ default:
+ return -1;
+ }
+
+ return 0;
+}
+
static int nl80211_get_htmodelist(const char *ifname, int *buf)
{
struct nl80211_modes m = { 0 };
return 0;
}
+static int nl80211_hardware_id_from_fdt(struct iwinfo_hardware_id *id, const char *ifname)
+{
+ char *phy, compat[64], path[PATH_MAX];
+ int i;
+
+ /* Try to determine the phy name from the given interface */
+ phy = nl80211_ifname2phy(ifname);
+
+ snprintf(path, sizeof(path), "/sys/class/%s/%s/device/of_node/compatible",
+ phy ? "ieee80211" : "net", phy ? phy : ifname);
+
+ if (nl80211_readstr(path, compat, sizeof(compat)) <= 0)
+ return -1;
+
+ if (!strcmp(compat, "qcom,ipq4019-wifi")) {
+ id->vendor_id = 0x168c;
+ id->device_id = 0x003c;
+ id->subsystem_vendor_id = 0x168c;
+ id->subsystem_device_id = 0x4019;
+ }
+
+ return (id->vendor_id && id->device_id) ? 0 : -1;
+}
+
+
static int nl80211_get_hardware_id(const char *ifname, char *buf)
{
struct iwinfo_hardware_id *id = (struct iwinfo_hardware_id *)buf;
*lookup[i].dest = strtoul(num, NULL, 16);
}
+ /* Failed to obtain hardware IDs, try FDT */
+ if (id->vendor_id == 0 || id->device_id == 0)
+ if (!nl80211_hardware_id_from_fdt(id, ifname))
+ return 0;
+
/* Failed to obtain hardware IDs, search board config */
if (id->vendor_id == 0 || id->device_id == 0)
return iwinfo_hardware_id_from_mtd(id);
.mbssid_support = nl80211_get_mbssid_support,
.hwmodelist = nl80211_get_hwmodelist,
.htmodelist = nl80211_get_htmodelist,
+ .htmode = nl80211_get_htmode,
.mode = nl80211_get_mode,
.ssid = nl80211_get_ssid,
.bssid = nl80211_get_bssid,
.countrylist = nl80211_get_countrylist,
.survey = nl80211_get_survey,
.lookup_phy = nl80211_lookup_phyname,
- .close = nl80211_close
+ .close = nl80211_close,
+ .center_chan1 = nl80211_get_center_chan1,
+ .center_chan2 = nl80211_get_center_chan2
};