Skip to content
Snippets Groups Projects
brcm.c 30.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * brcm.c - for cfg80211 based broadcom driver
     *
     * Copyright (C) 2020 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 <stdarg.h>
    #include <string.h>
    #include <stdint.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <fcntl.h>
    #include <net/if.h>
    #include <time.h>
    #include <syslog.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <dirent.h>
    
    #include <wlioctl_defs.h>
    
    Anjan Chanda's avatar
    Anjan Chanda committed
    #include <easy/easy.h>
    
    #include "nlwifi.h"
    #include "wpactrl_util.h"
    
    #include "wlctrl.h"
    
    #include "debug.h"
    
    static int radio_list(struct radio_entry *radio, int *num)
    {
    	struct dirent *p;
    	struct radio_entry *entry;
    	int count = 0;
    	DIR *d;
    
    	if (WARN_ON(!(d = opendir("/sys/class/ieee80211"))))
    		return -1;
    
    	while ((p = readdir(d))) {
    		if (!strcmp(p->d_name, "."))
    			continue;
    		if (!strcmp(p->d_name, ".."))
    			continue;
    		if (WARN_ON(count >= *num))
    			break;
    
    		libwifi_dbg("radio: %s\n", p->d_name);
    		entry = &radio[count];
    		strncpy(entry->name, p->d_name, sizeof(entry->name));
    		count++;
    	}
    
    	closedir(d);
    	*num = count;
    
    	return 0;
    }
    
    static int is_valid_ap_iface(const char *ifname)
    {
    	int valid_iface = 0;
    	int ret;
    	enum wifi_mode mode;
    	DIR *d;
    	struct dirent *dir;
    
    	d = opendir("/sys/class/net");
    	if (WARN_ON(!d))
    		return 0;
    
    	while ((dir = readdir(d))) {
    		if (strcmp(dir->d_name, ".") == 0 ||
    		    strcmp(dir->d_name, "..") == 0)
    			continue;
    		if (strcmp(ifname, dir->d_name) == 0) {
    			valid_iface = 1;
    		}
    	}
    	closedir(d);
    
    	if (!valid_iface)
    		return 0;
    
    	ret = nlwifi_get_mode(ifname, &mode);
    	if (WARN_ON(ret))
    		return 0;
    
    	if (mode == WIFI_MODE_AP)  {
    		return 1;
    	}
    
    	return 0;
    }
    
    /* Radio callbacks */
    static int radio_info(const char *name, struct wifi_radio *radio)
    {
    	char netdev[16];
    
    	int n = 32, m = 32;
    
    	int ret;
    
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	ret = nlwifi_radio_info(name, radio);
    	if (ret)
    		return ret;
    
    	ret = bcmwl_get_supp_stds(name, &radio->supp_std);
    
    	WARN_ON(bcmwl_radio_get_beacon_int(name, &radio->beacon_int));
    	WARN_ON(bcmwl_radio_get_dtim(name, &radio->dtim_period));
    
    	WARN_ON(bcmwl_radio_get_country(name, radio->regdomain));
    
    	WARN_ON(wifi_get_sideband(name, &radio->extch));
    
    	WARN_ON(bcmwl_radio_get_sideband(name, &radio->extch));
    
    	WARN_ON(bcmwl_radio_get_txpower(name, &radio->txpower_dbm, &radio->txpower));
    
    	WARN_ON(bcmwl_radio_get_supp_bw(name, &radio->supp_bw));
    
    	WARN_ON(bcmwl_radio_get_stats(name, &radio->stats));
    
    	WARN_ON(bcmwl_radio_get_hw_txchain(name, &radio->tx_streams));
    	WARN_ON(bcmwl_radio_get_hw_rxchain(name, &radio->rx_streams));
    
    	WARN_ON(bcmwl_radio_get_diag(name, &radio->diag));
    
    	WARN_ON(bcmwl_radio_get_basic_rates(name, &n, radio->basic_rates));
    	WARN_ON(bcmwl_radio_get_oper_rates(name, &m, radio->supp_rates));
    
    	if (radio->oper_band != BAND_2)
    		WARN_ON(bcmwl_radio_get_cac_methods(name, &radio->cac_methods));
    
    
    	ret = nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_AP);
    	if (ret == 0) {
    
    		uint8_t he = 0;
    
    		WARN_ON(bcmwl_ap_get_oper_stds(netdev, &radio->oper_std));
    		he = !!(radio->oper_std & WIFI_AX);
    		WARN_ON(bcmwl_radio_get_gi(name, he, &radio->gi));
    		return 0;
    
    	}
    
    	ret = nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_STA);
    	if (ret == 0) {
    		return supplicant_cli_get_oper_std(netdev, &radio->oper_std);
    	}
    
    	radio->oper_std = radio->supp_std;
    	return 0;
    }
    
    static int radio_get_supp_band(const char *name, uint32_t *bands)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    	return nlwifi_get_supp_band(name, bands);
    
    }
    
    static int radio_get_oper_band(const char *name, enum wifi_band *band)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    	return bcmwl_get_oper_band(name, band);
    
    }
    
    static int radio_get_caps(const char *name, struct wifi_caps *caps)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return nlwifi_radio_get_caps(name, caps);
    }
    
    static int radio_get_supp_stds(const char *name, uint8_t *std)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    	return bcmwl_get_supp_stds(name, std);
    
    }
    
    static int radio_get_oper_stds(const char *name, uint8_t *std)
    {
    	char netdev[16];
    	int ret;
    
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    	ret = nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_AP);
    	if (ret == 0) {
    
    		ret = bcmwl_ap_get_oper_stds(netdev, std);
    
    		ret = bcmwl_get_supp_stds(name, std);
    
    	}
    
    	return ret;
    }
    
    static int radio_get_country(const char *name, char *alpha2)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    	return bcmwl_radio_get_country(name, alpha2);
    
    static int radio_get_countrylist(const char *name, char *cc, int *num)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return bcmwl_radio_get_countrylist(name, cc, num);
    }
    
    
    static int radio_get_channel(const char *name, uint32_t *channel, enum wifi_bw *bw)
    {
    
    	char netdev[16];
    	nlwifi_phy_to_netdev(name, netdev, sizeof(netdev));
    	libwifi_dbg("[%s, %s] %s called\n", name, netdev, __func__);
    
    	return bcmwl_radio_get_channel(netdev, channel, bw);
    
    }
    
    static int radio_set_channel(const char *name, uint32_t channel, enum wifi_bw bw)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return -1;
    }
    
    static int radio_get_supp_channels(const char *name, uint32_t *chlist, int *num,
    				   const char *alpha2, enum wifi_band f, enum wifi_bw b)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return nlwifi_get_supp_channels(name, chlist, num, alpha2, f, b);
    }
    
    static int radio_get_oper_channels(const char *name, uint32_t *chlist, int *num,
    				   const char *alpha2, enum wifi_band f, enum wifi_bw b)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return -1;
    }
    
    static int radio_get_curr_opclass(const char *name, struct wifi_opclass *o)
    {
    	char netdev[16];
    
    	nlwifi_phy_to_netdev(name, netdev, sizeof(netdev));
    	libwifi_dbg("[%s, %s] %s called\n", name, netdev, __func__);
    	return wifi_get_opclass(netdev, o);;
    }
    
    static int radio_get_bandwidth(const char *name, enum wifi_bw *bw)
    {
    	char netdev[16];
    
    
    	nlwifi_phy_to_netdev(name, netdev, sizeof(netdev));
    	libwifi_dbg("[%s, %s] %s called\n", name, netdev, __func__);
    
    	return bcmwl_radio_get_channel(netdev, &channel, bw);
    
    }
    
    static int radio_get_maxrate(const char *name, unsigned long *rate_Mbps)
    {
    
    	uint32_t chan;
    
    	uint32_t lrate;
    	uint32_t he_mcs, vht_mcs, ht_mcs;
    	uint32_t max_mcs = 0;
    	enum wifi_bw bw;
    	enum wifi_guard gi;
    
    
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    
    	if (!rate_Mbps)
    		return -EINVAL;
    
    	WARN_ON(bcmwl_radio_get_channel(name, &chan, &bw));
    	WARN_ON(bcmwl_radio_get_hw_txchain(name, &txchains));
    
    	WARN_ON(bcmwl_radio_get_maxrates(name, &he_mcs, &vht_mcs, &ht_mcs, &lrate));
    
    
    	if (he_mcs > 0) {
    		bcmwl_radio_get_gi(name, 1, &gi);
    
    		if (gi == WIFI_AUTOGI)
    			gi = WIFI_4xLTF_GI800;
    
    		max_mcs = he_mcs;
    
    		*rate_Mbps = wifi_mcs2rate(max_mcs, wifi_bw_enum2MHz(bw), txchains, gi);
    
    		return 0;
    	}
    
    	bcmwl_radio_get_gi(name, 0, &gi);
    
    	if (gi == WIFI_AUTOGI)
    			gi = WIFI_SGI;
    
    
    	if (vht_mcs > 0) {
    		max_mcs = vht_mcs;
    	} else if (ht_mcs > 0) {
    		max_mcs = ht_mcs % 8;
    	}
    
    	if (max_mcs > 0) {
    		*rate_Mbps = wifi_mcs2rate(max_mcs, wifi_bw_enum2MHz(bw), txchains, gi);
    		return 0;
    	}
    
    	*rate_Mbps = lrate;
    	return 0;
    
    static int radio_get_basic_rates(const char *name, int *num, uint32_t *rates)
    
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    
    	return bcmwl_radio_get_basic_rates(name, num, rates);
    
    static int radio_get_oper_rates(const char *name, int *num, uint32_t *rates)
    
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    
    	return bcmwl_radio_get_oper_rates(name, num, rates);
    
    }
    
    static int radio_get_supp_rates(const char *name, int *num, uint32_t *rates)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return -1;
    }
    
    
    static int radio_scan(const char *name, struct scan_param *p)
    {
    	char netdev[16];
    
    	nlwifi_phy_to_netdev(name, netdev, sizeof(netdev));
    	libwifi_dbg("[%s, %s] %s called\n", name, netdev, __func__);
    
    
    	WARN_ON(bcmwl_radio_reset_chanim_stats(name));
    
    int radio_scan_ex(const char *ifname, struct scan_param_ex *sp)
    {
    	char netdev[16];
    
    	nlwifi_phy_to_netdev(ifname, netdev, sizeof(netdev));
    	libwifi_dbg("[%s, %s] %s called\n", ifname, netdev, __func__);
    
    	WARN_ON(bcmwl_radio_reset_chanim_stats(ifname));
    	return nlwifi_scan_ex(netdev, sp);
    }
    
    
    static int radio_get_scan_results(const char *name, struct wifi_bss *bsss, int *num)
    {
    	char netdev[16];
    
    	nlwifi_phy_to_netdev(name, netdev, sizeof(netdev));
    	libwifi_dbg("[%s, %s] %s called\n", name, netdev, __func__);
    	return nlwifi_get_scan_results(netdev, bsss, num);
    }
    
    static int radio_get_bss_scan_result(const char *name, uint8_t *bssid,
    				     struct wifi_bss_detail *b)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return -1;
    }
    
    static int radio_get_noise(const char *name, int *noise)
    {
    	char netdev[16];
    
    	nlwifi_phy_to_netdev(name, netdev, sizeof(netdev));
    	libwifi_dbg("[%s, %s] %s called\n", name, netdev, __func__);
    
    	if (bcmwl_radio_get_noise(netdev, noise))
    
    		*noise = -90;
    
    	return 0;
    }
    
    static int radio_acs(const char *name, struct acs_param *p)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    	return bcmwl_radio_acs(name, p);
    
    int radio_start_cac(const char *name, int channel, enum wifi_bw bw,
    		   enum wifi_cac_method method)
    {
    
    	char netdev[16] = {};
    
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    
    	WARN_ON(nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_AP));
    	return bcmwl_start_cac(netdev, channel, bw, method);
    
    }
    
    int radio_stop_cac(const char *name)
    {
    
    	char netdev[16] = {};
    
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    
    	WARN_ON(nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_AP));
    	return bcmwl_stop_cac(netdev);
    
    static int radio_get_opclass_preferences(const char *name,
    					 struct wifi_opclass *opclass,
    					 int *num)
    
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    
    	/* First get this using common code */
    	ret = wifi_get_opclass_pref(name, num, opclass);
    
    	/* If module specific channes required apply here */
    
    	return ret;
    
    int radio_simulate_radar(const char *name, struct wifi_radar_args *radar)
    {
    	char netdev[16] = {};
    
    	libwifi_dbg("[%s] %s called ch:%d, bandwidth:%d, type:0x%x, subband:0x%x\n",
    	            name, __func__, radar->channel, radar->bandwidth, radar->type,
    	            radar->subband_mask);
    
    	WARN_ON(nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_AP));
    
    	return bcmwl_simulate_radar(netdev, radar);
    }
    
    
    static int radio_get_param(const char *name, const char *param, int *len, void *val)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return -1;
    }
    
    static int radio_set_param(const char *name, const char *param, int len, void *val)
    {
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    	return -1;
    }
    
    static int radio_add_iface(const char *name, enum wifi_mode m, char *argv[])
    {
    	char buf[1024];
    	char *ifname = NULL;
    	int i = 0;
    
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    	while (argv[i]) {
    		if (!strcmp(argv[i], "ifname")) {
    			ifname = argv[i+1];
    			break;
    		}
    
    		i+=2;
    	}
    
    	if (WARN_ON(!ifname))
    		return -1;
    
    	switch (m) {
    	case WIFI_MODE_STA:
    
    		/* Today for sta we allow only radio netdev */
    		if (WARN_ON(strcmp(name, ifname)))
    			return -1;
    
    		chrCmd(buf, sizeof(buf), "iw %s interface add %s type managed", name, ifname);
    		break;
    	case WIFI_MODE_AP:
    		chrCmd(buf, sizeof(buf), "iw %s interface add %s type ap", name, ifname);
    		break;
    	case WIFI_MODE_MONITOR:
    		chrCmd(buf, sizeof(buf), "iw %s interface add %s type monitor", name, ifname);
    		break;
    	default:
    		return -1;
    	}
    
    	return 0;
    }
    
    static int radio_del_iface(const char *name, const char *ifname)
    {
    	char buf[1024];
    
    	libwifi_dbg("[%s] %s called\n", name, __func__);
    
    	chrCmd(buf, sizeof(buf), "iw %s del", ifname);
    
    	return 0;
    }
    
    static int radio_list_iface(const char *name, struct iface_entry *iface, int *num)
    {
    	char path[256];
    	struct dirent *p;
    	struct iface_entry *entry;
    	int count = 0;
    	DIR *d;
    
    	snprintf(path, sizeof(path), "/sys/class/ieee80211/%s/device/net", name);
    	if (WARN_ON(!(d = opendir(path))))
    		return -1;
    
    	while ((p = readdir(d))) {
    		if (!strcmp(p->d_name, "."))
    			continue;
    		if (!strcmp(p->d_name, ".."))
    			continue;
    		if (WARN_ON(count >= *num))
    			break;
    
    		libwifi_dbg("[%s] iface  %s\n", name, p->d_name);
    		entry = &iface[count];
    		strncpy(entry->name, p->d_name, sizeof(entry->name));
    		nlwifi_get_mode(entry->name, &entry->mode);
    		count++;
    	}
    
    	closedir(d);
    	*num = count;
    
    	return 0;
    }
    
    /* common iface callbacks */
    
    static int iface_start_wps(const char *ifname, struct wps_param wps)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return hostapd_cli_iface_start_wps(ifname, wps);
    }
    
    static int iface_stop_wps(const char *ifname)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return hostapd_cli_iface_stop_wps(ifname);
    }
    
    static int iface_get_wps_status(const char *ifname, enum wps_status *s)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return hostapd_cli_iface_get_wps_status(ifname, s);
    }
    
    static int iface_get_wps_pin(const char *ifname, unsigned long *pin)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return hostapd_cli_iface_get_wps_ap_pin(ifname, pin);
    }
    
    static int iface_set_wps_pin(const char *ifname, unsigned long pin)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return hostapd_cli_iface_set_wps_ap_pin(ifname, pin);
    }
    
    static int iface_get_wps_device_info(const char *ifname, struct wps_device *info)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    static int iface_get_caps(const char *ifname, struct wifi_caps *caps)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return nlwifi_ap_get_caps(ifname, caps);
    }
    
    static int iface_get_mode(const char *ifname, enum wifi_mode *mode)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return nlwifi_get_mode(ifname, mode);
    }
    
    static int iface_get_security(const char *ifname, uint32_t *auth, uint32_t *enc)
    {
    
    	uint32_t wpa_auth;
    	uint32_t wl_sec;
    
    
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    
    	if (WARN_ON(!auth) || WARN_ON(!enc))
    		return -EINVAL;
    
    	*auth = 0;
    	*enc = 0;
    
    	WARN_ON(bcmwl_iface_get_wsec(ifname, &wl_sec));
    	WARN_ON(bcmwl_iface_get_wpa_auth(ifname, &wpa_auth));
    
    	if (wpa_auth == WPA_AUTH_DISABLED) {
    		*auth = AUTH_OPEN;
    		*enc = CIPHER_NONE;
    	}
    
    	if (WSEC_WEP_ENABLED(wl_sec)) {
    		*enc = CIPHER_WEP;
    		*auth = AUTH_OPEN;
    		return 0;
    	}
    
    	if (WSEC_TKIP_ENABLED(wl_sec)) {
    		*enc |= CIPHER_TKIP;
    	}
    
    	if (WSEC_AES_ENABLED(wl_sec)) {
    		*enc |= CIPHER_AES;
    	}
    
    	if (*enc ==0 && *auth == 0) {
    		*enc = CIPHER_UNKNOWN;
    		*auth = AUTH_UNKNOWN;
    	}
    
    	return 0;
    
    }
    
    static int iface_add_vendor_ie(const char *ifname, struct vendor_iereq *req)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	return bcmwl_iface_add_vendor_ie(ifname, req);
    
    }
    
    static int iface_del_vendor_ie(const char *ifname, struct vendor_iereq *req)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	return bcmwl_iface_del_vendor_ie(ifname, req);
    
    }
    
    static int iface_get_vendor_ies(const char *ifname, struct vendor_ie *ies, int *num_ies)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    static int iface_get_param(const char *ifname, const char *param, int *len, void *val)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    static int iface_set_param(const char *ifname, const char *param, int len, void *val)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    static int iface_vendor_cmd(const char *ifname, uint32_t vid, uint32_t subcmd,
    			    uint8_t *in, int inlen, uint8_t *out, int *outlen)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	return -1;
    
    }
    
    static int iface_subscribe_frame(const char *ifname, uint8_t type, uint8_t stype)
    {
    	enum wifi_mode mode;
    	int ret = -1;
    
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	ret = iface_get_mode(ifname, &mode);
    	if (WARN_ON(ret))
    		return ret;
    
    	switch (mode) {
    	case WIFI_MODE_AP:
    		ret = hostapd_ubus_iface_subscribe_frame(ifname, type, stype);
    		break;
    	default:
    		ret = -1;
    		break;
    	}
    
    	return ret;
    }
    
    static int iface_unsubscribe_frame(const char *ifname, uint8_t type, uint8_t stype)
    {
    	enum wifi_mode mode;
    	int ret = -1;
    
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	ret = iface_get_mode(ifname, &mode);
    	if (WARN_ON(ret))
    		return ret;
    
    	switch (mode) {
    	case WIFI_MODE_AP:
    		ret = hostapd_ubus_iface_unsubscribe_frame(ifname, type, stype);
    		break;
    	default:
    		ret = -1;
    		break;
    	}
    
    	return ret;
    }
    
    static int iface_set_4addr(const char *ifname, bool enable)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    
    static int iface_get_4addr(const char *ifname, bool *enabled)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	return nlwifi_get_4addr(ifname, enabled);
    }
    
    
    static int iface_get_4addr_parent(const char *ifname, char *parent)
    
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	return bcmwl_iface_get_4addr_parent(ifname, parent);
    }
    
    static int iface_set_vlan(const char *ifname, struct vlan_param vlan)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    
    int iface_link_measure(const char *ifname, uint8_t *sta)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	if (!sta || hwaddr_is_zero(sta)) {
    		libwifi_dbg("[%s] %s invalid arg\n", ifname, __func__);
    		return -1;
    	}
    
    	return bcmwl_iface_link_measure(ifname, sta);
    }
    
    
    /* ap interface ops */
    static int iface_ap_info(const char *ifname, struct wifi_ap *ap)
    {
    	/* TODO
    	*	ap->isolate_enabled
    	*/
    	struct wifi_bss *bss;
    	uint32_t ch = 0;
    
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	if (!is_valid_ap_iface(ifname))
    		return -1;
    
    	bss = &ap->bss;
    	memset(bss, 0, sizeof(*bss));
    
    	bcmwl_iface_ap_info(ifname, ap);
    
    	nlwifi_get_bssid(ifname, bss->bssid);
    	nlwifi_get_ssid(ifname, (char *)bss->ssid);
    
    	radio_get_channel(ifname, &ch, &bss->curr_bw);
    
    	bcmwl_get_supp_stds(ifname, &ap->bss.supp_std);
    
    	if (hostapd_cli_get_security_cap(ifname, &ap->sec.supp_modes))
    		bcmwl_get_supported_security_const(ifname, &ap->sec.supp_modes);
    
    
    	return 0;
    }
    
    static int iface_get_bssid(const char *ifname, uint8_t *bssid)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return nlwifi_get_bssid(ifname, bssid);
    }
    
    static int iface_get_ssid(const char *ifname, char *ssid)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return nlwifi_get_ssid(ifname, ssid);
    }
    
    static int iface_get_assoclist(const char *ifname, uint8_t *stas, int *num_stas)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    
    	return bcmwl_iface_get_assoclist(ifname, stas, num_stas);
    
    }
    
    static int iface_get_sta_info(const char *ifname, uint8_t *addr, struct wifi_sta *info)
    {
    
    	libwifi_dbg("[%s] %s called " MACSTR "\n", ifname, __func__, MAC2STR(addr));
    
    	memset(info, 0, sizeof(*info));
    	memcpy(info->macaddr, addr, 6);
    
    
    	WARN_ON(bcmwl_iface_get_exp_tp(ifname, addr, info));
    
    
    	return bcmwl_get_sta_info(ifname, addr, info);
    
    }
    
    static int iface_get_sta_stats(const char *ifname, uint8_t *addr, struct wifi_sta_stats *s)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    static int iface_disconnect_sta(const char *ifname, uint8_t *sta, uint16_t reason)
    {
    	libwifi_dbg("[%s] %s called reason[%d]\n", ifname, __func__, reason);
    	return hostapd_cli_disconnect_sta(ifname, sta, reason);
    }
    
    static int iface_restrict_sta(const char *ifname, uint8_t *sta, int enable)
    {
    
    	libwifi_dbg("[%s] %s called " MACSTR " %s\n", ifname, __func__,
    		    MAC2STR(sta), enable ? "enable" : "disable");
    
    	return bcmwl_iface_restrict_sta(ifname, sta, enable);
    
    }
    
    static int iface_monitor_sta(const char *ifname, uint8_t *sta, struct wifi_monsta_config *cfg)
    {
    	libwifi_dbg("[%s] %s called " MACSTR " %s\n", ifname, __func__,
    		    MAC2STR(sta), cfg->enable ? "enable" : "disable");
    
    
    	return bcmwl_iface_monitor_sta(ifname, sta, cfg);
    
    static int iface_probe_sta(const char *ifname, uint8_t *sta)
    {
    	libwifi_dbg("[%s] %s called " MACSTR "\n", ifname, __func__, MAC2STR(sta));
    
    	return hostapd_cli_probe_sta(ifname, sta);
    }
    
    
    static int iface_get_monitor_sta(const char *ifname, uint8_t *sta, struct wifi_monsta *mon)
    {
    	libwifi_dbg("[%s] %s " MACSTR " called\n", ifname, __func__, MAC2STR(sta));
    
    
    	return bcmwl_iface_get_monitor_sta(ifname, sta, mon);
    
    }
    
    static int iface_get_monitor_stas(const char *ifname, struct wifi_monsta *stas, int *num)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    }
    
    static int iface_add_neighbor(const char *ifname, struct nbr nbr)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return hostapd_cli_iface_add_neighbor(ifname, &nbr, sizeof(nbr));
    }
    
    static int iface_del_neighbor(const char *ifname, unsigned char *bssid)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return hostapd_cli_iface_del_neighbor(ifname, bssid);
    }
    
    static int iface_get_neighbor_list(const char *ifname, struct nbr *nbr, int *nr)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return hostapd_cli_iface_get_neighbor_list(ifname, nbr, nr);
    }
    
    
    static int iface_req_beacon_report(const char *ifname, uint8_t *sta,
    
    				    struct wifi_beacon_req *param, size_t param_sz)
    
    {
    	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);
    
    	/* No params passed - use default (minimal) configuration */
    
    	return hostapd_cli_iface_req_beacon_default(ifname, sta);
    
    }
    
    static int iface_get_beacon_report(const char *ifname, uint8_t *sta,
    				   struct sta_nbr *snbr, int *nr)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    
    #define MAX_BTM_NBR_NUM 64
    
    static int iface_req_bss_transition(const char *ifname, unsigned char *sta,
    				    int bsss_nr, unsigned char *bsss, unsigned int tmo)
    {
    	struct bss_transition_params params = { 0 };
    
    	struct nbr nbss[MAX_BTM_NBR_NUM] = {};
    	unsigned char *bss;
    	int i;
    
    
    	libwifi_dbg("[%s] %s called " MACSTR " nr %d\n", ifname, __func__,
    		    MAC2STR(sta), bsss_nr);
    
    
    	if (bsss_nr > MAX_BTM_NBR_NUM)
    		return -1;
    
    
    	params.valid_int = tmo;
    	params.pref = bsss_nr ? 1 : 0;
    	params.disassoc_imminent = 1;
    
    
    	for (i = 0; i < bsss_nr; i++) {
    		bss = bsss + i * 6;
    		memcpy(&nbss[i].bssid, bss, 6);
    	}
    
    	return hostapd_cli_iface_req_bss_transition(ifname, sta, bsss_nr, nbss, &params);
    
    }
    
    static int iface_req_btm(const char *ifname, unsigned char *sta,
    
    			 int bsss_nr, struct nbr *bsss, struct wifi_btmreq *b)
    
    {
    	struct bss_transition_params params = { 0 };
    
    	libwifi_dbg("[%s] %s called " MACSTR " nr %d\n", ifname, __func__,
    		    MAC2STR(sta), bsss_nr);
    
    	params.valid_int = b->validity_int;
    	params.disassoc_timer = b->disassoc_tmo;
    
    	if (b->mode & WIFI_BTMREQ_PREF_INC)
    		params.pref = 1;
    	if (b->mode & WIFI_BTMREQ_ABRIDGED)
    		params.abridged = 1;
    	if (b->mode & WIFI_BTMREQ_DISASSOC_IMM)
    		params.disassoc_imminent = 1;
    	if (b->mode & WIFI_BTMREQ_BSSTERM_INC)
    		params.bss_term = b->bssterm_dur;
    
    
    	if (b->flags & BTMREQ_F_NBR_PARAM_SET)
    		params.is_nbr_set = true;
    
    
    	memcpy(&params.mbo, &b->mbo, sizeof(params.mbo));
    
    
    	return hostapd_cli_iface_req_bss_transition(ifname, sta, bsss_nr, bsss, &params);
    }
    
    static int iface_get_11rkeys(const char *ifname, unsigned char *sta, uint8_t *r1khid)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    static int iface_set_11rkeys(const char *ifname, struct fbt_keys *fk)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    static int iface_chan_switch(const char *ifname, struct chan_switch_param *param)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	return bcmwl_iface_chan_switch(ifname, param);
    
    static int iface_mbo_disallow_assoc(const char *ifname, uint8_t reason)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return bcmwl_iface_mbo_disallow_assoc(ifname, reason);
    }
    
    
    static int iface_ap_set_state(const char *ifname, bool up)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    	return bcmwl_iface_ap_set_state(ifname, up);
    }
    
    
    /* sta interface ops */
    static int iface_sta_info(const char *ifname, struct wifi_sta *sta)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    
    
    	memset(sta, 0, sizeof(*sta));
    
    	return bcmwl_iface_sta_info(ifname, sta);
    
    }
    
    static int iface_sta_get_stats(const char *ifname, struct wifi_sta_stats *s)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return nlwifi_sta_get_stats(ifname, s);
    }
    
    static int iface_sta_get_ap_info(const char *ifname, struct wifi_bss *info)
    {
    	libwifi_dbg("[%s] %s called\n", ifname, __func__);
    	return -1;
    }
    
    static int iface_sta_disconnect_ap(const char *ifname, uint32_t reason)
    {
    	libwifi_dbg("[%s] %s called reason %d\n", ifname, __func__, reason);
    	return hostapd_cli_sta_disconnect_ap(ifname, reason);