/* SPDX-License-Identifier: LGPL-2.1-only */
/*
 * brcm.c - for cfg80211 based broadcom driver
 *
 * Copyright (C) 2020-2024 Iopsys Software Solutions AB. All rights reserved.
 */
#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.h>
#include <wlioctl_defs.h>

#include <easy/easy.h>
#include "wifi.h"
#include "wifiutils.h"
#include "nlwifi.h"
#include "hostapd_ctrl.h"
#include "supplicant_ctrl.h"
#include "wlctrl.h"
#include "debug.h"

static enum wifi_acs_mode radio_get_acs_mode(const char *name);
static int radio_get_noise(const char *name, int *noise);
static int radio_get_maxrate(const char *name, unsigned long *rate_Mbps);

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];
	uint32_t channel = 0;
	enum wifi_bw bw = BW20;
	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);
	if (ret)
		return ret;

	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_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));
	WARN_ON(radio_get_noise(name, &radio->noise));
	WARN_ON(radio_get_maxrate(name, &radio->maxrate));
	if (!bcmwl_radio_get_channel(name, &channel, &bw)) {
		radio->channel = channel;
		radio->curr_bw = bw;
	}

	WARN_ON(bcmwl_radio_get_txpower(name, radio->channel, &radio->txpower_dbm, &radio->txpower));
	radio->acs_mode = radio_get_acs_mode(name);
	if (radio->oper_band == BAND_5) {
		radio->dot11h_capable = 1;
		WARN_ON(bcmwl_iface_get_doth11h(name, &radio->dot11h_enabled));
		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));
		correct_oper_std_by_band(radio->oper_band, &radio->oper_std);
		he = !!(radio->oper_std & WIFI_AX);
		WARN_ON(bcmwl_radio_get_gi(name, he, &radio->gi));
		radio->curr_opclass = wifi_radio_opclass(radio);
		radio->max_iface_sta = 1;
		radio->max_iface_ap = 8;
		return 0;
	}

	ret = nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_STA);
	if (ret == 0) {
		radio->curr_opclass = wifi_radio_opclass(radio);
		radio->max_iface_sta = 1;
		radio->max_iface_ap = 8;
		return supplicant_cli_get_oper_std(netdev, &radio->oper_std);
	}

	radio->curr_opclass = wifi_radio_opclass(radio);
	radio->oper_std = radio->supp_std;
	radio->max_iface_sta = 1;
	radio->max_iface_ap = 8;
	return 0;
}

static int radio_info_band(const char *name, enum wifi_band band, struct wifi_radio *radio)
{
	libwifi_dbg("[%s, %s] %s called\n", name, wifi_band_to_str(band), __func__);
	return radio_info(name, radio);
}

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];
	enum wifi_band oper_band;
	int ret;

	libwifi_dbg("[%s] %s called\n", name, __func__);

	WARN_ON(bcmwl_get_oper_band(name, &oper_band));
	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);
		correct_oper_std_by_band(oper_band, std);
	} else {
		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_get_band_channel(const char *name, enum wifi_band band,
				  uint32_t *channel, enum wifi_bw *bw)
{
	libwifi_dbg("[%s, %s] %s called\n", name, wifi_band_to_str(band), __func__);
	return bcmwl_radio_get_channel(name, 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];
	uint32_t channel;

	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_band_supp_bandwidths(const char *name, enum wifi_band band, uint32_t *supp_bw)
{
	libwifi_dbg("[%s, %s] %s called\n", name, wifi_band_to_str(band), __func__);
	return bcmwl_radio_get_supp_bw(name, supp_bw);
}

static int radio_get_maxrate(const char *name, unsigned long *rate_Mbps)
{
	uint32_t chan;
	uint8_t txchains;
	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_get_diagnostic(const char *name, enum wifi_band band,
				struct wifi_radio_diagnostic *diag)
{
	libwifi_dbg("[%s, %s] %s called\n", name,  __func__, wifi_band_to_str(band));

	return bcmwl_radio_get_diag(name, diag);
}

static int radio_scan(const char *name, struct scan_param *p)
{
	char netdev[16];
	int ret;

	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));
	ret = nlwifi_scan(netdev, p);
	if (ret == -EAGAIN) {
		char buf[256] = {};
		libwifi_dbg("[%s, %s] %s unlock scan\n", name, netdev, __func__);
		Cmd(buf, sizeof(buf), "wl -i %s dfs_ap_move -1", name);
		ret = nlwifi_scan(netdev, p);
	}

	libwifi_dbg("[%s, %s] %s ret %d\n", name, netdev, __func__, ret);
	return ret;
}

static int radio_scan_ex(const char *ifname, struct scan_param_ex *sp)
{
	char netdev[16];
	int ret;

	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));
	ret = nlwifi_scan_ex(netdev, sp);
	if (ret == -EAGAIN) {
		char buf[256] = {};
		libwifi_dbg("[%s, %s] %s unlock scan\n", ifname, netdev, __func__);
		Cmd(buf, sizeof(buf), "wl -i %s dfs_ap_move -1", ifname);
		ret = nlwifi_scan_ex(netdev, sp);
	}

	libwifi_dbg("[%s, %s] %s ret %d\n", ifname, netdev, __func__, ret);
	return ret;
}

static int radio_scan_band_ex(const char *ifname, enum wifi_band band, struct scan_param_ex *sp)
{
	return radio_scan_ex(ifname, 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_band_scan_results(const char *name, enum wifi_band band,
				       struct wifi_bss *bsss, int *num)
{
	char netdev[16];

	if (nlwifi_phy_to_netdev_with_type_and_band(name, netdev, sizeof(netdev), NLWIFI_MODE_AP_STA, band))
	    nlwifi_phy_to_netdev(name, netdev, sizeof(netdev));

	libwifi_dbg("[%s, %s, %s] %s called\n", name, netdev, wifi_band_to_str(band), __func__);
	return nlwifi_get_band_scan_results(netdev, band, 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_band_noise(const char *name, enum wifi_band band, 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_get_noise(const char *name, int *noise)
{
	return radio_get_band_noise(name, BAND_ANY, noise);
}

static int get_acs_ifname(const char *name, char *ifname, int size)
{
	char buf[256] = {};
	char *p, *out, *word;
	int ret;

	/* wl1.1 wl0.1 */
	ret = Cmd(buf, sizeof(buf), "nvram get acs_ifnames");
	if (ret)
		return ret;

	out = strdup(buf);
	if (!out)
		return -1;

	p = out;
	ret = -1;

	while ((word = strsep(&p, " "))) {
		libwifi_dbg("[%s] %s check %s\n", name, __func__, word);
		if (strstr(word, name)) {
			strncpy(ifname, word, size);
			ret = 0;
			break;
		}
        }

	free(out);
	return ret;
}

static int radio_acs(const char *name, struct acs_param *p)
{
	char ifname[16];
	int ret;

	ret = get_acs_ifname(name, ifname, sizeof(ifname));
	libwifi_dbg("[%s] %s called - acs_ifname %s\n", name, __func__, ifname);

	return !ret ? bcmwl_radio_acs(ifname, p) : -1;
}

static enum wifi_acs_mode radio_get_acs_mode(const char *name)
{
	char ifname[16] = {};
	int ret;

	ret = get_acs_ifname(name, ifname, sizeof(ifname));
	if (ret)
		return WIFI_ACS_MODE_UNSET;

	return bcmwl_radio_get_acs_mode(ifname);
}



int radio_start_cac(const char *name, int channel, enum wifi_bw bw,
		   enum wifi_cac_method method)
{
	char netdev[16] = {};

	WARN_ON(nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_AP));
	libwifi_dbg("[%s, %s] %s called\n", name, netdev, __func__);

	return bcmwl_start_cac(netdev, channel, bw, method);
}

int radio_stop_cac(const char *name, uint32_t channel, enum wifi_bw bw)
{
	char netdev[16] = {};

	WARN_ON(nlwifi_phy_to_netdev_with_type(name, netdev, sizeof(netdev), NLWIFI_MODE_AP));
	libwifi_dbg("[%s, %s] %s called\n", name, netdev, __func__);

	return bcmwl_stop_cac(netdev, channel, bw);
}

static int radio_get_opclass_preferences(const char *name,
					 struct wifi_opclass *opclass,
					 int *num)
{
	int ret;
	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;
}

static int radio_get_band_opclass_preferences(const char *name,
					      enum wifi_band band,
					      struct wifi_opclass *opclass,
					      int *num)
{
	int ret;
	libwifi_dbg("[%s] %s called\n", name, __func__);

	ret = wifi_get_band_opclass_pref(name, band, num, opclass);
	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;
}

static int radio_channels_info_band(const char *name, enum wifi_band band,
				    struct chan_entry *channel, int *num)
{
	libwifi_dbg("[%s, %s] %s called\n", name, wifi_band_to_str(band), __func__);
	return bcmwl_radio_channels_info(name, channel, num);
}

/* 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_send_action_frame(const char *ifname, struct wifi_frame_arg *arg,
				   uint8_t *frame, size_t len, uint64_t *cookie)
{
	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	if (len > ACTION_FRAME_SIZE)
		return -EINVAL;

	return hostapd_cli_iface_send_action_frame(ifname, arg, frame, len, cookie);
}

static int iface_dpp_listen(const char *ifname, uint32_t freq)
{
	libwifi_dbg("[%s] %s called\n", ifname, __func__);
	return hostapd_cli_iface_dpp_listen(ifname, freq);
}

static int iface_dpp_stop_listen(const char *ifname)
{
	libwifi_dbg("[%s] %s called\n", ifname, __func__);
	return hostapd_cli_iface_dpp_stop_listen(ifname);
}


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_band(const char *ifname, enum wifi_band band, struct wifi_ap *ap)
{
	/* TODO
	*	ap->isolate_enabled
	*/
	struct wifi_bss *bss;
	enum wifi_band oper_band;
	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));

	/* Fallback to hostapd_cli in case of fail */
	if (bcmwl_iface_ap_info(ifname, ap))
		hostapd_cli_iface_ap_info(ifname, ap);

	bcmwl_iface_ap_enabled(ifname, &ap->enabled);

	nlwifi_get_bssid(ifname, bss->bssid);
	nlwifi_get_ssid(ifname, bss->ssid, &bss->ssidlen);
	radio_get_channel(ifname, &ch, &bss->curr_bw);
	bss->channel = ch;
	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);

	bcmwl_get_oper_band(ifname, &oper_band);
	ap->bss.band = oper_band;
	correct_oper_std_by_band(oper_band, &ap->bss.oper_std);
	correct_supp_sec_by_band(oper_band, &ap->sec.supp_modes);

	return 0;
}

static int iface_ap_info(const char *ifname, struct wifi_ap *ap)
{
	return iface_ap_info_band(ifname, BAND_ANY, ap);
}

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, uint8_t *ssid, size_t *ssidlen)
{
	libwifi_dbg("[%s] %s called\n", ifname, __func__);
	return nlwifi_get_ssid(ifname, ssid, ssidlen);
}

static int iface_get_beacon_ies_band(const char *ifname, enum wifi_band band, uint8_t *ies, int *len)
{
	return bcmwl_iface_get_beacon_ies(ifname, ies, len);
}

static int iface_get_assoclist_band(const char *ifname, enum wifi_band band, 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_assoclist(const char *ifname, uint8_t *stas, int *num_stas)
{
	return iface_get_assoclist_band(ifname, BAND_ANY, stas, num_stas);
}

static int iface_block_sta(const char *ifname, enum wifi_band band, uint8_t *sta, int block)
{
	libwifi_dbg("[%s, %s] %s called " MACSTR " %s\n", ifname, wifi_band_to_str(band),
		    __func__, MAC2STR(sta), block ? "block" : "unblock");

	return bcmwl_iface_restrict_sta(ifname, sta, !block);
}

static int iface_get_blocked_stas(const char *ifname, enum wifi_band band, uint8_t *stas, int *num_stas)
{
	libwifi_dbg("[%s, %s] %s called\n", ifname, wifi_band_to_str(band), __func__);

	return bcmwl_iface_get_maclist(ifname, stas, num_stas);
}

static int iface_get_sta_info_band(const char *ifname, enum wifi_band band, uint8_t *addr, struct wifi_sta *info)
{
	int ret;

	libwifi_dbg("[%s] %s called " MACSTR "\n", ifname, __func__, MAC2STR(addr));

	memset(info, 0, sizeof(*info));
	memcpy(info->macaddr, addr, 6);

	ret = bcmwl_get_sta_info(ifname, addr, info);
	WARN_ON(bcmwl_iface_get_snr_exp_tp(ifname, addr, info));

	if (band != BAND_ANY)
		info->band = band;
	else
		bcmwl_get_oper_band(ifname, &info->band);

	info->oper_std = wifi_standard_from_caps(ifname, info->band, &info->caps);

	return ret;
}

static int iface_get_sta_info(const char *ifname, uint8_t *addr, struct wifi_sta *info)
{
	return iface_get_sta_info_band(ifname, BAND_ANY, 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__);
	return bcmwl_iface_get_monitor_stas(ifname, stas, num);
}

static int iface_add_neighbor(const char *ifname, struct nbr nbr)
{
	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	/* Driver need it for RNR IE in beacon/probe_resp */
	bcmwl_add_neighbor(ifname, &nbr, sizeof(nbr));

	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__);
	bcmwl_del_neighbor(ifname, bssid);
	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 = {
		.dialog_token = 1,
	};
	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 = {
		.dialog_token = 1,
	};

	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->dialog_token)
		params.dialog_token = b->dialog_token;

	if (b->mode & WIFI_BTMREQ_PREF_INC || bsss_nr)
		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;

	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);
}

static int iface_ap_set_qos_map(const char *ifname,
	struct dscp_pcp_map *map,
	struct dscp_exception *exception,
	int num_exceptions)
{
	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	return bcmwl_iface_ap_set_qos_map(ifname, map, exception,
		num_exceptions);
}

static int iface_ap_send_qos_map_conf(const char *ifname, uint8_t *sta)
{
	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	return bcmwl_iface_ap_send_qos_map_conf(ifname, sta);
}

static int get_ctrl_interface_band(const char *ifname, enum wifi_band band, char **ctrl_interface)
{
       *ctrl_interface = strdup(ifname);
       return 0;
}

static int get_ctrl_interface(const char *ifname, char **ctrl_interface)
{
       return get_ctrl_interface_band(ifname, BAND_ANY, ctrl_interface);
}

/* sta interface ops */
static int iface_sta_info(const char *ifname, struct wifi_sta *sta)
{
	int ret;

	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	memset(sta, 0, sizeof(*sta));
	sta->mlo_capable = false;

	ret = bcmwl_iface_sta_info(ifname, sta);

	nlwifi_get_4addr(ifname, &sta->is4addr);
	nlwifi_get_ssid(ifname, sta->ssid, &sta->ssidlen);

	return ret;
}

static int iface_mlsta_interface_info(const char *ifname, struct wifi_mlsta *mlsta)
{
	int ret;

	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	memset(mlsta, 0, sizeof(*mlsta));

	ret = iface_sta_info(ifname, &mlsta->sta[0]);
	if (!ret)
		memcpy(mlsta->macaddr, mlsta->sta[0].macaddr, 6);

	return ret;
}

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);
}

/* Event wrapper, allow us to handle multiple families */
struct event_ctx {
	void *handle;
	char family[32];
};

static int register_event(const char *ifname, struct event_struct *ev,
			  void **evhandle)
{
	struct event_ctx *ctx;
	void *handle = NULL;
	int ret;

	libwifi_dbg("%s %s called family %s group %s\n", ifname, __func__, ev->family, ev->group);

	ctx = calloc(1, sizeof(struct event_ctx));
	WARN_ON(!ctx);
	if (!ctx)
		return -1;

	if (!strcmp(ev->family, "bcmwl"))
		ret = bcmwl_register_event(ifname, ev, &handle);
	else if (!strcmp(ev->family, "nl80211"))
		ret = nlwifi_register_event(ifname, ev, &handle);
	else
		ret = -1;

	strncpy(ctx->family, ev->family, sizeof(ctx->family));
	ctx->handle = handle;

	if (ret == -1) {
		free(ctx);
		ctx = NULL;
	}

	*evhandle = ctx;
	return ret;
}

static int unregister_event(const char *ifname, void *evhandle)
{
	struct event_ctx *ctx = evhandle;
	int ret;

	if (WARN_ON(!ctx))
		return -1;

	libwifi_dbg("[%s] %s called\n", ifname, __func__);

	if (!strcmp(ctx->family, "bcmwl"))
		ret = bcmwl_unregister_event(ifname, ctx->handle);
	else if (!strcmp(ctx->family, "nl80211"))
		ret = nlwifi_unregister_event(ifname, ctx->handle);
	else
		ret = -1;

	free(ctx);
	return ret;
}

static int recv_event(const char *ifname, void *evhandle)
{
	struct event_ctx *ctx = evhandle;
	int ret;

	//libwifi_dbg("[%s] %s called %s\n", ifname, __func__, ctx->family ?: "null");

	if (!strcmp(ctx->family, "bcmwl"))
		ret = bcmwl_recv_event(ifname, ctx->handle);
	else if (!strcmp(ctx->family, "nl80211"))
		ret = nlwifi_recv_event(ifname, ctx->handle);
	else
		ret = -1;

	return ret;
}

const struct wifi_driver brcm_driver = {
	.name = "wl,phy,wds",
	.info = bcmwl_driver_info,

	/* Radio/phy callbacks */
	.radio.info = radio_info,
	.radio.info_band = radio_info_band,
	.get_supp_band = radio_get_supp_band,
	.get_oper_band = radio_get_oper_band,
	.radio.get_caps = radio_get_caps,
	.radio.get_supp_stds = radio_get_supp_stds,
	.get_oper_stds = radio_get_oper_stds,

	.get_country = radio_get_country,
	.get_countrylist = radio_get_countrylist,
	.get_channel = radio_get_channel,
	.radio.get_band_channel = radio_get_band_channel,
	.set_channel = radio_set_channel,
	.get_supp_channels = radio_get_supp_channels,
	.get_oper_channels = radio_get_oper_channels,

	.get_curr_opclass = radio_get_curr_opclass,

	.get_bandwidth = radio_get_bandwidth,
	.get_supp_bandwidths = bcmwl_radio_get_supp_bw,
	.radio.get_band_supp_bandwidths = radio_get_band_supp_bandwidths,
	.get_maxrate = radio_get_maxrate,
	.radio.get_basic_rates = radio_get_basic_rates,
	.radio.get_oper_rates = radio_get_oper_rates,
	.radio.get_supp_rates = radio_get_supp_rates,
	.radio.get_stats = bcmwl_radio_get_stats,
	.radio.get_diagnostic = radio_get_diagnostic,

	.scan = radio_scan,
	.scan_ex = radio_scan_ex,
	.radio.scan_band_ex = radio_scan_band_ex,
	.get_scan_results = radio_get_scan_results,
	.radio.get_band_scan_results = radio_get_band_scan_results,
	.get_bss_scan_result = radio_get_bss_scan_result,

	.get_noise = radio_get_noise,
	.radio.get_band_noise = radio_get_band_noise,

	.acs = radio_acs,
	.start_cac = radio_start_cac,
	.stop_cac = radio_stop_cac,
	.get_opclass_preferences = radio_get_opclass_preferences,
	.radio.get_band_opclass_preferences = radio_get_band_opclass_preferences,
	.simulate_radar = radio_simulate_radar,

	.radio.get_param = radio_get_param,
	.radio.set_param = radio_set_param,

	.add_iface = radio_add_iface,
	.del_iface = radio_del_iface,
	.list_iface = radio_list_iface,
	.channels_info = bcmwl_radio_channels_info,
	.radio.channels_info_band = radio_channels_info_band,

	/* Interface/vif common callbacks */
	.iface.start_wps = iface_start_wps,
	.iface.stop_wps = iface_stop_wps,
	.iface.get_wps_status = iface_get_wps_status,
	.iface.get_wps_pin = iface_get_wps_pin,
	.iface.set_wps_pin = iface_set_wps_pin,
	.iface.get_wps_device_info = iface_get_wps_device_info,

	.iface.get_caps = iface_get_caps,
	.iface.get_mode = iface_get_mode,
	.iface.get_security = iface_get_security,

	.iface.add_vendor_ie = iface_add_vendor_ie,
	.iface.del_vendor_ie = iface_del_vendor_ie,
	.iface.get_vendor_ies = iface_get_vendor_ies,

	.iface.get_param = iface_get_param,
	.iface.set_param = iface_set_param,
	.vendor_cmd = iface_vendor_cmd,
	.iface.subscribe_frame = iface_subscribe_frame,
	.iface.unsubscribe_frame = iface_unsubscribe_frame,
	.iface.send_action_frame = iface_send_action_frame,
	.iface.dpp_listen = iface_dpp_listen,
	.iface.dpp_stop_listen = iface_dpp_stop_listen,
	.set_4addr = iface_set_4addr,
	.get_4addr = iface_get_4addr,
	.get_4addr_parent = iface_get_4addr_parent,
	.set_vlan = iface_set_vlan,
	.link_measure = iface_link_measure,

        /* Interface/vif ap callbacks */
	.iface.ap_info = iface_ap_info,
	.iface.ap_info_band = iface_ap_info_band,
	.get_bssid = iface_get_bssid,
	.get_ssid = iface_get_ssid,
	.iface.get_stats = bcmwl_iface_get_stats,
	.get_beacon_ies = bcmwl_iface_get_beacon_ies,
	.iface.get_beacon_ies_band = iface_get_beacon_ies_band,

	.get_assoclist = iface_get_assoclist,
	.iface.get_assoclist_band = iface_get_assoclist_band,
	.iface.if_get_channel = nlwifi_iface_get_channel,
	.get_sta_info = iface_get_sta_info,
	.iface.get_sta_info_band = iface_get_sta_info_band,
	.get_sta_stats = iface_get_sta_stats,
	.disconnect_sta = iface_disconnect_sta,
	.restrict_sta = iface_restrict_sta,
	.monitor_sta = iface_monitor_sta,
	.iface.probe_sta = iface_probe_sta,
	.get_monitor_sta = iface_get_monitor_sta,
	.get_monitor_stas = iface_get_monitor_stas,
	.iface.add_neighbor = iface_add_neighbor,
	.iface.del_neighbor = iface_del_neighbor,
	.iface.get_neighbor_list = iface_get_neighbor_list,
	.iface.req_beacon_report = iface_req_beacon_report,
	.iface.get_beacon_report = iface_get_beacon_report,

	.iface.req_bss_transition = iface_req_bss_transition,
	.iface.req_btm = iface_req_btm,

	.iface.get_11rkeys = iface_get_11rkeys,
	.iface.set_11rkeys = iface_set_11rkeys,

	.chan_switch = iface_chan_switch,
	.mbo_disallow_assoc = iface_mbo_disallow_assoc,
	.ap_set_state = iface_ap_set_state,
	.ap_set_qos_map = iface_ap_set_qos_map,
	.ap_send_qos_map_conf = iface_ap_send_qos_map_conf,

	.iface.get_ctrl_interface = get_ctrl_interface,
	.iface.get_ctrl_interface_band = get_ctrl_interface_band,
	.iface.block_sta = iface_block_sta,
	.iface.get_blocked_stas = iface_get_blocked_stas,

         /* Interface/vif sta callbacks */
	.iface.sta_info = iface_sta_info,
	.iface.mlsta_interface_info = iface_mlsta_interface_info,
	.iface.sta_get_stats = iface_sta_get_stats,
	.iface.sta_get_ap_info = iface_sta_get_ap_info,
	.iface.sta_disconnect_ap = iface_sta_disconnect_ap,

	.radio_list = radio_list,
	.register_event = register_event,
	.unregister_event = unregister_event,
	.recv_event = recv_event,
};
