Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmfmac/cfg80211.c
178665 views
// SPDX-License-Identifier: ISC1/*2* Copyright (c) 2010 Broadcom Corporation3*/45/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */67#if defined(__FreeBSD__)8#include <net/cfg80211.h>9#endif10#include <linux/kernel.h>11#include <linux/etherdevice.h>12#include <linux/module.h>13#include <linux/vmalloc.h>14#if defined(__linux__)15#include <net/cfg80211.h>16#endif17#include <net/netlink.h>18#include <uapi/linux/if_arp.h>19#if defined(__FreeBSD__)20#include <linux/delay.h>21#endif2223#include <brcmu_utils.h>24#include <defs.h>25#include <brcmu_wifi.h>26#include <brcm_hw_ids.h>27#include "core.h"28#include "debug.h"29#include "tracepoint.h"30#include "fwil_types.h"31#include "p2p.h"32#include "btcoex.h"33#include "pno.h"34#include "fwsignal.h"35#include "cfg80211.h"36#include "feature.h"37#include "fwil.h"38#include "proto.h"39#include "vendor.h"40#include "bus.h"41#include "common.h"42#include "fwvid.h"4344#define BRCMF_SCAN_IE_LEN_MAX 20484546#if defined(__FreeBSD__)47#ifdef WPA_OUI48#undef WPA_OUI49#endif50#ifdef WPA_OUI_TYPE51#undef WPA_OUI_TYPE52#endif53#ifdef RSN_OUI54#undef RSN_OUI55#endif56#ifdef WME_OUI_TYPE57#undef WME_OUI_TYPE58#endif59#ifdef WPS_OUI_TYPE60#undef WPS_OUI_TYPE61#endif62#endif6364#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */65#define WPA_OUI_TYPE 166#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */67#define WME_OUI_TYPE 268#define WPS_OUI_TYPE 46970#define VS_IE_FIXED_HDR_LEN 671#define WPA_IE_VERSION_LEN 272#define WPA_IE_MIN_OUI_LEN 473#define WPA_IE_SUITE_COUNT_LEN 27475#define WPA_CIPHER_NONE 0 /* None */76#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */77#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */78#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */79#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */8081#define RSN_AKM_NONE 0 /* None (IBSS) */82#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */83#define RSN_AKM_PSK 2 /* Pre-shared Key */84#define RSN_AKM_SHA256_1X 5 /* SHA256, 802.1X */85#define RSN_AKM_SHA256_PSK 6 /* SHA256, Pre-shared Key */86#define RSN_AKM_SAE 8 /* SAE */87#define RSN_CAP_LEN 2 /* Length of RSN capabilities */88#define RSN_CAP_PTK_REPLAY_CNTR_MASK (BIT(2) | BIT(3))89#define RSN_CAP_MFPR_MASK BIT(6)90#define RSN_CAP_MFPC_MASK BIT(7)91#define RSN_PMKID_COUNT_LEN 29293#define VNDR_IE_CMD_LEN 4 /* length of the set command94* string :"add", "del" (+ NUL)95*/96#define VNDR_IE_COUNT_OFFSET 497#define VNDR_IE_PKTFLAG_OFFSET 898#define VNDR_IE_VSIE_OFFSET 1299#define VNDR_IE_HDR_SIZE 12100#define VNDR_IE_PARSE_LIMIT 5101102#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */103104#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320105#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400106#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20107108#define BRCMF_SCAN_CHANNEL_TIME 40109#define BRCMF_SCAN_UNASSOC_TIME 40110#define BRCMF_SCAN_PASSIVE_TIME 120111112#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000)113114#define BRCMF_PS_MAX_TIMEOUT_MS 2000115116/* Dump obss definitions */117#define ACS_MSRMNT_DELAY 80118#define CHAN_NOISE_DUMMY (-80)119#define OBSS_TOKEN_IDX 15120#define IBSS_TOKEN_IDX 15121#define TX_TOKEN_IDX 14122#define CTG_TOKEN_IDX 13123#define PKT_TOKEN_IDX 15124#define IDLE_TOKEN_IDX 12125126#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \127(sizeof(struct brcmf_assoc_params_le) - sizeof(u16))128129#define BRCMF_MAX_CHANSPEC_LIST \130(BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1)131132struct brcmf_dump_survey {133u32 obss;134u32 ibss;135u32 no_ctg;136u32 no_pckt;137u32 tx;138u32 idle;139};140141struct cca_stats_n_flags {142u32 msrmnt_time; /* Time for Measurement (msec) */143u32 msrmnt_done; /* flag set when measurement complete */144char buf[1];145};146147struct cca_msrmnt_query {148u32 msrmnt_query;149u32 time_req;150};151152static bool check_vif_up(struct brcmf_cfg80211_vif *vif)153{154if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {155brcmf_dbg(INFO, "device is not ready : status (%lu)\n",156vif->sme_state);157return false;158}159return true;160}161162#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)163#define RATETAB_ENT(_rateid, _flags) \164{ \165.bitrate = RATE_TO_BASE100KBPS(_rateid), \166.hw_value = (_rateid), \167.flags = (_flags), \168}169170static struct ieee80211_rate __wl_rates[] = {171RATETAB_ENT(BRCM_RATE_1M, 0),172RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),173RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),174RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),175RATETAB_ENT(BRCM_RATE_6M, 0),176RATETAB_ENT(BRCM_RATE_9M, 0),177RATETAB_ENT(BRCM_RATE_12M, 0),178RATETAB_ENT(BRCM_RATE_18M, 0),179RATETAB_ENT(BRCM_RATE_24M, 0),180RATETAB_ENT(BRCM_RATE_36M, 0),181RATETAB_ENT(BRCM_RATE_48M, 0),182RATETAB_ENT(BRCM_RATE_54M, 0),183};184185#define wl_g_rates (__wl_rates + 0)186#define wl_g_rates_size ARRAY_SIZE(__wl_rates)187#define wl_a_rates (__wl_rates + 4)188#define wl_a_rates_size (wl_g_rates_size - 4)189190#define CHAN2G(_channel, _freq) { \191.band = NL80211_BAND_2GHZ, \192.center_freq = (_freq), \193.hw_value = (_channel), \194.max_antenna_gain = 0, \195.max_power = 30, \196}197198#define CHAN5G(_channel) { \199.band = NL80211_BAND_5GHZ, \200.center_freq = 5000 + (5 * (_channel)), \201.hw_value = (_channel), \202.max_antenna_gain = 0, \203.max_power = 30, \204}205206static struct ieee80211_channel __wl_2ghz_channels[] = {207CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),208CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),209CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),210CHAN2G(13, 2472), CHAN2G(14, 2484)211};212213static struct ieee80211_channel __wl_5ghz_channels[] = {214CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),215CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),216CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),217CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),218CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),219CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)220};221222/* Band templates duplicated per wiphy. The channel info223* above is added to the band during setup.224*/225static const struct ieee80211_supported_band __wl_band_2ghz = {226.band = NL80211_BAND_2GHZ,227.bitrates = wl_g_rates,228.n_bitrates = wl_g_rates_size,229};230231static const struct ieee80211_supported_band __wl_band_5ghz = {232.band = NL80211_BAND_5GHZ,233.bitrates = wl_a_rates,234.n_bitrates = wl_a_rates_size,235};236237/* This is to override regulatory domains defined in cfg80211 module (reg.c)238* By default world regulatory domain defined in reg.c puts the flags239* NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).240* With respect to these flags, wpa_supplicant doesn't * start p2p241* operations on 5GHz channels. All the changes in world regulatory242* domain are to be done here.243*/244static const struct ieee80211_regdomain brcmf_regdom = {245.n_reg_rules = 4,246.alpha2 = "99",247.reg_rules = {248/* IEEE 802.11b/g, channels 1..11 */249REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),250/* If any */251/* IEEE 802.11 channel 14 - Only JP enables252* this and for 802.11b only253*/254REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),255/* IEEE 802.11a, channel 36..64 */256REG_RULE(5150-10, 5350+10, 160, 6, 20, 0),257/* IEEE 802.11a, channel 100..165 */258REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), }259};260261/* Note: brcmf_cipher_suites is an array of int defining which cipher suites262* are supported. A pointer to this array and the number of entries is passed263* on to upper layers. AES_CMAC defines whether or not the driver supports MFP.264* So the cipher suite AES_CMAC has to be the last one in the array, and when265* device does not support MFP then the number of suites will be decreased by 1266*/267static const u32 brcmf_cipher_suites[] = {268WLAN_CIPHER_SUITE_WEP40,269WLAN_CIPHER_SUITE_WEP104,270WLAN_CIPHER_SUITE_TKIP,271WLAN_CIPHER_SUITE_CCMP,272/* Keep as last entry: */273WLAN_CIPHER_SUITE_AES_CMAC274};275276/* Vendor specific ie. id = 221, oui and type defines exact ie */277struct brcmf_vs_tlv {278u8 id;279u8 len;280u8 oui[3];281u8 oui_type;282};283284struct parsed_vndr_ie_info {285#if defined(__linux__)286u8 *ie_ptr;287#elif defined(__FreeBSD__)288const u8 *ie_ptr;289#endif290u32 ie_len; /* total length including id & length field */291struct brcmf_vs_tlv vndrie;292};293294struct parsed_vndr_ies {295u32 count;296struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];297};298299#define WL_INTERFACE_CREATE_VER_1 1300#define WL_INTERFACE_CREATE_VER_2 2301#define WL_INTERFACE_CREATE_VER_3 3302#define WL_INTERFACE_CREATE_VER_MAX WL_INTERFACE_CREATE_VER_3303304#define WL_INTERFACE_MAC_DONT_USE 0x0305#define WL_INTERFACE_MAC_USE 0x2306307#define WL_INTERFACE_CREATE_STA 0x0308#define WL_INTERFACE_CREATE_AP 0x1309310struct wl_interface_create_v1 {311u16 ver; /* structure version */312u32 flags; /* flags for operation */313u8 mac_addr[ETH_ALEN]; /* MAC address */314u32 wlc_index; /* optional for wlc index */315};316317struct wl_interface_create_v2 {318u16 ver; /* structure version */319u8 pad1[2];320u32 flags; /* flags for operation */321u8 mac_addr[ETH_ALEN]; /* MAC address */322u8 iftype; /* type of interface created */323u8 pad2;324u32 wlc_index; /* optional for wlc index */325};326327struct wl_interface_create_v3 {328u16 ver; /* structure version */329u16 len; /* length of structure + data */330u16 fixed_len; /* length of structure */331u8 iftype; /* type of interface created */332u8 wlc_index; /* optional for wlc index */333u32 flags; /* flags for operation */334u8 mac_addr[ETH_ALEN]; /* MAC address */335u8 bssid[ETH_ALEN]; /* optional for BSSID */336u8 if_index; /* interface index request */337u8 pad[3];338u8 data[]; /* Optional for specific data */339};340341static u8 nl80211_band_to_fwil(enum nl80211_band band)342{343switch (band) {344case NL80211_BAND_2GHZ:345return WLC_BAND_2G;346case NL80211_BAND_5GHZ:347return WLC_BAND_5G;348default:349WARN_ON(1);350break;351}352return 0;353}354355static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,356struct cfg80211_chan_def *ch)357{358struct brcmu_chan ch_inf;359s32 primary_offset;360361brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",362ch->chan->center_freq, ch->center_freq1, ch->width);363ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);364primary_offset = ch->chan->center_freq - ch->center_freq1;365switch (ch->width) {366case NL80211_CHAN_WIDTH_20:367case NL80211_CHAN_WIDTH_20_NOHT:368ch_inf.bw = BRCMU_CHAN_BW_20;369WARN_ON(primary_offset != 0);370break;371case NL80211_CHAN_WIDTH_40:372ch_inf.bw = BRCMU_CHAN_BW_40;373if (primary_offset > 0)374ch_inf.sb = BRCMU_CHAN_SB_U;375else376ch_inf.sb = BRCMU_CHAN_SB_L;377break;378case NL80211_CHAN_WIDTH_80:379ch_inf.bw = BRCMU_CHAN_BW_80;380if (primary_offset == -30)381ch_inf.sb = BRCMU_CHAN_SB_LL;382else if (primary_offset == -10)383ch_inf.sb = BRCMU_CHAN_SB_LU;384else if (primary_offset == 10)385ch_inf.sb = BRCMU_CHAN_SB_UL;386else387ch_inf.sb = BRCMU_CHAN_SB_UU;388break;389case NL80211_CHAN_WIDTH_160:390ch_inf.bw = BRCMU_CHAN_BW_160;391if (primary_offset == -70)392ch_inf.sb = BRCMU_CHAN_SB_LLL;393else if (primary_offset == -50)394ch_inf.sb = BRCMU_CHAN_SB_LLU;395else if (primary_offset == -30)396ch_inf.sb = BRCMU_CHAN_SB_LUL;397else if (primary_offset == -10)398ch_inf.sb = BRCMU_CHAN_SB_LUU;399else if (primary_offset == 10)400ch_inf.sb = BRCMU_CHAN_SB_ULL;401else if (primary_offset == 30)402ch_inf.sb = BRCMU_CHAN_SB_ULU;403else if (primary_offset == 50)404ch_inf.sb = BRCMU_CHAN_SB_UUL;405else406ch_inf.sb = BRCMU_CHAN_SB_UUU;407break;408case NL80211_CHAN_WIDTH_80P80:409case NL80211_CHAN_WIDTH_5:410case NL80211_CHAN_WIDTH_10:411default:412WARN_ON_ONCE(1);413}414switch (ch->chan->band) {415case NL80211_BAND_2GHZ:416ch_inf.band = BRCMU_CHAN_BAND_2G;417break;418case NL80211_BAND_5GHZ:419ch_inf.band = BRCMU_CHAN_BAND_5G;420break;421case NL80211_BAND_60GHZ:422default:423WARN_ON_ONCE(1);424}425d11inf->encchspec(&ch_inf);426427brcmf_dbg(TRACE, "chanspec: 0x%x\n", ch_inf.chspec);428return ch_inf.chspec;429}430431u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,432struct ieee80211_channel *ch)433{434struct brcmu_chan ch_inf;435436ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);437ch_inf.bw = BRCMU_CHAN_BW_20;438d11inf->encchspec(&ch_inf);439440return ch_inf.chspec;441}442443/* Traverse a string of 1-byte tag/1-byte length/variable-length value444* triples, returning a pointer to the substring whose first element445* matches tag446*/447static const struct brcmf_tlv *448brcmf_parse_tlvs(const void *buf, int buflen, uint key)449{450const struct brcmf_tlv *elt = buf;451int totlen = buflen;452453/* find tagged parameter */454while (totlen >= TLV_HDR_LEN) {455int len = elt->len;456457/* validate remaining totlen */458if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))459return elt;460461#if defined(__linux__)462elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));463#elif defined(__FreeBSD__)464elt = (const struct brcmf_tlv *)((const u8 *)elt + (len + TLV_HDR_LEN));465#endif466totlen -= (len + TLV_HDR_LEN);467}468469return NULL;470}471472/* Is any of the tlvs the expected entry? If473* not update the tlvs buffer pointer/length.474*/475static bool476brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,477const u8 *oui, u32 oui_len, u8 type)478{479/* If the contents match the OUI and the type */480if (ie[TLV_LEN_OFF] >= oui_len + 1 &&481!memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&482type == ie[TLV_BODY_OFF + oui_len]) {483return true;484}485486if (tlvs == NULL)487return false;488/* point to the next ie */489ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;490/* calculate the length of the rest of the buffer */491*tlvs_len -= (int)(ie - *tlvs);492/* update the pointer to the start of the buffer */493*tlvs = ie;494495return false;496}497498#if defined(__linux__)499static struct brcmf_vs_tlv *500#elif defined(__FreeBSD__)501static const struct brcmf_vs_tlv *502#endif503brcmf_find_wpaie(const u8 *parse, u32 len)504{505const struct brcmf_tlv *ie;506507while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {508if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,509WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))510#if defined(__linux__)511return (struct brcmf_vs_tlv *)ie;512#elif defined(__FreeBSD__)513return (const struct brcmf_vs_tlv *)ie;514#endif515}516return NULL;517}518519#if defined(__linux__)520static struct brcmf_vs_tlv *521#elif defined(__FreeBSD__)522static const struct brcmf_vs_tlv *523#endif524brcmf_find_wpsie(const u8 *parse, u32 len)525{526const struct brcmf_tlv *ie;527528while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {529#if defined(__linux__)530if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,531WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))532return (struct brcmf_vs_tlv *)ie;533#elif defined(__FreeBSD__)534if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,535WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))536return (const struct brcmf_vs_tlv *)ie;537#endif538}539return NULL;540}541542static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,543struct brcmf_cfg80211_vif *vif,544enum nl80211_iftype new_type)545{546struct brcmf_cfg80211_vif *pos;547bool check_combos = false;548int ret = 0;549struct iface_combination_params params = {550.num_different_channels = 1,551};552553list_for_each_entry(pos, &cfg->vif_list, list)554if (pos == vif) {555params.iftype_num[new_type]++;556} else {557/* concurrent interfaces so need check combinations */558check_combos = true;559params.iftype_num[pos->wdev.iftype]++;560}561562if (check_combos)563ret = cfg80211_check_combinations(cfg->wiphy, ¶ms);564565return ret;566}567568static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,569enum nl80211_iftype new_type)570{571struct brcmf_cfg80211_vif *pos;572struct iface_combination_params params = {573.num_different_channels = 1,574};575576list_for_each_entry(pos, &cfg->vif_list, list)577params.iftype_num[pos->wdev.iftype]++;578579params.iftype_num[new_type]++;580return cfg80211_check_combinations(cfg->wiphy, ¶ms);581}582583static void convert_key_from_CPU(struct brcmf_wsec_key *key,584struct brcmf_wsec_key_le *key_le)585{586key_le->index = cpu_to_le32(key->index);587key_le->len = cpu_to_le32(key->len);588key_le->algo = cpu_to_le32(key->algo);589key_le->flags = cpu_to_le32(key->flags);590key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);591key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);592key_le->iv_initialized = cpu_to_le32(key->iv_initialized);593memcpy(key_le->data, key->data, sizeof(key->data));594memcpy(key_le->ea, key->ea, sizeof(key->ea));595}596597static int598send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)599{600struct brcmf_pub *drvr = ifp->drvr;601int err;602struct brcmf_wsec_key_le key_le;603604convert_key_from_CPU(key, &key_le);605606brcmf_netdev_wait_pend8021x(ifp);607608err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,609sizeof(key_le));610611if (err)612bphy_err(drvr, "wsec_key error (%d)\n", err);613return err;614}615616static void617brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)618{619struct brcmf_cfg80211_vif *vif;620struct brcmf_if *ifp;621622vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);623ifp = vif->ifp;624625if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||626(wdev->iftype == NL80211_IFTYPE_AP) ||627(wdev->iftype == NL80211_IFTYPE_P2P_GO))628brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,629ADDR_DIRECT);630else631brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,632ADDR_INDIRECT);633}634635static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)636{637int bsscfgidx;638639for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) {640/* bsscfgidx 1 is reserved for legacy P2P */641if (bsscfgidx == 1)642continue;643if (!drvr->iflist[bsscfgidx])644return bsscfgidx;645}646647return -ENOMEM;648}649650static void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr)651{652u8 mac_idx = ifp->drvr->sta_mac_idx;653654/* set difference MAC address with locally administered bit */655memcpy(mac_addr, ifp->mac_addr, ETH_ALEN);656mac_addr[0] |= 0x02;657mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0;658mac_idx++;659mac_idx = mac_idx % 2;660ifp->drvr->sta_mac_idx = mac_idx;661}662663static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr)664{665struct wl_interface_create_v1 iface_v1;666struct wl_interface_create_v2 iface_v2;667struct wl_interface_create_v3 iface_v3;668u32 iface_create_ver;669int err;670671/* interface_create version 1 */672memset(&iface_v1, 0, sizeof(iface_v1));673iface_v1.ver = WL_INTERFACE_CREATE_VER_1;674iface_v1.flags = WL_INTERFACE_CREATE_STA |675WL_INTERFACE_MAC_USE;676if (!is_zero_ether_addr(macaddr))677memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN);678else679brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);680681err = brcmf_fil_iovar_data_get(ifp, "interface_create",682&iface_v1,683sizeof(iface_v1));684if (err) {685brcmf_info("failed to create interface(v1), err=%d\n",686err);687} else {688brcmf_dbg(INFO, "interface created(v1)\n");689return 0;690}691692/* interface_create version 2 */693memset(&iface_v2, 0, sizeof(iface_v2));694iface_v2.ver = WL_INTERFACE_CREATE_VER_2;695iface_v2.flags = WL_INTERFACE_MAC_USE;696iface_v2.iftype = WL_INTERFACE_CREATE_STA;697if (!is_zero_ether_addr(macaddr))698memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN);699else700brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);701702err = brcmf_fil_iovar_data_get(ifp, "interface_create",703&iface_v2,704sizeof(iface_v2));705if (err) {706brcmf_info("failed to create interface(v2), err=%d\n",707err);708} else {709brcmf_dbg(INFO, "interface created(v2)\n");710return 0;711}712713/* interface_create version 3+ */714/* get supported version from firmware side */715iface_create_ver = 0;716err = brcmf_fil_bsscfg_int_query(ifp, "interface_create",717&iface_create_ver);718if (err) {719brcmf_err("fail to get supported version, err=%d\n", err);720return -EOPNOTSUPP;721}722723switch (iface_create_ver) {724case WL_INTERFACE_CREATE_VER_3:725memset(&iface_v3, 0, sizeof(iface_v3));726iface_v3.ver = WL_INTERFACE_CREATE_VER_3;727iface_v3.flags = WL_INTERFACE_MAC_USE;728iface_v3.iftype = WL_INTERFACE_CREATE_STA;729if (!is_zero_ether_addr(macaddr))730memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN);731else732brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);733734err = brcmf_fil_iovar_data_get(ifp, "interface_create",735&iface_v3,736sizeof(iface_v3));737738if (!err)739brcmf_dbg(INFO, "interface created(v3)\n");740break;741default:742brcmf_err("not support interface create(v%d)\n",743iface_create_ver);744err = -EOPNOTSUPP;745break;746}747748if (err) {749brcmf_info("station interface creation failed (%d)\n",750err);751return -EIO;752}753754return 0;755}756757static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)758{759struct wl_interface_create_v1 iface_v1;760struct wl_interface_create_v2 iface_v2;761struct wl_interface_create_v3 iface_v3;762u32 iface_create_ver;763struct brcmf_pub *drvr = ifp->drvr;764struct brcmf_mbss_ssid_le mbss_ssid_le;765int bsscfgidx;766int err;767768/* interface_create version 1 */769memset(&iface_v1, 0, sizeof(iface_v1));770iface_v1.ver = WL_INTERFACE_CREATE_VER_1;771iface_v1.flags = WL_INTERFACE_CREATE_AP |772WL_INTERFACE_MAC_USE;773774brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);775776err = brcmf_fil_iovar_data_get(ifp, "interface_create",777&iface_v1,778sizeof(iface_v1));779if (err) {780brcmf_info("failed to create interface(v1), err=%d\n",781err);782} else {783brcmf_dbg(INFO, "interface created(v1)\n");784return 0;785}786787/* interface_create version 2 */788memset(&iface_v2, 0, sizeof(iface_v2));789iface_v2.ver = WL_INTERFACE_CREATE_VER_2;790iface_v2.flags = WL_INTERFACE_MAC_USE;791iface_v2.iftype = WL_INTERFACE_CREATE_AP;792793brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);794795err = brcmf_fil_iovar_data_get(ifp, "interface_create",796&iface_v2,797sizeof(iface_v2));798if (err) {799brcmf_info("failed to create interface(v2), err=%d\n",800err);801} else {802brcmf_dbg(INFO, "interface created(v2)\n");803return 0;804}805806/* interface_create version 3+ */807/* get supported version from firmware side */808iface_create_ver = 0;809err = brcmf_fil_bsscfg_int_query(ifp, "interface_create",810&iface_create_ver);811if (err) {812brcmf_err("fail to get supported version, err=%d\n", err);813return -EOPNOTSUPP;814}815816switch (iface_create_ver) {817case WL_INTERFACE_CREATE_VER_3:818memset(&iface_v3, 0, sizeof(iface_v3));819iface_v3.ver = WL_INTERFACE_CREATE_VER_3;820iface_v3.flags = WL_INTERFACE_MAC_USE;821iface_v3.iftype = WL_INTERFACE_CREATE_AP;822brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);823824err = brcmf_fil_iovar_data_get(ifp, "interface_create",825&iface_v3,826sizeof(iface_v3));827828if (!err)829brcmf_dbg(INFO, "interface created(v3)\n");830break;831default:832brcmf_err("not support interface create(v%d)\n",833iface_create_ver);834err = -EOPNOTSUPP;835break;836}837838if (err) {839brcmf_info("Does not support interface_create (%d)\n",840err);841memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));842bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr);843if (bsscfgidx < 0)844return bsscfgidx;845846mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);847mbss_ssid_le.SSID_len = cpu_to_le32(5);848sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx);849850err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,851sizeof(mbss_ssid_le));852853if (err < 0)854bphy_err(drvr, "setting ssid failed %d\n", err);855}856857return err;858}859860/**861* brcmf_apsta_add_vif() - create a new AP or STA virtual interface862*863* @wiphy: wiphy device of new interface.864* @name: name of the new interface.865* @params: contains mac address for AP or STA device.866* @type: interface type.867*868* Return: pointer to new vif on success, ERR_PTR(-errno) if not869*/870static871struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name,872struct vif_params *params,873enum nl80211_iftype type)874{875struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);876struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));877struct brcmf_pub *drvr = cfg->pub;878struct brcmf_cfg80211_vif *vif;879int err;880881if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP)882return ERR_PTR(-EINVAL);883884if (brcmf_cfg80211_vif_event_armed(cfg))885return ERR_PTR(-EBUSY);886887brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);888889vif = brcmf_alloc_vif(cfg, type);890if (IS_ERR(vif))891return (struct wireless_dev *)vif;892893brcmf_cfg80211_arm_vif_event(cfg, vif);894895if (type == NL80211_IFTYPE_STATION)896err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr);897else898err = brcmf_cfg80211_request_ap_if(ifp);899if (err) {900brcmf_cfg80211_arm_vif_event(cfg, NULL);901goto fail;902}903904/* wait for firmware event */905err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,906BRCMF_VIF_EVENT_TIMEOUT);907brcmf_cfg80211_arm_vif_event(cfg, NULL);908if (!err) {909bphy_err(drvr, "timeout occurred\n");910err = -EIO;911goto fail;912}913914/* interface created in firmware */915ifp = vif->ifp;916if (!ifp) {917bphy_err(drvr, "no if pointer provided\n");918err = -ENOENT;919goto fail;920}921922strscpy(ifp->ndev->name, name, sizeof(ifp->ndev->name));923err = brcmf_net_attach(ifp, true);924if (err) {925bphy_err(drvr, "Registering netdevice failed\n");926free_netdev(ifp->ndev);927goto fail;928}929930return &ifp->vif->wdev;931932fail:933brcmf_free_vif(vif);934return ERR_PTR(err);935}936937static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)938{939enum nl80211_iftype iftype;940941iftype = vif->wdev.iftype;942return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;943}944945static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)946{947return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;948}949950/**951* brcmf_mon_add_vif() - create monitor mode virtual interface952*953* @wiphy: wiphy device of new interface.954* @name: name of the new interface.955*956* Return: pointer to new vif on success, ERR_PTR(-errno) if not957*/958static struct wireless_dev *brcmf_mon_add_vif(struct wiphy *wiphy,959const char *name)960{961struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);962struct brcmf_cfg80211_vif *vif;963struct net_device *ndev;964struct brcmf_if *ifp;965int err;966967if (cfg->pub->mon_if) {968err = -EEXIST;969goto err_out;970}971972vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_MONITOR);973if (IS_ERR(vif)) {974err = PTR_ERR(vif);975goto err_out;976}977978ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, ether_setup);979if (!ndev) {980err = -ENOMEM;981goto err_free_vif;982}983ndev->type = ARPHRD_IEEE80211_RADIOTAP;984ndev->ieee80211_ptr = &vif->wdev;985ndev->needs_free_netdev = true;986ndev->priv_destructor = brcmf_cfg80211_free_netdev;987SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));988989ifp = netdev_priv(ndev);990ifp->vif = vif;991ifp->ndev = ndev;992ifp->drvr = cfg->pub;993994vif->ifp = ifp;995vif->wdev.netdev = ndev;996997err = brcmf_net_mon_attach(ifp);998if (err) {999brcmf_err("Failed to attach %s device\n", ndev->name);1000free_netdev(ndev);1001goto err_free_vif;1002}10031004cfg->pub->mon_if = ifp;10051006return &vif->wdev;10071008err_free_vif:1009brcmf_free_vif(vif);1010err_out:1011return ERR_PTR(err);1012}10131014static int brcmf_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)1015{1016struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1017struct net_device *ndev = wdev->netdev;10181019ndev->netdev_ops->ndo_stop(ndev);10201021brcmf_net_detach(ndev, true);10221023cfg->pub->mon_if = NULL;10241025return 0;1026}10271028static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,1029const char *name,1030unsigned char name_assign_type,1031enum nl80211_iftype type,1032struct vif_params *params)1033{1034struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1035struct brcmf_pub *drvr = cfg->pub;1036struct wireless_dev *wdev;1037int err;10381039brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);1040err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);1041if (err) {1042bphy_err(drvr, "iface validation failed: err=%d\n", err);1043return ERR_PTR(err);1044}1045switch (type) {1046case NL80211_IFTYPE_ADHOC:1047case NL80211_IFTYPE_AP_VLAN:1048case NL80211_IFTYPE_WDS:1049case NL80211_IFTYPE_MESH_POINT:1050return ERR_PTR(-EOPNOTSUPP);1051case NL80211_IFTYPE_MONITOR:1052return brcmf_mon_add_vif(wiphy, name);1053case NL80211_IFTYPE_STATION:1054case NL80211_IFTYPE_AP:1055wdev = brcmf_apsta_add_vif(wiphy, name, params, type);1056break;1057case NL80211_IFTYPE_P2P_CLIENT:1058case NL80211_IFTYPE_P2P_GO:1059case NL80211_IFTYPE_P2P_DEVICE:1060wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params);1061break;1062case NL80211_IFTYPE_UNSPECIFIED:1063default:1064return ERR_PTR(-EINVAL);1065}10661067if (IS_ERR(wdev))1068bphy_err(drvr, "add iface %s type %d failed: err=%d\n", name,1069type, (int)PTR_ERR(wdev));1070else1071brcmf_cfg80211_update_proto_addr_mode(wdev);10721073return wdev;1074}10751076static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)1077{1078if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))1079brcmf_set_mpc(ifp, mpc);1080}10811082void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)1083{1084struct brcmf_pub *drvr = ifp->drvr;1085s32 err = 0;10861087if (check_vif_up(ifp->vif)) {1088err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);1089if (err) {1090bphy_err(drvr, "fail to set mpc\n");1091return;1092}1093brcmf_dbg(INFO, "MPC : %d\n", mpc);1094}1095}10961097bool brcmf_is_apmode_operating(struct wiphy *wiphy)1098{1099struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1100struct brcmf_cfg80211_vif *vif;1101bool ret = false;11021103list_for_each_entry(vif, &cfg->vif_list, list) {1104if (brcmf_is_apmode(vif) &&1105test_bit(BRCMF_VIF_STATUS_AP_CREATED, &vif->sme_state))1106ret = true;1107}11081109return ret;1110}11111112static void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le,1113struct brcmf_scan_params_le *params_le)1114{1115size_t params_size;1116u32 ch;1117int n_channels, n_ssids;11181119memcpy(¶ms_le->ssid_le, ¶ms_v2_le->ssid_le,1120sizeof(params_le->ssid_le));1121memcpy(¶ms_le->bssid, ¶ms_v2_le->bssid,1122sizeof(params_le->bssid));11231124params_le->bss_type = params_v2_le->bss_type;1125params_le->scan_type = le32_to_cpu(params_v2_le->scan_type);1126params_le->nprobes = params_v2_le->nprobes;1127params_le->active_time = params_v2_le->active_time;1128params_le->passive_time = params_v2_le->passive_time;1129params_le->home_time = params_v2_le->home_time;1130params_le->channel_num = params_v2_le->channel_num;11311132ch = le32_to_cpu(params_v2_le->channel_num);1133n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK;1134n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT;11351136params_size = sizeof(u16) * n_channels;1137if (n_ssids > 0) {1138params_size = roundup(params_size, sizeof(u32));1139params_size += sizeof(struct brcmf_ssid_le) * n_ssids;1140}11411142memcpy(¶ms_le->channel_list[0],1143¶ms_v2_le->channel_list[0], params_size);1144}11451146static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,1147struct brcmf_scan_params_v2_le *params_le,1148struct cfg80211_scan_request *request)1149{1150u32 n_ssids;1151u32 n_channels;1152s32 i;1153s32 offset;1154u16 chanspec;1155char *ptr;1156int length;1157struct brcmf_ssid_le ssid_le;11581159eth_broadcast_addr(params_le->bssid);11601161length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;11621163params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2);1164params_le->bss_type = DOT11_BSSTYPE_ANY;1165params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE);1166params_le->channel_num = 0;1167params_le->nprobes = cpu_to_le32(-1);1168params_le->active_time = cpu_to_le32(-1);1169params_le->passive_time = cpu_to_le32(-1);1170params_le->home_time = cpu_to_le32(-1);1171memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le));11721173/* Scan abort */1174if (!request) {1175length += sizeof(u16);1176params_le->channel_num = cpu_to_le32(1);1177params_le->channel_list[0] = cpu_to_le16(-1);1178params_le->length = cpu_to_le16(length);1179return;1180}11811182n_ssids = request->n_ssids;1183n_channels = request->n_channels;11841185/* Copy channel array if applicable */1186brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",1187n_channels);1188if (n_channels > 0) {1189length += roundup(sizeof(u16) * n_channels, sizeof(u32));1190for (i = 0; i < n_channels; i++) {1191chanspec = channel_to_chanspec(&cfg->d11inf,1192request->channels[i]);1193brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",1194request->channels[i]->hw_value, chanspec);1195params_le->channel_list[i] = cpu_to_le16(chanspec);1196}1197} else {1198brcmf_dbg(SCAN, "Scanning all channels\n");1199}12001201/* Copy ssid array if applicable */1202brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);1203if (n_ssids > 0) {1204offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) +1205n_channels * sizeof(u16);1206offset = roundup(offset, sizeof(u32));1207length += sizeof(ssid_le) * n_ssids;1208ptr = (char *)params_le + offset;1209for (i = 0; i < n_ssids; i++) {1210memset(&ssid_le, 0, sizeof(ssid_le));1211ssid_le.SSID_len =1212cpu_to_le32(request->ssids[i].ssid_len);1213memcpy(ssid_le.SSID, request->ssids[i].ssid,1214request->ssids[i].ssid_len);1215if (!ssid_le.SSID_len)1216brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);1217else1218brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n",1219i, ssid_le.SSID, ssid_le.SSID_len);1220memcpy(ptr, &ssid_le, sizeof(ssid_le));1221ptr += sizeof(ssid_le);1222}1223} else {1224brcmf_dbg(SCAN, "Performing passive scan\n");1225params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE);1226}1227params_le->length = cpu_to_le16(length);1228/* Adding mask to channel numbers */1229params_le->channel_num =1230cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |1231(n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));1232}12331234s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,1235struct brcmf_if *ifp, bool aborted,1236bool fw_abort)1237{1238struct brcmf_pub *drvr = cfg->pub;1239struct brcmf_scan_params_v2_le params_v2_le;1240struct cfg80211_scan_request *scan_request;1241u64 reqid;1242u32 bucket;1243s32 err = 0;12441245brcmf_dbg(SCAN, "Enter\n");12461247/* clear scan request, because the FW abort can cause a second call */1248/* to this functon and might cause a double cfg80211_scan_done */1249scan_request = cfg->scan_request;1250cfg->scan_request = NULL;12511252timer_delete_sync(&cfg->escan_timeout);12531254if (fw_abort) {1255/* Do a scan abort to stop the driver's scan engine */1256brcmf_dbg(SCAN, "ABORT scan in firmware\n");12571258brcmf_escan_prep(cfg, ¶ms_v2_le, NULL);12591260/* E-Scan (or anyother type) can be aborted by SCAN */1261if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {1262err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,1263¶ms_v2_le,1264sizeof(params_v2_le));1265} else {1266struct brcmf_scan_params_le params_le;12671268brcmf_scan_params_v2_to_v1(¶ms_v2_le, ¶ms_le);1269err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,1270¶ms_le,1271sizeof(params_le));1272}12731274if (err)1275bphy_err(drvr, "Scan abort failed\n");1276}12771278brcmf_scan_config_mpc(ifp, 1);12791280/*1281* e-scan can be initiated internally1282* which takes precedence.1283*/1284if (cfg->int_escan_map) {1285brcmf_dbg(SCAN, "scheduled scan completed (%x)\n",1286cfg->int_escan_map);1287while (cfg->int_escan_map) {1288bucket = __ffs(cfg->int_escan_map);1289cfg->int_escan_map &= ~BIT(bucket);1290reqid = brcmf_pno_find_reqid_by_bucket(cfg->pno,1291bucket);1292if (!aborted) {1293#if defined(__linux__)1294brcmf_dbg(SCAN, "report results: reqid=%llu\n",1295reqid);1296#elif defined(__FreeBSD__)1297brcmf_dbg(SCAN, "report results: reqid=%ju\n",1298(uintmax_t)reqid);1299#endif1300cfg80211_sched_scan_results(cfg_to_wiphy(cfg),1301reqid);1302}1303}1304} else if (scan_request) {1305struct cfg80211_scan_info info = {1306.aborted = aborted,1307};13081309brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",1310aborted ? "Aborted" : "Done");1311cfg80211_scan_done(scan_request, &info);1312}1313if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))1314brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");13151316return err;1317}13181319static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy,1320struct wireless_dev *wdev)1321{1322struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1323struct net_device *ndev = wdev->netdev;1324struct brcmf_if *ifp = netdev_priv(ndev);1325struct brcmf_pub *drvr = cfg->pub;1326int ret;1327int err;13281329brcmf_cfg80211_arm_vif_event(cfg, ifp->vif);13301331err = brcmf_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0);1332if (err) {1333bphy_err(drvr, "interface_remove failed %d\n", err);1334goto err_unarm;1335}13361337/* wait for firmware event */1338ret = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,1339BRCMF_VIF_EVENT_TIMEOUT);1340if (!ret) {1341bphy_err(drvr, "timeout occurred\n");1342err = -EIO;1343goto err_unarm;1344}13451346brcmf_remove_interface(ifp, true);13471348err_unarm:1349brcmf_cfg80211_arm_vif_event(cfg, NULL);1350return err;1351}13521353static1354int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)1355{1356struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1357struct net_device *ndev = wdev->netdev;13581359if (ndev && ndev == cfg_to_ndev(cfg))1360return -ENOTSUPP;13611362/* vif event pending in firmware */1363if (brcmf_cfg80211_vif_event_armed(cfg))1364return -EBUSY;13651366if (ndev) {1367if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&1368cfg->escan_info.ifp == netdev_priv(ndev))1369brcmf_notify_escan_complete(cfg, netdev_priv(ndev),1370true, true);13711372brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);1373}13741375switch (wdev->iftype) {1376case NL80211_IFTYPE_ADHOC:1377case NL80211_IFTYPE_AP_VLAN:1378case NL80211_IFTYPE_WDS:1379case NL80211_IFTYPE_MESH_POINT:1380return -EOPNOTSUPP;1381case NL80211_IFTYPE_MONITOR:1382return brcmf_mon_del_vif(wiphy, wdev);1383case NL80211_IFTYPE_STATION:1384case NL80211_IFTYPE_AP:1385return brcmf_cfg80211_del_apsta_iface(wiphy, wdev);1386case NL80211_IFTYPE_P2P_CLIENT:1387case NL80211_IFTYPE_P2P_GO:1388case NL80211_IFTYPE_P2P_DEVICE:1389return brcmf_p2p_del_vif(wiphy, wdev);1390case NL80211_IFTYPE_UNSPECIFIED:1391default:1392return -EINVAL;1393}1394return -EOPNOTSUPP;1395}13961397static s321398brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,1399enum nl80211_iftype type,1400struct vif_params *params)1401{1402struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1403struct brcmf_if *ifp = netdev_priv(ndev);1404struct brcmf_cfg80211_vif *vif = ifp->vif;1405struct brcmf_pub *drvr = cfg->pub;1406s32 infra = 0;1407s32 ap = 0;1408s32 err = 0;14091410brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,1411type);14121413/* WAR: There are a number of p2p interface related problems which1414* need to be handled initially (before doing the validate).1415* wpa_supplicant tends to do iface changes on p2p device/client/go1416* which are not always possible/allowed. However we need to return1417* OK otherwise the wpa_supplicant wont start. The situation differs1418* on configuration and setup (p2pon=1 module param). The first check1419* is to see if the request is a change to station for p2p iface.1420*/1421if ((type == NL80211_IFTYPE_STATION) &&1422((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||1423(vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||1424(vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {1425brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");1426/* Now depending on whether module param p2pon=1 was used the1427* response needs to be either 0 or EOPNOTSUPP. The reason is1428* that if p2pon=1 is used, but a newer supplicant is used then1429* we should return an error, as this combination wont work.1430* In other situations 0 is returned and supplicant will start1431* normally. It will give a trace in cfg80211, but it is the1432* only way to get it working. Unfortunately this will result1433* in situation where we wont support new supplicant in1434* combination with module param p2pon=1, but that is the way1435* it is. If the user tries this then unloading of driver might1436* fail/lock.1437*/1438if (cfg->p2p.p2pdev_dynamically)1439return -EOPNOTSUPP;1440else1441return 0;1442}1443err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);1444if (err) {1445bphy_err(drvr, "iface validation failed: err=%d\n", err);1446return err;1447}1448switch (type) {1449case NL80211_IFTYPE_MONITOR:1450case NL80211_IFTYPE_WDS:1451bphy_err(drvr, "type (%d) : currently we do not support this type\n",1452type);1453return -EOPNOTSUPP;1454case NL80211_IFTYPE_ADHOC:1455infra = 0;1456break;1457case NL80211_IFTYPE_STATION:1458infra = 1;1459break;1460case NL80211_IFTYPE_AP:1461case NL80211_IFTYPE_P2P_GO:1462ap = 1;1463break;1464default:1465err = -EINVAL;1466goto done;1467}14681469if (ap) {1470if (type == NL80211_IFTYPE_P2P_GO) {1471brcmf_dbg(INFO, "IF Type = P2P GO\n");1472err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);1473}1474if (!err) {1475brcmf_dbg(INFO, "IF Type = AP\n");1476}1477} else {1478err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);1479if (err) {1480bphy_err(drvr, "WLC_SET_INFRA error (%d)\n", err);1481err = -EAGAIN;1482goto done;1483}1484brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?1485"Adhoc" : "Infra");1486}1487ndev->ieee80211_ptr->iftype = type;14881489brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);14901491done:1492brcmf_dbg(TRACE, "Exit\n");14931494return err;1495}14961497static s321498brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,1499struct cfg80211_scan_request *request)1500{1501struct brcmf_pub *drvr = cfg->pub;1502s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE +1503offsetof(struct brcmf_escan_params_le, params_v2_le);1504struct brcmf_escan_params_le *params;1505s32 err = 0;15061507brcmf_dbg(SCAN, "E-SCAN START\n");15081509if (request != NULL) {1510/* Allocate space for populating ssids in struct */1511params_size += sizeof(u32) * ((request->n_channels + 1) / 2);15121513/* Allocate space for populating ssids in struct */1514params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;1515}15161517params = kzalloc(params_size, GFP_KERNEL);1518if (!params) {1519err = -ENOMEM;1520goto exit;1521}1522BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);1523brcmf_escan_prep(cfg, ¶ms->params_v2_le, request);15241525params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2);15261527if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {1528struct brcmf_escan_params_le *params_v1;15291530params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;1531params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE;1532params_v1 = kzalloc(params_size, GFP_KERNEL);1533if (!params_v1) {1534err = -ENOMEM;1535goto exit_params;1536}1537params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);1538brcmf_scan_params_v2_to_v1(¶ms->params_v2_le, ¶ms_v1->params_le);1539kfree(params);1540params = params_v1;1541}15421543params->action = cpu_to_le16(WL_ESCAN_ACTION_START);1544params->sync_id = cpu_to_le16(0x1234);15451546err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);1547if (err) {1548if (err == -EBUSY)1549brcmf_dbg(INFO, "system busy : escan canceled\n");1550else1551bphy_err(drvr, "error (%d)\n", err);1552}15531554exit_params:1555kfree(params);1556exit:1557return err;1558}15591560static s321561brcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request)1562{1563struct brcmf_cfg80211_info *cfg = ifp->drvr->config;1564s32 err;1565struct brcmf_scan_results *results;1566struct escan_info *escan = &cfg->escan_info;15671568brcmf_dbg(SCAN, "Enter\n");1569escan->ifp = ifp;1570escan->wiphy = cfg->wiphy;1571escan->escan_state = WL_ESCAN_STATE_SCANNING;15721573brcmf_scan_config_mpc(ifp, 0);1574results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;1575results->version = 0;1576results->count = 0;1577results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;15781579err = escan->run(cfg, ifp, request);1580if (err)1581brcmf_scan_config_mpc(ifp, 1);1582return err;1583}15841585static s321586brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)1587{1588struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1589struct brcmf_pub *drvr = cfg->pub;1590struct brcmf_cfg80211_vif *vif;1591s32 err = 0;15921593brcmf_dbg(TRACE, "Enter\n");1594vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);1595if (!check_vif_up(vif))1596return -EIO;15971598if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {1599bphy_err(drvr, "Scanning already: status (%lu)\n",1600cfg->scan_status);1601return -EAGAIN;1602}1603if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {1604bphy_err(drvr, "Scanning being aborted: status (%lu)\n",1605cfg->scan_status);1606return -EAGAIN;1607}1608if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {1609bphy_err(drvr, "Scanning suppressed: status (%lu)\n",1610cfg->scan_status);1611return -EAGAIN;1612}1613if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state)) {1614bphy_err(drvr, "Connecting: status (%lu)\n", vif->sme_state);1615return -EAGAIN;1616}16171618brcmf_dbg(SCAN, "START ESCAN\n");16191620cfg->scan_request = request;1621set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);16221623cfg->escan_info.run = brcmf_run_escan;1624err = brcmf_p2p_scan_prep(wiphy, request, vif);1625if (err)1626goto scan_out;16271628err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,1629request->ie, request->ie_len);1630if (err)1631goto scan_out;16321633/* If scan req comes for p2p0, send it over primary I/F */1634if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)1635vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;16361637err = brcmf_do_escan(vif->ifp, request);1638if (err)1639goto scan_out;16401641/* Arm scan timeout timer */1642mod_timer(&cfg->escan_timeout,1643jiffies + msecs_to_jiffies(BRCMF_ESCAN_TIMER_INTERVAL_MS));16441645return 0;16461647scan_out:1648bphy_err(drvr, "scan error (%d)\n", err);1649clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);1650cfg->scan_request = NULL;1651return err;1652}16531654static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)1655{1656struct brcmf_if *ifp = netdev_priv(ndev);1657struct brcmf_pub *drvr = ifp->drvr;1658s32 err = 0;16591660err = brcmf_fil_iovar_int_set(ifp, "rtsthresh", rts_threshold);1661if (err)1662bphy_err(drvr, "Error (%d)\n", err);16631664return err;1665}16661667static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)1668{1669struct brcmf_if *ifp = netdev_priv(ndev);1670struct brcmf_pub *drvr = ifp->drvr;1671s32 err = 0;16721673err = brcmf_fil_iovar_int_set(ifp, "fragthresh",1674frag_threshold);1675if (err)1676bphy_err(drvr, "Error (%d)\n", err);16771678return err;1679}16801681static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)1682{1683struct brcmf_if *ifp = netdev_priv(ndev);1684struct brcmf_pub *drvr = ifp->drvr;1685s32 err = 0;1686u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);16871688err = brcmf_fil_cmd_int_set(ifp, cmd, retry);1689if (err) {1690bphy_err(drvr, "cmd (%d) , error (%d)\n", cmd, err);1691return err;1692}1693return err;1694}16951696static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, int radio_idx,1697u32 changed)1698{1699struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1700struct net_device *ndev = cfg_to_ndev(cfg);1701struct brcmf_if *ifp = netdev_priv(ndev);1702s32 err = 0;17031704brcmf_dbg(TRACE, "Enter\n");1705if (!check_vif_up(ifp->vif))1706return -EIO;17071708if (changed & WIPHY_PARAM_RTS_THRESHOLD &&1709(cfg->conf->rts_threshold != wiphy->rts_threshold)) {1710cfg->conf->rts_threshold = wiphy->rts_threshold;1711err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);1712if (!err)1713goto done;1714}1715if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&1716(cfg->conf->frag_threshold != wiphy->frag_threshold)) {1717cfg->conf->frag_threshold = wiphy->frag_threshold;1718err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);1719if (!err)1720goto done;1721}1722if (changed & WIPHY_PARAM_RETRY_LONG1723&& (cfg->conf->retry_long != wiphy->retry_long)) {1724cfg->conf->retry_long = wiphy->retry_long;1725err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);1726if (!err)1727goto done;1728}1729if (changed & WIPHY_PARAM_RETRY_SHORT1730&& (cfg->conf->retry_short != wiphy->retry_short)) {1731cfg->conf->retry_short = wiphy->retry_short;1732err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);1733if (!err)1734goto done;1735}17361737done:1738brcmf_dbg(TRACE, "Exit\n");1739return err;1740}17411742static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)1743{1744memset(prof, 0, sizeof(*prof));1745}17461747static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)1748{1749u16 reason;17501751switch (e->event_code) {1752case BRCMF_E_DEAUTH:1753case BRCMF_E_DEAUTH_IND:1754case BRCMF_E_DISASSOC_IND:1755reason = e->reason;1756break;1757case BRCMF_E_LINK:1758default:1759reason = 0;1760break;1761}1762return reason;1763}17641765int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags)1766{1767struct brcmf_pub *drvr = ifp->drvr;1768struct brcmf_wsec_pmk_le pmk;1769int err;17701771if (key_len > sizeof(pmk.key)) {1772bphy_err(drvr, "key must be less than %zu bytes\n",1773sizeof(pmk.key));1774return -EINVAL;1775}17761777memset(&pmk, 0, sizeof(pmk));17781779/* pass key material directly */1780pmk.key_len = cpu_to_le16(key_len);1781pmk.flags = cpu_to_le16(flags);1782memcpy(pmk.key, key, key_len);17831784/* store key material in firmware */1785err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,1786&pmk, sizeof(pmk));1787if (err < 0)1788bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n",1789key_len);17901791return err;1792}1793BRCMF_EXPORT_SYMBOL_GPL(brcmf_set_wsec);17941795static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)1796{1797return brcmf_set_wsec(ifp, pmk_data, pmk_len, 0);1798}17991800static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason,1801bool locally_generated)1802{1803struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);1804struct brcmf_pub *drvr = cfg->pub;1805bool bus_up = drvr->bus_if->state == BRCMF_BUS_UP;1806s32 err = 0;18071808brcmf_dbg(TRACE, "Enter\n");18091810if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {1811if (bus_up) {1812brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n");1813err = brcmf_fil_cmd_data_set(vif->ifp,1814BRCMF_C_DISASSOC, NULL, 0);1815if (err)1816bphy_err(drvr, "WLC_DISASSOC failed (%d)\n",1817err);1818}18191820if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||1821(vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))1822cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,1823locally_generated, GFP_KERNEL);1824}1825clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);1826clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);1827clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);1828clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);1829brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);1830if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {1831if (bus_up)1832brcmf_set_pmk(vif->ifp, NULL, 0);1833vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE;1834}1835brcmf_dbg(TRACE, "Exit\n");1836}18371838static s321839brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,1840struct cfg80211_ibss_params *params)1841{1842struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);1843struct brcmf_if *ifp = netdev_priv(ndev);1844struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;1845struct brcmf_pub *drvr = cfg->pub;1846struct brcmf_join_params join_params;1847size_t join_params_size = 0;1848s32 err = 0;1849s32 wsec = 0;1850s32 bcnprd;1851u16 chanspec;1852u32 ssid_len;18531854brcmf_dbg(TRACE, "Enter\n");1855if (!check_vif_up(ifp->vif))1856return -EIO;18571858if (params->ssid)1859brcmf_dbg(CONN, "SSID: %s\n", params->ssid);1860else {1861brcmf_dbg(CONN, "SSID: NULL, Not supported\n");1862return -EOPNOTSUPP;1863}18641865set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);18661867if (params->bssid)1868brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);1869else1870brcmf_dbg(CONN, "No BSSID specified\n");18711872if (params->chandef.chan)1873brcmf_dbg(CONN, "channel: %d\n",1874params->chandef.chan->center_freq);1875else1876brcmf_dbg(CONN, "no channel specified\n");18771878if (params->channel_fixed)1879brcmf_dbg(CONN, "fixed channel required\n");1880else1881brcmf_dbg(CONN, "no fixed channel required\n");18821883if (params->ie && params->ie_len)1884brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);1885else1886brcmf_dbg(CONN, "no ie specified\n");18871888if (params->beacon_interval)1889brcmf_dbg(CONN, "beacon interval: %d\n",1890params->beacon_interval);1891else1892brcmf_dbg(CONN, "no beacon interval specified\n");18931894if (params->basic_rates)1895brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);1896else1897brcmf_dbg(CONN, "no basic rates specified\n");18981899if (params->privacy)1900brcmf_dbg(CONN, "privacy required\n");1901else1902brcmf_dbg(CONN, "no privacy required\n");19031904/* Configure Privacy for starter */1905if (params->privacy)1906wsec |= WEP_ENABLED;19071908err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);1909if (err) {1910bphy_err(drvr, "wsec failed (%d)\n", err);1911goto done;1912}19131914/* Configure Beacon Interval for starter */1915if (params->beacon_interval)1916bcnprd = params->beacon_interval;1917else1918bcnprd = 100;19191920err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);1921if (err) {1922bphy_err(drvr, "WLC_SET_BCNPRD failed (%d)\n", err);1923goto done;1924}19251926/* Configure required join parameter */1927memset(&join_params, 0, sizeof(struct brcmf_join_params));19281929/* SSID */1930ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);1931memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);1932join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);1933join_params_size = sizeof(join_params.ssid_le);19341935/* BSSID */1936if (params->bssid) {1937memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);1938join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;1939memcpy(profile->bssid, params->bssid, ETH_ALEN);1940} else {1941eth_broadcast_addr(join_params.params_le.bssid);1942eth_zero_addr(profile->bssid);1943}19441945/* Channel */1946if (params->chandef.chan) {1947u32 target_channel;19481949cfg->channel =1950ieee80211_frequency_to_channel(1951params->chandef.chan->center_freq);1952if (params->channel_fixed) {1953/* adding chanspec */1954chanspec = chandef_to_chanspec(&cfg->d11inf,1955¶ms->chandef);1956join_params.params_le.chanspec_list[0] =1957cpu_to_le16(chanspec);1958join_params.params_le.chanspec_num = cpu_to_le32(1);1959join_params_size += sizeof(join_params.params_le);1960}19611962/* set channel for starter */1963target_channel = cfg->channel;1964err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,1965target_channel);1966if (err) {1967bphy_err(drvr, "WLC_SET_CHANNEL failed (%d)\n", err);1968goto done;1969}1970} else1971cfg->channel = 0;19721973cfg->ibss_starter = false;197419751976err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,1977&join_params, join_params_size);1978if (err) {1979bphy_err(drvr, "WLC_SET_SSID failed (%d)\n", err);1980goto done;1981}19821983done:1984if (err)1985clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);1986brcmf_dbg(TRACE, "Exit\n");1987return err;1988}19891990static s321991brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)1992{1993struct brcmf_if *ifp = netdev_priv(ndev);19941995brcmf_dbg(TRACE, "Enter\n");1996if (!check_vif_up(ifp->vif)) {1997/* When driver is being unloaded, it can end up here. If an1998* error is returned then later on a debug trace in the wireless1999* core module will be printed. To avoid this 0 is returned.2000*/2001return 0;2002}20032004brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING, true);2005brcmf_net_setcarrier(ifp, false);20062007brcmf_dbg(TRACE, "Exit\n");20082009return 0;2010}20112012static s32 brcmf_set_wpa_version(struct net_device *ndev,2013struct cfg80211_connect_params *sme)2014{2015struct brcmf_if *ifp = netdev_priv(ndev);2016struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);2017struct brcmf_pub *drvr = ifp->drvr;2018struct brcmf_cfg80211_security *sec;2019s32 val;2020s32 err;20212022if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) {2023val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;2024} else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) {2025if (drvr->bus_if->fwvid == BRCMF_FWVENDOR_CYW &&2026sme->crypto.akm_suites[0] == WLAN_AKM_SUITE_SAE)2027val = WPA3_AUTH_SAE_PSK;2028else2029val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;2030} else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) {2031val = WPA3_AUTH_SAE_PSK;2032} else {2033val = WPA_AUTH_DISABLED;2034}2035brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);2036err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val);2037if (err) {2038bphy_err(drvr, "set wpa_auth failed (%d)\n", err);2039return err;2040}2041sec = &profile->sec;2042sec->wpa_versions = sme->crypto.wpa_versions;2043return err;2044}20452046static s32 brcmf_set_auth_type(struct net_device *ndev,2047struct cfg80211_connect_params *sme)2048{2049struct brcmf_if *ifp = netdev_priv(ndev);2050struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);2051struct brcmf_pub *drvr = ifp->drvr;2052struct brcmf_cfg80211_security *sec;2053s32 val = 0;2054s32 err = 0;20552056switch (sme->auth_type) {2057case NL80211_AUTHTYPE_OPEN_SYSTEM:2058val = 0;2059brcmf_dbg(CONN, "open system\n");2060break;2061case NL80211_AUTHTYPE_SHARED_KEY:2062val = 1;2063brcmf_dbg(CONN, "shared key\n");2064break;2065case NL80211_AUTHTYPE_SAE:2066val = 3;2067brcmf_dbg(CONN, "SAE authentication\n");2068break;2069default:2070val = 2;2071brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);2072break;2073}20742075err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);2076if (err) {2077bphy_err(drvr, "set auth failed (%d)\n", err);2078return err;2079}2080sec = &profile->sec;2081sec->auth_type = sme->auth_type;2082return err;2083}20842085static s322086brcmf_set_wsec_mode(struct net_device *ndev,2087struct cfg80211_connect_params *sme)2088{2089struct brcmf_if *ifp = netdev_priv(ndev);2090struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);2091struct brcmf_pub *drvr = ifp->drvr;2092struct brcmf_cfg80211_security *sec;2093s32 pval = 0;2094s32 gval = 0;2095s32 wsec;2096s32 err = 0;20972098if (sme->crypto.n_ciphers_pairwise) {2099switch (sme->crypto.ciphers_pairwise[0]) {2100case WLAN_CIPHER_SUITE_WEP40:2101case WLAN_CIPHER_SUITE_WEP104:2102pval = WEP_ENABLED;2103break;2104case WLAN_CIPHER_SUITE_TKIP:2105pval = TKIP_ENABLED;2106break;2107case WLAN_CIPHER_SUITE_CCMP:2108pval = AES_ENABLED;2109break;2110case WLAN_CIPHER_SUITE_AES_CMAC:2111pval = AES_ENABLED;2112break;2113default:2114bphy_err(drvr, "invalid cipher pairwise (%d)\n",2115sme->crypto.ciphers_pairwise[0]);2116return -EINVAL;2117}2118}2119if (sme->crypto.cipher_group) {2120switch (sme->crypto.cipher_group) {2121case WLAN_CIPHER_SUITE_WEP40:2122case WLAN_CIPHER_SUITE_WEP104:2123gval = WEP_ENABLED;2124break;2125case WLAN_CIPHER_SUITE_TKIP:2126gval = TKIP_ENABLED;2127break;2128case WLAN_CIPHER_SUITE_CCMP:2129gval = AES_ENABLED;2130break;2131case WLAN_CIPHER_SUITE_AES_CMAC:2132gval = AES_ENABLED;2133break;2134default:2135bphy_err(drvr, "invalid cipher group (%d)\n",2136sme->crypto.cipher_group);2137return -EINVAL;2138}2139}21402141brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);2142/* In case of privacy, but no security and WPS then simulate */2143/* setting AES. WPS-2.0 allows no security */2144if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&2145sme->privacy)2146pval = AES_ENABLED;21472148wsec = pval | gval;2149err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);2150if (err) {2151bphy_err(drvr, "error (%d)\n", err);2152return err;2153}21542155sec = &profile->sec;2156sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];2157sec->cipher_group = sme->crypto.cipher_group;21582159return err;2160}21612162static s322163brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)2164{2165struct brcmf_if *ifp = netdev_priv(ndev);2166struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;2167struct brcmf_pub *drvr = ifp->drvr;2168s32 val;2169s32 err;2170const struct brcmf_tlv *rsn_ie;2171const u8 *ie;2172u32 ie_len;2173u32 offset;2174u16 rsn_cap;2175u32 mfp;2176u16 count;21772178profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE;2179profile->is_ft = false;21802181if (!sme->crypto.n_akm_suites)2182return 0;21832184err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),2185"wpa_auth", &val);2186if (err) {2187bphy_err(drvr, "could not get wpa_auth (%d)\n", err);2188return err;2189}2190if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {2191switch (sme->crypto.akm_suites[0]) {2192case WLAN_AKM_SUITE_8021X:2193val = WPA_AUTH_UNSPECIFIED;2194if (sme->want_1x)2195profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;2196break;2197case WLAN_AKM_SUITE_PSK:2198val = WPA_AUTH_PSK;2199break;2200default:2201bphy_err(drvr, "invalid akm suite (%d)\n",2202sme->crypto.akm_suites[0]);2203return -EINVAL;2204}2205} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {2206switch (sme->crypto.akm_suites[0]) {2207case WLAN_AKM_SUITE_8021X:2208val = WPA2_AUTH_UNSPECIFIED;2209if (sme->want_1x)2210profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;2211break;2212case WLAN_AKM_SUITE_8021X_SHA256:2213val = WPA2_AUTH_1X_SHA256;2214if (sme->want_1x)2215profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;2216break;2217case WLAN_AKM_SUITE_PSK_SHA256:2218val = WPA2_AUTH_PSK_SHA256;2219break;2220case WLAN_AKM_SUITE_PSK:2221val = WPA2_AUTH_PSK;2222break;2223case WLAN_AKM_SUITE_FT_8021X:2224val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT;2225profile->is_ft = true;2226if (sme->want_1x)2227profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;2228break;2229case WLAN_AKM_SUITE_FT_PSK:2230val = WPA2_AUTH_PSK | WPA2_AUTH_FT;2231profile->is_ft = true;2232break;2233default:2234bphy_err(drvr, "invalid akm suite (%d)\n",2235sme->crypto.akm_suites[0]);2236return -EINVAL;2237}2238} else if (val & WPA3_AUTH_SAE_PSK) {2239switch (sme->crypto.akm_suites[0]) {2240case WLAN_AKM_SUITE_SAE:2241val = WPA3_AUTH_SAE_PSK;2242break;2243case WLAN_AKM_SUITE_FT_OVER_SAE:2244val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT;2245profile->is_ft = true;2246break;2247default:2248bphy_err(drvr, "invalid akm suite (%d)\n",2249sme->crypto.akm_suites[0]);2250return -EINVAL;2251}2252if (sme->crypto.sae_pwd) {2253profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;2254}2255}22562257if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)2258brcmf_dbg(INFO, "using 1X offload\n");2259if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE)2260brcmf_dbg(INFO, "using SAE offload\n");22612262if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))2263goto skip_mfp_config;2264/* The MFP mode (1 or 2) needs to be determined, parse IEs. The2265* IE will not be verified, just a quick search for MFP config2266*/2267rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,2268WLAN_EID_RSN);2269if (!rsn_ie)2270goto skip_mfp_config;2271ie = (const u8 *)rsn_ie;2272ie_len = rsn_ie->len + TLV_HDR_LEN;2273/* Skip unicast suite */2274offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;2275if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)2276goto skip_mfp_config;2277/* Skip multicast suite */2278count = ie[offset] + (ie[offset + 1] << 8);2279offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);2280if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)2281goto skip_mfp_config;2282/* Skip auth key management suite(s) */2283count = ie[offset] + (ie[offset + 1] << 8);2284offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);2285if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)2286goto skip_mfp_config;2287/* Ready to read capabilities */2288mfp = BRCMF_MFP_NONE;2289rsn_cap = ie[offset] + (ie[offset + 1] << 8);2290if (rsn_cap & RSN_CAP_MFPR_MASK)2291mfp = BRCMF_MFP_REQUIRED;2292else if (rsn_cap & RSN_CAP_MFPC_MASK)2293mfp = BRCMF_MFP_CAPABLE;2294brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);22952296skip_mfp_config:2297brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);2298err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);2299if (err) {2300bphy_err(drvr, "could not set wpa_auth (%d)\n", err);2301return err;2302}23032304return err;2305}23062307static s322308brcmf_set_sharedkey(struct net_device *ndev,2309struct cfg80211_connect_params *sme)2310{2311struct brcmf_if *ifp = netdev_priv(ndev);2312struct brcmf_pub *drvr = ifp->drvr;2313struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);2314struct brcmf_cfg80211_security *sec;2315struct brcmf_wsec_key key;2316s32 val;2317s32 err = 0;23182319brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);23202321if (sme->key_len == 0)2322return 0;23232324sec = &profile->sec;2325brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",2326sec->wpa_versions, sec->cipher_pairwise);23272328if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 |2329NL80211_WPA_VERSION_3))2330return 0;23312332if (!(sec->cipher_pairwise &2333(WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))2334return 0;23352336memset(&key, 0, sizeof(key));2337key.len = (u32) sme->key_len;2338key.index = (u32) sme->key_idx;2339if (key.len > sizeof(key.data)) {2340bphy_err(drvr, "Too long key length (%u)\n", key.len);2341return -EINVAL;2342}2343memcpy(key.data, sme->key, key.len);2344key.flags = BRCMF_PRIMARY_KEY;2345switch (sec->cipher_pairwise) {2346case WLAN_CIPHER_SUITE_WEP40:2347key.algo = CRYPTO_ALGO_WEP1;2348break;2349case WLAN_CIPHER_SUITE_WEP104:2350key.algo = CRYPTO_ALGO_WEP128;2351break;2352default:2353bphy_err(drvr, "Invalid algorithm (%d)\n",2354sme->crypto.ciphers_pairwise[0]);2355return -EINVAL;2356}2357/* Set the new key/index */2358brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",2359key.len, key.index, key.algo);2360brcmf_dbg(CONN, "key \"%s\"\n", key.data);2361err = send_key_to_dongle(ifp, &key);2362if (err)2363return err;23642365if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {2366brcmf_dbg(CONN, "set auth_type to shared key\n");2367val = WL_AUTH_SHARED_KEY; /* shared key */2368err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);2369if (err)2370bphy_err(drvr, "set auth failed (%d)\n", err);2371}2372return err;2373}23742375static2376enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,2377enum nl80211_auth_type type)2378{2379if (type == NL80211_AUTHTYPE_AUTOMATIC &&2380brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {2381brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");2382type = NL80211_AUTHTYPE_OPEN_SYSTEM;2383}2384return type;2385}23862387static void brcmf_set_join_pref(struct brcmf_if *ifp,2388struct cfg80211_bss_selection *bss_select)2389{2390struct brcmf_pub *drvr = ifp->drvr;2391struct brcmf_join_pref_params join_pref_params[2];2392enum nl80211_band band;2393int err, i = 0;23942395join_pref_params[i].len = 2;2396join_pref_params[i].rssi_gain = 0;23972398if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF)2399brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO);24002401switch (bss_select->behaviour) {2402case __NL80211_BSS_SELECT_ATTR_INVALID:2403brcmf_c_set_joinpref_default(ifp);2404return;2405case NL80211_BSS_SELECT_ATTR_BAND_PREF:2406join_pref_params[i].type = BRCMF_JOIN_PREF_BAND;2407band = bss_select->param.band_pref;2408join_pref_params[i].band = nl80211_band_to_fwil(band);2409i++;2410break;2411case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST:2412join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA;2413band = bss_select->param.adjust.band;2414join_pref_params[i].band = nl80211_band_to_fwil(band);2415join_pref_params[i].rssi_gain = bss_select->param.adjust.delta;2416i++;2417break;2418case NL80211_BSS_SELECT_ATTR_RSSI:2419default:2420break;2421}2422join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI;2423join_pref_params[i].len = 2;2424join_pref_params[i].rssi_gain = 0;2425join_pref_params[i].band = 0;2426err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,2427sizeof(join_pref_params));2428if (err)2429bphy_err(drvr, "Set join_pref error (%d)\n", err);2430}24312432static s322433brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,2434struct cfg80211_connect_params *sme)2435{2436struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);2437struct brcmf_if *ifp = netdev_priv(ndev);2438struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;2439struct ieee80211_channel *chan = sme->channel;2440struct brcmf_pub *drvr = ifp->drvr;2441struct brcmf_join_params join_params;2442size_t join_params_size;2443const struct brcmf_tlv *rsn_ie;2444const struct brcmf_vs_tlv *wpa_ie;2445const void *ie;2446u32 ie_len;2447struct brcmf_ext_join_params_le *ext_join_params;2448u16 chanspec;2449s32 err = 0;2450u32 ssid_len;24512452brcmf_dbg(TRACE, "Enter\n");2453if (!check_vif_up(ifp->vif))2454return -EIO;24552456if (!sme->ssid) {2457bphy_err(drvr, "Invalid ssid\n");2458return -EOPNOTSUPP;2459}24602461if (sme->channel_hint)2462chan = sme->channel_hint;24632464if (sme->bssid_hint)2465sme->bssid = sme->bssid_hint;24662467if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {2468/* A normal (non P2P) connection request setup. */2469ie = NULL;2470ie_len = 0;2471/* find the WPA_IE */2472#if defined(__linux__)2473wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);2474#elif defined(__FreeBSD__)2475wpa_ie = brcmf_find_wpaie(sme->ie, sme->ie_len);2476#endif2477if (wpa_ie) {2478ie = wpa_ie;2479ie_len = wpa_ie->len + TLV_HDR_LEN;2480} else {2481/* find the RSN_IE */2482rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,2483sme->ie_len,2484WLAN_EID_RSN);2485if (rsn_ie) {2486ie = rsn_ie;2487ie_len = rsn_ie->len + TLV_HDR_LEN;2488}2489}2490brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);2491}24922493err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,2494sme->ie, sme->ie_len);2495if (err)2496bphy_err(drvr, "Set Assoc REQ IE Failed\n");2497else2498brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");24992500set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);25012502if (chan) {2503cfg->channel =2504ieee80211_frequency_to_channel(chan->center_freq);2505chanspec = channel_to_chanspec(&cfg->d11inf, chan);2506brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",2507cfg->channel, chan->center_freq, chanspec);2508} else {2509cfg->channel = 0;2510chanspec = 0;2511}25122513#if defined(__linux__)2514brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);2515#elif defined(__FreeBSD__)2516brcmf_dbg(INFO, "ie (%p), ie_len (%u)\n", sme->ie, sme->ie_len);2517#endif25182519err = brcmf_set_wpa_version(ndev, sme);2520if (err) {2521bphy_err(drvr, "wl_set_wpa_version failed (%d)\n", err);2522goto done;2523}25242525sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);2526err = brcmf_set_auth_type(ndev, sme);2527if (err) {2528bphy_err(drvr, "wl_set_auth_type failed (%d)\n", err);2529goto done;2530}25312532err = brcmf_set_wsec_mode(ndev, sme);2533if (err) {2534bphy_err(drvr, "wl_set_set_cipher failed (%d)\n", err);2535goto done;2536}25372538err = brcmf_set_key_mgmt(ndev, sme);2539if (err) {2540bphy_err(drvr, "wl_set_key_mgmt failed (%d)\n", err);2541goto done;2542}25432544err = brcmf_set_sharedkey(ndev, sme);2545if (err) {2546bphy_err(drvr, "brcmf_set_sharedkey failed (%d)\n", err);2547goto done;2548}25492550if (sme->crypto.psk &&2551profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {2552if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {2553err = -EINVAL;2554goto done;2555}2556brcmf_dbg(INFO, "using PSK offload\n");2557profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;2558}25592560if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {2561/* enable firmware supplicant for this interface */2562err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1);2563if (err < 0) {2564bphy_err(drvr, "failed to enable fw supplicant\n");2565goto done;2566}2567}25682569if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK)2570err = brcmf_set_pmk(ifp, sme->crypto.psk,2571BRCMF_WSEC_MAX_PSK_LEN);2572else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) {2573/* clean up user-space RSNE */2574err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0);2575if (err) {2576bphy_err(drvr, "failed to clean up user-space RSNE\n");2577goto done;2578}2579err = brcmf_fwvid_set_sae_password(ifp, &sme->crypto);2580if (!err && sme->crypto.psk)2581err = brcmf_set_pmk(ifp, sme->crypto.psk,2582BRCMF_WSEC_MAX_PSK_LEN);2583}2584if (err)2585goto done;25862587/* Join with specific BSSID and cached SSID2588* If SSID is zero join based on BSSID only2589*/2590join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +2591offsetof(struct brcmf_assoc_params_le, chanspec_list);2592if (cfg->channel)2593join_params_size += sizeof(u16);2594ext_join_params = kzalloc(sizeof(*ext_join_params), GFP_KERNEL);2595if (ext_join_params == NULL) {2596err = -ENOMEM;2597goto done;2598}2599ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);2600ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);2601memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);2602if (ssid_len < IEEE80211_MAX_SSID_LEN)2603brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",2604ext_join_params->ssid_le.SSID, ssid_len);26052606/* Set up join scan parameters */2607ext_join_params->scan_le.scan_type = -1;2608ext_join_params->scan_le.home_time = cpu_to_le32(-1);26092610if (sme->bssid)2611memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);2612else2613eth_broadcast_addr(ext_join_params->assoc_le.bssid);26142615if (cfg->channel) {2616ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);26172618ext_join_params->assoc_le.chanspec_list[0] =2619cpu_to_le16(chanspec);2620/* Increase dwell time to receive probe response or detect2621* beacon from target AP at a noisy air only during connect2622* command.2623*/2624ext_join_params->scan_le.active_time =2625cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);2626ext_join_params->scan_le.passive_time =2627cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);2628/* To sync with presence period of VSDB GO send probe request2629* more frequently. Probe request will be stopped when it gets2630* probe response from target AP/GO.2631*/2632ext_join_params->scan_le.nprobes =2633cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /2634BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);2635} else {2636ext_join_params->scan_le.active_time = cpu_to_le32(-1);2637ext_join_params->scan_le.passive_time = cpu_to_le32(-1);2638ext_join_params->scan_le.nprobes = cpu_to_le32(-1);2639}26402641brcmf_set_join_pref(ifp, &sme->bss_select);26422643err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,2644join_params_size);2645kfree(ext_join_params);2646if (!err)2647/* This is it. join command worked, we are done */2648goto done;26492650/* join command failed, fallback to set ssid */2651memset(&join_params, 0, sizeof(join_params));2652join_params_size = sizeof(join_params.ssid_le);26532654memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);2655join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);26562657if (sme->bssid)2658memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);2659else2660eth_broadcast_addr(join_params.params_le.bssid);26612662if (cfg->channel) {2663join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);2664join_params.params_le.chanspec_num = cpu_to_le32(1);2665join_params_size += sizeof(join_params.params_le);2666}2667err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,2668&join_params, join_params_size);2669if (err)2670bphy_err(drvr, "BRCMF_C_SET_SSID failed (%d)\n", err);26712672done:2673if (err)2674clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);2675brcmf_dbg(TRACE, "Exit\n");2676return err;2677}26782679static s322680brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,2681u16 reason_code)2682{2683struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);2684struct brcmf_if *ifp = netdev_priv(ndev);2685struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;2686struct brcmf_pub *drvr = cfg->pub;2687struct brcmf_scb_val_le scbval;2688s32 err = 0;26892690brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);2691if (!check_vif_up(ifp->vif))2692return -EIO;26932694clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);2695clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);2696clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state);2697clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state);2698cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);26992700memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);2701scbval.val = cpu_to_le32(reason_code);2702err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,2703&scbval, sizeof(scbval));2704if (err)2705bphy_err(drvr, "error (%d)\n", err);27062707brcmf_dbg(TRACE, "Exit\n");2708return err;2709}27102711static s322712brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,2713int radio_idx, enum nl80211_tx_power_setting type,2714s32 mbm)2715{2716struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);2717struct net_device *ndev = cfg_to_ndev(cfg);2718struct brcmf_if *ifp = netdev_priv(ndev);2719struct brcmf_pub *drvr = cfg->pub;2720s32 err;2721s32 disable;2722u32 qdbm = 127;27232724brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);2725if (!check_vif_up(ifp->vif))2726return -EIO;27272728switch (type) {2729case NL80211_TX_POWER_AUTOMATIC:2730break;2731case NL80211_TX_POWER_LIMITED:2732case NL80211_TX_POWER_FIXED:2733if (mbm < 0) {2734bphy_err(drvr, "TX_POWER_FIXED - dbm is negative\n");2735err = -EINVAL;2736goto done;2737}2738qdbm = MBM_TO_DBM(4 * mbm);2739if (qdbm > 127)2740qdbm = 127;2741qdbm |= WL_TXPWR_OVERRIDE;2742break;2743default:2744bphy_err(drvr, "Unsupported type %d\n", type);2745err = -EINVAL;2746goto done;2747}2748/* Make sure radio is off or on as far as software is concerned */2749disable = WL_RADIO_SW_DISABLE << 16;2750err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);2751if (err)2752bphy_err(drvr, "WLC_SET_RADIO error (%d)\n", err);27532754err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);2755if (err)2756bphy_err(drvr, "qtxpower error (%d)\n", err);27572758done:2759brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);2760return err;2761}27622763static s322764brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,2765int radio_idx, unsigned int link_id, s32 *dbm)2766{2767struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);2768struct brcmf_cfg80211_vif *vif = wdev_to_vif(wdev);2769struct brcmf_pub *drvr = cfg->pub;2770s32 qdbm;2771s32 err;27722773brcmf_dbg(TRACE, "Enter\n");2774if (!check_vif_up(vif))2775return -EIO;27762777err = brcmf_fil_iovar_int_get(vif->ifp, "qtxpower", &qdbm);2778if (err) {2779bphy_err(drvr, "error (%d)\n", err);2780goto done;2781}2782*dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;27832784done:2785brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);2786return err;2787}27882789static s322790brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,2791int link_id, u8 key_idx, bool unicast,2792bool multicast)2793{2794struct brcmf_if *ifp = netdev_priv(ndev);2795struct brcmf_pub *drvr = ifp->drvr;2796u32 index;2797u32 wsec;2798s32 err = 0;27992800brcmf_dbg(TRACE, "Enter\n");2801brcmf_dbg(CONN, "key index (%d)\n", key_idx);2802if (!check_vif_up(ifp->vif))2803return -EIO;28042805err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);2806if (err) {2807bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);2808goto done;2809}28102811if (wsec & WEP_ENABLED) {2812/* Just select a new current key */2813index = key_idx;2814err = brcmf_fil_cmd_int_set(ifp,2815BRCMF_C_SET_KEY_PRIMARY, index);2816if (err)2817bphy_err(drvr, "error (%d)\n", err);2818}2819done:2820brcmf_dbg(TRACE, "Exit\n");2821return err;2822}28232824static s322825brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,2826int link_id, u8 key_idx, bool pairwise,2827const u8 *mac_addr)2828{2829struct brcmf_if *ifp = netdev_priv(ndev);2830struct brcmf_wsec_key *key;2831s32 err;28322833brcmf_dbg(TRACE, "Enter\n");2834brcmf_dbg(CONN, "key index (%d)\n", key_idx);28352836if (!check_vif_up(ifp->vif))2837return -EIO;28382839if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {2840/* we ignore this key index in this case */2841return -EINVAL;2842}28432844key = &ifp->vif->profile.key[key_idx];28452846if (key->algo == CRYPTO_ALGO_OFF) {2847brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");2848return -EINVAL;2849}28502851memset(key, 0, sizeof(*key));2852key->index = (u32)key_idx;2853key->flags = BRCMF_PRIMARY_KEY;28542855/* Clear the key/index */2856err = send_key_to_dongle(ifp, key);28572858brcmf_dbg(TRACE, "Exit\n");2859return err;2860}28612862static s322863brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,2864int link_id, u8 key_idx, bool pairwise,2865const u8 *mac_addr, struct key_params *params)2866{2867struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);2868struct brcmf_if *ifp = netdev_priv(ndev);2869struct brcmf_pub *drvr = cfg->pub;2870struct brcmf_wsec_key *key;2871s32 val;2872s32 wsec;2873s32 err;2874u8 keybuf[8];2875bool ext_key;28762877brcmf_dbg(TRACE, "Enter\n");2878brcmf_dbg(CONN, "key index (%d)\n", key_idx);2879if (!check_vif_up(ifp->vif))2880return -EIO;28812882if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {2883/* we ignore this key index in this case */2884bphy_err(drvr, "invalid key index (%d)\n", key_idx);2885return -EINVAL;2886}28872888if (params->key_len == 0)2889return brcmf_cfg80211_del_key(wiphy, ndev, -1, key_idx,2890pairwise, mac_addr);28912892if (params->key_len > sizeof(key->data)) {2893bphy_err(drvr, "Too long key length (%u)\n", params->key_len);2894return -EINVAL;2895}28962897ext_key = false;2898if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&2899(params->cipher != WLAN_CIPHER_SUITE_WEP104)) {2900brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);2901ext_key = true;2902}29032904key = &ifp->vif->profile.key[key_idx];2905memset(key, 0, sizeof(*key));2906if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))2907#if defined(__linux__)2908memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);2909#elif defined(__FreeBSD__)2910memcpy((char *)&key->ea, mac_addr, ETH_ALEN);2911#endif2912key->len = params->key_len;2913key->index = key_idx;2914memcpy(key->data, params->key, key->len);2915if (!ext_key)2916key->flags = BRCMF_PRIMARY_KEY;29172918if (params->seq && params->seq_len == 6) {2919/* rx iv */2920#if defined(__linux__)2921u8 *ivptr;29222923ivptr = (u8 *)params->seq;2924#elif defined(__FreeBSD__)2925const u8 *ivptr;29262927ivptr = params->seq;2928#endif2929key->rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |2930(ivptr[3] << 8) | ivptr[2];2931key->rxiv.lo = (ivptr[1] << 8) | ivptr[0];2932key->iv_initialized = true;2933}29342935switch (params->cipher) {2936case WLAN_CIPHER_SUITE_WEP40:2937key->algo = CRYPTO_ALGO_WEP1;2938val = WEP_ENABLED;2939brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");2940break;2941case WLAN_CIPHER_SUITE_WEP104:2942key->algo = CRYPTO_ALGO_WEP128;2943val = WEP_ENABLED;2944brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");2945break;2946case WLAN_CIPHER_SUITE_TKIP:2947if (!brcmf_is_apmode(ifp->vif)) {2948brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");2949memcpy(keybuf, &key->data[24], sizeof(keybuf));2950memcpy(&key->data[24], &key->data[16], sizeof(keybuf));2951memcpy(&key->data[16], keybuf, sizeof(keybuf));2952}2953key->algo = CRYPTO_ALGO_TKIP;2954val = TKIP_ENABLED;2955brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");2956break;2957case WLAN_CIPHER_SUITE_AES_CMAC:2958key->algo = CRYPTO_ALGO_AES_CCM;2959val = AES_ENABLED;2960brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");2961break;2962case WLAN_CIPHER_SUITE_CCMP:2963key->algo = CRYPTO_ALGO_AES_CCM;2964val = AES_ENABLED;2965brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");2966break;2967default:2968bphy_err(drvr, "Invalid cipher (0x%x)\n", params->cipher);2969err = -EINVAL;2970goto done;2971}29722973err = send_key_to_dongle(ifp, key);2974if (ext_key || err)2975goto done;29762977err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);2978if (err) {2979bphy_err(drvr, "get wsec error (%d)\n", err);2980goto done;2981}2982wsec |= val;2983err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);2984if (err) {2985bphy_err(drvr, "set wsec error (%d)\n", err);2986goto done;2987}29882989done:2990brcmf_dbg(TRACE, "Exit\n");2991return err;2992}29932994static s322995brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,2996int link_id, u8 key_idx, bool pairwise,2997const u8 *mac_addr, void *cookie,2998void (*callback)(void *cookie,2999struct key_params *params))3000{3001struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);3002struct key_params params;3003struct brcmf_if *ifp = netdev_priv(ndev);3004struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;3005struct brcmf_pub *drvr = cfg->pub;3006struct brcmf_cfg80211_security *sec;3007s32 wsec;3008s32 err = 0;30093010brcmf_dbg(TRACE, "Enter\n");3011brcmf_dbg(CONN, "key index (%d)\n", key_idx);3012if (!check_vif_up(ifp->vif))3013return -EIO;30143015memset(¶ms, 0, sizeof(params));30163017err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);3018if (err) {3019bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);3020/* Ignore this error, may happen during DISASSOC */3021err = -EAGAIN;3022goto done;3023}3024if (wsec & WEP_ENABLED) {3025sec = &profile->sec;3026if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {3027params.cipher = WLAN_CIPHER_SUITE_WEP40;3028brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");3029} else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {3030params.cipher = WLAN_CIPHER_SUITE_WEP104;3031brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");3032}3033} else if (wsec & TKIP_ENABLED) {3034params.cipher = WLAN_CIPHER_SUITE_TKIP;3035brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");3036} else if (wsec & AES_ENABLED) {3037params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;3038brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");3039} else {3040bphy_err(drvr, "Invalid algo (0x%x)\n", wsec);3041err = -EINVAL;3042goto done;3043}3044callback(cookie, ¶ms);30453046done:3047brcmf_dbg(TRACE, "Exit\n");3048return err;3049}30503051static s323052brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,3053struct net_device *ndev, int link_id,3054u8 key_idx)3055{3056struct brcmf_if *ifp = netdev_priv(ndev);30573058brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);30593060if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))3061return 0;30623063brcmf_dbg(INFO, "Not supported\n");30643065return -EOPNOTSUPP;3066}30673068static void3069brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)3070{3071struct brcmf_pub *drvr = ifp->drvr;3072s32 err;3073u8 key_idx;3074struct brcmf_wsec_key *key;3075s32 wsec;30763077for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {3078key = &ifp->vif->profile.key[key_idx];3079if ((key->algo == CRYPTO_ALGO_WEP1) ||3080(key->algo == CRYPTO_ALGO_WEP128))3081break;3082}3083if (key_idx == BRCMF_MAX_DEFAULT_KEYS)3084return;30853086err = send_key_to_dongle(ifp, key);3087if (err) {3088bphy_err(drvr, "Setting WEP key failed (%d)\n", err);3089return;3090}3091err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);3092if (err) {3093bphy_err(drvr, "get wsec error (%d)\n", err);3094return;3095}3096wsec |= WEP_ENABLED;3097err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);3098if (err)3099bphy_err(drvr, "set wsec error (%d)\n", err);3100}31013102static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)3103{3104struct nl80211_sta_flag_update *sfu;31053106brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);3107si->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS);3108sfu = &si->sta_flags;3109sfu->mask = BIT(NL80211_STA_FLAG_WME) |3110BIT(NL80211_STA_FLAG_AUTHENTICATED) |3111BIT(NL80211_STA_FLAG_ASSOCIATED) |3112BIT(NL80211_STA_FLAG_AUTHORIZED);3113if (fw_sta_flags & BRCMF_STA_WME)3114sfu->set |= BIT(NL80211_STA_FLAG_WME);3115if (fw_sta_flags & BRCMF_STA_AUTHE)3116sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);3117if (fw_sta_flags & BRCMF_STA_ASSOC)3118sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);3119if (fw_sta_flags & BRCMF_STA_AUTHO)3120sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);3121}31223123static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)3124{3125struct brcmf_pub *drvr = ifp->drvr;3126struct {3127__le32 len;3128struct brcmf_bss_info_le bss_le;3129} *buf;3130u16 capability;3131int err;31323133buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);3134if (!buf)3135return;31363137buf->len = cpu_to_le32(WL_BSS_INFO_MAX);3138err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,3139WL_BSS_INFO_MAX);3140if (err) {3141bphy_err(drvr, "Failed to get bss info (%d)\n", err);3142goto out_kfree;3143}3144si->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM);3145si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);3146si->bss_param.dtim_period = buf->bss_le.dtim_period;3147capability = le16_to_cpu(buf->bss_le.capability);3148if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)3149si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;3150if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)3151si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;3152if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)3153si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;31543155out_kfree:3156kfree(buf);3157}31583159static s323160brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,3161struct station_info *sinfo)3162{3163struct brcmf_pub *drvr = ifp->drvr;3164struct brcmf_scb_val_le scbval;3165struct brcmf_pktcnt_le pktcnt;3166s32 err;3167u32 rate;3168u32 rssi;31693170/* Get the current tx rate */3171err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);3172if (err < 0) {3173bphy_err(drvr, "BRCMF_C_GET_RATE error (%d)\n", err);3174return err;3175}3176sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);3177sinfo->txrate.legacy = rate * 5;31783179memset(&scbval, 0, sizeof(scbval));3180err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,3181sizeof(scbval));3182if (err) {3183bphy_err(drvr, "BRCMF_C_GET_RSSI error (%d)\n", err);3184return err;3185}3186rssi = le32_to_cpu(scbval.val);3187sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);3188sinfo->signal = rssi;31893190err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,3191sizeof(pktcnt));3192if (err) {3193bphy_err(drvr, "BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);3194return err;3195}3196sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |3197BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |3198BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |3199BIT_ULL(NL80211_STA_INFO_TX_FAILED);3200sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);3201sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);3202sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);3203sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);32043205return 0;3206}32073208static s323209brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,3210const u8 *mac, struct station_info *sinfo)3211{3212struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);3213struct brcmf_if *ifp = netdev_priv(ndev);3214struct brcmf_pub *drvr = cfg->pub;3215struct brcmf_scb_val_le scb_val;3216s32 err = 0;3217struct brcmf_sta_info_le sta_info_le;3218u32 sta_flags;3219u32 is_tdls_peer;3220s32 total_rssi_avg = 0;3221s32 total_rssi = 0;3222s32 count_rssi = 0;3223int rssi;3224u32 i;32253226brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);3227if (!check_vif_up(ifp->vif))3228return -EIO;32293230if (brcmf_is_ibssmode(ifp->vif))3231return brcmf_cfg80211_get_station_ibss(ifp, sinfo);32323233memset(&sta_info_le, 0, sizeof(sta_info_le));3234memcpy(&sta_info_le, mac, ETH_ALEN);3235err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",3236&sta_info_le,3237sizeof(sta_info_le));3238is_tdls_peer = !err;3239if (err) {3240err = brcmf_fil_iovar_data_get(ifp, "sta_info",3241&sta_info_le,3242sizeof(sta_info_le));3243if (err < 0) {3244bphy_err(drvr, "GET STA INFO failed, %d\n", err);3245goto done;3246}3247}3248brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));3249sinfo->filled = BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);3250sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;3251sta_flags = le32_to_cpu(sta_info_le.flags);3252brcmf_convert_sta_flags(sta_flags, sinfo);3253sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);3254if (is_tdls_peer)3255sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);3256else3257sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);3258if (sta_flags & BRCMF_STA_ASSOC) {3259sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);3260sinfo->connected_time = le32_to_cpu(sta_info_le.in);3261brcmf_fill_bss_param(ifp, sinfo);3262}3263if (sta_flags & BRCMF_STA_SCBSTATS) {3264sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);3265sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);3266sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);3267sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);3268sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);3269sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);3270sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);3271sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);3272if (sinfo->tx_packets) {3273sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);3274sinfo->txrate.legacy =3275le32_to_cpu(sta_info_le.tx_rate) / 100;3276}3277if (sinfo->rx_packets) {3278sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);3279sinfo->rxrate.legacy =3280le32_to_cpu(sta_info_le.rx_rate) / 100;3281}3282if (le16_to_cpu(sta_info_le.ver) >= 4) {3283sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);3284sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);3285sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);3286sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);3287}3288for (i = 0; i < BRCMF_ANT_MAX; i++) {3289if (sta_info_le.rssi[i] == 0 ||3290sta_info_le.rx_lastpkt_rssi[i] == 0)3291continue;3292sinfo->chains |= BIT(count_rssi);3293sinfo->chain_signal[count_rssi] =3294sta_info_le.rx_lastpkt_rssi[i];3295sinfo->chain_signal_avg[count_rssi] =3296sta_info_le.rssi[i];3297total_rssi += sta_info_le.rx_lastpkt_rssi[i];3298total_rssi_avg += sta_info_le.rssi[i];3299count_rssi++;3300}3301if (count_rssi) {3302sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);3303sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);3304sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);3305sinfo->filled |=3306BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);3307sinfo->signal = total_rssi / count_rssi;3308sinfo->signal_avg = total_rssi_avg / count_rssi;3309} else if (test_bit(BRCMF_VIF_STATUS_CONNECTED,3310&ifp->vif->sme_state)) {3311memset(&scb_val, 0, sizeof(scb_val));3312err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,3313&scb_val, sizeof(scb_val));3314if (err) {3315bphy_err(drvr, "Could not get rssi (%d)\n",3316err);3317goto done;3318} else {3319rssi = le32_to_cpu(scb_val.val);3320sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);3321sinfo->signal = rssi;3322brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);3323}3324}3325}3326done:3327brcmf_dbg(TRACE, "Exit\n");3328return err;3329}33303331static int3332brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,3333int idx, u8 *mac, struct station_info *sinfo)3334{3335struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);3336struct brcmf_if *ifp = netdev_priv(ndev);3337struct brcmf_pub *drvr = cfg->pub;3338s32 err;33393340brcmf_dbg(TRACE, "Enter, idx %d\n", idx);33413342if (idx == 0) {3343cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);3344err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,3345&cfg->assoclist,3346sizeof(cfg->assoclist));3347if (err) {3348/* GET_ASSOCLIST unsupported by firmware of older chips */3349if (err == -EBADE)3350bphy_info_once(drvr, "BRCMF_C_GET_ASSOCLIST unsupported\n");3351else3352bphy_err(drvr, "BRCMF_C_GET_ASSOCLIST failed, err=%d\n",3353err);33543355cfg->assoclist.count = 0;3356return -EOPNOTSUPP;3357}3358}3359if (idx < le32_to_cpu(cfg->assoclist.count)) {3360memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);3361return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);3362}3363return -ENOENT;3364}33653366static s323367brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,3368bool enabled, s32 timeout)3369{3370s32 pm;3371s32 err = 0;3372struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);3373struct brcmf_if *ifp = netdev_priv(ndev);3374struct brcmf_pub *drvr = cfg->pub;33753376brcmf_dbg(TRACE, "Enter\n");33773378/*3379* Powersave enable/disable request is coming from the3380* cfg80211 even before the interface is up. In that3381* scenario, driver will be storing the power save3382* preference in cfg struct to apply this to3383* FW later while initializing the dongle3384*/3385cfg->pwr_save = enabled;3386if (!check_vif_up(ifp->vif)) {33873388brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");3389goto done;3390}33913392pm = enabled ? PM_FAST : PM_OFF;3393/* Do not enable the power save after assoc if it is a p2p interface */3394if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {3395brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");3396pm = PM_OFF;3397}3398brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));33993400err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);3401if (err) {3402if (err == -ENODEV)3403bphy_err(drvr, "net_device is not ready yet\n");3404else3405bphy_err(drvr, "error (%d)\n", err);3406}34073408err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret",3409min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS));3410if (err)3411bphy_err(drvr, "Unable to set pm timeout, (%d)\n", err);34123413done:3414brcmf_dbg(TRACE, "Exit\n");3415return err;3416}34173418static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,3419struct brcmf_bss_info_le *bi)3420{3421struct wiphy *wiphy = cfg_to_wiphy(cfg);3422struct brcmf_pub *drvr = cfg->pub;3423struct cfg80211_bss *bss;3424enum nl80211_band band;3425struct brcmu_chan ch;3426u16 channel;3427u32 freq;3428u16 notify_capability;3429u16 notify_interval;3430u8 *notify_ie;3431size_t notify_ielen;3432struct cfg80211_inform_bss bss_data = {};34333434if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {3435bphy_err(drvr, "Bss info is larger than buffer. Discarding\n");3436return -EINVAL;3437}34383439if (!bi->ctl_ch) {3440ch.chspec = le16_to_cpu(bi->chanspec);3441cfg->d11inf.decchspec(&ch);3442bi->ctl_ch = ch.control_ch_num;3443}3444channel = bi->ctl_ch;34453446if (channel <= CH_MAX_2G_CHANNEL)3447band = NL80211_BAND_2GHZ;3448else3449band = NL80211_BAND_5GHZ;34503451freq = ieee80211_channel_to_frequency(channel, band);3452bss_data.chan = ieee80211_get_channel(wiphy, freq);3453bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());34543455notify_capability = le16_to_cpu(bi->capability);3456notify_interval = le16_to_cpu(bi->beacon_period);3457notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);3458notify_ielen = le32_to_cpu(bi->ie_length);3459bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100;34603461brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);3462brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);3463brcmf_dbg(CONN, "Capability: %X\n", notify_capability);3464brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);3465brcmf_dbg(CONN, "Signal: %d\n", bss_data.signal);34663467bss = cfg80211_inform_bss_data(wiphy, &bss_data,3468CFG80211_BSS_FTYPE_UNKNOWN,3469(const u8 *)bi->BSSID,34700, notify_capability,3471notify_interval, notify_ie,3472notify_ielen, GFP_KERNEL);34733474if (!bss)3475return -ENOMEM;34763477cfg80211_put_bss(wiphy, bss);34783479return 0;3480}34813482static struct brcmf_bss_info_le *3483next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)3484{3485if (bss == NULL)3486return list->bss_info_le;3487return (struct brcmf_bss_info_le *)((unsigned long)bss +3488le32_to_cpu(bss->length));3489}34903491static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)3492{3493struct brcmf_pub *drvr = cfg->pub;3494struct brcmf_scan_results *bss_list;3495struct brcmf_bss_info_le *bi = NULL; /* must be initialized */3496s32 err = 0;3497int i;34983499bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;3500if (bss_list->count != 0 &&3501bss_list->version != BRCMF_BSS_INFO_VERSION) {3502bphy_err(drvr, "Version %d != WL_BSS_INFO_VERSION\n",3503bss_list->version);3504return -EOPNOTSUPP;3505}3506brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);3507for (i = 0; i < bss_list->count; i++) {3508bi = next_bss_le(bss_list, bi);3509err = brcmf_inform_single_bss(cfg, bi);3510if (err)3511break;3512}3513return err;3514}35153516static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,3517struct net_device *ndev, const u8 *bssid)3518{3519struct wiphy *wiphy = cfg_to_wiphy(cfg);3520struct brcmf_pub *drvr = cfg->pub;3521struct ieee80211_channel *notify_channel;3522struct brcmf_bss_info_le *bi = NULL;3523struct ieee80211_supported_band *band;3524struct cfg80211_bss *bss;3525struct brcmu_chan ch;3526u8 *buf = NULL;3527s32 err = 0;3528u32 freq;3529u16 notify_capability;3530u16 notify_interval;3531u8 *notify_ie;3532size_t notify_ielen;3533s32 notify_signal;35343535brcmf_dbg(TRACE, "Enter\n");35363537buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);3538if (buf == NULL) {3539err = -ENOMEM;3540goto CleanUp;3541}35423543*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);35443545err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,3546buf, WL_BSS_INFO_MAX);3547if (err) {3548bphy_err(drvr, "WLC_GET_BSS_INFO failed: %d\n", err);3549goto CleanUp;3550}35513552bi = (struct brcmf_bss_info_le *)(buf + 4);35533554ch.chspec = le16_to_cpu(bi->chanspec);3555cfg->d11inf.decchspec(&ch);35563557if (ch.band == BRCMU_CHAN_BAND_2G)3558band = wiphy->bands[NL80211_BAND_2GHZ];3559else3560band = wiphy->bands[NL80211_BAND_5GHZ];35613562freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);3563cfg->channel = freq;3564notify_channel = ieee80211_get_channel(wiphy, freq);35653566notify_capability = le16_to_cpu(bi->capability);3567notify_interval = le16_to_cpu(bi->beacon_period);3568notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);3569notify_ielen = le32_to_cpu(bi->ie_length);3570notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;35713572brcmf_dbg(CONN, "channel: %d(%d)\n", ch.control_ch_num, freq);3573brcmf_dbg(CONN, "capability: %X\n", notify_capability);3574brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);3575brcmf_dbg(CONN, "signal: %d\n", notify_signal);35763577bss = cfg80211_inform_bss(wiphy, notify_channel,3578CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,3579notify_capability, notify_interval,3580notify_ie, notify_ielen, notify_signal,3581GFP_KERNEL);35823583if (!bss) {3584err = -ENOMEM;3585goto CleanUp;3586}35873588cfg80211_put_bss(wiphy, bss);35893590CleanUp:35913592kfree(buf);35933594brcmf_dbg(TRACE, "Exit\n");35953596return err;3597}35983599static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,3600struct brcmf_if *ifp)3601{3602struct brcmf_pub *drvr = cfg->pub;3603struct brcmf_bss_info_le *bi = NULL;3604s32 err = 0;36053606brcmf_dbg(TRACE, "Enter\n");3607if (brcmf_is_ibssmode(ifp->vif))3608return err;36093610*(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);3611err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,3612cfg->extra_buf, WL_EXTRA_BUF_MAX);3613if (err) {3614bphy_err(drvr, "Could not get bss info %d\n", err);3615goto update_bss_info_out;3616}3617bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);3618err = brcmf_inform_single_bss(cfg, bi);36193620update_bss_info_out:3621brcmf_dbg(TRACE, "Exit");3622return err;3623}36243625void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)3626{3627struct escan_info *escan = &cfg->escan_info;36283629set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);3630if (cfg->int_escan_map || cfg->scan_request) {3631escan->escan_state = WL_ESCAN_STATE_IDLE;3632brcmf_notify_escan_complete(cfg, escan->ifp, true, true);3633}3634clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);3635clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);3636}36373638static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)3639{3640struct brcmf_cfg80211_info *cfg =3641container_of(work, struct brcmf_cfg80211_info,3642escan_timeout_work);36433644brcmf_inform_bss(cfg);3645brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);3646}36473648static void brcmf_escan_timeout(struct timer_list *t)3649{3650struct brcmf_cfg80211_info *cfg =3651timer_container_of(cfg, t, escan_timeout);3652struct brcmf_pub *drvr = cfg->pub;36533654if (cfg->int_escan_map || cfg->scan_request) {3655bphy_err(drvr, "timer expired\n");3656schedule_work(&cfg->escan_timeout_work);3657}3658}36593660static s323661brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,3662struct brcmf_bss_info_le *bss,3663struct brcmf_bss_info_le *bss_info_le)3664{3665struct brcmu_chan ch_bss, ch_bss_info_le;36663667ch_bss.chspec = le16_to_cpu(bss->chanspec);3668cfg->d11inf.decchspec(&ch_bss);3669ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);3670cfg->d11inf.decchspec(&ch_bss_info_le);36713672if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&3673ch_bss.band == ch_bss_info_le.band &&3674bss_info_le->SSID_len == bss->SSID_len &&3675!memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {3676if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==3677(bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {3678s16 bss_rssi = le16_to_cpu(bss->RSSI);3679s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);36803681/* preserve max RSSI if the measurements are3682* both on-channel or both off-channel3683*/3684if (bss_info_rssi > bss_rssi)3685bss->RSSI = bss_info_le->RSSI;3686} else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&3687(bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {3688/* preserve the on-channel rssi measurement3689* if the new measurement is off channel3690*/3691bss->RSSI = bss_info_le->RSSI;3692bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;3693}3694return 1;3695}3696return 0;3697}36983699static s323700brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,3701const struct brcmf_event_msg *e, void *data)3702{3703struct brcmf_pub *drvr = ifp->drvr;3704struct brcmf_cfg80211_info *cfg = drvr->config;3705s32 status;3706struct brcmf_escan_result_le *escan_result_le;3707u32 escan_buflen;3708struct brcmf_bss_info_le *bss_info_le;3709struct brcmf_bss_info_le *bss = NULL;3710u32 bi_length;3711struct brcmf_scan_results *list;3712u32 i;3713bool aborted;37143715status = e->status;37163717if (status == BRCMF_E_STATUS_ABORT)3718goto exit;37193720if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {3721bphy_err(drvr, "scan not ready, bsscfgidx=%d\n",3722ifp->bsscfgidx);3723return -EPERM;3724}37253726if (status == BRCMF_E_STATUS_PARTIAL) {3727brcmf_dbg(SCAN, "ESCAN Partial result\n");3728if (e->datalen < sizeof(*escan_result_le)) {3729bphy_err(drvr, "invalid event data length\n");3730goto exit;3731}3732escan_result_le = (struct brcmf_escan_result_le *) data;3733if (!escan_result_le) {3734bphy_err(drvr, "Invalid escan result (NULL pointer)\n");3735goto exit;3736}3737escan_buflen = le32_to_cpu(escan_result_le->buflen);3738if (escan_buflen > BRCMF_ESCAN_BUF_SIZE ||3739escan_buflen > e->datalen ||3740escan_buflen < sizeof(*escan_result_le)) {3741bphy_err(drvr, "Invalid escan buffer length: %d\n",3742escan_buflen);3743goto exit;3744}3745if (le16_to_cpu(escan_result_le->bss_count) != 1) {3746bphy_err(drvr, "Invalid bss_count %d: ignoring\n",3747escan_result_le->bss_count);3748goto exit;3749}3750bss_info_le = &escan_result_le->bss_info_le;37513752if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))3753goto exit;37543755if (!cfg->int_escan_map && !cfg->scan_request) {3756brcmf_dbg(SCAN, "result without cfg80211 request\n");3757goto exit;3758}37593760bi_length = le32_to_cpu(bss_info_le->length);3761if (bi_length != escan_buflen - WL_ESCAN_RESULTS_FIXED_SIZE) {3762bphy_err(drvr, "Ignoring invalid bss_info length: %d\n",3763bi_length);3764goto exit;3765}37663767if (!(cfg_to_wiphy(cfg)->interface_modes &3768BIT(NL80211_IFTYPE_ADHOC))) {3769if (le16_to_cpu(bss_info_le->capability) &3770WLAN_CAPABILITY_IBSS) {3771bphy_err(drvr, "Ignoring IBSS result\n");3772goto exit;3773}3774}37753776list = (struct brcmf_scan_results *)3777cfg->escan_info.escan_buf;3778if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {3779bphy_err(drvr, "Buffer is too small: ignoring\n");3780goto exit;3781}37823783for (i = 0; i < list->count; i++) {3784bss = bss ? (struct brcmf_bss_info_le *)3785((unsigned char *)bss +3786le32_to_cpu(bss->length)) : list->bss_info_le;3787if (brcmf_compare_update_same_bss(cfg, bss,3788bss_info_le))3789goto exit;3790}3791memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,3792bi_length);3793list->version = le32_to_cpu(bss_info_le->version);3794list->buflen += bi_length;3795list->count++;3796} else {3797cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;3798if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))3799goto exit;3800if (cfg->int_escan_map || cfg->scan_request) {3801brcmf_inform_bss(cfg);3802aborted = status != BRCMF_E_STATUS_SUCCESS;3803brcmf_notify_escan_complete(cfg, ifp, aborted, false);3804} else3805brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",3806status);3807}3808exit:3809return 0;3810}38113812static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)3813{3814brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,3815brcmf_cfg80211_escan_handler);3816cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;3817/* Init scan_timeout timer */3818timer_setup(&cfg->escan_timeout, brcmf_escan_timeout, 0);3819INIT_WORK(&cfg->escan_timeout_work,3820brcmf_cfg80211_escan_timeout_worker);3821}38223823static struct cfg80211_scan_request *3824brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) {3825struct cfg80211_scan_request *req;3826size_t req_size;38273828req_size = sizeof(*req) +3829n_netinfo * sizeof(req->channels[0]) +3830n_netinfo * sizeof(*req->ssids);38313832req = kzalloc(req_size, GFP_KERNEL);3833if (req) {3834req->wiphy = wiphy;3835#if defined(__linux__)3836req->ssids = (void *)(&req->channels[0]) +3837n_netinfo * sizeof(req->channels[0]);3838#elif defined(__FreeBSD__)3839req->ssids = (void *)((&req->channels[0]) +3840n_netinfo * sizeof(req->channels[0]));3841#endif3842}3843return req;3844}38453846static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,3847u8 *ssid, u8 ssid_len, u8 channel)3848{3849struct ieee80211_channel *chan;3850enum nl80211_band band;3851int freq, i;38523853if (channel <= CH_MAX_2G_CHANNEL)3854band = NL80211_BAND_2GHZ;3855else3856band = NL80211_BAND_5GHZ;38573858freq = ieee80211_channel_to_frequency(channel, band);3859if (!freq)3860return -EINVAL;38613862chan = ieee80211_get_channel(req->wiphy, freq);3863if (!chan)3864return -EINVAL;38653866for (i = 0; i < req->n_channels; i++) {3867if (req->channels[i] == chan)3868break;3869}3870if (i == req->n_channels) {3871req->n_channels++;3872req->channels[i] = chan;3873}38743875for (i = 0; i < req->n_ssids; i++) {3876if (req->ssids[i].ssid_len == ssid_len &&3877!memcmp(req->ssids[i].ssid, ssid, ssid_len))3878break;3879}3880if (i == req->n_ssids) {3881memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);3882req->ssids[req->n_ssids++].ssid_len = ssid_len;3883}3884return 0;3885}38863887static int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap,3888struct cfg80211_scan_request *request)3889{3890struct brcmf_cfg80211_info *cfg = ifp->drvr->config;3891int err;38923893if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {3894if (cfg->int_escan_map)3895brcmf_dbg(SCAN, "aborting internal scan: map=%u\n",3896cfg->int_escan_map);3897/* Abort any on-going scan */3898brcmf_abort_scanning(cfg);3899}39003901brcmf_dbg(SCAN, "start internal scan: map=%u\n", fwmap);3902set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);3903cfg->escan_info.run = brcmf_run_escan;3904err = brcmf_do_escan(ifp, request);3905if (err) {3906clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);3907return err;3908}3909cfg->int_escan_map = fwmap;3910return 0;3911}39123913static struct brcmf_pno_net_info_le *3914brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1)3915{3916struct brcmf_pno_scanresults_v2_le *pfn_v2;3917struct brcmf_pno_net_info_le *netinfo;39183919switch (pfn_v1->version) {3920default:3921WARN_ON(1);3922fallthrough;3923case cpu_to_le32(1):3924netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1);3925break;3926case cpu_to_le32(2):3927pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1;3928netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1);3929break;3930}39313932return netinfo;3933}39343935/* PFN result doesn't have all the info which are required by the supplicant3936* (For e.g IEs) Do a target Escan so that sched scan results are reported3937* via wl_inform_single_bss in the required format. Escan does require the3938* scan request in the form of cfg80211_scan_request. For timebeing, create3939* cfg80211_scan_request one out of the received PNO event.3940*/3941static s323942brcmf_notify_sched_scan_results(struct brcmf_if *ifp,3943const struct brcmf_event_msg *e, void *data)3944{3945struct brcmf_pub *drvr = ifp->drvr;3946struct brcmf_cfg80211_info *cfg = drvr->config;3947struct brcmf_pno_net_info_le *netinfo, *netinfo_start;3948struct cfg80211_scan_request *request = NULL;3949struct wiphy *wiphy = cfg_to_wiphy(cfg);3950int i, err = 0;3951struct brcmf_pno_scanresults_le *pfn_result;3952u32 bucket_map;3953u32 result_count;3954u32 status;3955u32 datalen;39563957brcmf_dbg(SCAN, "Enter\n");39583959if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {3960brcmf_dbg(SCAN, "Event data too small. Ignore\n");3961return 0;3962}39633964if (e->event_code == BRCMF_E_PFN_NET_LOST) {3965brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");3966return 0;3967}39683969pfn_result = (struct brcmf_pno_scanresults_le *)data;3970result_count = le32_to_cpu(pfn_result->count);3971status = le32_to_cpu(pfn_result->status);39723973/* PFN event is limited to fit 512 bytes so we may get3974* multiple NET_FOUND events. For now place a warning here.3975*/3976WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);3977brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);3978if (!result_count) {3979bphy_err(drvr, "FALSE PNO Event. (pfn_count == 0)\n");3980goto out_err;3981}39823983netinfo_start = brcmf_get_netinfo_array(pfn_result);3984#if defined(__linux__)3985datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);3986#elif defined(__FreeBSD__)3987datalen = e->datalen - ((u8 *)netinfo_start - (u8 *)pfn_result);3988#endif3989if (datalen < result_count * sizeof(*netinfo)) {3990bphy_err(drvr, "insufficient event data\n");3991goto out_err;3992}39933994request = brcmf_alloc_internal_escan_request(wiphy,3995result_count);3996if (!request) {3997err = -ENOMEM;3998goto out_err;3999}40004001bucket_map = 0;4002for (i = 0; i < result_count; i++) {4003netinfo = &netinfo_start[i];40044005if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)4006netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;4007brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",4008netinfo->SSID, netinfo->channel);4009bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo);4010err = brcmf_internal_escan_add_info(request,4011netinfo->SSID,4012netinfo->SSID_len,4013netinfo->channel);4014if (err)4015goto out_err;4016}40174018if (!bucket_map)4019goto free_req;40204021err = brcmf_start_internal_escan(ifp, bucket_map, request);4022if (!err)4023goto free_req;40244025out_err:4026cfg80211_sched_scan_stopped(wiphy, 0);4027free_req:4028kfree(request);4029return err;4030}40314032static int4033brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,4034struct net_device *ndev,4035struct cfg80211_sched_scan_request *req)4036{4037struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);4038struct brcmf_if *ifp = netdev_priv(ndev);4039struct brcmf_pub *drvr = cfg->pub;40404041brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n",4042req->n_match_sets, req->n_ssids);40434044if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {4045bphy_err(drvr, "Scanning suppressed: status=%lu\n",4046cfg->scan_status);4047return -EAGAIN;4048}40494050if (req->n_match_sets <= 0) {4051brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n",4052req->n_match_sets);4053return -EINVAL;4054}40554056return brcmf_pno_start_sched_scan(ifp, req);4057}40584059static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,4060struct net_device *ndev, u64 reqid)4061{4062struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);4063struct brcmf_if *ifp = netdev_priv(ndev);40644065brcmf_dbg(SCAN, "enter\n");4066brcmf_pno_stop_sched_scan(ifp, reqid);4067if (cfg->int_escan_map)4068brcmf_notify_escan_complete(cfg, ifp, true, true);4069return 0;4070}40714072static __always_inline void brcmf_delay(u32 ms)4073{4074if (ms < 1000 / HZ) {4075cond_resched();4076mdelay(ms);4077} else {4078#if defined(__linux__)4079msleep(ms);4080#elif defined(__FreeBSD__)4081linux_msleep(ms);4082#endif4083}4084}40854086static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],4087u8 *pattern, u32 patternsize, u8 *mask,4088u32 packet_offset)4089{4090struct brcmf_fil_wowl_pattern_le *filter;4091u32 masksize;4092u32 patternoffset;4093u8 *buf;4094u32 bufsize;4095s32 ret;40964097masksize = (patternsize + 7) / 8;4098patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;40994100bufsize = sizeof(*filter) + patternsize + masksize;4101buf = kzalloc(bufsize, GFP_KERNEL);4102if (!buf)4103return -ENOMEM;4104filter = (struct brcmf_fil_wowl_pattern_le *)buf;41054106memcpy(filter->cmd, cmd, 4);4107filter->masksize = cpu_to_le32(masksize);4108filter->offset = cpu_to_le32(packet_offset);4109filter->patternoffset = cpu_to_le32(patternoffset);4110filter->patternsize = cpu_to_le32(patternsize);4111filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);41124113if ((mask) && (masksize))4114memcpy(buf + sizeof(*filter), mask, masksize);4115if ((pattern) && (patternsize))4116memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);41174118ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);41194120kfree(buf);4121return ret;4122}41234124static s324125brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,4126void *data)4127{4128struct brcmf_pub *drvr = ifp->drvr;4129struct brcmf_cfg80211_info *cfg = drvr->config;4130struct brcmf_pno_scanresults_le *pfn_result;4131struct brcmf_pno_net_info_le *netinfo;41324133brcmf_dbg(SCAN, "Enter\n");41344135if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {4136brcmf_dbg(SCAN, "Event data too small. Ignore\n");4137return 0;4138}41394140pfn_result = (struct brcmf_pno_scanresults_le *)data;41414142if (e->event_code == BRCMF_E_PFN_NET_LOST) {4143brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");4144return 0;4145}41464147if (le32_to_cpu(pfn_result->count) < 1) {4148bphy_err(drvr, "Invalid result count, expected 1 (%d)\n",4149le32_to_cpu(pfn_result->count));4150return -EINVAL;4151}41524153netinfo = brcmf_get_netinfo_array(pfn_result);4154if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)4155netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;4156memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);4157cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;4158cfg->wowl.nd->n_channels = 1;4159cfg->wowl.nd->channels[0] =4160ieee80211_channel_to_frequency(netinfo->channel,4161netinfo->channel <= CH_MAX_2G_CHANNEL ?4162NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);4163cfg->wowl.nd_info->n_matches = 1;4164cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;41654166/* Inform (the resume task) that the net detect information was recvd */4167cfg->wowl.nd_data_completed = true;4168wake_up(&cfg->wowl.nd_data_wait);41694170return 0;4171}41724173#ifdef CONFIG_PM41744175static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)4176{4177struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);4178struct brcmf_pub *drvr = cfg->pub;4179struct brcmf_wowl_wakeind_le wake_ind_le;4180struct cfg80211_wowlan_wakeup wakeup_data;4181struct cfg80211_wowlan_wakeup *wakeup;4182u32 wakeind;4183s32 err;4184long time_left;41854186err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,4187sizeof(wake_ind_le));4188if (err) {4189bphy_err(drvr, "Get wowl_wakeind failed, err = %d\n", err);4190return;4191}41924193wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);4194if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |4195BRCMF_WOWL_RETR | BRCMF_WOWL_NET |4196BRCMF_WOWL_PFN_FOUND)) {4197wakeup = &wakeup_data;4198memset(&wakeup_data, 0, sizeof(wakeup_data));4199wakeup_data.pattern_idx = -1;42004201if (wakeind & BRCMF_WOWL_MAGIC) {4202brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");4203wakeup_data.magic_pkt = true;4204}4205if (wakeind & BRCMF_WOWL_DIS) {4206brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");4207wakeup_data.disconnect = true;4208}4209if (wakeind & BRCMF_WOWL_BCN) {4210brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");4211wakeup_data.disconnect = true;4212}4213if (wakeind & BRCMF_WOWL_RETR) {4214brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");4215wakeup_data.disconnect = true;4216}4217if (wakeind & BRCMF_WOWL_NET) {4218brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");4219/* For now always map to pattern 0, no API to get4220* correct information available at the moment.4221*/4222wakeup_data.pattern_idx = 0;4223}4224if (wakeind & BRCMF_WOWL_PFN_FOUND) {4225brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");4226time_left = wait_event_timeout(cfg->wowl.nd_data_wait,4227cfg->wowl.nd_data_completed,4228BRCMF_ND_INFO_TIMEOUT);4229if (!time_left)4230bphy_err(drvr, "No result for wowl net detect\n");4231else4232wakeup_data.net_detect = cfg->wowl.nd_info;4233}4234if (wakeind & BRCMF_WOWL_GTK_FAILURE) {4235brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");4236wakeup_data.gtk_rekey_failure = true;4237}4238} else {4239wakeup = NULL;4240}4241cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);4242}42434244#else42454246static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)4247{4248}42494250#endif /* CONFIG_PM */42514252static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)4253{4254struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);4255struct net_device *ndev = cfg_to_ndev(cfg);4256struct brcmf_if *ifp = netdev_priv(ndev);42574258brcmf_dbg(TRACE, "Enter\n");42594260if (cfg->wowl.active) {4261brcmf_report_wowl_wakeind(wiphy, ifp);4262brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);4263brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);4264if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))4265brcmf_configure_arp_nd_offload(ifp, true);4266brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,4267cfg->wowl.pre_pmmode);4268cfg->wowl.active = false;4269if (cfg->wowl.nd_enabled) {4270brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0);4271brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);4272brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,4273brcmf_notify_sched_scan_results);4274cfg->wowl.nd_enabled = false;4275}4276}4277return 0;4278}42794280static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,4281struct brcmf_if *ifp,4282struct cfg80211_wowlan *wowl)4283{4284u32 wowl_config;4285struct brcmf_wowl_wakeind_le wowl_wakeind;4286u32 i;42874288brcmf_dbg(TRACE, "Suspend, wowl config.\n");42894290if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))4291brcmf_configure_arp_nd_offload(ifp, false);4292brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);4293brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);42944295wowl_config = 0;4296if (wowl->disconnect)4297wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;4298if (wowl->magic_pkt)4299wowl_config |= BRCMF_WOWL_MAGIC;4300if ((wowl->patterns) && (wowl->n_patterns)) {4301wowl_config |= BRCMF_WOWL_NET;4302for (i = 0; i < wowl->n_patterns; i++) {4303brcmf_config_wowl_pattern(ifp, "add",4304(u8 *)wowl->patterns[i].pattern,4305wowl->patterns[i].pattern_len,4306(u8 *)wowl->patterns[i].mask,4307wowl->patterns[i].pkt_offset);4308}4309}4310if (wowl->nd_config) {4311brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,4312wowl->nd_config);4313wowl_config |= BRCMF_WOWL_PFN_FOUND;43144315cfg->wowl.nd_data_completed = false;4316cfg->wowl.nd_enabled = true;4317/* Now reroute the event for PFN to the wowl function. */4318brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);4319brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,4320brcmf_wowl_nd_results);4321}4322if (wowl->gtk_rekey_failure)4323wowl_config |= BRCMF_WOWL_GTK_FAILURE;4324if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))4325wowl_config |= BRCMF_WOWL_UNASSOC;43264327memcpy(&wowl_wakeind, "clear", 6);4328brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", &wowl_wakeind,4329sizeof(wowl_wakeind));4330brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);4331brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);4332brcmf_bus_wowl_config(cfg->pub->bus_if, true);4333cfg->wowl.active = true;4334}43354336static int brcmf_keepalive_start(struct brcmf_if *ifp, unsigned int interval)4337{4338struct brcmf_mkeep_alive_pkt_le kalive = {0};4339int ret = 0;43404341/* Configure Null function/data keepalive */4342kalive.version = cpu_to_le16(1);4343kalive.period_msec = cpu_to_le32(interval * MSEC_PER_SEC);4344kalive.len_bytes = cpu_to_le16(0);4345kalive.keep_alive_id = 0;43464347ret = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", &kalive, sizeof(kalive));4348if (ret)4349brcmf_err("keep-alive packet config failed, ret=%d\n", ret);43504351return ret;4352}43534354static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,4355struct cfg80211_wowlan *wowl)4356{4357struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);4358struct net_device *ndev = cfg_to_ndev(cfg);4359struct brcmf_if *ifp = netdev_priv(ndev);4360struct brcmf_cfg80211_vif *vif;43614362brcmf_dbg(TRACE, "Enter\n");43634364/* if the primary net_device is not READY there is nothing4365* we can do but pray resume goes smoothly.4366*/4367if (!check_vif_up(ifp->vif))4368goto exit;43694370/* Stop scheduled scan */4371if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))4372brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0);43734374/* end any scanning */4375if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))4376brcmf_abort_scanning(cfg);43774378if (wowl == NULL) {4379brcmf_bus_wowl_config(cfg->pub->bus_if, false);4380list_for_each_entry(vif, &cfg->vif_list, list) {4381if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))4382continue;4383/* While going to suspend if associated with AP4384* disassociate from AP to save power while system is4385* in suspended state4386*/4387brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED, true);4388/* Make sure WPA_Supplicant receives all the event4389* generated due to DISASSOC call to the fw to keep4390* the state fw and WPA_Supplicant state consistent4391*/4392brcmf_delay(500);4393}4394/* Configure MPC */4395brcmf_set_mpc(ifp, 1);43964397} else {4398/* Configure WOWL parameters */4399brcmf_configure_wowl(cfg, ifp, wowl);44004401/* Prevent disassociation due to inactivity with keep-alive */4402brcmf_keepalive_start(ifp, 30);4403}44044405exit:4406brcmf_dbg(TRACE, "Exit\n");4407/* clear any scanning activity */4408cfg->scan_status = 0;4409return 0;4410}44114412static s324413brcmf_pmksa_v3_op(struct brcmf_if *ifp, struct cfg80211_pmksa *pmksa,4414bool alive)4415{4416struct brcmf_pmk_op_v3_le *pmk_op;4417int length = offsetof(struct brcmf_pmk_op_v3_le, pmk);4418int ret;44194420pmk_op = kzalloc(sizeof(*pmk_op), GFP_KERNEL);4421if (!pmk_op)4422return -ENOMEM;44234424pmk_op->version = cpu_to_le16(BRCMF_PMKSA_VER_3);44254426if (!pmksa) {4427/* Flush operation, operate on entire list */4428pmk_op->count = cpu_to_le16(0);4429} else {4430/* Single PMK operation */4431pmk_op->count = cpu_to_le16(1);4432length += sizeof(struct brcmf_pmksa_v3);4433if (pmksa->bssid)4434memcpy(pmk_op->pmk[0].bssid, pmksa->bssid, ETH_ALEN);4435if (pmksa->pmkid) {4436memcpy(pmk_op->pmk[0].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);4437pmk_op->pmk[0].pmkid_len = WLAN_PMKID_LEN;4438}4439if (pmksa->ssid && pmksa->ssid_len) {4440memcpy(pmk_op->pmk[0].ssid.SSID, pmksa->ssid, pmksa->ssid_len);4441pmk_op->pmk[0].ssid.SSID_len = pmksa->ssid_len;4442}4443pmk_op->pmk[0].time_left = cpu_to_le32(alive ? BRCMF_PMKSA_NO_EXPIRY : 0);4444}44454446pmk_op->length = cpu_to_le16(length);44474448ret = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_op, sizeof(*pmk_op));4449kfree(pmk_op);4450return ret;4451}44524453static __used s324454brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)4455{4456struct brcmf_pmk_list_le *pmk_list;4457int i;4458u32 npmk;44594460pmk_list = &cfg->pmk_list;4461npmk = le32_to_cpu(pmk_list->npmk);44624463brcmf_dbg(CONN, "No of elements %d\n", npmk);4464for (i = 0; i < npmk; i++)4465brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);44664467return brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,4468sizeof(*pmk_list));4469}44704471static s324472brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,4473struct cfg80211_pmksa *pmksa)4474{4475struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);4476struct brcmf_if *ifp = netdev_priv(ndev);4477struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];4478struct brcmf_pub *drvr = cfg->pub;4479s32 err;4480u32 npmk, i;44814482brcmf_dbg(TRACE, "Enter\n");4483if (!check_vif_up(ifp->vif))4484return -EIO;44854486brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmksa->bssid);4487brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmksa->pmkid);44884489if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))4490return brcmf_pmksa_v3_op(ifp, pmksa, true);44914492/* TODO: implement PMKID_V2 */44934494npmk = le32_to_cpu(cfg->pmk_list.npmk);4495for (i = 0; i < npmk; i++)4496if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))4497break;4498if (i < BRCMF_MAXPMKID) {4499memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);4500memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);4501if (i == npmk) {4502npmk++;4503cfg->pmk_list.npmk = cpu_to_le32(npmk);4504}4505} else {4506bphy_err(drvr, "Too many PMKSA entries cached %d\n", npmk);4507return -EINVAL;4508}45094510err = brcmf_update_pmklist(cfg, ifp);45114512brcmf_dbg(TRACE, "Exit\n");4513return err;4514}45154516static s324517brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,4518struct cfg80211_pmksa *pmksa)4519{4520struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);4521struct brcmf_if *ifp = netdev_priv(ndev);4522struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];4523struct brcmf_pub *drvr = cfg->pub;4524s32 err;4525u32 npmk, i;45264527brcmf_dbg(TRACE, "Enter\n");4528if (!check_vif_up(ifp->vif))4529return -EIO;45304531brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid);45324533if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))4534return brcmf_pmksa_v3_op(ifp, pmksa, false);45354536/* TODO: implement PMKID_V2 */45374538npmk = le32_to_cpu(cfg->pmk_list.npmk);4539for (i = 0; i < npmk; i++)4540if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))4541break;45424543if ((npmk > 0) && (i < npmk)) {4544for (; i < (npmk - 1); i++) {4545memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);4546memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,4547WLAN_PMKID_LEN);4548}4549memset(&pmk[i], 0, sizeof(*pmk));4550cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);4551} else {4552bphy_err(drvr, "Cache entry not found\n");4553return -EINVAL;4554}45554556err = brcmf_update_pmklist(cfg, ifp);45574558brcmf_dbg(TRACE, "Exit\n");4559return err;45604561}45624563static s324564brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)4565{4566struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);4567struct brcmf_if *ifp = netdev_priv(ndev);4568s32 err;45694570brcmf_dbg(TRACE, "Enter\n");4571if (!check_vif_up(ifp->vif))4572return -EIO;45734574if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))4575return brcmf_pmksa_v3_op(ifp, NULL, false);45764577/* TODO: implement PMKID_V2 */45784579memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));4580err = brcmf_update_pmklist(cfg, ifp);45814582brcmf_dbg(TRACE, "Exit\n");4583return err;45844585}45864587static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)4588{4589struct brcmf_pub *drvr = ifp->drvr;4590s32 err;4591s32 wpa_val;45924593/* set auth */4594err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);4595if (err < 0) {4596bphy_err(drvr, "auth error %d\n", err);4597return err;4598}4599/* set wsec */4600err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);4601if (err < 0) {4602bphy_err(drvr, "wsec error %d\n", err);4603return err;4604}4605/* set upper-layer auth */4606if (brcmf_is_ibssmode(ifp->vif))4607wpa_val = WPA_AUTH_NONE;4608else4609wpa_val = WPA_AUTH_DISABLED;4610err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_val);4611if (err < 0) {4612bphy_err(drvr, "wpa_auth error %d\n", err);4613return err;4614}46154616return 0;4617}46184619#if defined(__linux__)4620static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)4621#elif defined(__FreeBSD__)4622static bool brcmf_valid_wpa_oui(const u8 *oui, bool is_rsn_ie)4623#endif4624{4625if (is_rsn_ie)4626return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);46274628return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);4629}46304631static s324632brcmf_configure_wpaie(struct brcmf_if *ifp,4633const struct brcmf_vs_tlv *wpa_ie,4634bool is_rsn_ie)4635{4636struct brcmf_pub *drvr = ifp->drvr;4637u32 auth = 0; /* d11 open authentication */4638u16 count;4639s32 err = 0;4640s32 len;4641u32 i;4642u32 wsec;4643u32 pval = 0;4644u32 gval = 0;4645u32 wpa_auth = 0;4646u32 offset;4647#if defined(__linux__)4648u8 *data;4649#elif defined(__FreeBSD__)4650const u8 *data;4651#endif4652u16 rsn_cap;4653u32 wme_bss_disable;4654u32 mfp;46554656brcmf_dbg(TRACE, "Enter\n");4657if (wpa_ie == NULL)4658goto exit;46594660len = wpa_ie->len + TLV_HDR_LEN;4661#if defined(__linux__)4662data = (u8 *)wpa_ie;4663#elif defined(__FreeBSD__)4664data = (const u8 *)wpa_ie;4665#endif4666offset = TLV_HDR_LEN;4667if (!is_rsn_ie)4668offset += VS_IE_FIXED_HDR_LEN;4669else4670offset += WPA_IE_VERSION_LEN;46714672/* check for multicast cipher suite */4673if (offset + WPA_IE_MIN_OUI_LEN > len) {4674err = -EINVAL;4675bphy_err(drvr, "no multicast cipher suite\n");4676goto exit;4677}46784679if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {4680err = -EINVAL;4681bphy_err(drvr, "invalid OUI\n");4682goto exit;4683}4684offset += TLV_OUI_LEN;46854686/* pick up multicast cipher */4687switch (data[offset]) {4688case WPA_CIPHER_NONE:4689gval = 0;4690break;4691case WPA_CIPHER_WEP_40:4692case WPA_CIPHER_WEP_104:4693gval = WEP_ENABLED;4694break;4695case WPA_CIPHER_TKIP:4696gval = TKIP_ENABLED;4697break;4698case WPA_CIPHER_AES_CCM:4699gval = AES_ENABLED;4700break;4701default:4702err = -EINVAL;4703bphy_err(drvr, "Invalid multi cast cipher info\n");4704goto exit;4705}47064707offset++;4708/* walk thru unicast cipher list and pick up what we recognize */4709count = data[offset] + (data[offset + 1] << 8);4710offset += WPA_IE_SUITE_COUNT_LEN;4711/* Check for unicast suite(s) */4712if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {4713err = -EINVAL;4714bphy_err(drvr, "no unicast cipher suite\n");4715goto exit;4716}4717for (i = 0; i < count; i++) {4718if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {4719err = -EINVAL;4720bphy_err(drvr, "invalid OUI\n");4721goto exit;4722}4723offset += TLV_OUI_LEN;4724switch (data[offset]) {4725case WPA_CIPHER_NONE:4726break;4727case WPA_CIPHER_WEP_40:4728case WPA_CIPHER_WEP_104:4729pval |= WEP_ENABLED;4730break;4731case WPA_CIPHER_TKIP:4732pval |= TKIP_ENABLED;4733break;4734case WPA_CIPHER_AES_CCM:4735pval |= AES_ENABLED;4736break;4737default:4738bphy_err(drvr, "Invalid unicast security info\n");4739}4740offset++;4741}4742/* walk thru auth management suite list and pick up what we recognize */4743count = data[offset] + (data[offset + 1] << 8);4744offset += WPA_IE_SUITE_COUNT_LEN;4745/* Check for auth key management suite(s) */4746if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {4747err = -EINVAL;4748bphy_err(drvr, "no auth key mgmt suite\n");4749goto exit;4750}4751for (i = 0; i < count; i++) {4752if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {4753err = -EINVAL;4754bphy_err(drvr, "invalid OUI\n");4755goto exit;4756}4757offset += TLV_OUI_LEN;4758switch (data[offset]) {4759case RSN_AKM_NONE:4760brcmf_dbg(TRACE, "RSN_AKM_NONE\n");4761wpa_auth |= WPA_AUTH_NONE;4762break;4763case RSN_AKM_UNSPECIFIED:4764brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");4765is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :4766(wpa_auth |= WPA_AUTH_UNSPECIFIED);4767break;4768case RSN_AKM_PSK:4769brcmf_dbg(TRACE, "RSN_AKM_PSK\n");4770is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :4771(wpa_auth |= WPA_AUTH_PSK);4772break;4773case RSN_AKM_SHA256_PSK:4774brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");4775wpa_auth |= WPA2_AUTH_PSK_SHA256;4776break;4777case RSN_AKM_SHA256_1X:4778brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");4779wpa_auth |= WPA2_AUTH_1X_SHA256;4780break;4781case RSN_AKM_SAE:4782brcmf_dbg(TRACE, "RSN_AKM_SAE\n");4783wpa_auth |= WPA3_AUTH_SAE_PSK;4784break;4785default:4786bphy_err(drvr, "Invalid key mgmt info\n");4787}4788offset++;4789}47904791mfp = BRCMF_MFP_NONE;4792if (is_rsn_ie) {4793wme_bss_disable = 1;4794if ((offset + RSN_CAP_LEN) <= len) {4795rsn_cap = data[offset] + (data[offset + 1] << 8);4796if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)4797wme_bss_disable = 0;4798if (rsn_cap & RSN_CAP_MFPR_MASK) {4799brcmf_dbg(TRACE, "MFP Required\n");4800mfp = BRCMF_MFP_REQUIRED;4801/* Firmware only supports mfp required in4802* combination with WPA2_AUTH_PSK_SHA256,4803* WPA2_AUTH_1X_SHA256, or WPA3_AUTH_SAE_PSK.4804*/4805if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |4806WPA2_AUTH_1X_SHA256 |4807WPA3_AUTH_SAE_PSK))) {4808err = -EINVAL;4809goto exit;4810}4811/* Firmware has requirement that WPA2_AUTH_PSK/4812* WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI4813* is to be included in the rsn ie.4814*/4815if (wpa_auth & WPA2_AUTH_PSK_SHA256)4816wpa_auth |= WPA2_AUTH_PSK;4817else if (wpa_auth & WPA2_AUTH_1X_SHA256)4818wpa_auth |= WPA2_AUTH_UNSPECIFIED;4819} else if (rsn_cap & RSN_CAP_MFPC_MASK) {4820brcmf_dbg(TRACE, "MFP Capable\n");4821mfp = BRCMF_MFP_CAPABLE;4822}4823}4824offset += RSN_CAP_LEN;4825/* set wme_bss_disable to sync RSN Capabilities */4826err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",4827wme_bss_disable);4828if (err < 0) {4829bphy_err(drvr, "wme_bss_disable error %d\n", err);4830goto exit;4831}48324833/* Skip PMKID cnt as it is know to be 0 for AP. */4834offset += RSN_PMKID_COUNT_LEN;48354836/* See if there is BIP wpa suite left for MFP */4837if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&4838((offset + WPA_IE_MIN_OUI_LEN) <= len)) {4839err = brcmf_fil_bsscfg_data_set(ifp, "bip",4840&data[offset],4841WPA_IE_MIN_OUI_LEN);4842if (err < 0) {4843bphy_err(drvr, "bip error %d\n", err);4844goto exit;4845}4846}4847}4848/* FOR WPS , set SES_OW_ENABLED */4849wsec = (pval | gval | SES_OW_ENABLED);48504851/* set auth */4852err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);4853if (err < 0) {4854bphy_err(drvr, "auth error %d\n", err);4855goto exit;4856}4857/* set wsec */4858err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);4859if (err < 0) {4860bphy_err(drvr, "wsec error %d\n", err);4861goto exit;4862}4863/* Configure MFP, this needs to go after wsec otherwise the wsec command4864* will overwrite the values set by MFP4865*/4866if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {4867err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);4868if (err < 0) {4869bphy_err(drvr, "mfp error %d\n", err);4870goto exit;4871}4872}4873/* set upper-layer auth */4874err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);4875if (err < 0) {4876bphy_err(drvr, "wpa_auth error %d\n", err);4877goto exit;4878}48794880exit:4881return err;4882}48834884static s324885brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,4886struct parsed_vndr_ies *vndr_ies)4887{4888#if defined(__linux__)4889struct brcmf_vs_tlv *vndrie;4890struct brcmf_tlv *ie;4891#elif defined(__FreeBSD__)4892const struct brcmf_vs_tlv *vndrie;4893const struct brcmf_tlv *ie;4894#endif4895struct parsed_vndr_ie_info *parsed_info;4896s32 remaining_len;48974898remaining_len = (s32)vndr_ie_len;4899memset(vndr_ies, 0, sizeof(*vndr_ies));49004901#if defined(__linux__)4902ie = (struct brcmf_tlv *)vndr_ie_buf;4903#elif defined(__FreeBSD__)4904ie = (const struct brcmf_tlv *)vndr_ie_buf;4905#endif4906while (ie) {4907if (ie->id != WLAN_EID_VENDOR_SPECIFIC)4908goto next;4909#if defined(__linux__)4910vndrie = (struct brcmf_vs_tlv *)ie;4911#elif defined(__FreeBSD__)4912vndrie = (const struct brcmf_vs_tlv *)ie;4913#endif4914/* len should be bigger than OUI length + one */4915if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {4916brcmf_err("invalid vndr ie. length is too small %d\n",4917vndrie->len);4918goto next;4919}4920/* if wpa or wme ie, do not add ie */4921if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&4922((vndrie->oui_type == WPA_OUI_TYPE) ||4923(vndrie->oui_type == WME_OUI_TYPE))) {4924brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");4925goto next;4926}49274928parsed_info = &vndr_ies->ie_info[vndr_ies->count];49294930/* save vndr ie information */4931#if defined(__linux__)4932parsed_info->ie_ptr = (char *)vndrie;4933#elif defined(__FreeBSD__)4934parsed_info->ie_ptr = (const char *)vndrie;4935#endif4936parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;4937memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));49384939vndr_ies->count++;49404941brcmf_dbg(TRACE, "** OUI %3ph, type 0x%02x\n",4942parsed_info->vndrie.oui,4943parsed_info->vndrie.oui_type);49444945if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)4946break;4947next:4948remaining_len -= (ie->len + TLV_HDR_LEN);4949if (remaining_len <= TLV_HDR_LEN)4950ie = NULL;4951else4952#if defined(__linux__)4953ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +4954#elif defined(__FreeBSD__)4955ie = (const struct brcmf_tlv *)(((const u8 *)ie) + ie->len +4956#endif4957TLV_HDR_LEN);4958}4959return 0;4960}49614962static u324963#if defined(__linux__)4964brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)4965#elif defined(__FreeBSD__)4966brcmf_vndr_ie(u8 *iebuf, s32 pktflag, const u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)4967#endif4968{4969strscpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN);49704971put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);49724973put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);49744975memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);49764977return ie_len + VNDR_IE_HDR_SIZE;4978}49794980s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,4981const u8 *vndr_ie_buf, u32 vndr_ie_len)4982{4983struct brcmf_pub *drvr;4984struct brcmf_if *ifp;4985struct vif_saved_ie *saved_ie;4986s32 err = 0;4987u8 *iovar_ie_buf;4988u8 *curr_ie_buf;4989u8 *mgmt_ie_buf = NULL;4990int mgmt_ie_buf_len;4991u32 *mgmt_ie_len;4992u32 del_add_ie_buf_len = 0;4993u32 total_ie_buf_len = 0;4994u32 parsed_ie_buf_len = 0;4995struct parsed_vndr_ies old_vndr_ies;4996struct parsed_vndr_ies new_vndr_ies;4997struct parsed_vndr_ie_info *vndrie_info;4998s32 i;4999u8 *ptr;5000int remained_buf_len;50015002if (!vif)5003return -ENODEV;5004ifp = vif->ifp;5005drvr = ifp->drvr;5006saved_ie = &vif->saved_ie;50075008brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,5009pktflag);5010iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);5011if (!iovar_ie_buf)5012return -ENOMEM;5013curr_ie_buf = iovar_ie_buf;5014switch (pktflag) {5015case BRCMF_VNDR_IE_PRBREQ_FLAG:5016mgmt_ie_buf = saved_ie->probe_req_ie;5017mgmt_ie_len = &saved_ie->probe_req_ie_len;5018mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);5019break;5020case BRCMF_VNDR_IE_PRBRSP_FLAG:5021mgmt_ie_buf = saved_ie->probe_res_ie;5022mgmt_ie_len = &saved_ie->probe_res_ie_len;5023mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);5024break;5025case BRCMF_VNDR_IE_BEACON_FLAG:5026mgmt_ie_buf = saved_ie->beacon_ie;5027mgmt_ie_len = &saved_ie->beacon_ie_len;5028mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);5029break;5030case BRCMF_VNDR_IE_ASSOCREQ_FLAG:5031mgmt_ie_buf = saved_ie->assoc_req_ie;5032mgmt_ie_len = &saved_ie->assoc_req_ie_len;5033mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);5034break;5035case BRCMF_VNDR_IE_ASSOCRSP_FLAG:5036mgmt_ie_buf = saved_ie->assoc_res_ie;5037mgmt_ie_len = &saved_ie->assoc_res_ie_len;5038mgmt_ie_buf_len = sizeof(saved_ie->assoc_res_ie);5039break;5040default:5041err = -EPERM;5042bphy_err(drvr, "not suitable type\n");5043goto exit;5044}50455046if (vndr_ie_len > mgmt_ie_buf_len) {5047err = -ENOMEM;5048bphy_err(drvr, "extra IE size too big\n");5049goto exit;5050}50515052/* parse and save new vndr_ie in curr_ie_buff before comparing it */5053if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {5054ptr = curr_ie_buf;5055brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);5056for (i = 0; i < new_vndr_ies.count; i++) {5057vndrie_info = &new_vndr_ies.ie_info[i];5058memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,5059vndrie_info->ie_len);5060parsed_ie_buf_len += vndrie_info->ie_len;5061}5062}50635064if (mgmt_ie_buf && *mgmt_ie_len) {5065if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&5066(memcmp(mgmt_ie_buf, curr_ie_buf,5067parsed_ie_buf_len) == 0)) {5068brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");5069goto exit;5070}50715072/* parse old vndr_ie */5073brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);50745075/* make a command to delete old ie */5076for (i = 0; i < old_vndr_ies.count; i++) {5077vndrie_info = &old_vndr_ies.ie_info[i];50785079brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%3ph\n",5080vndrie_info->vndrie.id,5081vndrie_info->vndrie.len,5082vndrie_info->vndrie.oui);50835084del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,5085vndrie_info->ie_ptr,5086vndrie_info->ie_len,5087"del");5088curr_ie_buf += del_add_ie_buf_len;5089total_ie_buf_len += del_add_ie_buf_len;5090}5091}50925093*mgmt_ie_len = 0;5094/* Add if there is any extra IE */5095if (mgmt_ie_buf && parsed_ie_buf_len) {5096ptr = mgmt_ie_buf;50975098remained_buf_len = mgmt_ie_buf_len;50995100/* make a command to add new ie */5101for (i = 0; i < new_vndr_ies.count; i++) {5102vndrie_info = &new_vndr_ies.ie_info[i];51035104/* verify remained buf size before copy data */5105if (remained_buf_len < (vndrie_info->vndrie.len +5106VNDR_IE_VSIE_OFFSET)) {5107bphy_err(drvr, "no space in mgmt_ie_buf: len left %d",5108remained_buf_len);5109break;5110}5111remained_buf_len -= (vndrie_info->ie_len +5112VNDR_IE_VSIE_OFFSET);51135114brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%3ph\n",5115vndrie_info->vndrie.id,5116vndrie_info->vndrie.len,5117vndrie_info->vndrie.oui);51185119del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,5120vndrie_info->ie_ptr,5121vndrie_info->ie_len,5122"add");51235124/* save the parsed IE in wl struct */5125memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,5126vndrie_info->ie_len);5127*mgmt_ie_len += vndrie_info->ie_len;51285129curr_ie_buf += del_add_ie_buf_len;5130total_ie_buf_len += del_add_ie_buf_len;5131}5132}5133if (total_ie_buf_len) {5134err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,5135total_ie_buf_len);5136if (err)5137bphy_err(drvr, "vndr ie set error : %d\n", err);5138}51395140exit:5141kfree(iovar_ie_buf);5142return err;5143}51445145s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)5146{5147static const s32 pktflags[] = {5148BRCMF_VNDR_IE_PRBRSP_FLAG,5149BRCMF_VNDR_IE_BEACON_FLAG5150};5151int i;51525153if (vif->wdev.iftype == NL80211_IFTYPE_AP)5154brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG, NULL, 0);5155else5156brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, NULL, 0);51575158for (i = 0; i < ARRAY_SIZE(pktflags); i++)5159brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);51605161memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));5162return 0;5163}51645165static s325166brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,5167struct cfg80211_beacon_data *beacon)5168{5169struct brcmf_pub *drvr = vif->ifp->drvr;5170s32 err;51715172/* Set Beacon IEs to FW */5173err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,5174beacon->tail, beacon->tail_len);5175if (err) {5176bphy_err(drvr, "Set Beacon IE Failed\n");5177return err;5178}5179brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");51805181/* Set Probe Response IEs to FW */5182err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,5183beacon->proberesp_ies,5184beacon->proberesp_ies_len);5185if (err)5186bphy_err(drvr, "Set Probe Resp IE Failed\n");5187else5188brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");51895190/* Set Assoc Response IEs to FW */5191err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG,5192beacon->assocresp_ies,5193beacon->assocresp_ies_len);5194if (err)5195brcmf_err("Set Assoc Resp IE Failed\n");5196else5197brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc Resp\n");51985199return err;5200}52015202static s325203brcmf_parse_configure_security(struct brcmf_if *ifp,5204struct cfg80211_ap_settings *settings,5205enum nl80211_iftype dev_role)5206{5207const struct brcmf_tlv *rsn_ie;5208const struct brcmf_vs_tlv *wpa_ie;5209s32 err = 0;52105211/* find the RSN_IE */5212#if defined(__linux__)5213rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,5214#elif defined(__FreeBSD__)5215rsn_ie = brcmf_parse_tlvs(settings->beacon.tail,5216#endif5217settings->beacon.tail_len, WLAN_EID_RSN);52185219/* find the WPA_IE */5220#if defined(__linux__)5221wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,5222#elif defined(__FreeBSD__)5223wpa_ie = brcmf_find_wpaie(settings->beacon.tail,5224#endif5225settings->beacon.tail_len);52265227if (wpa_ie || rsn_ie) {5228brcmf_dbg(TRACE, "WPA(2) IE is found\n");5229if (wpa_ie) {5230/* WPA IE */5231err = brcmf_configure_wpaie(ifp, wpa_ie, false);5232if (err < 0)5233return err;5234} else {5235#if defined(__linux__)5236struct brcmf_vs_tlv *tmp_ie;52375238tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;5239#elif defined(__FreeBSD__)5240const struct brcmf_vs_tlv *tmp_ie;52415242tmp_ie = (const struct brcmf_vs_tlv *)rsn_ie;5243#endif524452455246/* RSN IE */5247err = brcmf_configure_wpaie(ifp, tmp_ie, true);5248if (err < 0)5249return err;5250}5251} else {5252brcmf_dbg(TRACE, "No WPA(2) IEs found\n");5253brcmf_configure_opensecurity(ifp);5254}52555256return err;5257}52585259static s325260brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,5261struct cfg80211_ap_settings *settings)5262{5263s32 ie_offset;5264struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5265struct brcmf_if *ifp = netdev_priv(ndev);5266struct brcmf_pub *drvr = cfg->pub;5267struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;5268struct cfg80211_crypto_settings *crypto = &settings->crypto;5269const struct brcmf_tlv *ssid_ie;5270const struct brcmf_tlv *country_ie;5271struct brcmf_ssid_le ssid_le;5272s32 err = -EPERM;5273struct brcmf_join_params join_params;5274enum nl80211_iftype dev_role;5275struct brcmf_fil_bss_enable_le bss_enable;5276u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);5277bool mbss;5278int is_11d;5279bool supports_11d;5280bool closednet;52815282brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",5283settings->chandef.chan->hw_value,5284settings->chandef.center_freq1, settings->chandef.width,5285settings->beacon_interval, settings->dtim_period);5286brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",5287settings->ssid, settings->ssid_len, settings->auth_type,5288settings->inactivity_timeout);5289dev_role = ifp->vif->wdev.iftype;5290mbss = ifp->vif->mbss;52915292/* store current 11d setting */5293if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY,5294&ifp->vif->is_11d)) {5295is_11d = supports_11d = false;5296} else {5297#if defined(__linux__)5298country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,5299#elif defined(__FreeBSD__)5300country_ie = brcmf_parse_tlvs(settings->beacon.tail,5301#endif5302settings->beacon.tail_len,5303WLAN_EID_COUNTRY);5304is_11d = country_ie ? 1 : 0;5305supports_11d = true;5306}53075308memset(&ssid_le, 0, sizeof(ssid_le));5309if (settings->ssid == NULL || settings->ssid_len == 0) {5310ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;5311ssid_ie = brcmf_parse_tlvs(5312#if defined(__linux__)5313(u8 *)&settings->beacon.head[ie_offset],5314#elif defined(__FreeBSD__)5315&settings->beacon.head[ie_offset],5316#endif5317settings->beacon.head_len - ie_offset,5318WLAN_EID_SSID);5319if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)5320return -EINVAL;53215322memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);5323ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);5324brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);5325} else {5326memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);5327ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);5328}53295330if (!mbss) {5331brcmf_set_mpc(ifp, 0);5332brcmf_configure_arp_nd_offload(ifp, false);5333}53345335/* Parameters shared by all radio interfaces */5336if (!mbss) {5337if ((supports_11d) && (is_11d != ifp->vif->is_11d)) {5338err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,5339is_11d);5340if (err < 0) {5341bphy_err(drvr, "Regulatory Set Error, %d\n",5342err);5343goto exit;5344}5345}5346if (settings->beacon_interval) {5347err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,5348settings->beacon_interval);5349if (err < 0) {5350bphy_err(drvr, "Beacon Interval Set Error, %d\n",5351err);5352goto exit;5353}5354}5355if (settings->dtim_period) {5356err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,5357settings->dtim_period);5358if (err < 0) {5359bphy_err(drvr, "DTIM Interval Set Error, %d\n",5360err);5361goto exit;5362}5363}53645365if ((dev_role == NL80211_IFTYPE_AP) &&5366((ifp->ifidx == 0) ||5367(!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB) &&5368!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)))) {5369err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);5370if (err < 0) {5371bphy_err(drvr, "BRCMF_C_DOWN error %d\n",5372err);5373goto exit;5374}5375brcmf_fil_iovar_int_set(ifp, "apsta", 0);5376}53775378err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);5379if (err < 0) {5380bphy_err(drvr, "SET INFRA error %d\n", err);5381goto exit;5382}5383} else if (WARN_ON(supports_11d && (is_11d != ifp->vif->is_11d))) {5384/* Multiple-BSS should use same 11d configuration */5385err = -EINVAL;5386goto exit;5387}53885389/* Interface specific setup */5390if (dev_role == NL80211_IFTYPE_AP) {5391if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))5392brcmf_fil_iovar_int_set(ifp, "mbss", 1);53935394err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);5395if (err < 0) {5396bphy_err(drvr, "setting AP mode failed %d\n",5397err);5398goto exit;5399}5400if (!mbss) {5401/* Firmware 10.x requires setting channel after enabling5402* AP and before bringing interface up.5403*/5404err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);5405if (err < 0) {5406bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",5407chanspec, err);5408goto exit;5409}5410}5411err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);5412if (err < 0) {5413bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err);5414goto exit;5415}54165417if (crypto->psk) {5418brcmf_dbg(INFO, "using PSK offload\n");5419profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_PSK);5420err = brcmf_set_pmk(ifp, crypto->psk,5421BRCMF_WSEC_MAX_PSK_LEN);5422if (err < 0)5423goto exit;5424}5425if (crypto->sae_pwd) {5426brcmf_dbg(INFO, "using SAE offload\n");5427profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_SAE);5428err = brcmf_fwvid_set_sae_password(ifp, crypto);5429if (err < 0)5430goto exit;5431}5432if (profile->use_fwauth == 0)5433profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);54345435err = brcmf_parse_configure_security(ifp, settings,5436NL80211_IFTYPE_AP);5437if (err < 0) {5438bphy_err(drvr, "brcmf_parse_configure_security error\n");5439goto exit;5440}54415442/* On DOWN the firmware removes the WEP keys, reconfigure5443* them if they were set.5444*/5445brcmf_cfg80211_reconfigure_wep(ifp);54465447memset(&join_params, 0, sizeof(join_params));5448/* join parameters starts with ssid */5449memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));5450/* create softap */5451err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,5452&join_params, sizeof(join_params));5453if (err < 0) {5454bphy_err(drvr, "SET SSID error (%d)\n", err);5455goto exit;5456}54575458closednet =5459(settings->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE);5460err = brcmf_fil_iovar_int_set(ifp, "closednet", closednet);5461if (err) {5462bphy_err(drvr, "%s closednet error (%d)\n",5463(closednet ? "enabled" : "disabled"),5464err);5465goto exit;5466}54675468brcmf_dbg(TRACE, "AP mode configuration complete\n");5469} else if (dev_role == NL80211_IFTYPE_P2P_GO) {5470err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);5471if (err < 0) {5472bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",5473chanspec, err);5474goto exit;5475}54765477err = brcmf_parse_configure_security(ifp, settings,5478NL80211_IFTYPE_P2P_GO);5479if (err < 0) {5480brcmf_err("brcmf_parse_configure_security error\n");5481goto exit;5482}54835484err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,5485sizeof(ssid_le));5486if (err < 0) {5487bphy_err(drvr, "setting ssid failed %d\n", err);5488goto exit;5489}5490bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);5491bss_enable.enable = cpu_to_le32(1);5492err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,5493sizeof(bss_enable));5494if (err < 0) {5495bphy_err(drvr, "bss_enable config failed %d\n", err);5496goto exit;5497}54985499brcmf_dbg(TRACE, "GO mode configuration complete\n");5500} else {5501WARN_ON(1);5502}55035504brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);5505set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);5506brcmf_net_setcarrier(ifp, true);55075508exit:5509if ((err) && (!mbss)) {5510brcmf_set_mpc(ifp, 1);5511brcmf_configure_arp_nd_offload(ifp, true);5512}5513return err;5514}55155516static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev,5517unsigned int link_id)5518{5519struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5520struct brcmf_if *ifp = netdev_priv(ndev);5521struct brcmf_pub *drvr = cfg->pub;5522struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;5523s32 err;5524struct brcmf_fil_bss_enable_le bss_enable;5525struct brcmf_join_params join_params;55265527brcmf_dbg(TRACE, "Enter\n");55285529if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {5530/* Due to most likely deauths outstanding we sleep */5531/* first to make sure they get processed by fw. */5532#if defined(__linux__)5533msleep(400);5534#elif defined(__FreeBSD__)5535linux_msleep(400);5536#endif55375538if (profile->use_fwauth != BIT(BRCMF_PROFILE_FWAUTH_NONE)) {5539struct cfg80211_crypto_settings crypto = {};55405541if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_PSK))5542brcmf_set_pmk(ifp, NULL, 0);5543if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_SAE))5544brcmf_fwvid_set_sae_password(ifp, &crypto);5545profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);5546}55475548if (ifp->vif->mbss) {5549err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);5550return err;5551}55525553/* First BSS doesn't get a full reset */5554if (ifp->bsscfgidx == 0)5555brcmf_fil_iovar_int_set(ifp, "closednet", 0);55565557memset(&join_params, 0, sizeof(join_params));5558err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,5559&join_params, sizeof(join_params));5560if (err < 0)5561bphy_err(drvr, "SET SSID error (%d)\n", err);5562err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);5563if (err < 0)5564bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err);5565err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);5566if (err < 0)5567bphy_err(drvr, "setting AP mode failed %d\n", err);5568if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))5569brcmf_fil_iovar_int_set(ifp, "mbss", 0);5570brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,5571ifp->vif->is_11d);5572/* Bring device back up so it can be used again */5573err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);5574if (err < 0)5575bphy_err(drvr, "BRCMF_C_UP error %d\n", err);55765577brcmf_vif_clear_mgmt_ies(ifp->vif);5578} else {5579bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);5580bss_enable.enable = cpu_to_le32(0);5581err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,5582sizeof(bss_enable));5583if (err < 0)5584bphy_err(drvr, "bss_enable config failed %d\n", err);5585}5586brcmf_set_mpc(ifp, 1);5587clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);5588brcmf_configure_arp_nd_offload(ifp, true);5589brcmf_net_setcarrier(ifp, false);55905591return err;5592}55935594static s325595brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,5596struct cfg80211_ap_update *info)5597{5598struct brcmf_if *ifp = netdev_priv(ndev);55995600brcmf_dbg(TRACE, "Enter\n");56015602return brcmf_config_ap_mgmt_ie(ifp->vif, &info->beacon);5603}56045605static int5606brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,5607struct station_del_parameters *params)5608{5609struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5610struct brcmf_pub *drvr = cfg->pub;5611struct brcmf_scb_val_le scbval;5612struct brcmf_if *ifp = netdev_priv(ndev);5613s32 err;56145615if (!params->mac)5616return -EFAULT;56175618brcmf_dbg(TRACE, "Enter %pM\n", params->mac);56195620if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)5621ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;5622if (!check_vif_up(ifp->vif))5623return -EIO;56245625memcpy(&scbval.ea, params->mac, ETH_ALEN);5626scbval.val = cpu_to_le32(params->reason_code);5627err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,5628&scbval, sizeof(scbval));5629if (err)5630bphy_err(drvr, "SCB_DEAUTHENTICATE_FOR_REASON failed %d\n",5631err);56325633brcmf_dbg(TRACE, "Exit\n");5634return err;5635}56365637static int5638brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,5639const u8 *mac, struct station_parameters *params)5640{5641struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5642struct brcmf_pub *drvr = cfg->pub;5643struct brcmf_if *ifp = netdev_priv(ndev);5644s32 err;56455646brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,5647params->sta_flags_mask, params->sta_flags_set);56485649/* Ignore all 00 MAC */5650if (is_zero_ether_addr(mac))5651return 0;56525653if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))5654return 0;56555656if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))5657err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,5658#if defined(__linux__)5659(void *)mac, ETH_ALEN);5660#elif defined(__FreeBSD__)5661__DECONST(u8 *, mac), ETH_ALEN);5662#endif5663else5664err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,5665#if defined(__linux__)5666(void *)mac, ETH_ALEN);5667#elif defined(__FreeBSD__)5668__DECONST(u8 *, mac), ETH_ALEN);5669#endif5670if (err < 0)5671bphy_err(drvr, "Setting SCB (de-)authorize failed, %d\n", err);56725673return err;5674}56755676static void5677brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,5678struct wireless_dev *wdev,5679struct mgmt_frame_regs *upd)5680{5681struct brcmf_cfg80211_vif *vif;56825683vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);56845685vif->mgmt_rx_reg = upd->interface_stypes;5686}568756885689int5690brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,5691struct cfg80211_mgmt_tx_params *params, u64 *cookie)5692{5693struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5694struct ieee80211_channel *chan = params->chan;5695struct brcmf_pub *drvr = cfg->pub;5696const u8 *buf = params->buf;5697size_t len = params->len;5698const struct ieee80211_mgmt *mgmt;5699struct brcmf_cfg80211_vif *vif;5700s32 err = 0;5701s32 ie_offset;5702s32 ie_len;5703struct brcmf_fil_action_frame_le *action_frame;5704struct brcmf_fil_af_params_le *af_params;5705bool ack;5706__le32 hw_ch;57075708brcmf_dbg(TRACE, "Enter\n");57095710*cookie = 0;57115712mgmt = (const struct ieee80211_mgmt *)buf;57135714if (!ieee80211_is_mgmt(mgmt->frame_control)) {5715bphy_err(drvr, "Driver only allows MGMT packet type\n");5716return -EPERM;5717}57185719vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);57205721if (ieee80211_is_probe_resp(mgmt->frame_control)) {5722/* Right now the only reason to get a probe response */5723/* is for p2p listen response or for p2p GO from */5724/* wpa_supplicant. Unfortunately the probe is send */5725/* on primary ndev, while dongle wants it on the p2p */5726/* vif. Since this is only reason for a probe */5727/* response to be sent, the vif is taken from cfg. */5728/* If ever desired to send proberesp for non p2p */5729/* response then data should be checked for */5730/* "DIRECT-". Note in future supplicant will take */5731/* dedicated p2p wdev to do this and then this 'hack'*/5732/* is not needed anymore. */5733ie_offset = DOT11_MGMT_HDR_LEN +5734DOT11_BCN_PRB_FIXED_LEN;5735ie_len = len - ie_offset;5736if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)5737vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;5738err = brcmf_vif_set_mgmt_ie(vif,5739BRCMF_VNDR_IE_PRBRSP_FLAG,5740&buf[ie_offset],5741ie_len);5742cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,5743GFP_KERNEL);5744} else if (ieee80211_is_action(mgmt->frame_control)) {5745if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) {5746bphy_err(drvr, "invalid action frame length\n");5747err = -EINVAL;5748goto exit;5749}5750af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);5751if (af_params == NULL) {5752bphy_err(drvr, "unable to allocate frame\n");5753err = -ENOMEM;5754goto exit;5755}5756action_frame = &af_params->action_frame;5757/* Add the packet Id */5758action_frame->packet_id = cpu_to_le32(*cookie);5759/* Add BSSID */5760memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);5761memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);5762/* Add the length exepted for 802.11 header */5763action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);5764/* Add the channel. Use the one specified as parameter if any or5765* the current one (got from the firmware) otherwise5766*/5767if (chan) {5768hw_ch = cpu_to_le32(chan->hw_value);5769} else {5770err = brcmf_fil_cmd_data_get(vif->ifp,5771BRCMF_C_GET_CHANNEL,5772&hw_ch, sizeof(hw_ch));5773if (err) {5774bphy_err(drvr,5775"unable to get current hw channel\n");5776goto free;5777}5778}5779af_params->channel = hw_ch;57805781af_params->dwell_time = cpu_to_le32(params->wait);5782memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],5783le16_to_cpu(action_frame->len));57845785#if defined(__linux__)5786brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, channel=%d\n",5787*cookie, le16_to_cpu(action_frame->len),5788#elif defined(__FreeBSD__)5789brcmf_dbg(TRACE, "Action frame, cookie=%ju, len=%d, channel=%d\n",5790(uintmax_t)*cookie, le16_to_cpu(action_frame->len),5791#endif5792le32_to_cpu(af_params->channel));57935794ack = brcmf_p2p_send_action_frame(vif->ifp, af_params);57955796cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,5797GFP_KERNEL);5798free:5799kfree(af_params);5800} else {5801brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);5802brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len);5803}58045805exit:5806return err;5807}5808BRCMF_EXPORT_SYMBOL_GPL(brcmf_cfg80211_mgmt_tx);58095810static int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy,5811struct net_device *ndev,5812s32 rssi_low, s32 rssi_high)5813{5814struct brcmf_cfg80211_vif *vif;5815struct brcmf_if *ifp;5816int err = 0;58175818brcmf_dbg(TRACE, "low=%d high=%d", rssi_low, rssi_high);58195820ifp = netdev_priv(ndev);5821vif = ifp->vif;58225823if (rssi_low != vif->cqm_rssi_low || rssi_high != vif->cqm_rssi_high) {5824/* The firmware will send an event when the RSSI is less than or5825* equal to a configured level and the previous RSSI event was5826* less than or equal to a different level. Set a third level5827* so that we also detect the transition from rssi <= rssi_high5828* to rssi > rssi_high.5829*/5830struct brcmf_rssi_event_le config = {5831.rate_limit_msec = cpu_to_le32(0),5832.rssi_level_num = 3,5833.rssi_levels = {5834clamp_val(rssi_low, S8_MIN, S8_MAX - 2),5835clamp_val(rssi_high, S8_MIN + 1, S8_MAX - 1),5836S8_MAX,5837},5838};58395840err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &config,5841sizeof(config));5842if (err) {5843err = -EINVAL;5844} else {5845vif->cqm_rssi_low = rssi_low;5846vif->cqm_rssi_high = rssi_high;5847}5848}58495850return err;5851}58525853static int5854brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,5855struct wireless_dev *wdev,5856u64 cookie)5857{5858struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5859struct brcmf_pub *drvr = cfg->pub;5860struct brcmf_cfg80211_vif *vif;5861int err = 0;58625863brcmf_dbg(TRACE, "Enter p2p listen cancel\n");58645865vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;5866if (vif == NULL) {5867bphy_err(drvr, "No p2p device available for probe response\n");5868err = -ENODEV;5869goto exit;5870}5871brcmf_p2p_cancel_remain_on_channel(vif->ifp);5872exit:5873return err;5874}58755876static int brcmf_cfg80211_get_channel(struct wiphy *wiphy,5877struct wireless_dev *wdev,5878unsigned int link_id,5879struct cfg80211_chan_def *chandef)5880{5881struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5882struct net_device *ndev = wdev->netdev;5883struct brcmf_pub *drvr = cfg->pub;5884struct brcmu_chan ch;5885enum nl80211_band band = 0;5886enum nl80211_chan_width width = 0;5887u32 chanspec;5888int freq, err;58895890if (!ndev || drvr->bus_if->state != BRCMF_BUS_UP)5891return -ENODEV;58925893err = brcmf_fil_iovar_int_get(netdev_priv(ndev), "chanspec", &chanspec);5894if (err) {5895bphy_err(drvr, "chanspec failed (%d)\n", err);5896return err;5897}58985899ch.chspec = chanspec;5900cfg->d11inf.decchspec(&ch);59015902switch (ch.band) {5903case BRCMU_CHAN_BAND_2G:5904band = NL80211_BAND_2GHZ;5905break;5906case BRCMU_CHAN_BAND_5G:5907band = NL80211_BAND_5GHZ;5908break;5909}59105911switch (ch.bw) {5912case BRCMU_CHAN_BW_80:5913width = NL80211_CHAN_WIDTH_80;5914break;5915case BRCMU_CHAN_BW_40:5916width = NL80211_CHAN_WIDTH_40;5917break;5918case BRCMU_CHAN_BW_20:5919width = NL80211_CHAN_WIDTH_20;5920break;5921case BRCMU_CHAN_BW_80P80:5922width = NL80211_CHAN_WIDTH_80P80;5923break;5924case BRCMU_CHAN_BW_160:5925width = NL80211_CHAN_WIDTH_160;5926break;5927}59285929freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);5930chandef->chan = ieee80211_get_channel(wiphy, freq);5931chandef->width = width;5932chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band);5933chandef->center_freq2 = 0;59345935return 0;5936}59375938static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,5939struct wireless_dev *wdev,5940enum nl80211_crit_proto_id proto,5941u16 duration)5942{5943struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5944struct brcmf_cfg80211_vif *vif;59455946vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);59475948/* only DHCP support for now */5949if (proto != NL80211_CRIT_PROTO_DHCP)5950return -EINVAL;59515952/* suppress and abort scanning */5953set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);5954brcmf_abort_scanning(cfg);59555956return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);5957}59585959static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,5960struct wireless_dev *wdev)5961{5962struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);5963struct brcmf_cfg80211_vif *vif;59645965vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);59665967brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);5968clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);5969}59705971static s325972brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,5973const struct brcmf_event_msg *e, void *data)5974{5975switch (e->reason) {5976case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:5977brcmf_dbg(TRACE, "TDLS Peer Discovered\n");5978break;5979case BRCMF_E_REASON_TDLS_PEER_CONNECTED:5980brcmf_dbg(TRACE, "TDLS Peer Connected\n");5981#if defined(__linux__)5982brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);5983#elif defined(__FreeBSD__)5984brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, e->addr);5985#endif5986break;5987case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:5988brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");5989#if defined(__linux__)5990brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);5991#elif defined(__FreeBSD__)5992brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, e->addr);5993#endif5994break;5995}59965997return 0;5998}59996000static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)6001{6002int ret;60036004switch (oper) {6005case NL80211_TDLS_DISCOVERY_REQ:6006ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;6007break;6008case NL80211_TDLS_SETUP:6009ret = BRCMF_TDLS_MANUAL_EP_CREATE;6010break;6011case NL80211_TDLS_TEARDOWN:6012ret = BRCMF_TDLS_MANUAL_EP_DELETE;6013break;6014default:6015brcmf_err("unsupported operation: %d\n", oper);6016ret = -EOPNOTSUPP;6017}6018return ret;6019}60206021static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,6022struct net_device *ndev, const u8 *peer,6023enum nl80211_tdls_operation oper)6024{6025struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);6026struct brcmf_pub *drvr = cfg->pub;6027struct brcmf_if *ifp;6028struct brcmf_tdls_iovar_le info;6029int ret = 0;60306031ret = brcmf_convert_nl80211_tdls_oper(oper);6032if (ret < 0)6033return ret;60346035ifp = netdev_priv(ndev);6036memset(&info, 0, sizeof(info));6037info.mode = (u8)ret;6038if (peer)6039memcpy(info.ea, peer, ETH_ALEN);60406041ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",6042&info, sizeof(info));6043if (ret < 0)6044bphy_err(drvr, "tdls_endpoint iovar failed: ret=%d\n", ret);60456046return ret;6047}60486049static int6050brcmf_cfg80211_update_conn_params(struct wiphy *wiphy,6051struct net_device *ndev,6052struct cfg80211_connect_params *sme,6053u32 changed)6054{6055struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);6056struct brcmf_pub *drvr = cfg->pub;6057struct brcmf_if *ifp;6058int err;60596060if (!(changed & UPDATE_ASSOC_IES))6061return 0;60626063ifp = netdev_priv(ndev);6064err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,6065sme->ie, sme->ie_len);6066if (err)6067bphy_err(drvr, "Set Assoc REQ IE Failed\n");6068else6069brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");60706071return err;6072}60736074#ifdef CONFIG_PM6075static int6076brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,6077struct cfg80211_gtk_rekey_data *gtk)6078{6079struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);6080struct brcmf_pub *drvr = cfg->pub;6081struct brcmf_if *ifp = netdev_priv(ndev);6082struct brcmf_gtk_keyinfo_le gtk_le;6083int ret;60846085brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);60866087memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));6088memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));6089memcpy(gtk_le.replay_counter, gtk->replay_ctr,6090sizeof(gtk_le.replay_counter));60916092ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", >k_le,6093sizeof(gtk_le));6094if (ret < 0)6095bphy_err(drvr, "gtk_key_info iovar failed: ret=%d\n", ret);60966097return ret;6098}6099#endif61006101static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev,6102const struct cfg80211_pmk_conf *conf)6103{6104struct brcmf_if *ifp;61056106brcmf_dbg(TRACE, "enter\n");61076108/* expect using firmware supplicant for 1X */6109ifp = netdev_priv(dev);6110if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))6111return -EINVAL;61126113if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN)6114return -ERANGE;61156116return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len);6117}61186119static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev,6120const u8 *aa)6121{6122struct brcmf_if *ifp;61236124brcmf_dbg(TRACE, "enter\n");6125ifp = netdev_priv(dev);6126if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))6127return -EINVAL;61286129return brcmf_set_pmk(ifp, NULL, 0);6130}61316132static int brcmf_cfg80211_change_bss(struct wiphy *wiphy, struct net_device *dev,6133struct bss_parameters *params)6134{6135struct brcmf_if *ifp = netdev_priv(dev);6136int ret = 0;61376138/* In AP mode, the "ap_isolate" value represents6139* 0 = allow low-level bridging of frames between associated stations6140* 1 = restrict low-level bridging of frames to isolate associated stations6141* -1 = do not change existing setting6142*/6143if (params->ap_isolate >= 0) {6144ret = brcmf_fil_iovar_int_set(ifp, "ap_isolate", params->ap_isolate);6145if (ret < 0)6146brcmf_err("ap_isolate iovar failed: ret=%d\n", ret);6147}61486149return ret;6150}61516152static struct cfg80211_ops brcmf_cfg80211_ops = {6153.add_virtual_intf = brcmf_cfg80211_add_iface,6154.del_virtual_intf = brcmf_cfg80211_del_iface,6155.change_virtual_intf = brcmf_cfg80211_change_iface,6156.scan = brcmf_cfg80211_scan,6157.set_wiphy_params = brcmf_cfg80211_set_wiphy_params,6158.join_ibss = brcmf_cfg80211_join_ibss,6159.leave_ibss = brcmf_cfg80211_leave_ibss,6160.get_station = brcmf_cfg80211_get_station,6161.dump_station = brcmf_cfg80211_dump_station,6162.set_tx_power = brcmf_cfg80211_set_tx_power,6163.get_tx_power = brcmf_cfg80211_get_tx_power,6164.add_key = brcmf_cfg80211_add_key,6165.del_key = brcmf_cfg80211_del_key,6166.get_key = brcmf_cfg80211_get_key,6167.set_default_key = brcmf_cfg80211_config_default_key,6168.set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,6169.set_power_mgmt = brcmf_cfg80211_set_power_mgmt,6170.connect = brcmf_cfg80211_connect,6171.disconnect = brcmf_cfg80211_disconnect,6172.suspend = brcmf_cfg80211_suspend,6173.resume = brcmf_cfg80211_resume,6174.set_pmksa = brcmf_cfg80211_set_pmksa,6175.del_pmksa = brcmf_cfg80211_del_pmksa,6176.flush_pmksa = brcmf_cfg80211_flush_pmksa,6177.start_ap = brcmf_cfg80211_start_ap,6178.stop_ap = brcmf_cfg80211_stop_ap,6179.change_beacon = brcmf_cfg80211_change_beacon,6180.del_station = brcmf_cfg80211_del_station,6181.change_station = brcmf_cfg80211_change_station,6182.sched_scan_start = brcmf_cfg80211_sched_scan_start,6183.sched_scan_stop = brcmf_cfg80211_sched_scan_stop,6184.update_mgmt_frame_registrations =6185brcmf_cfg80211_update_mgmt_frame_registrations,6186.mgmt_tx = brcmf_cfg80211_mgmt_tx,6187.set_cqm_rssi_range_config = brcmf_cfg80211_set_cqm_rssi_range_config,6188.remain_on_channel = brcmf_p2p_remain_on_channel,6189.cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,6190.get_channel = brcmf_cfg80211_get_channel,6191.start_p2p_device = brcmf_p2p_start_device,6192.stop_p2p_device = brcmf_p2p_stop_device,6193.crit_proto_start = brcmf_cfg80211_crit_proto_start,6194.crit_proto_stop = brcmf_cfg80211_crit_proto_stop,6195.tdls_oper = brcmf_cfg80211_tdls_oper,6196.update_connect_params = brcmf_cfg80211_update_conn_params,6197.set_pmk = brcmf_cfg80211_set_pmk,6198.del_pmk = brcmf_cfg80211_del_pmk,6199.change_bss = brcmf_cfg80211_change_bss,6200};62016202struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings)6203{6204struct cfg80211_ops *ops;62056206ops = kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops),6207GFP_KERNEL);62086209if (ops && settings->roamoff)6210ops->update_connect_params = NULL;62116212return ops;6213}62146215struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,6216enum nl80211_iftype type)6217{6218struct brcmf_cfg80211_vif *vif_walk;6219struct brcmf_cfg80211_vif *vif;6220bool mbss;6221struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);62226223brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",6224sizeof(*vif));6225vif = kzalloc(sizeof(*vif), GFP_KERNEL);6226if (!vif)6227return ERR_PTR(-ENOMEM);62286229vif->wdev.wiphy = cfg->wiphy;6230vif->wdev.iftype = type;6231init_completion(&vif->mgmt_tx);62326233brcmf_init_prof(&vif->profile);62346235if (type == NL80211_IFTYPE_AP &&6236brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {6237mbss = false;6238list_for_each_entry(vif_walk, &cfg->vif_list, list) {6239if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {6240mbss = true;6241break;6242}6243}6244vif->mbss = mbss;6245}62466247list_add_tail(&vif->list, &cfg->vif_list);6248return vif;6249}62506251void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)6252{6253list_del(&vif->list);6254kfree(vif);6255}62566257void brcmf_cfg80211_free_netdev(struct net_device *ndev)6258{6259struct brcmf_cfg80211_vif *vif;6260struct brcmf_if *ifp;62616262ifp = netdev_priv(ndev);6263vif = ifp->vif;62646265if (vif)6266brcmf_free_vif(vif);6267}62686269static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,6270const struct brcmf_event_msg *e)6271{6272u32 event = e->event_code;6273u32 status = e->status;62746275if ((vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_PSK ||6276vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_SAE) &&6277event == BRCMF_E_PSK_SUP &&6278status == BRCMF_E_STATUS_FWSUP_COMPLETED)6279set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);6280if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {6281brcmf_dbg(CONN, "Processing set ssid\n");6282memcpy(vif->profile.bssid, e->addr, ETH_ALEN);6283if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK &&6284vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE)6285return true;62866287set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);6288}62896290if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) &&6291test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) {6292clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);6293clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);6294return true;6295}6296return false;6297}62986299static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif,6300const struct brcmf_event_msg *e)6301{6302u32 event = e->event_code;6303u16 flags = e->flags;63046305if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||6306(event == BRCMF_E_DISASSOC_IND) ||6307((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {6308brcmf_dbg(CONN, "Processing link down\n");6309clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);6310clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);6311return true;6312}6313return false;6314}63156316static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,6317const struct brcmf_event_msg *e)6318{6319u32 event = e->event_code;6320u32 status = e->status;63216322if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {6323brcmf_dbg(CONN, "Processing Link %s & no network found\n",6324e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");6325return true;6326}63276328if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {6329brcmf_dbg(CONN, "Processing connecting & no network found\n");6330return true;6331}63326333if (event == BRCMF_E_PSK_SUP &&6334status != BRCMF_E_STATUS_FWSUP_COMPLETED) {6335brcmf_dbg(CONN, "Processing failed supplicant state: %u\n",6336status);6337return true;6338}63396340return false;6341}63426343static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)6344{6345struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);63466347kfree(conn_info->req_ie);6348conn_info->req_ie = NULL;6349conn_info->req_ie_len = 0;6350kfree(conn_info->resp_ie);6351conn_info->resp_ie = NULL;6352conn_info->resp_ie_len = 0;6353}63546355u8 brcmf_map_prio_to_prec(void *config, u8 prio)6356{6357struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;63586359if (!cfg)6360return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?6361(prio ^ 2) : prio;63626363/* For those AC(s) with ACM flag set to 1, convert its 4-level priority6364* to an 8-level precedence which is the same as BE's6365*/6366if (prio > PRIO_8021D_EE &&6367cfg->ac_priority[prio] == cfg->ac_priority[PRIO_8021D_BE])6368return cfg->ac_priority[prio] * 2;63696370/* Conversion of 4-level priority to 8-level precedence */6371if (prio == PRIO_8021D_BE || prio == PRIO_8021D_BK ||6372prio == PRIO_8021D_CL || prio == PRIO_8021D_VO)6373return cfg->ac_priority[prio] * 2;6374else6375return cfg->ac_priority[prio] * 2 + 1;6376}63776378u8 brcmf_map_prio_to_aci(void *config, u8 prio)6379{6380/* Prio here refers to the 802.1d priority in range of 0 to 7.6381* ACI here refers to the WLAN AC Index in range of 0 to 3.6382* This function will return ACI corresponding to input prio.6383*/6384struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;63856386if (cfg)6387return cfg->ac_priority[prio];63886389return prio;6390}63916392static void brcmf_init_wmm_prio(u8 *priority)6393{6394/* Initialize AC priority array to default6395* 802.1d priority as per following table:6396* 802.1d prio 0,3 maps to BE6397* 802.1d prio 1,2 maps to BK6398* 802.1d prio 4,5 maps to VI6399* 802.1d prio 6,7 maps to VO6400*/6401priority[0] = BRCMF_FWS_FIFO_AC_BE;6402priority[3] = BRCMF_FWS_FIFO_AC_BE;6403priority[1] = BRCMF_FWS_FIFO_AC_BK;6404priority[2] = BRCMF_FWS_FIFO_AC_BK;6405priority[4] = BRCMF_FWS_FIFO_AC_VI;6406priority[5] = BRCMF_FWS_FIFO_AC_VI;6407priority[6] = BRCMF_FWS_FIFO_AC_VO;6408priority[7] = BRCMF_FWS_FIFO_AC_VO;6409}64106411static void brcmf_wifi_prioritize_acparams(const6412struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority)6413{6414u8 aci;6415u8 aifsn;6416u8 ecwmin;6417u8 ecwmax;6418u8 acm;6419u8 ranking_basis[EDCF_AC_COUNT];6420u8 aci_prio[EDCF_AC_COUNT]; /* AC_BE, AC_BK, AC_VI, AC_VO */6421u8 index;64226423for (aci = 0; aci < EDCF_AC_COUNT; aci++, acp++) {6424aifsn = acp->ACI & EDCF_AIFSN_MASK;6425acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0;6426ecwmin = acp->ECW & EDCF_ECWMIN_MASK;6427ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;6428brcmf_dbg(CONN, "ACI %d aifsn %d acm %d ecwmin %d ecwmax %d\n",6429aci, aifsn, acm, ecwmin, ecwmax);6430/* Default AC_VO will be the lowest ranking value */6431ranking_basis[aci] = aifsn + ecwmin + ecwmax;6432/* Initialise priority starting at 0 (AC_BE) */6433aci_prio[aci] = 0;64346435/* If ACM is set, STA can't use this AC as per 802.11.6436* Change the ranking to BE6437*/6438if (aci != AC_BE && aci != AC_BK && acm == 1)6439ranking_basis[aci] = ranking_basis[AC_BE];6440}64416442/* Ranking method which works for AC priority6443* swapping when values for cwmin, cwmax and aifsn are varied6444* Compare each aci_prio against each other aci_prio6445*/6446for (aci = 0; aci < EDCF_AC_COUNT; aci++) {6447for (index = 0; index < EDCF_AC_COUNT; index++) {6448if (index != aci) {6449/* Smaller ranking value has higher priority,6450* so increment priority for each ACI which has6451* a higher ranking value6452*/6453if (ranking_basis[aci] < ranking_basis[index])6454aci_prio[aci]++;6455}6456}6457}64586459/* By now, aci_prio[] will be in range of 0 to 3.6460* Use ACI prio to get the new priority value for6461* each 802.1d traffic type, in this range.6462*/6463if (!(aci_prio[AC_BE] == aci_prio[AC_BK] &&6464aci_prio[AC_BK] == aci_prio[AC_VI] &&6465aci_prio[AC_VI] == aci_prio[AC_VO])) {6466/* 802.1d 0,3 maps to BE */6467priority[0] = aci_prio[AC_BE];6468priority[3] = aci_prio[AC_BE];64696470/* 802.1d 1,2 maps to BK */6471priority[1] = aci_prio[AC_BK];6472priority[2] = aci_prio[AC_BK];64736474/* 802.1d 4,5 maps to VO */6475priority[4] = aci_prio[AC_VI];6476priority[5] = aci_prio[AC_VI];64776478/* 802.1d 6,7 maps to VO */6479priority[6] = aci_prio[AC_VO];6480priority[7] = aci_prio[AC_VO];6481} else {6482/* Initialize to default priority */6483brcmf_init_wmm_prio(priority);6484}64856486brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n",6487priority[0], priority[1], priority[2], priority[3]);64886489brcmf_dbg(CONN, "Adj prio VI 4->%d, VI 5->%d, VO 6->%d, VO 7->%d\n",6490priority[4], priority[5], priority[6], priority[7]);6491}64926493static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,6494struct brcmf_if *ifp)6495{6496struct brcmf_pub *drvr = cfg->pub;6497struct brcmf_cfg80211_assoc_ielen_le *assoc_info;6498struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);6499struct brcmf_cfg80211_edcf_acparam edcf_acparam_info[EDCF_AC_COUNT];6500u32 req_len;6501u32 resp_len;6502s32 err = 0;65036504brcmf_clear_assoc_ies(cfg);65056506err = brcmf_fil_iovar_data_get(ifp, "assoc_info",6507cfg->extra_buf, WL_ASSOC_INFO_MAX);6508if (err) {6509bphy_err(drvr, "could not get assoc info (%d)\n", err);6510return err;6511}6512assoc_info =6513(struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;6514req_len = le32_to_cpu(assoc_info->req_len);6515resp_len = le32_to_cpu(assoc_info->resp_len);6516if (req_len > WL_EXTRA_BUF_MAX || resp_len > WL_EXTRA_BUF_MAX) {6517bphy_err(drvr, "invalid lengths in assoc info: req %u resp %u\n",6518req_len, resp_len);6519return -EINVAL;6520}6521if (req_len) {6522err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",6523cfg->extra_buf,6524WL_ASSOC_INFO_MAX);6525if (err) {6526bphy_err(drvr, "could not get assoc req (%d)\n", err);6527return err;6528}6529conn_info->req_ie_len = req_len;6530conn_info->req_ie =6531kmemdup(cfg->extra_buf, conn_info->req_ie_len,6532GFP_KERNEL);6533if (!conn_info->req_ie)6534conn_info->req_ie_len = 0;6535} else {6536conn_info->req_ie_len = 0;6537conn_info->req_ie = NULL;6538}6539if (resp_len) {6540err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",6541cfg->extra_buf,6542WL_ASSOC_INFO_MAX);6543if (err) {6544bphy_err(drvr, "could not get assoc resp (%d)\n", err);6545return err;6546}6547conn_info->resp_ie_len = resp_len;6548conn_info->resp_ie =6549kmemdup(cfg->extra_buf, conn_info->resp_ie_len,6550GFP_KERNEL);6551if (!conn_info->resp_ie)6552conn_info->resp_ie_len = 0;65536554err = brcmf_fil_iovar_data_get(ifp, "wme_ac_sta",6555edcf_acparam_info,6556sizeof(edcf_acparam_info));6557if (err) {6558brcmf_err("could not get wme_ac_sta (%d)\n", err);6559return err;6560}65616562brcmf_wifi_prioritize_acparams(edcf_acparam_info,6563cfg->ac_priority);6564} else {6565conn_info->resp_ie_len = 0;6566conn_info->resp_ie = NULL;6567}6568brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",6569conn_info->req_ie_len, conn_info->resp_ie_len);65706571return err;6572}65736574static s326575brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,6576struct net_device *ndev,6577const struct brcmf_event_msg *e)6578{6579struct brcmf_if *ifp = netdev_priv(ndev);6580struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;6581struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);6582struct wiphy *wiphy = cfg_to_wiphy(cfg);6583struct ieee80211_channel *notify_channel = NULL;6584struct ieee80211_supported_band *band;6585struct brcmf_bss_info_le *bi;6586struct brcmu_chan ch;6587struct cfg80211_roam_info roam_info = {};6588u32 freq;6589s32 err = 0;6590u8 *buf;65916592brcmf_dbg(TRACE, "Enter\n");65936594brcmf_get_assoc_ies(cfg, ifp);6595memcpy(profile->bssid, e->addr, ETH_ALEN);6596brcmf_update_bss_info(cfg, ifp);65976598buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);6599if (buf == NULL) {6600err = -ENOMEM;6601goto done;6602}66036604/* data sent to dongle has to be little endian */6605*(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);6606err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,6607buf, WL_BSS_INFO_MAX);66086609if (err)6610goto done;66116612bi = (struct brcmf_bss_info_le *)(buf + 4);6613ch.chspec = le16_to_cpu(bi->chanspec);6614cfg->d11inf.decchspec(&ch);66156616if (ch.band == BRCMU_CHAN_BAND_2G)6617band = wiphy->bands[NL80211_BAND_2GHZ];6618else6619band = wiphy->bands[NL80211_BAND_5GHZ];66206621freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);6622notify_channel = ieee80211_get_channel(wiphy, freq);66236624done:6625kfree(buf);66266627roam_info.links[0].channel = notify_channel;6628roam_info.links[0].bssid = profile->bssid;6629roam_info.req_ie = conn_info->req_ie;6630roam_info.req_ie_len = conn_info->req_ie_len;6631roam_info.resp_ie = conn_info->resp_ie;6632roam_info.resp_ie_len = conn_info->resp_ie_len;66336634cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);6635brcmf_dbg(CONN, "Report roaming result\n");66366637if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) {6638cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL);6639brcmf_dbg(CONN, "Report port authorized\n");6640}66416642set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);6643brcmf_dbg(TRACE, "Exit\n");6644return err;6645}66466647static s326648brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,6649struct net_device *ndev, const struct brcmf_event_msg *e,6650bool completed)6651{6652struct brcmf_if *ifp = netdev_priv(ndev);6653struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;6654struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);6655struct cfg80211_connect_resp_params conn_params;66566657brcmf_dbg(TRACE, "Enter\n");66586659if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,6660&ifp->vif->sme_state)) {6661memset(&conn_params, 0, sizeof(conn_params));6662if (completed) {6663brcmf_get_assoc_ies(cfg, ifp);6664brcmf_update_bss_info(cfg, ifp);6665set_bit(BRCMF_VIF_STATUS_CONNECTED,6666&ifp->vif->sme_state);6667conn_params.status = WLAN_STATUS_SUCCESS;6668} else {6669clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS,6670&ifp->vif->sme_state);6671clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS,6672&ifp->vif->sme_state);6673conn_params.status = WLAN_STATUS_AUTH_TIMEOUT;6674}6675conn_params.links[0].bssid = profile->bssid;6676conn_params.req_ie = conn_info->req_ie;6677conn_params.req_ie_len = conn_info->req_ie_len;6678conn_params.resp_ie = conn_info->resp_ie;6679conn_params.resp_ie_len = conn_info->resp_ie_len;6680cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL);6681brcmf_dbg(CONN, "Report connect result - connection %s\n",6682completed ? "succeeded" : "failed");6683}6684brcmf_dbg(TRACE, "Exit\n");6685return 0;6686}66876688static s326689brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,6690struct net_device *ndev,6691const struct brcmf_event_msg *e, void *data)6692{6693struct brcmf_pub *drvr = cfg->pub;6694static int generation;6695u32 event = e->event_code;6696u32 reason = e->reason;6697struct station_info *sinfo;66986699brcmf_dbg(CONN, "event %s (%u), reason %d\n",6700brcmf_fweh_event_name(event), event, reason);6701if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&6702ndev != cfg_to_ndev(cfg)) {6703brcmf_dbg(CONN, "AP mode link down\n");6704complete(&cfg->vif_disabled);6705return 0;6706}67076708if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&6709(reason == BRCMF_E_STATUS_SUCCESS)) {6710if (!data) {6711bphy_err(drvr, "No IEs present in ASSOC/REASSOC_IND\n");6712return -EINVAL;6713}67146715sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);6716if (!sinfo)6717return -ENOMEM;67186719sinfo->assoc_req_ies = data;6720sinfo->assoc_req_ies_len = e->datalen;6721generation++;6722sinfo->generation = generation;6723cfg80211_new_sta(ndev, e->addr, sinfo, GFP_KERNEL);67246725kfree(sinfo);6726} else if ((event == BRCMF_E_DISASSOC_IND) ||6727(event == BRCMF_E_DEAUTH_IND) ||6728(event == BRCMF_E_DEAUTH)) {6729cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);6730}6731return 0;6732}67336734static s326735brcmf_notify_connect_status(struct brcmf_if *ifp,6736const struct brcmf_event_msg *e, void *data)6737{6738struct brcmf_cfg80211_info *cfg = ifp->drvr->config;6739struct net_device *ndev = ifp->ndev;6740struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;6741struct ieee80211_channel *chan;6742s32 err = 0;67436744if ((e->event_code == BRCMF_E_DEAUTH) ||6745(e->event_code == BRCMF_E_DEAUTH_IND) ||6746(e->event_code == BRCMF_E_DISASSOC_IND) ||6747((e->event_code == BRCMF_E_LINK) && (!e->flags))) {6748#if defined(__linux__)6749brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);6750#elif defined(__FreeBSD__)6751brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, e->addr);6752#endif6753}67546755if (brcmf_is_apmode(ifp->vif)) {6756err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);6757} else if (brcmf_is_linkup(ifp->vif, e)) {6758brcmf_dbg(CONN, "Linkup\n");6759if (brcmf_is_ibssmode(ifp->vif)) {6760brcmf_inform_ibss(cfg, ndev, e->addr);6761chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);6762memcpy(profile->bssid, e->addr, ETH_ALEN);6763cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);6764clear_bit(BRCMF_VIF_STATUS_CONNECTING,6765&ifp->vif->sme_state);6766set_bit(BRCMF_VIF_STATUS_CONNECTED,6767&ifp->vif->sme_state);6768} else6769brcmf_bss_connect_done(cfg, ndev, e, true);6770brcmf_net_setcarrier(ifp, true);6771} else if (brcmf_is_linkdown(ifp->vif, e)) {6772brcmf_dbg(CONN, "Linkdown\n");6773if (!brcmf_is_ibssmode(ifp->vif) &&6774(test_bit(BRCMF_VIF_STATUS_CONNECTED,6775&ifp->vif->sme_state) ||6776test_bit(BRCMF_VIF_STATUS_CONNECTING,6777&ifp->vif->sme_state))) {6778if (test_bit(BRCMF_VIF_STATUS_CONNECTED,6779&ifp->vif->sme_state) &&6780memcmp(profile->bssid, e->addr, ETH_ALEN))6781return err;67826783brcmf_bss_connect_done(cfg, ndev, e, false);6784brcmf_link_down(ifp->vif,6785brcmf_map_fw_linkdown_reason(e),6786e->event_code &6787(BRCMF_E_DEAUTH_IND |6788BRCMF_E_DISASSOC_IND)6789? false : true);6790brcmf_init_prof(ndev_to_prof(ndev));6791if (ndev != cfg_to_ndev(cfg))6792complete(&cfg->vif_disabled);6793brcmf_net_setcarrier(ifp, false);6794}6795} else if (brcmf_is_nonetwork(cfg, e)) {6796if (brcmf_is_ibssmode(ifp->vif))6797clear_bit(BRCMF_VIF_STATUS_CONNECTING,6798&ifp->vif->sme_state);6799else6800brcmf_bss_connect_done(cfg, ndev, e, false);6801}68026803return err;6804}68056806static s326807brcmf_notify_roaming_status(struct brcmf_if *ifp,6808const struct brcmf_event_msg *e, void *data)6809{6810struct brcmf_cfg80211_info *cfg = ifp->drvr->config;6811u32 event = e->event_code;6812u32 status = e->status;68136814if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {6815if (test_bit(BRCMF_VIF_STATUS_CONNECTED,6816&ifp->vif->sme_state)) {6817brcmf_bss_roaming_done(cfg, ifp->ndev, e);6818} else {6819brcmf_bss_connect_done(cfg, ifp->ndev, e, true);6820brcmf_net_setcarrier(ifp, true);6821}6822}68236824return 0;6825}68266827static s326828brcmf_notify_mic_status(struct brcmf_if *ifp,6829const struct brcmf_event_msg *e, void *data)6830{6831u16 flags = e->flags;6832enum nl80211_key_type key_type;68336834if (flags & BRCMF_EVENT_MSG_GROUP)6835key_type = NL80211_KEYTYPE_GROUP;6836else6837key_type = NL80211_KEYTYPE_PAIRWISE;68386839#if defined(__linux__)6840cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,6841#elif defined(__FreeBSD__)6842cfg80211_michael_mic_failure(ifp->ndev, e->addr, key_type, -1,6843#endif6844NULL, GFP_KERNEL);68456846return 0;6847}68486849static s32 brcmf_notify_rssi(struct brcmf_if *ifp,6850const struct brcmf_event_msg *e, void *data)6851{6852struct brcmf_cfg80211_vif *vif = ifp->vif;6853struct brcmf_rssi_be *info = data;6854s32 rssi, snr = 0, noise = 0;6855s32 low, high, last;68566857if (e->datalen >= sizeof(*info)) {6858rssi = be32_to_cpu(info->rssi);6859snr = be32_to_cpu(info->snr);6860noise = be32_to_cpu(info->noise);6861} else if (e->datalen >= sizeof(rssi)) {6862rssi = be32_to_cpu(*(__be32 *)data);6863} else {6864brcmf_err("insufficient RSSI event data\n");6865return 0;6866}68676868low = vif->cqm_rssi_low;6869high = vif->cqm_rssi_high;6870last = vif->cqm_rssi_last;68716872brcmf_dbg(TRACE, "rssi=%d snr=%d noise=%d low=%d high=%d last=%d\n",6873rssi, snr, noise, low, high, last);68746875vif->cqm_rssi_last = rssi;68766877if (rssi <= low || rssi == 0) {6878brcmf_dbg(INFO, "LOW rssi=%d\n", rssi);6879cfg80211_cqm_rssi_notify(ifp->ndev,6880NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,6881rssi, GFP_KERNEL);6882} else if (rssi > high) {6883brcmf_dbg(INFO, "HIGH rssi=%d\n", rssi);6884cfg80211_cqm_rssi_notify(ifp->ndev,6885NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,6886rssi, GFP_KERNEL);6887}68886889return 0;6890}68916892static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,6893const struct brcmf_event_msg *e, void *data)6894{6895struct brcmf_cfg80211_info *cfg = ifp->drvr->config;6896struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;6897struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;6898struct brcmf_cfg80211_vif *vif;68996900brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",6901ifevent->action, ifevent->flags, ifevent->ifidx,6902ifevent->bsscfgidx);69036904spin_lock(&event->vif_event_lock);6905event->action = ifevent->action;6906vif = event->vif;69076908switch (ifevent->action) {6909case BRCMF_E_IF_ADD:6910/* waiting process may have timed out */6911if (!cfg->vif_event.vif) {6912spin_unlock(&event->vif_event_lock);6913return -EBADF;6914}69156916ifp->vif = vif;6917vif->ifp = ifp;6918if (ifp->ndev) {6919vif->wdev.netdev = ifp->ndev;6920ifp->ndev->ieee80211_ptr = &vif->wdev;6921SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));6922}6923spin_unlock(&event->vif_event_lock);6924wake_up(&event->vif_wq);6925return 0;69266927case BRCMF_E_IF_DEL:6928spin_unlock(&event->vif_event_lock);6929/* event may not be upon user request */6930if (brcmf_cfg80211_vif_event_armed(cfg))6931wake_up(&event->vif_wq);6932return 0;69336934case BRCMF_E_IF_CHANGE:6935spin_unlock(&event->vif_event_lock);6936wake_up(&event->vif_wq);6937return 0;69386939default:6940spin_unlock(&event->vif_event_lock);6941break;6942}6943return -EINVAL;6944}69456946static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)6947{6948conf->frag_threshold = (u32)-1;6949conf->rts_threshold = (u32)-1;6950conf->retry_short = (u32)-1;6951conf->retry_long = (u32)-1;6952}69536954static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)6955{6956brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,6957brcmf_notify_connect_status);6958brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,6959brcmf_notify_connect_status);6960brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,6961brcmf_notify_connect_status);6962brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,6963brcmf_notify_connect_status);6964brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,6965brcmf_notify_connect_status);6966brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,6967brcmf_notify_connect_status);6968brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,6969brcmf_notify_roaming_status);6970brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,6971brcmf_notify_mic_status);6972brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,6973brcmf_notify_connect_status);6974brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,6975brcmf_notify_sched_scan_results);6976brcmf_fweh_register(cfg->pub, BRCMF_E_IF,6977brcmf_notify_vif_event);6978brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,6979brcmf_p2p_notify_rx_mgmt_p2p_probereq);6980brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,6981brcmf_p2p_notify_listen_complete);6982brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,6983brcmf_p2p_notify_action_frame_rx);6984brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,6985brcmf_p2p_notify_action_tx_complete);6986brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,6987brcmf_p2p_notify_action_tx_complete);6988brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,6989brcmf_notify_connect_status);6990brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi);69916992brcmf_fwvid_register_event_handlers(cfg->pub);6993}69946995static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)6996{6997kfree(cfg->conf);6998cfg->conf = NULL;6999kfree(cfg->extra_buf);7000cfg->extra_buf = NULL;7001kfree(cfg->wowl.nd);7002cfg->wowl.nd = NULL;7003kfree(cfg->wowl.nd_info);7004cfg->wowl.nd_info = NULL;7005kfree(cfg->escan_info.escan_buf);7006cfg->escan_info.escan_buf = NULL;7007}70087009static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)7010{7011cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);7012if (!cfg->conf)7013goto init_priv_mem_out;7014cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);7015if (!cfg->extra_buf)7016goto init_priv_mem_out;7017cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);7018if (!cfg->wowl.nd)7019goto init_priv_mem_out;7020cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +7021sizeof(struct cfg80211_wowlan_nd_match *),7022GFP_KERNEL);7023if (!cfg->wowl.nd_info)7024goto init_priv_mem_out;7025cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);7026if (!cfg->escan_info.escan_buf)7027goto init_priv_mem_out;70287029return 0;70307031init_priv_mem_out:7032brcmf_deinit_priv_mem(cfg);70337034return -ENOMEM;7035}70367037static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)7038{7039s32 err = 0;70407041cfg->scan_request = NULL;7042cfg->pwr_save = true;7043cfg->dongle_up = false; /* dongle is not up yet */7044err = brcmf_init_priv_mem(cfg);7045if (err)7046return err;7047brcmf_register_event_handlers(cfg);7048mutex_init(&cfg->usr_sync);7049brcmf_init_escan(cfg);7050brcmf_init_conf(cfg->conf);7051brcmf_init_wmm_prio(cfg->ac_priority);7052init_completion(&cfg->vif_disabled);7053return err;7054}70557056static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)7057{7058cfg->dongle_up = false; /* dongle down */7059brcmf_abort_scanning(cfg);7060brcmf_deinit_priv_mem(cfg);7061brcmf_clear_assoc_ies(cfg);7062}70637064static void init_vif_event(struct brcmf_cfg80211_vif_event *event)7065{7066init_waitqueue_head(&event->vif_wq);7067spin_lock_init(&event->vif_event_lock);7068}70697070static s32 brcmf_dongle_roam(struct brcmf_if *ifp)7071{7072struct brcmf_pub *drvr = ifp->drvr;7073s32 err;7074u32 bcn_timeout;7075__le32 roamtrigger[2];7076__le32 roam_delta[2];70777078/* Configure beacon timeout value based upon roaming setting */7079if (ifp->drvr->settings->roamoff)7080bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;7081else7082bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;7083err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);7084if (err) {7085bphy_err(drvr, "bcn_timeout error (%d)\n", err);7086goto roam_setup_done;7087}70887089/* Enable/Disable built-in roaming to allow supplicant to take care of7090* roaming.7091*/7092brcmf_dbg(INFO, "Internal Roaming = %s\n",7093ifp->drvr->settings->roamoff ? "Off" : "On");7094err = brcmf_fil_iovar_int_set(ifp, "roam_off",7095ifp->drvr->settings->roamoff);7096if (err) {7097bphy_err(drvr, "roam_off error (%d)\n", err);7098goto roam_setup_done;7099}71007101roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);7102roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);7103err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,7104(void *)roamtrigger, sizeof(roamtrigger));7105if (err)7106bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err);71077108roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);7109roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);7110err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,7111(void *)roam_delta, sizeof(roam_delta));7112if (err)7113bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err);71147115return 0;71167117roam_setup_done:7118return err;7119}71207121static s327122brcmf_dongle_scantime(struct brcmf_if *ifp)7123{7124struct brcmf_pub *drvr = ifp->drvr;7125s32 err = 0;71267127err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,7128BRCMF_SCAN_CHANNEL_TIME);7129if (err) {7130bphy_err(drvr, "Scan assoc time error (%d)\n", err);7131goto dongle_scantime_out;7132}7133err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,7134BRCMF_SCAN_UNASSOC_TIME);7135if (err) {7136bphy_err(drvr, "Scan unassoc time error (%d)\n", err);7137goto dongle_scantime_out;7138}71397140err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,7141BRCMF_SCAN_PASSIVE_TIME);7142if (err) {7143bphy_err(drvr, "Scan passive time error (%d)\n", err);7144goto dongle_scantime_out;7145}71467147dongle_scantime_out:7148return err;7149}71507151static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,7152struct brcmu_chan *ch)7153{7154u32 ht40_flag;71557156ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;7157if (ch->sb == BRCMU_CHAN_SB_U) {7158if (ht40_flag == IEEE80211_CHAN_NO_HT40)7159channel->flags &= ~IEEE80211_CHAN_NO_HT40;7160channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;7161} else {7162/* It should be one of7163* IEEE80211_CHAN_NO_HT40 or7164* IEEE80211_CHAN_NO_HT40PLUS7165*/7166channel->flags &= ~IEEE80211_CHAN_NO_HT40;7167if (ht40_flag == IEEE80211_CHAN_NO_HT40)7168channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;7169}7170}71717172static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,7173u32 bw_cap[])7174{7175struct wiphy *wiphy = cfg_to_wiphy(cfg);7176struct brcmf_pub *drvr = cfg->pub;7177struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);7178struct ieee80211_supported_band *band;7179struct ieee80211_channel *channel;7180struct brcmf_chanspec_list *list;7181struct brcmu_chan ch;7182int err;7183u8 *pbuf;7184u32 i, j;7185u32 total;7186u32 chaninfo;71877188pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);71897190if (pbuf == NULL)7191return -ENOMEM;71927193list = (struct brcmf_chanspec_list *)pbuf;71947195err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,7196BRCMF_DCMD_MEDLEN);7197if (err) {7198bphy_err(drvr, "get chanspecs error (%d)\n", err);7199goto fail_pbuf;7200}72017202band = wiphy->bands[NL80211_BAND_2GHZ];7203if (band)7204for (i = 0; i < band->n_channels; i++)7205band->channels[i].flags = IEEE80211_CHAN_DISABLED;7206band = wiphy->bands[NL80211_BAND_5GHZ];7207if (band)7208for (i = 0; i < band->n_channels; i++)7209band->channels[i].flags = IEEE80211_CHAN_DISABLED;72107211total = le32_to_cpu(list->count);7212if (total > BRCMF_MAX_CHANSPEC_LIST) {7213bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",7214total);7215err = -EINVAL;7216goto fail_pbuf;7217}72187219for (i = 0; i < total; i++) {7220ch.chspec = (u16)le32_to_cpu(list->element[i]);7221cfg->d11inf.decchspec(&ch);72227223if (ch.band == BRCMU_CHAN_BAND_2G) {7224band = wiphy->bands[NL80211_BAND_2GHZ];7225} else if (ch.band == BRCMU_CHAN_BAND_5G) {7226band = wiphy->bands[NL80211_BAND_5GHZ];7227} else {7228bphy_err(drvr, "Invalid channel Spec. 0x%x.\n",7229ch.chspec);7230continue;7231}7232if (!band)7233continue;7234if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&7235ch.bw == BRCMU_CHAN_BW_40)7236continue;7237if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&7238ch.bw == BRCMU_CHAN_BW_80)7239continue;72407241channel = NULL;7242for (j = 0; j < band->n_channels; j++) {7243if (band->channels[j].hw_value == ch.control_ch_num) {7244channel = &band->channels[j];7245break;7246}7247}7248if (!channel) {7249/* It seems firmware supports some channel we never7250* considered. Something new in IEEE standard?7251*/7252bphy_err(drvr, "Ignoring unexpected firmware channel %d\n",7253ch.control_ch_num);7254continue;7255}72567257if (channel->orig_flags & IEEE80211_CHAN_DISABLED)7258continue;72597260/* assuming the chanspecs order is HT20,7261* HT40 upper, HT40 lower, and VHT80.7262*/7263switch (ch.bw) {7264case BRCMU_CHAN_BW_160:7265channel->flags &= ~IEEE80211_CHAN_NO_160MHZ;7266break;7267case BRCMU_CHAN_BW_80:7268channel->flags &= ~IEEE80211_CHAN_NO_80MHZ;7269break;7270case BRCMU_CHAN_BW_40:7271brcmf_update_bw40_channel_flag(channel, &ch);7272break;7273default:7274wiphy_warn(wiphy, "Firmware reported unsupported bandwidth %d\n",7275ch.bw);7276fallthrough;7277case BRCMU_CHAN_BW_20:7278/* enable the channel and disable other bandwidths7279* for now as mentioned order assure they are enabled7280* for subsequent chanspecs.7281*/7282channel->flags = IEEE80211_CHAN_NO_HT40 |7283IEEE80211_CHAN_NO_80MHZ |7284IEEE80211_CHAN_NO_160MHZ;7285ch.bw = BRCMU_CHAN_BW_20;7286cfg->d11inf.encchspec(&ch);7287chaninfo = ch.chspec;7288err = brcmf_fil_bsscfg_int_query(ifp, "per_chan_info",7289&chaninfo);7290if (!err) {7291if (chaninfo & WL_CHAN_RADAR)7292channel->flags |=7293(IEEE80211_CHAN_RADAR |7294IEEE80211_CHAN_NO_IR);7295if (chaninfo & WL_CHAN_PASSIVE)7296channel->flags |=7297IEEE80211_CHAN_NO_IR;7298}7299}7300}73017302fail_pbuf:7303kfree(pbuf);7304return err;7305}73067307static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)7308{7309struct brcmf_pub *drvr = cfg->pub;7310struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);7311struct ieee80211_supported_band *band;7312struct brcmf_fil_bwcap_le band_bwcap;7313struct brcmf_chanspec_list *list;7314u8 *pbuf;7315u32 val;7316int err;7317struct brcmu_chan ch;7318u32 num_chan;7319int i, j;73207321/* verify support for bw_cap command */7322val = WLC_BAND_5G;7323err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &val);73247325if (!err) {7326/* only set 2G bandwidth using bw_cap command */7327band_bwcap.band = cpu_to_le32(WLC_BAND_2G);7328band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);7329err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,7330sizeof(band_bwcap));7331} else {7332brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");7333val = WLC_N_BW_40ALL;7334err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);7335}73367337if (!err) {7338/* update channel info in 2G band */7339pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);73407341if (pbuf == NULL)7342return -ENOMEM;73437344ch.band = BRCMU_CHAN_BAND_2G;7345ch.bw = BRCMU_CHAN_BW_40;7346ch.sb = BRCMU_CHAN_SB_NONE;7347ch.chnum = 0;7348cfg->d11inf.encchspec(&ch);73497350/* pass encoded chanspec in query */7351*(__le16 *)pbuf = cpu_to_le16(ch.chspec);73527353err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,7354BRCMF_DCMD_MEDLEN);7355if (err) {7356bphy_err(drvr, "get chanspecs error (%d)\n", err);7357kfree(pbuf);7358return err;7359}73607361band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];7362list = (struct brcmf_chanspec_list *)pbuf;7363num_chan = le32_to_cpu(list->count);7364if (num_chan > BRCMF_MAX_CHANSPEC_LIST) {7365bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",7366num_chan);7367kfree(pbuf);7368return -EINVAL;7369}73707371for (i = 0; i < num_chan; i++) {7372ch.chspec = (u16)le32_to_cpu(list->element[i]);7373cfg->d11inf.decchspec(&ch);7374if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))7375continue;7376if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))7377continue;7378for (j = 0; j < band->n_channels; j++) {7379if (band->channels[j].hw_value == ch.control_ch_num)7380break;7381}7382if (WARN_ON(j == band->n_channels))7383continue;73847385brcmf_update_bw40_channel_flag(&band->channels[j], &ch);7386}7387kfree(pbuf);7388}7389return err;7390}73917392static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])7393{7394struct brcmf_pub *drvr = ifp->drvr;7395u32 band, mimo_bwcap;7396int err;73977398band = WLC_BAND_2G;7399err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);7400if (!err) {7401bw_cap[NL80211_BAND_2GHZ] = band;7402band = WLC_BAND_5G;7403err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);7404if (!err) {7405bw_cap[NL80211_BAND_5GHZ] = band;7406return;7407}7408WARN_ON(1);7409return;7410}7411brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");7412err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);7413if (err)7414/* assume 20MHz if firmware does not give a clue */7415mimo_bwcap = WLC_N_BW_20ALL;74167417switch (mimo_bwcap) {7418case WLC_N_BW_40ALL:7419bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;7420fallthrough;7421case WLC_N_BW_20IN2G_40IN5G:7422bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;7423fallthrough;7424case WLC_N_BW_20ALL:7425bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;7426bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;7427break;7428default:7429bphy_err(drvr, "invalid mimo_bw_cap value\n");7430}7431}74327433static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,7434u32 bw_cap[2], u32 nchain)7435{7436band->ht_cap.ht_supported = true;7437if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {7438band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;7439band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;7440}7441band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;7442band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;7443band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;7444band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;7445memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);7446band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;7447}74487449static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)7450{7451u16 mcs_map;7452int i;74537454for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)7455mcs_map = (mcs_map << 2) | supp;74567457return cpu_to_le16(mcs_map);7458}74597460static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,7461u32 bw_cap[2], u32 nchain, u32 txstreams,7462u32 txbf_bfe_cap, u32 txbf_bfr_cap)7463{7464__le16 mcs_map;74657466/* not allowed in 2.4G band */7467if (band->band == NL80211_BAND_2GHZ)7468return;74697470band->vht_cap.vht_supported = true;7471/* 80MHz is mandatory */7472band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;7473if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {7474band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;7475band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;7476}7477/* all support 256-QAM */7478mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);7479band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;7480band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;74817482/* Beamforming support information */7483if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)7484band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;7485if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)7486band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;7487if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)7488band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;7489if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)7490band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;74917492if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {7493band->vht_cap.cap |=7494(2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);7495band->vht_cap.cap |= ((txstreams - 1) <<7496IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);7497band->vht_cap.cap |=7498IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;7499}7500}75017502static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)7503{7504struct brcmf_pub *drvr = cfg->pub;7505struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);7506struct wiphy *wiphy = cfg_to_wiphy(cfg);7507u32 nmode;7508u32 vhtmode = 0;7509u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };7510u32 rxchain;7511u32 nchain;7512int err;7513s32 i;7514struct ieee80211_supported_band *band;7515u32 txstreams = 0;7516u32 txbf_bfe_cap = 0;7517u32 txbf_bfr_cap = 0;75187519(void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);7520err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);7521if (err) {7522bphy_err(drvr, "nmode error (%d)\n", err);7523} else {7524brcmf_get_bwcap(ifp, bw_cap);7525}7526brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",7527nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],7528bw_cap[NL80211_BAND_5GHZ]);75297530err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);7531if (err) {7532/* rxchain unsupported by firmware of older chips */7533if (err == -EBADE)7534bphy_info_once(drvr, "rxchain unsupported\n");7535else7536bphy_err(drvr, "rxchain error (%d)\n", err);75377538nchain = 1;7539} else {7540for (nchain = 0; rxchain; nchain++)7541rxchain = rxchain & (rxchain - 1);7542}7543brcmf_dbg(INFO, "nchain=%d\n", nchain);75447545err = brcmf_construct_chaninfo(cfg, bw_cap);7546if (err) {7547bphy_err(drvr, "brcmf_construct_chaninfo failed (%d)\n", err);7548return err;7549}75507551if (vhtmode) {7552(void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);7553(void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",7554&txbf_bfe_cap);7555(void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",7556&txbf_bfr_cap);7557}75587559for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {7560band = wiphy->bands[i];7561if (band == NULL)7562continue;75637564if (nmode)7565brcmf_update_ht_cap(band, bw_cap, nchain);7566if (vhtmode)7567brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,7568txbf_bfe_cap, txbf_bfr_cap);7569}75707571return 0;7572}75737574static const struct ieee80211_txrx_stypes7575brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {7576[NL80211_IFTYPE_STATION] = {7577.tx = 0xffff,7578.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |7579BIT(IEEE80211_STYPE_AUTH >> 4) |7580BIT(IEEE80211_STYPE_PROBE_REQ >> 4)7581},7582[NL80211_IFTYPE_P2P_CLIENT] = {7583.tx = 0xffff,7584.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |7585BIT(IEEE80211_STYPE_PROBE_REQ >> 4)7586},7587[NL80211_IFTYPE_P2P_GO] = {7588.tx = 0xffff,7589.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |7590BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |7591BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |7592BIT(IEEE80211_STYPE_DISASSOC >> 4) |7593BIT(IEEE80211_STYPE_AUTH >> 4) |7594BIT(IEEE80211_STYPE_DEAUTH >> 4) |7595BIT(IEEE80211_STYPE_ACTION >> 4)7596},7597[NL80211_IFTYPE_P2P_DEVICE] = {7598.tx = 0xffff,7599.rx = BIT(IEEE80211_STYPE_ACTION >> 4) |7600BIT(IEEE80211_STYPE_PROBE_REQ >> 4)7601},7602[NL80211_IFTYPE_AP] = {7603.tx = 0xffff,7604.rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |7605BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |7606BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |7607BIT(IEEE80211_STYPE_DISASSOC >> 4) |7608BIT(IEEE80211_STYPE_AUTH >> 4) |7609BIT(IEEE80211_STYPE_DEAUTH >> 4) |7610BIT(IEEE80211_STYPE_ACTION >> 4)7611}7612};76137614/**7615* brcmf_setup_ifmodes() - determine interface modes and combinations.7616*7617* @wiphy: wiphy object.7618* @ifp: interface object needed for feat module api.7619*7620* The interface modes and combinations are determined dynamically here7621* based on firmware functionality.7622*7623* no p2p and no mbss:7624*7625* #STA <= 1, #AP <= 1, channels = 1, 2 total7626*7627* no p2p and mbss:7628*7629* #STA <= 1, #AP <= 1, channels = 1, 2 total7630* #AP <= 4, matching BI, channels = 1, 4 total7631*7632* no p2p and rsdb:7633* #STA <= 1, #AP <= 2, channels = 2, 4 total7634*7635* p2p, no mchan, and mbss:7636*7637* #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total7638* #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total7639* #AP <= 4, matching BI, channels = 1, 4 total7640*7641* p2p, mchan, and mbss:7642*7643* #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total7644* #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total7645* #AP <= 4, matching BI, channels = 1, 4 total7646*7647* p2p, rsdb, and no mbss:7648* #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2,7649* channels = 2, 4 total7650*7651* Return: 0 on success, negative errno on failure7652*/7653static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)7654{7655struct ieee80211_iface_combination *combo = NULL;7656struct ieee80211_iface_limit *c0_limits = NULL;7657struct ieee80211_iface_limit *p2p_limits = NULL;7658struct ieee80211_iface_limit *mbss_limits = NULL;7659bool mon_flag, mbss, p2p, rsdb, mchan;7660int i, c, n_combos, n_limits;76617662mon_flag = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FLAG);7663mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);7664p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);7665rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB);7666mchan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN);76677668n_combos = 1 + !!(p2p && !rsdb) + !!mbss;7669combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);7670if (!combo)7671goto err;76727673wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |7674BIT(NL80211_IFTYPE_ADHOC) |7675BIT(NL80211_IFTYPE_AP);7676if (mon_flag)7677wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);7678if (p2p)7679wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |7680BIT(NL80211_IFTYPE_P2P_GO) |7681BIT(NL80211_IFTYPE_P2P_DEVICE);76827683c = 0;7684i = 0;7685n_limits = 1 + mon_flag + (p2p ? 2 : 0) + (rsdb || !p2p);7686c0_limits = kcalloc(n_limits, sizeof(*c0_limits), GFP_KERNEL);7687if (!c0_limits)7688goto err;76897690combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan));7691c0_limits[i].max = 1 + (p2p && mchan);7692c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);7693if (mon_flag) {7694c0_limits[i].max = 1;7695c0_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);7696}7697if (p2p) {7698c0_limits[i].max = 1;7699c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);7700c0_limits[i].max = 1 + rsdb;7701c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |7702BIT(NL80211_IFTYPE_P2P_GO);7703}7704if (p2p && rsdb) {7705c0_limits[i].max = 2;7706c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);7707combo[c].max_interfaces = 4;7708} else if (p2p) {7709combo[c].max_interfaces = i;7710} else if (rsdb) {7711c0_limits[i].max = 2;7712c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);7713combo[c].max_interfaces = 3;7714} else {7715c0_limits[i].max = 1;7716c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);7717combo[c].max_interfaces = i;7718}7719combo[c].n_limits = i;7720combo[c].limits = c0_limits;77217722if (p2p && !rsdb) {7723c++;7724i = 0;7725p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);7726if (!p2p_limits)7727goto err;7728p2p_limits[i].max = 1;7729p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);7730p2p_limits[i].max = 1;7731p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);7732p2p_limits[i].max = 1;7733p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);7734p2p_limits[i].max = 1;7735p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);7736combo[c].num_different_channels = 1;7737combo[c].max_interfaces = i;7738combo[c].n_limits = i;7739combo[c].limits = p2p_limits;7740}77417742if (mbss) {7743c++;7744i = 0;7745n_limits = 1 + mon_flag;7746mbss_limits = kcalloc(n_limits, sizeof(*mbss_limits),7747GFP_KERNEL);7748if (!mbss_limits)7749goto err;7750mbss_limits[i].max = 4;7751mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP);7752if (mon_flag) {7753mbss_limits[i].max = 1;7754mbss_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);7755}7756combo[c].beacon_int_infra_match = true;7757combo[c].num_different_channels = 1;7758combo[c].max_interfaces = 4 + mon_flag;7759combo[c].n_limits = i;7760combo[c].limits = mbss_limits;7761}77627763wiphy->n_iface_combinations = n_combos;7764wiphy->iface_combinations = combo;7765return 0;77667767err:7768kfree(c0_limits);7769kfree(p2p_limits);7770kfree(mbss_limits);7771kfree(combo);7772return -ENOMEM;7773}77747775#ifdef CONFIG_PM7776static const struct wiphy_wowlan_support brcmf_wowlan_support = {7777.flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,7778.n_patterns = BRCMF_WOWL_MAXPATTERNS,7779.pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,7780.pattern_min_len = 1,7781.max_pkt_offset = 1500,7782};7783#endif77847785static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)7786{7787#ifdef CONFIG_PM7788struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);7789struct brcmf_pub *drvr = cfg->pub;7790struct wiphy_wowlan_support *wowl;77917792wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),7793GFP_KERNEL);7794if (!wowl) {7795bphy_err(drvr, "only support basic wowlan features\n");7796wiphy->wowlan = &brcmf_wowlan_support;7797return;7798}77997800if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {7801if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {7802wowl->flags |= WIPHY_WOWLAN_NET_DETECT;7803wowl->max_nd_match_sets = BRCMF_PNO_MAX_PFN_COUNT;7804init_waitqueue_head(&cfg->wowl.nd_data_wait);7805}7806}7807if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {7808wowl->flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;7809wowl->flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;7810}78117812wiphy->wowlan = wowl;7813#endif7814}78157816static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)7817{7818struct brcmf_pub *drvr = ifp->drvr;7819const struct ieee80211_iface_combination *combo;7820struct ieee80211_supported_band *band;7821u16 max_interfaces = 0;7822bool gscan;7823__le32 bandlist[3];7824u32 n_bands;7825int err, i;78267827wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;7828wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;7829wiphy->max_num_pmkids = BRCMF_MAXPMKID;78307831err = brcmf_setup_ifmodes(wiphy, ifp);7832if (err)7833return err;78347835for (i = 0, combo = wiphy->iface_combinations;7836i < wiphy->n_iface_combinations; i++, combo++) {7837max_interfaces = max(max_interfaces, combo->max_interfaces);7838}78397840for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);7841i++) {7842u8 *addr = drvr->addresses[i].addr;78437844memcpy(addr, drvr->mac, ETH_ALEN);7845if (i) {7846addr[0] |= BIT(1);7847addr[ETH_ALEN - 1] ^= i;7848}7849}7850wiphy->addresses = drvr->addresses;7851wiphy->n_addresses = i;78527853wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;7854wiphy->cipher_suites = brcmf_cipher_suites;7855wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);7856if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))7857wiphy->n_cipher_suites--;7858wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) |7859BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |7860BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);78617862wiphy->bss_param_support = WIPHY_BSS_PARAM_AP_ISOLATE;78637864wiphy->flags |= WIPHY_FLAG_NETNS_OK |7865WIPHY_FLAG_PS_ON_BY_DEFAULT |7866WIPHY_FLAG_HAVE_AP_SME |7867WIPHY_FLAG_OFFCHAN_TX |7868WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;7869if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))7870wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;7871if (!ifp->drvr->settings->roamoff)7872wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;7873if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) {7874wiphy_ext_feature_set(wiphy,7875NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);7876wiphy_ext_feature_set(wiphy,7877NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);7878if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))7879wiphy_ext_feature_set(wiphy,7880NL80211_EXT_FEATURE_SAE_OFFLOAD);7881}7882if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWAUTH)) {7883wiphy_ext_feature_set(wiphy,7884NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK);7885if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))7886wiphy_ext_feature_set(wiphy,7887NL80211_EXT_FEATURE_SAE_OFFLOAD_AP);7888}7889if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE_EXT))7890wiphy->features |= NL80211_FEATURE_SAE;7891wiphy->mgmt_stypes = brcmf_txrx_stypes;7892wiphy->max_remain_on_channel_duration = 5000;7893if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {7894gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN);7895brcmf_pno_wiphy_params(wiphy, gscan);7896}7897/* vendor commands/events support */7898wiphy->vendor_commands = brcmf_vendor_cmds;7899wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;79007901if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))7902brcmf_wiphy_wowl_params(wiphy, ifp);7903err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,7904sizeof(bandlist));7905if (err) {7906bphy_err(drvr, "could not obtain band info: err=%d\n", err);7907return err;7908}7909/* first entry in bandlist is number of bands */7910n_bands = le32_to_cpu(bandlist[0]);7911for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {7912if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {7913band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),7914GFP_KERNEL);7915if (!band)7916return -ENOMEM;79177918band->channels = kmemdup(&__wl_2ghz_channels,7919sizeof(__wl_2ghz_channels),7920GFP_KERNEL);7921if (!band->channels) {7922kfree(band);7923return -ENOMEM;7924}79257926band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);7927wiphy->bands[NL80211_BAND_2GHZ] = band;7928}7929if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {7930band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),7931GFP_KERNEL);7932if (!band)7933return -ENOMEM;79347935band->channels = kmemdup(&__wl_5ghz_channels,7936sizeof(__wl_5ghz_channels),7937GFP_KERNEL);7938if (!band->channels) {7939kfree(band);7940return -ENOMEM;7941}79427943band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);7944wiphy->bands[NL80211_BAND_5GHZ] = band;7945}7946}79477948if (wiphy->bands[NL80211_BAND_5GHZ] &&7949brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DOT11H))7950wiphy_ext_feature_set(wiphy,7951NL80211_EXT_FEATURE_DFS_OFFLOAD);79527953wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);79547955wiphy_read_of_freq_limits(wiphy);79567957return 0;7958}79597960static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)7961{7962struct brcmf_pub *drvr = cfg->pub;7963struct net_device *ndev;7964struct wireless_dev *wdev;7965struct brcmf_if *ifp;7966s32 power_mode;7967s32 err = 0;79687969if (cfg->dongle_up)7970return err;79717972ndev = cfg_to_ndev(cfg);7973wdev = ndev->ieee80211_ptr;7974ifp = netdev_priv(ndev);79757976/* make sure RF is ready for work */7977brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);79787979brcmf_dongle_scantime(ifp);79807981power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;7982err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);7983if (err)7984goto default_conf_out;7985brcmf_dbg(INFO, "power save set to %s\n",7986(power_mode ? "enabled" : "disabled"));79877988err = brcmf_dongle_roam(ifp);7989if (err)7990goto default_conf_out;7991err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,7992NULL);7993if (err)7994goto default_conf_out;79957996brcmf_configure_arp_nd_offload(ifp, true);79977998err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_FAKEFRAG, 1);7999if (err) {8000bphy_err(drvr, "failed to set frameburst mode\n");8001goto default_conf_out;8002}80038004cfg->dongle_up = true;8005default_conf_out:80068007return err;80088009}80108011static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)8012{8013set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);80148015return brcmf_config_dongle(ifp->drvr->config);8016}80178018static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)8019{8020struct brcmf_cfg80211_info *cfg = ifp->drvr->config;80218022/*8023* While going down, if associated with AP disassociate8024* from AP to save power8025*/8026if (check_vif_up(ifp->vif)) {8027brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED, true);80288029/* Make sure WPA_Supplicant receives all the event8030generated due to DISASSOC call to the fw to keep8031the state fw and WPA_Supplicant state consistent8032*/8033brcmf_delay(500);8034}80358036brcmf_abort_scanning(cfg);8037clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);80388039return 0;8040}80418042s32 brcmf_cfg80211_up(struct net_device *ndev)8043{8044struct brcmf_if *ifp = netdev_priv(ndev);8045struct brcmf_cfg80211_info *cfg = ifp->drvr->config;8046s32 err = 0;80478048mutex_lock(&cfg->usr_sync);8049err = __brcmf_cfg80211_up(ifp);8050mutex_unlock(&cfg->usr_sync);80518052return err;8053}80548055s32 brcmf_cfg80211_down(struct net_device *ndev)8056{8057struct brcmf_if *ifp = netdev_priv(ndev);8058struct brcmf_cfg80211_info *cfg = ifp->drvr->config;8059s32 err = 0;80608061mutex_lock(&cfg->usr_sync);8062err = __brcmf_cfg80211_down(ifp);8063mutex_unlock(&cfg->usr_sync);80648065return err;8066}80678068bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,8069unsigned long state)8070{8071struct brcmf_cfg80211_vif *vif;80728073list_for_each_entry(vif, &cfg->vif_list, list) {8074if (test_bit(state, &vif->sme_state))8075return true;8076}8077return false;8078}80798080static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,8081u8 action)8082{8083u8 evt_action;80848085spin_lock(&event->vif_event_lock);8086evt_action = event->action;8087spin_unlock(&event->vif_event_lock);8088return evt_action == action;8089}80908091void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,8092struct brcmf_cfg80211_vif *vif)8093{8094struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;80958096spin_lock(&event->vif_event_lock);8097event->vif = vif;8098event->action = 0;8099spin_unlock(&event->vif_event_lock);8100}81018102bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)8103{8104struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;8105bool armed;81068107spin_lock(&event->vif_event_lock);8108armed = event->vif != NULL;8109spin_unlock(&event->vif_event_lock);81108111return armed;8112}81138114int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,8115u8 action, ulong timeout)8116{8117struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;81188119return wait_event_timeout(event->vif_wq,8120vif_event_equals(event, action), timeout);8121}81228123static bool brmcf_use_iso3166_ccode_fallback(struct brcmf_pub *drvr)8124{8125if (drvr->settings->trivial_ccode_map)8126return true;81278128switch (drvr->bus_if->chip) {8129case BRCM_CC_43430_CHIP_ID:8130case BRCM_CC_4345_CHIP_ID:8131case BRCM_CC_4356_CHIP_ID:8132case BRCM_CC_43602_CHIP_ID:8133return true;8134default:8135return false;8136}8137}81388139static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],8140struct brcmf_fil_country_le *ccreq)8141{8142struct brcmfmac_pd_cc *country_codes;8143struct brcmfmac_pd_cc_entry *cc;8144s32 found_index;8145int i;81468147if ((alpha2[0] == ccreq->country_abbrev[0]) &&8148(alpha2[1] == ccreq->country_abbrev[1])) {8149brcmf_dbg(TRACE, "Country code already set\n");8150return -EAGAIN;8151}81528153country_codes = drvr->settings->country_codes;8154if (!country_codes) {8155if (brmcf_use_iso3166_ccode_fallback(drvr)) {8156brcmf_dbg(TRACE, "No country codes configured for device, using ISO3166 code and 0 rev\n");8157memset(ccreq, 0, sizeof(*ccreq));8158ccreq->country_abbrev[0] = alpha2[0];8159ccreq->country_abbrev[1] = alpha2[1];8160ccreq->ccode[0] = alpha2[0];8161ccreq->ccode[1] = alpha2[1];8162return 0;8163}81648165brcmf_dbg(TRACE, "No country codes configured for device\n");8166return -EINVAL;8167}81688169found_index = -1;8170for (i = 0; i < country_codes->table_size; i++) {8171cc = &country_codes->table[i];8172if ((cc->iso3166[0] == '\0') && (found_index == -1))8173found_index = i;8174if ((cc->iso3166[0] == alpha2[0]) &&8175(cc->iso3166[1] == alpha2[1])) {8176found_index = i;8177break;8178}8179}8180if (found_index == -1) {8181brcmf_dbg(TRACE, "No country code match found\n");8182return -EINVAL;8183}8184memset(ccreq, 0, sizeof(*ccreq));8185ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);8186memcpy(ccreq->ccode, country_codes->table[found_index].cc,8187BRCMF_COUNTRY_BUF_SZ);8188ccreq->country_abbrev[0] = alpha2[0];8189ccreq->country_abbrev[1] = alpha2[1];8190ccreq->country_abbrev[2] = 0;81918192return 0;8193}81948195static int8196brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey)8197{8198int i;8199char *token;8200char delim[] = "\n ";8201unsigned long val;8202int err = 0;82038204token = strsep(&buf, delim);8205while (token) {8206if (!strcmp(token, "OBSS")) {8207for (i = 0; i < OBSS_TOKEN_IDX; i++)8208token = strsep(&buf, delim);8209err = kstrtoul(token, 10, &val);8210if (err)8211break;8212survey->obss = val;8213}82148215if (!strcmp(token, "IBSS")) {8216for (i = 0; i < IBSS_TOKEN_IDX; i++)8217token = strsep(&buf, delim);8218err = kstrtoul(token, 10, &val);8219if (err)8220break;8221survey->ibss = val;8222}82238224if (!strcmp(token, "TXDur")) {8225for (i = 0; i < TX_TOKEN_IDX; i++)8226token = strsep(&buf, delim);8227err = kstrtoul(token, 10, &val);8228if (err)8229break;8230survey->tx = val;8231}82328233if (!strcmp(token, "Category")) {8234for (i = 0; i < CTG_TOKEN_IDX; i++)8235token = strsep(&buf, delim);8236err = kstrtoul(token, 10, &val);8237if (err)8238break;8239survey->no_ctg = val;8240}82418242if (!strcmp(token, "Packet")) {8243for (i = 0; i < PKT_TOKEN_IDX; i++)8244token = strsep(&buf, delim);8245err = kstrtoul(token, 10, &val);8246if (err)8247break;8248survey->no_pckt = val;8249}82508251if (!strcmp(token, "Opp(time):")) {8252for (i = 0; i < IDLE_TOKEN_IDX; i++)8253token = strsep(&buf, delim);8254err = kstrtoul(token, 10, &val);8255if (err)8256break;8257survey->idle = val;8258}82598260token = strsep(&buf, delim);8261}82628263return err;8264}82658266static int8267brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req,8268struct brcmf_dump_survey *survey)8269{8270struct cca_stats_n_flags *results;8271char *buf;8272int err;82738274buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL);8275if (!buf)8276return -ENOMEM;82778278memcpy(buf, &req, sizeof(struct cca_msrmnt_query));8279err = brcmf_fil_iovar_data_get(ifp, "dump_obss",8280buf, BRCMF_DCMD_MEDLEN);8281if (err) {8282brcmf_err("dump_obss error (%d)\n", err);8283err = -EINVAL;8284goto exit;8285}8286results = (struct cca_stats_n_flags *)(buf);82878288if (req.msrmnt_query)8289brcmf_parse_dump_obss(results->buf, survey);82908291exit:8292kfree(buf);8293return err;8294}82958296static s328297brcmf_set_channel(struct brcmf_cfg80211_info *cfg, struct ieee80211_channel *chan)8298{8299u16 chspec = 0;8300int err = 0;8301struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));83028303if (chan->flags & IEEE80211_CHAN_DISABLED)8304return -EINVAL;83058306/* set_channel */8307chspec = channel_to_chanspec(&cfg->d11inf, chan);8308if (chspec != INVCHANSPEC) {8309err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec);8310if (err) {8311brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err);8312err = -EINVAL;8313}8314} else {8315brcmf_err("failed to convert host chanspec to fw chanspec\n");8316err = -EINVAL;8317}83188319return err;8320}83218322static int8323brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,8324int idx, struct survey_info *info)8325{8326struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);8327struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));8328struct brcmf_dump_survey survey = {};8329struct ieee80211_supported_band *band;8330enum nl80211_band band_id;8331struct cca_msrmnt_query req;8332u32 noise;8333int err;83348335brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx);83368337/* Do not run survey when VIF in CONNECTING / CONNECTED states */8338if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) ||8339(test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) {8340return -EBUSY;8341}83428343for (band_id = 0; band_id < NUM_NL80211_BANDS; band_id++) {8344band = wiphy->bands[band_id];8345if (!band)8346continue;8347if (idx >= band->n_channels) {8348idx -= band->n_channels;8349continue;8350}83518352info->channel = &band->channels[idx];8353break;8354}8355if (band_id == NUM_NL80211_BANDS)8356return -ENOENT;83578358/* Setting current channel to the requested channel */8359info->filled = 0;8360if (brcmf_set_channel(cfg, info->channel))8361return 0;83628363/* Disable mpc */8364brcmf_set_mpc(ifp, 0);83658366/* Set interface up, explicitly. */8367err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);8368if (err) {8369brcmf_err("set interface up failed, err = %d\n", err);8370goto exit;8371}83728373/* Get noise value */8374err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise);8375if (err) {8376brcmf_err("Get Phy Noise failed, use dummy value\n");8377noise = CHAN_NOISE_DUMMY;8378}83798380/* Start Measurement for obss stats on current channel */8381req.msrmnt_query = 0;8382req.time_req = ACS_MSRMNT_DELAY;8383err = brcmf_dump_obss(ifp, req, &survey);8384if (err)8385goto exit;83868387/* Add 10 ms for IOVAR completion */8388#if defined(__linux__)8389msleep(ACS_MSRMNT_DELAY + 10);8390#elif defined(__FreeBSD__)8391linux_msleep(ACS_MSRMNT_DELAY + 10);8392#endif83938394/* Issue IOVAR to collect measurement results */8395req.msrmnt_query = 1;8396err = brcmf_dump_obss(ifp, req, &survey);8397if (err)8398goto exit;83998400info->noise = noise;8401info->time = ACS_MSRMNT_DELAY;8402info->time_busy = ACS_MSRMNT_DELAY - survey.idle;8403info->time_rx = survey.obss + survey.ibss + survey.no_ctg +8404survey.no_pckt;8405info->time_tx = survey.tx;8406info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |8407SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX |8408SURVEY_INFO_TIME_TX;84098410brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n",8411ieee80211_frequency_to_channel(info->channel->center_freq),8412ACS_MSRMNT_DELAY);8413#if defined(__linux__)8414brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n",8415info->noise, info->time_busy, info->time_rx, info->time_tx);8416#elif defined(__FreeBSD__)8417brcmf_dbg(INFO, "noise(%d) busy(%ju) rx(%ju) tx(%ju)\n",8418info->noise, (uintmax_t)info->time_busy, (uintmax_t)info->time_rx, (uintmax_t)info->time_tx);8419#endif84208421exit:8422if (!brcmf_is_apmode(ifp->vif))8423brcmf_set_mpc(ifp, 1);8424return err;8425}84268427static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,8428struct regulatory_request *req)8429{8430struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);8431struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);8432struct brcmf_pub *drvr = cfg->pub;8433struct brcmf_fil_country_le ccreq;8434s32 err;8435int i;84368437/* The country code gets set to "00" by default at boot, ignore */8438if (req->alpha2[0] == '0' && req->alpha2[1] == '0')8439return;84408441/* ignore non-ISO3166 country codes */8442for (i = 0; i < 2; i++)8443if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {8444bphy_err(drvr, "not an ISO3166 code (0x%02x 0x%02x)\n",8445req->alpha2[0], req->alpha2[1]);8446return;8447}84488449brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,8450req->alpha2[0], req->alpha2[1]);84518452err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));8453if (err) {8454bphy_err(drvr, "Country code iovar returned err = %d\n", err);8455return;8456}84578458err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);8459if (err)8460return;84618462err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));8463if (err) {8464bphy_err(drvr, "Firmware rejected country setting\n");8465return;8466}8467brcmf_setup_wiphybands(cfg);8468}84698470static void brcmf_free_wiphy(struct wiphy *wiphy)8471{8472int i;84738474if (!wiphy)8475return;84768477if (wiphy->iface_combinations) {8478for (i = 0; i < wiphy->n_iface_combinations; i++)8479kfree(wiphy->iface_combinations[i].limits);8480}8481kfree(wiphy->iface_combinations);8482if (wiphy->bands[NL80211_BAND_2GHZ]) {8483kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);8484kfree(wiphy->bands[NL80211_BAND_2GHZ]);8485}8486if (wiphy->bands[NL80211_BAND_5GHZ]) {8487kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);8488kfree(wiphy->bands[NL80211_BAND_5GHZ]);8489}8490#if IS_ENABLED(CONFIG_PM)8491if (wiphy->wowlan != &brcmf_wowlan_support)8492kfree(wiphy->wowlan);8493#endif8494}84958496struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,8497struct cfg80211_ops *ops,8498bool p2pdev_forced)8499{8500struct wiphy *wiphy = drvr->wiphy;8501struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;8502struct brcmf_cfg80211_info *cfg;8503struct brcmf_cfg80211_vif *vif;8504struct brcmf_if *ifp;8505s32 err = 0;8506s32 io_type;8507u16 *cap = NULL;85088509if (!ndev) {8510bphy_err(drvr, "ndev is invalid\n");8511return NULL;8512}85138514cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);8515if (!cfg) {8516bphy_err(drvr, "Could not allocate wiphy device\n");8517return NULL;8518}85198520cfg->wiphy = wiphy;8521cfg->pub = drvr;8522init_vif_event(&cfg->vif_event);8523INIT_LIST_HEAD(&cfg->vif_list);85248525vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION);8526if (IS_ERR(vif))8527goto wiphy_out;85288529ifp = netdev_priv(ndev);8530vif->ifp = ifp;8531vif->wdev.netdev = ndev;8532ndev->ieee80211_ptr = &vif->wdev;8533SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));85348535err = wl_init_priv(cfg);8536if (err) {8537bphy_err(drvr, "Failed to init iwm_priv (%d)\n", err);8538brcmf_free_vif(vif);8539goto wiphy_out;8540}8541ifp->vif = vif;85428543/* determine d11 io type before wiphy setup */8544err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);8545if (err) {8546bphy_err(drvr, "Failed to get D11 version (%d)\n", err);8547goto priv_out;8548}8549cfg->d11inf.io_type = (u8)io_type;8550brcmu_d11_attach(&cfg->d11inf);85518552/* regulatory notifier below needs access to cfg so8553* assign it now.8554*/8555drvr->config = cfg;85568557err = brcmf_setup_wiphy(wiphy, ifp);8558if (err < 0)8559goto priv_out;85608561brcmf_dbg(INFO, "Registering custom regulatory\n");8562wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;8563wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;8564wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);85658566/* firmware defaults to 40MHz disabled in 2G band. We signal8567* cfg80211 here that we do and have it decide we can enable8568* it. But first check if device does support 2G operation.8569*/8570if (wiphy->bands[NL80211_BAND_2GHZ]) {8571cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;8572*cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;8573}8574#ifdef CONFIG_PM8575if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))8576ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;8577#endif8578if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS))8579ops->dump_survey = brcmf_cfg80211_dump_survey;85808581err = wiphy_register(wiphy);8582if (err < 0) {8583bphy_err(drvr, "Could not register wiphy device (%d)\n", err);8584goto priv_out;8585}85868587err = brcmf_setup_wiphybands(cfg);8588if (err) {8589bphy_err(drvr, "Setting wiphy bands failed (%d)\n", err);8590goto wiphy_unreg_out;8591}85928593/* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),8594* setup 40MHz in 2GHz band and enable OBSS scanning.8595*/8596if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {8597err = brcmf_enable_bw40_2g(cfg);8598if (!err)8599err = brcmf_fil_iovar_int_set(ifp, "obss_coex",8600BRCMF_OBSS_COEX_AUTO);8601else8602*cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;8603}86048605err = brcmf_fweh_activate_events(ifp);8606if (err) {8607bphy_err(drvr, "FWEH activation failed (%d)\n", err);8608goto wiphy_unreg_out;8609}86108611err = brcmf_p2p_attach(cfg, p2pdev_forced);8612if (err) {8613bphy_err(drvr, "P2P initialisation failed (%d)\n", err);8614goto wiphy_unreg_out;8615}8616err = brcmf_btcoex_attach(cfg);8617if (err) {8618bphy_err(drvr, "BT-coex initialisation failed (%d)\n", err);8619brcmf_p2p_detach(&cfg->p2p);8620goto wiphy_unreg_out;8621}8622err = brcmf_pno_attach(cfg);8623if (err) {8624bphy_err(drvr, "PNO initialisation failed (%d)\n", err);8625brcmf_btcoex_detach(cfg);8626brcmf_p2p_detach(&cfg->p2p);8627goto wiphy_unreg_out;8628}86298630if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {8631err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);8632if (err) {8633brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);8634wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;8635} else {8636brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,8637brcmf_notify_tdls_peer_event);8638}8639}86408641/* (re-) activate FWEH event handling */8642err = brcmf_fweh_activate_events(ifp);8643if (err) {8644bphy_err(drvr, "FWEH activation failed (%d)\n", err);8645goto detach;8646}86478648/* Fill in some of the advertised nl80211 supported features */8649if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {8650wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;8651#ifdef CONFIG_PM8652if (wiphy->wowlan &&8653wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)8654wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;8655#endif8656}86578658return cfg;86598660detach:8661brcmf_pno_detach(cfg);8662brcmf_btcoex_detach(cfg);8663brcmf_p2p_detach(&cfg->p2p);8664wiphy_unreg_out:8665wiphy_unregister(cfg->wiphy);8666priv_out:8667wl_deinit_priv(cfg);8668brcmf_free_vif(vif);8669ifp->vif = NULL;8670wiphy_out:8671brcmf_free_wiphy(wiphy);8672kfree(cfg);8673return NULL;8674}86758676void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)8677{8678if (!cfg)8679return;86808681brcmf_pno_detach(cfg);8682brcmf_btcoex_detach(cfg);8683wiphy_unregister(cfg->wiphy);8684wl_deinit_priv(cfg);8685cancel_work_sync(&cfg->escan_timeout_work);8686brcmf_free_wiphy(cfg->wiphy);8687kfree(cfg);8688}868986908691