Newer
Older
/*
* bcm.c - implements for Broadcom wifi
*
* Copyright (C) 2019 iopsys Software Solutions AB. All rights reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <net/if.h>
#include <stdbool.h>
int
wl_ether_atoe(const char *a, struct wl_ether_addr *n)
{
char *c = NULL;
int i = 0;
memset(n, 0, ETHER_ADDR_LEN);
for (;;) {
n->octet[i++] = (uint8)strtoul(a, &c, 16);
if (!*c++ || i == ETHER_ADDR_LEN)
break;
a = c;
}
return (i == ETHER_ADDR_LEN);
}
char *
wl_ether_etoa(const struct wl_ether_addr *n)
{
static char etoa_buf[ETHER_ADDR_LEN * 3];
char *c = etoa_buf;
int i;
for (i = 0; i < ETHER_ADDR_LEN; i++) {
if (i)
*c++ = ':';
c += sprintf(c, "%02X", n->octet[i] & 0xff);
}
return etoa_buf;
}
static int wl_ioctl(const char *ifname, int cmd, void *buf, int len)
int s;
wioc.cmd = cmd;
wioc.buf = buf;
wioc.len = len;
strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
ifr.ifr_data = (caddr_t) &wioc;
s = socket(AF_INET, SOCK_DGRAM, 0);
if (s < 0)
return -1;
fcntl(s, F_SETFD, fcntl(s, F_GETFD) | FD_CLOEXEC);
if (ioctl(s, SIOCDEVPRIVATE, &ifr) < 0) {
close(s);
return -1;
}
close(s);
return 0;
}
static int wl_iovar(const char *ifname, int set, char *iovar,
void *param, int paramlen,
void *bufptr, int buflen)
{
int len;
int total_len;
len = strlen(iovar) + 1;
total_len = len + paramlen;
if (buflen < total_len)
return -1;
snprintf(bufptr, buflen, "%s", iovar);
memcpy((uint8_t *)bufptr + len, param, paramlen);
if (set)
return wl_ioctl(ifname, WLC_SET_VAR, bufptr, total_len);
return wl_ioctl(ifname, WLC_GET_VAR, bufptr, buflen);
int wl_iovar_set(const char *ifname, char *iovar, void *param, int paramlen,
void *buf, int buflen)
{
return wl_iovar(ifname, 1, iovar, param, paramlen, buf, buflen);
}
int wl_iovar_get_noparam(const char *ifname, char *iovar, void *bufptr, int buflen)
{
return wl_iovar(ifname, 0, iovar, NULL, 0, bufptr, buflen);
}
int wl_iovar_get(const char *ifname, char *iovar, void *param, int paramlen,
void *buf, int buflen)
{
return wl_iovar(ifname, 0, iovar, param, paramlen, buf, buflen);
}
static int wl_swap(const char *ifname)
{
int val = 0;
if (wl_ioctl(ifname, WLC_GET_MAGIC, &val, sizeof(int)) < 0)
return 0; /* don't swap on error */
/* is endian swap needed */
if (val == (int)BCMSWAP32(WLC_IOCTL_MAGIC))
return 1;
return 0;
}
#if 0 // TODO: remove
static int bcm_get_ifstatus(const char *ifname, uint32_t *buf)
{
unsigned int isup;
int swap = 0;
swap = wl_swap(ifname);
if (wl_ioctl(ifname, WLC_GET_UP, &isup, sizeof(isup)) < 0)
isup = 0;
*buf = swap ? BCMSWAP32(isup) : isup;
return 0;
}
int bcm_get_isap(char *ifname, int *buf)
{
unsigned int isap;
int swap = 0;
swap = wl_swap(ifname);
if (wl_ioctl(ifname, WLC_GET_AP, &isap, sizeof(isap)) < 0)
return -1;
*buf = swap ? BCMSWAP32(isap) : isap;
return 0;
}
static int bcm_get_oper_band(const char *ifname, enum wifi_band *band)
int swap = 0;
swap = wl_swap(ifname);
if (wl_ioctl(ifname, WLC_GET_BAND, &b, sizeof(b)) < 0) {
return -1;
}
b = swap ? BCMSWAP32(b) : b ;
static int bcm_get_country(const char *ifname, char *alpha2)
{
wl_country_t cc;
if (!alpha2)
return -1;
memset(&cc, 0, sizeof(cc));
if (wl_iovar_get_noparam(ifname, "country", &cc, sizeof(cc)) < 0)
return -1;
snprintf(alpha2, 4, "%s", cc.country_abbrev);
int wl_format_ssid(char *ssid_buf, uint8_t *ssid, int ssid_len)
{
int i, c;
char *p = ssid_buf;
if (ssid_len > 32)
ssid_len = 32;
for (i = 0; i < ssid_len; i++) {
c = (int)ssid[i];
if (c == '\\') {
*p++ = '\\';
*p++ = '\\';
} else if (isprint((uchar)c)) {
*p++ = (char)c;
} else {
p += sprintf(p, "\\x%02X", c);
}
}
*p = '\0';
return p - ssid_buf;
}
int bcm_get_bitrate(char *ifname, unsigned long *buf)
{
int rate = 0;
int swap = 0;
swap = wl_swap(ifname);
if (wl_ioctl(ifname, WLC_GET_RATE, &rate, sizeof(rate)) < 0)
rate = 0;
*buf = swap ? BCMSWAP32(rate) : rate;
static int bcm_get_maxrate(const char *ifname, unsigned long *out)
{
int ioctl_req_version = 0x2000;
wl_bss_info_t *bi;
chanspec_t chspec;
uint16_t vht_rxmap;
int swap = 0;
uint8_t supp_mcs[2] = {0};
int bandwidth = 20;
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
int nss = 0;
int octet = 0;
int max_mcs = -1;
int sgi = 1;
char *tmp;
int l;
swap = wl_swap(ifname);
tmp = malloc(WLC_IOCTL_MAXLEN * sizeof(char));
if (tmp == NULL)
return -1;
memset(tmp, 0, WLC_IOCTL_MAXLEN);
memcpy(tmp, &ioctl_req_version, sizeof(ioctl_req_version));
if (wl_ioctl(ifname, WLC_GET_BSS_INFO, tmp, WLC_IOCTL_MAXLEN) < 0) {
free(tmp);
return -1;
}
bi = (wl_bss_info_t *)(tmp + 4);
chspec = bi->chanspec;
if (CHSPEC_IS160(chspec) || CHSPEC_IS8080(chspec))
bandwidth = 160;
else if (CHSPEC_IS80(chspec))
bandwidth = 80;
else if (CHSPEC_IS40(chspec))
bandwidth = 40;
vht_rxmap = swap ? BCMSWAP16(bi->vht_rxmcsmap) : bi->vht_rxmcsmap;
if (bi->vht_cap) {
*((uint16_t *)supp_mcs) = vht_rxmap;
for (l = 0; l < 16; l += 2) {
uint8_t supp_mcs_mask = 0;
if (l && !(l % 8))
octet++;
supp_mcs_mask = supp_mcs[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;
}
maxrate = wifi_mcs2rate(max_mcs, bandwidth, nss, sgi ? WIFI_SGI : WIFI_LGI);
} else if (bi->n_cap) {
int i;
int bit_i = 0;
int more = 1;
for (i = 0; i < MCSSET_LEN && more; i++) {
for (bit_i = 0; bit_i < 8; bit_i++) {
if (!(bi->basic_mcs[i] & (1 << bit_i))) {
more = 0;
break;
}
max_mcs++;
}
}
nss = (max_mcs / 8) + 1;
max_mcs %= 8;
maxrate = wifi_mcs2rate(max_mcs, bandwidth, nss, sgi ? WIFI_SGI : WIFI_LGI);
*out = (unsigned long)maxrate; /* in Mbps */
free(tmp);
return 0;
}
static int bcm_get_noise(const char *ifname, int *buf)
{
unsigned int noise;
int swap = 0;
swap = wl_swap(ifname);
if (wl_ioctl(ifname, WLC_GET_PHY_NOISE, &noise, sizeof(noise)) < 0)
noise = 0;
*buf = swap ? BCMSWAP32(noise) : noise;
return 0;
}
static int bcm_get_security(const char *ifname, uint32_t *auth, uint32_t *enc)
unsigned int wpa_auth = 0;
unsigned int wsec = 0;
*auth = 0;
*enc = 0;
swap = wl_swap(ifname);
if (wl_ioctl(ifname, WLC_GET_WPA_AUTH, &wpa_auth,
sizeof(wpa_auth)) < 0) {
*auth = AUTH_UNKNOWN;
*enc = CIPHER_UNKNOWN;
return -1;
}
if (wl_ioctl(ifname, WLC_GET_WSEC, &wsec, sizeof(wsec)) < 0) {
*enc = CIPHER_UNKNOWN;
return -1;
}
wpa_auth = swap ? BCMSWAP32(wpa_auth) : wpa_auth;
wsec = swap ? BCMSWAP32(wsec) : wsec;
/* libwifi_dbg("wpa_auth = 0x%x wsec = 0x%x\n", wpa_auth, wsec); */
if (!!(wsec & 0x1)) {
*enc = CIPHER_WEP;
*auth = AUTH_OPEN;
return 0;
}
if (wpa_auth == WPA_AUTH_DISABLED) {
*auth = AUTH_OPEN;
*enc = CIPHER_NONE;
return 0;
}
/* in wpa_auth mode ... */
if (!!(wpa_auth & WPA_AUTH_PSK) && !!(wpa_auth & WPA2_AUTH_PSK)) {
*auth = AUTH_WPAPSK | AUTH_WPA2PSK;
} else if (!!(wpa_auth & WPA2_AUTH_PSK)) {
} else if (!!(wpa_auth & WPA_AUTH_PSK)) {
} else if (!!(wpa_auth & WPA_AUTH_UNSPECIFIED) &&
!!(wpa_auth & WPA2_AUTH_UNSPECIFIED)) {
} else if (!!(wpa_auth & WPA2_AUTH_UNSPECIFIED)) {
}
else if (!!(wpa_auth & WPA_AUTH_UNSPECIFIED)) {
/* else if (wpa_auth & WPA2_AUTH_1X_SHA256)
strcpy(wpa, "1X-SHA256");
else if (wpa_auth & WPA2_AUTH_FT)
strcpy(wpa, "FT"); // TODO
else if (wpa_auth & WPA2_AUTH_PSK_SHA256)
strcpy(wpa, "PSK-SHA256"); */
*enc |= CIPHER_AES;
return 0;
}
int bcm_get_bssinfo(const char *ifname, enum wifi_bw *bandwidth,
uint32_t *channel,
int *noise)
{
wl_bss_info_t *bi;
int ioctl_req_version = 0x2000;
char *tmp;
int swap = 0;
swap = wl_swap(ifname);
tmp = malloc(WLC_IOCTL_MAXLEN * sizeof(char));
if (tmp == NULL)
return -1;
memset(tmp, 0, WLC_IOCTL_MAXLEN);
memcpy(tmp, &ioctl_req_version, sizeof(ioctl_req_version));
if (wl_ioctl(ifname, WLC_GET_BSS_INFO, tmp, WLC_IOCTL_MAXLEN) < 0) {
free(tmp);
return 0;
}
bi = (wl_bss_info_t *)(tmp + 4);
if (channel != NULL) {
if (bi->ctl_ch) {
*channel = bi->ctl_ch;
} else {
chanspec_t chs;
chs = swap ? BCMSWAP16(bi->chanspec) : bi->chanspec;
*channel = CHSPEC_CHANNEL(chs);
}
}
if (noise != NULL)
*noise = bi->phy_noise;
bw = (CHSPEC_IS160(BCMSWAP16(bi->chanspec)) ?
160 : (CHSPEC_IS8080(BCMSWAP16(bi->chanspec)) ?
8080 : (CHSPEC_IS80(BCMSWAP16(bi->chanspec)) ?
80 : (CHSPEC_IS40(BCMSWAP16(bi->chanspec)) ?
40 : (CHSPEC_IS20(BCMSWAP16(bi->chanspec)) ?
bw = (CHSPEC_IS160(bi->chanspec) ?
160 : (CHSPEC_IS8080(bi->chanspec) ?
8080 : (CHSPEC_IS80(bi->chanspec)) ?
80 : (CHSPEC_IS40(bi->chanspec) ?
40 : (CHSPEC_IS20(bi->chanspec) ?
20 : 10))));
}
free(tmp);
return 0;
}
static int bcm_get_bssload_from_beacon(const char *ifname, struct wifi_ap_load *load)
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
{
unsigned char *ie, *ie_start;
int bcnlen, len;
int swap = 0;
unsigned char *buf;
unsigned char bssid[6] = {0};
int offset;
if (wl_ioctl(ifname, WLC_GET_BSSID, bssid, 6))
return -1;
swap = wl_swap(ifname);
buf = malloc(WLC_IOCTL_MAXLEN);
if (buf == NULL)
return -1;
memset(buf, 0, WLC_IOCTL_MAXLEN);
if (wl_iovar_get_noparam(ifname, "beacon_info", &buf[0], WLC_IOCTL_MAXLEN) < 0) {
free(buf);
return -1;
}
bcnlen = *((int *)buf);
bcnlen = swap ? BCMSWAP32(bcnlen) : bcnlen;
if (bcnlen <= 0) {
free(buf);
return -1;
}
/* Don't trust BCM's API -
* Frame starts after the first 4 bytes, which holds sizeof
* returned buffer.
* This however is not always true across all BCM implementations.
* So, look for beacon frame start pattern in the returned buffer.
*/
offset = 4;
while (offset < bcnlen) {
if (buf[offset] == 0x80) {
if (!memcmp(&buf[offset + 4], "\xff\xff\xff\xff\xff\xff", 6) &&
!memcmp(&buf[offset + 10], bssid, 6) &&
!memcmp(&buf[offset + 16], bssid, 6))
/* found start of bcn frame */
break;
}
offset++;
}
if (offset >= bcnlen) {
free(buf);
return -1;
}
/* ie starts after buf_len + mac header + tsf + bcnintv + capinfo */
ie_start = buf + offset + 24 + 8 + 2 + 2;
bcnlen -= (offset + 24 + 8 + 2 + 2);
for (ie = ie_start; ie < ie_start + bcnlen; ie += len + 2) {
len = ie[1];
libwifi_dbg("Elm: 0x%02x len = 0x%02x\n", ie[0], len);
/* QoS Load Elm */
load->sta_count = *((uint16_t *)&ie[2]);
load->utilization = (int)ie[4];
load->utilization *= (float)100 / (float)255;
if (load->utilization > 100)
load->utilization = 100;
load->available = *((uint16_t *)&ie[5]);
libwifi_dbg("load = %d sta_count = %d\n",
load->utilization, load->sta_count);
break;
}
}
free(buf);
return 0;
}
/* This function is almost duplicate of bcm_get_bssload_from_beacon().
* TODO - merge both the functions and cleanup.
*/
static int bcm_get_beacon_ies(const char *ifname, uint8_t *ies, int *ies_len)
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
int bcnlen;
int swap = 0;
unsigned char *buf;
unsigned char bssid[6] = {0};
int offset;
if (wl_ioctl(ifname, WLC_GET_BSSID, bssid, 6))
return -1;
swap = wl_swap(ifname);
buf = malloc(WLC_IOCTL_MAXLEN);
if (buf == NULL)
return -1;
memset(buf, 0, WLC_IOCTL_MAXLEN);
if (wl_iovar_get_noparam(ifname, "beacon_info", &buf[0], WLC_IOCTL_MAXLEN) < 0) {
free(buf);
return -1;
}
bcnlen = *((int *)buf);
bcnlen = swap ? BCMSWAP32(bcnlen) : bcnlen;
if (bcnlen <= 0) {
free(buf);
return -1;
}
offset = 4;
/* Look for beacon frame start in the returned buffer */
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
while (offset < bcnlen) {
if (buf[offset] == 0x80) {
if (!memcmp(&buf[offset + 4], "\xff\xff\xff\xff\xff\xff", 6) &&
!memcmp(&buf[offset + 10], bssid, 6) &&
!memcmp(&buf[offset + 16], bssid, 6))
/* found start of bcn frame */
break;
}
offset++;
}
if (offset >= bcnlen) {
free(buf);
return -1;
}
/* ie starts after buf_len + mac header + tsf + bcnintv + capinfo */
ie_start = buf + offset + 24 + 8 + 2 + 2;
bcnlen -= (offset + 24 + 8 + 2 + 2);
if (*ies_len >= bcnlen) {
memcpy(ies, ie_start, bcnlen);
*ies_len = bcnlen;
} else
*ies_len = 0;
free(buf);
return 0;
}
int bcm_get_oper_stds(const char *ifname, uint8_t *mode)
{
#define INVALID_MODE 0xffffffff
uint32_t gmode = 0;
uint32_t nmode = 0;
uint32_t vhtmode = 0;
unsigned char _iovar_buf[256] = {0};
int swap;
enum wifi_band band = 0;
bool band_is_2g = false;
bool band_is_5g = false;
*mode = 0;
swap = wl_swap(ifname);
bcm_get_oper_band(ifname, &band);
switch (band) {
band_is_5g = true;
break;
band_is_2g = true;
break;
band_is_2g = true;
band_is_5g = true;
break;
default:
break;
}
if (band_is_2g &&
!wl_ioctl(ifname, WLC_GET_GMODE, &gmode, sizeof(gmode))) {
gmode = swap ? BCMSWAP32(gmode) : gmode;
#define GMODE_LEGACY_B 0
#define GMODE_AUTO 1
#define GMODE_ONLY 2
if (gmode == GMODE_LEGACY_B)
else if (gmode == GMODE_AUTO)
*mode |= (WIFI_B | WIFI_G);
else if (gmode == GMODE_ONLY)
} else
gmode = INVALID_MODE;
if (!wl_iovar_get_noparam(ifname, "nmode", &_iovar_buf[0], 256)) {
nmode = *((uint32_t *)_iovar_buf);
nmode = swap ? BCMSWAP32(nmode) : nmode;
if (nmode)
} else
nmode = INVALID_MODE;
memset(_iovar_buf, 0, sizeof(_iovar_buf));
if (band_is_5g &&
!wl_iovar_get_noparam(ifname, "vhtmode", &_iovar_buf[0], 256)) {
vhtmode = *((uint32_t *)_iovar_buf);
vhtmode = swap ? BCMSWAP32(vhtmode) : vhtmode;
if (vhtmode)
*mode |= WIFI_AC;
else if (!(*mode & WIFI_N))
*mode |= WIFI_A;
} else
vhtmode = INVALID_MODE;
/* libwifi_dbg("g = 0x%x n = 0x%x vht = 0x%x\n",
gmode, nmode, vhtmode); */
return 0;
}
static int bcm_wifi_bss_info(const char *ifname, struct wifi_bss *bss)
{
int ioctl_req_version = 0x2000;
struct wl_maclist *macs;
char bufptr[1536] = {0};
//char ssid[33] = {0};
//char bssid[18] = {0};
cca_req_t req, *res;
wl_bss_info_t *bi;
chanspec_t chspec;
int macs_len;
int swap = 0;
char *tmp;
cca_t *c;
uint8_t *ext_ie, *rrm_ie, *ft_ie;
uint8_t *vht_ie, *ht_ie;
uint16_t capability = 0;
uint8_t *ie_start;
uint16_t ie_offset = 0;
uint32_t ie_len = 0;
uint8_t *ies = (uint8_t *)bufptr;
int ies_length = sizeof(bufptr);
swap = wl_swap(ifname);
tmp = malloc(WLC_IOCTL_MAXLEN * sizeof(char));
if (tmp == NULL)
memset(tmp, 0, WLC_IOCTL_MAXLEN);
memcpy(tmp, &ioctl_req_version, sizeof(ioctl_req_version));
if (wl_ioctl(ifname, WLC_GET_BSS_INFO, tmp, WLC_IOCTL_MAXLEN) < 0) {
free(tmp);
return 0;
}
bi = (wl_bss_info_t *)(tmp + 4);
chspec = bi->chanspec;
if (bi->ctl_ch) {
bss->channel = bi->ctl_ch;
} else {
chanspec_t chs;
chs = swap ? BCMSWAP16(bi->chanspec) : bi->chanspec;
bss->channel = CHSPEC_CHANNEL(chs);
}
bss->rssi = swap ? BCMSWAP16(bi->RSSI) : bi->RSSI;
bss->noise = bi->phy_noise;
libwifi_dbg("last_rssi = %d phy_noise = %d\n", bss->rssi, bss->noise);
bw = (CHSPEC_IS160(BCMSWAP16(bi->chanspec)) ?
160 : (CHSPEC_IS8080(BCMSWAP16(bi->chanspec)) ?
8080 : (CHSPEC_IS80(BCMSWAP16(bi->chanspec)) ?
80 : (CHSPEC_IS40(BCMSWAP16(bi->chanspec)) ?
40 : (CHSPEC_IS20(BCMSWAP16(bi->chanspec)) ?
20 : 10)))));
bw = (CHSPEC_IS160(bi->chanspec) ?
160 : (CHSPEC_IS8080(bi->chanspec) ?
8080 : (CHSPEC_IS80(bi->chanspec)) ?
80 : (CHSPEC_IS40(bi->chanspec) ?
40 : (CHSPEC_IS20(bi->chanspec) ?
bss->curr_bw = BW_UNKNOWN;
snprintf((char *)bss->ssid, bi->SSID_len + 1, "%s", bi->SSID);
memcpy(bss->bssid, bi->BSSID.octet, 6);
/* get capabilities */
capability = swap ? BCMSWAP16(bi->capability) : bi->capability;
wifi_cap_set_from_ie(bss->cbitmap, (uint8_t *)&capability, 2);
ie_len = swap ? BCMSWAP32(bi->ie_length) : bi->ie_length;
ie_offset = swap ? BCMSWAP16(bi->ie_offset) : bi->ie_offset;
ie_start = (uint8_t *)((uint8_t *)bi + ie_offset);
if (ie_len == 0) {
if (bcm_get_beacon_ies(ifname, ies, &ies_length) == 0) {
ie_start = ies;
ie_len = ies_length;
}
}
/* {
int _x;
for (_x = 0; _x < ie_len; _x++)
libwifi_dbg("%02X ", ie_start[_x]);
libwifi_dbg("]\n");
ht_ie = wifi_find_ie(ie_start, ie_len, IE_HT_CAP);
vht_ie = wifi_find_ie(ie_start, ie_len, IE_VHT_CAP);
ext_ie = wifi_find_ie(ie_start, ie_len, IE_EXT_CAP);
rrm_ie = wifi_find_ie(ie_start, ie_len, IE_RRM);
ft_ie = wifi_find_ie(ie_start, ie_len, IE_MDE);
he_ie = wifi_find_ie_ext(ie_start, ie_len, IE_EXT_HE_CAP);
wmm_ie = wifi_find_vsie(ie_start, ie_len, (uint8_t *)"\x00\x50\xf2", 2, 0xff);
if (wmm_ie) {
wifi_cap_set(bss->cbitmap, WIFI_CAP_WMM);
wmm_ie += 2 + 3 + 2 + 1; /* skip upto 'version' */
if (!!(wmm_ie[0] & 0x80))
wifi_cap_set(bss->cbitmap, WIFI_CAP_APSD);
}
if (ext_ie) {
bss->caps.valid |= WIFI_CAP_EXT_VALID;
memcpy(bss->caps.ext.byte, &ext_ie[2], min(ext_ie[1], 16));
wifi_cap_set_from_ie(bss->cbitmap, ext_ie, ext_ie[1] + 2);
}
if (rrm_ie) {
bss->caps.valid |= WIFI_CAP_RM_VALID;
memcpy(&bss->caps.rrm, &rrm_ie[2], rrm_ie[1]);
wifi_cap_set_from_ie(bss->cbitmap, rrm_ie, rrm_ie[1] + 2);
}
if (ft_ie) {
wifi_cap_set_from_ie(bss->cbitmap, ft_ie, ft_ie[1] + 2);
}
if (ht_ie) {
bss->caps.valid |= WIFI_CAP_HT_VALID;
memcpy(&bss->caps.ht, &ht_ie[2], ht_ie[1]);
wifi_cap_set_from_ie(bss->cbitmap, ht_ie, ht_ie[1] + 2);
}
if (vht_ie) {
bss->caps.valid |= WIFI_CAP_VHT_VALID;
memcpy(&bss->caps.vht, &vht_ie[2], vht_ie[1]);
wifi_cap_set_from_ie(bss->cbitmap, vht_ie, vht_ie[1] + 2);
}
if (he_ie) {
bss->caps.valid |= WIFI_CAP_HE_VALID;
memcpy(&bss->caps.he, &he_ie[3], min(he_ie[1], sizeof(struct wifi_caps_he)));
wifi_cap_set_from_ie(bss->cbitmap, he_ie, he_ie[1] + 2);
}
bcm_get_security(ifname, &bss->auth, &bss->enc); // TODO: deprecate
wifi_get_bss_security_from_ies(bss, ie_start, ie_len);
/* get bss ch utilization */
memset(bufptr, 0, sizeof(bufptr));
req.chanspec = chspec;
req.num_secs = 1;
if (wl_iovar_get(ifname, "cca_get_stats", &req, sizeof(req), bufptr, 1536) < 0)
return bcm_get_bssload_from_beacon(ifname, &bss->load);
res = (cca_req_t *)bufptr;
if (res->chanspec == 0 || res->num_secs == 0)
return -1;
c = &res->secs[0];
if (swap) {
res->num_secs = BCMSWAP16(res->num_secs);
c->duration = BCMSWAP32(c->duration);
c->congest_ibss = BCMSWAP32(c->congest_ibss);
c->congest_obss = BCMSWAP32(c->congest_obss);
c->interference = BCMSWAP32(c->interference);
c->timestamp = BCMSWAP32(c->timestamp);
}
ibss_pcnt = c->congest_ibss * 100 /c->duration;
obss_pcnt = c->congest_obss * 100 /c->duration;
inter_pcnt = c->interference * 100 /c->duration;
bss->load.utilization = ibss_pcnt + obss_pcnt + inter_pcnt;
if (bss->load.utilization > 100)
bss->load.utilization = 100;
}
/* get connected stas num */
macs_len = 4 + WL_MAX_STA_COUNT * 6;
macs = (struct wl_maclist *)malloc(macs_len);
if (macs == NULL)
memset(macs, 0, macs_len);
macs->count = WL_MAX_STA_COUNT;
if (wl_ioctl(ifname, WLC_GET_ASSOCLIST, macs, macs_len)) {
free(macs);
return -1;
}
macs->count = swap ? BCMSWAP32(macs->count) : macs->count;
bss->load.sta_count = macs->count;
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
static int bcm_get_ap_wmm_params(const char *ifname,
struct wifi_ap_wmm_ac a[])
{
struct edcf_acparam p[WIFI_NUM_AC];
int swap = 0;
int ret;
swap = wl_swap(ifname);
ret = wl_iovar_get_noparam(ifname, "wme_ac_ap", &p, sizeof(p));
if (ret)
return -1;
a[0].ac = BE;
a[0].aifsn = p[0].ACI & EDCF_AIFSN_MASK;
a[0].cwmin = p[0].ECW & EDCF_ECWMIN_MASK;
a[0].cwmax = (p[0].ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
a[0].txop = swap ? BCMSWAP16(p[0].TXOP) : p[0].TXOP;
a[1].ac = BK;
a[1].aifsn = p[1].ACI & EDCF_AIFSN_MASK;
a[1].cwmin = p[1].ECW & EDCF_ECWMIN_MASK;
a[1].cwmax = (p[1].ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
a[1].txop = swap ? BCMSWAP16(p[1].TXOP) : p[1].TXOP;
a[2].ac = VI;
a[2].aifsn = p[2].ACI & EDCF_AIFSN_MASK;
a[2].cwmin = p[2].ECW & EDCF_ECWMIN_MASK;
a[2].cwmax = (p[2].ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
a[2].txop = swap ? BCMSWAP16(p[2].TXOP) : p[2].TXOP;
a[3].ac = VO;
a[3].aifsn = p[3].ACI & EDCF_AIFSN_MASK;
a[3].cwmin = p[3].ECW & EDCF_ECWMIN_MASK;
a[3].cwmax = (p[3].ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
a[3].txop = swap ? BCMSWAP16(p[3].TXOP) : p[3].TXOP;
return 0;
}