diff --git a/libwifi/chlist.c b/libwifi/chlist.c index d4ab5df48c4ec6e700eaeb4700bf031866b1c0a3..b5eb34467b92d79366a47b4b9e4fc8400d128cc7 100644 --- a/libwifi/chlist.c +++ b/libwifi/chlist.c @@ -1754,3 +1754,23 @@ int wifi_get_opclass_pref(const char *name, int *num_opclass, struct wifi_opclas { return radio_get_supported_opclass(name, num_opclass, o, 1); } + +enum wifi_bw bcmwl_opclass_to_bw(uint32_t opclass) +{ + struct wifi_opclass *tab; + struct wifi_opclass *ptr; + int i; + + tab = (struct wifi_opclass *)wifi_opclass_global; + + for (i = 0; i < wifi_opclass_global_size; i++) { + ptr = tab + i; + if (ptr->g_opclass == opclass) + break; + } + + if (i == wifi_opclass_global_size) + return BW20; + + return ptr->bw; +} diff --git a/libwifi/modules/broadcom/brcm.c b/libwifi/modules/broadcom/brcm.c index 72a7c22903ae4d4e750768332dab52981488951d..0b9d231e09cd7f7f349aadbe4d407a3ec5d79d4c 100644 --- a/libwifi/modules/broadcom/brcm.c +++ b/libwifi/modules/broadcom/brcm.c @@ -845,8 +845,9 @@ static int iface_req_beacon_report(const char *ifname, uint8_t *sta, { libwifi_dbg("[%s] %s called\n", ifname, __func__); - if (param_sz > 0 && param != NULL) - return hostapd_cli_iface_req_beacon(ifname, sta, param, param_sz); + if (param_sz > 0 && param != NULL) { + return bcmwl_req_beacon_report(ifname, sta, param, param_sz); + } /* No params passed - use default (minimal) configuration */ return hostapd_cli_iface_req_beacon_default(ifname, sta); diff --git a/libwifi/modules/broadcom/broadcom.h b/libwifi/modules/broadcom/broadcom.h index b1c167145589c3baa9734e3d37ed93adbf84c13a..6ec5a74ad2d36cd0a3441912a9e9b04448f84265 100644 --- a/libwifi/modules/broadcom/broadcom.h +++ b/libwifi/modules/broadcom/broadcom.h @@ -497,6 +497,7 @@ typedef double float64; #define DOT11_BSSTYPE_ANY 2 /* d11 any BSS type */ #define DOT11_SCANTYPE_ACTIVE 0 /* d11 scan active */ #define DOT11_SCANTYPE_PASSIVE 1 /* d11 scan passive */ +#define DOT11_MAX_SSID_LEN 32 /* d11 max ssid length */ /* uint32 list */ typedef struct wl_uint32_list { @@ -1111,6 +1112,27 @@ typedef struct bcnreq { uint16_t reps; } bcnreq_t; +typedef struct { + uint32_t num; + chanspec_t list[1]; +} chanspec_list_t; + +#define WL_RRM_BCN_REQ_VER 1 +typedef struct bcn_req { + uint8_t version; + uint8_t bcn_mode; + uint8_t pad_1[2]; + int32_t dur; + int32_t channel; + struct ether_addr da; + uint16_t random_int; + wlc_ssid_t ssid; + uint16_t reps; + uint8_t req_elements; + uint8_t pad_2; + chanspec_list_t chspec_list; +} bcn_req_t; + #ifdef BCM_imp26 typedef struct wl_bsstrans_req { uint16 tbtt; /* time of BSS to end of life, in unit of TBTT */ diff --git a/libwifi/modules/broadcom/wlctrl.c b/libwifi/modules/broadcom/wlctrl.c index 3520b36505f444e37088ac84fcaf423922b16a89..474f2701f43abc4d07b02acc480c2ef32cb06d77 100644 --- a/libwifi/modules/broadcom/wlctrl.c +++ b/libwifi/modules/broadcom/wlctrl.c @@ -4809,3 +4809,174 @@ int bcmwl_iface_get_4addr_parent(const char *ifname, char *parent) return 0; } + +static int bcm_calc_bcnreq_size(struct wifi_beacon_req *param, size_t param_sz) +{ + int alloc_len = sizeof(bcn_req_t); + uint8_t *pos = NULL; + uint8_t *end = NULL; + int i; + + if (param_sz == 0) + return alloc_len; + + pos = (uint8_t *) param->variable; + end = pos + param_sz - sizeof(struct wifi_beacon_req); + + while (pos < end) { + int channel_num = 0; + + switch (pos[0]) { + case WIFI_BCNREQ_AP_CHAN_REPORT: + if (pos[1] > 1) { + for (i = 0; i < pos[1] - 1; i++) { + channel_num ++; + } + } + if (channel_num > 0) + alloc_len += (sizeof(chanspec_t) * channel_num); + pos += pos[1] + 2; + break; + default: + pos += pos[1] + 2; + break; + } + } + + return alloc_len; +} + +int bcmwl_req_beacon_report(const char *ifname, uint8_t *sta, + struct wifi_beacon_req *param, size_t param_sz) +{ + uint8_t *pos = NULL; + uint8_t *end = NULL; + char buf[512] = {0}; + bcn_req_t *bcnreq; + int swap, i = 0; + int alloc_len = 0; + + if (sta == NULL) { + libwifi_err("[%s] Destination address of STA is required\n", __func__); + return -1; + } + + if (param->mode > WIFI_BCNREQ_MODE_TABLE) { + libwifi_err("[%s] Invalid bcn mode: %i\n", __func__, param->mode); + return -1; + } + + alloc_len = bcm_calc_bcnreq_size(param, param_sz); + bcnreq = calloc(1, alloc_len); + if (!bcnreq) { + libwifi_err("[%s] Failed to alloc bcn req, requested size:%i \n", __func__, alloc_len); + return -1; + } + + swap = wl_swap(ifname); + + memcpy(bcnreq->da.octet, sta, 6); + + bcnreq->version = WL_RRM_BCN_REQ_VER; + bcnreq->bcn_mode = param->mode; + + pos = (uint8_t *) param->variable; + + if (param_sz > 0) + end = pos + param_sz - sizeof(struct wifi_beacon_req); + + while (pos < end) { + int channel_num = 0; + + switch (pos[0]) { + case WIFI_BCNREQ_SSID: + if (pos[1] > DOT11_MAX_SSID_LEN) { + libwifi_err("ERROR: SSID size:%d exceed max allowed: %d\n", pos[1], DOT11_MAX_SSID_LEN); + return -1; + } + bcnreq->ssid.SSID_len = pos[1]; + strncpy(bcnreq->ssid.SSID, &pos[2], bcnreq->ssid.SSID_len); + pos += pos[1] + 2; + break; + case WIFI_BCNREQ_REP_DETAIL: + if (pos[1] == 1) + libwifi_dbg("Beacon req reporting detail:%i\n", pos[2]); + pos += pos[1] + 2; + break; + case WIFI_BCNREQ_REQUEST: + if (pos[1] > 0) + bcnreq->req_elements = 1; + pos += pos[1] + 2; + break; + case WIFI_BCNREQ_AP_CHAN_REPORT: + if (pos[1] > 1) { + for (i = 0; i < pos[1] - 1; i++) { + libwifi_dbg("channel report opclas:%i, ch:%i, bw:%i\n", pos[2], pos[3+i], (int)bcmwl_opclass_to_bw(pos[2])); + channel_num ++; + } + } + if (channel_num > 0) { + enum wifi_bw bw; + chanspec_t chspec_bw, chspec_band; + i = bcnreq->chspec_list.num; + bw = bcmwl_opclass_to_bw(pos[2]); + /* figure out chspec bw */ + switch (bw) { + case BW20: + chspec_bw = WL_CHANSPEC_BW_20; + break; + case BW40: + chspec_bw = WL_CHANSPEC_BW_40; + break; + case BW80: + chspec_bw = WL_CHANSPEC_BW_80; + break; + case BW160: + chspec_bw = WL_CHANSPEC_BW_160; + break; + case BW8080: + chspec_bw = WL_CHANSPEC_BW_8080; + break; + default: + chspec_bw = WL_CHANSPEC_BW_20; + } + for (; i < (bcnreq->chspec_list.num + channel_num); i++) { + if (pos[3 + i] > 35) + chspec_band = WL_CHANSPEC_BAND_5G; + else + chspec_band = 0; + + libwifi_dbg("chanspec list[%i]=0x%x\n", i, (pos[3+i] | chspec_bw | chspec_band)); + bcnreq->chspec_list.list[i] = (pos[3+i] | chspec_bw | chspec_band); + } + bcnreq->chspec_list.num += channel_num; + } + pos += pos[1] + 2; + break; + default: + pos += pos[1] + 2; + break; + } + } + + if (swap) { + bcnreq->dur = BCMSWAP16(param->duration); + bcnreq->random_int = BCMSWAP16(param->rand_interval); + bcnreq->channel = BCMSWAP32(param->channel); + bcnreq->reps = BCMSWAP16(0); + } else { + bcnreq->dur = param->duration; + bcnreq->random_int = param->rand_interval; + bcnreq->channel = param->channel; + bcnreq->reps = 0; + } + + if (wl_iovar_set(ifname, "rrm_bcn_req", bcnreq, alloc_len, buf, 512) < 0) { + libwifi_err("wl_iovar_set error!!\n"); + free(bcnreq); + return -1; + } + + free(bcnreq); + return 0; +} diff --git a/libwifi/modules/broadcom/wlctrl.h b/libwifi/modules/broadcom/wlctrl.h index dacbcfbebbfa5b5fd37c5e52f7595d5a438b389d..116c5c5a93cffef16c2e87af7326113b427468d4 100644 --- a/libwifi/modules/broadcom/wlctrl.h +++ b/libwifi/modules/broadcom/wlctrl.h @@ -59,4 +59,7 @@ int bcmwl_enable_event_bit(const char *ifname, unsigned int bit); int bcmwl_disable_event_bit(const char *ifname, unsigned int bit); int bcmwl_radio_reset_chanim_stats(const char *name); + +int bcmwl_req_beacon_report(const char *ifname, uint8_t *sta, + struct wifi_beacon_req *param, size_t param_sz); #endif /* WLCTRL_H */ diff --git a/libwifi/wifi.h b/libwifi/wifi.h index fcd816ff0a960a973d5704a0a9b63db7c4936a57..d8f080da177e243045a4ea934ece708df5349950 100644 --- a/libwifi/wifi.h +++ b/libwifi/wifi.h @@ -2233,6 +2233,8 @@ int wifi_freq_to_channel(int freq); const int *chan2list(int chan, int bw); +enum wifi_bw bcmwl_opclass_to_bw(uint32_t opclass); + /** Iterator for information elements */ #define wifi_foreach_ie(e, _iebuf, _len) \ for ((e) = (_iebuf); \