Files
wifimngr/wifimngr.c
2025-12-15 10:06:25 +01:00

6503 lines
182 KiB
C

/* SPDX-License-Identifier: GPL-2.0-only */
/*
* wifimngr.c - provides "wifi" object for management and control.
*
* Copyright (C) 2019-2024 Iopsys Software Solutions AB. All rights reserved.
* Copyright (C) 2025 Genexis AB.
*
* Author: Anjan Chanda <anjan.chanda@genexis.eu>
*/
#include <stdio.h>
#include <stdlib.h>
#include <dirent.h>
#include <limits.h>
#include <net/if.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <time.h>
#include <json-c/json.h>
#include <libubox/blobmsg.h>
#include <libubox/blobmsg_json.h>
#include <libubox/uloop.h>
#include <libubox/ustream.h>
#include <libubox/utils.h>
#include <libubus.h>
#include <easy/easy.h>
#include <wifi.h>
#include "wifimngr.h"
#include "version.h"
#include "help.h"
#include "debug.h"
#include "policy.c"
char *helpbuf = NULL;
size_t helpbuflen;
#ifndef BIT
#define BIT(x) (1 << (x))
#endif
static int signal_pending;
static void wifimngr_sighandler(int sig)
{
signal_pending = sig;
}
void *libsta_ratings;
float (*sta_ratings_calc)(void *libctx, const char *ap_ifname, struct wifi_sta *sta);
void (*sta_ratings_free_lib)(void *libctx);
/* if IF_OPER_* not defined */
#ifndef IF_OPER_UNKNOWN
enum {
IF_OPER_UNKNOWN,
IF_OPER_NOTPRESENT,
IF_OPER_DOWN,
IF_OPER_LOWERLAYERDOWN,
IF_OPER_TESTING,
IF_OPER_DORMANT,
IF_OPER_UP,
};
#endif /* IF_OPER_UNKNOWN */
const char *standard_str[] = {
"b",
"g",
"a",
"n",
"ac",
"ax",
"be",
"unknown"
};
const char *guard_interval_str[] = {
"400ns",
"800ns",
"1xLTF_800ns",
"1xLTF_1600ns",
"2xLTF_800ns",
"2xLTF_1600ns",
"4xLTF_800ns",
"4xLTF_3200ns",
"Auto"
};
#if 0 // TODO: new
const char *auth_str[] = {
"none",
"wep",
"psk",
"psk2",
"wpa",
"wpa2",
"unknown"
};
const char *cipher_str[] = {
"cipher_grp",
"wep40",
"tkip",
"",
"ccmp",
"wep104",
"cmac",
"cipher_nogrp",
"gcmp",
"gcmp256",
"ccmp256",
"gmac",
"gmac256",
"cmac256",
"unknown"
};
#else
/* deprecate */
const char *auth_str[] = {"OPEN", "SHARED", "WEPAUTO", "WPANONE", "WPA1", \
"WPAPSK", "WPA2", "WPA2PSK", "Unknown"};
const char *cipher_str[] = {"NONE", "WEP", "TKIP", "AES", "CCMP256", \
"GCMP128", "GCMP256", "Unknown"};
#endif
const char *wifi_secstr[] = {
"NONE",
"WEP64",
"WEP128",
"WPAPSK",
"WPA2PSK",
"WPA2PSK+FT",
"WPA3PSK",
"WPA2PSK+WPA3PSK",
"WPA3PSK+FT",
"WPA",
"WPA2",
"WPA3",
};
const char *wifi_cipherstr[] = {
"CIPHER_GRP",
"WEP40",
"TKIP",
"",
"CCMP",
"WEP104",
"CMAC",
"CIPHER_NOGRP",
"GCMP",
"GCMP256",
"CCMP256",
"GMAC",
"GMAC256",
"CMAC256",
};
const char *akm_str[] = {
"none",
"wpa",
"psk",
"ft-wpa",
"ft-psk",
"wpa-sha256",
"psk-sha256",
"tdls",
"sae",
"ft-sae",
"ap-peerkey",
"suiteB",
"suiteB-gmac",
"suiteB-gmac256",
"suiteB-cmac256",
"unknown",
};
const char *operstate_str[] = {
"up",
"down",
"unknown",
"dormant",
"notpresent",
"lowerdown",
"error",
};
static uint32_t bw_value(enum wifi_bw bw)
{
switch (bw) {
case BW20: return 20;
case BW40: return 40;
case BW80: return 80;
case BW160: return 160;
case BW320: return 320;
case BW8080: return 8080;
case BW_AUTO:
case BW_UNKNOWN:
default:
return 0;
}
}
static enum wifi_band band_to_radio_band(uint8_t band)
{
switch (band) {
case 2:
return BAND_2;
case 5:
return BAND_5;
case 6:
return BAND_6;
default:
break;
}
return BAND_UNKNOWN;
}
static const char *radio_band_to_band(enum wifi_band band)
{
switch (band) {
case BAND_2:
return "2g";
case BAND_5:
return "5g";
case BAND_6:
return "6g";
case BAND_ANY:
return "any";
default:
break;
}
return "unknown";
}
static const char *radio_band_to_band_ghz(enum wifi_band band)
{
switch (band) {
case BAND_2:
return "2.4GHz";
case BAND_5:
return "5GHz";
case BAND_6:
return "6GHz";
case BAND_60:
return "60GHz";
default:
break;
}
return "unknown";
}
static int wifimngr_extract_help(void)
{
size_t helpstrlen = strlen(helpstr);
size_t hlen = helpstrlen * 3 / 4 + 2;
uint8_t *buf;
int ret = 0;
buf = calloc(1, hlen);
if (!buf)
return -1;
ret = base64_decode((const uint8_t *)helpstr, helpstrlen, buf, &hlen);
if (ret) {
wifimngr_dbg("failed to extract help\n");
free(buf);
return -1;
}
helpbuf = (char *)buf;
helpbuflen = hlen;
return 0;
}
static void wifimngr_free_help()
{
free(helpbuf);
helpbuf = NULL;
helpbuflen = 0;
}
int wl_help_command(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg, const char *command)
{
const struct blobmsg_policy attr[1] = {
[0] = { .name = command, .type = BLOBMSG_TYPE_ARRAY },
};
struct blob_buf bb = {0}, bo = {0};
struct blob_attr *tb[1];
int ret = 0;
blob_buf_init(&bb, 0);
blob_buf_init(&bo, 0);
if (!helpbuf || !blobmsg_add_json_from_string(&bb, helpbuf)) {
wifimngr_dbg("failed to load help from string\n");
goto err;
}
ret = blobmsg_parse(attr, 1, tb, blobmsg_data(bb.head), blobmsg_len(bb.head));
if (!ret && tb[0]) {
blobmsg_add_blob(&bo, tb[0]);
ubus_send_reply(ctx, req, bo.head);
}
err:
blob_buf_free(&bb);
blob_buf_free(&bo);
return ret;
}
static const char *ifstatus_str(ifstatus_t f)
{
const char *s;
if (f & IFF_UP) {
s = (f & IFF_RUNNING) ? "running" : "up";
} else {
s = "down";
}
return s;
}
static char *wifi_security_str(uint32_t sec, char *out, size_t size)
{
char x[128] = {0};
bool sep = false;
int i, len;
len = sizeof(wifi_secstr)/sizeof(wifi_secstr[0]);
for (i = 0; i < len; i++) {
if (!!(sec & (1 << i)) && strlen(wifi_secstr[i])) {
snprintf(x + strlen(x), 127, "%s%s", sep ? "/" : "",
wifi_secstr[i]);
sep = true;
}
}
strncpy(out, x, size);
out[size - 1] = '\0';
return out;
}
static char *etostr(uint32_t e, char *out, size_t sizeof_out, int elen, const char **arr)
{
char *c = "";
int i;
for (i = 0; i < elen; i++) {
if (e & (1 << i)) {
snprintf(out + strlen(out), sizeof_out - 1, "%s%s", c, arr[i]);
c = "/";
}
}
return out;
}
struct wifimngr_device *wifimngr_ifname_to_device(struct wifimngr *w, const char *ifname)
{
const char *device = NULL;
int i;
for (i = 0; i < w->num_wifi_iface; i++) {
if (!strncmp(w->ifs[i].iface, ifname, strlen(ifname))) {
device = w->ifs[i].device;
break;
}
}
if (i == w->num_wifi_iface)
return NULL;
for (i = 0; i < w->num_wifi_device; i++) {
if (!strncmp(w->wdev[i].device, device, strlen(device)))
return &w->wdev[i];
}
return NULL;
}
const char *ubus_objname_to_ifname(struct ubus_object *obj)
{
if (strstr(obj->name, WIFI_RADIO_OBJECT_PREFIX))
return obj->name + strlen(WIFI_RADIO_OBJECT_PREFIX);
if (strstr(obj->name, WIFI_APMLD_OBJECT_PREFIX))
return obj->name + strlen(WIFI_APMLD_OBJECT_PREFIX);
if (strstr(obj->name, WIFI_BSTAMLD_OBJECT_PREFIX))
return obj->name + strlen(WIFI_BSTAMLD_OBJECT_PREFIX);
if (strstr(obj->name, WIFI_AP_OBJECT_PREFIX))
return obj->name + strlen(WIFI_AP_OBJECT_PREFIX);
if (strstr(obj->name, WIFI_BSTA_OBJECT_PREFIX))
return obj->name + strlen(WIFI_BSTA_OBJECT_PREFIX);
return "\0";
}
#define ubus_ap_to_ifname(o) ubus_objname_to_ifname(o)
#define ubus_sta_to_ifname(o) ubus_objname_to_ifname(o)
static int ieee80211_readint(const char *path)
{
int fd;
int rv = -1;
char buffer[16];
if ((fd = open(path, O_RDONLY)) > -1)
{
if (read(fd, buffer, sizeof(buffer)) > 0)
rv = atoi(buffer);
close(fd);
}
return rv;
}
static const char *ieee80211_phy_path_str(const char *phyname)
{
static char path[PATH_MAX];
const char *prefix = "/sys/devices/";
int prefix_len = strlen(prefix);
int buf_len, offset;
struct dirent *e;
char buf[512], *link;
int phy_idx;
int seq = 0;
DIR *d;
snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", phyname);
phy_idx = ieee80211_readint(buf);
if (phy_idx < 0)
return NULL;
buf_len = snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/device", phyname);
link = realpath(buf, path);
if (!link)
return NULL;
if (strncmp(link, prefix, prefix_len) != 0)
return NULL;
link += prefix_len;
prefix = "platform/";
prefix_len = strlen(prefix);
if (!strncmp(link, prefix, prefix_len) && strstr(link, "/pci"))
link += prefix_len;
snprintf(buf + buf_len, sizeof(buf) - buf_len, "/ieee80211");
d = opendir(buf);
if (!d)
return link;
while ((e = readdir(d)) != NULL) {
int cur_idx;
snprintf(buf, sizeof(buf), "/sys/class/ieee80211/%s/index", e->d_name);
cur_idx = ieee80211_readint(buf);
if (cur_idx < 0)
continue;
if (cur_idx >= phy_idx)
continue;
seq++;
}
closedir(d);
if (!seq)
return link;
offset = link - path + strlen(link);
snprintf(path + offset, sizeof(path) - offset, "+%d", seq);
return link;
}
int find_phy_from_device_path(char *path, char *phy, size_t size)
{
bool found = false;
struct dirent *p;
DIR *d;
d = opendir("/sys/class/ieee80211");
if (!d)
return -1;
while ((p = readdir(d))) {
const char *phypath;
if (!strcmp(p->d_name, "."))
continue;
if (!strcmp(p->d_name, ".."))
continue;
phypath = ieee80211_phy_path_str(p->d_name);
if (!phypath)
continue;
wifimngr_dbg("%s called |%s| vs |%s|\n", __func__, phypath, path);
if (!strcmp(phypath, path)) {
strncpy(phy, p->d_name, size);
found = true;
}
if (found)
break;
}
closedir(d);
return found ? 0 : -1;
}
bool phy_dir_exist(char *phy)
{
bool found = false;
struct dirent *p;
DIR *d;
d = opendir("/sys/class/ieee80211");
if (!d)
return found;
while ((p = readdir(d))) {
if (!strcmp(p->d_name, "."))
continue;
if (!strcmp(p->d_name, ".."))
continue;
if (!strcmp(phy, p->d_name)) {
found = true;
break;
}
}
closedir(d);
return found;
}
int wifimngr_get_wifi_devices(struct wifimngr *w, const char *conffile)
{
memset(w->wdev, 0, WIFI_DEV_MAX_NUM * sizeof(struct wifimngr_device));
w->num_wifi_device = 0;
return uci_get_wifi_devices(w, conffile);
}
int wifimngr_get_wifi_interfaces(struct wifimngr *w, const char *conffile)
{
memset(w->ifs, 0, WIFI_IF_MAX_NUM * sizeof(struct wifimngr_iface));
w->num_wifi_iface = 0;
return uci_get_wifi_interfaces(w, conffile);
}
int wifimngr_get_wifi_mlds(struct wifimngr *w, const char *conffile)
{
memset(w->mld, 0, sizeof(struct wifimngr_mld) * WIFI_MLD_MAX_NUM);
w->num_wifi_mld = 0;
return uci_get_wifi_mlds(w, conffile);
}
static void wl_dump_capabilities(enum wifi_band band, struct blob_buf *bb,
struct wifi_caps *caps, uint8_t *bitmap,
int bitmap_len)
{
char bitmap_str[128] = {0};
void *f;
int i;
f = blobmsg_open_table(bb, "capabilities");
if ((sizeof(bitmap_str) - 1) >= (2 * bitmap_len)) {
btostr(bitmap, bitmap_len, bitmap_str);
blobmsg_add_string(bb, "bitmap", bitmap_str);
}
blobmsg_add_u8(bb, "wmm", wifi_cap_isset(bitmap, WIFI_CAP_WMM) ? true : false);
blobmsg_add_u8(bb, "apsd", wifi_cap_isset(bitmap, WIFI_CAP_APSD) ? true : false);
blobmsg_add_u8(bb, "shortslot", wifi_cap_isset(bitmap, WIFI_CAP_SHORT_SLOT) ? true : false);
blobmsg_add_u8(bb, "dot11h", wifi_cap_isset(bitmap, WIFI_CAP_SPECTRUM_MGMT) ? true : false);
blobmsg_add_u8(bb, "mu_edca", wifi_cap_isset(bitmap, WIFI_CAP_MU_EDCA) ? true : false);
if (!!(caps->valid & WIFI_CAP_EXT_VALID)) {
blobmsg_add_u8(bb, "2040coex", wifi_cap_isset(bitmap, WIFI_CAP_2040_COEX) ? true : false);
blobmsg_add_u8(bb, "psmp", wifi_cap_isset(bitmap, WIFI_CAP_PSMP) ? true : false);
blobmsg_add_u8(bb, "proxy_arp", wifi_cap_isset(bitmap, WIFI_CAP_PROXY_ARP) ? true : false);
blobmsg_add_u8(bb, "dot11v_btm", wifi_cap_isset(bitmap, WIFI_CAP_11V_BSS_TRANS) ? true : false);
blobmsg_add_u8(bb, "multi_bssid", wifi_cap_isset(bitmap, WIFI_CAP_MULTI_BSSID) ? true : false);
blobmsg_add_u8(bb, "ssidlist", wifi_cap_isset(bitmap, WIFI_CAP_SSID_LIST) ? true : false);
blobmsg_add_u8(bb, "interworking", wifi_cap_isset(bitmap, WIFI_CAP_INTERWORKING) ? true : false);
blobmsg_add_u8(bb, "qosmap", wifi_cap_isset(bitmap, WIFI_CAP_QOSMAP) ? true : false);
blobmsg_add_u8(bb, "tdls", wifi_cap_isset(bitmap, WIFI_CAP_TDLS) ? true : false);
blobmsg_add_u8(bb, "qos_scs", wifi_cap_isset(bitmap, WIFI_CAP_SCS) ? true : false);
blobmsg_add_u8(bb, "qload_report", wifi_cap_isset(bitmap, WIFI_CAP_QLOAD_REPORT) ? true : false);
blobmsg_add_u8(bb, "omi", wifi_cap_isset(bitmap, WIFI_CAP_OMI) ? true : false);
blobmsg_add_u8(bb, "qos_mscs", wifi_cap_isset(bitmap, WIFI_CAP_MSCS) ? true : false);
blobmsg_add_u8(bb, "twt_requester", wifi_cap_isset(bitmap, WIFI_CAP_TWT_REQ) ? true : false);
blobmsg_add_u8(bb, "twt_responder", wifi_cap_isset(bitmap, WIFI_CAP_TWT_RSP) ? true : false);
}
if (!!(caps->valid & WIFI_CAP_HT_VALID)) {
uint8_t *supp_mcs = caps->ht.supp_mcs;
int max_mcs = -1;
int octet = 0;
void *n;
int l;
n = blobmsg_open_table(bb, "dot11n");
blobmsg_add_u8(bb, "dot11n_ldpc", wifi_cap_isset(bitmap, WIFI_CAP_HT_LDPC) ? true : false);
blobmsg_add_u8(bb, "dot11n_40", wifi_cap_isset(bitmap, WIFI_CAP_2040) ? true : false);
blobmsg_add_u8(bb, "dot11n_ps", wifi_cap_isset(bitmap, WIFI_CAP_HT_SMPS) ? true : false);
blobmsg_add_u8(bb, "dot11n_sgi20", wifi_cap_isset(bitmap, WIFI_CAP_SGI20) ? true : false);
blobmsg_add_u8(bb, "dot11n_sgi40", wifi_cap_isset(bitmap, WIFI_CAP_SGI40) ? true : false);
blobmsg_add_u8(bb, "dot11n_tx_stbc", wifi_cap_isset(bitmap, WIFI_CAP_HT_TX_STBC) ? true : false);
blobmsg_add_u8(bb, "dot11n_rx_stbc", wifi_cap_isset(bitmap, WIFI_CAP_HT_RX_STBC) ? true : false);
for (l = 0; l < 77; l++) {
if (l && !(l % 8))
octet++;
if (!!(supp_mcs[octet] & (1 << (l % 8))))
max_mcs++;
}
blobmsg_add_u32(bb, "dot11n_supp_max_mcs", max_mcs);
blobmsg_close_table(bb, n);
}/* else {
blobmsg_add_u8(bb, "dot11n", false);
}*/
if (!!(caps->valid & WIFI_CAP_VHT_VALID)) {
void *ac;
ac = blobmsg_open_table(bb, "dot11ac");
blobmsg_add_u8(bb, "dot11ac_160", wifi_cap_isset(bitmap, WIFI_CAP_160) ? true : false);
blobmsg_add_u8(bb, "dot11ac_8080", wifi_cap_isset(bitmap, WIFI_CAP_8080) ? true : false);
if (wifi_cap_isset(bitmap, WIFI_CAP_VHT_MPDU_11454))
blobmsg_add_u32(bb, "dot11ac_mpdu_max", 11454);
else if (wifi_cap_isset(bitmap, WIFI_CAP_VHT_MPDU_7991))
blobmsg_add_u32(bb, "dot11ac_mpdu_max", 7991);
else if (wifi_cap_isset(bitmap, WIFI_CAP_VHT_MPDU_3895))
blobmsg_add_u32(bb, "dot11ac_mpdu_max", 3895);
blobmsg_add_u8(bb, "dot11ac_sgi80", wifi_cap_isset(bitmap, WIFI_CAP_SGI80) ? true : false);
blobmsg_add_u8(bb, "dot11ac_sgi160", wifi_cap_isset(bitmap, WIFI_CAP_SGI160) ? true : false);
blobmsg_add_u8(bb, "dot11ac_rx_ldpc", wifi_cap_isset(bitmap, WIFI_CAP_VHT_RX_LDPC) ? true : false);
blobmsg_add_u8(bb, "dot11ac_tx_stbc", wifi_cap_isset(bitmap, WIFI_CAP_VHT_TX_STBC) ? true : false);
blobmsg_add_u8(bb, "dot11ac_rx_stbc_1ss", wifi_cap_isset(bitmap, WIFI_CAP_VHT_RX_STBC_1SS) ? true : false);
blobmsg_add_u8(bb, "dot11ac_rx_stbc_2ss", wifi_cap_isset(bitmap, WIFI_CAP_VHT_RX_STBC_2SS) ? true : false);
blobmsg_add_u8(bb, "dot11ac_rx_stbc_3ss", wifi_cap_isset(bitmap, WIFI_CAP_VHT_RX_STBC_3SS) ? true : false);
blobmsg_add_u8(bb, "dot11ac_rx_stbc_4ss", wifi_cap_isset(bitmap, WIFI_CAP_VHT_RX_STBC_4SS) ? true : false);
blobmsg_add_u8(bb, "dot11ac_su_beamformer", wifi_cap_isset(bitmap, WIFI_CAP_VHT_SU_BFR) ? true : false);
blobmsg_add_u8(bb, "dot11ac_su_beamformee", wifi_cap_isset(bitmap, WIFI_CAP_VHT_SU_BFE) ? true : false);
blobmsg_add_u8(bb, "dot11ac_mu_beamformer", wifi_cap_isset(bitmap, WIFI_CAP_VHT_MU_BFR) ? true : false);
blobmsg_add_u8(bb, "dot11ac_mu_beamformee", wifi_cap_isset(bitmap, WIFI_CAP_VHT_MU_BFE) ? true : false);
for (i = 0; i < 2; i++) {
uint8_t *mcs_map = &caps->vht.supp_mcs[i*4];
int max_mcs = -1;
int octet = 0;
int nss = 0;
int l;
for (l = 0; l < 16; l += 2) {
uint8_t supp_mcs_mask = 0;
if (l && !(l % 8))
octet++;
supp_mcs_mask = mcs_map[octet] & (0x3 << (l % 8));
supp_mcs_mask >>= (l % 8);
if (supp_mcs_mask == 3)
break;
nss++;
if (supp_mcs_mask == 0)
max_mcs = 7;
else if (supp_mcs_mask == 1)
max_mcs = 8;
else if (supp_mcs_mask == 2)
max_mcs = 9;
}
if (i == 0) {
blobmsg_add_u32(bb, "dot11ac_supp_max_rx_mcs", max_mcs);
blobmsg_add_u32(bb, "dot11ac_supp_max_rx_nss", nss);
} else {
blobmsg_add_u32(bb, "dot11ac_supp_max_tx_mcs", max_mcs);
blobmsg_add_u32(bb, "dot11ac_supp_max_tx_nss", nss);
}
}
blobmsg_close_table(bb, ac);
}/* else {
blobmsg_add_u8(bb, "dot11ac", false);
}*/
if (!!(caps->valid & WIFI_CAP_HE_VALID)) {
uint8_t supp_chwidth = (caps->he.byte_phy[0] & 0xf7) >> 1;
bool b0 = !!(supp_chwidth & BIT(0));
//bool b1 = !!(supp_chwidth & BIT(1));
bool b2 = !!(supp_chwidth & BIT(2));
bool b3 = !!(supp_chwidth & BIT(3));
int mcs_maplen = 4;
uint8_t *mcs_map;
void *ax;
ax = blobmsg_open_table(bb, "dot11ax");
blobmsg_add_u8(bb, "dot11ax_twt_requester", wifi_cap_isset(bitmap, WIFI_CAP_HE_TWT_REQ) ? true : false);
blobmsg_add_u8(bb, "dot11ax_twt_responder", wifi_cap_isset(bitmap, WIFI_CAP_HE_TWT_RSP) ? true : false);
blobmsg_add_u8(bb, "dot11ax_all_ack", wifi_cap_isset(bitmap, WIFI_CAP_HE_ALL_ACK) ? true : false);
blobmsg_add_u8(bb, "dot11ax_trs", wifi_cap_isset(bitmap, WIFI_CAP_HE_TRS) ? true : false);
blobmsg_add_u8(bb, "dot11ax_bsr", wifi_cap_isset(bitmap, WIFI_CAP_HE_BSR) ? true : false);
blobmsg_add_u8(bb, "dot11ax_twt_bcast", wifi_cap_isset(bitmap, WIFI_CAP_HE_BCAST_TWT) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ba_32bit", wifi_cap_isset(bitmap, WIFI_CAP_HE_32BIT_BA_BMP) ? true : false);
blobmsg_add_u8(bb, "dot11ax_om_control", wifi_cap_isset(bitmap, WIFI_CAP_HE_OM_CONTROL) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ofdma_ra", wifi_cap_isset(bitmap, WIFI_CAP_HE_OFDMA_RA) ? true : false);
blobmsg_add_u8(bb, "dot11ax_amsdu_frag", wifi_cap_isset(bitmap, WIFI_CAP_HE_AMSDU_FRAG) ? true : false);
blobmsg_add_u8(bb, "dot11ax_twt_flexible", wifi_cap_isset(bitmap, WIFI_CAP_HE_FLEX_TWT) ? true : false);
blobmsg_add_u8(bb, "dot11ax_qtp", wifi_cap_isset(bitmap, WIFI_CAP_HE_QTP) ? true : false);
blobmsg_add_u8(bb, "dot11ax_bqr", wifi_cap_isset(bitmap, WIFI_CAP_HE_BQR) ? true : false);
blobmsg_add_u8(bb, "dot11ax_srp_responder", wifi_cap_isset(bitmap, WIFI_CAP_HE_SRP_RSP) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ops", wifi_cap_isset(bitmap, WIFI_CAP_HE_OPS) ? true : false);
blobmsg_add_u8(bb, "dot11ax_amsdu_in_ampdu", wifi_cap_isset(bitmap, WIFI_CAP_HE_AMSDU_IN_AMPDU) ? true : false);
blobmsg_add_u8(bb, "dot11ax_dynamic_smps", wifi_cap_isset(bitmap, WIFI_CAP_HE_DYN_SMPS) ? true : false);
blobmsg_add_u8(bb, "dot11ax_2g_40", wifi_cap_isset(bitmap, WIFI_CAP_HE_40_BAND2) ? true : false);
blobmsg_add_u8(bb, "dot11ax_5g_4080", wifi_cap_isset(bitmap, WIFI_CAP_HE_4080_BAND5) ? true : false);
blobmsg_add_u8(bb, "dot11ax_5g_160", wifi_cap_isset(bitmap, WIFI_CAP_HE_160_BAND5) ? true : false);
blobmsg_add_u8(bb, "dot11ax_5g_160_and_8080", wifi_cap_isset(bitmap, WIFI_CAP_HE_160_8080_BAND5) ? true : false);
blobmsg_add_u8(bb, "dot11ax_2g_242ru", wifi_cap_isset(bitmap, WIFI_CAP_HE_242RU_BAND2) ? true : false);
blobmsg_add_u8(bb, "dot11ax_5g_242ru", wifi_cap_isset(bitmap, WIFI_CAP_HE_242RU_BAND2) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ldpc_payload", wifi_cap_isset(bitmap, WIFI_CAP_HE_LDPC_PAYLOAD) ? true : false);
blobmsg_add_u8(bb, "dot11ax_tx_stbc_80", wifi_cap_isset(bitmap, WIFI_CAP_HE_STBC_TX_80) ? true : false);
blobmsg_add_u8(bb, "dot11ax_rx_stbc_80", wifi_cap_isset(bitmap, WIFI_CAP_HE_STBC_RX_80) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ul_mumimo_full", wifi_cap_isset(bitmap, WIFI_CAP_HE_FULL_BW_UL_MUMIMO) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ul_mumimo_partial", wifi_cap_isset(bitmap, WIFI_CAP_HE_PART_BW_UL_MUMIMO) ? true : false);
blobmsg_add_u8(bb, "dot11ax_su_beamformer", wifi_cap_isset(bitmap, WIFI_CAP_HE_SU_BFR) ? true : false);
blobmsg_add_u8(bb, "dot11ax_su_beamformee", wifi_cap_isset(bitmap, WIFI_CAP_HE_SU_BFE) ? true : false);
blobmsg_add_u8(bb, "dot11ax_mu_beamformer", wifi_cap_isset(bitmap, WIFI_CAP_HE_MU_BFR) ? true : false);
blobmsg_add_u8(bb, "dot11ax_bfe_sts_le_80", wifi_cap_isset(bitmap, WIFI_CAP_HE_BFE_STS_LE_80MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11ax_bfe_sts_gt_80", wifi_cap_isset(bitmap, WIFI_CAP_HE_BFE_STS_GT_80MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ng16_su_feedback", wifi_cap_isset(bitmap, WIFI_CAP_HE_NG16_SU_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ng16_mu_feedback", wifi_cap_isset(bitmap, WIFI_CAP_HE_NG16_MU_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11ax_codebook_su_feedback", wifi_cap_isset(bitmap, WIFI_CAP_HE_CODEBOOK_42_SU_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11ax_codebook_mu_feedback", wifi_cap_isset(bitmap, WIFI_CAP_HE_CODEBOOK_75_MU_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11ax_triggered_su_bf_feedback", wifi_cap_isset(bitmap, WIFI_CAP_HE_TRIGGERED_SU_BF_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11ax_triggered_mu_bf_partial_bw_feedback", wifi_cap_isset(bitmap, WIFI_CAP_HE_TRIGGERED_MU_BF_PARTIAL_BW_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11ax_triggered_cqi_feedback", wifi_cap_isset(bitmap, WIFI_CAP_HE_TRIGGERED_CQI_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11ax_partial_bw_er", wifi_cap_isset(bitmap, WIFI_CAP_HE_PARTIAL_BW_ER) ? true : false);
blobmsg_add_u8(bb, "dot11ax_dl_mumimo_partial", wifi_cap_isset(bitmap, WIFI_CAP_HE_PARTIAL_BW_DL_MUMIMO) ? true : false);
blobmsg_add_u8(bb, "dot11ax_ppe_thresh_present", wifi_cap_isset(bitmap, WIFI_CAP_HE_PPE_THRESHOLDS_PRESENT) ? true : false);
blobmsg_add_u8(bb, "dot11ax_psr_sr", wifi_cap_isset(bitmap, WIFI_CAP_HE_PSR_SR) ? true : false);
blobmsg_add_u8(bb, "dot11ax_doppler_tx", wifi_cap_isset(bitmap, WIFI_CAP_HE_DOPPLER_TX) ? true : false);
blobmsg_add_u8(bb, "dot11ax_doppler_rx", wifi_cap_isset(bitmap, WIFI_CAP_HE_DOPPLER_RX) ? true : false);
blobmsg_add_u8(bb, "dot11ax_stbc_tx_gt_80", wifi_cap_isset(bitmap, WIFI_CAP_HE_STBC_TX_GT_80MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11ax_stbc_rx_gt_80", wifi_cap_isset(bitmap, WIFI_CAP_HE_STBC_RX_GT_80MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11ax_rts", wifi_cap_isset(bitmap, WIFI_CAP_HE_RTS_TXOP_BASED) ? true : false);
blobmsg_add_u8(bb, "dot11ax_mu_rts", wifi_cap_isset(bitmap, WIFI_CAP_MU_RTS) ? true : false);
blobmsg_add_u8(bb, "dot11ax_multi_bssid", wifi_cap_isset(bitmap, WIFI_CAP_MULTI_BSSID) ? true : false);
blobmsg_add_u8(bb, "dot11ax_mu_edca", wifi_cap_isset(bitmap, WIFI_CAP_MU_EDCA) ? true : false);
/* get max MCS and NSS */
if (band == BAND_5 || band == BAND_6) {
if (b2)
mcs_maplen += 4;
if (b3)
mcs_maplen += 4;
}
for (i = 0; i < mcs_maplen; i += 2) {
int max_mcs = -1;
int octet = 0;
int nss = 0;
int l;
mcs_map = &caps->he.byte_opt[i];
for (l = 0; l < 16; l += 2) {
uint8_t supp_mcs_mask = 0;
if (l && !(l % 8))
octet++;
supp_mcs_mask = mcs_map[octet] & (0x3 << (l % 8));
supp_mcs_mask >>= (l % 8);
if (supp_mcs_mask == 3)
break;
nss++;
if (supp_mcs_mask == 0)
max_mcs = 7;
else if (supp_mcs_mask == 1)
max_mcs = 9;
else if (supp_mcs_mask == 2)
max_mcs = 11;
}
if (max_mcs < 0)
continue;
switch (i) {
case 0:
if (band == BAND_2) {
if (b0) {
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_mcs_40", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_nss_40", nss);
} else {
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_mcs_20", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_nss_20", nss);
}
} else {
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_mcs_80", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_nss_80", nss);
}
break;
case 2:
if (band == BAND_2) {
if (b0) {
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_mcs_40", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_nss_40", nss);
} else {
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_mcs_20", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_nss_20", nss);
}
} else {
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_mcs_80", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_nss_80", nss);
}
break;
case 4:
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_mcs_160", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_nss_160", nss);
break;
case 6:
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_mcs_160", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_nss_160", nss);
break;
case 8:
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_mcs_8080", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_rx_nss_8080", nss);
break;
case 10:
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_mcs_8080", max_mcs);
blobmsg_add_u32(bb, "dot11ax_supp_max_tx_nss_8080", nss);
break;
default:
break;
}
}
blobmsg_close_table(bb, ax);
}
if (!!(caps->valid & WIFI_CAP_EHT_VALID)) {
void *be;
be = blobmsg_open_table(bb, "dot11be");
blobmsg_add_u8(bb, "dot11be_epcs", wifi_cap_isset(bitmap, WIFI_CAP_EHT_EPCS) ? true : false);
blobmsg_add_u8(bb, "dot11be_om_control", wifi_cap_isset(bitmap, WIFI_CAP_EHT_OM_CONTROL) ? true : false);
blobmsg_add_u8(bb, "dot11be_triggered_txop_mode1", wifi_cap_isset(bitmap, WIFI_CAP_EHT_TRIGGERED_TXOP_MODE_1) ? true : false);
blobmsg_add_u8(bb, "dot11be_triggered_txop_mode2", wifi_cap_isset(bitmap, WIFI_CAP_EHT_TRIGGERED_TXOP_MODE_2) ? true : false);
blobmsg_add_u8(bb, "dot11be_rtwt", wifi_cap_isset(bitmap, WIFI_CAP_EHT_RTWT) ? true : false);
blobmsg_add_u8(bb, "dot11be_scs_tdesc", wifi_cap_isset(bitmap, WIFI_CAP_EHT_SCS_TDESC) ? true : false);
if (wifi_cap_isset(bitmap, WIFI_CAP_EHT_MPDU_11454))
blobmsg_add_u32(bb, "dot11be_mpdu_max", 11454);
else if (wifi_cap_isset(bitmap, WIFI_CAP_EHT_MPDU_7991))
blobmsg_add_u32(bb, "dot11be_mpdu_max", 7991);
else if (wifi_cap_isset(bitmap, WIFI_CAP_EHT_MPDU_3895))
blobmsg_add_u32(bb, "dot11be_mpdu_max", 3895);
blobmsg_add_u8(bb, "dot11be_trs", wifi_cap_isset(bitmap, WIFI_CAP_EHT_TRS) ? true : false);
blobmsg_add_u8(bb, "dot11be_txop_return_mode2", wifi_cap_isset(bitmap, WIFI_CAP_EHT_TXOP_RETURN_IN_MODE_2) ? true : false);
blobmsg_add_u8(bb, "dot11be_two_bqr", wifi_cap_isset(bitmap, WIFI_CAP_EHT_TWO_BQR) ? true : false);
blobmsg_add_u8(bb, "dot11be_link_adapt", wifi_cap_isset(bitmap, WIFI_CAP_EHT_LINK_ADAPT) ? true : false);
blobmsg_add_u8(bb, "dot11be_6g_320", wifi_cap_isset(bitmap, WIFI_CAP_EHT_320_BAND6) ? true : false);
blobmsg_add_u8(bb, "dot11be_242ru_bw20_plus", wifi_cap_isset(bitmap, WIFI_CAP_EHT_242RU_IN_BW_GT_20MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11be_ndp_4xltf_gi32", wifi_cap_isset(bitmap, WIFI_CAP_EHT_NDP_WITH_4xEHT_LTF_AND_3_2_GI) ? true : false);
blobmsg_add_u8(bb, "dot11be_ul_mumimo_partial", wifi_cap_isset(bitmap, WIFI_CAP_EHT_PARTIAL_BW_UL_MUMIMO) ? true : false);
blobmsg_add_u8(bb, "dot11be_dl_mumimo_partial", wifi_cap_isset(bitmap, WIFI_CAP_EHT_PARTIAL_BW_DL_MUMIMO) ? true : false);
blobmsg_add_u8(bb, "dot11be_su_beamformer", wifi_cap_isset(bitmap, WIFI_CAP_EHT_SU_BFR) ? true : false);
blobmsg_add_u8(bb, "dot11be_su_beamformee", wifi_cap_isset(bitmap, WIFI_CAP_EHT_SU_BFE) ? true : false);
blobmsg_add_u8(bb, "dot11be_ng16_su_feedback", wifi_cap_isset(bitmap, WIFI_CAP_EHT_NG16_SU_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11be_ng16_mu_feedback", wifi_cap_isset(bitmap, WIFI_CAP_EHT_NG16_MU_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11be_codebook_su_feedback", wifi_cap_isset(bitmap, WIFI_CAP_EHT_CODEBOOK_42_SU_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11be_codebook_mu_feedback", wifi_cap_isset(bitmap, WIFI_CAP_EHT_CODEBOOK_75_MU_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11be_triggered_cqi_feedback", wifi_cap_isset(bitmap, WIFI_CAP_EHT_TRIGGERED_CQI_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11be_psr_sr", wifi_cap_isset(bitmap, WIFI_CAP_EHT_PSR_SR) ? true : false);
blobmsg_add_u8(bb, "dot11be_power_boost", wifi_cap_isset(bitmap, WIFI_CAP_EHT_POWER_BOOST_FACTOR) ? true : false);
blobmsg_add_u8(bb, "dot11be_mu_ppdu_4xltf_gi08", wifi_cap_isset(bitmap, WIFI_CAP_EHT_MU_PPDU_WITH_4xEHT_LTF_AND_0_8_GI) ? true : false);
blobmsg_add_u8(bb, "dot11be_non_triggered_cqi_feedback", wifi_cap_isset(bitmap, WIFI_CAP_EHT_NON_TRIGGERED_CQI_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11be_tx_1k_4k_qam_lt_242ru", wifi_cap_isset(bitmap, WIFI_CAP_EHT_TX_1K_4K_QAM_WITH_LT_242RU) ? true : false);
blobmsg_add_u8(bb, "dot11be_rx_1k_4k_qam_lt_242ru", wifi_cap_isset(bitmap, WIFI_CAP_EHT_RX_1K_4K_QAM_WITH_LT_242RU) ? true : false);
blobmsg_add_u8(bb, "dot11be_ppe_thresh_present", wifi_cap_isset(bitmap, WIFI_CAP_EHT_PPE_THRESHOLDS_PRESENT) ? true : false);
blobmsg_add_u8(bb, "dot11be_dup_in_6g", wifi_cap_isset(bitmap, WIFI_CAP_EHT_DUP_IN_BAND6) ? true : false);
blobmsg_add_u8(bb, "dot11be_ul_non_ofdma_80", wifi_cap_isset(bitmap, WIFI_CAP_EHT_NON_OFDMA_UL_MUMIMO_80MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11be_ul_non_ofdma_160", wifi_cap_isset(bitmap, WIFI_CAP_EHT_NON_OFDMA_UL_MUMIMO_160MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11be_ul_non_ofdma_320", wifi_cap_isset(bitmap, WIFI_CAP_EHT_NON_OFDMA_UL_MUMIMO_320MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11be_mu_beamformer_80", wifi_cap_isset(bitmap, WIFI_CAP_EHT_MU_BFR_80MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11be_mu_beamformer_160", wifi_cap_isset(bitmap, WIFI_CAP_EHT_MU_BFR_160MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11be_mu_beamformer_320", wifi_cap_isset(bitmap, WIFI_CAP_EHT_MU_BFR_320MHZ) ? true : false);
blobmsg_add_u8(bb, "dot11be_ratelimit_tb_sounding", wifi_cap_isset(bitmap, WIFI_CAP_EHT_TB_SOUND_FEEDBACK_RATELIMIT) ? true : false);
blobmsg_add_u8(bb, "dot11be_rx_1k_qam_dl_ofdma_wider_bw", wifi_cap_isset(bitmap, WIFI_CAP_EHT_RX_1K_QAM_DL_OFDMA_WIDER_BW) ? true : false);
blobmsg_add_u8(bb, "dot11be_rx_4k_qam_dl_ofdma_wider_bw", wifi_cap_isset(bitmap, WIFI_CAP_EHT_RX_4K_QAM_DL_OFDMA_WIDER_BW) ? true : false);
blobmsg_add_u8(bb, "dot11be_20_only", wifi_cap_isset(bitmap, WIFI_CAP_EHT_20MHZ_ONLY) ? true : false);
blobmsg_add_u8(bb, "dot11be_20_only_triggered_mu_bf_full_bw_feedback", wifi_cap_isset(bitmap, WIFI_CAP_EHT_20MHZ_ONLY_TRIGGERED_MU_BF_FULL_BW_FEEDBACK) ? true : false);
blobmsg_add_u8(bb, "dot11be_20_only_mru", wifi_cap_isset(bitmap, WIFI_CAP_EHT_20MHZ_ONLY_MRU) ? true : false);
blobmsg_close_table(bb, be);
}
if (!!(caps->valid & WIFI_CAP_RM_VALID)) {
void *k;
k = blobmsg_open_table(bb, "dot11k");
blobmsg_add_u8(bb, "dot11k_link_meas", wifi_cap_isset(bitmap, WIFI_CAP_RM_LINK) ? true : false);
blobmsg_add_u8(bb, "dot11k_nbr_report", wifi_cap_isset(bitmap, WIFI_CAP_RM_NBR_REPORT) ? true : false);
blobmsg_add_u8(bb, "dot11k_bcn_passive", wifi_cap_isset(bitmap, WIFI_CAP_RM_BCN_PASSIVE) ? true : false);
blobmsg_add_u8(bb, "dot11k_bcn_active", wifi_cap_isset(bitmap, WIFI_CAP_RM_BCN_ACTIVE) ? true : false);
blobmsg_add_u8(bb, "dot11k_bcn_table", wifi_cap_isset(bitmap, WIFI_CAP_RM_BCN_TABLE) ? true : false);
blobmsg_add_u8(bb, "dot11k_rcpi", wifi_cap_isset(bitmap, WIFI_CAP_RM_RCPI) ? true : false);
blobmsg_add_u8(bb, "dot11k_rsni", wifi_cap_isset(bitmap, WIFI_CAP_RM_RSNI) ? true : false);
blobmsg_close_table(bb, k);
} /* else
blobmsg_add_u8(bb, "dot11k", false); */
//blobmsg_add_u8(bb, "dot11r", wifi_cap_isset(bitmap, WIFI_CAP_FT_BSS) ? true : false);
blobmsg_close_table(bb, f);
}
static void wl_print_sta_stats(struct blob_buf *bb, struct wifi_sta_stats *stats)
{
void *t;
t = blobmsg_open_table(bb, "stats");
blobmsg_add_u64(bb, "tx_total_pkts", stats->tx_pkts);
blobmsg_add_u64(bb, "tx_total_bytes", stats->tx_bytes);
blobmsg_add_u64(bb, "tx_failures", stats->tx_fail_pkts);
blobmsg_add_u64(bb, "tx_pkts_retries", stats->tx_retry_pkts);
blobmsg_add_u64(bb, "tx_ucast_pkts", stats->tx_ucast_pkts);
blobmsg_add_u64(bb, "tx_ucast_bytes", stats->tx_ucast_bytes);
blobmsg_add_u64(bb, "tx_mcast_pkts", stats->tx_mcast_pkts);
blobmsg_add_u64(bb, "tx_mcast_bytes", stats->tx_mcast_bytes);
blobmsg_add_u64(bb, "rx_data_pkts", stats->rx_pkts);
blobmsg_add_u64(bb, "rx_data_bytes", stats->rx_bytes);
blobmsg_add_u64(bb, "rx_failures", stats->rx_fail_pkts);
blobmsg_add_u64(bb, "rx_ucast_pkts", stats->rx_ucast_pkts);
blobmsg_add_u64(bb, "rx_ucast_bytes", stats->rx_ucast_bytes);
blobmsg_add_u64(bb, "rx_mcast_pkts", stats->rx_mcast_pkts);
blobmsg_add_u64(bb, "rx_mcast_bytes", stats->rx_mcast_bytes);
blobmsg_close_table(bb, t);
}
static void wl_print_rate(struct blob_buf *bb, const char *label, struct wifi_rate *rate)
{
void *t;
t = blobmsg_open_table(bb, label);
blobmsg_add_u32(bb, "rate", rate->rate);
blobmsg_add_u32(bb, "mcs", rate->m.mcs);
blobmsg_add_u32(bb, "bandwidth", rate->m.bw);
blobmsg_add_u32(bb, "sgi", rate->m.sgi);
blobmsg_add_u32(bb, "nss", rate->m.nss);
blobmsg_add_u32(bb, "phy", rate->phy);
blobmsg_close_table(bb, t);
}
static void wl_print_rssi_chains(struct blob_buf *bb, const char *label, int8_t rssi[])
{
void *t;
t = blobmsg_open_array(bb, label);
for (int j = 0; j < WIFI_NUM_ANTENNA; j++)
blobmsg_add_u32(bb, "", rssi[j]);
blobmsg_close_array(bb, t);
}
static void wifi_print_radio_stats(struct blob_buf *bb,
struct wifi_radio_stats *s)
{
blobmsg_add_u64(bb, "tx_bytes", s->tx_bytes);
blobmsg_add_u64(bb, "tx_packets", s->tx_pkts);
blobmsg_add_u64(bb, "tx_error_packets", s->tx_err_pkts);
blobmsg_add_u64(bb, "tx_dropped_packets", s->tx_dropped_pkts);
blobmsg_add_u64(bb, "rx_bytes", s->rx_bytes);
blobmsg_add_u64(bb, "rx_packets", s->rx_pkts);
blobmsg_add_u64(bb, "rx_error_packets", s->rx_err_pkts);
blobmsg_add_u64(bb, "rx_dropped_packets", s->rx_dropped_pkts);
blobmsg_add_u64(bb, "rx_plcp_error_packets", s->rx_plcp_err_pkts);
blobmsg_add_u64(bb, "rx_fcs_error_packets", s->rx_fcs_err_pkts);
blobmsg_add_u64(bb, "rx_mac_error_packets", s->rx_mac_err_pkts);
blobmsg_add_u64(bb, "rx_unknown_packets", s->rx_unknown_pkts);
}
static void wifi_print_radio_diagnostics(struct blob_buf *bb,
struct wifi_radio_diagnostic *d)
{
void *t;
t = blobmsg_open_table(bb, "diagnostics");
blobmsg_add_u64(bb, "channel_busy", d->channel_busy);
blobmsg_add_u64(bb, "tx_airtime", d->tx_airtime);
blobmsg_add_u64(bb, "rx_airtime", d->rx_airtime);
blobmsg_add_u64(bb, "obss_airtime", d->obss_airtime);
blobmsg_add_u64(bb, "cca_time", d->cca_time);
blobmsg_add_u64(bb, "false_cca_count", d->false_cca_count);
blobmsg_close_table(bb, t);
}
static void wifi_print_radio_akms(struct blob_buf *bb, const char *label,
uint32_t *akms, int num_akms)
{
void *t;
t = blobmsg_open_array(bb, label);
for (int i = 0; i < num_akms; i++) {
char akmstr[16] = "";
snprintf(akmstr, sizeof(akmstr), "%x", akms[i]);
blobmsg_add_string(bb, "", akmstr);
}
blobmsg_close_array(bb, t);
}
static void wifi_print_radio_supp_bands(struct blob_buf *bb,
uint32_t supp_band)
{
void *a;
a = blobmsg_open_array(bb, "supp_bands");
if (supp_band & BAND_2)
blobmsg_add_string(bb, "", "2.4GHz");
if (supp_band & BAND_5)
blobmsg_add_string(bb, "", "5GHz");
if (supp_band & BAND_6)
blobmsg_add_string(bb, "", "6GHz");
if (supp_band & BAND_60)
blobmsg_add_string(bb, "", "60GHz");
blobmsg_close_array(bb, a);
}
static void wifi_print_radio_supp_std(struct blob_buf *bb,
uint32_t supp_std)
{
void *a;
a = blobmsg_open_array(bb, "supp_std");
if (supp_std & WIFI_B)
blobmsg_add_string(bb, "", "11b");
if (supp_std & WIFI_G)
blobmsg_add_string(bb, "", "11g");
if (supp_std & WIFI_A)
blobmsg_add_string(bb, "", "11a");
if (supp_std & WIFI_N)
blobmsg_add_string(bb, "", "11n");
if (supp_std & WIFI_AC)
blobmsg_add_string(bb, "", "11ac");
if (supp_std & WIFI_AX)
blobmsg_add_string(bb, "", "11ax");
if (supp_std & WIFI_BE)
blobmsg_add_string(bb, "", "11be");
blobmsg_close_array(bb, a);
}
static void wifi_print_radio_supp_bw(struct blob_buf *bb,
uint32_t supp_bw)
{
void *a;
a = blobmsg_open_array(bb, "supp_bw");
if (supp_bw & BIT(BW20))
blobmsg_add_string(bb, "", "20MHz");
if (supp_bw & BIT(BW40))
blobmsg_add_string(bb, "", "40MHz");
if (supp_bw & BIT(BW80))
blobmsg_add_string(bb, "", "80MHz");
if (supp_bw & BIT(BW160))
blobmsg_add_string(bb, "", "160MHz");
if (supp_bw & BIT(BW8080))
blobmsg_add_string(bb, "", "80+80MHz");
if (supp_bw & BIT(BW320))
blobmsg_add_string(bb, "", "320MHz");
blobmsg_close_array(bb, a);
}
static void wifi_print_band(struct blob_buf *bb, enum wifi_band band)
{
switch (band) {
case BAND_2:
blobmsg_add_string(bb, "band", "2.4GHz");
break;
case BAND_5:
blobmsg_add_string(bb, "band", "5GHz");
break;
case BAND_6:
blobmsg_add_string(bb, "band", "6GHz");
break;
default:
blobmsg_add_string(bb, "band", "unknown");
break;
}
}
static void wifi_print_acs_mode(struct blob_buf *bb, enum wifi_acs_mode mode)
{
switch (mode) {
case WIFI_ACS_MODE_DISABLE:
blobmsg_add_string(bb, "acs_mode", "disabled");
break;
case WIFI_ACS_MODE_ENABLE:
blobmsg_add_string(bb, "acs_mode", "enabled");
break;
case WIFI_ACS_MODE_MONITOR:
blobmsg_add_string(bb, "acs_mode", "monitor");
break;
default:
blobmsg_add_string(bb, "acs_mode", "unknown");
break;
}
}
static void wifi_print_mode(struct blob_buf *bb, enum wifi_mode mode)
{
switch (mode) {
case WIFI_MODE_AP:
blobmsg_add_string(bb, "mode", "ap");
break;
case WIFI_MODE_AP_VLAN:
blobmsg_add_string(bb, "mode", "ap-vlan");
break;
case WIFI_MODE_STA:
blobmsg_add_string(bb, "mode", "sta");
break;
case WIFI_MODE_MONITOR:
blobmsg_add_string(bb, "mode", "monitor");
break;
default:
blobmsg_add_string(bb, "mode", "unknown");
break;
}
}
static void wifi_print_radio_cac_methods(struct blob_buf *bb,
uint32_t cac_methods)
{
void *a;
a = blobmsg_open_array(bb, "cac_methods");
if (cac_methods & BIT(WIFI_CAC_CONTINUOUS))
blobmsg_add_string(bb, "", "continous");
if (cac_methods & BIT(WIFI_CAC_DEDICATED))
blobmsg_add_string(bb, "", "continous-dedicated");
if (cac_methods & BIT(WIFI_CAC_MIMO_REDUCED))
blobmsg_add_string(bb, "", "mimo-reduced");
if (cac_methods & BIT(WIFI_CAC_TIME_SLICED))
blobmsg_add_string(bb, "", "time-sliced");
blobmsg_close_array(bb, a);
}
static void wifi_print_radio_caps(struct blob_buf *bb, struct wifi_caps *caps, enum wifi_mode m)
{
char bitmap_str[512] = {0};
void *f;
if (caps->valid & WIFI_CAP_HT_VALID) {
f = blobmsg_open_table(bb, "ht");
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->ht.byte))) {
bitmap_str[0] = '\0';
btostr(caps->ht.byte, sizeof(caps->ht.byte), bitmap_str);
blobmsg_add_string(bb, "caps", bitmap_str);
}
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->ht.supp_mcs))) {
bitmap_str[0] = '\0';
btostr(caps->ht.supp_mcs, sizeof(caps->ht.supp_mcs), bitmap_str);
blobmsg_add_string(bb, "mcs", bitmap_str);
}
blobmsg_close_table(bb, f);
}
if (caps->valid & WIFI_CAP_VHT_VALID) {
f = blobmsg_open_table(bb, "vht");
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->vht.byte))) {
bitmap_str[0] = '\0';
btostr(caps->vht.byte, sizeof(caps->vht.byte), bitmap_str);
blobmsg_add_string(bb, "caps", bitmap_str);
}
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->vht.supp_mcs))) {
bitmap_str[0] = '\0';
btostr(caps->vht.supp_mcs, sizeof(caps->vht.supp_mcs), bitmap_str);
blobmsg_add_string(bb, "mcs", bitmap_str);
}
blobmsg_close_table(bb, f);
}
if (caps->valid & WIFI_CAP_HE_VALID) {
f = blobmsg_open_table(bb, "he");
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->eht.byte_phy))) {
bitmap_str[0] = '\0';
btostr(caps->he.byte_phy, sizeof(caps->he.byte_phy), bitmap_str);
blobmsg_add_string(bb, "phy_caps", bitmap_str);
}
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->eht.byte_mac))) {
bitmap_str[0] = '\0';
btostr(caps->he.byte_mac, sizeof(caps->he.byte_mac), bitmap_str);
blobmsg_add_string(bb, "mac_caps", bitmap_str);
}
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->he.byte_mcs))) {
bitmap_str[0] = '\0';
btostr(caps->he.byte_mcs, caps->he.byte_mcs_len, bitmap_str);
blobmsg_add_string(bb, "mcs", bitmap_str);
}
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->he.byte_ppe))) {
bitmap_str[0] = '\0';
btostr(caps->he.byte_ppe, caps->he.byte_ppe_len, bitmap_str);
blobmsg_add_string(bb, "ppe", bitmap_str);
}
blobmsg_close_table(bb, f);
}
if (caps->valid & WIFI_CAP_EHT_VALID) {
f = blobmsg_open_table(bb, "eht");
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->eht.byte_phy))) {
bitmap_str[0] = '\0';
btostr(caps->eht.byte_phy, sizeof(caps->eht.byte_phy), bitmap_str);
blobmsg_add_string(bb, "phy_caps", bitmap_str);
}
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->eht.byte_mac))) {
bitmap_str[0] = '\0';
btostr(caps->eht.byte_mac, sizeof(caps->eht.byte_mac), bitmap_str);
blobmsg_add_string(bb, "mac_caps", bitmap_str);
}
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->eht.supp_mcs))) {
bitmap_str[0] = '\0';
btostr(caps->eht.supp_mcs, caps->eht.supp_mcs_len, bitmap_str);
blobmsg_add_string(bb, "mcs", bitmap_str);
}
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->eht.byte_ppe_th))) {
bitmap_str[0] = '\0';
btostr(caps->eht.byte_ppe_th, caps->eht.byte_ppe_th_len, bitmap_str);
blobmsg_add_string(bb, "ppe", bitmap_str);
}
blobmsg_close_table(bb, f);
}
if (caps->valid & WIFI_CAP_ML_VALID) {
f = blobmsg_open_table(bb, "ml");
if (caps->ml.valid & WIFI_CAP_ML_EML_VALID) {
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->ml.eml))) {
bitmap_str[0] = '\0';
btostr(caps->ml.eml, sizeof(caps->ml.eml), bitmap_str);
blobmsg_add_string(bb, "eml", bitmap_str);
}
}
if (caps->ml.valid & WIFI_CAP_ML_MLD_VALID) {
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->ml.mld))) {
bitmap_str[0] = '\0';
btostr(caps->ml.eml, sizeof(caps->ml.mld), bitmap_str);
blobmsg_add_string(bb, "mld", bitmap_str);
}
}
if (caps->ml.valid & WIFI_CAP_ML_EMLD_VALID) {
if ((sizeof(bitmap_str) - 1) >= (2 * sizeof(caps->ml.emld))) {
bitmap_str[0] = '\0';
btostr(caps->ml.emld, sizeof(caps->ml.emld), bitmap_str);
blobmsg_add_string(bb, "emld", bitmap_str);
}
}
blobmsg_close_table(bb, f);
}
if (caps->valid & WIFI_CAP_ML_VALID) {
if (!!(caps->ml.valid & WIFI_CAP_ML_EML_VALID)) {
uint8_t *eml = caps->ml.eml;
blobmsg_add_u8(bb, "emlsr_supported", !!(eml[0] & BIT(0)) ? true : false);
blobmsg_add_u8(bb, "emlmr_supported", !!(eml[0] & BIT(7)) ? true : false);
}
if (!!(caps->ml.valid & WIFI_CAP_ML_MLD_VALID)) {
uint8_t *mld = caps->ml.mld;
uint8_t max_simlinks = (mld[0] & 0x0f);
bool nstr_mobile_ap = !!(mld[0] & 0x80);
if (max_simlinks > 0) {
if (m == WIFI_MODE_AP && nstr_mobile_ap)
blobmsg_add_u8(bb, "nstr", true);
else
blobmsg_add_u8(bb, "nstr", false);
if (m == WIFI_MODE_AP)
blobmsg_add_u8(bb, "str", !nstr_mobile_ap ? true : false);
else
blobmsg_add_u8(bb, "str", true);
}
blobmsg_add_u32(bb, "max_links", max_simlinks);
blobmsg_add_u32(bb, "ttlm", ((mld[0] >> 1) & 0x3));
}
}
}
int wl_radio_help(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return wl_help_command(ctx, obj, req, method, msg, WIFI_RADIO_OBJECT);
}
int wl_radio_status(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
char std_buf2[32] = "802.11";
char std_buf[32] = {0};
uint8_t hwaddr[6] = {0};
const char *wldev;
const char *radioname;
enum wifi_band band = BAND_2;
struct wifi_metainfo minfo = {0};
struct wifi_radio radio = {};
struct wifi_opclass supp_opclass[64] = {0};
int num_opclass = ARRAY_SIZE(supp_opclass);
bool radio_disabled;
struct blob_buf bb;
void *c, *d, *dd;
bool multiband = false;
int i, j;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
radioname = ubus_objname_to_ifname(obj);
wldev = wdev->phy;
band = wdev->band;
radio_disabled = wdev->disabled;
/* Get required information */
wifi_driver_info(wldev, &minfo);
wifi_radio_is_multiband(wldev, &multiband);
if (wifi_radio_get_hwaddr(wldev, hwaddr) != 0)
if_gethwaddr(wldev, hwaddr);
if (hwaddr_is_zero(hwaddr) && !hwaddr_is_zero(wdev->macaddr))
memcpy(hwaddr, wdev->macaddr, 6);
wifi_radio_info_band(wldev, band, &radio);
wifi_get_band_supp_opclass(wldev, band, &num_opclass, supp_opclass);
if (multiband) {
/* Temporary workaround for multiband/single wiphy device */
switch (band) {
case BAND_5:
hwaddr[5] += 1;
break;
case BAND_6:
hwaddr[5] += 2;
break;
default:
break;
}
}
/* Print required information */
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "radio", radioname);
blobmsg_add_string(&bb, "phyname", wldev);
blobmsg_add_macaddr(&bb, "macaddr", hwaddr);
blobmsg_add_string(&bb, "firmware", minfo.fw_data);
blobmsg_add_string(&bb, "vendor_id", minfo.vendor_id);
blobmsg_add_string(&bb, "device_id", minfo.device_id);
blobmsg_add_u8(&bb, "isup", !radio_disabled);
wifi_print_band(&bb, band);
snprintf(std_buf2 + strlen(std_buf2), 31, "%s",
etostr(radio.oper_std, std_buf, sizeof(std_buf), WIFI_NUM_STD, standard_str));
blobmsg_add_string(&bb, "standard", std_buf2);
blobmsg_add_u32(&bb, "num_iface", radio.num_iface);
c = blobmsg_open_array(&bb, "iface");
for (i = 0; i < radio.num_iface; i++) {
d = blobmsg_open_table(&bb, "");
blobmsg_add_string(&bb, "name", radio.iface[i].name);
wifi_print_mode(&bb, radio.iface[i].mode);
blobmsg_close_table(&bb, d);
}
blobmsg_close_array(&bb, c);
blobmsg_add_u32(&bb, "opclass", radio.curr_opclass);
blobmsg_add_u32(&bb, "channel", radio.channel);
blobmsg_add_u32(&bb, "bandwidth", bw_value(radio.curr_bw));
blobmsg_add_u32(&bb, "ccfs0", radio.ccfs0);
blobmsg_add_u32(&bb, "ccfs1", radio.ccfs1);
blobmsg_add_string(&bb, "channel_ext",
radio.extch == EXTCH_NONE ? "none" :
radio.extch == EXTCH_ABOVE ? "above" :
radio.extch == EXTCH_BELOW ? "below" : "auto");
blobmsg_add_u32(&bb, "tx_streams", radio.tx_streams);
blobmsg_add_u32(&bb, "rx_streams", radio.rx_streams);
blobmsg_add_u32(&bb, "noise", radio.noise);
blobmsg_add_string(&bb, "guard_int", guard_interval_str[radio.gi]);
blobmsg_add_u64(&bb, "maxrate", radio.maxrate);
wifi_print_acs_mode(&bb, radio.acs_mode);
blobmsg_add_u8(&bb, "mlo_capable", radio.mlo_capable ? true : false);
c = blobmsg_open_table(&bb, "ap_caps");
wifi_print_radio_caps(&bb, &radio.ap_caps, WIFI_MODE_AP);
blobmsg_close_table(&bb, c);
c = blobmsg_open_table(&bb, "sta_caps");
wifi_print_radio_caps(&bb, &radio.sta_caps, WIFI_MODE_STA);
blobmsg_close_table(&bb, c);
wifi_print_radio_supp_bands(&bb, radio.supp_band);
wifi_print_radio_supp_std(&bb, radio.supp_std);
wifi_print_radio_supp_bw(&bb, radio.supp_bw);
wifi_print_radio_cac_methods(&bb, radio.cac_methods);
c = blobmsg_open_array(&bb, "supp_rates");
for (i = 0; i < 32 && radio.supp_rates[i] != 0; i++) {
blobmsg_add_u32(&bb, "", radio.supp_rates[i]);
}
blobmsg_close_array(&bb, c);
c = blobmsg_open_array(&bb, "basic_rates");
for (i = 0; i < 32 && radio.basic_rates[i] != 0; i++) {
blobmsg_add_u32(&bb, "", radio.basic_rates[i]);
}
blobmsg_close_array(&bb, c);
c = blobmsg_open_array(&bb, "supp_channels");
#if 0
for (i = 0; i < 32 && radio.supp_channels[i] != 0; i++) {
blobmsg_add_u32(&bb, "", radio.supp_channels[i]);
}
#else
for (i = 0; i < num_opclass; i++) {
d = blobmsg_open_table(&bb, "");
blobmsg_add_u32(&bb, "opclass", supp_opclass[i].g_opclass);
blobmsg_add_u32(&bb, "bandwidth", bw_value(supp_opclass[i].bw));
blobmsg_add_u32(&bb, "txpower", supp_opclass[i].opchannel.txpower);
dd = blobmsg_open_array(&bb, "channels");
for (j = 0; j < supp_opclass[i].opchannel.num; j++)
blobmsg_add_u32(&bb, "", supp_opclass[i].opchannel.ch[j].channel);
blobmsg_close_array(&bb, dd);
blobmsg_close_table(&bb, d);
}
#endif
blobmsg_close_array(&bb, c);
if (radio.acs_capable) {
c = blobmsg_open_table(&bb, "autochannel");
blobmsg_add_u8(&bb, "enabled", radio.acs_enabled ? true : false);
blobmsg_add_u32(&bb, "interval", radio.acs_interval);
blobmsg_close_table(&bb, c);
}
blobmsg_add_u32(&bb, "txpower_dbm", radio.txpower_dbm);
blobmsg_add_u32(&bb, "txpower", radio.txpower);
blobmsg_add_u8(&bb, "dot11h_capable", radio.dot11h_capable ? true : false);
blobmsg_add_u8(&bb, "dot11h_enabled", radio.dot11h_enabled ? true : false);
blobmsg_add_string(&bb, "regdomain", radio.regdomain);
blobmsg_add_u32(&bb, "beacon_int", radio.beacon_int);
blobmsg_add_u32(&bb, "dtim_period", radio.dtim_period);
blobmsg_add_string(&bb, "preamble",
radio.pr == SHORT_PREAMBLE ? "short" :
radio.pr == LONG_PREAMBLE ? "long" : "auto");
blobmsg_add_u32(&bb, "short_retry_limit", radio.srl);
blobmsg_add_u32(&bb, "long_retry_limit", radio.lrl);
blobmsg_add_u32(&bb, "frag_threshold", radio.frag);
blobmsg_add_u32(&bb, "rts_threshold", radio.rts);
blobmsg_add_u8(&bb, "aggr_enabled", radio.aggr_enable ? true : false);
c = blobmsg_open_table(&bb, "stats");
wifi_print_radio_stats(&bb, &radio.stats);
blobmsg_close_table(&bb, c);
wifi_print_radio_diagnostics(&bb, &radio.diag);
blobmsg_add_u32(&bb, "max_akms", radio.max_akms);
wifi_print_radio_akms(&bb, "ap_akms", radio.ap_akms, radio.num_ap_akms);
wifi_print_radio_akms(&bb, "sta_akms", radio.sta_akms, radio.num_sta_akms);
blobmsg_add_u32(&bb, "max_iface_ap", radio.max_iface_ap);
blobmsg_add_u32(&bb, "max_iface_sta", radio.max_iface_sta);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
int wl_radio_stats(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const char *ifname;
const char *radioname;
enum wifi_band band;
struct wifi_radio_stats s;
struct wifi_radio_diagnostic diag = {};
struct blob_buf bb = {0};
bool multiband = false;
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
radioname = ubus_objname_to_ifname(obj);
UNUSED(radioname);
ifname = wdev->phy;
band = wdev->band;
wifi_radio_is_multiband(ifname, &multiband);
if (multiband)
ret = wifi_radio_get_band_stats(ifname, band, &s);
else
ret = wifi_radio_get_stats(ifname, &s);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
wifi_get_diagnostic(ifname, band, &diag);
blob_buf_init(&bb, 0);
wifi_print_radio_stats(&bb, &s);
wifi_print_radio_diagnostics(&bb, &diag);
blobmsg_add_u32(&bb, "noise", s.noise);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
static int wl_dump_beacon(struct ubus_context *ctx, struct ubus_request_data *req,
struct blob_buf *bb, const char *ifname, enum wifi_band band,
const char *aname)
{
char iestr[513] = {0}; /* 2 x 256 + 1 */
int len = 2400;
uint8_t *bcn;
uint8_t *ie;
int ret;
void *a;
bcn = calloc(1, len * sizeof(uint8_t));
if (!bcn)
return UBUS_STATUS_UNKNOWN_ERROR;
ret = wifi_get_beacon_ies_band(ifname, band, bcn, &len);
if (ret) {
free(bcn);
return UBUS_STATUS_UNKNOWN_ERROR;
}
a = blobmsg_open_array(bb, aname ? aname : radio_band_to_band(band));
wifi_foreach_ie(ie, bcn, len) {
btostr(ie, ie[1] + 2, iestr);
blobmsg_add_string(bb, "", iestr);
memset(iestr, 0, sizeof(iestr));
}
blobmsg_close_array(bb, a);
free(bcn);
return 0;
}
static int wl_dump_beacon_mld(struct ubus_context *ctx, struct ubus_request_data *req,
struct blob_buf *bb, const char *ifname, enum wifi_band band)
{
char iestr[513] = {0}; /* 2 x 256 + 1 */
int len = 2400;
uint8_t *bcn;
uint8_t *ie;
int ret;
void *t, *a;
bcn = calloc(1, len * sizeof(uint8_t));
if (!bcn)
return UBUS_STATUS_UNKNOWN_ERROR;
ret = wifi_get_beacon_ies_band(ifname, band, bcn, &len);
if (ret) {
free(bcn);
return UBUS_STATUS_UNKNOWN_ERROR;
}
t = blobmsg_open_table(bb, "");
blobmsg_add_string(bb, "band", radio_band_to_band_ghz(band));
a = blobmsg_open_array(bb, "ies");
wifi_foreach_ie(ie, bcn, len) {
btostr(ie, ie[1] + 2, iestr);
blobmsg_add_string(bb, "", iestr);
memset(iestr, 0, sizeof(iestr));
}
blobmsg_close_array(bb, a);
blobmsg_close_table(bb, t);
free(bcn);
return 0;
}
int wl_ap_dump_beacon(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_iface *iface = (struct wifimngr_iface *)wo->priv;
struct blob_buf bb = {0};
const char *ifname;
int ret;
if (strlen(iface->mld_netdev) && if_nametoindex(iface->mld_netdev) != 0)
ifname = iface->mld_netdev;
else
ifname = ubus_ap_to_ifname(obj);
blob_buf_init(&bb, 0);
ret = wl_dump_beacon(ctx, req, &bb, ifname, iface->band, "beacon-ies");
if (!ret)
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return ret;
}
int wl_apmld_dump_beacon(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__DUMP_BEACON_MAX];
enum wifi_band band = BAND_ANY;
struct wifi_mlo_link link[4];
int num = ARRAY_SIZE(link);
struct blob_buf bb = {0};
const char *ifname;
int ret = 0;
void *a;
int i;
blobmsg_parse(dump_beacon_policy, __DUMP_BEACON_MAX, tb, blob_data(msg),
blob_len(msg));
ifname = ubus_objname_to_ifname(obj);
if (tb[DUMP_BEACON_BAND])
band = band_to_radio_band(blobmsg_get_u32(tb[DUMP_BEACON_BAND]));
if (wifi_get_mlo_links(ifname, band, link, &num))
return UBUS_STATUS_UNKNOWN_ERROR;
if (!num)
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
if (num == 1) {
/* Single band - use same format as wifi.ap.X */
ret = wl_dump_beacon(ctx, req, &bb, ifname, link[0].band, "beacon-ies");
} else {
/* Multiple bands - use new format with band field */
a = blobmsg_open_array(&bb, "beacon-ies");
for (i = 0; i < num; i++)
ret |= wl_dump_beacon_mld(ctx, req, &bb, ifname, link[i].band);
blobmsg_close_array(&bb, a);
}
if (!ret)
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return ret;
}
static void wl_ap_wmm_status(struct blob_buf *bb, struct wifi_ap_wmm_ac ac[])
{
const char *ac_str[] = { "BE", "BK", "VI", "VO" };
void *a, *t;
int i;
a = blobmsg_open_array(bb, "wmm_params");
for (i = 0; i < WIFI_NUM_AC; i++) {
t = blobmsg_open_table(bb, "");
blobmsg_add_string(bb, "ac", ac_str[ac[i].ac]);
blobmsg_add_u32(bb, "aifsn", ac[i].aifsn);
blobmsg_add_u32(bb, "cwmin", ac[i].cwmin);
blobmsg_add_u32(bb, "cwmax", ac[i].cwmax);
blobmsg_add_u32(bb, "txop", ac[i].txop);
blobmsg_close_table(bb, t);
}
blobmsg_close_array(bb, a);
}
static void wl_dump_supp_security(struct blob_buf *bb, uint32_t sec)
{
void *a;
a = blobmsg_open_array(bb, "supp_security");
if (!!(sec & BIT(WIFI_SECURITY_NONE)))
blobmsg_add_string(bb, "", "NONE");
if (!!(sec & BIT(WIFI_SECURITY_WEP64)))
blobmsg_add_string(bb, "", "WEP64");
if (!!(sec & BIT(WIFI_SECURITY_WEP128)))
blobmsg_add_string(bb, "", "WEP128");
if (!!(sec & BIT(WIFI_SECURITY_WPAPSK)))
blobmsg_add_string(bb, "", "WPAPSK");
if (!!(sec & BIT(WIFI_SECURITY_WPA2PSK)))
blobmsg_add_string(bb, "", "WPA2PSK");
if (!!(sec & BIT(WIFI_SECURITY_WPA3PSK)))
blobmsg_add_string(bb, "", "WPA3PSK");
if (!!(sec & BIT(WIFI_SECURITY_WPAPSK)) && (sec & BIT(WIFI_SECURITY_WPA2PSK)))
blobmsg_add_string(bb, "", "WPAPSK+WPA2PSK");
if (!!(sec & BIT(WIFI_SECURITY_WPA2PSK)) && (sec & BIT(WIFI_SECURITY_WPA3PSK)))
blobmsg_add_string(bb, "", "WPA2PSK+WPA3PSK");
if (!!(sec & BIT(WIFI_SECURITY_WPA)))
blobmsg_add_string(bb, "", "WPA");
if (!!(sec & BIT(WIFI_SECURITY_WPA2)))
blobmsg_add_string(bb, "", "WPA2");
if (!!(sec & BIT(WIFI_SECURITY_WPA3)))
blobmsg_add_string(bb, "", "WPA3");
if (!!(sec & BIT(WIFI_SECURITY_WPA)) && (sec & BIT(WIFI_SECURITY_WPA2)))
blobmsg_add_string(bb, "", "WPA+WPA2");
if (!!(sec & BIT(WIFI_SECURITY_WPA2)) && (sec & BIT(WIFI_SECURITY_WPA3)))
blobmsg_add_string(bb, "", "WPA2+WPA3");
if (!!(sec & BIT(WIFI_SECURITY_FT_WPA3PSK)))
blobmsg_add_string(bb, "", "WPA3PSK+FT");
if (!!(sec & BIT(WIFI_SECURITY_FT_WPA2PSK)))
blobmsg_add_string(bb, "", "WPA2PSK+FT");
blobmsg_close_array(bb, a);
}
int wl_ap_help(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return wl_help_command(ctx, obj, req, method, msg, WIFI_AP_OBJECT);
}
int wl_ap_status(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_iface *iface = (struct wifimngr_iface *)wo->priv;
struct blob_buf bb = {0};
struct wifi_ap ap = {0};
struct wifi_bss *bss;
ifopstatus_t opstatus;
ifstatus_t ifs = 0;
char std_buf2[32] = "802.11";
char std_buf[32] = {0};
char sec_str[128] = {0};
char enc_buf[32] = {0};
const char *ifname;
int ret;
if (strlen(iface->mld_netdev) && if_nametoindex(iface->mld_netdev) != 0)
ifname = iface->mld_netdev;
else
ifname = ubus_ap_to_ifname(obj);
ret = wifi_ap_info_band(ifname, iface->band, &ap);
if (ret) {
fprintf(stderr, "%s: error %d\n", __func__, ret);
return -1;
}
bss = &ap.bss;
//wifi_get_security(ifname, &akm, &enc, &g_enc); // TODO: new
wifi_security_str(ap.bss.security, sec_str, sizeof(sec_str));
wifi_get_ifstatus(ifname, &ifs);
wifi_get_ifoperstatus(ifname, &opstatus);
if (opstatus > IF_OPER_UP) {
opstatus = IF_OPER_UNKNOWN;
}
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "ifname", iface->iface);
if (strlen(iface->mld_netdev)) {
blobmsg_add_string(&bb, "mld", iface->mld);
blobmsg_add_string(&bb, "mld_ifname", iface->mld_netdev);
}
if (iface->mlo)
blobmsg_add_u32(&bb, "linkid", bss->mlo_link_id);
blobmsg_add_u8(&bb, "enabled", ap.enabled ? true : false);
blobmsg_add_string(&bb, "status", ifstatus_str(ifs));
//blobmsg_add_string(&bb, "operstate", operstate_str[opstatus]);
blobmsg_add_string(&bb, "ssid", (char *)bss->ssid);
blobmsg_add_macaddr(&bb, "bssid", bss->bssid);
blobmsg_add_u32(&bb, "beacon_int", bss->beacon_int);
blobmsg_add_u32(&bb, "dtim_period", bss->dtim_period);
blobmsg_add_string(&bb, "security", sec_str); // TODO: new
blobmsg_add_string(&bb, "encryption", etostr(ap.bss.rsn.pair_ciphers,
enc_buf, sizeof(enc_buf), 14, wifi_cipherstr));
wifi_print_band(&bb, bss->band);
blobmsg_add_u32(&bb, "channel", bss->channel);
blobmsg_add_u32(&bb, "bandwidth", bw_value(bss->curr_bw));
blobmsg_add_u32(&bb, "ccfs0", bss->ccfs0);
blobmsg_add_u32(&bb, "ccfs1", bss->ccfs1);
blobmsg_add_u32(&bb, "puncture_bitmap", (int)BUF_GET_LE16(bss->puncture));
if (!bss->bss_color_disabled)
blobmsg_add_u32(&bb, "bss_color", bss->bss_color);
//blobmsg_add_u64(&bb, "rate", rate);
snprintf(std_buf2 + strlen(std_buf2), 31, "%s",
etostr(ap.bss.oper_std, std_buf, sizeof(std_buf), WIFI_NUM_STD, standard_str));
blobmsg_add_string(&bb, "standard", std_buf2);
blobmsg_add_u32(&bb, "num_stations", ap.bss.load.sta_count);
blobmsg_add_u32(&bb, "max_stations", ap.assoclist_max);
blobmsg_add_u32(&bb, "utilization", ap.bss.load.utilization);
blobmsg_add_u32(&bb, "adm_capacity", ap.bss.load.available);
blobmsg_add_u8(&bb, "hidden", !ap.ssid_advertised ? true : false);
blobmsg_add_string(&bb, "eml_mode", ap.bss.mlo_eml_mode == WIFI_EML_MODE_NONE ? "none" :
ap.bss.mlo_eml_mode == WIFI_EML_MODE_EMLMR ? "EMLMR" : "EMLSR");
wl_dump_supp_security(&bb, ap.sec.supp_modes);
wl_dump_capabilities(bss->band, &bb, &bss->caps, bss->cbitmap, sizeof(bss->cbitmap));
wl_ap_wmm_status(&bb, ap.ac);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
static void wl_ap_wmm_stats(struct blob_buf *bb, struct wifi_ap_wmm_ac ac[])
{
const char *ac_str[] = { "BE", "BK", "VI", "VO" };
void *a, *t;
int i;
a = blobmsg_open_array(bb, "wmm_stats");
for (i = 0; i < WIFI_NUM_AC; i++) {
t = blobmsg_open_table(bb, "");
blobmsg_add_string(bb, "ac", ac_str[ac[i].ac]);
blobmsg_add_u64(bb, "tx_bytes", ac[i].stats.tx_bytes);
blobmsg_add_u32(bb, "tx_packets", ac[i].stats.tx_pkts);
blobmsg_add_u32(bb, "tx_error_packets", ac[i].stats.tx_err_pkts);
blobmsg_add_u32(bb, "tx_retrans_packets", ac[i].stats.tx_rtx_pkts);
blobmsg_add_u64(bb, "rx_bytes", ac[i].stats.rx_bytes);
blobmsg_add_u32(bb, "rx_packets", ac[i].stats.rx_pkts);
blobmsg_add_u32(bb, "rx_error_packets", ac[i].stats.rx_err_pkts);
blobmsg_close_table(bb, t);
}
blobmsg_close_array(bb, a);
}
static void print_apstats(struct blob_buf *bb, struct wifi_ap_stats *s)
{
blobmsg_add_u64(bb, "tx_bytes", s->tx_bytes);
blobmsg_add_u64(bb, "tx_packets", s->tx_pkts);
blobmsg_add_u64(bb, "tx_unicast_packets", s->tx_ucast_pkts);
blobmsg_add_u64(bb, "tx_multicast_packets", s->tx_mcast_pkts);
blobmsg_add_u64(bb, "tx_broadcast_packets", s->tx_bcast_pkts);
blobmsg_add_u64(bb, "tx_error_packets", s->tx_err_pkts);
blobmsg_add_u64(bb, "tx_retrans_packets", s->tx_rtx_pkts);
blobmsg_add_u64(bb, "tx_retrans_fail_packets", s->tx_rtx_fail_pkts);
blobmsg_add_u64(bb, "tx_retry_packets", s->tx_retry_pkts);
blobmsg_add_u64(bb, "tx_multi_retry_packets", s->tx_mretry_pkts);
blobmsg_add_u64(bb, "tx_dropped_packets", s->tx_dropped_pkts);
blobmsg_add_u64(bb, "ack_fail_packets", s->ack_fail_pkts);
blobmsg_add_u64(bb, "aggregate_packets", s->aggr_pkts);
blobmsg_add_u64(bb, "rx_bytes", s->rx_bytes);
blobmsg_add_u64(bb, "rx_packets", s->rx_pkts);
blobmsg_add_u64(bb, "rx_unicast_packets", s->rx_ucast_pkts);
blobmsg_add_u64(bb, "rx_multicast_packets", s->rx_mcast_pkts);
blobmsg_add_u64(bb, "rx_broadcast_packets", s->rx_bcast_pkts);
blobmsg_add_u64(bb, "rx_error_packets", s->rx_err_pkts);
blobmsg_add_u64(bb, "rx_dropped_packets", s->rx_dropped_pkts);
blobmsg_add_u64(bb, "rx_unknown_packets", s->rx_unknown_pkts);
}
static void print_aploads(struct blob_buf *bb, struct wifi_ap_load *load)
{
void *t;
t = blobmsg_open_table(bb, "load");
blobmsg_add_u32(bb, "utilization", load->utilization);
blobmsg_add_u32(bb, "sta_count", load->sta_count);
blobmsg_close_table(bb, t);
}
int wl_ap_stats(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_ap_stats s;
struct wifi_ap ap;
const char *ifname;
struct blob_buf bb;
int ret;
ifname = ubus_ap_to_ifname(obj);
ret = wifi_ap_get_stats(ifname, &s);
if (ret != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
memset(&ap, 0, sizeof(struct wifi_ap));
ret = wifi_ap_info(ifname, &ap);
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
print_apstats(&bb, &s);
wl_ap_wmm_stats(&bb, ap.ac);
print_aploads(&bb, &ap.bss.load);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
int wl_assoclist(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_iface *iface = (struct wifimngr_iface *)wo->priv;
unsigned char stas[768] = {0}; /* max 128 per interface */
struct blob_buf bb = {0};
const char *ifname;
int nr = 128;
void *a;
int i;
if (strlen(iface->mld_netdev) && if_nametoindex(iface->mld_netdev) != 0)
ifname = iface->mld_netdev;
else
ifname = ubus_ap_to_ifname(obj);
if (wifi_get_assoclist_band(ifname, iface->band, stas, &nr) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
a = blobmsg_open_array(&bb, "assoclist");
for (i = 0; i < nr; i++) {
blobmsg_add_macaddr(&bb, "", &stas[i*6]);
}
blobmsg_close_array(&bb, a);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
int wl_blocked_stas_print(struct blob_buf *bb, const char *ifname,
enum wifi_band band, const char *array_name)
{
unsigned char stas[768] = {0}; /* max 128 per interface */
int nr = 128;
void *a;
int i;
if (wifi_get_blocked_stas(ifname, band, stas, &nr) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
a = blobmsg_open_array(bb, array_name ? array_name : radio_band_to_band(band));
for (i = 0; i < nr; i++) {
blobmsg_add_macaddr(bb, "", &stas[i*6]);
}
blobmsg_close_array(bb, a);
return 0;
}
static int wl_blocked_stas_print_mld(struct blob_buf *bb, const char *ifname,
enum wifi_band band)
{
unsigned char stas[768] = {0}; /* max 128 per interface */
int nr = 128;
void *t, *a;
int i;
if (wifi_get_blocked_stas(ifname, band, stas, &nr) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
t = blobmsg_open_table(bb, "");
blobmsg_add_string(bb, "band", radio_band_to_band_ghz(band));
a = blobmsg_open_array(bb, "stas");
for (i = 0; i < nr; i++) {
blobmsg_add_macaddr(bb, "", &stas[i*6]);
}
blobmsg_close_array(bb, a);
blobmsg_close_table(bb, t);
return 0;
}
int wl_blocked_stas(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_buf bb = {0};
const char *ifname;
int ret;
ifname = ubus_ap_to_ifname(obj);
blob_buf_init(&bb, 0);
ret = wl_blocked_stas_print(&bb, ifname, BAND_ANY, "blocked");
if (!ret)
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return ret;
}
int wl_apmld_blocked_stas(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__APMLD_BLOCKED_MACLIST_MAX];
struct blob_buf bb = {0};
enum wifi_band band = BAND_ANY;
struct wifi_mlo_link link[4];
int num = ARRAY_SIZE(link);
const char *ifname;
int ret = 0;
void *a;
int i;
blobmsg_parse(apmld_blocked_maclist_policy, __APMLD_BLOCKED_MACLIST_MAX, tb, blob_data(msg),
blob_len(msg));
if (tb[APMLD_BLOCKED_MACLIST_BAND])
band = band_to_radio_band(blobmsg_get_u32(tb[APMLD_BLOCKED_MACLIST_BAND]));
ifname = ubus_objname_to_ifname(obj);
if (wifi_get_mlo_links(ifname, band, link, &num))
return UBUS_STATUS_UNKNOWN_ERROR;
if (!num)
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
if (num == 1) {
/* Single band - use same format as wifi.ap.X */
ret = wl_blocked_stas_print(&bb, ifname, link[0].band, "blocked");
} else {
/* Multiple bands - use new format with band field */
a = blobmsg_open_array(&bb, "blocked");
for (i = 0; i < num; i++)
ret |= wl_blocked_stas_print_mld(&bb, ifname, link[i].band);
blobmsg_close_array(&bb, a);
}
if (!ret)
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return ret;
}
int wl_block_sta(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__BLOCK_STATION_MAX];
uint8_t sta[6] = {0};
const char *ifname;
int block;
int ret;
blobmsg_parse(block_station_policy, __BLOCK_STATION_MAX, tb, blob_data(msg),
blob_len(msg));
ifname = ubus_ap_to_ifname(obj);
if (!tb[BLOCK_STATION_MACADDR] ||
!tb[BLOCK_STATION_BLOCK])
return UBUS_STATUS_INVALID_ARGUMENT;
if (hwaddr_aton(blobmsg_get_string(tb[BLOCK_STATION_MACADDR]), sta) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
block = blobmsg_get_u32(tb[BLOCK_STATION_BLOCK]);
ret = wifi_block_sta(ifname, BAND_ANY, sta, block);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return 0;
}
int wl_apmld_block_sta(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__APMLD_BLOCK_STATION_MAX];
enum wifi_band band = BAND_ANY;
uint8_t sta[6] = {0};
const char *ifname;
int block;
int ret;
blobmsg_parse(apmld_block_station_policy, __APMLD_BLOCK_STATION_MAX, tb,
blob_data(msg), blob_len(msg));
ifname = ubus_objname_to_ifname(obj);
if (!tb[APMLD_BLOCK_STATION_MACADDR] ||
!tb[APMLD_BLOCK_STATION_BLOCK])
return UBUS_STATUS_INVALID_ARGUMENT;
if (tb[APMLD_BLOCK_STATION_BAND])
band = band_to_radio_band(blobmsg_get_u32(tb[APMLD_BLOCK_STATION_BAND]));
if (hwaddr_aton(blobmsg_get_string(tb[APMLD_BLOCK_STATION_MACADDR]), sta) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
block = blobmsg_get_u32(tb[APMLD_BLOCK_STATION_BLOCK]);
ret = wifi_block_sta(ifname, band, sta, block);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return 0;
}
int ap_chan_switch(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_iface *iface = (struct wifimngr_iface *)wo->priv;
struct blob_attr *tb[__CHAN_SWITCH_MAX];
struct chan_switch_param param = {
.count = 5, /* after these many beacons */
.blocktx = 1,
};
const char *ifname;
int ret;
blobmsg_parse(chan_switch_policy, __CHAN_SWITCH_MAX, tb, blob_data(msg),
blob_len(msg));
if (strlen(iface->mld_netdev) && if_nametoindex(iface->mld_netdev) != 0)
ifname = iface->mld_netdev;
else
ifname = ubus_ap_to_ifname(obj);
if (!tb[CHAN_SWITCH_FREQ] && !tb[CHAN_SWITCH_CHANNEL])
return UBUS_STATUS_INVALID_ARGUMENT;
if (tb[CHAN_SWITCH_FREQ]) {
param.freq = blobmsg_get_u32(tb[CHAN_SWITCH_FREQ]);
} else {
uint32_t channel = blobmsg_get_u32(tb[CHAN_SWITCH_CHANNEL]);
param.freq = wifi_channel_to_freq_ex(channel, iface->band);
if (param.freq < 0)
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (tb[CHAN_SWITCH_COUNT])
param.count = blobmsg_get_u32(tb[CHAN_SWITCH_COUNT]);
if (tb[CHAN_SWITCH_SEC_CHAN_OFFSET])
param.sec_chan_offset = blobmsg_get_u32(tb[CHAN_SWITCH_SEC_CHAN_OFFSET]);
if (tb[CHAN_SWITCH_CF1])
param.cf1 = blobmsg_get_u32(tb[CHAN_SWITCH_CF1]);
if (tb[CHAN_SWITCH_CF2])
param.cf2 = blobmsg_get_u32(tb[CHAN_SWITCH_CF2]);
if (tb[CHAN_SWITCH_BW])
param.bandwidth = blobmsg_get_u32(tb[CHAN_SWITCH_BW]);
if (tb[CHAN_SWITCH_BLOCK_TX])
param.blocktx = blobmsg_get_bool(tb[CHAN_SWITCH_BLOCK_TX]);
if (tb[CHAN_SWITCH_HT])
param.ht = blobmsg_get_bool(tb[CHAN_SWITCH_HT]);
if (tb[CHAN_SWITCH_VHT])
param.vht = blobmsg_get_bool(tb[CHAN_SWITCH_VHT]);
if (tb[CHAN_SWITCH_HE])
param.he = blobmsg_get_bool(tb[CHAN_SWITCH_HE]);
if (tb[CHAN_SWITCH_EHT])
param.eht = blobmsg_get_bool(tb[CHAN_SWITCH_EHT]);
if (tb[CHAN_SWITCH_AUTO_HT])
param.auto_ht = blobmsg_get_bool(tb[CHAN_SWITCH_AUTO_HT]);
ret = wifi_chan_switch(ifname, &param);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return 0;
}
static int wl_load_sta_ratings_lib(const char *filename)
{
char *sofile = getenv(filename);
int ret = 0;
if (!sofile)
return -EINVAL;
if (libsta_ratings)
return 0;
libsta_ratings = dlopen(sofile, RTLD_NOW | RTLD_GLOBAL);
if (!libsta_ratings) {
return errno;
}
sta_ratings_calc = dlsym(libsta_ratings, "sta_ratings_calculate");
if (!sta_ratings_calc) {
ret = -1;
goto out_error;
}
sta_ratings_free_lib = dlsym(libsta_ratings, "sta_ratings_free_lib");
if (!sta_ratings_free_lib) {
ret = -1;
goto out_error;
}
return 0;
out_error:
dlclose(libsta_ratings);
return ret;
}
static void wl_unload_sta_ratings_lib(void)
{
if (libsta_ratings) {
if (sta_ratings_free_lib)
sta_ratings_free_lib(NULL);
sta_ratings_calc = NULL;
sta_ratings_free_lib = NULL;
dlclose(libsta_ratings);
libsta_ratings = NULL;
}
}
static int wl_dump_stations(struct blob_buf *bb, const char *ifname,
enum wifi_band band, unsigned char *macaddr,
struct wifi_mlo_link *link)
{
void *t, *s, *a;
int i;
struct wifi_sta sx;
enum wifi_bw bw;
int noise, snr;
unsigned char stas[768] = {0};
int nr = 64;
if (wifi_get_assoclist_band(ifname, band, stas, &nr) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
a = blobmsg_open_array(bb, "stations");
for (i = 0; i < nr; i++) {
uint32_t channel = 0;
char std_buf2[32] = "802.11";
char std_buf[32] = {0};
unsigned char *sta = &stas[i*6];
float rating = -1.0;
if (macaddr && !hwaddr_is_zero(macaddr)
&& memcmp(macaddr, sta, 6))
continue;
memset(&sx, 0, sizeof(sx));
if (wifi_get_sta_info_band(ifname, band, sta, &sx))
continue;
if (sta_ratings_calc)
rating = sta_ratings_calc(NULL, ifname, &sx);
wifi_get_band_channel(ifname, band, &channel, &bw);
if (!sx.noise_avg)
wifi_get_band_noise(ifname, band, &noise);
else
noise = sx.noise_avg;
snr = sx.rssi_avg - noise;
t = blobmsg_open_table(bb, "");
blobmsg_add_macaddr(bb, "macaddr", sx.macaddr);
if (sta_ratings_calc)
blobmsg_add_double(bb, "rating", rating);
blobmsg_add_macaddr(bb, "bssid", sx.bssid);
blobmsg_add_string(bb, "wdev", ifname);
if (link) {
blobmsg_add_u32(bb, "link_id", link->id);
//wifi_print_band(&bb, link->band);
//blobmsg_add_u32(&bb, "link_channel", link->channel);
//blobmsg_add_u32(&bb, "link_bandwidth", bw_value(link->bandwidth));
}
snprintf(std_buf2 + strlen(std_buf2), 31, "%s",
etostr(sx.oper_std, std_buf, sizeof(std_buf), WIFI_NUM_STD, standard_str));
blobmsg_add_string(bb, "standard", std_buf2);
blobmsg_add_u32(bb, "channel", channel);
blobmsg_add_u32(bb, "frequency", wifi_channel_to_freq_ex(channel, band));
blobmsg_add_u32(bb, "rssi", sx.rssi_avg);
blobmsg_add_u32(bb, "noise", noise);
blobmsg_add_u32(bb, "snr", snr);
blobmsg_add_u32(bb, "idle", sx.idle_time);
blobmsg_add_u64(bb, "in_network", sx.conn_time);
blobmsg_add_u32(bb, "tx_airtime", sx.tx_airtime);
blobmsg_add_u32(bb, "rx_airtime", sx.rx_airtime);
blobmsg_add_u32(bb, "airtime", sx.airtime);
blobmsg_add_u32(bb, "maxrate", sx.maxrate);
blobmsg_add_u32(bb, "nss", sx.rate.m.nss);
blobmsg_add_u32(bb, "bandwidth", sx.rate.m.bw);
blobmsg_add_u32(bb, "est_rx_thput", sx.est_rx_thput);
blobmsg_add_u32(bb, "est_tx_thput", sx.est_tx_thput);
s = blobmsg_open_table(bb, "status");
blobmsg_add_u8(bb, "wmm",
wifi_status_isset(sx.sbitmap, WIFI_STATUS_WMM) ? true : false);
blobmsg_add_u8(bb, "ps",
wifi_status_isset(sx.sbitmap, WIFI_STATUS_PS) ? true : false);
blobmsg_close_table(bb, s);
wl_dump_capabilities(band, bb, &sx.caps, sx.cbitmap, sizeof(sx.cbitmap));
wl_print_sta_stats(bb, &sx.stats);
wl_print_rate(bb, "tx_rate_latest", &sx.tx_rate);
wl_print_rate(bb, "rx_rate_latest", &sx.rx_rate);
wl_print_rssi_chains(bb, "rssi_per_antenna", sx.rssi);
blobmsg_close_table(bb, t);
}
blobmsg_close_array(bb, a);
return 0;
}
int wl_stations(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__STAINFO_MAX];
char sta_macstr[18] = {0};
uint8_t sta[6] = {0};
const char *ifname;
struct blob_buf bb;
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_iface *iface = (struct wifimngr_iface *)wo->priv;
memset(&bb, 0, sizeof(bb));
blobmsg_parse(stainfo_policy, __STAINFO_MAX, tb, blob_data(msg), blob_len(msg));
ifname = ubus_ap_to_ifname(obj);
if (strlen(iface->mld_netdev) && if_nametoindex(iface->mld_netdev) != 0)
ifname = iface->mld_netdev;
if ((tb[STAINFO_MACADDR])) {
strncpy(sta_macstr, blobmsg_data(tb[STAINFO_MACADDR]),
sizeof(sta_macstr)-1);
if (hwaddr_aton(sta_macstr, sta) == NULL) {
return UBUS_STATUS_INVALID_ARGUMENT;
}
}
blob_buf_init(&bb, 0);
ret = wl_dump_stations(&bb, ifname, iface->band, sta, NULL);
if (!ret)
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return ret;
}
int wl_sta_ratings(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_iface *iface = (struct wifimngr_iface *)wo->priv;
struct blob_attr *tb[__STAINFO_MAX];
uint8_t sta_macaddr[6] = {0};
int ret = UBUS_STATUS_OK;
uint8_t stas[768] = {0};
struct wifi_ap ap = {0};
char macstr[18] = {0};
const char *ifname;
struct blob_buf bb;
int num_stas = 64;
void *a;
memset(&bb, 0, sizeof(bb));
blobmsg_parse(stainfo_policy, __STAINFO_MAX, tb, blob_data(msg), blob_len(msg));
ifname = ubus_ap_to_ifname(obj);
if (strlen(iface->mld_netdev) && if_nametoindex(iface->mld_netdev) != 0)
ifname = iface->mld_netdev;
if ((tb[STAINFO_MACADDR])) {
strncpy(macstr, blobmsg_data(tb[STAINFO_MACADDR]), sizeof(macstr) - 1);
if (hwaddr_aton(macstr, sta_macaddr) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
}
ret = wifi_get_assoclist_band(ifname, iface->band, stas, &num_stas);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
ret = wifi_ap_info_band(ifname, iface->band, &ap);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
a = blobmsg_open_array(&bb, "sta-ratings");
for (int i = 0; i < num_stas; i++) {
uint8_t *sta = &stas[i*6];
struct wifi_sta sx = {0};
float rating = -1.0;
void *t;
if (!hwaddr_is_zero(sta_macaddr) && memcmp(sta_macaddr, sta, 6))
continue;
if (wifi_get_sta_info_band(ifname, iface->band, sta, &sx))
continue;
if (sta_ratings_calc)
rating = sta_ratings_calc(NULL, ifname, &sx);
t = blobmsg_open_table(&bb, "");
blobmsg_add_macaddr(&bb, "macaddr", sx.macaddr);
if (sta_ratings_calc)
blobmsg_add_double(&bb, "rating", rating);
blobmsg_close_table(&bb, t);
}
blobmsg_close_array(&bb, a);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return ret;
}
int wl_scan(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__SCAN_MAX];
struct scan_param p = {0};
const char *ifname;
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
blobmsg_parse(wl_scan_policy, __SCAN_MAX, tb, blob_data(msg),
blob_len(msg));
ifname = wdev->phy;
if (tb[SCAN_SSID])
strncpy(p.ssid, blobmsg_data(tb[SCAN_SSID]), 32);
if (tb[SCAN_CHANNEL] && tb[SCAN_OPCLASS]) {
wifimngr_err("%s(): Either opclass or channel allowed\n",
__func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (tb[SCAN_CHANNEL])
p.channel = blobmsg_get_u32(tb[SCAN_CHANNEL]);
if (tb[SCAN_OPCLASS])
p.opclass = blobmsg_get_u32(tb[SCAN_OPCLASS]);
ret = wifi_scan(ifname, &p);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
#define UBUS_STATUS_BUSY 16
int wl_scan_ex(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__SCAN_EX_MAX];
struct scan_param_ex sp = {0};
const char *radio;
const char *country = NULL;
char alpha2[3] = {0};
uint32_t opclasses[128];
uint32_t channels[128];
int op_idx = 0, ch_idx = 0, ret;
enum wifi_band band = BAND_2;
const char *radioname;
bool multiband = false;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
int m, idx = 0;
blobmsg_parse(wl_scan_ex_policy, __SCAN_EX_MAX, tb, blob_data(msg),
blob_len(msg));
radioname = ubus_objname_to_ifname(obj);
UNUSED(radioname);
radio = wdev->phy;
band = wdev->band;
/* Get country from driver first, fallback to cached UCI value */
if (!wifi_get_country(radio, alpha2))
country = alpha2;
else if (wdev->country[0] != '\0')
country = wdev->country;
else
country = "";
/* example calls:
* ubus call wifi.radio.wl0 scan_ex '{"ssid":["iopsysWrt-44D4376AF4C0"]}'
* ubus call wifi.radio.wl0 scan_ex '{"channel":[36]}'
* ubus call wifi.radio.wl0 scan_ex '{"opclass":[115]}'
* ubus call wifi.radio.wl0 scan_ex '{"opclass":[115, 118], "channel":[36, 40, 52]}'
*/
/* scan measurement duration */
if (tb[SCAN_EX_DURATION]) {
sp.duration = blobmsg_get_u16(tb[SCAN_EX_DURATION]);
}
/* array of ssids */
if (tb[SCAN_EX_SSID]) {
struct blob_attr *attr;
int rem, i = 0;
sp.flag |= WIFI_SCAN_REQ_SSID;
sp.num_ssid = blobmsg_check_array(
tb[SCAN_EX_SSID], BLOBMSG_TYPE_STRING);
wifimngr_dbg("scan_ex: num_ssid = %d\n", sp.num_ssid);
blobmsg_for_each_attr(attr, tb[SCAN_EX_SSID], rem) {
if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING)
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(sp.ssid[i], blobmsg_data(attr),
sizeof(sp.ssid[i]) - 1);
i++;
}
}
/* array of opclasses */
if (tb[SCAN_EX_OPCLASS]) {
struct blob_attr *attr;
struct wifi_opclass supp_opclass[64] = {0};
int num_sup_opclass = ARRAY_SIZE(supp_opclass);
int num_opclass;
uint32_t opclass;
int rem, j;
num_opclass = blobmsg_check_array(
tb[SCAN_EX_OPCLASS], BLOBMSG_TYPE_INT32);
wifi_get_supp_opclass(radio, &num_sup_opclass, supp_opclass);
/* Verify opclasses provided are BW20 and supported by radio */
blobmsg_for_each_attr(attr, tb[SCAN_EX_OPCLASS], rem) {
if (blobmsg_type(attr) != BLOBMSG_TYPE_INT32)
return UBUS_STATUS_INVALID_ARGUMENT;
opclass = blobmsg_get_u32(attr);
for (j = 0; j < num_sup_opclass; j++) {
if (supp_opclass[j].opclass == opclass
&& supp_opclass[j].bw == BW20
&& supp_opclass[j].band == band) {
opclasses[op_idx++] = opclass;
break; /* for */
}
}
}
if (op_idx != num_opclass) {
wifimngr_dbg("scan_ex: opclass unsupported or not 20MHz\n");
return UBUS_STATUS_INVALID_ARGUMENT;
}
}
/* array of channels */
if (tb[SCAN_EX_CHANNEL]) {
struct blob_attr *attr;
uint32_t channel, supp_chan[128] = {0};
int num_sup_chan;
int num_channel;
int rem, k;
num_channel = blobmsg_check_array(
tb[SCAN_EX_CHANNEL], BLOBMSG_TYPE_INT32);
num_sup_chan = ARRAY_SIZE(supp_chan);
wifi_get_supp_channels(radio, supp_chan, &num_sup_chan, country,
/* only allow scanning on current operating band */
band,
/* only allow 20MHz control channels */
BW20);
blobmsg_for_each_attr(attr, tb[SCAN_EX_CHANNEL], rem) {
if (blobmsg_type(attr) != BLOBMSG_TYPE_INT32)
return UBUS_STATUS_INVALID_ARGUMENT;
channel = blobmsg_get_u32(attr);
/* check channel value supported by the radio */
for (k = 0; k < num_sup_chan; k++) {
if (supp_chan[k] == channel) {
channels[ch_idx++] = channel;
break; /* for */
}
}
}
if (ch_idx != num_channel) {
/* channel from the list unsupported, out of band or not a control 20MHz */
wifimngr_dbg("scan_ex: unsupported / out of band / not 20MHz control channel\n");
return UBUS_STATUS_INVALID_ARGUMENT;
}
}
wifimngr_dbg("Num of opclasses=%d, num of channels=%d \n", op_idx, ch_idx);
/* opclass(es) provided - update channels[] */
if (op_idx > 0) {
int op;
for (op = 0; op < op_idx; op++) {
uint32_t opcch[128] = {0};
uint32_t supch[128] = {0};
int num_opc_ch = ARRAY_SIZE(opcch);
int num_sup_ch = ARRAY_SIZE(supch);
int i, j, k;
/* full list of channels in given opclass */
if (wifi_opclass_to_channels(opclasses[op], &num_opc_ch, opcch)) {
wifimngr_err("Coulnd't get channels for opclass %d\n",
opclasses[op]);
return UBUS_STATUS_INVALID_ARGUMENT;
}
/* list of BW20 channels supported by radio */
wifi_get_supp_channels(radio, supch, &num_sup_ch, country, band, BW20);
/* add only supported channels to final channels[] list */
for (i = 0; i < num_opc_ch; i++) {
for (j = 0; j < num_sup_ch; j++) {
if (opcch[i] == supch[j]) {
/* channel supported */
for (k = 0; k < ch_idx; k++) {
if (channels[k] == opcch[i])
/* already on the list */
break;
}
if (k == ch_idx)
/* not on the list - add */
channels[ch_idx++] = opcch[i];
break;
}
}
}
}
}
/* Translate opclasses & channels to freqs. */
for (m = 0; m < ch_idx; m++) {
sp.freq[idx] = wifi_channel_to_freq_ex(channels[m], band);
if (sp.freq[idx] < 0) {
wifimngr_err("No freq for channel %d\n", channels[m]);
continue;
}
idx++;
}
sp.num_freq = idx;
wifi_radio_is_multiband(radio, &multiband);
if (multiband)
ret = wifi_scan_band_ex(radio, band, &sp);
else
ret = wifi_scan_ex(radio, &sp);
wifimngr_dbg("scan_ex: ret = %d\n", ret);
if (ret == -EBUSY)
/* case for an ongoing unfinished scan */
return UBUS_STATUS_BUSY;
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
struct wifimngr_channel *wifimngr_device_lookup_channel(struct wifimngr_device *wdev,
uint32_t channel)
{
for (int i = 0; i < wdev->num_ch; i++) {
if (wdev->ch[i].channel == channel)
return &wdev->ch[i];
}
return NULL;
}
struct wifimngr_channel *wifimngr_device_lookup_freq(struct wifimngr_device *wdev,
uint32_t freq)
{
for (int i = 0; i < wdev->num_ch; i++) {
if (wdev->ch[i].freq == freq)
return &wdev->ch[i];
}
return NULL;
}
static void wl_scanresult_print(struct blob_buf *bb, void *buf, bool detail)
{
struct wifi_bss *b = (struct wifi_bss *)buf;
//char enc_buf[32] = {0};
//char cpr_buf[32] = {0};
char std_buf[32] = {0};
char std_buf2[32] = "802.11";
char sec_str[64] = {0};
char cstr[64] = {0};
blobmsg_add_string(bb, "ssid", (char *)b->ssid);
blobmsg_add_macaddr(bb, "bssid", b->bssid);
if (!!(b->caps.valid & WIFI_CAP_ML_VALID)) {
blobmsg_add_macaddr(bb, "mld_bssid", b->mld_macaddr);
blobmsg_add_u32(bb, "mlo_linkid", b->mlo_link_id);
}
blobmsg_add_u32(bb, "channel", b->channel);
blobmsg_add_u32(bb, "bandwidth", bw_value(b->curr_bw));
blobmsg_add_u32(bb, "ccfs0", b->ccfs0);
blobmsg_add_u32(bb, "ccfs1", b->ccfs1);
//wifi_security_str(b->sec, b->enc, b->g_enc, sec_str);
wifi_security_str(b->security, sec_str, sizeof(sec_str));
etostr(b->rsn.pair_ciphers, cstr, sizeof(cstr), 14, wifi_cipherstr);
blobmsg_add_string(bb, "encryption", sec_str);
blobmsg_add_string(bb, "ciphers", cstr);
wifi_print_band(bb, b->band);
blobmsg_add_u32(bb, "rssi", b->rssi);
//blobmsg_add_u32(bb, "snr", b->snr);
snprintf(std_buf2 + strlen(std_buf2), 31, "%s",
etostr(b->oper_std, std_buf, sizeof(std_buf), WIFI_NUM_STD, standard_str));
blobmsg_add_string(bb, "standard", std_buf2);
if (!b->bss_color_disabled)
blobmsg_add_u32(bb, "bss_color", b->bss_color);
blobmsg_add_u32(bb, "load_stas", b->load.sta_count);
blobmsg_add_u32(bb, "load_utilization", b->load.utilization);
blobmsg_add_u32(bb, "load_available", b->load.available);
wifi_print_radio_caps(bb, &b->caps, WIFI_MODE_AP);
if (detail) {
struct wifi_bss_detail *bx =
(struct wifi_bss_detail *)buf;
char iebuf[1024] = {0};
int i;
for (i = 0; i < bx->ielen; i++) {
sprintf(iebuf + strlen(iebuf), "%02x", bx->ie[i]);
}
blobmsg_add_string(bb, "ielist", iebuf);
}
}
int wl_scanresults(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
struct blob_attr *tb[__SCANRES_MAX];
struct wifi_bss scanres[NUM_SCANRES] = {0};
struct wifi_bss *bsss = scanres;
struct blob_buf bb = {0};
uint8_t bssid[6] = {0};
const char *radioname;
int num = NUM_SCANRES;
bool detail = false;
enum wifi_band band;
bool cache = true; /* default - return cached results from last scan */
const char *radio;
void *a, *t;
int i;
blobmsg_parse(wl_scanres_policy, __SCANRES_MAX, tb, blob_data(msg),
blob_len(msg));
radioname = ubus_objname_to_ifname(obj);
UNUSED(radioname);
radio = wdev->phy;
band = wdev->band;
if (tb[SCANRES_BSSID]) {
struct wifi_bss_detail bx;
char bssid_str[18] = {0};
strncpy(bssid_str, blobmsg_data(tb[SCANRES_BSSID]),
sizeof(bssid_str)-1);
if (!hwaddr_aton(bssid_str, bssid)) {
wifimngr_err("%s(): Invalid bssid\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
detail = true;
memset(&bx, 0, sizeof(bx));
if (wifi_get_bss_scan_result(radio, bssid, &bx) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
wl_scanresult_print(&bb, &bx, detail);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
blob_buf_init(&bb, 0);
a = blobmsg_open_array(&bb, "accesspoints");
#if WIFI_CACHE_SCANRESULTS
if (tb[SCANRES_CACHE])
cache = blobmsg_get_bool(tb[SCANRES_CACHE]);
if (cache) {
for (i = 0; i < wdev->num_ch; i++) {
if (!wdev->ch[i].num_scanres)
continue;
for (int j = 0; j < wdev->ch[i].num_scanres; j++) {
t = blobmsg_open_table(&bb, "");
wl_scanresult_print(&bb, &wdev->ch[i].scanres[j], detail);
blobmsg_add_u32(&bb, "elapsed", difftime(time(NULL), wdev->ch[i].scanres_ts));
blobmsg_close_table(&bb, t);
}
}
}
#else
wifimngr_trace("WIFI_CACHE_SCANRESULTS is not set\n");
cache = false; /* fetch from kernel */
#endif /* WIFI_CACHE_SCANRESULTS */
if (!cache) {
int ret;
ret = wifi_get_band_scan_results(radio, band, bsss, &num);
if (ret) {
blob_buf_free(&bb);
return UBUS_STATUS_UNKNOWN_ERROR;
}
for (i = 0; i < num; i++) {
t = blobmsg_open_table(&bb, "");
wl_scanresult_print(&bb, &bsss[i], detail);
blobmsg_close_table(&bb, t);
}
}
blobmsg_close_array(&bb, a);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
/**
* By default, CS includes all supported opclass and channels supported by radio.
* Exception list can be provided via one of the following ways -
*
* == exclude DFS channels.
* ubus call wifi.radio.<radioname> autochannel '{"exclude_dfs": true}'
*
* == exclude non-PSC channels in 6GHz.
* ubus call wifi.radio.<radioname> autochannel '{"exclude_6ghz_non_psc": true}'
*
* == exclude channels 120, 124, 128 and 144 for all bandwidths.
* ubus call wifi.radio.<radioname> autochannel '{"exclude_channels":[120,124,128,144]}'
*
* == exlucde channels belonging to opclasses 128 and 129.
* ubus call wifi.radio.<radioname> autochannel '{"exclude_opclass":[128,129]}'
*
* == exclude channels 120,124,128 and channels for opclass 115.
* ubus call wifi.radio.<radioname> autochannel '{"exclude_channels":[120,124,128], "exclude_opclass":[115]}'
*
*/
int wl_autochannel(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[NUM_ACS_PARAMS];
enum wifi_band band = BAND_2;
struct acs_param p = {0};
struct blob_buf bb = {0};
const char *radioname;
const char *device;
struct acs_param_channel *ach = NULL, *ach2 = NULL;
size_t total_num_channels = 0;
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
device = wdev->phy;
if (!msg || !blob_data(msg) || !blob_len(msg)) {
/* legacy usage with no args */
ret = wifi_acs(device, NULL);
blob_buf_init(&bb, 0);
blobmsg_add_u32(&bb, "code", ret);
if (ret == 0) {
enum wifi_bw bw;
uint32_t ch;
ret = wifi_get_channel(device, &ch, &bw);
if (ret == 0)
blobmsg_add_u32(&bb, "new_channel", ch);
blobmsg_add_string(&bb, "status", "success");
} else
blobmsg_add_string(&bb, "status", "error");
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
radioname = ubus_objname_to_ifname(obj);
UNUSED(radioname);
band = wdev->band;
blobmsg_parse(wl_acs_policy, NUM_ACS_PARAMS, tb, blob_data(msg), blob_len(msg));
if (tb[ACS_PARAM_MODE]) {
const char *mode = blobmsg_get_string(tb[ACS_PARAM_MODE]);
if (!strcmp(mode, "disable")) {
p.mode = WIFI_ACS_MODE_DISABLE;
} else if (!strcmp(mode, "enable")) {
p.mode = WIFI_ACS_MODE_ENABLE;
} else if (!strcmp(mode, "monitor")) {
p.mode = WIFI_ACS_MODE_MONITOR;
} else {
return UBUS_STATUS_INVALID_ARGUMENT;
}
}
if (tb[ACS_PARAM_EXCLUDE_DFS])
p.exclude_dfs = blobmsg_get_bool(tb[ACS_PARAM_EXCLUDE_DFS]);
if (tb[ACS_PARAM_EXCLUDE_6GHZ_NON_PSC])
p.exclude_6ghz_non_psc = blobmsg_get_bool(tb[ACS_PARAM_EXCLUDE_6GHZ_NON_PSC]);
if (tb[ACS_PARAM_EXCLUDE_OPCLASS]) {
int num_opclass = blobmsg_check_array(tb[ACS_PARAM_EXCLUDE_OPCLASS], BLOBMSG_TYPE_INT32);
struct blob_attr *cur;
int i = 0;
int rem;
if (num_opclass) {
blobmsg_for_each_attr(cur, tb[ACS_PARAM_EXCLUDE_OPCLASS], rem) {
uint32_t channels[32] = {0};
int num_channels = 32;
uint32_t opclass = 0;
enum wifi_bw bw;
if (blobmsg_type(cur) != BLOBMSG_TYPE_INT32) {
free(ach);
return UBUS_STATUS_INVALID_ARGUMENT;
}
opclass = blobmsg_get_u32(cur);
ret = wifi_opclass_to_channels(opclass, &num_channels, channels);
if (ret) {
free(ach);
return UBUS_STATUS_INVALID_ARGUMENT;
}
ret = wifi_opclass_bandwidth(opclass, &bw);
if (ret || bw == BW_UNKNOWN) {
free(ach);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (num_channels == 0)
continue;
ach2 = calloc((total_num_channels + num_channels), sizeof(struct acs_param_channel));
if (!ach2) {
free(ach);
return -1;
}
if (ach) {
memcpy(ach2, ach, total_num_channels * sizeof(struct acs_param_channel));
free(ach);
}
ach = ach2;
for (i = 0; i < num_channels; i++) {
ach[total_num_channels + i].channel = channels[i];
ach[total_num_channels + i].band = band;
ach[total_num_channels + i].weight = 0;
ach[total_num_channels + i].bw = bw;
wifimngr_dbg("%s: Exclude_opclass channel[%d] = %u, band = %d\n",
__func__, i,
ach[total_num_channels + i].channel,
ach[total_num_channels + i].band);
}
total_num_channels += num_channels;
}
}
p.num_channels = total_num_channels;
p.channels = ach;
}
if (tb[ACS_PARAM_EXCLUDE_CHANNELS]) {
int num_channels = blobmsg_check_array(tb[ACS_PARAM_EXCLUDE_CHANNELS], BLOBMSG_TYPE_INT32);
struct blob_attr *cur;
int i = 0;
int rem;
if (num_channels) {
ach2 = calloc((total_num_channels + num_channels), sizeof(struct acs_param_channel));
if (!ach2) {
free(ach);
return -1;
}
if (ach) {
memcpy(ach2, ach, total_num_channels * sizeof(struct acs_param_channel));
free(ach);
}
ach = ach2;
blobmsg_for_each_attr(cur, tb[ACS_PARAM_EXCLUDE_CHANNELS], rem) {
if (blobmsg_type(cur) != BLOBMSG_TYPE_INT32) {
free(ach);
return UBUS_STATUS_INVALID_ARGUMENT;
}
ach[total_num_channels + i].channel = blobmsg_get_u32(cur);
ach[total_num_channels + i].band = band;
ach[total_num_channels + i].weight = 0;
ach[total_num_channels + i].bw = BW_UNKNOWN;
wifimngr_dbg("%s: exclude channel[%d] = %u, band = %d\n",
__func__, i,
ach[total_num_channels + i].channel,
ach[total_num_channels + i].band);
i++;
}
total_num_channels += num_channels;
}
p.num_channels = total_num_channels;
p.channels = ach;
}
ret = wifi_acs(device, &p);
blob_buf_init(&bb, 0);
blobmsg_add_u32(&bb, "code", ret);
blobmsg_add_string(&bb, "status", !ret ? "success" : "error");
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
free(ach);
return UBUS_STATUS_OK;
}
int wl_start_cac(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
enum wifi_cac_method m = WIFI_CAC_MIMO_REDUCED;
struct blob_attr *tb[__CAC_MAX];
enum wifi_bw bw = BW20;
const char *ifname;
uint32_t ch = 0;
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
ifname = wdev->phy;
blobmsg_parse(wl_cac_policy, __CAC_MAX, tb, blob_data(msg), blob_len(msg));
if (!tb[CAC_CHANNEL])
return UBUS_STATUS_INVALID_ARGUMENT;
ch = blobmsg_get_u32(tb[CAC_CHANNEL]);
if (tb[CAC_BANDWIDTH]) {
int bandwidth;
bandwidth = blobmsg_get_u32(tb[CAC_BANDWIDTH]);
if (bandwidth == 20)
bw = BW20;
else if (bandwidth == 40)
bw = BW40;
else if (bandwidth == 80)
bw = BW80;
else if (bandwidth == 160)
bw = BW160;
else
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (tb[CAC_METHOD]) {
m = blobmsg_get_u32(tb[CAC_METHOD]);
if (m < 0 || m > WIFI_CAC_TIME_SLICED)
return UBUS_STATUS_INVALID_ARGUMENT;
}
ret = wifi_start_cac(ifname, ch, bw, m);
return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}
int wl_stop_cac(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__CAC_MAX];
const char *ifname;
uint32_t channel = 0;
enum wifi_bw bw = BW20;
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
ifname = wdev->phy;
blobmsg_parse(wl_cac_policy, __CAC_MAX, tb, blob_data(msg), blob_len(msg));
if (tb[CAC_CHANNEL])
channel = blobmsg_get_u32(tb[CAC_CHANNEL]);
if (tb[CAC_BANDWIDTH]) {
int bandwidth;
bandwidth = blobmsg_get_u32(tb[CAC_BANDWIDTH]);
switch (bandwidth) {
case 40:
bw = BW40;
break;
case 80:
bw = BW80;
break;
case 160:
bw = BW160;
break;
default:
bw = BW20;
break;
}
}
ret = wifi_stop_cac(ifname, channel, bw);
return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}
int wl_simulate_radar(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_radar_args radar = { 0 };
struct blob_attr *tb[__RADAR_MAX];
const char *ifname;
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
ifname = wdev->phy;
blobmsg_parse(wl_radar_policy, __RADAR_MAX, tb, blob_data(msg), blob_len(msg));
if (tb[RADAR_CHANNEL]) {
radar.channel = blobmsg_get_u32(tb[RADAR_CHANNEL]);
}
if (tb[RADAR_BANDWIDTH]) {
int bandwidth;
bandwidth = blobmsg_get_u32(tb[RADAR_BANDWIDTH]);
switch (bandwidth) {
case 20:
radar.bandwidth = BW20;
break;
case 40:
radar.bandwidth = BW40;
break;
case 80:
radar.bandwidth = BW80;
break;
case 160:
radar.bandwidth = BW160;
break;
default:
radar.bandwidth = BW_UNKNOWN;
}
}
if (tb[RADAR_TYPE]) {
radar.type = blobmsg_get_u32(tb[RADAR_TYPE]);
}
if (tb[RADAR_SUBBAND_MASK]) {
/* subband mask can be from range 0 - 0x0FF
* here is the rule:
* bit: 3 2 1 0
* low freq | 20MHz | 20MHz | 20MHz| 20MHz | high freq
* | 80MHz |
*/
radar.subband_mask = blobmsg_get_u32(tb[RADAR_SUBBAND_MASK]);
}
ret = wifi_simulate_radar(ifname, &radar);
return ret == 0 ? UBUS_STATUS_OK : UBUS_STATUS_UNKNOWN_ERROR;
}
int wl_add_iface(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
#define ADD_IFACE_ARGS_MAX 17
enum wifi_mode mode = WIFI_MODE_UNKNOWN;
struct blob_attr *tb[__ADD_IFACE_MAX];
char argv[ADD_IFACE_ARGS_MAX * 2 + 1][128] = { 0 };
char *argvp[ADD_IFACE_ARGS_MAX * 2 + 1] = { 0 };
bool config = true;
const char *device;
int i = 0;
int ret;
int rem;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
blobmsg_parse(add_iface_policy, __ADD_IFACE_MAX, tb, blob_data(msg),
blob_len(msg));
device = wdev->phy;
/* add the 'device' option */
strcpy(argv[i], "device");
argvp[i] = argv[i];
strncpy(argv[++i], device, 127);
argvp[i] = argv[i];
i++;
if (tb[ADD_IFACE_CONF])
config = blobmsg_get_bool(tb[ADD_IFACE_CONF]);
if (tb[ADD_IFACE_ARGS]) {
struct blob_attr *attr;
blobmsg_for_each_attr(attr, tb[ADD_IFACE_ARGS], rem) {
if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING)
return UBUS_STATUS_INVALID_ARGUMENT;
wifimngr_dbg("name = %s\n", blobmsg_name(attr));
strncpy(argv[i], blobmsg_name(attr), 127);
argvp[i] = argv[i];
memcpy(argv[i + 1], blobmsg_data(attr),
blobmsg_data_len(attr));
argvp[i + 1] = argv[i + 1];
if (!strcmp(argvp[i], "mode")) {
if (!strcmp(argvp[i + 1], "ap"))
mode = WIFI_MODE_AP;
else if (!strcmp(argvp[i + 1], "sta"))
mode = WIFI_MODE_STA;
else if (!strcmp(argvp[i + 1], "monitor"))
mode = WIFI_MODE_MONITOR;
else
return UBUS_STATUS_INVALID_ARGUMENT;
}
i += 2;
if (i > 31)
break;
}
}
/* if mode is not specified, assume "ap" */
if (mode == WIFI_MODE_UNKNOWN) {
mode = WIFI_MODE_AP;
strcpy(argv[i], "mode");
argvp[i] = argv[i];
strcpy(argv[++i], "ap");
argvp[i] = argv[i];
}
if (config) {
ret = uci_add_wifi_iface(argvp);
if (!ret)
wifimngr_dbg("Successfully added wifi-iface\n");
}
ret = wifi_add_iface(device, mode, argvp);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int wl_del_iface(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__DEL_IFACE_MAX];
bool config = true;
const char *device;
char ifname[16] = {0};
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
blobmsg_parse(del_iface_policy, __DEL_IFACE_MAX, tb, blob_data(msg),
blob_len(msg));
device = wdev->phy;
if (!tb[DEL_IFACE_IFNAME])
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(ifname, blobmsg_data(tb[DEL_IFACE_IFNAME]), 15);
if (tb[DEL_IFACE_CONF])
config = blobmsg_get_bool(tb[DEL_IFACE_CONF]);
if (config) {
ret = uci_del_wifi_iface(ifname);
if (!ret)
wifimngr_dbg("Successfully removed wifi-iface\n");
}
ret = wifi_del_iface(device, ifname);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
static char *dfs_state_str(enum dfs_state state)
{
switch (state) {
case WIFI_DFS_STATE_USABLE:
return "usable";
case WIFI_DFS_STATE_CAC:
return "cac";
case WIFI_DFS_STATE_UNAVAILABLE:
return "unavailable";
case WIFI_DFS_STATE_AVAILABLE:
return "available";
default:
break;
}
return "unknown";
}
int wl_channels_info(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct chan_entry channels[32] = {};
int num = ARRAY_SIZE(channels);
struct chan_entry *entry;
struct blob_buf bb;
const char *device;
const char *radioname;
enum wifi_band band;
bool multiband = false;
void *a;
void *t;
int ret;
int i;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
radioname = ubus_objname_to_ifname(obj);
UNUSED(radioname);
device = wdev->phy;
band = wdev->band;
wifi_radio_is_multiband(device, &multiband);
if (multiband)
ret = wifi_channels_info_band(device, band, channels, &num);
else
ret = wifi_channels_info(device, channels, &num);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
a = blobmsg_open_array(&bb, "channels");
for (i = 0; i < num; i++) {
entry = &channels[i];
t = blobmsg_open_table(&bb, "");
blobmsg_add_u32(&bb, "channel", entry->channel);
blobmsg_add_u32(&bb, "freq", entry->freq);
blobmsg_add_u32(&bb, "noise", entry->noise);
if (entry->dfs) {
blobmsg_add_u8(&bb, "radar", entry->dfs);
blobmsg_add_string(&bb, "dfs_state", dfs_state_str(entry->dfs_state));
blobmsg_add_u32(&bb, "cac_time", entry->cac_time);
if (entry->dfs_state == WIFI_DFS_STATE_UNAVAILABLE)
blobmsg_add_u32(&bb, "nop_time", entry->nop_time);
}
wifi_print_radio_diagnostics(&bb, &entry->survey);
blobmsg_close_table(&bb, t);
}
blobmsg_close_array(&bb, a);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int is_opclass_chgrp_supported(uint32_t *ctrl_channels, int num, uint32_t *supp_channels)
{
for (int k = 0; k < 32 && ctrl_channels[k] != 0; k++) {
int s;
for (s = 0; s < num; s++) {
if (supp_channels[s] == ctrl_channels[k])
break;
}
if (s == num)
return 0;
}
return 1;
}
int wl_channels(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_opclass supp_opclass[64] = {0};
size_t num_opclass = ARRAY_SIZE(supp_opclass);
struct blob_attr *tb[NUM_DUMP_CHANNELS];
uint32_t supp_channels[64] = {0};
enum wifi_band band = BAND_2;
int num_supp_channels = 64;
uint8_t channels[256] = {0};
struct blob_buf bb = {0};
bool multiband = false;
const char *radioname;
const char *ifname;
uint32_t channel;
enum wifi_bw bw;
void *t5;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
blob_buf_init(&bb, 0);
radioname = ubus_objname_to_ifname(obj);
UNUSED(radioname);
ifname = wdev->phy;
band = wdev->band;
blobmsg_parse(wl_dump_channels_policy, NUM_DUMP_CHANNELS, tb,
blob_data(msg), blob_len(msg));
if (tb[DUMP_CHANNELS_FOR_BANDWIDTH]) {
int bandwidth;
bandwidth = blobmsg_get_u32(tb[DUMP_CHANNELS_FOR_BANDWIDTH]);
if (bandwidth == 20)
bw = BW20;
else if (bandwidth == 40)
bw = BW40;
else if (bandwidth == 80)
bw = BW80;
else if (bandwidth == 160)
bw = BW160;
else if (bandwidth == 8080)
bw = BW8080;
else if (bandwidth == 320)
bw = BW320;
else {
blob_buf_free(&bb);
return UBUS_STATUS_INVALID_ARGUMENT;
}
} else {
/* use currently set bandwidth */
wifi_radio_is_multiband(ifname, &multiband);
if (multiband) {
wifi_get_band_channel(ifname, band, &channel, &bw);
} else {
wifi_get_channel(ifname, &channel, &bw);
}
/* in case of no nedev use BW20 */
if (bw == BW_UNKNOWN)
bw = BW20;
}
wifi_get_opclass_e4table(band, (1 << bw), &num_opclass, supp_opclass);
wifi_get_supp_channels(ifname, supp_channels, &num_supp_channels, "", band, bw);
for (int i = 0; i < num_opclass; i++) {
for (int j = 0; j < supp_opclass[i].opchannel.num; j++) {
if (is_opclass_chgrp_supported(supp_opclass[i].opchannel.ch[j].ctrl_channels,
num_supp_channels, supp_channels)) {
for (int k = 0; k < 32 && supp_opclass[i].opchannel.ch[j].ctrl_channels[k] != 0; k++) {
channels[supp_opclass[i].opchannel.ch[j].ctrl_channels[k]] = 1;
}
}
}
}
t5 = blobmsg_open_array(&bb, "channels");
for (int i = 0; i < sizeof(channels); i++) {
if (channels[i] == 1) {
blobmsg_add_u32(&bb, "", i);
}
}
blobmsg_close_array(&bb, t5);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int wl_opclass_preferences(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_opclass supp_opclass[64] = {0};
int num_opclass = ARRAY_SIZE(supp_opclass);
void *a, *t, *aa, *tt, *aaa;
struct blob_buf bb;
const char *radio;
const char *radioname;
enum wifi_band band;
bool multiband = false;
int i, j, k;
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
radioname = ubus_objname_to_ifname(obj);
UNUSED(radioname);
radio = wdev->phy;
band = wdev->band;
wifi_radio_is_multiband(radio, &multiband);
if (multiband)
ret = wifi_get_band_opclass_preferences(radio, band, supp_opclass, &num_opclass);
else
ret = wifi_get_opclass_preferences(radio, supp_opclass, &num_opclass);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
a = blobmsg_open_array(&bb, "pref_opclass");
for (i = 0; i < num_opclass; i++) {
t = blobmsg_open_table(&bb, "");
blobmsg_add_u32(&bb, "opclass", supp_opclass[i].g_opclass);
blobmsg_add_u32(&bb, "bandwidth", bw_value(supp_opclass[i].bw));
blobmsg_add_u32(&bb, "txpower", supp_opclass[i].opchannel.txpower);
aa = blobmsg_open_array(&bb, "channels");
for (j = 0; j < supp_opclass[i].opchannel.num; j++) {
tt = blobmsg_open_table(&bb, "");
blobmsg_add_u32(&bb, "channel", supp_opclass[i].opchannel.ch[j].channel);
blobmsg_add_u32(&bb, "score", supp_opclass[i].opchannel.ch[j].score);
blobmsg_add_u32(&bb, "dfs", supp_opclass[i].opchannel.ch[j].dfs);
if (supp_opclass[i].opchannel.ch[j].dfs) {
blobmsg_add_string(&bb, "dfs_state", dfs_state_str(supp_opclass[i].opchannel.ch[j].dfs_state));
blobmsg_add_u32(&bb, "cac_time", supp_opclass[i].opchannel.ch[j].cac_time);
if (supp_opclass[i].opchannel.ch[j].dfs_state == WIFI_DFS_STATE_UNAVAILABLE)
blobmsg_add_u32(&bb, "nop_time", supp_opclass[i].opchannel.ch[j].nop_time);
}
aaa = blobmsg_open_array(&bb, "ctrl_channels");
for (k = 0; k < ARRAY_SIZE(supp_opclass[i].opchannel.ch[j].ctrl_channels); k++) {
if (supp_opclass[i].opchannel.ch[j].ctrl_channels[k])
blobmsg_add_u32(&bb, "", supp_opclass[i].opchannel.ch[j].ctrl_channels[k]);
}
blobmsg_close_array(&bb, aaa);
blobmsg_close_table(&bb, tt);
}
blobmsg_close_array(&bb, aa);
blobmsg_close_table(&bb, t);
}
blobmsg_close_array(&bb, a);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int sta_disconnect(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__STA_DISCONNECT_MAX];
uint8_t sta[6] = {0};
char sta_str[18] = {0};
const char *ifname;
uint16_t reason = 0;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(sta_disconnect_policy, __STA_DISCONNECT_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[STA_DISCONNECT_STA])) {
wifimngr_err("%s(): sta mac not specified!\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
strncpy(sta_str, blobmsg_data(tb[STA_DISCONNECT_STA]), sizeof(sta_str)-1);
if (hwaddr_aton(sta_str, sta) == NULL) {
wifimngr_err("%s(): Invalid address format. Use 00:10:22:..\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (tb[STA_DISCONNECT_REASON])
reason = blobmsg_get_u32(tb[STA_DISCONNECT_REASON]);
if (wifi_disconnect_sta(ifname, sta, reason) != 0) {
return UBUS_STATUS_UNKNOWN_ERROR;
}
return UBUS_STATUS_OK;
}
int sta_probe(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__STA_MONITOR_MAX];
uint8_t sta[6] = {0};
char sta_str[18] = {0};
const char *ifname;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(sta_monitor_policy, __STA_MONITOR_MAX, tb, blob_data(msg),
blob_len(msg));
if (!(tb[STA_MONITOR_ADDR])) {
wifimngr_err("%s(): sta mac not specified!\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
strncpy(sta_str, blobmsg_data(tb[STA_MONITOR_ADDR]), sizeof(sta_str)-1);
if (hwaddr_aton(sta_str, sta) == NULL) {
wifimngr_err("%s(): Invalid address format. Use 00:10:22:..\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (wifi_probe_sta(ifname, sta) != 0) {
return UBUS_STATUS_UNKNOWN_ERROR;
}
return UBUS_STATUS_OK;
}
int sta_monitor_add_del(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg, bool enable)
{
struct blob_attr *tb[__STA_MONITOR_MAX];
struct wifi_monsta_config cfg = {};
const char *ifname;
uint8_t sta[6] = {0};
cfg.enable = enable;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(sta_monitor_policy, __STA_MONITOR_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[STA_MONITOR_ADDR])) {
wifimngr_err("%s(): sta mac not specified!\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (hwaddr_aton(blobmsg_data(tb[STA_MONITOR_ADDR]), sta) == NULL) {
wifimngr_err("%s(): Invalid address\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (wifi_monitor_sta(ifname, sta, &cfg) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int sta_monitor_add(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return sta_monitor_add_del(ctx, obj, req, method, msg, true);
}
int sta_monitor_del(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return sta_monitor_add_del(ctx, obj, req, method, msg, false);
}
static void
sta_monitor_add_entry(struct blob_buf *bb, struct wifi_monsta *mon)
{
char sta_str[20] = {};
void *t;
void *a;
int i;
snprintf(sta_str, 19, "%02x:%02x:%02x:%02x:%02x:%02x", MAC2STR(mon->macaddr));
t = blobmsg_open_table(bb, "sta");
blobmsg_add_string(bb, "macaddr", sta_str);
blobmsg_add_u32(bb, "seen", mon->last_seen);
blobmsg_add_u32(bb, "rssi_avg", mon->rssi_avg);
a = blobmsg_open_array(bb, "rssi");
for (i = 0; i < ARRAY_SIZE(mon->rssi); i++)
blobmsg_add_u32(bb, "", mon->rssi[i]);
blobmsg_close_array(bb, a);
blobmsg_close_table(bb, t);
}
int sta_monitor_get(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__STA_MONITOR_MAX];
struct blob_buf bb;
const char *ifname;
struct wifi_monsta stamon = {};
struct wifi_monsta stas[128] = {};
int num = ARRAY_SIZE(stas);
uint8_t sta[6] = {0};
void *array;
int i;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(sta_monitor_policy, __STA_MONITOR_MAX, tb,
blob_data(msg), blob_len(msg));
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
if ((tb[STA_MONITOR_ADDR])) {
if (hwaddr_aton(blobmsg_data(tb[STA_MONITOR_ADDR]), sta) == NULL) {
wifimngr_err("%s(): Invalid address\n", __func__);
blob_buf_free(&bb);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (wifi_get_monitor_sta(ifname, sta, &stamon) != 0) {
blob_buf_free(&bb);
return UBUS_STATUS_UNKNOWN_ERROR;
}
sta_monitor_add_entry(&bb, &stamon);
} else {
if (wifi_get_monitor_stas(ifname, stas, &num)) {
blob_buf_free(&bb);
return UBUS_STATUS_UNKNOWN_ERROR;
}
array = blobmsg_open_array(&bb, "stations");
for (i = 0; i < num; i++)
sta_monitor_add_entry(&bb, &stas[i]);
blobmsg_close_array(&bb, array);
}
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int subscribe_unsubscribe_frame(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg, bool subscribe)
{
struct blob_attr *tb[__SUBSCRIBE_FRAME_MAX];
const char *ifname;
unsigned int stype;
unsigned int type;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(subscribe_frame_policy, __SUBSCRIBE_FRAME_MAX, tb,
blob_data(msg), blob_len(msg));
if (!tb[SUBSCRIBE_FRAME_TYPE])
return UBUS_STATUS_INVALID_ARGUMENT;
if (!tb[SUBSCRIBE_FRAME_SUBTYPE])
return UBUS_STATUS_INVALID_ARGUMENT;
type = blobmsg_get_u32(tb[SUBSCRIBE_FRAME_TYPE]);
stype = blobmsg_get_u32(tb[SUBSCRIBE_FRAME_SUBTYPE]);
if (subscribe) {
if (wifi_subscribe_frame(ifname, type, stype))
return UBUS_STATUS_UNKNOWN_ERROR;
} else {
if (wifi_unsubscribe_frame(ifname, type, stype))
return UBUS_STATUS_UNKNOWN_ERROR;
}
return UBUS_STATUS_OK;
}
int subscribe_frame(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return subscribe_unsubscribe_frame(ctx, obj, req, method, msg, true);
}
int unsubscribe_frame(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return subscribe_unsubscribe_frame(ctx, obj, req, method, msg, false);
}
int ap_measure_link(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__LINKMEAS_MAX];
char macstr[18] = {0};
uint8_t sta[6] = {0};
const char *ifname;
int ret;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(linkmeas_policy, __LINKMEAS_MAX, tb, blob_data(msg),
blob_len(msg));
if (!(tb[LINKMEAS_STA]))
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(macstr, blobmsg_data(tb[LINKMEAS_STA]), sizeof(macstr)-1);
if (hwaddr_aton(macstr, sta) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
ret = wifi_link_measure(ifname, sta);
if (ret != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int ap_mbo_disallow_assoc(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__MBO_DISALLOW_ASSOC_MAX];
const char *ifname;
int ret;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(mbo_disallow_assoc_policy, __MBO_DISALLOW_ASSOC_MAX, tb, blob_data(msg),
blob_len(msg));
if (!(tb[MBO_DISALLOW_ASSOC_REASON]))
return UBUS_STATUS_INVALID_ARGUMENT;
ret = wifi_mbo_disallow_assoc(ifname, blobmsg_get_u32(tb[MBO_DISALLOW_ASSOC_REASON]));
if (ret != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int ap_bss_up(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const char *ifname;
int ret;
ifname = ubus_ap_to_ifname(obj);
ret = wifi_ap_set_state(ifname, true);
if (ret != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int ap_bss_down(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const char *ifname;
int ret;
ifname = ubus_ap_to_ifname(obj);
ret = wifi_ap_set_state(ifname, false);
if (ret != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int ap_send_action(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const char *ifname;
struct blob_attr *tb[__ACTION_MAX];
struct wifi_frame_arg arg = {0};
int ret;
uint8_t dst[6] = {0};
uint8_t src[6] = {0};
uint32_t wait = 0;
uint32_t freq = 0;
size_t framelen = 0;
uint8_t *frame;
uint64_t cookie = 0;
char *framestr;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(send_action_policy, __ACTION_MAX, tb, blob_data(msg),
blob_len(msg));
if (!(tb[ACTION_DST]) || !(tb[ACTION_FRAME]))
return UBUS_STATUS_INVALID_ARGUMENT;
if (tb[ACTION_WAIT])
wait = blobmsg_get_u32(tb[ACTION_WAIT]);
if (tb[ACTION_FREQ])
freq = blobmsg_get_u32(tb[ACTION_FREQ]);
framestr = blobmsg_data(tb[ACTION_FRAME]);
framelen = strlen(framestr) / 2;
frame = calloc(1, framelen);
if (!frame)
return UBUS_STATUS_UNKNOWN_ERROR;
if (!strtob(framestr, framelen, frame)) {
free(frame);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (hwaddr_aton(blobmsg_data(tb[ACTION_DST]), dst) == NULL) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
memcpy(arg.dst, dst, 6);
arg.duration = wait;
arg.freq = freq;
if (tb[ACTION_SRC]) {
if (hwaddr_aton(blobmsg_data(tb[ACTION_DST]), src) == NULL) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
memcpy(arg.src, src, 6);
}
ret = wifi_send_action_frame(ifname, &arg, frame, framelen, &cookie);
if (ret != 0) {
ret = UBUS_STATUS_UNKNOWN_ERROR;
goto out;
}
out:
free(frame);
return ret;
}
int wl_sta_dpp_listen(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const char *ifname;
struct blob_attr *tb[__DPP_LISTEN_MAX];
int ret;
uint32_t freq = 0;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(dpp_listen_policy, __DPP_LISTEN_MAX, tb, blob_data(msg),
blob_len(msg));
if (!(tb[DPP_LISTEN_FREQ]))
return UBUS_STATUS_INVALID_ARGUMENT;
freq = blobmsg_get_u32(tb[DPP_LISTEN_FREQ]);
ret = wifi_dpp_listen(ifname, freq);
if (ret != 0)
ret = UBUS_STATUS_UNKNOWN_ERROR;
return ret;
}
int wl_sta_dpp_stop_listen(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const char *ifname;
int ret;
ifname = ubus_ap_to_ifname(obj);
ret = wifi_dpp_stop_listen(ifname);
if (ret != 0)
ret = UBUS_STATUS_UNKNOWN_ERROR;
return ret;
}
/*
* This function receives the QoS map in the following format:
* (dscp_exc_i, pcp_exc_i,)*(dscp_min_0,dscp_max_0),...,(dscp_min_7,dscp_max_7)
* First tuples are up to 21 DSCP exceptions: the are made of specific DSCP
* values and associated UP values.
* The second group of tuples are DSCP minimum/DSCP maximum values for the UP
* equal to the index of a tuple.
*
* Usage example:
* ubus call wifi.ap.wlan0_1 set_qos_map '{ "set": \
* [ 255,255,255,255,255,255,255,255,255,255,255,255,0,63,255,255 ] }'
* This example assigns mandatory UP = 6 to practically all possible DSCP
* values (range [0, 63]).
*/
int set_qos_map(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
int ret;
const char *ifname;
struct blob_attr *tb[__AP_SET_QOS_MAP_SET_MAX];
#define QOS_MAP_MAX_EXCEPTION_COUNT (21)
#define QOS_MAP_MANDATORY_ELEMENT_COUNT (8)
#define QOS_MAP_PAIR_LEN (2)
uint8_t smap[QOS_MAP_MAX_EXCEPTION_COUNT * QOS_MAP_PAIR_LEN +
QOS_MAP_MANDATORY_ELEMENT_COUNT * QOS_MAP_PAIR_LEN];
int smap_exp_len;
int smap_len;
struct blob_attr *attr;
int rem;
int i;
struct dscp_exception *exc;
size_t exc_count;
struct dscp_pcp_map map;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(ap_set_qos_map_policy, __AP_SET_QOS_MAP_SET_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[AP_SET_QOS_MAP_SET])) {
wifimngr_err("%s(): QoS map is not specified\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
smap_exp_len = blobmsg_check_array(
tb[AP_SET_QOS_MAP_SET], BLOBMSG_TYPE_INT32);
if (smap_exp_len < QOS_MAP_MANDATORY_ELEMENT_COUNT * QOS_MAP_PAIR_LEN) {
wifimngr_err("%s(): map doesn't have mandatory elements: %d\n",
__func__, smap_exp_len);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (smap_exp_len > sizeof(smap) / sizeof(*smap)) {
wifimngr_err("%s(): input map is too big: %d\n", __func__,
smap_exp_len);
return UBUS_STATUS_INVALID_ARGUMENT;
}
exc_count = (smap_exp_len - QOS_MAP_MANDATORY_ELEMENT_COUNT *
QOS_MAP_PAIR_LEN) / QOS_MAP_PAIR_LEN;
if (((smap_exp_len - QOS_MAP_MANDATORY_ELEMENT_COUNT *
QOS_MAP_PAIR_LEN) % QOS_MAP_PAIR_LEN) != 0) {
wifimngr_err("%s(): number of DSCP exception elements is invalid\n",
__func__);
return UBUS_STATUS_UNKNOWN_ERROR;
}
smap_len = 0;
blobmsg_for_each_attr(attr, tb[AP_SET_QOS_MAP_SET], rem) {
if (blobmsg_type(attr) != BLOBMSG_TYPE_INT32)
return UBUS_STATUS_INVALID_ARGUMENT;
smap[smap_len++] = blobmsg_get_u32(attr);
}
if (smap_len != smap_exp_len) {
wifimngr_err("%s(): map is parsed incorrectly\n", __func__);
return UBUS_STATUS_UNKNOWN_ERROR;
}
exc = calloc(exc_count, sizeof(struct dscp_exception));
if (exc == NULL) {
wifimngr_err("%s(): failed to allocate memory for DSCP exceptions\n",
__func__);
return UBUS_STATUS_UNKNOWN_ERROR;
}
/* Convert flat sequence to map and exceptions */
for (i = 0; i < smap_exp_len; ++i) {
int val = smap[i];
int idx = i % QOS_MAP_PAIR_LEN;
if (i < (exc_count * QOS_MAP_PAIR_LEN)) {
if (idx == 0) {
exc[i / QOS_MAP_PAIR_LEN].dscp = val;
} else {
exc[i / QOS_MAP_PAIR_LEN + 1].output_pcp = val;
}
} else {
int pcp = (i - (exc_count * QOS_MAP_PAIR_LEN)) / QOS_MAP_PAIR_LEN;
if (idx == 0) {
map.dscp_to_up[pcp].dscp_min = val;
} else {
map.dscp_to_up[pcp].dscp_max = val;
}
}
}
ret = wifi_ap_set_qos_map(ifname, &map, exc, exc_count);
free(exc);
if (ret != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
#undef QOS_MAP_MAX_EXCEPTION_COUNT
#undef QOS_MAP_MANDATORY_ELEMENT_COUNT
#undef QOS_MAP_PAIR_LEN
return UBUS_STATUS_OK;
}
int send_qos_map_conf(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const char *ifname;
int ret;
struct blob_attr *tb[__AP_SEND_QOS_MAP_CONF_MAX];
uint8_t sta[6] = {0};
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(ap_send_qos_map_conf_policy, __AP_SEND_QOS_MAP_CONF_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[AP_SEND_QOS_MAP_CONF_ADDR])) {
wifimngr_err("%s(): sta mac not specified!\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (hwaddr_aton(blobmsg_data(tb[AP_SEND_QOS_MAP_CONF_ADDR]), sta) == NULL) {
wifimngr_err("%s(): Invalid address\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
ret = wifi_ap_send_qos_map_conf(ifname, sta);
if (ret != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int nbr_add(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__NBR_ADD_MAX];
const char *ifname;
char bssid_str[18] = {0};
uint8_t bssid[6] = {0};
char binfo_str[12] = {0};
unsigned int bssid_info = 0;
unsigned int ch = 0;
unsigned int phy = 9;
unsigned int reg = 0;
struct nbr nbr;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(nbr_add_policy, __NBR_ADD_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[NBR_ADD_BSSID])) {
wifimngr_err("%s(): Neighbor Bssid not specified!\n", __func__);
return UBUS_STATUS_INVALID_ARGUMENT;
}
strncpy(bssid_str, blobmsg_data(tb[NBR_ADD_BSSID]), sizeof(bssid_str)-1);
if (hwaddr_aton(bssid_str, bssid) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
if (tb[NBR_ADD_BINFO]) {
strncpy(binfo_str, blobmsg_data(tb[NBR_ADD_BINFO]), sizeof(binfo_str)-1);
bssid_info = atoi(binfo_str);
}
if (tb[NBR_ADD_CHANNEL])
ch = blobmsg_get_u32(tb[NBR_ADD_CHANNEL]);
if (tb[NBR_ADD_REG])
reg = blobmsg_get_u32(tb[NBR_ADD_REG]);
if (tb[NBR_ADD_PHY])
phy = blobmsg_get_u32(tb[NBR_ADD_PHY]);
/* wifimngr_dbg("Add: %02x:%02x:%02x:%02x:%02x:%02x Ch = %d Phy = %d\n",
bssid[0], bssid[1], bssid[2],
bssid[3], bssid[4], bssid[5],
ch, phy); */
memset(&nbr, 0, sizeof(struct nbr));
memcpy(nbr.bssid, bssid, 6);
nbr.bssid_info = bssid_info;
nbr.channel = ch;
nbr.phy = phy;
nbr.reg = reg;
if (wifi_add_neighbor(ifname, nbr) != 0) {
return UBUS_STATUS_UNKNOWN_ERROR;
}
return UBUS_STATUS_OK;
}
int nbr_del(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__NBR_DEL_MAX];
const char *ifname;
char bssid_str[18] = {0};
uint8_t bssid[6] = {0};
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(nbr_del_policy, __NBR_DEL_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[NBR_DEL_BSSID]))
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(bssid_str, blobmsg_data(tb[NBR_DEL_BSSID]), sizeof(bssid_str)-1);
if (hwaddr_aton(bssid_str, bssid) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
if (wifi_del_neighbor(ifname, bssid) != 0) {
return UBUS_STATUS_UNKNOWN_ERROR;
}
return UBUS_STATUS_OK;
}
static int _nbr_list(struct blob_buf *bb, const char *ifname, bool wildcard)
{
char bssid_str[18] = {0};
struct nbr *nbr;
int nr = 32;
void *a, *t;
int ret;
int i;
nbr = calloc(nr, sizeof(struct nbr));
if (!nbr) {
wifimngr_err("OOM get_neighbor_list()\n");
return UBUS_STATUS_UNKNOWN_ERROR;
}
ret = wifi_get_neighbor_list(ifname, nbr, &nr);
if (ret)
goto out_exit;
if (!wildcard)
a = blobmsg_open_array(bb, "neighbors");
for (i = 0; i < nr; i++) {
struct nbr *e;
e = nbr + i;
t = blobmsg_open_table(bb, "");
sprintf(bssid_str, "%02X:%02X:%02X:%02X:%02X:%02X",
e->bssid[0], e->bssid[1], e->bssid[2],
e->bssid[3], e->bssid[4], e->bssid[5]);
blobmsg_add_string(bb, "bssid", bssid_str);
blobmsg_add_u32(bb, "bss_info", e->bssid_info);
blobmsg_add_u32(bb, "regulatory", e->reg);
blobmsg_add_u32(bb, "channel", e->channel);
blobmsg_add_u32(bb, "phy", e->phy);
blobmsg_close_table(bb, t);
}
if (!wildcard)
blobmsg_close_array(bb, a);
out_exit:
free(nbr);
return ret;
}
static int _sta_nbr_list(struct blob_buf *bb, const char *ifname,
unsigned char *sta)
{
struct sta_nbr *snbr;
int nr = 32;
void *a, *t;
char bssid_str[18] = {0};
int ret;
int i;
snbr = calloc(nr, sizeof(struct sta_nbr));
if (!snbr) {
wifimngr_err("OOM get_neighbor_list()\n");
return UBUS_STATUS_UNKNOWN_ERROR;
}
ret = wifi_get_beacon_report(ifname, sta, snbr, &nr);
if (ret)
goto out_exit;
a = blobmsg_open_array(bb, "neighbors");
for (i = 0; i < nr; i++) {
struct sta_nbr *e;
e = snbr + i;
t = blobmsg_open_table(bb, "");
sprintf(bssid_str, "%02X:%02X:%02X:%02X:%02X:%02X",
e->bssid[0], e->bssid[1], e->bssid[2],
e->bssid[3], e->bssid[4], e->bssid[5]);
blobmsg_add_string(bb, "bssid", bssid_str);
blobmsg_add_u32(bb, "rssi", e->rssi);
blobmsg_add_u32(bb, "rsni", e->rsni);
blobmsg_close_table(bb, t);
}
blobmsg_close_array(bb, a);
out_exit:
free(snbr);
return ret;
}
int nbr_list(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__NBR_LIST_MAX];
char sta_macstr[18] = {0};
int ret = UBUS_STATUS_OK;
struct blob_buf bb = {0};
uint8_t sta[6] = {0};
const char *ifname;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(nbr_list_policy, __NBR_LIST_MAX, tb, blob_data(msg),
blob_len(msg));
blob_buf_init(&bb, 0);
if ((tb[NBR_LIST_CLIENT])) {
strncpy(sta_macstr, blobmsg_data(tb[NBR_LIST_CLIENT]),
sizeof(sta_macstr)-1);
if (hwaddr_aton(sta_macstr, sta) == NULL) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
if (_sta_nbr_list(&bb, ifname, sta) != 0) {
ret = UBUS_STATUS_UNKNOWN_ERROR;
goto out;
}
} else {
if (_nbr_list(&bb, ifname, false) != 0) {
ret = UBUS_STATUS_UNKNOWN_ERROR;
goto out;
}
}
ubus_send_reply(ctx, req, bb.head);
out:
blob_buf_free(&bb);
return ret;
}
int nbr_request(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__NBR_REQ_MAX];
const char *ifname;
char macstr[18] = {0};
uint8_t sta_macaddr[6] = {0};
char ssid[64];
char mode[32];
struct wifi_beacon_req *params;
uint8_t ssid_len = 0;
uint8_t *bcn_req;
uint8_t reporting_detail = 0;
uint8_t num_element = 0;
int pos = 0;
size_t req_size = sizeof(struct wifi_beacon_req);
int ret = UBUS_STATUS_OK;
blobmsg_parse(nbr_req_policy, __NBR_REQ_MAX, tb,
blob_data(msg), blob_len(msg));
/* Table 9-88 - Optional subelement IDs for Beacon request */
if (tb[NBR_REQ_SSID]) {
strncpy(ssid, blobmsg_data(tb[NBR_REQ_SSID]), sizeof(ssid)-1);
ssid_len = strlen(ssid);
if (ssid_len > 32)
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (ssid_len && tb[NBR_REQ_SSID]) {
req_size += 2; /* tlv */
req_size += ssid_len;
}
if (tb[NBR_REQ_REP_DETAIL]) {
reporting_detail = (uint8_t)blobmsg_get_u32(
tb[NBR_REQ_REP_DETAIL]);
req_size += 3; /* 1 octet value */
}
if (tb[NBR_REQ_ELEMENT_IDS]) {
if (reporting_detail != 1)
return UBUS_STATUS_INVALID_ARGUMENT;
num_element = blobmsg_check_array(
tb[NBR_REQ_ELEMENT_IDS], BLOBMSG_TYPE_INT32);
/* 9.4.2.10 - Request element */
req_size += 2 + num_element;
}
/* Allocate space for the request */
bcn_req = calloc(1, req_size);
if (!bcn_req)
return -ENOMEM;
/* Fill in request parameters */
params = (struct wifi_beacon_req *) bcn_req;
if (!(tb[NBR_REQ_CLIENT])) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
strncpy(macstr, blobmsg_data(tb[NBR_REQ_CLIENT]), sizeof(macstr)-1);
if (hwaddr_aton(macstr, sta_macaddr) == NULL) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
if (tb[NBR_REQ_OPCLASS])
params->oper_class = blobmsg_get_u32(tb[NBR_REQ_OPCLASS]);
if (tb[NBR_REQ_CHANNEL])
params->channel = blobmsg_get_u32(tb[NBR_REQ_CHANNEL]);
if (tb[NBR_REQ_DURATION])
params->duration = blobmsg_get_u32(tb[NBR_REQ_DURATION]);
params->mode = WIFI_BCNREQ_MODE_UNSET;
if (tb[NBR_REQ_MODE]) {
strncpy(mode, blobmsg_data(tb[NBR_REQ_MODE]), sizeof(mode)-1);
if (strlen(mode) > 8) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
if (!strcmp(mode, "active"))
params->mode = WIFI_BCNREQ_MODE_ACTIVE;
else if (!strcmp(mode, "passive"))
params->mode = WIFI_BCNREQ_MODE_PASSIVE;
else if (!strcmp(mode, "table"))
params->mode = WIFI_BCNREQ_MODE_TABLE;
}
if (tb[NBR_REQ_BSSID]) {
strncpy(macstr, blobmsg_data(tb[NBR_REQ_BSSID]), sizeof(macstr)-1);
if (hwaddr_aton(macstr, params->bssid) == NULL) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
}
if (ssid_len && tb[NBR_REQ_SSID]) {
params->variable[pos] = WIFI_BCNREQ_SSID;
params->variable[pos+1] = ssid_len;
memcpy(&params->variable[pos+2], ssid, ssid_len);
pos += 2 + ssid_len;
}
if (tb[NBR_REQ_REP_DETAIL]) {
params->variable[pos] = WIFI_BCNREQ_REP_DETAIL;
params->variable[pos+1] = 1;
params->variable[pos+2] = reporting_detail;
pos += 3;
}
/* example: ubus call wifi.ap.wlan1 request_neighbor '{"client":"44:d4:37:4d:
* 84:83", "bssid":"44:d4:37:42:47:bf", "ssid":"iopsysWrt", "mode":"active",
* "channel_report":[{"opclass":81,"channels": [1, 6, 13]}, {"opclass":82,
* "channels": [1, 6, 13]}], "reporting_detail":1, "request_element":[7, 33]}'
*/
if (tb[NBR_REQ_CHAN_REPORT]) {
int num_report = blobmsg_check_array(
tb[NBR_REQ_CHAN_REPORT], BLOBMSG_TYPE_TABLE);
struct blob_attr *cur;
int rem;
wifimngr_dbg("nbr_request: num_report = %d\n", num_report);
/* 9.4.2.36 AP Channel Report element */
blobmsg_for_each_attr(cur, tb[NBR_REQ_CHAN_REPORT], rem) {
struct blob_attr *data[2], *attr;
int num_channels, j = 0;
int remm;
uint8_t *bcn_req_ext;
static const struct blobmsg_policy supp_attrs[2] = {
[0] = { .name = "opclass", .type = BLOBMSG_TYPE_INT32 },
[1] = { .name = "channels", .type = BLOBMSG_TYPE_ARRAY },
};
//if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
// continue;
blobmsg_parse(supp_attrs, 2, data, blobmsg_data(cur),
blobmsg_data_len(cur));
if (!data[0] || !data[1])
continue;
num_channels = blobmsg_check_array(data[1], BLOBMSG_TYPE_INT32);
/* Allocate additional space for Channel Report elements:
* - 3 octets: Element ID + Length + Operating Class.
* - Channel List consists of num_channels octets.
* - num_channels can be equal to 0.
*/
req_size = req_size + 3 + num_channels;
bcn_req_ext = realloc(bcn_req, req_size);
if (!bcn_req_ext) {
ret = -ENOMEM;
goto out;
}
bcn_req = bcn_req_ext;
// Iterate through all channels of the opclass
blobmsg_for_each_attr(attr, data[1], remm) {
if (blobmsg_type(attr) != BLOBMSG_TYPE_INT32)
continue;
/* Channel List */
params->variable[pos + (3 + j)] = (uint8_t) blobmsg_get_u32(attr);
j++;
}
if (num_channels != j) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
/* Element ID */
params->variable[pos] = WIFI_BCNREQ_AP_CHAN_REPORT;
/* Length = 1 (for opclass) + number_of_channels [octets] */
params->variable[pos+1] = 1 + j;
/* Operating Class */
params->variable[pos+2] = (uint8_t) blobmsg_get_u32(data[0]);
pos += (3 + j); /* El ID, len, opclass + channel list */
}
}
/* If the Reporting Detail equals 1, a Request element is optionally
* present in the optional subelements field. If included, the Request
* element lists the Element IDs of the elements requested to be
* reported in the Reported Frame Body of the Beacon Report. */
if (num_element && reporting_detail == 1) {
/* 9.4.2.10 - Request element */
struct blob_attr *attr_id;
int rem_id;
int k = 0;
wifimngr_dbg("nbr_request: num_element = %d\n", num_element);
params->variable[pos] = WIFI_BCNREQ_REQUEST;
params->variable[pos+1] = num_element;
blobmsg_for_each_attr(attr_id, tb[NBR_REQ_ELEMENT_IDS], rem_id) {
/* Table 9-77 - Element IDs */
if (blobmsg_type(attr_id) != BLOBMSG_TYPE_INT32)
continue;
params->variable[pos + (2 + k)] = (uint8_t) blobmsg_get_u32(attr_id);
k++;
}
if (num_element != k) {
ret = UBUS_STATUS_INVALID_ARGUMENT;
goto out;
}
pos += (2 + k);
}
ifname = ubus_ap_to_ifname(obj);
if (wifi_req_beacon_report(ifname, sta_macaddr, params, req_size) != 0)
ret = UBUS_STATUS_UNKNOWN_ERROR;
out:
free(bcn_req);
return ret;
}
/* btm_request
*
* Example calls:
*
* ubus call wifi.ap.wl0 "request_btm" '{"sta":"00:0a:52:06:2f:ab",
* "target_ap":[{"bssid":"7a:d4:37:6a:f7:df", "reg":128, "channel":36}]}'
*
* ubus call wifi.ap.wl0 "request_btm" '{"sta":"00:0a:52:06:2f:ab",
* "target_ap":[{"bssid":"7a:d4:37:6a:f7:df"}]}'
*
* ubus call wifi.ap.wl0 "request_btm" '{"sta":"00:0a:52:06:2f:ab",
* "target_ap":[{"bssid":"7a:d4:37:6a:f7:df"},
* {"bssid":"44:d4:37:6a:f4:cf", "channel":36}]}'
*
* ubus call wifi.ap.wl0 "request_btm" '{"sta":"00:0a:52:06:2f:ab",
* "target_ap":[{"bssid":"7a:d4:37:6a:f7:df", "reg":128, "channel":36},
* {"bssid":"44:d4:37:6a:f4:cf", "bssid_info":6319, "phy":14}],
* "mode":12,"disassoc_tmo":300,"bssterm_dur":1}'
*/
#define MAX_BTM_BSS_NUM 12
int btm_request(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__BTM_REQ_MAX];
const char *ifname;
char sta_macstr[18] = {0};
uint8_t sta[6] = {0};
int rem;
int bss_idx = 0;
struct wifi_btmreq param = {
.dialog_token = 0x1,
.mbo.valid = false,
};
struct nbr bsss[MAX_BTM_BSS_NUM] = {0};
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(btmreq_policy, __BTM_REQ_MAX, tb,
blob_data(msg), blob_len(msg));
/* STA mac address */
if (!(tb[BTM_REQ_STA]))
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(sta_macstr, blobmsg_data(tb[BTM_REQ_STA]), sizeof(sta_macstr)-1);
if (hwaddr_aton(sta_macstr, sta) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
/* Target BSSID and optional params */
if (tb[BTM_REQ_TARGET]) {
struct blob_attr *cur;
char bss_str[18] = {0};
uint8_t tbss[6] = {0};
int num_bss = blobmsg_check_array(tb[BTM_REQ_TARGET], BLOBMSG_TYPE_TABLE);
wifimngr_dbg("btm_request: num of BSSID = %d\n", num_bss);
blobmsg_for_each_attr(cur, tb[BTM_REQ_TARGET], rem) {
struct blob_attr *data[5];
static const struct blobmsg_policy supp_attrs[5] = {
[0] = { .name = "bssid", .type = BLOBMSG_TYPE_STRING },
[1] = { .name = "bssid_info", .type = BLOBMSG_TYPE_INT32 },
[2] = { .name = "reg", .type = BLOBMSG_TYPE_INT32 },
[3] = { .name = "channel", .type = BLOBMSG_TYPE_INT32 },
[4] = { .name = "phy", .type = BLOBMSG_TYPE_INT32 },
};
blobmsg_parse(supp_attrs, 5, data, blobmsg_data(cur),
blobmsg_data_len(cur));
if (!data[0])
return UBUS_STATUS_INVALID_ARGUMENT;
/* Pass the params */
strncpy(bss_str, blobmsg_data(data[0]), sizeof(bss_str)-1);
if (hwaddr_aton(bss_str, tbss) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
memcpy(&bsss[bss_idx].bssid, tbss, 6);
if (data[1])
bsss[bss_idx].bssid_info = blobmsg_get_u32(data[1]);
if (data[2])
bsss[bss_idx].reg = (uint8_t) blobmsg_get_u32(data[2]);
if (data[3])
bsss[bss_idx].channel = (uint8_t) blobmsg_get_u32(data[3]);
if (data[4])
bsss[bss_idx].phy = (uint8_t) blobmsg_get_u32(data[4]);
if (++bss_idx >= MAX_BTM_BSS_NUM)
break;
}
}
wifimngr_dbg("btm_request: bss_idx = %d\n", bss_idx);
if (tb[BTM_REQ_DIALOG_TOKEN]) {
uint32_t token = blobmsg_get_u32(tb[BTM_REQ_DIALOG_TOKEN]);
if (token > 255)
return UBUS_STATUS_INVALID_ARGUMENT;
param.dialog_token = token;
}
if (tb[BTM_REQ_MODE])
param.mode = blobmsg_get_u32(tb[BTM_REQ_MODE]);
if (tb[BTM_REQ_DISASSOC_TMO])
param.disassoc_tmo = blobmsg_get_u32(tb[BTM_REQ_DISASSOC_TMO]);
if (tb[BTM_REQ_VALIDITY_INT])
param.validity_int = blobmsg_get_u32(tb[BTM_REQ_VALIDITY_INT]);
if (tb[BTM_REQ_BSSTERM_DUR])
param.bssterm_dur = blobmsg_get_u32(tb[BTM_REQ_BSSTERM_DUR]);
/* MBO parameters */
if (tb[BTM_REQ_MBO_REASON])
param.mbo.reason = blobmsg_get_u32(tb[BTM_REQ_MBO_REASON]);
if (tb[BTM_REQ_MBO_CELL_PREF])
param.mbo.cell_pref = blobmsg_get_u32(tb[BTM_REQ_MBO_CELL_PREF]);
if (tb[BTM_REQ_MBO_REASSOC_DELAY])
param.mbo.reassoc_delay = blobmsg_get_u32(tb[BTM_REQ_MBO_REASSOC_DELAY]);
if (param.mbo.reason <= 255 || param.mbo.cell_pref <= 255
|| param.mbo.reassoc_delay <= 65535)
param.mbo.valid = true;
if (wifi_req_btm(ifname, sta, bss_idx, bsss, &param) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int assoc_control(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__ASSOC_CNTRL_MAX];
const char *ifname;
char sta_macstr[18] = {0};
uint8_t sta[6] = {0};
struct blob_attr *attr;
int enable, rem;
wifimngr_dbg("%s: entering\n", __func__);
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(assoc_cntrl_policy, __ASSOC_CNTRL_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[ASSOC_CNTRL_CLIENT]) || !(tb[ASSOC_CNTRL_ENABLE]))
return UBUS_STATUS_INVALID_ARGUMENT;
enable = blobmsg_get_u32(tb[ASSOC_CNTRL_ENABLE]);
blobmsg_for_each_attr(attr, tb[ASSOC_CNTRL_CLIENT], rem) {
if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING)
continue;
strncpy(sta_macstr, blobmsg_data(attr), sizeof(sta_macstr)-1);
if (hwaddr_aton(sta_macstr, sta) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
if (wifi_restrict_sta(ifname, sta, enable) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
}
return UBUS_STATUS_OK;
}
int vsie_add(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *ureq, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__VSIE_MAX];
const char *ifname;
uint8_t vsie_buf[256] = {0};
struct vendor_iereq *req = (struct vendor_iereq *)vsie_buf;
uint8_t *data = req->ie.data;
char data_str[499] = {0}; /* (256 - 7) * 2 + 1 */
char oui_str[7] = {0}; /* 3 * 2 + 1 */
uint8_t oui[3] = {0};
int data_len = 0;
int data_strlen;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(vsie_policy, __VSIE_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[VSIE_OUI]) || !(tb[VSIE_DATA]))
return UBUS_STATUS_INVALID_ARGUMENT;
req->mgmt_subtype = 0;
req->ie.ie_hdr.eid = 0xdd;
req->ie.ie_hdr.len = 0;
if (tb[VSIE_MGMT_STYPE])
req->mgmt_subtype = blobmsg_get_u32(tb[VSIE_MGMT_STYPE]);
if (blobmsg_data_len(tb[VSIE_OUI]) != sizeof(oui_str))
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(oui_str, blobmsg_data(tb[VSIE_OUI]), 6);
strtob(oui_str, 7, oui);
memcpy(req->ie.oui, oui, 3);
req->ie.ie_hdr.len += 3;
data_strlen = blobmsg_data_len(tb[VSIE_DATA]);
if (data_strlen > sizeof(data_str) - 1)
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(data_str, blobmsg_data(tb[VSIE_DATA]), data_strlen);
strtob(data_str, data_strlen, &data[0]);
data_len = (data_strlen - 1) / 2;
req->ie.ie_hdr.len += data_len;
if (wifi_add_vendor_ie(ifname, req) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int vsie_del(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *ureq, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__VSIE_MAX];
const char *ifname;
uint8_t vsie_buf[256] = {0};
struct vendor_iereq *req = (struct vendor_iereq *)vsie_buf;
uint8_t *data = req->ie.data;
char data_str[499] = {0}; /* (256 - 7) * 2 + 1 */
char oui_str[7] = {0}; /* 3 * 2 + 1 */
uint8_t oui[3] = {0};
int data_strlen;
int data_len = 0;
//uint32_t mgmt_frames = 0;
ifname = ubus_ap_to_ifname(obj);
blobmsg_parse(vsie_policy, __VSIE_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[VSIE_OUI]))
return UBUS_STATUS_INVALID_ARGUMENT;
req->mgmt_subtype = 0;
req->ie.ie_hdr.eid = 0xdd;
req->ie.ie_hdr.len = 0;
if (tb[VSIE_MGMT_STYPE])
req->mgmt_subtype = blobmsg_get_u32(tb[VSIE_MGMT_STYPE]);
if (blobmsg_data_len(tb[VSIE_OUI]) != sizeof(oui_str))
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(oui_str, blobmsg_data(tb[VSIE_OUI]), 6);
strtob(oui_str, 7, oui);
memcpy(req->ie.oui, oui, 3);
req->ie.ie_hdr.len += 3;
if (tb[VSIE_DATA]) {
/* match data starting with pattern */
data_strlen = blobmsg_data_len(tb[VSIE_DATA]);
if (data_strlen > sizeof(data_str) - 1)
return UBUS_STATUS_INVALID_ARGUMENT;
strncpy(data_str, blobmsg_data(tb[VSIE_DATA]), data_strlen);
strtob(data_str, data_strlen, &data[0]);
data_len = (data_strlen - 1) / 2;
req->ie.ie_hdr.len += data_len;
}
if (wifi_del_vendor_ie(ifname, req) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
static void print_sta_info(struct blob_buf *bb, struct wifi_sta *sta)
{
uint8_t bssid[6] = {0};
char std_buf2[32] = "802.11";
char std_buf[32] = {0};
char sec_str[128] = {0};
char enc_buf[32] = {0};
int snr;
void *t;
if (sta->mlo_capable) {
blobmsg_add_u32(bb, "mlo_link_id", sta->mlo_link_id);
//blobmsg_add_macaddr(bb, "mld_macaddr", sta->mld_macaddr);
//blobmsg_add_macaddr(bb, "mld_bssid", sta->mld_bssid);
}
blobmsg_add_u8(bb, "4addr", sta->is4addr);
blobmsg_add_macaddr(bb, "macaddr", sta->macaddr);
snprintf(std_buf2 + strlen(std_buf2), 31, "%s",
etostr(sta->oper_std, std_buf, sizeof(std_buf), WIFI_NUM_STD, standard_str));
blobmsg_add_string(bb, "standard", std_buf2);
wifi_security_str(sta->sec.curr_mode, sec_str, sizeof(sec_str));
blobmsg_add_string(bb, "security", sec_str);
blobmsg_add_string(bb, "encryption",
etostr(sta->sec.pair_ciphers, enc_buf, sizeof(enc_buf), 14, cipher_str)); //FIXME-CR
wl_dump_supp_security(bb, sta->sec.supp_modes);
if (memcmp(bssid, sta->bssid, sizeof(bssid))) {
snr = sta->rssi_avg - sta->noise_avg;
blobmsg_add_macaddr(bb, "bssid", sta->bssid);
blobmsg_add_string(bb, "ssid", sta->ssid);
blobmsg_add_u32(bb, "channel", sta->channel);
blobmsg_add_u32(bb, "bandwidth", bw_value(sta->bandwidth));
wifi_print_band(bb, sta->band);
blobmsg_add_u32(bb, "frequency", wifi_channel_to_freq_ex(sta->channel, sta->band));
blobmsg_add_u32(bb, "rssi", sta->rssi_avg);
blobmsg_add_u32(bb, "noise", sta->noise_avg);
blobmsg_add_u32(bb, "snr", snr);
blobmsg_add_u32(bb, "idle", sta->idle_time);
blobmsg_add_u64(bb, "in_network", sta->conn_time);
blobmsg_add_u32(bb, "tx_airtime", sta->tx_airtime);
blobmsg_add_u32(bb, "rx_airtime", sta->rx_airtime);
blobmsg_add_u32(bb, "airtime", sta->airtime);
blobmsg_add_u32(bb, "maxrate", sta->maxrate);
blobmsg_add_u32(bb, "tx_throughput", sta->tx_thput);
blobmsg_add_u32(bb, "est_rx_thput", sta->est_rx_thput);
blobmsg_add_u32(bb, "est_tx_thput", sta->est_tx_thput);
t = blobmsg_open_table(bb, "status");
blobmsg_add_u8(bb, "wmm",
wifi_status_isset(sta->sbitmap, WIFI_STATUS_WMM) ? true : false);
blobmsg_add_u8(bb, "ps",
wifi_status_isset(sta->sbitmap, WIFI_STATUS_PS) ? true : false);
blobmsg_close_table(bb, t);
wl_dump_capabilities(sta->band, bb, &sta->caps, sta->cbitmap, sizeof(sta->cbitmap));
wl_print_sta_stats(bb, &sta->stats);
wl_print_rate(bb, "tx_rate_latest", &sta->tx_rate);
wl_print_rate(bb, "rx_rate_latest", &sta->rx_rate);
wl_print_rssi_chains(bb, "rssi_per_antenna", sta->rssi);
}
}
int wl_sta_status(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_sta sta = {};
const char *ifname;
struct blob_buf bb;
ifstatus_t ifs = 0;
int ret;
ifname = ubus_sta_to_ifname(obj);
wifi_get_ifstatus(ifname, &ifs);
ret = wifi_sta_info(ifname, &sta);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "ifname", ifname);
blobmsg_add_string(&bb, "status", ifstatus_str(ifs));
print_sta_info(&bb, &sta);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
int wl_mldsta_status(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_mlsta mlsta = {};
const char *ifname;
struct blob_buf bb;
ifstatus_t ifs = 0;
int ret;
void *t, *a;
int i;
ifname = ubus_sta_to_ifname(obj);
wifi_mld_get_ifstatus(ifname, &ifs);
ret = wifi_mlsta_interface_info(ifname, &mlsta);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "ifname", ifname);
blobmsg_add_string(&bb, "status", ifstatus_str(ifs));
blobmsg_add_macaddr(&bb, "macaddr", mlsta.macaddr);
blobmsg_add_macaddr(&bb, "bssid", mlsta.bssid);
blobmsg_add_string(&bb, "ssid", mlsta.ssid);
blobmsg_add_u8(&bb, "4addr", mlsta.is4addr);
a = blobmsg_open_array(&bb, "mlo_links");
for (i = 0; i < mlsta.num_link ; i++) {
t = blobmsg_open_table(&bb, "");
print_sta_info(&bb, &mlsta.sta[i]);
blobmsg_close_table(&bb, t);
}
blobmsg_close_array(&bb, a);
wl_print_sta_stats(&bb, &mlsta.stats);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
int wl_sta_stats(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
const char *ifname;
struct wifi_sta_stats s;
struct blob_buf bb;
int ret;
ifname = ubus_sta_to_ifname(obj);
ret = wifi_sta_get_stats(ifname, &s);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "ifname", ifname);
void *t;
t = blobmsg_open_table(&bb, "stats");
blobmsg_add_u64(&bb, "tx_total_pkts", s.tx_pkts);
blobmsg_add_u64(&bb, "tx_total_bytes", s.tx_bytes);
blobmsg_add_u64(&bb, "tx_failures", s.tx_fail_pkts);
blobmsg_add_u64(&bb, "tx_pkts_retries", s.tx_retry_pkts);
blobmsg_add_u64(&bb, "rx_data_pkts", s.rx_pkts);
blobmsg_add_u64(&bb, "rx_data_bytes", s.rx_bytes);
blobmsg_add_u64(&bb, "rx_failures", s.rx_fail_pkts);
blobmsg_close_table(&bb, t);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
int wl_sta_disconnect_ap(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__AP_DISCONNECT_MAX];
unsigned int reason_code = 0;
const char *ifname;
int ret;
ifname = ubus_sta_to_ifname(obj);
blobmsg_parse(ap_disconnect_policy, __AP_DISCONNECT_MAX, tb,
blob_data(msg), blob_len(msg));
if (tb[AP_DISCONNECT_REASON])
reason_code = blobmsg_get_u32(tb[AP_DISCONNECT_REASON]);
ret = wifi_sta_disconnect_ap(ifname, reason_code);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
int wl_sta_4addr(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__STA_4ADDR_MAX];
struct blob_buf bb;
bool enable;
const char *ifname;
int ret;
ifname = ubus_sta_to_ifname(obj);
blobmsg_parse(sta_4addr_policy, __STA_4ADDR_MAX, tb,
blob_data(msg), blob_len(msg));
if (tb[STA_4ADDR_ENABLE]) {
enable = blobmsg_get_u32(tb[STA_4ADDR_ENABLE]);
ret = wifi_set_4addr(ifname, enable);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
return UBUS_STATUS_OK;
}
ret = wifi_get_4addr(ifname, &enable);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
memset(&bb, 0, sizeof(bb));
blob_buf_init(&bb, 0);
blobmsg_add_u8(&bb, "enable", enable);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int wl_status(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifimngr *w = container_of(obj, struct wifimngr, wifi_obj);
void *t, *tt, *ttt, *tttt, *t5;
struct blob_buf bb = {0};
int i;
blob_buf_init(&bb, 0);
t = blobmsg_open_array(&bb, "radios");
for (i = 0; i < w->num_wifi_device; i++) {
uint32_t channel;
uint32_t channels[64] = {0};
char *phyname = NULL;
ifstatus_t ifstat = 0;
enum wifi_band band = BAND_2;
uint32_t supp_band = 0;
uint8_t supp_std = 0;
unsigned long maxrate;
int noise;
enum wifi_bw bw = BW20;
//int bwi = 20;
uint8_t s = 0;
//char s_str[32] = {0};
char std_buf[32] = {0};
char std_buf2[32] = "802.11";
struct wifi_caps radiocaps;
char alpha2[3] = {0};
const char *country;
bool multiband = false;
int nr = ARRAY_SIZE(channels);
bool radio_disabled;
int j;
phyname = w->wdev[i].phy;
if (!phyname)
continue;
band = w->wdev[i].band;
radio_disabled = w->wdev[i].disabled;
wifi_radio_is_multiband(phyname, &multiband);
wifi_get_supp_band(phyname, &supp_band);
/* Get country from driver first, fallback to cached UCI value */
if (!wifi_get_country(phyname, alpha2))
country = alpha2;
else if (w->wdev[i].country[0] != '\0')
country = w->wdev[i].country;
else
country = "";
wifi_get_supp_channels(phyname, channels, &nr, country, band, bw);
if (multiband) {
//wifi_radio_get_band_ifstatus(phyname, band, &ifstat);
wifi_get_band_channel(phyname, band, &channel, &bw);
wifi_get_band_supp_stds(phyname, band, &supp_std);
wifi_radio_get_band_caps(phyname, band, &radiocaps);
wifi_get_band_maxrate(phyname, band, &maxrate);
wifi_get_band_noise(phyname, band, &noise);
wifi_get_band_oper_stds(phyname, band, &s);
} else {
//wifi_radio_get_ifstatus(phyname, &ifstat);
wifi_get_channel(phyname, &channel, &bw);
wifi_get_supp_stds(phyname, &supp_std);
wifi_radio_get_caps(phyname, &radiocaps);
wifi_get_maxrate(phyname, &maxrate);
wifi_get_noise(phyname, &noise);
wifi_get_oper_stds(phyname, &s);
}
ttt = blobmsg_open_table(&bb, "");
blobmsg_add_string(&bb, "name", w->wdev[i].device);
blobmsg_add_string(&bb, "phyname", phyname);
blobmsg_add_u8(&bb, "isup", !radio_disabled);
//blobmsg_add_string(&bb, "standard", s_str);
snprintf(std_buf2 + strlen(std_buf2), 31, "%s",
etostr(s, std_buf, sizeof(std_buf), WIFI_NUM_STD, standard_str));
blobmsg_add_string(&bb, "standard", std_buf2);
blobmsg_add_string(&bb, "country", country);
wifi_print_band(&bb, band);
blobmsg_add_u32(&bb, "channel", channel);
blobmsg_add_u32(&bb, "frequency", wifi_channel_to_freq_ex(channel, band));
blobmsg_add_u32(&bb, "bandwidth", bw_value(bw));
blobmsg_add_u32(&bb, "noise", noise);
blobmsg_add_u64(&bb, "maxrate", maxrate);
wifi_print_radio_supp_bands(&bb, supp_band);
wifi_print_radio_supp_std(&bb, supp_std);
t5 = blobmsg_open_array(&bb, "channels");
for (j = 0; j < nr && channels[j] != 0; j++) {
blobmsg_add_u32(&bb, "", channels[j]);
}
blobmsg_close_array(&bb, t5);
tt = blobmsg_open_array(&bb, "accesspoints");
for (j = 0; j < w->num_wifi_iface; j++) {
struct wifi_mlo_link link[1] = {0};
char *ifname = w->ifs[j].iface;
int nlink = ARRAY_SIZE(link);
bool is_mlolink = false;
uint8_t bssid[6] = {0};
char ssid[64] = {0};
if (w->ifs[j].device[0] &&
!strncmp(w->ifs[j].device, w->wdev[i].device, 16) &&
w->ifs[j].mode == WIFI_MODE_AP) {
/* wifimngr_dbg("Matched: rad = %s if = %s\n",
ifs[j].device, ifname); */
//memset(w->ifs[j].device, 0, 16);
;
} else {
continue;
}
if (!strlen(w->ifs[j].mld_netdev)) {
wifi_get_ifstatus(ifname, &ifstat);
if (!!(ifstat & IFF_UP)) {
wifi_get_bssid(ifname, bssid);
wifi_get_ssid(ifname, ssid);
}
} else {
is_mlolink= true;
wifi_get_mlo_links(w->ifs[j].mld_netdev, w->ifs[j].band, link, &nlink);
wifi_get_bssid(w->ifs[j].mld_netdev, bssid);
wifi_get_ssid(w->ifs[j].mld_netdev, ssid);
ifstat |= IFF_UP; //FIXME
}
tttt = blobmsg_open_table(&bb, "");
blobmsg_add_string(&bb, "ifname", ifname);
blobmsg_add_string(&bb, "status", ifstatus_str(ifstat));
blobmsg_add_string(&bb, "ssid", ssid);
blobmsg_add_macaddr(&bb, "bssid", bssid);
if (is_mlolink) {
blobmsg_add_string(&bb, "mld_ifname", w->ifs[j].mld_netdev);
blobmsg_add_u32(&bb, "mlo_linkid", link[0].id);
blobmsg_add_macaddr(&bb, "mlo_linkaddr", link[0].macaddr);
}
blobmsg_close_table(&bb, tttt);
}
blobmsg_close_array(&bb, tt);
tt = blobmsg_open_array(&bb, "backhauls");
for (j = 0; j < w->num_wifi_iface; j++) {
char *ifname = w->ifs[j].iface;
struct wifi_mlo_link link[8] = {0};
int nlink = ARRAY_SIZE(link);
bool is_mlolink = false;
uint8_t bssid[6] = {0};
char ssid[64] = {0};
//ifopstatus_t opstatus;
if (w->ifs[j].device[0] &&
!strncmp(w->ifs[j].device, w->wdev[i].device, 16) &&
w->ifs[j].mode == WIFI_MODE_STA) {
;
} else {
continue;
}
if (!strlen(w->ifs[j].mld_netdev)) {
wifi_get_ifstatus(ifname, &ifstat);
if (!!(ifstat & IFF_UP)) {
wifi_get_bssid(ifname, bssid);
wifi_get_ssid(ifname, ssid);
}
} else {
is_mlolink= true;
wifi_get_mlo_links(w->ifs[j].mld_netdev, w->ifs[j].band, link, &nlink);
wifi_get_bssid(w->ifs[j].mld_netdev, bssid);
wifi_get_ssid(w->ifs[j].mld_netdev, ssid);
ifstat |= IFF_UP; //FIXME
}
tttt = blobmsg_open_table(&bb, "");
blobmsg_add_string(&bb, "ifname", ifname);
blobmsg_add_string(&bb, "status", ifstatus_str(ifstat));
if (is_mlolink)
blobmsg_add_string(&bb, "mld_ifname", w->ifs[j].mld_netdev);
if (is_mlolink && nlink == 1) {
blobmsg_add_string(&bb, "ssid", ssid);
blobmsg_add_macaddr(&bb, "bssid", bssid);
blobmsg_add_u32(&bb, "mlo_linkid", link[0].id);
blobmsg_add_macaddr(&bb, "mlo_linkaddr", link[0].macaddr);
}
blobmsg_close_table(&bb, tttt);
}
blobmsg_close_array(&bb, tt);
blobmsg_close_table(&bb, ttt);
}
blobmsg_close_array(&bb, t);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return 0;
}
int wl_help(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return wl_help_command(ctx, obj, req, method, msg, WIFI_OBJECT);
}
int wifi_set_debug(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__DEBUG_LEVEL_MAX];
char *envval = getenv("LIBWIFI_DEBUG_LEVEL");
char new_envval[8] = { 0 };
struct blob_buf bb = {0};
uint16_t debug_level;
debug_level = envval ? atoi(envval) : 0;
blobmsg_parse(wifi_set_debug_policy, __DEBUG_LEVEL_MAX, tb,
blob_data(msg), blob_len(msg));
if (!(tb[DEBUG_LEVEL])) {
blob_buf_init(&bb, 0);
blobmsg_add_u32(&bb, "LIBWIFI_DEBUG_LEVEL: ", debug_level);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
} else {
debug_level = blobmsg_get_u32(tb[DEBUG_LEVEL]);
snprintf(new_envval, 7, "%d", debug_level);
setenv("LIBWIFI_DEBUG_LEVEL", new_envval, 1);
}
return 0;
}
int wl_radio_get_param(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__GET_MAX];
const char *device;
struct blob_buf bb;
int data = 0; // FIXME: data type based on 'param' name
int len = 0; // also len
int ret;
struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
struct wifimngr_device *wdev = (struct wifimngr_device *)wo->priv;
memset(&bb, 0, sizeof(bb));
blobmsg_parse(radio_get_param_policy, __GET_MAX, tb,
blob_data(msg), blob_len(msg));
device = wdev->phy;
ret = wifi_radio_get_param(device, "temperature", &len, &data);
blob_buf_init(&bb, 0);
blobmsg_add_u32(&bb, "status", ret);
if (ret == 0) {
blobmsg_add_u32(&bb, "value_int", data);
}
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int wl_apmld_help(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return wl_help_command(ctx, obj, req, method, msg, WIFI_APMLD_OBJECT);
}
int wl_apmld_status(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
enum wifi_mode mode = WIFI_MODE_UNKNOWN;
struct wifi_ap_stats ifstats = {};
struct wifi_mlo_link link[4];
int num = ARRAY_SIZE(link);
uint8_t macaddr[6] = {0};
uint8_t bssid[6] = {0};
struct blob_buf bb = {};
const char *ifname;
char ssid[64] = {};
void *a, *t;
int ret;
int i;
ifstatus_t ifs = 0;
ifname = ubus_objname_to_ifname(obj);
wifi_get_ssid(ifname, ssid);
wifi_get_mode(ifname, &mode);
ret = wifi_get_mlo_links(ifname, BAND_ANY, link, &num);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
wifi_ap_get_stats(ifname, &ifstats);
wifi_mld_get_ifstatus(ifname, &ifs);
blob_buf_init(&bb, 0);
blobmsg_add_string(&bb, "ifname", ifname);
blobmsg_add_string(&bb, "status", ifstatus_str(ifs));
blobmsg_add_string(&bb, "ssid", ssid);
if_gethwaddr(ifname, macaddr);
blobmsg_add_macaddr(&bb, "macaddr", macaddr);
wifi_get_bssid(ifname, bssid);
blobmsg_add_macaddr(&bb, "bssid", bssid);
wifi_print_mode(&bb, mode);
a = blobmsg_open_array(&bb, "links");
for (i = 0; i < num; i++) {
struct wifi_ap ap = {};
char std_buf2[32] = "802.11";
char sec_str[128] = {0};
char std_buf[32] = {0};
char enc_buf[32] = {0};
if (mode != WIFI_MODE_AP)
continue;
t = blobmsg_open_table(&bb, "");
blobmsg_add_u32(&bb, "id", link[i].id);
wifi_print_band(&bb, link[i].band);
blobmsg_add_u32(&bb, "channel", link[i].channel);
blobmsg_add_u32(&bb, "ccfs0", link[i].ccfs0);
blobmsg_add_u32(&bb, "ccfs1", link[i].ccfs1);
blobmsg_add_u32(&bb, "bandwidth", bw_value(link[i].bandwidth));
blobmsg_add_macaddr(&bb, "macaddr", link[i].macaddr);
if (!wifi_ap_info_band(ifname, link[i].band, &ap)) {
blobmsg_add_u32(&bb, "puncture_bitmap", (int)BUF_GET_LE16(ap.bss.puncture));
wifi_security_str(ap.bss.security, sec_str, sizeof(sec_str));
blobmsg_add_string(&bb, "security", sec_str);
blobmsg_add_string(&bb, "encryption", etostr(ap.bss.rsn.pair_ciphers,
enc_buf, sizeof(enc_buf), 14, wifi_cipherstr));
snprintf(std_buf2 + strlen(std_buf2), 31, "%s",
etostr(ap.bss.oper_std, std_buf, sizeof(std_buf), WIFI_NUM_STD, standard_str));
blobmsg_add_string(&bb, "standard", std_buf2);
blobmsg_add_u32(&bb, "num_stations", ap.bss.load.sta_count);
blobmsg_add_u32(&bb, "max_stations", ap.assoclist_max);
blobmsg_add_u32(&bb, "utilization", ap.bss.load.utilization);
blobmsg_add_u32(&bb, "adm_capacity", ap.bss.load.available);
blobmsg_add_u8(&bb, "hidden", !ap.ssid_advertised ? true : false);
blobmsg_add_u32(&bb, "beacon_int", ap.bss.beacon_int);
blobmsg_add_u32(&bb, "dtim_period", ap.bss.dtim_period);
wl_dump_supp_security(&bb, ap.sec.supp_modes);
wl_dump_capabilities(ap.bss.band, &bb,
&ap.bss.caps,
ap.bss.cbitmap,
sizeof(ap.bss.cbitmap));
//wl_ap_wmm_status(&bb, link[i].ap.ac);
}
blobmsg_close_table(&bb, t);
}
blobmsg_close_array(&bb, a);
t = blobmsg_open_table(&bb, "stats");
if (num)
print_apstats(&bb, &ifstats);
blobmsg_close_table(&bb, t);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int wl_apmld_stats(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct wifi_ap_stats ifstats = {};
struct blob_buf bb = {};
const char *ifname;
void *t;
ifname = ubus_objname_to_ifname(obj);
wifi_ap_get_stats(ifname, &ifstats);
blob_buf_init(&bb, 0);
t = blobmsg_open_table(&bb, "stats");
print_apstats(&bb, &ifstats);
blobmsg_close_table(&bb, t);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
int wl_apmld_assoclist(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_buf bb = {0};
uint8_t stas[768] = {0}; /* 128 * 6 */
const char *ifname;
int nr = 128;
void *a;
int i;
ifname = ubus_objname_to_ifname(obj);
if (wifi_get_assoclist(ifname, stas, &nr) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
a = blobmsg_open_array(&bb, "assoclist");
for (i = 0; i < nr; i++) {
blobmsg_add_macaddr(&bb, "", &stas[i*6]);
}
blobmsg_close_array(&bb, a);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return UBUS_STATUS_OK;
}
static void print_station(struct blob_buf *bb, struct wifi_sta *s, bool mlo)
{
char std_buf2[32] = "802.11";
char std_buf[32] = {0};
blobmsg_add_u32(bb, "link_id", s->mlo_link_id);
if (mlo) {
blobmsg_add_macaddr(bb, "link_macaddr", s->macaddr);
blobmsg_add_macaddr(bb, "link_bssid", s->bssid);
}
wifi_print_band(bb, s->band);
snprintf(std_buf2 + strlen(std_buf2), 31, "%s",
etostr(s->oper_std, std_buf, sizeof(std_buf), WIFI_NUM_STD, standard_str));
blobmsg_add_string(bb, "standard", std_buf2);
blobmsg_add_u32(bb, "channel", s->channel);
blobmsg_add_u32(bb, "bandwidth", bw_value(s->bandwidth));
blobmsg_add_u32(bb, "nss", s->nss);
blobmsg_add_u32(bb, "rssi_avg", s->rssi_avg);
blobmsg_add_u32(bb, "idle", s->idle_time);
blobmsg_add_u64(bb, "in_network", s->conn_time);
blobmsg_add_u32(bb, "tx_airtime", s->tx_airtime);
blobmsg_add_u32(bb, "rx_airtime", s->rx_airtime);
blobmsg_add_u32(bb, "maxrate", s->maxrate);
blobmsg_add_u32(bb, "tx_throughput", s->tx_thput);
blobmsg_add_u32(bb, "est_rx_thput", s->est_rx_thput);
blobmsg_add_u32(bb, "est_tx_thput", s->est_tx_thput);
wl_dump_capabilities(s->band, bb, &s->caps, s->cbitmap, sizeof(s->cbitmap));
wl_print_rate(bb, "tx_rate_latest", &s->tx_rate);
wl_print_rate(bb, "rx_rate_latest", &s->rx_rate);
wl_print_rssi_chains(bb, "rssi_per_antenna", s->rssi);
}
int wl_apmld_stations(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
struct blob_attr *tb[__STAINFO_MAX];
uint8_t sta_macaddr[6] = {0};
char sta_macstr[18] = {0};
struct blob_buf bb = {0};
uint8_t stas[768] = {0}; /* 128 * 6 */
const char *ifname;
int nr = 128;
void *a, *r, *t;
int ret = UBUS_STATUS_OK;
ifname = ubus_objname_to_ifname(obj);
blobmsg_parse(stainfo_policy, __STAINFO_MAX, tb, blob_data(msg), blob_len(msg));
if ((tb[STAINFO_MACADDR])) {
strncpy(sta_macstr, blobmsg_data(tb[STAINFO_MACADDR]), sizeof(sta_macstr)-1);
if (hwaddr_aton(sta_macstr, sta_macaddr) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
}
if (wifi_get_assoclist(ifname, stas, &nr) != 0)
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
a = blobmsg_open_array(&bb, "stations");
for (int i = 0; i < nr; i++) {
uint8_t *macaddr = &stas[i*6];
struct wifi_mlsta mlsta = {0};
if (!hwaddr_is_zero(sta_macaddr) && memcmp(sta_macaddr, macaddr, 6))
continue;
ret = wifi_get_mlsta_info(ifname, macaddr, &mlsta);
if (ret) {
ret = UBUS_STATUS_UNKNOWN_ERROR;
goto out;
}
t = blobmsg_open_table(&bb, "");
blobmsg_add_macaddr(&bb, "macaddr", mlsta.sta[0].mld_macaddr);
if (sta_ratings_calc) {
float rating = -1.0;
rating = sta_ratings_calc(NULL, ifname, &mlsta.sta[0]);
blobmsg_add_double(&bb, "rating", rating);
}
blobmsg_add_macaddr(&bb, "bssid", mlsta.mlo_capable ? mlsta.sta[0].mld_bssid : mlsta.sta[0].bssid);
blobmsg_add_u8(&bb, "mlo_capable", mlsta.mlo_capable ? true : false);
if (mlsta.mlo_capable) {
r = blobmsg_open_array(&bb, "mlo_links");
for (int y = 0; y < mlsta.num_link; y++) {
struct wifi_sta *s = &mlsta.sta[y];
void *tt;
tt = blobmsg_open_table(&bb, "");
print_station(&bb, s, true);
blobmsg_close_table(&bb, tt);
}
blobmsg_close_array(&bb, r);
} else {
print_station(&bb, &mlsta.sta[0], false);
}
wl_print_sta_stats(&bb, &mlsta.stats);
blobmsg_close_table(&bb, t);
}
blobmsg_close_array(&bb, a);
ubus_send_reply(ctx, req, bb.head);
out:
blob_buf_free(&bb);
return ret;
}
int wl_apmld_sta_ratings(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
//struct wifi_ubus_object *wo = container_of(obj, struct wifi_ubus_object, obj);
//struct wifimngr_iface *iface = (struct wifimngr_iface *)wo->priv;
struct blob_attr *tb[__STAINFO_MAX];
uint8_t sta_macaddr[6] = {0};
int ret = UBUS_STATUS_OK;
uint8_t stas[768] = {0};
//struct wifi_ap ap = {0};
char macstr[18] = {0};
const char *ifname;
struct blob_buf bb = {0};
int num_stas = 64;
void *a;
ifname = ubus_objname_to_ifname(obj);
blobmsg_parse(stainfo_policy, __STAINFO_MAX, tb, blob_data(msg), blob_len(msg));
if ((tb[STAINFO_MACADDR])) {
strncpy(macstr, blobmsg_data(tb[STAINFO_MACADDR]), sizeof(macstr) - 1);
if (hwaddr_aton(macstr, sta_macaddr) == NULL)
return UBUS_STATUS_INVALID_ARGUMENT;
}
ret = wifi_get_assoclist(ifname, stas, &num_stas);
if (ret)
return UBUS_STATUS_UNKNOWN_ERROR;
blob_buf_init(&bb, 0);
a = blobmsg_open_array(&bb, "sta-ratings");
for (int i = 0; i < num_stas; i++) {
uint8_t *sta = &stas[i*6];
struct wifi_mlsta mlsta = {0};
float rating = -1.0;
void *t;
if (!hwaddr_is_zero(sta_macaddr) && memcmp(sta_macaddr, sta, 6))
continue;
if (wifi_get_mlsta_info(ifname, sta, &mlsta))
continue;
if (sta_ratings_calc)
rating = sta_ratings_calc(NULL, ifname, &mlsta.sta[0]);
t = blobmsg_open_table(&bb, "");
blobmsg_add_macaddr(&bb, "macaddr", mlsta.sta[0].mld_macaddr);
if (sta_ratings_calc)
blobmsg_add_double(&bb, "rating", rating);
blobmsg_close_table(&bb, t);
}
blobmsg_close_array(&bb, a);
ubus_send_reply(ctx, req, bb.head);
blob_buf_free(&bb);
return ret;
}
int wl_apmld_disconnect_sta(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return sta_disconnect(ctx, obj, req, method, msg);
}
int wl_mldsta_stats(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return 0;
}
int wl_mldsta_disconnect_ap(struct ubus_context *ctx, struct ubus_object *obj,
struct ubus_request_data *req, const char *method,
struct blob_attr *msg)
{
return wl_sta_disconnect_ap(ctx, obj, req, method, msg);
}
bool wifimngr_is_wifi_device_object_valid(struct wifimngr *w,
struct wifi_ubus_object *wobj)
{
const char *radioname = ubus_objname_to_ifname(&wobj->obj);
int i;
for (i = 0; i < w->num_wifi_device; i++) {
if (strcmp(radioname, w->wdev[i].device) == 0) {
wobj->priv = &w->wdev[i];
return true;
}
}
return false;
}
bool wifimngr_is_wifi_iface_object_valid(struct wifimngr *w,
struct wifi_ubus_object *wobj)
{
const char *ifname = ubus_objname_to_ifname(&wobj->obj);
int i;
for (i = 0; i < w->num_wifi_iface; i++) {
if (strcmp(ifname, w->ifs[i].iface) == 0 &&
w->ifs[i].disabled == 0) {
wobj->priv = &w->ifs[i];
return true;
}
}
return false;
}
int wifimngr_setup_events(struct wifimngr *w, const char *evmap_file)
{
struct json_object *jevs, *jev_array;
int len = 0;
int ret = 0;
int i, j;
if (!w)
return -1;
/* read from json file about events to listen for */
jevs = json_object_from_file(evmap_file);
if (!jevs) {
wifimngr_err("Failed to open '%s'\n", evmap_file);
return -1;
}
if (!json_object_is_type(jevs, json_type_object))
goto out_json;
json_object_object_get_ex(jevs, "events", &jev_array);
len = json_object_array_length(jev_array);
for (i = 0; i < len; i++) {
struct json_object *jev;
struct json_object *jev_family, *jev_ifname;
struct json_object *jev_group_array, *jev_grp;
const char *ifname, *family, *group;
int grplen = 0;
jev = json_object_array_get_idx(jev_array, i);
json_object_object_get_ex(jev, "ifname", &jev_ifname);
ifname = json_object_get_string(jev_ifname);
json_object_object_get_ex(jev, "family", &jev_family);
family = json_object_get_string(jev_family);
json_object_object_get_ex(jev, "group", &jev_group_array);
grplen = json_object_array_length(jev_group_array);
for (j = 0; j < grplen; j++) {
jev_grp = json_object_array_get_idx(jev_group_array, j);
group = json_object_get_string(jev_grp);
wifimngr_dbg("Setup event (%s, %s, %s)\n",
ifname, family, group);
ret = wifimngr_events_register(w, ifname, family, group);
if (ret < 0)
wifimngr_err("event_register failed %d\n", ret);
}
}
out_json:
json_object_put(jevs);
return ret;
}
struct wifimngr_device *wifimngr_lookup_wifi_device(struct wifimngr *w,
const char *device)
{
int i;
for (i = 0; i < WIFI_DEV_MAX_NUM; i++) {
if (!strlen(w->wdev[i].device))
continue;
if (!strcmp(w->wdev[i].device, device))
return &w->wdev[i];
}
return NULL;
}
int wifimngr_reconfig(struct wifimngr *w)
{
struct wifi_ubus_object *p = NULL, *tmp;
int ret;
int i;
#ifdef WIFI_CACHE_SCANRESULTS
wifimngr_flush_scanresults(w);
#endif
/* read radios and interfaces from wireless config */
ret = wifimngr_get_wifi_devices(w, w->conffile);
if (ret < 0) {
wifimngr_err("get_wifi_devices() ret = %d\n", ret);
return -1;
}
/* remove wifi radio objects that are no longer valid and
* update 'priv' context for the ones that are still valid.
*/
list_for_each_entry_safe(p, tmp, &w->radiolist, list) {
if (!wifimngr_is_wifi_device_object_valid(w, p)) {
list_del(&p->list);
wifimngr_del_object(w, &p->obj, true);
p->priv = NULL;
free(p);
}
}
/* add wifi radio objects */
for (i = 0; i < w->num_wifi_device; i++) {
ret = wifimngr_add_radio_object(w, &w->wdev[i]);
if (ret) {
wifimngr_err("Failed to add '%s.%s' ubus object: %s\n",
WIFI_RADIO_OBJECT, w->wdev[i].device,
ubus_strerror(ret));
return -1;
}
}
ret = wifimngr_get_wifi_interfaces(w, w->conffile);
if (ret < 0) {
wifimngr_err("get_wifi_interfaces() ret = %d\n", ret);
return -1;
}
ret = wifimngr_get_wifi_mlds(w, w->conffile);
if (ret < 0) {
wifimngr_err("get_wifi_mlds() ret = %d\n", ret);
return -1;
}
/* remove wifi.ap* and wifi.bsta* objects that are no longer valid and
* update 'priv' context for the ones that are still valid.
*/
list_for_each_entry_safe(p, tmp, &w->iflist, list) {
if (!wifimngr_is_wifi_iface_object_valid(w, p)) {
list_del(&p->list);
wifimngr_del_object(w, &p->obj, true);
p->priv = NULL;
free(p);
}
}
/* add wifi.ap/bsta.* objects */
for (i = 0; i < w->num_wifi_iface; i++) {
if (w->ifs[i].disabled)
continue;
#ifdef DONOT_CREATE_AFFILIATED_IFACE_OBJECT
/* skip creating interface object for affiliated interfaces */
if (strlen(w->ifs[i].mld))
continue;
#endif
ret = wifimngr_add_interface_object(w, &w->ifs[i]);
if (ret) {
wifimngr_err("Failed to add '%s.%s' ubus object: %s\n",
WIFI_AP_OBJECT, w->ifs[i].iface, ubus_strerror(ret));
return -1;
}
}
/* add wifi.apmld.* or wifi.bstamld.* objects */
for (i = 0; i < w->num_wifi_mld; i++) {
ret = wifimngr_add_mld_interface_object(w, &w->mld[i]);
if (ret) {
wifimngr_err("Failed to add '%s.%s' ubus object: %s\n",
w->mld[i].mode == WIFI_MODE_AP ?
WIFI_APMLD_OBJECT : WIFI_BSTAMLD_OBJECT,
w->mld[i].ifname, ubus_strerror(ret));
return -1;
}
}
wifimngr_event_exit(w);
wifimngr_setup_events(w, w->evmap_file);
#ifdef WIFI_CACHE_SCANRESULTS
wifimngr_get_initial_scanresults(w);
#endif
return 0;
}
#ifdef WIFI_CACHE_SCANRESULTS
int wifimngr_device_init_channels(struct wifimngr_device *wdev)
{
struct wifi_opclass supp_opclass[64] = {0};
size_t num_opclass = ARRAY_SIZE(supp_opclass);
uint32_t supp_channels[64] = {0};
int num_supp_channels = 64;
uint8_t channels[256] = {0};
wifi_get_opclass_e4table(wdev->band, WIFI_BANDWIDTH_20, &num_opclass, supp_opclass);
wifi_get_supp_channels(wdev->phy, supp_channels, &num_supp_channels, "", wdev->band, BW20);
for (int i = 0; i < num_opclass; i++) {
for (int j = 0; j < supp_opclass[i].opchannel.num; j++) {
if (is_opclass_chgrp_supported(supp_opclass[i].opchannel.ch[j].ctrl_channels,
num_supp_channels, supp_channels)) {
for (int k = 0; k < 32 && supp_opclass[i].opchannel.ch[j].ctrl_channels[k] != 0; k++) {
channels[supp_opclass[i].opchannel.ch[j].ctrl_channels[k]] = 1;
}
}
}
}
for (int i = 0; i < sizeof(channels); i++) {
if (channels[i] == 1) {
wdev->ch[wdev->num_ch].channel = i;
wdev->ch[wdev->num_ch++].freq = wifi_channel_to_freq_ex(i, wdev->band);
}
}
return 0;
}
static void wifimngr_flush_wifi_device_scanresults(struct wifimngr_device *wdev)
{
int k;
for (k = 0; k < wdev->num_ch; k++) {
if (wdev->ch[k].num_scanres > 0 || wdev->ch[k].scanres) {
free(wdev->ch[k].scanres);
wdev->ch[k].scanres = NULL;
wdev->ch[k].num_scanres = 0;
}
}
}
void wifimngr_flush_scanresults(struct wifimngr *w)
{
int i;
for (i = 0; i < w->num_wifi_device; i++) {
struct wifimngr_device *wdev = &w->wdev[i];
wifimngr_flush_wifi_device_scanresults(wdev);
}
}
static int wifimngr_update_scanresults(struct wifimngr_device *wdev,
int num_scanres,
struct wifi_bss *scanres)
{
int i;
for (i = 0; i < num_scanres; i++) {
struct wifimngr_channel *ch =
wifimngr_device_lookup_channel(wdev, scanres[i].channel);
if (ch && ch->num_scanres < MAX_NUM_PER_CHANNEL_SCANRES) {
struct wifi_bss *n;
n = realloc(ch->scanres, (ch->num_scanres + 1) * sizeof(struct wifi_bss));
if (!n) {
wifimngr_warn("%s: failed to realloc() scanres\n", wdev->device);
return -1;
}
ch->scanres = n;
memset(&ch->scanres[ch->num_scanres], 0, sizeof(struct wifi_bss));
memcpy(&ch->scanres[ch->num_scanres], &scanres[i], sizeof(struct wifi_bss));
time(&ch->scanres_ts);
ch->num_scanres++;
}
}
return 0;
}
int wifimngr_get_initial_scanresults(struct wifimngr *w)
{
int i;
for (i = 0; i < w->num_wifi_device; i++) {
struct wifi_bss scanres[NUM_SCANRES] = {0};
int num = ARRAY_SIZE(scanres);
int ret;
wifimngr_device_init_channels(&w->wdev[i]);
ret = wifi_get_band_scan_results(w->wdev[i].phy,
w->wdev[i].band,
scanres,
&num);
if (!ret && num > 0)
wifimngr_update_scanresults(&w->wdev[i], num, scanres);
}
return 0;
}
/* scan event data */
enum scan_event_data_attr {
SCAN_EVENT_ATTR_FREQ,
SCAN_EVENT_ATTR_SSID,
NUM_ATTRS_SCAN_EVENT,
};
static const struct blobmsg_policy scan_event_policy[NUM_ATTRS_SCAN_EVENT] = {
[SCAN_EVENT_ATTR_FREQ] = { .name = "freq", .type = BLOBMSG_TYPE_ARRAY },
[SCAN_EVENT_ATTR_SSID] = { .name = "ssid", .type = BLOBMSG_TYPE_ARRAY },
};
int wifimngr_update_scanresults_cache(struct wifimngr_device *wdev, int sz,
char *evbuf)
{
struct blob_attr *tb[NUM_ATTRS_SCAN_EVENT];
struct wifi_bss scanres[NUM_SCANRES] = {0};
uint32_t freq[MAX_NUM_SUPP_CHANNELS] = {0};
int num = ARRAY_SIZE(scanres);
struct blob_attr *msg, *attr;
struct blob_buf b = {0};
int num_freq = 0;
int num_ssid = 0;
int nfreq = 0;
//int nssid = 0;
int ret = 0;
int rem, i;
ret = wifi_get_band_scan_results(wdev->phy, wdev->band, scanres, &num);
if (ret) {
wifimngr_warn("%s: failed to get scanresults\n", wdev->device);
return -1;
}
blob_buf_init(&b, 0);
if (!blobmsg_add_json_from_string(&b, evbuf)) {
wifimngr_warn("Failed to parse scan-event data: %s\n", evbuf);
blob_buf_free(&b);
return -1;
}
msg = b.head;
blobmsg_parse(scan_event_policy, NUM_ATTRS_SCAN_EVENT, tb,
blob_data(msg), blob_len(msg));
if (tb[SCAN_EVENT_ATTR_FREQ])
num_freq = blobmsg_check_array(tb[SCAN_EVENT_ATTR_FREQ], BLOBMSG_TYPE_INT32);
if (tb[SCAN_EVENT_ATTR_SSID])
num_ssid = blobmsg_check_array(tb[SCAN_EVENT_ATTR_SSID], BLOBMSG_TYPE_STRING);
blobmsg_for_each_attr(attr, tb[SCAN_EVENT_ATTR_FREQ], rem) {
if (blobmsg_type(attr) != BLOBMSG_TYPE_INT32)
break;
freq[nfreq++] = blobmsg_get_u32(attr);
}
if (nfreq != num_freq) {
wifimngr_warn("Ignore invalid freq data in scan-event\n");
blob_buf_free(&b);
return -1;
}
blobmsg_for_each_attr(attr, tb[SCAN_EVENT_ATTR_SSID], rem) {
const char *ssid = NULL;
if (blobmsg_type(attr) != BLOBMSG_TYPE_STRING)
break;
ssid = blobmsg_get_string(attr);
UNUSED(ssid);
}
//TODO: update ssid specific result
UNUSED(num_ssid);
/* flush stored scanresults for scannned freqs */
for (i = 0; i < nfreq; i++) {
struct wifimngr_channel *ch =
wifimngr_device_lookup_freq(wdev, freq[i]);
if (ch && ch->num_scanres > 0) {
free(ch->scanres);
ch->scanres = NULL;
ch->num_scanres = 0;
}
}
wifimngr_update_scanresults(wdev, num, scanres);
blob_buf_free(&b);
return 0;
}
#endif /* WIFI_CACHE_SCANRESULTS */
int wl_sta_ratings_recalc(void *libctx)
{
struct radio_entry radios[4] = {0};
int num_radio = 4;
int ret;
ret = wifi_radio_list(radios, &num_radio);
if (ret)
return -1;
for (int i = 0; i < num_radio; i++) {
struct iface_entry ifaces[16] = {0};
int num_iface = 16;
ret = wifi_list_iface(radios[i].name, ifaces, &num_iface);
if (ret)
continue;
for (int j = 0; j < num_iface; j++) {
unsigned char stas[384] = {0};
int num_stas = 64;
if (ifaces[j].mode != WIFI_MODE_AP)
continue;
ret = wifi_get_assoclist(ifaces[j].name, stas, &num_stas);
if (ret)
continue;
for (int k = 0; k < num_stas; k++) {
struct wifi_sta sta = {0};
float rating = 0.0;
ret = wifi_get_sta_info(ifaces[j].name, &stas[k*6], &sta);
if (ret)
continue;
if (sta_ratings_calc) {
rating = sta_ratings_calc(NULL, ifaces[j].name, &sta);
wifimngr_dbg("STA " MACFMT": Rating = %.2f\n\n",
MAC2STR(sta.macaddr), rating);
}
}
}
}
return 0;
}
static void wifimngr_hbtimer_cb(struct uloop_timeout *t)
{
struct wifimngr *w = container_of(t, struct wifimngr, hbtimer);
w->ticks++;
switch (signal_pending) {
case SIGHUP:
wifimngr_dbg("Received SIGHUP.\n");
signal_pending = 0;
wifimngr_reconfig(w);
break;
case SIGINT:
case SIGTERM:
wifimngr_dbg("Received signal %d, exiting.\n", signal_pending);
uloop_end();
return;
default:
break;
}
if ((w->ticks % 5 == 0) && libsta_ratings)
wl_sta_ratings_recalc(NULL);
uloop_timeout_set(&w->hbtimer, 1000);
}
int wifimngr_init(struct wifimngr **w, struct wifimngr_cmdline_opts *opts)
{
struct wifimngr *wm;
int ret;
set_sighandler(SIGPIPE, SIG_IGN);
set_sighandler(SIGHUP, wifimngr_sighandler);
set_sighandler(SIGINT, wifimngr_sighandler);
set_sighandler(SIGTERM, wifimngr_sighandler);
wm = calloc(1, sizeof(struct wifimngr));
if (!wm)
return -ENOMEM;
wifimngr_extract_help();
wm->hbtimer.cb = wifimngr_hbtimer_cb;
INIT_LIST_HEAD(&wm->radiolist);
INIT_LIST_HEAD(&wm->iflist);
INIT_LIST_HEAD(&wm->event_list);
wm->conffile = opts->conffile;
wm->evmap_file = opts->evmap_file;
*w = wm;
ret = wl_load_sta_ratings_lib("LIBWIFI_STA_RATINGS_SOFILE");
if (ret < 0)
wifimngr_warn("failed to load sta-ratings lib (ret = %d)\n", ret);
ret = wifimngr_get_wifi_devices(wm, opts->conffile);
if (ret < 0)
wifimngr_err("failed to read wifi-device (ret = %d)\n", ret);
ret = wifimngr_get_wifi_interfaces(wm, opts->conffile);
if (ret < 0)
wifimngr_err("failed to read wifi-iface (ret = %d)\n", ret);
uloop_init();
#ifdef HAS_UBUS
wm->ubus_ctx = ubus_connect(opts->ubus_socket);
if (!wm->ubus_ctx) {
wifimngr_err("Failed to connect to ubus\n");
goto out_error;
}
ubus_add_uloop(wm->ubus_ctx);
#endif
ret = wifimngr_setup_events(wm, opts->evmap_file);
if (ret)
wifimngr_err("Failed to setup wifimngr events\n");
ret = wifimngr_add_objects(wm);
if (ret) {
wifimngr_err("Failed to add wifimngr objects! aborting..\n");
goto out_error;
}
#ifdef WIFI_CACHE_SCANRESULTS
wifimngr_get_initial_scanresults(wm);
#endif
uloop_timeout_set(&wm->hbtimer, 1000);
return 0;
out_error:
uloop_done();
wifimngr_event_exit(wm);
#ifdef HAS_UBUS
if (wm->ubus_ctx)
ubus_free(wm->ubus_ctx);
#endif
wifimngr_free_help();
wl_unload_sta_ratings_lib();
#ifdef WIFI_CACHE_SCANRESULTS
wifimngr_flush_scanresults(wm);
#endif
free(wm);
return -1;
}
int wifimngr_run(struct wifimngr *w)
{
UNUSED(w);
uloop_run();
return 0;
}
int wifimngr_exit(struct wifimngr *w)
{
struct wifi_ubus_object *p = NULL, *tmp;
if (!w)
return -EINVAL;
#ifdef WIFI_CACHE_SCANRESULTS
wifimngr_flush_scanresults(w);
#endif
wl_unload_sta_ratings_lib();
list_for_each_entry_safe(p, tmp, &w->iflist, list) {
list_del(&p->list);
wifimngr_del_object(w, &p->obj, false);
free(p);
}
p = NULL;
list_for_each_entry_safe(p, tmp, &w->radiolist, list) {
list_del(&p->list);
wifimngr_del_object(w, &p->obj, false);
free(p);
}
list_del_init(&w->iflist);
list_del_init(&w->radiolist);
wifimngr_del_object(w, &w->wifi_obj, false);
free(w->wifi_obj.type);
wifimngr_del_object(w, &w->wifi_wps_obj, false);
free(w->wifi_wps_obj.type);
wifimngr_event_exit(w);
#ifdef HAS_UBUS
ubus_free(w->ubus_ctx);
#endif
w->ubus_ctx = NULL;
free(w);
wifimngr_free_help();
return 0;
}
int wifimngr_main(struct wifimngr_cmdline_opts *opts)
{
struct wifimngr *w = NULL;
int ret;
restart_logging(opts);
ret = wifimngr_init(&w, opts);
if (ret)
return ret;
wifimngr_run(w);
wifimngr_exit(w);
stop_logging();
return 0;
}
void wifimngr_version(void)
{
printf("version : %s.%s\n", wifimngr_base_version, wifimngr_xtra_version);
printf("libwifi version: %s\n", libwifi_get_version());
}