blob: 620872a282c326ea30f39ae03cba7b41638697ec [file] [log] [blame]
/*
* Band Steering logic
*
* Feature by which dualband capable PEERs will be
* forced move on 5GHz interface
*
* Copyright (C) 1999-2017, Broadcom Corporation
*
* Unless you and Broadcom execute a separate written software license
* agreement governing use of this software, this software is licensed to you
* under the terms of the GNU General Public License version 2 (the "GPL"),
* available at http://www.broadcom.com/licenses/GPLv2.php, with the
* following added to such license:
*
* As a special exception, the copyright holders of this software give you
* permission to link this software with independent modules, and to copy and
* distribute the resulting executable under terms of your choice, provided that
* you also meet, for each linked independent module, the terms and conditions of
* the license of that module. An independent module is a module which is not
* derived from this software. The special exception does not apply to any
* modifications of the software.
*
* Notwithstanding the above, under no circumstances may you combine this
* software in any way with any other Broadcom software provided under a license
* other than the GPL, without Broadcom's express prior written consent.
*
* <<Broadcom-WL-IPTag/Open:>>
*
* $ Copyright Cypress Semiconductor $
*
* $Id: dhd_bandsteer.c 664149 2017-03-07 13:29:02Z $
*/
#include <typedefs.h>
#include <linuxver.h>
#include <osl.h>
#include <linux/kernel.h>
#include <linux/time.h>
#include <bcmutils.h>
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include <wl_cfg80211.h>
#include <wl_android.h>
#include <wldev_common.h>
#include <dhd_linux_wq.h>
#include <dhd_cfg80211.h>
#include <dhd_bandsteer.h>
#include <dhd_dbg.h>
/* defines */
/* BANDSTEER STATE MACHINE STATES */
#define DHD_BANDSTEER_START 0x0001
#define DHD_BANDSTEER_WNM_FRAME_SEND 0x0002
#define DHD_BANDSTEER_WNM_FRAME_RETRY 0x0004
#define DHD_BANDSTEER_WNM_FRAME_SENT 0x0008
#define DHD_BANDSTEER_TRIAL_DONE 0x0080
#define DHD_BANDSTEER_ON_PROCESS_MASK (DHD_BANDSTEER_START | DHD_BANDSTEER_WNM_FRAME_SEND \
| DHD_BANDSTEER_WNM_FRAME_RETRY | DHD_BANDSTEER_TRIAL_DONE)
#define DHD_BANDSTEER_WNM_FRAME_MAXRETRY 3
#define DHD_BANDSTEER_WNM_FRAME_DELAY 1000
#define MAX_NUM_OF_ASSOCLIST 64
#define DHD_BANDSTEER_MAXIFACES 2
#define CHANNEL_IS_5G(channel) (((channel >= 36) && (channel <= 165)) ? \
true : false)
/* ********************** Function declaration *********************** */
static s32
dhd_bandsteer_addmac_to_monitorlist(dhd_bandsteer_context_t *dhd_bandsteer_cntx, uint8 *mac_addr);
static s32
dhd_bandsteer_remove_mac_from_list(dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac, int all);
static dhd_bandsteer_mac_entry_t*
dhd_bandsteer_look_for_match(dhd_bandsteer_context_t *dhd_bandsteer_cntx, uint8 *mac_addr);
static s32
dhd_bandsteer_tx_wnm_actframe(dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac);
static void
dhd_bandsteer_add_to_black_list(dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac);
extern int
wl_android_set_ap_mac_list(struct net_device *dev, int macmode, struct maclist *maclist);
/* ********************** Function declartion ends ****************** */
static void
dhd_bandsteer_add_timer(dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac, unsigned long expires)
{
dhd_bandsteer_mac->dhd_bandsteer_timer.expires = jiffies + msecs_to_jiffies(expires);
add_timer(&dhd_bandsteer_mac->dhd_bandsteer_timer);
}
static void
dhd_bandsteer_delete_timer(dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac)
{
del_timer(&dhd_bandsteer_mac->dhd_bandsteer_timer);
}
/*
* Idea used to call same callback everytime you conifigure timer
* based on the status of the mac entry next step will be taken
*/
static void
dhd_bandsteer_state_machine(ulong arg)
{
dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac = (dhd_bandsteer_mac_entry_t *)arg;
if (dhd_bandsteer_mac == NULL) {
DHD_ERROR(("%s: dhd_bandsteer_mac is null\n", __FUNCTION__));
return;
}
DHD_TRACE(("%s: Peer STA BandSteer status 0x%x", __FUNCTION__,
dhd_bandsteer_mac->dhd_bandsteer_status));
switch (dhd_bandsteer_mac->dhd_bandsteer_status) {
case DHD_BANDSTEER_START:
case DHD_BANDSTEER_WNM_FRAME_RETRY:
dhd_bandsteer_mac->dhd_bandsteer_status = DHD_BANDSTEER_WNM_FRAME_SEND;
dhd_bandsteer_schedule_work_on_timeout(dhd_bandsteer_mac);
break;
case DHD_BANDSTEER_WNM_FRAME_SEND:
dhd_bandsteer_tx_wnm_actframe(dhd_bandsteer_mac);
if (dhd_bandsteer_mac->wnm_frame_counter < DHD_BANDSTEER_WNM_FRAME_MAXRETRY) {
/* Sending out WNM action frame as soon as assoc indication recieved */
dhd_bandsteer_mac->dhd_bandsteer_status = DHD_BANDSTEER_WNM_FRAME_RETRY;
}
else {
dhd_bandsteer_mac->dhd_bandsteer_status = DHD_BANDSTEER_TRIAL_DONE;
}
break;
case DHD_BANDSTEER_TRIAL_DONE:
dhd_bandsteer_remove_mac_from_list(dhd_bandsteer_mac, 0);
break;
}
return;
}
void
dhd_bandsteer_workqueue_wrapper(void *handle, void *event_info, u8 event)
{
dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac = (dhd_bandsteer_mac_entry_t *)event_info;
if (event != DHD_WQ_WORK_BANDSTEER_STEP_MOVE) {
DHD_ERROR(("%s: unexpected event \n", __FUNCTION__));
return;
}
dhd_bandsteer_state_machine((ulong)dhd_bandsteer_mac);
}
/*
* This API create and initilize an entry into list which need to be processed later
*/
static s32
dhd_bandsteer_addmac_to_monitorlist(dhd_bandsteer_context_t *dhd_bandsteer_cntx, uint8 *mac_addr)
{
dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac;
dhd_bandsteer_mac = kzalloc(sizeof(dhd_bandsteer_mac_entry_t), GFP_KERNEL);
if (unlikely(!dhd_bandsteer_mac)) {
DHD_ERROR(("%s: alloc failed\n", __FUNCTION__));
return BCME_NOMEM;
}
INIT_LIST_HEAD(&dhd_bandsteer_mac->list);
/* pointer dhd_bandsteer_cntx for future use */
dhd_bandsteer_mac->dhd_bandsteer_cntx = dhd_bandsteer_cntx;
dhd_bandsteer_mac->dhd_bandsteer_status = DHD_BANDSTEER_START;
memcpy(&dhd_bandsteer_mac->mac_addr.octet, mac_addr, ETHER_ADDR_LEN);
/* Configure timer for 20 Sec */
init_timer(&dhd_bandsteer_mac->dhd_bandsteer_timer);
dhd_bandsteer_mac->dhd_bandsteer_timer.data = (ulong)dhd_bandsteer_mac;
dhd_bandsteer_mac->dhd_bandsteer_timer.function = dhd_bandsteer_state_machine;
dhd_bandsteer_mac->wnm_frame_counter = 0;
/* Add new entry into the list */
list_add_tail(&dhd_bandsteer_mac->list,
&dhd_bandsteer_cntx->dhd_bandsteer_monitor_list);
DHD_TRACE(("%s: " MACDBG " added into list \n", __FUNCTION__,
MAC2STRDBG(dhd_bandsteer_mac->mac_addr.octet)));
/* This can be tweaked more */
dhd_bandsteer_add_timer(dhd_bandsteer_mac, DHD_BANDSTEER_WNM_FRAME_DELAY);
return BCME_OK;
}
/*
* This function removes one or all mac entry from the list
*/
static s32
dhd_bandsteer_remove_mac_from_list(dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac, int all)
{
dhd_bandsteer_context_t *dhd_bandsteer_cntx;
dhd_bandsteer_mac_entry_t *curr, *next;
DHD_INFO(("%s: entered \n", __FUNCTION__));
/* TODO:probably these sanity lines can be removed */
if (dhd_bandsteer_mac == NULL) {
DHD_ERROR(("%s: dhd_bandsteer_mac is NULL\n", __FUNCTION__));
return BCME_ERROR;
}
dhd_bandsteer_cntx = dhd_bandsteer_mac->dhd_bandsteer_cntx;
list_for_each_entry_safe(curr, next,
&dhd_bandsteer_cntx->dhd_bandsteer_monitor_list, list) {
if ((curr == dhd_bandsteer_mac) || all) {
DHD_ERROR(("%s: " MACDBG " deleted from list \n", __FUNCTION__,
MAC2STRDBG(dhd_bandsteer_mac->mac_addr.octet)));
list_del(&dhd_bandsteer_mac->list);
dhd_bandsteer_delete_timer(dhd_bandsteer_mac);
kfree(dhd_bandsteer_mac);
if (!all)
break;
}
}
return BCME_OK;
}
/*
* Logic to find corresponding node in list based given mac address
* Returns null if entry not seen
*/
static dhd_bandsteer_mac_entry_t*
dhd_bandsteer_look_for_match(dhd_bandsteer_context_t *dhd_bandsteer_cntx, uint8 *mac_addr)
{
dhd_bandsteer_mac_entry_t *curr = NULL, *next = NULL;
list_for_each_entry_safe(curr, next,
&dhd_bandsteer_cntx->dhd_bandsteer_monitor_list, list) {
if (memcmp(&curr->mac_addr.octet, mac_addr, ETHER_ADDR_LEN) == 0) {
return curr;
}
}
return NULL;
}
/*
* This API will send wnm action frame also configure timeout timer
*/
static s32
dhd_bandsteer_tx_wnm_actframe(dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac)
{
dhd_bandsteer_context_t *dhd_bandsteer_cntx = dhd_bandsteer_mac->dhd_bandsteer_cntx;
wl_action_frame_t *action_frame = NULL;
wl_af_params_t *af_params = NULL;
char *smbuf = NULL;
int error = BCME_ERROR;
uint8 *bp;
dhd_bandsteer_iface_info_t *if_info_5g, *if_info_2g;
if_info_5g = &dhd_bandsteer_cntx->bsd_ifaces[dhd_bandsteer_cntx->ifidx_5g];
if_info_2g = &dhd_bandsteer_cntx->bsd_ifaces[!dhd_bandsteer_cntx->ifidx_5g];
smbuf = kzalloc(WLC_IOCTL_MAXLEN, GFP_KERNEL);
if (smbuf == NULL) {
DHD_ERROR(("%s: failed to allocated memory %d bytes\n", __FUNCTION__,
WLC_IOCTL_MAXLEN));
goto send_action_frame_out;
}
af_params = (wl_af_params_t *) kzalloc(WL_WIFI_AF_PARAMS_SIZE, GFP_KERNEL);
if (af_params == NULL) {
DHD_ERROR(("%s: unable to allocate frame\n", __FUNCTION__));
goto send_action_frame_out;
}
af_params->channel = if_info_2g->channel;
af_params->dwell_time = -1;
memcpy(&af_params->BSSID, &dhd_bandsteer_mac->mac_addr.octet, ETHER_ADDR_LEN);
action_frame = &af_params->action_frame;
action_frame->packetId = 0;
memcpy(&action_frame->da, &dhd_bandsteer_mac->mac_addr.octet, ETHER_ADDR_LEN);
dhd_bandsteer_mac->wnm_frame_counter++;
bp = (uint8 *)action_frame->data;
*bp++ = 0xa; /* Category */
*bp++ = 0x7; /* Action ID */
*bp++ = dhd_bandsteer_mac->wnm_frame_counter; /* Dialog Token */
*bp++ = 0x1; /* Request mode */
*bp++ = 0x0; /* disassociation timer has two bytes */
*bp++ = 0x0;
*bp++ = 0x0; /* Validity interval */
*bp++ = 0x34; /* Element ID */
*bp++ = 0xd; /* Len */
memcpy(bp, if_info_5g->macaddr.octet, ETHER_ADDR_LEN);
bp += ETHER_ADDR_LEN;
bp +=4; /* Skip BSSID info 4 bytes in size */
*bp++ = 0x7d; /* Operating class */
*bp++ = if_info_5g->channel; /* Channel number */
*bp = 0x0; /* Phy Type */
action_frame->len = (bp - (uint8 *)&action_frame->data) + 1;
error = wldev_iovar_setbuf(if_info_2g->ndev, "actframe", af_params,
sizeof(wl_af_params_t), smbuf, WLC_IOCTL_MAXLEN, NULL);
if (error) {
DHD_ERROR(("Failed to set action frame, error=%d\n", error));
goto send_action_frame_out;
}
DHD_TRACE(("%s: BSS Trans Req frame sent to " MACDBG " try %d\n", __FUNCTION__,
MAC2STRDBG(dhd_bandsteer_mac->mac_addr.octet),
dhd_bandsteer_mac->wnm_frame_counter));
send_action_frame_out:
/* Re-schedule the timer */
dhd_bandsteer_add_timer(dhd_bandsteer_mac, DHD_BANDSTEER_WNM_FRAME_DELAY);
if (af_params)
kfree(af_params);
if (smbuf)
kfree(smbuf);
if (error)
return BCME_ERROR;
return BCME_OK;
}
/*
* Call dhd_bandsteer_remove_mac_from_list()
* Add into black list of corresponding interace at present 2.4
* Uses existing IOVAR calls
*/
static void
dhd_bandsteer_add_to_black_list(dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac)
{
dhd_bandsteer_context_t *dhd_bandsteer_cntx = dhd_bandsteer_mac->dhd_bandsteer_cntx;
dhd_bandsteer_iface_info_t *if_info_2g;
int err;
int macmode = MACLIST_MODE_DENY;
struct maclist *maclist;
uint8 *pmaclist_ea;
uint8 mac_buf[MAX_NUM_OF_ASSOCLIST *
sizeof(struct ether_addr) + sizeof(uint)] = {0};
if_info_2g = &dhd_bandsteer_cntx->bsd_ifaces[!dhd_bandsteer_cntx->ifidx_5g];
/* Black listing */
DHD_INFO(("%s: Black listing " MACDBG " on 2GHz IF\n", __FUNCTION__,
MAC2STRDBG(dhd_bandsteer_mac->mac_addr.octet)));
/* Get current black list */
if ((err = wldev_ioctl(if_info_2g->ndev, WLC_GET_MACLIST, mac_buf,
sizeof(mac_buf), false)) != 0) {
DHD_ERROR(("%s: WLC_GET_MACLIST error=%d\n", __FUNCTION__, err));
}
maclist = (struct maclist *)mac_buf;
pmaclist_ea = (uint8*) mac_buf +
(sizeof(struct ether_addr) * maclist->count) + sizeof(uint);
maclist->count++;
memcpy(pmaclist_ea, &dhd_bandsteer_mac->mac_addr.octet, ETHER_ADDR_LEN);
if ((err = wldev_iovar_setint(if_info_2g->ndev, "probresp_mac_filter", 1)) != BCME_OK) {
DHD_ERROR(("%s: failed to set probe response filter err %d\n", __FUNCTION__, err));
}
if ((err = wldev_ioctl(if_info_2g->ndev, WLC_SET_MACMODE, &macmode,
sizeof(macmode), true)) != 0) {
DHD_ERROR(("%s: WLC_SET_MACMODE error=%d\n", __FUNCTION__, err));
}
/* set the MAC filter list */
if ((err = wldev_ioctl(if_info_2g->ndev, WLC_SET_MACLIST, maclist,
sizeof(int) + sizeof(struct ether_addr) * maclist->count, true)) != 0) {
DHD_ERROR(("%s: WLC_SET_MACLIST error=%d\n", __FUNCTION__, err));
}
}
/*
* Check if mac association on 2.4 G
* If on 2.4
* * Ignore if we have already run Bandsteer cycle for this
* * Add PEER STA mac monitor_list
* * Send BSS transition request frame
* Else
* * We recieved assoc on 5g from Mac we forced to move onto 5G
*/
s32
dhd_bandsteer_trigger_bandsteer(struct net_device *ndev, uint8 *mac_addr)
{
struct wireless_dev *__wdev = (struct wireless_dev *)(ndev)->ieee80211_ptr;
struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)wiphy_priv(__wdev->wiphy);
struct net_device *netdev_5g = NULL;
dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac = NULL;
dhd_bandsteer_context_t *dhd_bandsteer_cntx = NULL;
DHD_TRACE(("%s: Start band-steer procedure for " MACDBG "\n", __FUNCTION__,
MAC2STRDBG(mac_addr)));
if (cfg == NULL) {
DHD_ERROR(("%s: bcmcfg is null\n", __FUNCTION__));
return BCME_ERROR;
}
if (cfg->dhd_bandsteer_cntx == NULL) {
DHD_ERROR(("%s: Band Steering not enabled\n", __FUNCTION__));
return BCME_ERROR;
}
dhd_bandsteer_cntx = cfg->dhd_bandsteer_cntx;
netdev_5g = dhd_bandsteer_cntx->bsd_ifaces[dhd_bandsteer_cntx->ifidx_5g].ndev;
dhd_bandsteer_mac = dhd_bandsteer_look_for_match(dhd_bandsteer_cntx, mac_addr);
if (dhd_bandsteer_mac == NULL) {
/*
* This STA entry not found in list check if bandsteer is already done for this
* Check if this on 2.4/5Ghz
*/
if (ndev == netdev_5g) {
/* Ignore as device aleady connectd to 5G */
DHD_INFO(("%s: " MACDBG " is on 5GHz interface\n", __FUNCTION__,
MAC2STRDBG(mac_addr)));
return BCME_OK;
}
else {
dhd_bandsteer_addmac_to_monitorlist(dhd_bandsteer_cntx, mac_addr);
/*
* TODO: Time for us to enable PROB_REQ MSG
*/
}
}
else {
/*
* Start post connect process as bandsteer is successful for this entry
*/
if (ndev == netdev_5g) {
DHD_INFO(("%s: Band Steer for " MACDBG " successful\n", __FUNCTION__,
MAC2STRDBG(mac_addr)));
dhd_bandsteer_add_to_black_list(dhd_bandsteer_mac);
dhd_bandsteer_remove_mac_from_list(dhd_bandsteer_mac, 0);
/* Probabaly add this mac into black list */
}
}
return BCME_OK;
}
s32
dhd_bandsteer_module_init(struct net_device *ndev)
{
/* Initialize */
dhd_bandsteer_context_t *dhd_bandsteer_cntx = NULL;
struct channel_info ci;
uint8 ifidx;
struct wireless_dev *__wdev = (struct wireless_dev *)(ndev)->ieee80211_ptr;
struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)wiphy_priv(__wdev->wiphy);
int err;
DHD_INFO(("%s: entered\n", __FUNCTION__));
if (cfg == NULL) {
DHD_ERROR(("%s: bcmcfg is null\n", __FUNCTION__));
return BCME_ERROR;
}
if (cfg->dhd_bandsteer_cntx != NULL) {
DHD_ERROR(("%s: Band Steering already enabled\n", __FUNCTION__));
return BCME_ERROR;
}
dhd_bandsteer_cntx = (dhd_bandsteer_context_t *)kzalloc(sizeof(dhd_bandsteer_context_t),
GFP_KERNEL);
if (unlikely(!dhd_bandsteer_cntx)) {
DHD_ERROR(("%s: dhd_bandsteer_cntx alloc failed\n", __FUNCTION__));
return BCME_NOMEM;
}
if (dhd_bandsteer_get_ifaces(cfg->pub, &dhd_bandsteer_cntx->bsd_ifaces)) {
DHD_ERROR(("%s: AP interfaces count != 2", __FUNCTION__));
err = BCME_ERROR;
goto failed;
}
for (ifidx = 0; ifidx < DHD_BANDSTEER_MAXIFACES; ifidx++) {
err = wldev_iovar_getbuf_bsscfg(dhd_bandsteer_cntx->bsd_ifaces[ifidx].ndev,
"cur_etheraddr", NULL, 0, cfg->ioctl_buf,
WLC_IOCTL_SMLEN, 0, &cfg->ioctl_buf_sync);
if (err) {
DHD_ERROR(("%s: Failed to get mac address\n", __FUNCTION__));
goto failed;
}
memcpy(dhd_bandsteer_cntx->bsd_ifaces[ifidx].macaddr.octet,
cfg->ioctl_buf, ETHER_ADDR_LEN);
memset(&ci, 0, sizeof(struct channel_info));
err = wldev_ioctl(dhd_bandsteer_cntx->bsd_ifaces[ifidx].ndev, WLC_GET_CHANNEL,
&ci, sizeof(ci), false);
if (err) {
DHD_ERROR(("%s: Failed to get channel\n", __FUNCTION__));
goto failed;
}
if (CHANNEL_IS_5G(ci.hw_channel))
dhd_bandsteer_cntx->ifidx_5g = ifidx;
dhd_bandsteer_cntx->bsd_ifaces[ifidx].channel = ci.hw_channel;
}
/* Enable software proberesponse on 2GHz interface */
if ((err = wldev_iovar_setint(
dhd_bandsteer_cntx->bsd_ifaces[!dhd_bandsteer_cntx->ifidx_5g].ndev,
"probresp_sw", 1)) != BCME_OK) {
DHD_ERROR(("%s: Failed to enable software ProbeRsp err = %d\n", __FUNCTION__, err));
}
if ((err = wldev_iovar_setint(ndev, "bandsteer", 1)) != BCME_OK) {
DHD_ERROR(("%s: Failed to enable bandsteer in FW err = %d\n", __FUNCTION__, err));
}
INIT_LIST_HEAD(&dhd_bandsteer_cntx->dhd_bandsteer_monitor_list);
dhd_bandsteer_cntx->dhd_pub = cfg->pub;
cfg->dhd_bandsteer_cntx = (void *) dhd_bandsteer_cntx;
return BCME_OK;
failed:
kfree(dhd_bandsteer_cntx);
return err;
}
s32
dhd_bandsteer_module_deinit(struct net_device *ndev)
{
dhd_bandsteer_mac_entry_t *dhd_bandsteer_mac = NULL;
dhd_bandsteer_context_t *dhd_bandsteer_cntx = NULL;
struct wireless_dev *__wdev = (struct wireless_dev *)(ndev)->ieee80211_ptr;
struct bcm_cfg80211 *cfg = (struct bcm_cfg80211 *)wiphy_priv(__wdev->wiphy);
int macmode = MACLIST_MODE_DISABLED;
int err;
struct maclist maclist;
DHD_INFO(("%s: entered\n", __FUNCTION__));
if (cfg == NULL) {
DHD_ERROR(("%s: bcmcfg is null\n", __FUNCTION__));
return BCME_ERROR;
}
if (cfg->dhd_bandsteer_cntx == NULL) {
DHD_ERROR(("%s: Band Steering not enabled\n", __FUNCTION__));
return BCME_ERROR;
}
dhd_bandsteer_cntx = cfg->dhd_bandsteer_cntx;
/* Disable mac filter */
if ((err = wldev_ioctl(dhd_bandsteer_cntx->bsd_ifaces[!dhd_bandsteer_cntx->ifidx_5g].ndev,
WLC_SET_MACMODE, &macmode, sizeof(macmode), true)) != 0) {
DHD_ERROR(("%s: WLC_SET_MACMODE error=%d\n", __FUNCTION__, err));
}
/* Set the MAC filter list */
memset(&maclist, 0, sizeof(struct maclist));
if ((err = wldev_ioctl(dhd_bandsteer_cntx->bsd_ifaces[!dhd_bandsteer_cntx->ifidx_5g].ndev,
WLC_SET_MACLIST, &maclist, sizeof(struct maclist), true)) != 0) {
DHD_ERROR(("%s: WLC_SET_MACLIST error=%d\n", __FUNCTION__, err));
}
/* Disable software proberesponse on 2GHz interface */
if ((err = wldev_iovar_setint(
dhd_bandsteer_cntx->bsd_ifaces[!dhd_bandsteer_cntx->ifidx_5g].ndev,
"probresp_sw", 0)) != BCME_OK) {
DHD_ERROR(("%s: Failed to disable software ProbeRsp err = %d\n",
__FUNCTION__, err));
}
if ((err = wldev_iovar_setint(ndev, "bandsteer", 0)) != BCME_OK) {
DHD_ERROR(("%s: Failed to disable bandsteer in FW err = %d\n", __FUNCTION__, err));
}
/* Get the first element of the list & pass it to remove */
if (dhd_bandsteer_cntx->dhd_bandsteer_monitor_list.next !=
&dhd_bandsteer_cntx->dhd_bandsteer_monitor_list) {
dhd_bandsteer_mac = (dhd_bandsteer_mac_entry_t *)list_entry(
dhd_bandsteer_cntx->dhd_bandsteer_monitor_list.next,
dhd_bandsteer_mac_entry_t, list);
}
if (dhd_bandsteer_mac) {
dhd_bandsteer_remove_mac_from_list(dhd_bandsteer_mac, 1);
}
kfree(dhd_bandsteer_cntx);
cfg->dhd_bandsteer_cntx = NULL;
return BCME_OK;
}