162306a36Sopenharmony_ci// SPDX-License-Identifier: ISC 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci 662306a36Sopenharmony_ci/* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/etherdevice.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/vmalloc.h> 1262306a36Sopenharmony_ci#include <net/cfg80211.h> 1362306a36Sopenharmony_ci#include <net/netlink.h> 1462306a36Sopenharmony_ci#include <uapi/linux/if_arp.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <brcmu_utils.h> 1762306a36Sopenharmony_ci#include <defs.h> 1862306a36Sopenharmony_ci#include <brcmu_wifi.h> 1962306a36Sopenharmony_ci#include <brcm_hw_ids.h> 2062306a36Sopenharmony_ci#include "core.h" 2162306a36Sopenharmony_ci#include "debug.h" 2262306a36Sopenharmony_ci#include "tracepoint.h" 2362306a36Sopenharmony_ci#include "fwil_types.h" 2462306a36Sopenharmony_ci#include "p2p.h" 2562306a36Sopenharmony_ci#include "btcoex.h" 2662306a36Sopenharmony_ci#include "pno.h" 2762306a36Sopenharmony_ci#include "fwsignal.h" 2862306a36Sopenharmony_ci#include "cfg80211.h" 2962306a36Sopenharmony_ci#include "feature.h" 3062306a36Sopenharmony_ci#include "fwil.h" 3162306a36Sopenharmony_ci#include "proto.h" 3262306a36Sopenharmony_ci#include "vendor.h" 3362306a36Sopenharmony_ci#include "bus.h" 3462306a36Sopenharmony_ci#include "common.h" 3562306a36Sopenharmony_ci 3662306a36Sopenharmony_ci#define BRCMF_SCAN_IE_LEN_MAX 2048 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define WPA_OUI "\x00\x50\xF2" /* WPA OUI */ 3962306a36Sopenharmony_ci#define WPA_OUI_TYPE 1 4062306a36Sopenharmony_ci#define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */ 4162306a36Sopenharmony_ci#define WME_OUI_TYPE 2 4262306a36Sopenharmony_ci#define WPS_OUI_TYPE 4 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define VS_IE_FIXED_HDR_LEN 6 4562306a36Sopenharmony_ci#define WPA_IE_VERSION_LEN 2 4662306a36Sopenharmony_ci#define WPA_IE_MIN_OUI_LEN 4 4762306a36Sopenharmony_ci#define WPA_IE_SUITE_COUNT_LEN 2 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#define WPA_CIPHER_NONE 0 /* None */ 5062306a36Sopenharmony_ci#define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */ 5162306a36Sopenharmony_ci#define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */ 5262306a36Sopenharmony_ci#define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */ 5362306a36Sopenharmony_ci#define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#define RSN_AKM_NONE 0 /* None (IBSS) */ 5662306a36Sopenharmony_ci#define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */ 5762306a36Sopenharmony_ci#define RSN_AKM_PSK 2 /* Pre-shared Key */ 5862306a36Sopenharmony_ci#define RSN_AKM_SHA256_1X 5 /* SHA256, 802.1X */ 5962306a36Sopenharmony_ci#define RSN_AKM_SHA256_PSK 6 /* SHA256, Pre-shared Key */ 6062306a36Sopenharmony_ci#define RSN_AKM_SAE 8 /* SAE */ 6162306a36Sopenharmony_ci#define RSN_CAP_LEN 2 /* Length of RSN capabilities */ 6262306a36Sopenharmony_ci#define RSN_CAP_PTK_REPLAY_CNTR_MASK (BIT(2) | BIT(3)) 6362306a36Sopenharmony_ci#define RSN_CAP_MFPR_MASK BIT(6) 6462306a36Sopenharmony_ci#define RSN_CAP_MFPC_MASK BIT(7) 6562306a36Sopenharmony_ci#define RSN_PMKID_COUNT_LEN 2 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define VNDR_IE_CMD_LEN 4 /* length of the set command 6862306a36Sopenharmony_ci * string :"add", "del" (+ NUL) 6962306a36Sopenharmony_ci */ 7062306a36Sopenharmony_ci#define VNDR_IE_COUNT_OFFSET 4 7162306a36Sopenharmony_ci#define VNDR_IE_PKTFLAG_OFFSET 8 7262306a36Sopenharmony_ci#define VNDR_IE_VSIE_OFFSET 12 7362306a36Sopenharmony_ci#define VNDR_IE_HDR_SIZE 12 7462306a36Sopenharmony_ci#define VNDR_IE_PARSE_LIMIT 5 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci#define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */ 7762306a36Sopenharmony_ci#define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */ 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320 8062306a36Sopenharmony_ci#define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400 8162306a36Sopenharmony_ci#define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define BRCMF_SCAN_CHANNEL_TIME 40 8462306a36Sopenharmony_ci#define BRCMF_SCAN_UNASSOC_TIME 40 8562306a36Sopenharmony_ci#define BRCMF_SCAN_PASSIVE_TIME 120 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci#define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000) 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci#define BRCMF_PS_MAX_TIMEOUT_MS 2000 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Dump obss definitions */ 9262306a36Sopenharmony_ci#define ACS_MSRMNT_DELAY 80 9362306a36Sopenharmony_ci#define CHAN_NOISE_DUMMY (-80) 9462306a36Sopenharmony_ci#define OBSS_TOKEN_IDX 15 9562306a36Sopenharmony_ci#define IBSS_TOKEN_IDX 15 9662306a36Sopenharmony_ci#define TX_TOKEN_IDX 14 9762306a36Sopenharmony_ci#define CTG_TOKEN_IDX 13 9862306a36Sopenharmony_ci#define PKT_TOKEN_IDX 15 9962306a36Sopenharmony_ci#define IDLE_TOKEN_IDX 12 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \ 10262306a36Sopenharmony_ci (sizeof(struct brcmf_assoc_params_le) - sizeof(u16)) 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci#define BRCMF_MAX_CHANSPEC_LIST \ 10562306a36Sopenharmony_ci (BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1) 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistruct brcmf_dump_survey { 10862306a36Sopenharmony_ci u32 obss; 10962306a36Sopenharmony_ci u32 ibss; 11062306a36Sopenharmony_ci u32 no_ctg; 11162306a36Sopenharmony_ci u32 no_pckt; 11262306a36Sopenharmony_ci u32 tx; 11362306a36Sopenharmony_ci u32 idle; 11462306a36Sopenharmony_ci}; 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_cistruct cca_stats_n_flags { 11762306a36Sopenharmony_ci u32 msrmnt_time; /* Time for Measurement (msec) */ 11862306a36Sopenharmony_ci u32 msrmnt_done; /* flag set when measurement complete */ 11962306a36Sopenharmony_ci char buf[1]; 12062306a36Sopenharmony_ci}; 12162306a36Sopenharmony_ci 12262306a36Sopenharmony_cistruct cca_msrmnt_query { 12362306a36Sopenharmony_ci u32 msrmnt_query; 12462306a36Sopenharmony_ci u32 time_req; 12562306a36Sopenharmony_ci}; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_cistatic bool check_vif_up(struct brcmf_cfg80211_vif *vif) 12862306a36Sopenharmony_ci{ 12962306a36Sopenharmony_ci if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) { 13062306a36Sopenharmony_ci brcmf_dbg(INFO, "device is not ready : status (%lu)\n", 13162306a36Sopenharmony_ci vif->sme_state); 13262306a36Sopenharmony_ci return false; 13362306a36Sopenharmony_ci } 13462306a36Sopenharmony_ci return true; 13562306a36Sopenharmony_ci} 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci#define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2) 13862306a36Sopenharmony_ci#define RATETAB_ENT(_rateid, _flags) \ 13962306a36Sopenharmony_ci { \ 14062306a36Sopenharmony_ci .bitrate = RATE_TO_BASE100KBPS(_rateid), \ 14162306a36Sopenharmony_ci .hw_value = (_rateid), \ 14262306a36Sopenharmony_ci .flags = (_flags), \ 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic struct ieee80211_rate __wl_rates[] = { 14662306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_1M, 0), 14762306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE), 14862306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE), 14962306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE), 15062306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_6M, 0), 15162306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_9M, 0), 15262306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_12M, 0), 15362306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_18M, 0), 15462306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_24M, 0), 15562306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_36M, 0), 15662306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_48M, 0), 15762306a36Sopenharmony_ci RATETAB_ENT(BRCM_RATE_54M, 0), 15862306a36Sopenharmony_ci}; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci#define wl_g_rates (__wl_rates + 0) 16162306a36Sopenharmony_ci#define wl_g_rates_size ARRAY_SIZE(__wl_rates) 16262306a36Sopenharmony_ci#define wl_a_rates (__wl_rates + 4) 16362306a36Sopenharmony_ci#define wl_a_rates_size (wl_g_rates_size - 4) 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci#define CHAN2G(_channel, _freq) { \ 16662306a36Sopenharmony_ci .band = NL80211_BAND_2GHZ, \ 16762306a36Sopenharmony_ci .center_freq = (_freq), \ 16862306a36Sopenharmony_ci .hw_value = (_channel), \ 16962306a36Sopenharmony_ci .max_antenna_gain = 0, \ 17062306a36Sopenharmony_ci .max_power = 30, \ 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci#define CHAN5G(_channel) { \ 17462306a36Sopenharmony_ci .band = NL80211_BAND_5GHZ, \ 17562306a36Sopenharmony_ci .center_freq = 5000 + (5 * (_channel)), \ 17662306a36Sopenharmony_ci .hw_value = (_channel), \ 17762306a36Sopenharmony_ci .max_antenna_gain = 0, \ 17862306a36Sopenharmony_ci .max_power = 30, \ 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic struct ieee80211_channel __wl_2ghz_channels[] = { 18262306a36Sopenharmony_ci CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427), 18362306a36Sopenharmony_ci CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447), 18462306a36Sopenharmony_ci CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467), 18562306a36Sopenharmony_ci CHAN2G(13, 2472), CHAN2G(14, 2484) 18662306a36Sopenharmony_ci}; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cistatic struct ieee80211_channel __wl_5ghz_channels[] = { 18962306a36Sopenharmony_ci CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42), 19062306a36Sopenharmony_ci CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56), 19162306a36Sopenharmony_ci CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108), 19262306a36Sopenharmony_ci CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128), 19362306a36Sopenharmony_ci CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149), 19462306a36Sopenharmony_ci CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165) 19562306a36Sopenharmony_ci}; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci/* Band templates duplicated per wiphy. The channel info 19862306a36Sopenharmony_ci * above is added to the band during setup. 19962306a36Sopenharmony_ci */ 20062306a36Sopenharmony_cistatic const struct ieee80211_supported_band __wl_band_2ghz = { 20162306a36Sopenharmony_ci .band = NL80211_BAND_2GHZ, 20262306a36Sopenharmony_ci .bitrates = wl_g_rates, 20362306a36Sopenharmony_ci .n_bitrates = wl_g_rates_size, 20462306a36Sopenharmony_ci}; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic const struct ieee80211_supported_band __wl_band_5ghz = { 20762306a36Sopenharmony_ci .band = NL80211_BAND_5GHZ, 20862306a36Sopenharmony_ci .bitrates = wl_a_rates, 20962306a36Sopenharmony_ci .n_bitrates = wl_a_rates_size, 21062306a36Sopenharmony_ci}; 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci/* This is to override regulatory domains defined in cfg80211 module (reg.c) 21362306a36Sopenharmony_ci * By default world regulatory domain defined in reg.c puts the flags 21462306a36Sopenharmony_ci * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165). 21562306a36Sopenharmony_ci * With respect to these flags, wpa_supplicant doesn't * start p2p 21662306a36Sopenharmony_ci * operations on 5GHz channels. All the changes in world regulatory 21762306a36Sopenharmony_ci * domain are to be done here. 21862306a36Sopenharmony_ci */ 21962306a36Sopenharmony_cistatic const struct ieee80211_regdomain brcmf_regdom = { 22062306a36Sopenharmony_ci .n_reg_rules = 4, 22162306a36Sopenharmony_ci .alpha2 = "99", 22262306a36Sopenharmony_ci .reg_rules = { 22362306a36Sopenharmony_ci /* IEEE 802.11b/g, channels 1..11 */ 22462306a36Sopenharmony_ci REG_RULE(2412-10, 2472+10, 40, 6, 20, 0), 22562306a36Sopenharmony_ci /* If any */ 22662306a36Sopenharmony_ci /* IEEE 802.11 channel 14 - Only JP enables 22762306a36Sopenharmony_ci * this and for 802.11b only 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci REG_RULE(2484-10, 2484+10, 20, 6, 20, 0), 23062306a36Sopenharmony_ci /* IEEE 802.11a, channel 36..64 */ 23162306a36Sopenharmony_ci REG_RULE(5150-10, 5350+10, 160, 6, 20, 0), 23262306a36Sopenharmony_ci /* IEEE 802.11a, channel 100..165 */ 23362306a36Sopenharmony_ci REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), } 23462306a36Sopenharmony_ci}; 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci/* Note: brcmf_cipher_suites is an array of int defining which cipher suites 23762306a36Sopenharmony_ci * are supported. A pointer to this array and the number of entries is passed 23862306a36Sopenharmony_ci * on to upper layers. AES_CMAC defines whether or not the driver supports MFP. 23962306a36Sopenharmony_ci * So the cipher suite AES_CMAC has to be the last one in the array, and when 24062306a36Sopenharmony_ci * device does not support MFP then the number of suites will be decreased by 1 24162306a36Sopenharmony_ci */ 24262306a36Sopenharmony_cistatic const u32 brcmf_cipher_suites[] = { 24362306a36Sopenharmony_ci WLAN_CIPHER_SUITE_WEP40, 24462306a36Sopenharmony_ci WLAN_CIPHER_SUITE_WEP104, 24562306a36Sopenharmony_ci WLAN_CIPHER_SUITE_TKIP, 24662306a36Sopenharmony_ci WLAN_CIPHER_SUITE_CCMP, 24762306a36Sopenharmony_ci /* Keep as last entry: */ 24862306a36Sopenharmony_ci WLAN_CIPHER_SUITE_AES_CMAC 24962306a36Sopenharmony_ci}; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci/* Vendor specific ie. id = 221, oui and type defines exact ie */ 25262306a36Sopenharmony_cistruct brcmf_vs_tlv { 25362306a36Sopenharmony_ci u8 id; 25462306a36Sopenharmony_ci u8 len; 25562306a36Sopenharmony_ci u8 oui[3]; 25662306a36Sopenharmony_ci u8 oui_type; 25762306a36Sopenharmony_ci}; 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistruct parsed_vndr_ie_info { 26062306a36Sopenharmony_ci u8 *ie_ptr; 26162306a36Sopenharmony_ci u32 ie_len; /* total length including id & length field */ 26262306a36Sopenharmony_ci struct brcmf_vs_tlv vndrie; 26362306a36Sopenharmony_ci}; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_cistruct parsed_vndr_ies { 26662306a36Sopenharmony_ci u32 count; 26762306a36Sopenharmony_ci struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT]; 26862306a36Sopenharmony_ci}; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_VER_1 1 27162306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_VER_2 2 27262306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_VER_3 3 27362306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_VER_MAX WL_INTERFACE_CREATE_VER_3 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci#define WL_INTERFACE_MAC_DONT_USE 0x0 27662306a36Sopenharmony_ci#define WL_INTERFACE_MAC_USE 0x2 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_STA 0x0 27962306a36Sopenharmony_ci#define WL_INTERFACE_CREATE_AP 0x1 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistruct wl_interface_create_v1 { 28262306a36Sopenharmony_ci u16 ver; /* structure version */ 28362306a36Sopenharmony_ci u32 flags; /* flags for operation */ 28462306a36Sopenharmony_ci u8 mac_addr[ETH_ALEN]; /* MAC address */ 28562306a36Sopenharmony_ci u32 wlc_index; /* optional for wlc index */ 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistruct wl_interface_create_v2 { 28962306a36Sopenharmony_ci u16 ver; /* structure version */ 29062306a36Sopenharmony_ci u8 pad1[2]; 29162306a36Sopenharmony_ci u32 flags; /* flags for operation */ 29262306a36Sopenharmony_ci u8 mac_addr[ETH_ALEN]; /* MAC address */ 29362306a36Sopenharmony_ci u8 iftype; /* type of interface created */ 29462306a36Sopenharmony_ci u8 pad2; 29562306a36Sopenharmony_ci u32 wlc_index; /* optional for wlc index */ 29662306a36Sopenharmony_ci}; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_cistruct wl_interface_create_v3 { 29962306a36Sopenharmony_ci u16 ver; /* structure version */ 30062306a36Sopenharmony_ci u16 len; /* length of structure + data */ 30162306a36Sopenharmony_ci u16 fixed_len; /* length of structure */ 30262306a36Sopenharmony_ci u8 iftype; /* type of interface created */ 30362306a36Sopenharmony_ci u8 wlc_index; /* optional for wlc index */ 30462306a36Sopenharmony_ci u32 flags; /* flags for operation */ 30562306a36Sopenharmony_ci u8 mac_addr[ETH_ALEN]; /* MAC address */ 30662306a36Sopenharmony_ci u8 bssid[ETH_ALEN]; /* optional for BSSID */ 30762306a36Sopenharmony_ci u8 if_index; /* interface index request */ 30862306a36Sopenharmony_ci u8 pad[3]; 30962306a36Sopenharmony_ci u8 data[]; /* Optional for specific data */ 31062306a36Sopenharmony_ci}; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic u8 nl80211_band_to_fwil(enum nl80211_band band) 31362306a36Sopenharmony_ci{ 31462306a36Sopenharmony_ci switch (band) { 31562306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 31662306a36Sopenharmony_ci return WLC_BAND_2G; 31762306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 31862306a36Sopenharmony_ci return WLC_BAND_5G; 31962306a36Sopenharmony_ci default: 32062306a36Sopenharmony_ci WARN_ON(1); 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci } 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf, 32762306a36Sopenharmony_ci struct cfg80211_chan_def *ch) 32862306a36Sopenharmony_ci{ 32962306a36Sopenharmony_ci struct brcmu_chan ch_inf; 33062306a36Sopenharmony_ci s32 primary_offset; 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n", 33362306a36Sopenharmony_ci ch->chan->center_freq, ch->center_freq1, ch->width); 33462306a36Sopenharmony_ci ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1); 33562306a36Sopenharmony_ci primary_offset = ch->chan->center_freq - ch->center_freq1; 33662306a36Sopenharmony_ci switch (ch->width) { 33762306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20: 33862306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_20_NOHT: 33962306a36Sopenharmony_ci ch_inf.bw = BRCMU_CHAN_BW_20; 34062306a36Sopenharmony_ci WARN_ON(primary_offset != 0); 34162306a36Sopenharmony_ci break; 34262306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_40: 34362306a36Sopenharmony_ci ch_inf.bw = BRCMU_CHAN_BW_40; 34462306a36Sopenharmony_ci if (primary_offset > 0) 34562306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_U; 34662306a36Sopenharmony_ci else 34762306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_L; 34862306a36Sopenharmony_ci break; 34962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80: 35062306a36Sopenharmony_ci ch_inf.bw = BRCMU_CHAN_BW_80; 35162306a36Sopenharmony_ci if (primary_offset == -30) 35262306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_LL; 35362306a36Sopenharmony_ci else if (primary_offset == -10) 35462306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_LU; 35562306a36Sopenharmony_ci else if (primary_offset == 10) 35662306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_UL; 35762306a36Sopenharmony_ci else 35862306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_UU; 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_160: 36162306a36Sopenharmony_ci ch_inf.bw = BRCMU_CHAN_BW_160; 36262306a36Sopenharmony_ci if (primary_offset == -70) 36362306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_LLL; 36462306a36Sopenharmony_ci else if (primary_offset == -50) 36562306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_LLU; 36662306a36Sopenharmony_ci else if (primary_offset == -30) 36762306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_LUL; 36862306a36Sopenharmony_ci else if (primary_offset == -10) 36962306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_LUU; 37062306a36Sopenharmony_ci else if (primary_offset == 10) 37162306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_ULL; 37262306a36Sopenharmony_ci else if (primary_offset == 30) 37362306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_ULU; 37462306a36Sopenharmony_ci else if (primary_offset == 50) 37562306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_UUL; 37662306a36Sopenharmony_ci else 37762306a36Sopenharmony_ci ch_inf.sb = BRCMU_CHAN_SB_UUU; 37862306a36Sopenharmony_ci break; 37962306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_80P80: 38062306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_5: 38162306a36Sopenharmony_ci case NL80211_CHAN_WIDTH_10: 38262306a36Sopenharmony_ci default: 38362306a36Sopenharmony_ci WARN_ON_ONCE(1); 38462306a36Sopenharmony_ci } 38562306a36Sopenharmony_ci switch (ch->chan->band) { 38662306a36Sopenharmony_ci case NL80211_BAND_2GHZ: 38762306a36Sopenharmony_ci ch_inf.band = BRCMU_CHAN_BAND_2G; 38862306a36Sopenharmony_ci break; 38962306a36Sopenharmony_ci case NL80211_BAND_5GHZ: 39062306a36Sopenharmony_ci ch_inf.band = BRCMU_CHAN_BAND_5G; 39162306a36Sopenharmony_ci break; 39262306a36Sopenharmony_ci case NL80211_BAND_60GHZ: 39362306a36Sopenharmony_ci default: 39462306a36Sopenharmony_ci WARN_ON_ONCE(1); 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci d11inf->encchspec(&ch_inf); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci brcmf_dbg(TRACE, "chanspec: 0x%x\n", ch_inf.chspec); 39962306a36Sopenharmony_ci return ch_inf.chspec; 40062306a36Sopenharmony_ci} 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ciu16 channel_to_chanspec(struct brcmu_d11inf *d11inf, 40362306a36Sopenharmony_ci struct ieee80211_channel *ch) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct brcmu_chan ch_inf; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq); 40862306a36Sopenharmony_ci ch_inf.bw = BRCMU_CHAN_BW_20; 40962306a36Sopenharmony_ci d11inf->encchspec(&ch_inf); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return ch_inf.chspec; 41262306a36Sopenharmony_ci} 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci/* Traverse a string of 1-byte tag/1-byte length/variable-length value 41562306a36Sopenharmony_ci * triples, returning a pointer to the substring whose first element 41662306a36Sopenharmony_ci * matches tag 41762306a36Sopenharmony_ci */ 41862306a36Sopenharmony_cistatic const struct brcmf_tlv * 41962306a36Sopenharmony_cibrcmf_parse_tlvs(const void *buf, int buflen, uint key) 42062306a36Sopenharmony_ci{ 42162306a36Sopenharmony_ci const struct brcmf_tlv *elt = buf; 42262306a36Sopenharmony_ci int totlen = buflen; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* find tagged parameter */ 42562306a36Sopenharmony_ci while (totlen >= TLV_HDR_LEN) { 42662306a36Sopenharmony_ci int len = elt->len; 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_ci /* validate remaining totlen */ 42962306a36Sopenharmony_ci if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN))) 43062306a36Sopenharmony_ci return elt; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN)); 43362306a36Sopenharmony_ci totlen -= (len + TLV_HDR_LEN); 43462306a36Sopenharmony_ci } 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_ci return NULL; 43762306a36Sopenharmony_ci} 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci/* Is any of the tlvs the expected entry? If 44062306a36Sopenharmony_ci * not update the tlvs buffer pointer/length. 44162306a36Sopenharmony_ci */ 44262306a36Sopenharmony_cistatic bool 44362306a36Sopenharmony_cibrcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, 44462306a36Sopenharmony_ci const u8 *oui, u32 oui_len, u8 type) 44562306a36Sopenharmony_ci{ 44662306a36Sopenharmony_ci /* If the contents match the OUI and the type */ 44762306a36Sopenharmony_ci if (ie[TLV_LEN_OFF] >= oui_len + 1 && 44862306a36Sopenharmony_ci !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) && 44962306a36Sopenharmony_ci type == ie[TLV_BODY_OFF + oui_len]) { 45062306a36Sopenharmony_ci return true; 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (tlvs == NULL) 45462306a36Sopenharmony_ci return false; 45562306a36Sopenharmony_ci /* point to the next ie */ 45662306a36Sopenharmony_ci ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN; 45762306a36Sopenharmony_ci /* calculate the length of the rest of the buffer */ 45862306a36Sopenharmony_ci *tlvs_len -= (int)(ie - *tlvs); 45962306a36Sopenharmony_ci /* update the pointer to the start of the buffer */ 46062306a36Sopenharmony_ci *tlvs = ie; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci return false; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic struct brcmf_vs_tlv * 46662306a36Sopenharmony_cibrcmf_find_wpaie(const u8 *parse, u32 len) 46762306a36Sopenharmony_ci{ 46862306a36Sopenharmony_ci const struct brcmf_tlv *ie; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { 47162306a36Sopenharmony_ci if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len, 47262306a36Sopenharmony_ci WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE)) 47362306a36Sopenharmony_ci return (struct brcmf_vs_tlv *)ie; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci return NULL; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_cistatic struct brcmf_vs_tlv * 47962306a36Sopenharmony_cibrcmf_find_wpsie(const u8 *parse, u32 len) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci const struct brcmf_tlv *ie; 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) { 48462306a36Sopenharmony_ci if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len, 48562306a36Sopenharmony_ci WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE)) 48662306a36Sopenharmony_ci return (struct brcmf_vs_tlv *)ie; 48762306a36Sopenharmony_ci } 48862306a36Sopenharmony_ci return NULL; 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_cistatic int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg, 49262306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif, 49362306a36Sopenharmony_ci enum nl80211_iftype new_type) 49462306a36Sopenharmony_ci{ 49562306a36Sopenharmony_ci struct brcmf_cfg80211_vif *pos; 49662306a36Sopenharmony_ci bool check_combos = false; 49762306a36Sopenharmony_ci int ret = 0; 49862306a36Sopenharmony_ci struct iface_combination_params params = { 49962306a36Sopenharmony_ci .num_different_channels = 1, 50062306a36Sopenharmony_ci }; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci list_for_each_entry(pos, &cfg->vif_list, list) 50362306a36Sopenharmony_ci if (pos == vif) { 50462306a36Sopenharmony_ci params.iftype_num[new_type]++; 50562306a36Sopenharmony_ci } else { 50662306a36Sopenharmony_ci /* concurrent interfaces so need check combinations */ 50762306a36Sopenharmony_ci check_combos = true; 50862306a36Sopenharmony_ci params.iftype_num[pos->wdev.iftype]++; 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci if (check_combos) 51262306a36Sopenharmony_ci ret = cfg80211_check_combinations(cfg->wiphy, ¶ms); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci return ret; 51562306a36Sopenharmony_ci} 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_cistatic int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg, 51862306a36Sopenharmony_ci enum nl80211_iftype new_type) 51962306a36Sopenharmony_ci{ 52062306a36Sopenharmony_ci struct brcmf_cfg80211_vif *pos; 52162306a36Sopenharmony_ci struct iface_combination_params params = { 52262306a36Sopenharmony_ci .num_different_channels = 1, 52362306a36Sopenharmony_ci }; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci list_for_each_entry(pos, &cfg->vif_list, list) 52662306a36Sopenharmony_ci params.iftype_num[pos->wdev.iftype]++; 52762306a36Sopenharmony_ci 52862306a36Sopenharmony_ci params.iftype_num[new_type]++; 52962306a36Sopenharmony_ci return cfg80211_check_combinations(cfg->wiphy, ¶ms); 53062306a36Sopenharmony_ci} 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_cistatic void convert_key_from_CPU(struct brcmf_wsec_key *key, 53362306a36Sopenharmony_ci struct brcmf_wsec_key_le *key_le) 53462306a36Sopenharmony_ci{ 53562306a36Sopenharmony_ci key_le->index = cpu_to_le32(key->index); 53662306a36Sopenharmony_ci key_le->len = cpu_to_le32(key->len); 53762306a36Sopenharmony_ci key_le->algo = cpu_to_le32(key->algo); 53862306a36Sopenharmony_ci key_le->flags = cpu_to_le32(key->flags); 53962306a36Sopenharmony_ci key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi); 54062306a36Sopenharmony_ci key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo); 54162306a36Sopenharmony_ci key_le->iv_initialized = cpu_to_le32(key->iv_initialized); 54262306a36Sopenharmony_ci memcpy(key_le->data, key->data, sizeof(key->data)); 54362306a36Sopenharmony_ci memcpy(key_le->ea, key->ea, sizeof(key->ea)); 54462306a36Sopenharmony_ci} 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_cistatic int 54762306a36Sopenharmony_cisend_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key) 54862306a36Sopenharmony_ci{ 54962306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 55062306a36Sopenharmony_ci int err; 55162306a36Sopenharmony_ci struct brcmf_wsec_key_le key_le; 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci convert_key_from_CPU(key, &key_le); 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ci brcmf_netdev_wait_pend8021x(ifp); 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le, 55862306a36Sopenharmony_ci sizeof(key_le)); 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci if (err) 56162306a36Sopenharmony_ci bphy_err(drvr, "wsec_key error (%d)\n", err); 56262306a36Sopenharmony_ci return err; 56362306a36Sopenharmony_ci} 56462306a36Sopenharmony_ci 56562306a36Sopenharmony_cistatic void 56662306a36Sopenharmony_cibrcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 56962306a36Sopenharmony_ci struct brcmf_if *ifp; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); 57262306a36Sopenharmony_ci ifp = vif->ifp; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci if ((wdev->iftype == NL80211_IFTYPE_ADHOC) || 57562306a36Sopenharmony_ci (wdev->iftype == NL80211_IFTYPE_AP) || 57662306a36Sopenharmony_ci (wdev->iftype == NL80211_IFTYPE_P2P_GO)) 57762306a36Sopenharmony_ci brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, 57862306a36Sopenharmony_ci ADDR_DIRECT); 57962306a36Sopenharmony_ci else 58062306a36Sopenharmony_ci brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx, 58162306a36Sopenharmony_ci ADDR_INDIRECT); 58262306a36Sopenharmony_ci} 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr) 58562306a36Sopenharmony_ci{ 58662306a36Sopenharmony_ci int bsscfgidx; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) { 58962306a36Sopenharmony_ci /* bsscfgidx 1 is reserved for legacy P2P */ 59062306a36Sopenharmony_ci if (bsscfgidx == 1) 59162306a36Sopenharmony_ci continue; 59262306a36Sopenharmony_ci if (!drvr->iflist[bsscfgidx]) 59362306a36Sopenharmony_ci return bsscfgidx; 59462306a36Sopenharmony_ci } 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci return -ENOMEM; 59762306a36Sopenharmony_ci} 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_cistatic void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr) 60062306a36Sopenharmony_ci{ 60162306a36Sopenharmony_ci u8 mac_idx = ifp->drvr->sta_mac_idx; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci /* set difference MAC address with locally administered bit */ 60462306a36Sopenharmony_ci memcpy(mac_addr, ifp->mac_addr, ETH_ALEN); 60562306a36Sopenharmony_ci mac_addr[0] |= 0x02; 60662306a36Sopenharmony_ci mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0; 60762306a36Sopenharmony_ci mac_idx++; 60862306a36Sopenharmony_ci mac_idx = mac_idx % 2; 60962306a36Sopenharmony_ci ifp->drvr->sta_mac_idx = mac_idx; 61062306a36Sopenharmony_ci} 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_cistatic int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr) 61362306a36Sopenharmony_ci{ 61462306a36Sopenharmony_ci struct wl_interface_create_v1 iface_v1; 61562306a36Sopenharmony_ci struct wl_interface_create_v2 iface_v2; 61662306a36Sopenharmony_ci struct wl_interface_create_v3 iface_v3; 61762306a36Sopenharmony_ci u32 iface_create_ver; 61862306a36Sopenharmony_ci int err; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci /* interface_create version 1 */ 62162306a36Sopenharmony_ci memset(&iface_v1, 0, sizeof(iface_v1)); 62262306a36Sopenharmony_ci iface_v1.ver = WL_INTERFACE_CREATE_VER_1; 62362306a36Sopenharmony_ci iface_v1.flags = WL_INTERFACE_CREATE_STA | 62462306a36Sopenharmony_ci WL_INTERFACE_MAC_USE; 62562306a36Sopenharmony_ci if (!is_zero_ether_addr(macaddr)) 62662306a36Sopenharmony_ci memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN); 62762306a36Sopenharmony_ci else 62862306a36Sopenharmony_ci brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); 62962306a36Sopenharmony_ci 63062306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "interface_create", 63162306a36Sopenharmony_ci &iface_v1, 63262306a36Sopenharmony_ci sizeof(iface_v1)); 63362306a36Sopenharmony_ci if (err) { 63462306a36Sopenharmony_ci brcmf_info("failed to create interface(v1), err=%d\n", 63562306a36Sopenharmony_ci err); 63662306a36Sopenharmony_ci } else { 63762306a36Sopenharmony_ci brcmf_dbg(INFO, "interface created(v1)\n"); 63862306a36Sopenharmony_ci return 0; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci 64162306a36Sopenharmony_ci /* interface_create version 2 */ 64262306a36Sopenharmony_ci memset(&iface_v2, 0, sizeof(iface_v2)); 64362306a36Sopenharmony_ci iface_v2.ver = WL_INTERFACE_CREATE_VER_2; 64462306a36Sopenharmony_ci iface_v2.flags = WL_INTERFACE_MAC_USE; 64562306a36Sopenharmony_ci iface_v2.iftype = WL_INTERFACE_CREATE_STA; 64662306a36Sopenharmony_ci if (!is_zero_ether_addr(macaddr)) 64762306a36Sopenharmony_ci memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN); 64862306a36Sopenharmony_ci else 64962306a36Sopenharmony_ci brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); 65062306a36Sopenharmony_ci 65162306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "interface_create", 65262306a36Sopenharmony_ci &iface_v2, 65362306a36Sopenharmony_ci sizeof(iface_v2)); 65462306a36Sopenharmony_ci if (err) { 65562306a36Sopenharmony_ci brcmf_info("failed to create interface(v2), err=%d\n", 65662306a36Sopenharmony_ci err); 65762306a36Sopenharmony_ci } else { 65862306a36Sopenharmony_ci brcmf_dbg(INFO, "interface created(v2)\n"); 65962306a36Sopenharmony_ci return 0; 66062306a36Sopenharmony_ci } 66162306a36Sopenharmony_ci 66262306a36Sopenharmony_ci /* interface_create version 3+ */ 66362306a36Sopenharmony_ci /* get supported version from firmware side */ 66462306a36Sopenharmony_ci iface_create_ver = 0; 66562306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", 66662306a36Sopenharmony_ci &iface_create_ver); 66762306a36Sopenharmony_ci if (err) { 66862306a36Sopenharmony_ci brcmf_err("fail to get supported version, err=%d\n", err); 66962306a36Sopenharmony_ci return -EOPNOTSUPP; 67062306a36Sopenharmony_ci } 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_ci switch (iface_create_ver) { 67362306a36Sopenharmony_ci case WL_INTERFACE_CREATE_VER_3: 67462306a36Sopenharmony_ci memset(&iface_v3, 0, sizeof(iface_v3)); 67562306a36Sopenharmony_ci iface_v3.ver = WL_INTERFACE_CREATE_VER_3; 67662306a36Sopenharmony_ci iface_v3.flags = WL_INTERFACE_MAC_USE; 67762306a36Sopenharmony_ci iface_v3.iftype = WL_INTERFACE_CREATE_STA; 67862306a36Sopenharmony_ci if (!is_zero_ether_addr(macaddr)) 67962306a36Sopenharmony_ci memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN); 68062306a36Sopenharmony_ci else 68162306a36Sopenharmony_ci brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "interface_create", 68462306a36Sopenharmony_ci &iface_v3, 68562306a36Sopenharmony_ci sizeof(iface_v3)); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (!err) 68862306a36Sopenharmony_ci brcmf_dbg(INFO, "interface created(v3)\n"); 68962306a36Sopenharmony_ci break; 69062306a36Sopenharmony_ci default: 69162306a36Sopenharmony_ci brcmf_err("not support interface create(v%d)\n", 69262306a36Sopenharmony_ci iface_create_ver); 69362306a36Sopenharmony_ci err = -EOPNOTSUPP; 69462306a36Sopenharmony_ci break; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (err) { 69862306a36Sopenharmony_ci brcmf_info("station interface creation failed (%d)\n", 69962306a36Sopenharmony_ci err); 70062306a36Sopenharmony_ci return -EIO; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci return 0; 70462306a36Sopenharmony_ci} 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cistatic int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp) 70762306a36Sopenharmony_ci{ 70862306a36Sopenharmony_ci struct wl_interface_create_v1 iface_v1; 70962306a36Sopenharmony_ci struct wl_interface_create_v2 iface_v2; 71062306a36Sopenharmony_ci struct wl_interface_create_v3 iface_v3; 71162306a36Sopenharmony_ci u32 iface_create_ver; 71262306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 71362306a36Sopenharmony_ci struct brcmf_mbss_ssid_le mbss_ssid_le; 71462306a36Sopenharmony_ci int bsscfgidx; 71562306a36Sopenharmony_ci int err; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci /* interface_create version 1 */ 71862306a36Sopenharmony_ci memset(&iface_v1, 0, sizeof(iface_v1)); 71962306a36Sopenharmony_ci iface_v1.ver = WL_INTERFACE_CREATE_VER_1; 72062306a36Sopenharmony_ci iface_v1.flags = WL_INTERFACE_CREATE_AP | 72162306a36Sopenharmony_ci WL_INTERFACE_MAC_USE; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr); 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "interface_create", 72662306a36Sopenharmony_ci &iface_v1, 72762306a36Sopenharmony_ci sizeof(iface_v1)); 72862306a36Sopenharmony_ci if (err) { 72962306a36Sopenharmony_ci brcmf_info("failed to create interface(v1), err=%d\n", 73062306a36Sopenharmony_ci err); 73162306a36Sopenharmony_ci } else { 73262306a36Sopenharmony_ci brcmf_dbg(INFO, "interface created(v1)\n"); 73362306a36Sopenharmony_ci return 0; 73462306a36Sopenharmony_ci } 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci /* interface_create version 2 */ 73762306a36Sopenharmony_ci memset(&iface_v2, 0, sizeof(iface_v2)); 73862306a36Sopenharmony_ci iface_v2.ver = WL_INTERFACE_CREATE_VER_2; 73962306a36Sopenharmony_ci iface_v2.flags = WL_INTERFACE_MAC_USE; 74062306a36Sopenharmony_ci iface_v2.iftype = WL_INTERFACE_CREATE_AP; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr); 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "interface_create", 74562306a36Sopenharmony_ci &iface_v2, 74662306a36Sopenharmony_ci sizeof(iface_v2)); 74762306a36Sopenharmony_ci if (err) { 74862306a36Sopenharmony_ci brcmf_info("failed to create interface(v2), err=%d\n", 74962306a36Sopenharmony_ci err); 75062306a36Sopenharmony_ci } else { 75162306a36Sopenharmony_ci brcmf_dbg(INFO, "interface created(v2)\n"); 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci /* interface_create version 3+ */ 75662306a36Sopenharmony_ci /* get supported version from firmware side */ 75762306a36Sopenharmony_ci iface_create_ver = 0; 75862306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_get(ifp, "interface_create", 75962306a36Sopenharmony_ci &iface_create_ver); 76062306a36Sopenharmony_ci if (err) { 76162306a36Sopenharmony_ci brcmf_err("fail to get supported version, err=%d\n", err); 76262306a36Sopenharmony_ci return -EOPNOTSUPP; 76362306a36Sopenharmony_ci } 76462306a36Sopenharmony_ci 76562306a36Sopenharmony_ci switch (iface_create_ver) { 76662306a36Sopenharmony_ci case WL_INTERFACE_CREATE_VER_3: 76762306a36Sopenharmony_ci memset(&iface_v3, 0, sizeof(iface_v3)); 76862306a36Sopenharmony_ci iface_v3.ver = WL_INTERFACE_CREATE_VER_3; 76962306a36Sopenharmony_ci iface_v3.flags = WL_INTERFACE_MAC_USE; 77062306a36Sopenharmony_ci iface_v3.iftype = WL_INTERFACE_CREATE_AP; 77162306a36Sopenharmony_ci brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr); 77262306a36Sopenharmony_ci 77362306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "interface_create", 77462306a36Sopenharmony_ci &iface_v3, 77562306a36Sopenharmony_ci sizeof(iface_v3)); 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci if (!err) 77862306a36Sopenharmony_ci brcmf_dbg(INFO, "interface created(v3)\n"); 77962306a36Sopenharmony_ci break; 78062306a36Sopenharmony_ci default: 78162306a36Sopenharmony_ci brcmf_err("not support interface create(v%d)\n", 78262306a36Sopenharmony_ci iface_create_ver); 78362306a36Sopenharmony_ci err = -EOPNOTSUPP; 78462306a36Sopenharmony_ci break; 78562306a36Sopenharmony_ci } 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci if (err) { 78862306a36Sopenharmony_ci brcmf_info("Does not support interface_create (%d)\n", 78962306a36Sopenharmony_ci err); 79062306a36Sopenharmony_ci memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le)); 79162306a36Sopenharmony_ci bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr); 79262306a36Sopenharmony_ci if (bsscfgidx < 0) 79362306a36Sopenharmony_ci return bsscfgidx; 79462306a36Sopenharmony_ci 79562306a36Sopenharmony_ci mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx); 79662306a36Sopenharmony_ci mbss_ssid_le.SSID_len = cpu_to_le32(5); 79762306a36Sopenharmony_ci sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx); 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le, 80062306a36Sopenharmony_ci sizeof(mbss_ssid_le)); 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci if (err < 0) 80362306a36Sopenharmony_ci bphy_err(drvr, "setting ssid failed %d\n", err); 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci return err; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_ci/** 81062306a36Sopenharmony_ci * brcmf_apsta_add_vif() - create a new AP or STA virtual interface 81162306a36Sopenharmony_ci * 81262306a36Sopenharmony_ci * @wiphy: wiphy device of new interface. 81362306a36Sopenharmony_ci * @name: name of the new interface. 81462306a36Sopenharmony_ci * @params: contains mac address for AP or STA device. 81562306a36Sopenharmony_ci * @type: interface type. 81662306a36Sopenharmony_ci */ 81762306a36Sopenharmony_cistatic 81862306a36Sopenharmony_cistruct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name, 81962306a36Sopenharmony_ci struct vif_params *params, 82062306a36Sopenharmony_ci enum nl80211_iftype type) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 82362306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); 82462306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 82562306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 82662306a36Sopenharmony_ci int err; 82762306a36Sopenharmony_ci 82862306a36Sopenharmony_ci if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP) 82962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_ci if (brcmf_cfg80211_vif_event_armed(cfg)) 83262306a36Sopenharmony_ci return ERR_PTR(-EBUSY); 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci brcmf_dbg(INFO, "Adding vif \"%s\"\n", name); 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci vif = brcmf_alloc_vif(cfg, type); 83762306a36Sopenharmony_ci if (IS_ERR(vif)) 83862306a36Sopenharmony_ci return (struct wireless_dev *)vif; 83962306a36Sopenharmony_ci 84062306a36Sopenharmony_ci brcmf_cfg80211_arm_vif_event(cfg, vif); 84162306a36Sopenharmony_ci 84262306a36Sopenharmony_ci if (type == NL80211_IFTYPE_STATION) 84362306a36Sopenharmony_ci err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr); 84462306a36Sopenharmony_ci else 84562306a36Sopenharmony_ci err = brcmf_cfg80211_request_ap_if(ifp); 84662306a36Sopenharmony_ci if (err) { 84762306a36Sopenharmony_ci brcmf_cfg80211_arm_vif_event(cfg, NULL); 84862306a36Sopenharmony_ci goto fail; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci /* wait for firmware event */ 85262306a36Sopenharmony_ci err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD, 85362306a36Sopenharmony_ci BRCMF_VIF_EVENT_TIMEOUT); 85462306a36Sopenharmony_ci brcmf_cfg80211_arm_vif_event(cfg, NULL); 85562306a36Sopenharmony_ci if (!err) { 85662306a36Sopenharmony_ci bphy_err(drvr, "timeout occurred\n"); 85762306a36Sopenharmony_ci err = -EIO; 85862306a36Sopenharmony_ci goto fail; 85962306a36Sopenharmony_ci } 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci /* interface created in firmware */ 86262306a36Sopenharmony_ci ifp = vif->ifp; 86362306a36Sopenharmony_ci if (!ifp) { 86462306a36Sopenharmony_ci bphy_err(drvr, "no if pointer provided\n"); 86562306a36Sopenharmony_ci err = -ENOENT; 86662306a36Sopenharmony_ci goto fail; 86762306a36Sopenharmony_ci } 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1); 87062306a36Sopenharmony_ci err = brcmf_net_attach(ifp, true); 87162306a36Sopenharmony_ci if (err) { 87262306a36Sopenharmony_ci bphy_err(drvr, "Registering netdevice failed\n"); 87362306a36Sopenharmony_ci free_netdev(ifp->ndev); 87462306a36Sopenharmony_ci goto fail; 87562306a36Sopenharmony_ci } 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return &ifp->vif->wdev; 87862306a36Sopenharmony_ci 87962306a36Sopenharmony_cifail: 88062306a36Sopenharmony_ci brcmf_free_vif(vif); 88162306a36Sopenharmony_ci return ERR_PTR(err); 88262306a36Sopenharmony_ci} 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cistatic bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif) 88562306a36Sopenharmony_ci{ 88662306a36Sopenharmony_ci enum nl80211_iftype iftype; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci iftype = vif->wdev.iftype; 88962306a36Sopenharmony_ci return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO; 89062306a36Sopenharmony_ci} 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_cistatic bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci return vif->wdev.iftype == NL80211_IFTYPE_ADHOC; 89562306a36Sopenharmony_ci} 89662306a36Sopenharmony_ci 89762306a36Sopenharmony_ci/** 89862306a36Sopenharmony_ci * brcmf_mon_add_vif() - create monitor mode virtual interface 89962306a36Sopenharmony_ci * 90062306a36Sopenharmony_ci * @wiphy: wiphy device of new interface. 90162306a36Sopenharmony_ci * @name: name of the new interface. 90262306a36Sopenharmony_ci */ 90362306a36Sopenharmony_cistatic struct wireless_dev *brcmf_mon_add_vif(struct wiphy *wiphy, 90462306a36Sopenharmony_ci const char *name) 90562306a36Sopenharmony_ci{ 90662306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 90762306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 90862306a36Sopenharmony_ci struct net_device *ndev; 90962306a36Sopenharmony_ci struct brcmf_if *ifp; 91062306a36Sopenharmony_ci int err; 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci if (cfg->pub->mon_if) { 91362306a36Sopenharmony_ci err = -EEXIST; 91462306a36Sopenharmony_ci goto err_out; 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_MONITOR); 91862306a36Sopenharmony_ci if (IS_ERR(vif)) { 91962306a36Sopenharmony_ci err = PTR_ERR(vif); 92062306a36Sopenharmony_ci goto err_out; 92162306a36Sopenharmony_ci } 92262306a36Sopenharmony_ci 92362306a36Sopenharmony_ci ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, ether_setup); 92462306a36Sopenharmony_ci if (!ndev) { 92562306a36Sopenharmony_ci err = -ENOMEM; 92662306a36Sopenharmony_ci goto err_free_vif; 92762306a36Sopenharmony_ci } 92862306a36Sopenharmony_ci ndev->type = ARPHRD_IEEE80211_RADIOTAP; 92962306a36Sopenharmony_ci ndev->ieee80211_ptr = &vif->wdev; 93062306a36Sopenharmony_ci ndev->needs_free_netdev = true; 93162306a36Sopenharmony_ci ndev->priv_destructor = brcmf_cfg80211_free_netdev; 93262306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); 93362306a36Sopenharmony_ci 93462306a36Sopenharmony_ci ifp = netdev_priv(ndev); 93562306a36Sopenharmony_ci ifp->vif = vif; 93662306a36Sopenharmony_ci ifp->ndev = ndev; 93762306a36Sopenharmony_ci ifp->drvr = cfg->pub; 93862306a36Sopenharmony_ci 93962306a36Sopenharmony_ci vif->ifp = ifp; 94062306a36Sopenharmony_ci vif->wdev.netdev = ndev; 94162306a36Sopenharmony_ci 94262306a36Sopenharmony_ci err = brcmf_net_mon_attach(ifp); 94362306a36Sopenharmony_ci if (err) { 94462306a36Sopenharmony_ci brcmf_err("Failed to attach %s device\n", ndev->name); 94562306a36Sopenharmony_ci free_netdev(ndev); 94662306a36Sopenharmony_ci goto err_free_vif; 94762306a36Sopenharmony_ci } 94862306a36Sopenharmony_ci 94962306a36Sopenharmony_ci cfg->pub->mon_if = ifp; 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_ci return &vif->wdev; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_cierr_free_vif: 95462306a36Sopenharmony_ci brcmf_free_vif(vif); 95562306a36Sopenharmony_cierr_out: 95662306a36Sopenharmony_ci return ERR_PTR(err); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int brcmf_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev) 96062306a36Sopenharmony_ci{ 96162306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 96262306a36Sopenharmony_ci struct net_device *ndev = wdev->netdev; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci ndev->netdev_ops->ndo_stop(ndev); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci brcmf_net_detach(ndev, true); 96762306a36Sopenharmony_ci 96862306a36Sopenharmony_ci cfg->pub->mon_if = NULL; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return 0; 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy, 97462306a36Sopenharmony_ci const char *name, 97562306a36Sopenharmony_ci unsigned char name_assign_type, 97662306a36Sopenharmony_ci enum nl80211_iftype type, 97762306a36Sopenharmony_ci struct vif_params *params) 97862306a36Sopenharmony_ci{ 97962306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 98062306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 98162306a36Sopenharmony_ci struct wireless_dev *wdev; 98262306a36Sopenharmony_ci int err; 98362306a36Sopenharmony_ci 98462306a36Sopenharmony_ci brcmf_dbg(TRACE, "enter: %s type %d\n", name, type); 98562306a36Sopenharmony_ci err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type); 98662306a36Sopenharmony_ci if (err) { 98762306a36Sopenharmony_ci bphy_err(drvr, "iface validation failed: err=%d\n", err); 98862306a36Sopenharmony_ci return ERR_PTR(err); 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci switch (type) { 99162306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 99262306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 99362306a36Sopenharmony_ci case NL80211_IFTYPE_WDS: 99462306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 99562306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 99662306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 99762306a36Sopenharmony_ci return brcmf_mon_add_vif(wiphy, name); 99862306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 99962306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 100062306a36Sopenharmony_ci wdev = brcmf_apsta_add_vif(wiphy, name, params, type); 100162306a36Sopenharmony_ci break; 100262306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 100362306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 100462306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 100562306a36Sopenharmony_ci wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params); 100662306a36Sopenharmony_ci break; 100762306a36Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 100862306a36Sopenharmony_ci default: 100962306a36Sopenharmony_ci return ERR_PTR(-EINVAL); 101062306a36Sopenharmony_ci } 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci if (IS_ERR(wdev)) 101362306a36Sopenharmony_ci bphy_err(drvr, "add iface %s type %d failed: err=%d\n", name, 101462306a36Sopenharmony_ci type, (int)PTR_ERR(wdev)); 101562306a36Sopenharmony_ci else 101662306a36Sopenharmony_ci brcmf_cfg80211_update_proto_addr_mode(wdev); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci return wdev; 101962306a36Sopenharmony_ci} 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_cistatic void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc) 102262306a36Sopenharmony_ci{ 102362306a36Sopenharmony_ci if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC)) 102462306a36Sopenharmony_ci brcmf_set_mpc(ifp, mpc); 102562306a36Sopenharmony_ci} 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_civoid brcmf_set_mpc(struct brcmf_if *ifp, int mpc) 102862306a36Sopenharmony_ci{ 102962306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 103062306a36Sopenharmony_ci s32 err = 0; 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_ci if (check_vif_up(ifp->vif)) { 103362306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc); 103462306a36Sopenharmony_ci if (err) { 103562306a36Sopenharmony_ci bphy_err(drvr, "fail to set mpc\n"); 103662306a36Sopenharmony_ci return; 103762306a36Sopenharmony_ci } 103862306a36Sopenharmony_ci brcmf_dbg(INFO, "MPC : %d\n", mpc); 103962306a36Sopenharmony_ci } 104062306a36Sopenharmony_ci} 104162306a36Sopenharmony_ci 104262306a36Sopenharmony_cistatic void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le, 104362306a36Sopenharmony_ci struct brcmf_scan_params_le *params_le) 104462306a36Sopenharmony_ci{ 104562306a36Sopenharmony_ci size_t params_size; 104662306a36Sopenharmony_ci u32 ch; 104762306a36Sopenharmony_ci int n_channels, n_ssids; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci memcpy(¶ms_le->ssid_le, ¶ms_v2_le->ssid_le, 105062306a36Sopenharmony_ci sizeof(params_le->ssid_le)); 105162306a36Sopenharmony_ci memcpy(¶ms_le->bssid, ¶ms_v2_le->bssid, 105262306a36Sopenharmony_ci sizeof(params_le->bssid)); 105362306a36Sopenharmony_ci 105462306a36Sopenharmony_ci params_le->bss_type = params_v2_le->bss_type; 105562306a36Sopenharmony_ci params_le->scan_type = le32_to_cpu(params_v2_le->scan_type); 105662306a36Sopenharmony_ci params_le->nprobes = params_v2_le->nprobes; 105762306a36Sopenharmony_ci params_le->active_time = params_v2_le->active_time; 105862306a36Sopenharmony_ci params_le->passive_time = params_v2_le->passive_time; 105962306a36Sopenharmony_ci params_le->home_time = params_v2_le->home_time; 106062306a36Sopenharmony_ci params_le->channel_num = params_v2_le->channel_num; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci ch = le32_to_cpu(params_v2_le->channel_num); 106362306a36Sopenharmony_ci n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK; 106462306a36Sopenharmony_ci n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci params_size = sizeof(u16) * n_channels; 106762306a36Sopenharmony_ci if (n_ssids > 0) { 106862306a36Sopenharmony_ci params_size = roundup(params_size, sizeof(u32)); 106962306a36Sopenharmony_ci params_size += sizeof(struct brcmf_ssid_le) * n_ssids; 107062306a36Sopenharmony_ci } 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci memcpy(¶ms_le->channel_list[0], 107362306a36Sopenharmony_ci ¶ms_v2_le->channel_list[0], params_size); 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_cistatic void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg, 107762306a36Sopenharmony_ci struct brcmf_scan_params_v2_le *params_le, 107862306a36Sopenharmony_ci struct cfg80211_scan_request *request) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci u32 n_ssids; 108162306a36Sopenharmony_ci u32 n_channels; 108262306a36Sopenharmony_ci s32 i; 108362306a36Sopenharmony_ci s32 offset; 108462306a36Sopenharmony_ci u16 chanspec; 108562306a36Sopenharmony_ci char *ptr; 108662306a36Sopenharmony_ci int length; 108762306a36Sopenharmony_ci struct brcmf_ssid_le ssid_le; 108862306a36Sopenharmony_ci 108962306a36Sopenharmony_ci eth_broadcast_addr(params_le->bssid); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE; 109262306a36Sopenharmony_ci 109362306a36Sopenharmony_ci params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2); 109462306a36Sopenharmony_ci params_le->bss_type = DOT11_BSSTYPE_ANY; 109562306a36Sopenharmony_ci params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE); 109662306a36Sopenharmony_ci params_le->channel_num = 0; 109762306a36Sopenharmony_ci params_le->nprobes = cpu_to_le32(-1); 109862306a36Sopenharmony_ci params_le->active_time = cpu_to_le32(-1); 109962306a36Sopenharmony_ci params_le->passive_time = cpu_to_le32(-1); 110062306a36Sopenharmony_ci params_le->home_time = cpu_to_le32(-1); 110162306a36Sopenharmony_ci memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le)); 110262306a36Sopenharmony_ci 110362306a36Sopenharmony_ci /* Scan abort */ 110462306a36Sopenharmony_ci if (!request) { 110562306a36Sopenharmony_ci length += sizeof(u16); 110662306a36Sopenharmony_ci params_le->channel_num = cpu_to_le32(1); 110762306a36Sopenharmony_ci params_le->channel_list[0] = cpu_to_le16(-1); 110862306a36Sopenharmony_ci params_le->length = cpu_to_le16(length); 110962306a36Sopenharmony_ci return; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci n_ssids = request->n_ssids; 111362306a36Sopenharmony_ci n_channels = request->n_channels; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* Copy channel array if applicable */ 111662306a36Sopenharmony_ci brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n", 111762306a36Sopenharmony_ci n_channels); 111862306a36Sopenharmony_ci if (n_channels > 0) { 111962306a36Sopenharmony_ci length += roundup(sizeof(u16) * n_channels, sizeof(u32)); 112062306a36Sopenharmony_ci for (i = 0; i < n_channels; i++) { 112162306a36Sopenharmony_ci chanspec = channel_to_chanspec(&cfg->d11inf, 112262306a36Sopenharmony_ci request->channels[i]); 112362306a36Sopenharmony_ci brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n", 112462306a36Sopenharmony_ci request->channels[i]->hw_value, chanspec); 112562306a36Sopenharmony_ci params_le->channel_list[i] = cpu_to_le16(chanspec); 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci } else { 112862306a36Sopenharmony_ci brcmf_dbg(SCAN, "Scanning all channels\n"); 112962306a36Sopenharmony_ci } 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* Copy ssid array if applicable */ 113262306a36Sopenharmony_ci brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids); 113362306a36Sopenharmony_ci if (n_ssids > 0) { 113462306a36Sopenharmony_ci offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) + 113562306a36Sopenharmony_ci n_channels * sizeof(u16); 113662306a36Sopenharmony_ci offset = roundup(offset, sizeof(u32)); 113762306a36Sopenharmony_ci length += sizeof(ssid_le) * n_ssids, 113862306a36Sopenharmony_ci ptr = (char *)params_le + offset; 113962306a36Sopenharmony_ci for (i = 0; i < n_ssids; i++) { 114062306a36Sopenharmony_ci memset(&ssid_le, 0, sizeof(ssid_le)); 114162306a36Sopenharmony_ci ssid_le.SSID_len = 114262306a36Sopenharmony_ci cpu_to_le32(request->ssids[i].ssid_len); 114362306a36Sopenharmony_ci memcpy(ssid_le.SSID, request->ssids[i].ssid, 114462306a36Sopenharmony_ci request->ssids[i].ssid_len); 114562306a36Sopenharmony_ci if (!ssid_le.SSID_len) 114662306a36Sopenharmony_ci brcmf_dbg(SCAN, "%d: Broadcast scan\n", i); 114762306a36Sopenharmony_ci else 114862306a36Sopenharmony_ci brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n", 114962306a36Sopenharmony_ci i, ssid_le.SSID, ssid_le.SSID_len); 115062306a36Sopenharmony_ci memcpy(ptr, &ssid_le, sizeof(ssid_le)); 115162306a36Sopenharmony_ci ptr += sizeof(ssid_le); 115262306a36Sopenharmony_ci } 115362306a36Sopenharmony_ci } else { 115462306a36Sopenharmony_ci brcmf_dbg(SCAN, "Performing passive scan\n"); 115562306a36Sopenharmony_ci params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE); 115662306a36Sopenharmony_ci } 115762306a36Sopenharmony_ci params_le->length = cpu_to_le16(length); 115862306a36Sopenharmony_ci /* Adding mask to channel numbers */ 115962306a36Sopenharmony_ci params_le->channel_num = 116062306a36Sopenharmony_ci cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) | 116162306a36Sopenharmony_ci (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK)); 116262306a36Sopenharmony_ci} 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_cis32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg, 116562306a36Sopenharmony_ci struct brcmf_if *ifp, bool aborted, 116662306a36Sopenharmony_ci bool fw_abort) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 116962306a36Sopenharmony_ci struct brcmf_scan_params_v2_le params_v2_le; 117062306a36Sopenharmony_ci struct cfg80211_scan_request *scan_request; 117162306a36Sopenharmony_ci u64 reqid; 117262306a36Sopenharmony_ci u32 bucket; 117362306a36Sopenharmony_ci s32 err = 0; 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci brcmf_dbg(SCAN, "Enter\n"); 117662306a36Sopenharmony_ci 117762306a36Sopenharmony_ci /* clear scan request, because the FW abort can cause a second call */ 117862306a36Sopenharmony_ci /* to this functon and might cause a double cfg80211_scan_done */ 117962306a36Sopenharmony_ci scan_request = cfg->scan_request; 118062306a36Sopenharmony_ci cfg->scan_request = NULL; 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_ci timer_delete_sync(&cfg->escan_timeout); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci if (fw_abort) { 118562306a36Sopenharmony_ci /* Do a scan abort to stop the driver's scan engine */ 118662306a36Sopenharmony_ci brcmf_dbg(SCAN, "ABORT scan in firmware\n"); 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci brcmf_escan_prep(cfg, ¶ms_v2_le, NULL); 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci /* E-Scan (or anyother type) can be aborted by SCAN */ 119162306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) { 119262306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, 119362306a36Sopenharmony_ci ¶ms_v2_le, 119462306a36Sopenharmony_ci sizeof(params_v2_le)); 119562306a36Sopenharmony_ci } else { 119662306a36Sopenharmony_ci struct brcmf_scan_params_le params_le; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci brcmf_scan_params_v2_to_v1(¶ms_v2_le, ¶ms_le); 119962306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN, 120062306a36Sopenharmony_ci ¶ms_le, 120162306a36Sopenharmony_ci sizeof(params_le)); 120262306a36Sopenharmony_ci } 120362306a36Sopenharmony_ci 120462306a36Sopenharmony_ci if (err) 120562306a36Sopenharmony_ci bphy_err(drvr, "Scan abort failed\n"); 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci brcmf_scan_config_mpc(ifp, 1); 120962306a36Sopenharmony_ci 121062306a36Sopenharmony_ci /* 121162306a36Sopenharmony_ci * e-scan can be initiated internally 121262306a36Sopenharmony_ci * which takes precedence. 121362306a36Sopenharmony_ci */ 121462306a36Sopenharmony_ci if (cfg->int_escan_map) { 121562306a36Sopenharmony_ci brcmf_dbg(SCAN, "scheduled scan completed (%x)\n", 121662306a36Sopenharmony_ci cfg->int_escan_map); 121762306a36Sopenharmony_ci while (cfg->int_escan_map) { 121862306a36Sopenharmony_ci bucket = __ffs(cfg->int_escan_map); 121962306a36Sopenharmony_ci cfg->int_escan_map &= ~BIT(bucket); 122062306a36Sopenharmony_ci reqid = brcmf_pno_find_reqid_by_bucket(cfg->pno, 122162306a36Sopenharmony_ci bucket); 122262306a36Sopenharmony_ci if (!aborted) { 122362306a36Sopenharmony_ci brcmf_dbg(SCAN, "report results: reqid=%llu\n", 122462306a36Sopenharmony_ci reqid); 122562306a36Sopenharmony_ci cfg80211_sched_scan_results(cfg_to_wiphy(cfg), 122662306a36Sopenharmony_ci reqid); 122762306a36Sopenharmony_ci } 122862306a36Sopenharmony_ci } 122962306a36Sopenharmony_ci } else if (scan_request) { 123062306a36Sopenharmony_ci struct cfg80211_scan_info info = { 123162306a36Sopenharmony_ci .aborted = aborted, 123262306a36Sopenharmony_ci }; 123362306a36Sopenharmony_ci 123462306a36Sopenharmony_ci brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n", 123562306a36Sopenharmony_ci aborted ? "Aborted" : "Done"); 123662306a36Sopenharmony_ci cfg80211_scan_done(scan_request, &info); 123762306a36Sopenharmony_ci } 123862306a36Sopenharmony_ci if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) 123962306a36Sopenharmony_ci brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n"); 124062306a36Sopenharmony_ci 124162306a36Sopenharmony_ci return err; 124262306a36Sopenharmony_ci} 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_cistatic int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy, 124562306a36Sopenharmony_ci struct wireless_dev *wdev) 124662306a36Sopenharmony_ci{ 124762306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 124862306a36Sopenharmony_ci struct net_device *ndev = wdev->netdev; 124962306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 125062306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 125162306a36Sopenharmony_ci int ret; 125262306a36Sopenharmony_ci int err; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_ci brcmf_cfg80211_arm_vif_event(cfg, ifp->vif); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci err = brcmf_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0); 125762306a36Sopenharmony_ci if (err) { 125862306a36Sopenharmony_ci bphy_err(drvr, "interface_remove failed %d\n", err); 125962306a36Sopenharmony_ci goto err_unarm; 126062306a36Sopenharmony_ci } 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci /* wait for firmware event */ 126362306a36Sopenharmony_ci ret = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL, 126462306a36Sopenharmony_ci BRCMF_VIF_EVENT_TIMEOUT); 126562306a36Sopenharmony_ci if (!ret) { 126662306a36Sopenharmony_ci bphy_err(drvr, "timeout occurred\n"); 126762306a36Sopenharmony_ci err = -EIO; 126862306a36Sopenharmony_ci goto err_unarm; 126962306a36Sopenharmony_ci } 127062306a36Sopenharmony_ci 127162306a36Sopenharmony_ci brcmf_remove_interface(ifp, true); 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_cierr_unarm: 127462306a36Sopenharmony_ci brcmf_cfg80211_arm_vif_event(cfg, NULL); 127562306a36Sopenharmony_ci return err; 127662306a36Sopenharmony_ci} 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_cistatic 127962306a36Sopenharmony_ciint brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev) 128062306a36Sopenharmony_ci{ 128162306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 128262306a36Sopenharmony_ci struct net_device *ndev = wdev->netdev; 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci if (ndev && ndev == cfg_to_ndev(cfg)) 128562306a36Sopenharmony_ci return -ENOTSUPP; 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci /* vif event pending in firmware */ 128862306a36Sopenharmony_ci if (brcmf_cfg80211_vif_event_armed(cfg)) 128962306a36Sopenharmony_ci return -EBUSY; 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci if (ndev) { 129262306a36Sopenharmony_ci if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) && 129362306a36Sopenharmony_ci cfg->escan_info.ifp == netdev_priv(ndev)) 129462306a36Sopenharmony_ci brcmf_notify_escan_complete(cfg, netdev_priv(ndev), 129562306a36Sopenharmony_ci true, true); 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_ci brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1); 129862306a36Sopenharmony_ci } 129962306a36Sopenharmony_ci 130062306a36Sopenharmony_ci switch (wdev->iftype) { 130162306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 130262306a36Sopenharmony_ci case NL80211_IFTYPE_AP_VLAN: 130362306a36Sopenharmony_ci case NL80211_IFTYPE_WDS: 130462306a36Sopenharmony_ci case NL80211_IFTYPE_MESH_POINT: 130562306a36Sopenharmony_ci return -EOPNOTSUPP; 130662306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 130762306a36Sopenharmony_ci return brcmf_mon_del_vif(wiphy, wdev); 130862306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 130962306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 131062306a36Sopenharmony_ci return brcmf_cfg80211_del_apsta_iface(wiphy, wdev); 131162306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_CLIENT: 131262306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 131362306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_DEVICE: 131462306a36Sopenharmony_ci return brcmf_p2p_del_vif(wiphy, wdev); 131562306a36Sopenharmony_ci case NL80211_IFTYPE_UNSPECIFIED: 131662306a36Sopenharmony_ci default: 131762306a36Sopenharmony_ci return -EINVAL; 131862306a36Sopenharmony_ci } 131962306a36Sopenharmony_ci return -EOPNOTSUPP; 132062306a36Sopenharmony_ci} 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic s32 132362306a36Sopenharmony_cibrcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev, 132462306a36Sopenharmony_ci enum nl80211_iftype type, 132562306a36Sopenharmony_ci struct vif_params *params) 132662306a36Sopenharmony_ci{ 132762306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 132862306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 132962306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif = ifp->vif; 133062306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 133162306a36Sopenharmony_ci s32 infra = 0; 133262306a36Sopenharmony_ci s32 ap = 0; 133362306a36Sopenharmony_ci s32 err = 0; 133462306a36Sopenharmony_ci 133562306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx, 133662306a36Sopenharmony_ci type); 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci /* WAR: There are a number of p2p interface related problems which 133962306a36Sopenharmony_ci * need to be handled initially (before doing the validate). 134062306a36Sopenharmony_ci * wpa_supplicant tends to do iface changes on p2p device/client/go 134162306a36Sopenharmony_ci * which are not always possible/allowed. However we need to return 134262306a36Sopenharmony_ci * OK otherwise the wpa_supplicant wont start. The situation differs 134362306a36Sopenharmony_ci * on configuration and setup (p2pon=1 module param). The first check 134462306a36Sopenharmony_ci * is to see if the request is a change to station for p2p iface. 134562306a36Sopenharmony_ci */ 134662306a36Sopenharmony_ci if ((type == NL80211_IFTYPE_STATION) && 134762306a36Sopenharmony_ci ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) || 134862306a36Sopenharmony_ci (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) || 134962306a36Sopenharmony_ci (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) { 135062306a36Sopenharmony_ci brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n"); 135162306a36Sopenharmony_ci /* Now depending on whether module param p2pon=1 was used the 135262306a36Sopenharmony_ci * response needs to be either 0 or EOPNOTSUPP. The reason is 135362306a36Sopenharmony_ci * that if p2pon=1 is used, but a newer supplicant is used then 135462306a36Sopenharmony_ci * we should return an error, as this combination wont work. 135562306a36Sopenharmony_ci * In other situations 0 is returned and supplicant will start 135662306a36Sopenharmony_ci * normally. It will give a trace in cfg80211, but it is the 135762306a36Sopenharmony_ci * only way to get it working. Unfortunately this will result 135862306a36Sopenharmony_ci * in situation where we wont support new supplicant in 135962306a36Sopenharmony_ci * combination with module param p2pon=1, but that is the way 136062306a36Sopenharmony_ci * it is. If the user tries this then unloading of driver might 136162306a36Sopenharmony_ci * fail/lock. 136262306a36Sopenharmony_ci */ 136362306a36Sopenharmony_ci if (cfg->p2p.p2pdev_dynamically) 136462306a36Sopenharmony_ci return -EOPNOTSUPP; 136562306a36Sopenharmony_ci else 136662306a36Sopenharmony_ci return 0; 136762306a36Sopenharmony_ci } 136862306a36Sopenharmony_ci err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type); 136962306a36Sopenharmony_ci if (err) { 137062306a36Sopenharmony_ci bphy_err(drvr, "iface validation failed: err=%d\n", err); 137162306a36Sopenharmony_ci return err; 137262306a36Sopenharmony_ci } 137362306a36Sopenharmony_ci switch (type) { 137462306a36Sopenharmony_ci case NL80211_IFTYPE_MONITOR: 137562306a36Sopenharmony_ci case NL80211_IFTYPE_WDS: 137662306a36Sopenharmony_ci bphy_err(drvr, "type (%d) : currently we do not support this type\n", 137762306a36Sopenharmony_ci type); 137862306a36Sopenharmony_ci return -EOPNOTSUPP; 137962306a36Sopenharmony_ci case NL80211_IFTYPE_ADHOC: 138062306a36Sopenharmony_ci infra = 0; 138162306a36Sopenharmony_ci break; 138262306a36Sopenharmony_ci case NL80211_IFTYPE_STATION: 138362306a36Sopenharmony_ci infra = 1; 138462306a36Sopenharmony_ci break; 138562306a36Sopenharmony_ci case NL80211_IFTYPE_AP: 138662306a36Sopenharmony_ci case NL80211_IFTYPE_P2P_GO: 138762306a36Sopenharmony_ci ap = 1; 138862306a36Sopenharmony_ci break; 138962306a36Sopenharmony_ci default: 139062306a36Sopenharmony_ci err = -EINVAL; 139162306a36Sopenharmony_ci goto done; 139262306a36Sopenharmony_ci } 139362306a36Sopenharmony_ci 139462306a36Sopenharmony_ci if (ap) { 139562306a36Sopenharmony_ci if (type == NL80211_IFTYPE_P2P_GO) { 139662306a36Sopenharmony_ci brcmf_dbg(INFO, "IF Type = P2P GO\n"); 139762306a36Sopenharmony_ci err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO); 139862306a36Sopenharmony_ci } 139962306a36Sopenharmony_ci if (!err) { 140062306a36Sopenharmony_ci brcmf_dbg(INFO, "IF Type = AP\n"); 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci } else { 140362306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra); 140462306a36Sopenharmony_ci if (err) { 140562306a36Sopenharmony_ci bphy_err(drvr, "WLC_SET_INFRA error (%d)\n", err); 140662306a36Sopenharmony_ci err = -EAGAIN; 140762306a36Sopenharmony_ci goto done; 140862306a36Sopenharmony_ci } 140962306a36Sopenharmony_ci brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ? 141062306a36Sopenharmony_ci "Adhoc" : "Infra"); 141162306a36Sopenharmony_ci } 141262306a36Sopenharmony_ci ndev->ieee80211_ptr->iftype = type; 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci brcmf_cfg80211_update_proto_addr_mode(&vif->wdev); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_cidone: 141762306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_ci return err; 142062306a36Sopenharmony_ci} 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_cistatic s32 142362306a36Sopenharmony_cibrcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp, 142462306a36Sopenharmony_ci struct cfg80211_scan_request *request) 142562306a36Sopenharmony_ci{ 142662306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 142762306a36Sopenharmony_ci s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE + 142862306a36Sopenharmony_ci offsetof(struct brcmf_escan_params_le, params_v2_le); 142962306a36Sopenharmony_ci struct brcmf_escan_params_le *params; 143062306a36Sopenharmony_ci s32 err = 0; 143162306a36Sopenharmony_ci 143262306a36Sopenharmony_ci brcmf_dbg(SCAN, "E-SCAN START\n"); 143362306a36Sopenharmony_ci 143462306a36Sopenharmony_ci if (request != NULL) { 143562306a36Sopenharmony_ci /* Allocate space for populating ssids in struct */ 143662306a36Sopenharmony_ci params_size += sizeof(u32) * ((request->n_channels + 1) / 2); 143762306a36Sopenharmony_ci 143862306a36Sopenharmony_ci /* Allocate space for populating ssids in struct */ 143962306a36Sopenharmony_ci params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids; 144062306a36Sopenharmony_ci } 144162306a36Sopenharmony_ci 144262306a36Sopenharmony_ci params = kzalloc(params_size, GFP_KERNEL); 144362306a36Sopenharmony_ci if (!params) { 144462306a36Sopenharmony_ci err = -ENOMEM; 144562306a36Sopenharmony_ci goto exit; 144662306a36Sopenharmony_ci } 144762306a36Sopenharmony_ci BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN); 144862306a36Sopenharmony_ci brcmf_escan_prep(cfg, ¶ms->params_v2_le, request); 144962306a36Sopenharmony_ci 145062306a36Sopenharmony_ci params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2); 145162306a36Sopenharmony_ci 145262306a36Sopenharmony_ci if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) { 145362306a36Sopenharmony_ci struct brcmf_escan_params_le *params_v1; 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE; 145662306a36Sopenharmony_ci params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE; 145762306a36Sopenharmony_ci params_v1 = kzalloc(params_size, GFP_KERNEL); 145862306a36Sopenharmony_ci if (!params_v1) { 145962306a36Sopenharmony_ci err = -ENOMEM; 146062306a36Sopenharmony_ci goto exit_params; 146162306a36Sopenharmony_ci } 146262306a36Sopenharmony_ci params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION); 146362306a36Sopenharmony_ci brcmf_scan_params_v2_to_v1(¶ms->params_v2_le, ¶ms_v1->params_le); 146462306a36Sopenharmony_ci kfree(params); 146562306a36Sopenharmony_ci params = params_v1; 146662306a36Sopenharmony_ci } 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci params->action = cpu_to_le16(WL_ESCAN_ACTION_START); 146962306a36Sopenharmony_ci params->sync_id = cpu_to_le16(0x1234); 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size); 147262306a36Sopenharmony_ci if (err) { 147362306a36Sopenharmony_ci if (err == -EBUSY) 147462306a36Sopenharmony_ci brcmf_dbg(INFO, "system busy : escan canceled\n"); 147562306a36Sopenharmony_ci else 147662306a36Sopenharmony_ci bphy_err(drvr, "error (%d)\n", err); 147762306a36Sopenharmony_ci } 147862306a36Sopenharmony_ci 147962306a36Sopenharmony_ciexit_params: 148062306a36Sopenharmony_ci kfree(params); 148162306a36Sopenharmony_ciexit: 148262306a36Sopenharmony_ci return err; 148362306a36Sopenharmony_ci} 148462306a36Sopenharmony_ci 148562306a36Sopenharmony_cistatic s32 148662306a36Sopenharmony_cibrcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request) 148762306a36Sopenharmony_ci{ 148862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = ifp->drvr->config; 148962306a36Sopenharmony_ci s32 err; 149062306a36Sopenharmony_ci struct brcmf_scan_results *results; 149162306a36Sopenharmony_ci struct escan_info *escan = &cfg->escan_info; 149262306a36Sopenharmony_ci 149362306a36Sopenharmony_ci brcmf_dbg(SCAN, "Enter\n"); 149462306a36Sopenharmony_ci escan->ifp = ifp; 149562306a36Sopenharmony_ci escan->wiphy = cfg->wiphy; 149662306a36Sopenharmony_ci escan->escan_state = WL_ESCAN_STATE_SCANNING; 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci brcmf_scan_config_mpc(ifp, 0); 149962306a36Sopenharmony_ci results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; 150062306a36Sopenharmony_ci results->version = 0; 150162306a36Sopenharmony_ci results->count = 0; 150262306a36Sopenharmony_ci results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci err = escan->run(cfg, ifp, request); 150562306a36Sopenharmony_ci if (err) 150662306a36Sopenharmony_ci brcmf_scan_config_mpc(ifp, 1); 150762306a36Sopenharmony_ci return err; 150862306a36Sopenharmony_ci} 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_cistatic s32 151162306a36Sopenharmony_cibrcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request) 151262306a36Sopenharmony_ci{ 151362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 151462306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 151562306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 151662306a36Sopenharmony_ci s32 err = 0; 151762306a36Sopenharmony_ci 151862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 151962306a36Sopenharmony_ci vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev); 152062306a36Sopenharmony_ci if (!check_vif_up(vif)) 152162306a36Sopenharmony_ci return -EIO; 152262306a36Sopenharmony_ci 152362306a36Sopenharmony_ci if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { 152462306a36Sopenharmony_ci bphy_err(drvr, "Scanning already: status (%lu)\n", 152562306a36Sopenharmony_ci cfg->scan_status); 152662306a36Sopenharmony_ci return -EAGAIN; 152762306a36Sopenharmony_ci } 152862306a36Sopenharmony_ci if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) { 152962306a36Sopenharmony_ci bphy_err(drvr, "Scanning being aborted: status (%lu)\n", 153062306a36Sopenharmony_ci cfg->scan_status); 153162306a36Sopenharmony_ci return -EAGAIN; 153262306a36Sopenharmony_ci } 153362306a36Sopenharmony_ci if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { 153462306a36Sopenharmony_ci bphy_err(drvr, "Scanning suppressed: status (%lu)\n", 153562306a36Sopenharmony_ci cfg->scan_status); 153662306a36Sopenharmony_ci return -EAGAIN; 153762306a36Sopenharmony_ci } 153862306a36Sopenharmony_ci if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state)) { 153962306a36Sopenharmony_ci bphy_err(drvr, "Connecting: status (%lu)\n", vif->sme_state); 154062306a36Sopenharmony_ci return -EAGAIN; 154162306a36Sopenharmony_ci } 154262306a36Sopenharmony_ci 154362306a36Sopenharmony_ci /* If scan req comes for p2p0, send it over primary I/F */ 154462306a36Sopenharmony_ci if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) 154562306a36Sopenharmony_ci vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif; 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci brcmf_dbg(SCAN, "START ESCAN\n"); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci cfg->scan_request = request; 155062306a36Sopenharmony_ci set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); 155162306a36Sopenharmony_ci 155262306a36Sopenharmony_ci cfg->escan_info.run = brcmf_run_escan; 155362306a36Sopenharmony_ci err = brcmf_p2p_scan_prep(wiphy, request, vif); 155462306a36Sopenharmony_ci if (err) 155562306a36Sopenharmony_ci goto scan_out; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG, 155862306a36Sopenharmony_ci request->ie, request->ie_len); 155962306a36Sopenharmony_ci if (err) 156062306a36Sopenharmony_ci goto scan_out; 156162306a36Sopenharmony_ci 156262306a36Sopenharmony_ci err = brcmf_do_escan(vif->ifp, request); 156362306a36Sopenharmony_ci if (err) 156462306a36Sopenharmony_ci goto scan_out; 156562306a36Sopenharmony_ci 156662306a36Sopenharmony_ci /* Arm scan timeout timer */ 156762306a36Sopenharmony_ci mod_timer(&cfg->escan_timeout, 156862306a36Sopenharmony_ci jiffies + msecs_to_jiffies(BRCMF_ESCAN_TIMER_INTERVAL_MS)); 156962306a36Sopenharmony_ci 157062306a36Sopenharmony_ci return 0; 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_ciscan_out: 157362306a36Sopenharmony_ci bphy_err(drvr, "scan error (%d)\n", err); 157462306a36Sopenharmony_ci clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); 157562306a36Sopenharmony_ci cfg->scan_request = NULL; 157662306a36Sopenharmony_ci return err; 157762306a36Sopenharmony_ci} 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_cistatic s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold) 158062306a36Sopenharmony_ci{ 158162306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 158262306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 158362306a36Sopenharmony_ci s32 err = 0; 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "rtsthresh", rts_threshold); 158662306a36Sopenharmony_ci if (err) 158762306a36Sopenharmony_ci bphy_err(drvr, "Error (%d)\n", err); 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci return err; 159062306a36Sopenharmony_ci} 159162306a36Sopenharmony_ci 159262306a36Sopenharmony_cistatic s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold) 159362306a36Sopenharmony_ci{ 159462306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 159562306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 159662306a36Sopenharmony_ci s32 err = 0; 159762306a36Sopenharmony_ci 159862306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "fragthresh", 159962306a36Sopenharmony_ci frag_threshold); 160062306a36Sopenharmony_ci if (err) 160162306a36Sopenharmony_ci bphy_err(drvr, "Error (%d)\n", err); 160262306a36Sopenharmony_ci 160362306a36Sopenharmony_ci return err; 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_cistatic s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l) 160762306a36Sopenharmony_ci{ 160862306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 160962306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 161062306a36Sopenharmony_ci s32 err = 0; 161162306a36Sopenharmony_ci u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, cmd, retry); 161462306a36Sopenharmony_ci if (err) { 161562306a36Sopenharmony_ci bphy_err(drvr, "cmd (%d) , error (%d)\n", cmd, err); 161662306a36Sopenharmony_ci return err; 161762306a36Sopenharmony_ci } 161862306a36Sopenharmony_ci return err; 161962306a36Sopenharmony_ci} 162062306a36Sopenharmony_ci 162162306a36Sopenharmony_cistatic s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed) 162262306a36Sopenharmony_ci{ 162362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 162462306a36Sopenharmony_ci struct net_device *ndev = cfg_to_ndev(cfg); 162562306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 162662306a36Sopenharmony_ci s32 err = 0; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 162962306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 163062306a36Sopenharmony_ci return -EIO; 163162306a36Sopenharmony_ci 163262306a36Sopenharmony_ci if (changed & WIPHY_PARAM_RTS_THRESHOLD && 163362306a36Sopenharmony_ci (cfg->conf->rts_threshold != wiphy->rts_threshold)) { 163462306a36Sopenharmony_ci cfg->conf->rts_threshold = wiphy->rts_threshold; 163562306a36Sopenharmony_ci err = brcmf_set_rts(ndev, cfg->conf->rts_threshold); 163662306a36Sopenharmony_ci if (!err) 163762306a36Sopenharmony_ci goto done; 163862306a36Sopenharmony_ci } 163962306a36Sopenharmony_ci if (changed & WIPHY_PARAM_FRAG_THRESHOLD && 164062306a36Sopenharmony_ci (cfg->conf->frag_threshold != wiphy->frag_threshold)) { 164162306a36Sopenharmony_ci cfg->conf->frag_threshold = wiphy->frag_threshold; 164262306a36Sopenharmony_ci err = brcmf_set_frag(ndev, cfg->conf->frag_threshold); 164362306a36Sopenharmony_ci if (!err) 164462306a36Sopenharmony_ci goto done; 164562306a36Sopenharmony_ci } 164662306a36Sopenharmony_ci if (changed & WIPHY_PARAM_RETRY_LONG 164762306a36Sopenharmony_ci && (cfg->conf->retry_long != wiphy->retry_long)) { 164862306a36Sopenharmony_ci cfg->conf->retry_long = wiphy->retry_long; 164962306a36Sopenharmony_ci err = brcmf_set_retry(ndev, cfg->conf->retry_long, true); 165062306a36Sopenharmony_ci if (!err) 165162306a36Sopenharmony_ci goto done; 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci if (changed & WIPHY_PARAM_RETRY_SHORT 165462306a36Sopenharmony_ci && (cfg->conf->retry_short != wiphy->retry_short)) { 165562306a36Sopenharmony_ci cfg->conf->retry_short = wiphy->retry_short; 165662306a36Sopenharmony_ci err = brcmf_set_retry(ndev, cfg->conf->retry_short, false); 165762306a36Sopenharmony_ci if (!err) 165862306a36Sopenharmony_ci goto done; 165962306a36Sopenharmony_ci } 166062306a36Sopenharmony_ci 166162306a36Sopenharmony_cidone: 166262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 166362306a36Sopenharmony_ci return err; 166462306a36Sopenharmony_ci} 166562306a36Sopenharmony_ci 166662306a36Sopenharmony_cistatic void brcmf_init_prof(struct brcmf_cfg80211_profile *prof) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci memset(prof, 0, sizeof(*prof)); 166962306a36Sopenharmony_ci} 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_cistatic u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e) 167262306a36Sopenharmony_ci{ 167362306a36Sopenharmony_ci u16 reason; 167462306a36Sopenharmony_ci 167562306a36Sopenharmony_ci switch (e->event_code) { 167662306a36Sopenharmony_ci case BRCMF_E_DEAUTH: 167762306a36Sopenharmony_ci case BRCMF_E_DEAUTH_IND: 167862306a36Sopenharmony_ci case BRCMF_E_DISASSOC_IND: 167962306a36Sopenharmony_ci reason = e->reason; 168062306a36Sopenharmony_ci break; 168162306a36Sopenharmony_ci case BRCMF_E_LINK: 168262306a36Sopenharmony_ci default: 168362306a36Sopenharmony_ci reason = 0; 168462306a36Sopenharmony_ci break; 168562306a36Sopenharmony_ci } 168662306a36Sopenharmony_ci return reason; 168762306a36Sopenharmony_ci} 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_cistatic int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len) 169062306a36Sopenharmony_ci{ 169162306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 169262306a36Sopenharmony_ci struct brcmf_wsec_pmk_le pmk; 169362306a36Sopenharmony_ci int err; 169462306a36Sopenharmony_ci 169562306a36Sopenharmony_ci memset(&pmk, 0, sizeof(pmk)); 169662306a36Sopenharmony_ci 169762306a36Sopenharmony_ci /* pass pmk directly */ 169862306a36Sopenharmony_ci pmk.key_len = cpu_to_le16(pmk_len); 169962306a36Sopenharmony_ci pmk.flags = cpu_to_le16(0); 170062306a36Sopenharmony_ci memcpy(pmk.key, pmk_data, pmk_len); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci /* store psk in firmware */ 170362306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK, 170462306a36Sopenharmony_ci &pmk, sizeof(pmk)); 170562306a36Sopenharmony_ci if (err < 0) 170662306a36Sopenharmony_ci bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n", 170762306a36Sopenharmony_ci pmk_len); 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci return err; 171062306a36Sopenharmony_ci} 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_cistatic int brcmf_set_sae_password(struct brcmf_if *ifp, const u8 *pwd_data, 171362306a36Sopenharmony_ci u16 pwd_len) 171462306a36Sopenharmony_ci{ 171562306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 171662306a36Sopenharmony_ci struct brcmf_wsec_sae_pwd_le sae_pwd; 171762306a36Sopenharmony_ci int err; 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci if (pwd_len > BRCMF_WSEC_MAX_SAE_PASSWORD_LEN) { 172062306a36Sopenharmony_ci bphy_err(drvr, "sae_password must be less than %d\n", 172162306a36Sopenharmony_ci BRCMF_WSEC_MAX_SAE_PASSWORD_LEN); 172262306a36Sopenharmony_ci return -EINVAL; 172362306a36Sopenharmony_ci } 172462306a36Sopenharmony_ci 172562306a36Sopenharmony_ci sae_pwd.key_len = cpu_to_le16(pwd_len); 172662306a36Sopenharmony_ci memcpy(sae_pwd.key, pwd_data, pwd_len); 172762306a36Sopenharmony_ci 172862306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "sae_password", &sae_pwd, 172962306a36Sopenharmony_ci sizeof(sae_pwd)); 173062306a36Sopenharmony_ci if (err < 0) 173162306a36Sopenharmony_ci bphy_err(drvr, "failed to set SAE password in firmware (len=%u)\n", 173262306a36Sopenharmony_ci pwd_len); 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_ci return err; 173562306a36Sopenharmony_ci} 173662306a36Sopenharmony_ci 173762306a36Sopenharmony_cistatic void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason, 173862306a36Sopenharmony_ci bool locally_generated) 173962306a36Sopenharmony_ci{ 174062306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy); 174162306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 174262306a36Sopenharmony_ci bool bus_up = drvr->bus_if->state == BRCMF_BUS_UP; 174362306a36Sopenharmony_ci s32 err = 0; 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 174662306a36Sopenharmony_ci 174762306a36Sopenharmony_ci if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) { 174862306a36Sopenharmony_ci if (bus_up) { 174962306a36Sopenharmony_ci brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n"); 175062306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(vif->ifp, 175162306a36Sopenharmony_ci BRCMF_C_DISASSOC, NULL, 0); 175262306a36Sopenharmony_ci if (err) 175362306a36Sopenharmony_ci bphy_err(drvr, "WLC_DISASSOC failed (%d)\n", 175462306a36Sopenharmony_ci err); 175562306a36Sopenharmony_ci } 175662306a36Sopenharmony_ci 175762306a36Sopenharmony_ci if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) || 175862306a36Sopenharmony_ci (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT)) 175962306a36Sopenharmony_ci cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0, 176062306a36Sopenharmony_ci locally_generated, GFP_KERNEL); 176162306a36Sopenharmony_ci } 176262306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state); 176362306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); 176462306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); 176562306a36Sopenharmony_ci clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); 176662306a36Sopenharmony_ci brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); 176762306a36Sopenharmony_ci if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { 176862306a36Sopenharmony_ci if (bus_up) 176962306a36Sopenharmony_ci brcmf_set_pmk(vif->ifp, NULL, 0); 177062306a36Sopenharmony_ci vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE; 177162306a36Sopenharmony_ci } 177262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 177362306a36Sopenharmony_ci} 177462306a36Sopenharmony_ci 177562306a36Sopenharmony_cistatic s32 177662306a36Sopenharmony_cibrcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev, 177762306a36Sopenharmony_ci struct cfg80211_ibss_params *params) 177862306a36Sopenharmony_ci{ 177962306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 178062306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 178162306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 178262306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 178362306a36Sopenharmony_ci struct brcmf_join_params join_params; 178462306a36Sopenharmony_ci size_t join_params_size = 0; 178562306a36Sopenharmony_ci s32 err = 0; 178662306a36Sopenharmony_ci s32 wsec = 0; 178762306a36Sopenharmony_ci s32 bcnprd; 178862306a36Sopenharmony_ci u16 chanspec; 178962306a36Sopenharmony_ci u32 ssid_len; 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 179262306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 179362306a36Sopenharmony_ci return -EIO; 179462306a36Sopenharmony_ci 179562306a36Sopenharmony_ci if (params->ssid) 179662306a36Sopenharmony_ci brcmf_dbg(CONN, "SSID: %s\n", params->ssid); 179762306a36Sopenharmony_ci else { 179862306a36Sopenharmony_ci brcmf_dbg(CONN, "SSID: NULL, Not supported\n"); 179962306a36Sopenharmony_ci return -EOPNOTSUPP; 180062306a36Sopenharmony_ci } 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci if (params->bssid) 180562306a36Sopenharmony_ci brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid); 180662306a36Sopenharmony_ci else 180762306a36Sopenharmony_ci brcmf_dbg(CONN, "No BSSID specified\n"); 180862306a36Sopenharmony_ci 180962306a36Sopenharmony_ci if (params->chandef.chan) 181062306a36Sopenharmony_ci brcmf_dbg(CONN, "channel: %d\n", 181162306a36Sopenharmony_ci params->chandef.chan->center_freq); 181262306a36Sopenharmony_ci else 181362306a36Sopenharmony_ci brcmf_dbg(CONN, "no channel specified\n"); 181462306a36Sopenharmony_ci 181562306a36Sopenharmony_ci if (params->channel_fixed) 181662306a36Sopenharmony_ci brcmf_dbg(CONN, "fixed channel required\n"); 181762306a36Sopenharmony_ci else 181862306a36Sopenharmony_ci brcmf_dbg(CONN, "no fixed channel required\n"); 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci if (params->ie && params->ie_len) 182162306a36Sopenharmony_ci brcmf_dbg(CONN, "ie len: %d\n", params->ie_len); 182262306a36Sopenharmony_ci else 182362306a36Sopenharmony_ci brcmf_dbg(CONN, "no ie specified\n"); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci if (params->beacon_interval) 182662306a36Sopenharmony_ci brcmf_dbg(CONN, "beacon interval: %d\n", 182762306a36Sopenharmony_ci params->beacon_interval); 182862306a36Sopenharmony_ci else 182962306a36Sopenharmony_ci brcmf_dbg(CONN, "no beacon interval specified\n"); 183062306a36Sopenharmony_ci 183162306a36Sopenharmony_ci if (params->basic_rates) 183262306a36Sopenharmony_ci brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates); 183362306a36Sopenharmony_ci else 183462306a36Sopenharmony_ci brcmf_dbg(CONN, "no basic rates specified\n"); 183562306a36Sopenharmony_ci 183662306a36Sopenharmony_ci if (params->privacy) 183762306a36Sopenharmony_ci brcmf_dbg(CONN, "privacy required\n"); 183862306a36Sopenharmony_ci else 183962306a36Sopenharmony_ci brcmf_dbg(CONN, "no privacy required\n"); 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* Configure Privacy for starter */ 184262306a36Sopenharmony_ci if (params->privacy) 184362306a36Sopenharmony_ci wsec |= WEP_ENABLED; 184462306a36Sopenharmony_ci 184562306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec); 184662306a36Sopenharmony_ci if (err) { 184762306a36Sopenharmony_ci bphy_err(drvr, "wsec failed (%d)\n", err); 184862306a36Sopenharmony_ci goto done; 184962306a36Sopenharmony_ci } 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci /* Configure Beacon Interval for starter */ 185262306a36Sopenharmony_ci if (params->beacon_interval) 185362306a36Sopenharmony_ci bcnprd = params->beacon_interval; 185462306a36Sopenharmony_ci else 185562306a36Sopenharmony_ci bcnprd = 100; 185662306a36Sopenharmony_ci 185762306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd); 185862306a36Sopenharmony_ci if (err) { 185962306a36Sopenharmony_ci bphy_err(drvr, "WLC_SET_BCNPRD failed (%d)\n", err); 186062306a36Sopenharmony_ci goto done; 186162306a36Sopenharmony_ci } 186262306a36Sopenharmony_ci 186362306a36Sopenharmony_ci /* Configure required join parameter */ 186462306a36Sopenharmony_ci memset(&join_params, 0, sizeof(struct brcmf_join_params)); 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_ci /* SSID */ 186762306a36Sopenharmony_ci ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN); 186862306a36Sopenharmony_ci memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len); 186962306a36Sopenharmony_ci join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len); 187062306a36Sopenharmony_ci join_params_size = sizeof(join_params.ssid_le); 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci /* BSSID */ 187362306a36Sopenharmony_ci if (params->bssid) { 187462306a36Sopenharmony_ci memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN); 187562306a36Sopenharmony_ci join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE; 187662306a36Sopenharmony_ci memcpy(profile->bssid, params->bssid, ETH_ALEN); 187762306a36Sopenharmony_ci } else { 187862306a36Sopenharmony_ci eth_broadcast_addr(join_params.params_le.bssid); 187962306a36Sopenharmony_ci eth_zero_addr(profile->bssid); 188062306a36Sopenharmony_ci } 188162306a36Sopenharmony_ci 188262306a36Sopenharmony_ci /* Channel */ 188362306a36Sopenharmony_ci if (params->chandef.chan) { 188462306a36Sopenharmony_ci u32 target_channel; 188562306a36Sopenharmony_ci 188662306a36Sopenharmony_ci cfg->channel = 188762306a36Sopenharmony_ci ieee80211_frequency_to_channel( 188862306a36Sopenharmony_ci params->chandef.chan->center_freq); 188962306a36Sopenharmony_ci if (params->channel_fixed) { 189062306a36Sopenharmony_ci /* adding chanspec */ 189162306a36Sopenharmony_ci chanspec = chandef_to_chanspec(&cfg->d11inf, 189262306a36Sopenharmony_ci ¶ms->chandef); 189362306a36Sopenharmony_ci join_params.params_le.chanspec_list[0] = 189462306a36Sopenharmony_ci cpu_to_le16(chanspec); 189562306a36Sopenharmony_ci join_params.params_le.chanspec_num = cpu_to_le32(1); 189662306a36Sopenharmony_ci join_params_size += sizeof(join_params.params_le); 189762306a36Sopenharmony_ci } 189862306a36Sopenharmony_ci 189962306a36Sopenharmony_ci /* set channel for starter */ 190062306a36Sopenharmony_ci target_channel = cfg->channel; 190162306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL, 190262306a36Sopenharmony_ci target_channel); 190362306a36Sopenharmony_ci if (err) { 190462306a36Sopenharmony_ci bphy_err(drvr, "WLC_SET_CHANNEL failed (%d)\n", err); 190562306a36Sopenharmony_ci goto done; 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci } else 190862306a36Sopenharmony_ci cfg->channel = 0; 190962306a36Sopenharmony_ci 191062306a36Sopenharmony_ci cfg->ibss_starter = false; 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, 191462306a36Sopenharmony_ci &join_params, join_params_size); 191562306a36Sopenharmony_ci if (err) { 191662306a36Sopenharmony_ci bphy_err(drvr, "WLC_SET_SSID failed (%d)\n", err); 191762306a36Sopenharmony_ci goto done; 191862306a36Sopenharmony_ci } 191962306a36Sopenharmony_ci 192062306a36Sopenharmony_cidone: 192162306a36Sopenharmony_ci if (err) 192262306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); 192362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 192462306a36Sopenharmony_ci return err; 192562306a36Sopenharmony_ci} 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_cistatic s32 192862306a36Sopenharmony_cibrcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) 192962306a36Sopenharmony_ci{ 193062306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 193162306a36Sopenharmony_ci 193262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 193362306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) { 193462306a36Sopenharmony_ci /* When driver is being unloaded, it can end up here. If an 193562306a36Sopenharmony_ci * error is returned then later on a debug trace in the wireless 193662306a36Sopenharmony_ci * core module will be printed. To avoid this 0 is returned. 193762306a36Sopenharmony_ci */ 193862306a36Sopenharmony_ci return 0; 193962306a36Sopenharmony_ci } 194062306a36Sopenharmony_ci 194162306a36Sopenharmony_ci brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING, true); 194262306a36Sopenharmony_ci brcmf_net_setcarrier(ifp, false); 194362306a36Sopenharmony_ci 194462306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 194562306a36Sopenharmony_ci 194662306a36Sopenharmony_ci return 0; 194762306a36Sopenharmony_ci} 194862306a36Sopenharmony_ci 194962306a36Sopenharmony_cistatic s32 brcmf_set_wpa_version(struct net_device *ndev, 195062306a36Sopenharmony_ci struct cfg80211_connect_params *sme) 195162306a36Sopenharmony_ci{ 195262306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 195362306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); 195462306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 195562306a36Sopenharmony_ci struct brcmf_cfg80211_security *sec; 195662306a36Sopenharmony_ci s32 val = 0; 195762306a36Sopenharmony_ci s32 err = 0; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1) 196062306a36Sopenharmony_ci val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED; 196162306a36Sopenharmony_ci else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2) 196262306a36Sopenharmony_ci val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED; 196362306a36Sopenharmony_ci else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3) 196462306a36Sopenharmony_ci val = WPA3_AUTH_SAE_PSK; 196562306a36Sopenharmony_ci else 196662306a36Sopenharmony_ci val = WPA_AUTH_DISABLED; 196762306a36Sopenharmony_ci brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val); 196862306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val); 196962306a36Sopenharmony_ci if (err) { 197062306a36Sopenharmony_ci bphy_err(drvr, "set wpa_auth failed (%d)\n", err); 197162306a36Sopenharmony_ci return err; 197262306a36Sopenharmony_ci } 197362306a36Sopenharmony_ci sec = &profile->sec; 197462306a36Sopenharmony_ci sec->wpa_versions = sme->crypto.wpa_versions; 197562306a36Sopenharmony_ci return err; 197662306a36Sopenharmony_ci} 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_cistatic s32 brcmf_set_auth_type(struct net_device *ndev, 197962306a36Sopenharmony_ci struct cfg80211_connect_params *sme) 198062306a36Sopenharmony_ci{ 198162306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 198262306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); 198362306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 198462306a36Sopenharmony_ci struct brcmf_cfg80211_security *sec; 198562306a36Sopenharmony_ci s32 val = 0; 198662306a36Sopenharmony_ci s32 err = 0; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci switch (sme->auth_type) { 198962306a36Sopenharmony_ci case NL80211_AUTHTYPE_OPEN_SYSTEM: 199062306a36Sopenharmony_ci val = 0; 199162306a36Sopenharmony_ci brcmf_dbg(CONN, "open system\n"); 199262306a36Sopenharmony_ci break; 199362306a36Sopenharmony_ci case NL80211_AUTHTYPE_SHARED_KEY: 199462306a36Sopenharmony_ci val = 1; 199562306a36Sopenharmony_ci brcmf_dbg(CONN, "shared key\n"); 199662306a36Sopenharmony_ci break; 199762306a36Sopenharmony_ci case NL80211_AUTHTYPE_SAE: 199862306a36Sopenharmony_ci val = 3; 199962306a36Sopenharmony_ci brcmf_dbg(CONN, "SAE authentication\n"); 200062306a36Sopenharmony_ci break; 200162306a36Sopenharmony_ci default: 200262306a36Sopenharmony_ci val = 2; 200362306a36Sopenharmony_ci brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type); 200462306a36Sopenharmony_ci break; 200562306a36Sopenharmony_ci } 200662306a36Sopenharmony_ci 200762306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "auth", val); 200862306a36Sopenharmony_ci if (err) { 200962306a36Sopenharmony_ci bphy_err(drvr, "set auth failed (%d)\n", err); 201062306a36Sopenharmony_ci return err; 201162306a36Sopenharmony_ci } 201262306a36Sopenharmony_ci sec = &profile->sec; 201362306a36Sopenharmony_ci sec->auth_type = sme->auth_type; 201462306a36Sopenharmony_ci return err; 201562306a36Sopenharmony_ci} 201662306a36Sopenharmony_ci 201762306a36Sopenharmony_cistatic s32 201862306a36Sopenharmony_cibrcmf_set_wsec_mode(struct net_device *ndev, 201962306a36Sopenharmony_ci struct cfg80211_connect_params *sme) 202062306a36Sopenharmony_ci{ 202162306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 202262306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); 202362306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 202462306a36Sopenharmony_ci struct brcmf_cfg80211_security *sec; 202562306a36Sopenharmony_ci s32 pval = 0; 202662306a36Sopenharmony_ci s32 gval = 0; 202762306a36Sopenharmony_ci s32 wsec; 202862306a36Sopenharmony_ci s32 err = 0; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci if (sme->crypto.n_ciphers_pairwise) { 203162306a36Sopenharmony_ci switch (sme->crypto.ciphers_pairwise[0]) { 203262306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 203362306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 203462306a36Sopenharmony_ci pval = WEP_ENABLED; 203562306a36Sopenharmony_ci break; 203662306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 203762306a36Sopenharmony_ci pval = TKIP_ENABLED; 203862306a36Sopenharmony_ci break; 203962306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 204062306a36Sopenharmony_ci pval = AES_ENABLED; 204162306a36Sopenharmony_ci break; 204262306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_AES_CMAC: 204362306a36Sopenharmony_ci pval = AES_ENABLED; 204462306a36Sopenharmony_ci break; 204562306a36Sopenharmony_ci default: 204662306a36Sopenharmony_ci bphy_err(drvr, "invalid cipher pairwise (%d)\n", 204762306a36Sopenharmony_ci sme->crypto.ciphers_pairwise[0]); 204862306a36Sopenharmony_ci return -EINVAL; 204962306a36Sopenharmony_ci } 205062306a36Sopenharmony_ci } 205162306a36Sopenharmony_ci if (sme->crypto.cipher_group) { 205262306a36Sopenharmony_ci switch (sme->crypto.cipher_group) { 205362306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 205462306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 205562306a36Sopenharmony_ci gval = WEP_ENABLED; 205662306a36Sopenharmony_ci break; 205762306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 205862306a36Sopenharmony_ci gval = TKIP_ENABLED; 205962306a36Sopenharmony_ci break; 206062306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 206162306a36Sopenharmony_ci gval = AES_ENABLED; 206262306a36Sopenharmony_ci break; 206362306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_AES_CMAC: 206462306a36Sopenharmony_ci gval = AES_ENABLED; 206562306a36Sopenharmony_ci break; 206662306a36Sopenharmony_ci default: 206762306a36Sopenharmony_ci bphy_err(drvr, "invalid cipher group (%d)\n", 206862306a36Sopenharmony_ci sme->crypto.cipher_group); 206962306a36Sopenharmony_ci return -EINVAL; 207062306a36Sopenharmony_ci } 207162306a36Sopenharmony_ci } 207262306a36Sopenharmony_ci 207362306a36Sopenharmony_ci brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval); 207462306a36Sopenharmony_ci /* In case of privacy, but no security and WPS then simulate */ 207562306a36Sopenharmony_ci /* setting AES. WPS-2.0 allows no security */ 207662306a36Sopenharmony_ci if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval && 207762306a36Sopenharmony_ci sme->privacy) 207862306a36Sopenharmony_ci pval = AES_ENABLED; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci wsec = pval | gval; 208162306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); 208262306a36Sopenharmony_ci if (err) { 208362306a36Sopenharmony_ci bphy_err(drvr, "error (%d)\n", err); 208462306a36Sopenharmony_ci return err; 208562306a36Sopenharmony_ci } 208662306a36Sopenharmony_ci 208762306a36Sopenharmony_ci sec = &profile->sec; 208862306a36Sopenharmony_ci sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0]; 208962306a36Sopenharmony_ci sec->cipher_group = sme->crypto.cipher_group; 209062306a36Sopenharmony_ci 209162306a36Sopenharmony_ci return err; 209262306a36Sopenharmony_ci} 209362306a36Sopenharmony_ci 209462306a36Sopenharmony_cistatic s32 209562306a36Sopenharmony_cibrcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme) 209662306a36Sopenharmony_ci{ 209762306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 209862306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 209962306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 210062306a36Sopenharmony_ci s32 val; 210162306a36Sopenharmony_ci s32 err; 210262306a36Sopenharmony_ci const struct brcmf_tlv *rsn_ie; 210362306a36Sopenharmony_ci const u8 *ie; 210462306a36Sopenharmony_ci u32 ie_len; 210562306a36Sopenharmony_ci u32 offset; 210662306a36Sopenharmony_ci u16 rsn_cap; 210762306a36Sopenharmony_ci u32 mfp; 210862306a36Sopenharmony_ci u16 count; 210962306a36Sopenharmony_ci 211062306a36Sopenharmony_ci profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE; 211162306a36Sopenharmony_ci profile->is_ft = false; 211262306a36Sopenharmony_ci 211362306a36Sopenharmony_ci if (!sme->crypto.n_akm_suites) 211462306a36Sopenharmony_ci return 0; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev), "wpa_auth", &val); 211762306a36Sopenharmony_ci if (err) { 211862306a36Sopenharmony_ci bphy_err(drvr, "could not get wpa_auth (%d)\n", err); 211962306a36Sopenharmony_ci return err; 212062306a36Sopenharmony_ci } 212162306a36Sopenharmony_ci if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) { 212262306a36Sopenharmony_ci switch (sme->crypto.akm_suites[0]) { 212362306a36Sopenharmony_ci case WLAN_AKM_SUITE_8021X: 212462306a36Sopenharmony_ci val = WPA_AUTH_UNSPECIFIED; 212562306a36Sopenharmony_ci if (sme->want_1x) 212662306a36Sopenharmony_ci profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; 212762306a36Sopenharmony_ci break; 212862306a36Sopenharmony_ci case WLAN_AKM_SUITE_PSK: 212962306a36Sopenharmony_ci val = WPA_AUTH_PSK; 213062306a36Sopenharmony_ci break; 213162306a36Sopenharmony_ci default: 213262306a36Sopenharmony_ci bphy_err(drvr, "invalid akm suite (%d)\n", 213362306a36Sopenharmony_ci sme->crypto.akm_suites[0]); 213462306a36Sopenharmony_ci return -EINVAL; 213562306a36Sopenharmony_ci } 213662306a36Sopenharmony_ci } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) { 213762306a36Sopenharmony_ci switch (sme->crypto.akm_suites[0]) { 213862306a36Sopenharmony_ci case WLAN_AKM_SUITE_8021X: 213962306a36Sopenharmony_ci val = WPA2_AUTH_UNSPECIFIED; 214062306a36Sopenharmony_ci if (sme->want_1x) 214162306a36Sopenharmony_ci profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; 214262306a36Sopenharmony_ci break; 214362306a36Sopenharmony_ci case WLAN_AKM_SUITE_8021X_SHA256: 214462306a36Sopenharmony_ci val = WPA2_AUTH_1X_SHA256; 214562306a36Sopenharmony_ci if (sme->want_1x) 214662306a36Sopenharmony_ci profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; 214762306a36Sopenharmony_ci break; 214862306a36Sopenharmony_ci case WLAN_AKM_SUITE_PSK_SHA256: 214962306a36Sopenharmony_ci val = WPA2_AUTH_PSK_SHA256; 215062306a36Sopenharmony_ci break; 215162306a36Sopenharmony_ci case WLAN_AKM_SUITE_PSK: 215262306a36Sopenharmony_ci val = WPA2_AUTH_PSK; 215362306a36Sopenharmony_ci break; 215462306a36Sopenharmony_ci case WLAN_AKM_SUITE_FT_8021X: 215562306a36Sopenharmony_ci val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT; 215662306a36Sopenharmony_ci profile->is_ft = true; 215762306a36Sopenharmony_ci if (sme->want_1x) 215862306a36Sopenharmony_ci profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X; 215962306a36Sopenharmony_ci break; 216062306a36Sopenharmony_ci case WLAN_AKM_SUITE_FT_PSK: 216162306a36Sopenharmony_ci val = WPA2_AUTH_PSK | WPA2_AUTH_FT; 216262306a36Sopenharmony_ci profile->is_ft = true; 216362306a36Sopenharmony_ci break; 216462306a36Sopenharmony_ci default: 216562306a36Sopenharmony_ci bphy_err(drvr, "invalid akm suite (%d)\n", 216662306a36Sopenharmony_ci sme->crypto.akm_suites[0]); 216762306a36Sopenharmony_ci return -EINVAL; 216862306a36Sopenharmony_ci } 216962306a36Sopenharmony_ci } else if (val & WPA3_AUTH_SAE_PSK) { 217062306a36Sopenharmony_ci switch (sme->crypto.akm_suites[0]) { 217162306a36Sopenharmony_ci case WLAN_AKM_SUITE_SAE: 217262306a36Sopenharmony_ci val = WPA3_AUTH_SAE_PSK; 217362306a36Sopenharmony_ci if (sme->crypto.sae_pwd) { 217462306a36Sopenharmony_ci brcmf_dbg(INFO, "using SAE offload\n"); 217562306a36Sopenharmony_ci profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; 217662306a36Sopenharmony_ci } 217762306a36Sopenharmony_ci break; 217862306a36Sopenharmony_ci case WLAN_AKM_SUITE_FT_OVER_SAE: 217962306a36Sopenharmony_ci val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT; 218062306a36Sopenharmony_ci profile->is_ft = true; 218162306a36Sopenharmony_ci if (sme->crypto.sae_pwd) { 218262306a36Sopenharmony_ci brcmf_dbg(INFO, "using SAE offload\n"); 218362306a36Sopenharmony_ci profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE; 218462306a36Sopenharmony_ci } 218562306a36Sopenharmony_ci break; 218662306a36Sopenharmony_ci default: 218762306a36Sopenharmony_ci bphy_err(drvr, "invalid akm suite (%d)\n", 218862306a36Sopenharmony_ci sme->crypto.akm_suites[0]); 218962306a36Sopenharmony_ci return -EINVAL; 219062306a36Sopenharmony_ci } 219162306a36Sopenharmony_ci } 219262306a36Sopenharmony_ci 219362306a36Sopenharmony_ci if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X) 219462306a36Sopenharmony_ci brcmf_dbg(INFO, "using 1X offload\n"); 219562306a36Sopenharmony_ci 219662306a36Sopenharmony_ci if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) 219762306a36Sopenharmony_ci goto skip_mfp_config; 219862306a36Sopenharmony_ci /* The MFP mode (1 or 2) needs to be determined, parse IEs. The 219962306a36Sopenharmony_ci * IE will not be verified, just a quick search for MFP config 220062306a36Sopenharmony_ci */ 220162306a36Sopenharmony_ci rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len, 220262306a36Sopenharmony_ci WLAN_EID_RSN); 220362306a36Sopenharmony_ci if (!rsn_ie) 220462306a36Sopenharmony_ci goto skip_mfp_config; 220562306a36Sopenharmony_ci ie = (const u8 *)rsn_ie; 220662306a36Sopenharmony_ci ie_len = rsn_ie->len + TLV_HDR_LEN; 220762306a36Sopenharmony_ci /* Skip unicast suite */ 220862306a36Sopenharmony_ci offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN; 220962306a36Sopenharmony_ci if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) 221062306a36Sopenharmony_ci goto skip_mfp_config; 221162306a36Sopenharmony_ci /* Skip multicast suite */ 221262306a36Sopenharmony_ci count = ie[offset] + (ie[offset + 1] << 8); 221362306a36Sopenharmony_ci offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); 221462306a36Sopenharmony_ci if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len) 221562306a36Sopenharmony_ci goto skip_mfp_config; 221662306a36Sopenharmony_ci /* Skip auth key management suite(s) */ 221762306a36Sopenharmony_ci count = ie[offset] + (ie[offset + 1] << 8); 221862306a36Sopenharmony_ci offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN); 221962306a36Sopenharmony_ci if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len) 222062306a36Sopenharmony_ci goto skip_mfp_config; 222162306a36Sopenharmony_ci /* Ready to read capabilities */ 222262306a36Sopenharmony_ci mfp = BRCMF_MFP_NONE; 222362306a36Sopenharmony_ci rsn_cap = ie[offset] + (ie[offset + 1] << 8); 222462306a36Sopenharmony_ci if (rsn_cap & RSN_CAP_MFPR_MASK) 222562306a36Sopenharmony_ci mfp = BRCMF_MFP_REQUIRED; 222662306a36Sopenharmony_ci else if (rsn_cap & RSN_CAP_MFPC_MASK) 222762306a36Sopenharmony_ci mfp = BRCMF_MFP_CAPABLE; 222862306a36Sopenharmony_ci brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp); 222962306a36Sopenharmony_ci 223062306a36Sopenharmony_ciskip_mfp_config: 223162306a36Sopenharmony_ci brcmf_dbg(CONN, "setting wpa_auth to %d\n", val); 223262306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val); 223362306a36Sopenharmony_ci if (err) { 223462306a36Sopenharmony_ci bphy_err(drvr, "could not set wpa_auth (%d)\n", err); 223562306a36Sopenharmony_ci return err; 223662306a36Sopenharmony_ci } 223762306a36Sopenharmony_ci 223862306a36Sopenharmony_ci return err; 223962306a36Sopenharmony_ci} 224062306a36Sopenharmony_ci 224162306a36Sopenharmony_cistatic s32 224262306a36Sopenharmony_cibrcmf_set_sharedkey(struct net_device *ndev, 224362306a36Sopenharmony_ci struct cfg80211_connect_params *sme) 224462306a36Sopenharmony_ci{ 224562306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 224662306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 224762306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev); 224862306a36Sopenharmony_ci struct brcmf_cfg80211_security *sec; 224962306a36Sopenharmony_ci struct brcmf_wsec_key key; 225062306a36Sopenharmony_ci s32 val; 225162306a36Sopenharmony_ci s32 err = 0; 225262306a36Sopenharmony_ci 225362306a36Sopenharmony_ci brcmf_dbg(CONN, "key len (%d)\n", sme->key_len); 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci if (sme->key_len == 0) 225662306a36Sopenharmony_ci return 0; 225762306a36Sopenharmony_ci 225862306a36Sopenharmony_ci sec = &profile->sec; 225962306a36Sopenharmony_ci brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n", 226062306a36Sopenharmony_ci sec->wpa_versions, sec->cipher_pairwise); 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 | 226362306a36Sopenharmony_ci NL80211_WPA_VERSION_3)) 226462306a36Sopenharmony_ci return 0; 226562306a36Sopenharmony_ci 226662306a36Sopenharmony_ci if (!(sec->cipher_pairwise & 226762306a36Sopenharmony_ci (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104))) 226862306a36Sopenharmony_ci return 0; 226962306a36Sopenharmony_ci 227062306a36Sopenharmony_ci memset(&key, 0, sizeof(key)); 227162306a36Sopenharmony_ci key.len = (u32) sme->key_len; 227262306a36Sopenharmony_ci key.index = (u32) sme->key_idx; 227362306a36Sopenharmony_ci if (key.len > sizeof(key.data)) { 227462306a36Sopenharmony_ci bphy_err(drvr, "Too long key length (%u)\n", key.len); 227562306a36Sopenharmony_ci return -EINVAL; 227662306a36Sopenharmony_ci } 227762306a36Sopenharmony_ci memcpy(key.data, sme->key, key.len); 227862306a36Sopenharmony_ci key.flags = BRCMF_PRIMARY_KEY; 227962306a36Sopenharmony_ci switch (sec->cipher_pairwise) { 228062306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 228162306a36Sopenharmony_ci key.algo = CRYPTO_ALGO_WEP1; 228262306a36Sopenharmony_ci break; 228362306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 228462306a36Sopenharmony_ci key.algo = CRYPTO_ALGO_WEP128; 228562306a36Sopenharmony_ci break; 228662306a36Sopenharmony_ci default: 228762306a36Sopenharmony_ci bphy_err(drvr, "Invalid algorithm (%d)\n", 228862306a36Sopenharmony_ci sme->crypto.ciphers_pairwise[0]); 228962306a36Sopenharmony_ci return -EINVAL; 229062306a36Sopenharmony_ci } 229162306a36Sopenharmony_ci /* Set the new key/index */ 229262306a36Sopenharmony_ci brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n", 229362306a36Sopenharmony_ci key.len, key.index, key.algo); 229462306a36Sopenharmony_ci brcmf_dbg(CONN, "key \"%s\"\n", key.data); 229562306a36Sopenharmony_ci err = send_key_to_dongle(ifp, &key); 229662306a36Sopenharmony_ci if (err) 229762306a36Sopenharmony_ci return err; 229862306a36Sopenharmony_ci 229962306a36Sopenharmony_ci if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) { 230062306a36Sopenharmony_ci brcmf_dbg(CONN, "set auth_type to shared key\n"); 230162306a36Sopenharmony_ci val = WL_AUTH_SHARED_KEY; /* shared key */ 230262306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "auth", val); 230362306a36Sopenharmony_ci if (err) 230462306a36Sopenharmony_ci bphy_err(drvr, "set auth failed (%d)\n", err); 230562306a36Sopenharmony_ci } 230662306a36Sopenharmony_ci return err; 230762306a36Sopenharmony_ci} 230862306a36Sopenharmony_ci 230962306a36Sopenharmony_cistatic 231062306a36Sopenharmony_cienum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp, 231162306a36Sopenharmony_ci enum nl80211_auth_type type) 231262306a36Sopenharmony_ci{ 231362306a36Sopenharmony_ci if (type == NL80211_AUTHTYPE_AUTOMATIC && 231462306a36Sopenharmony_ci brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) { 231562306a36Sopenharmony_ci brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n"); 231662306a36Sopenharmony_ci type = NL80211_AUTHTYPE_OPEN_SYSTEM; 231762306a36Sopenharmony_ci } 231862306a36Sopenharmony_ci return type; 231962306a36Sopenharmony_ci} 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_cistatic void brcmf_set_join_pref(struct brcmf_if *ifp, 232262306a36Sopenharmony_ci struct cfg80211_bss_selection *bss_select) 232362306a36Sopenharmony_ci{ 232462306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 232562306a36Sopenharmony_ci struct brcmf_join_pref_params join_pref_params[2]; 232662306a36Sopenharmony_ci enum nl80211_band band; 232762306a36Sopenharmony_ci int err, i = 0; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci join_pref_params[i].len = 2; 233062306a36Sopenharmony_ci join_pref_params[i].rssi_gain = 0; 233162306a36Sopenharmony_ci 233262306a36Sopenharmony_ci if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF) 233362306a36Sopenharmony_ci brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO); 233462306a36Sopenharmony_ci 233562306a36Sopenharmony_ci switch (bss_select->behaviour) { 233662306a36Sopenharmony_ci case __NL80211_BSS_SELECT_ATTR_INVALID: 233762306a36Sopenharmony_ci brcmf_c_set_joinpref_default(ifp); 233862306a36Sopenharmony_ci return; 233962306a36Sopenharmony_ci case NL80211_BSS_SELECT_ATTR_BAND_PREF: 234062306a36Sopenharmony_ci join_pref_params[i].type = BRCMF_JOIN_PREF_BAND; 234162306a36Sopenharmony_ci band = bss_select->param.band_pref; 234262306a36Sopenharmony_ci join_pref_params[i].band = nl80211_band_to_fwil(band); 234362306a36Sopenharmony_ci i++; 234462306a36Sopenharmony_ci break; 234562306a36Sopenharmony_ci case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST: 234662306a36Sopenharmony_ci join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA; 234762306a36Sopenharmony_ci band = bss_select->param.adjust.band; 234862306a36Sopenharmony_ci join_pref_params[i].band = nl80211_band_to_fwil(band); 234962306a36Sopenharmony_ci join_pref_params[i].rssi_gain = bss_select->param.adjust.delta; 235062306a36Sopenharmony_ci i++; 235162306a36Sopenharmony_ci break; 235262306a36Sopenharmony_ci case NL80211_BSS_SELECT_ATTR_RSSI: 235362306a36Sopenharmony_ci default: 235462306a36Sopenharmony_ci break; 235562306a36Sopenharmony_ci } 235662306a36Sopenharmony_ci join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI; 235762306a36Sopenharmony_ci join_pref_params[i].len = 2; 235862306a36Sopenharmony_ci join_pref_params[i].rssi_gain = 0; 235962306a36Sopenharmony_ci join_pref_params[i].band = 0; 236062306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params, 236162306a36Sopenharmony_ci sizeof(join_pref_params)); 236262306a36Sopenharmony_ci if (err) 236362306a36Sopenharmony_ci bphy_err(drvr, "Set join_pref error (%d)\n", err); 236462306a36Sopenharmony_ci} 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_cistatic s32 236762306a36Sopenharmony_cibrcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev, 236862306a36Sopenharmony_ci struct cfg80211_connect_params *sme) 236962306a36Sopenharmony_ci{ 237062306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 237162306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 237262306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 237362306a36Sopenharmony_ci struct ieee80211_channel *chan = sme->channel; 237462306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 237562306a36Sopenharmony_ci struct brcmf_join_params join_params; 237662306a36Sopenharmony_ci size_t join_params_size; 237762306a36Sopenharmony_ci const struct brcmf_tlv *rsn_ie; 237862306a36Sopenharmony_ci const struct brcmf_vs_tlv *wpa_ie; 237962306a36Sopenharmony_ci const void *ie; 238062306a36Sopenharmony_ci u32 ie_len; 238162306a36Sopenharmony_ci struct brcmf_ext_join_params_le *ext_join_params; 238262306a36Sopenharmony_ci u16 chanspec; 238362306a36Sopenharmony_ci s32 err = 0; 238462306a36Sopenharmony_ci u32 ssid_len; 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 238762306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 238862306a36Sopenharmony_ci return -EIO; 238962306a36Sopenharmony_ci 239062306a36Sopenharmony_ci if (!sme->ssid) { 239162306a36Sopenharmony_ci bphy_err(drvr, "Invalid ssid\n"); 239262306a36Sopenharmony_ci return -EOPNOTSUPP; 239362306a36Sopenharmony_ci } 239462306a36Sopenharmony_ci 239562306a36Sopenharmony_ci if (sme->channel_hint) 239662306a36Sopenharmony_ci chan = sme->channel_hint; 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci if (sme->bssid_hint) 239962306a36Sopenharmony_ci sme->bssid = sme->bssid_hint; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) { 240262306a36Sopenharmony_ci /* A normal (non P2P) connection request setup. */ 240362306a36Sopenharmony_ci ie = NULL; 240462306a36Sopenharmony_ci ie_len = 0; 240562306a36Sopenharmony_ci /* find the WPA_IE */ 240662306a36Sopenharmony_ci wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len); 240762306a36Sopenharmony_ci if (wpa_ie) { 240862306a36Sopenharmony_ci ie = wpa_ie; 240962306a36Sopenharmony_ci ie_len = wpa_ie->len + TLV_HDR_LEN; 241062306a36Sopenharmony_ci } else { 241162306a36Sopenharmony_ci /* find the RSN_IE */ 241262306a36Sopenharmony_ci rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, 241362306a36Sopenharmony_ci sme->ie_len, 241462306a36Sopenharmony_ci WLAN_EID_RSN); 241562306a36Sopenharmony_ci if (rsn_ie) { 241662306a36Sopenharmony_ci ie = rsn_ie; 241762306a36Sopenharmony_ci ie_len = rsn_ie->len + TLV_HDR_LEN; 241862306a36Sopenharmony_ci } 241962306a36Sopenharmony_ci } 242062306a36Sopenharmony_ci brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len); 242162306a36Sopenharmony_ci } 242262306a36Sopenharmony_ci 242362306a36Sopenharmony_ci err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG, 242462306a36Sopenharmony_ci sme->ie, sme->ie_len); 242562306a36Sopenharmony_ci if (err) 242662306a36Sopenharmony_ci bphy_err(drvr, "Set Assoc REQ IE Failed\n"); 242762306a36Sopenharmony_ci else 242862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n"); 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); 243162306a36Sopenharmony_ci 243262306a36Sopenharmony_ci if (chan) { 243362306a36Sopenharmony_ci cfg->channel = 243462306a36Sopenharmony_ci ieee80211_frequency_to_channel(chan->center_freq); 243562306a36Sopenharmony_ci chanspec = channel_to_chanspec(&cfg->d11inf, chan); 243662306a36Sopenharmony_ci brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n", 243762306a36Sopenharmony_ci cfg->channel, chan->center_freq, chanspec); 243862306a36Sopenharmony_ci } else { 243962306a36Sopenharmony_ci cfg->channel = 0; 244062306a36Sopenharmony_ci chanspec = 0; 244162306a36Sopenharmony_ci } 244262306a36Sopenharmony_ci 244362306a36Sopenharmony_ci brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len); 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci err = brcmf_set_wpa_version(ndev, sme); 244662306a36Sopenharmony_ci if (err) { 244762306a36Sopenharmony_ci bphy_err(drvr, "wl_set_wpa_version failed (%d)\n", err); 244862306a36Sopenharmony_ci goto done; 244962306a36Sopenharmony_ci } 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type); 245262306a36Sopenharmony_ci err = brcmf_set_auth_type(ndev, sme); 245362306a36Sopenharmony_ci if (err) { 245462306a36Sopenharmony_ci bphy_err(drvr, "wl_set_auth_type failed (%d)\n", err); 245562306a36Sopenharmony_ci goto done; 245662306a36Sopenharmony_ci } 245762306a36Sopenharmony_ci 245862306a36Sopenharmony_ci err = brcmf_set_wsec_mode(ndev, sme); 245962306a36Sopenharmony_ci if (err) { 246062306a36Sopenharmony_ci bphy_err(drvr, "wl_set_set_cipher failed (%d)\n", err); 246162306a36Sopenharmony_ci goto done; 246262306a36Sopenharmony_ci } 246362306a36Sopenharmony_ci 246462306a36Sopenharmony_ci err = brcmf_set_key_mgmt(ndev, sme); 246562306a36Sopenharmony_ci if (err) { 246662306a36Sopenharmony_ci bphy_err(drvr, "wl_set_key_mgmt failed (%d)\n", err); 246762306a36Sopenharmony_ci goto done; 246862306a36Sopenharmony_ci } 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci err = brcmf_set_sharedkey(ndev, sme); 247162306a36Sopenharmony_ci if (err) { 247262306a36Sopenharmony_ci bphy_err(drvr, "brcmf_set_sharedkey failed (%d)\n", err); 247362306a36Sopenharmony_ci goto done; 247462306a36Sopenharmony_ci } 247562306a36Sopenharmony_ci 247662306a36Sopenharmony_ci if (sme->crypto.psk && 247762306a36Sopenharmony_ci profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) { 247862306a36Sopenharmony_ci if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) { 247962306a36Sopenharmony_ci err = -EINVAL; 248062306a36Sopenharmony_ci goto done; 248162306a36Sopenharmony_ci } 248262306a36Sopenharmony_ci brcmf_dbg(INFO, "using PSK offload\n"); 248362306a36Sopenharmony_ci profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK; 248462306a36Sopenharmony_ci } 248562306a36Sopenharmony_ci 248662306a36Sopenharmony_ci if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) { 248762306a36Sopenharmony_ci /* enable firmware supplicant for this interface */ 248862306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1); 248962306a36Sopenharmony_ci if (err < 0) { 249062306a36Sopenharmony_ci bphy_err(drvr, "failed to enable fw supplicant\n"); 249162306a36Sopenharmony_ci goto done; 249262306a36Sopenharmony_ci } 249362306a36Sopenharmony_ci } 249462306a36Sopenharmony_ci 249562306a36Sopenharmony_ci if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK) 249662306a36Sopenharmony_ci err = brcmf_set_pmk(ifp, sme->crypto.psk, 249762306a36Sopenharmony_ci BRCMF_WSEC_MAX_PSK_LEN); 249862306a36Sopenharmony_ci else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) { 249962306a36Sopenharmony_ci /* clean up user-space RSNE */ 250062306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0); 250162306a36Sopenharmony_ci if (err) { 250262306a36Sopenharmony_ci bphy_err(drvr, "failed to clean up user-space RSNE\n"); 250362306a36Sopenharmony_ci goto done; 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci err = brcmf_set_sae_password(ifp, sme->crypto.sae_pwd, 250662306a36Sopenharmony_ci sme->crypto.sae_pwd_len); 250762306a36Sopenharmony_ci if (!err && sme->crypto.psk) 250862306a36Sopenharmony_ci err = brcmf_set_pmk(ifp, sme->crypto.psk, 250962306a36Sopenharmony_ci BRCMF_WSEC_MAX_PSK_LEN); 251062306a36Sopenharmony_ci } 251162306a36Sopenharmony_ci if (err) 251262306a36Sopenharmony_ci goto done; 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci /* Join with specific BSSID and cached SSID 251562306a36Sopenharmony_ci * If SSID is zero join based on BSSID only 251662306a36Sopenharmony_ci */ 251762306a36Sopenharmony_ci join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) + 251862306a36Sopenharmony_ci offsetof(struct brcmf_assoc_params_le, chanspec_list); 251962306a36Sopenharmony_ci if (cfg->channel) 252062306a36Sopenharmony_ci join_params_size += sizeof(u16); 252162306a36Sopenharmony_ci ext_join_params = kzalloc(sizeof(*ext_join_params), GFP_KERNEL); 252262306a36Sopenharmony_ci if (ext_join_params == NULL) { 252362306a36Sopenharmony_ci err = -ENOMEM; 252462306a36Sopenharmony_ci goto done; 252562306a36Sopenharmony_ci } 252662306a36Sopenharmony_ci ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN); 252762306a36Sopenharmony_ci ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len); 252862306a36Sopenharmony_ci memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len); 252962306a36Sopenharmony_ci if (ssid_len < IEEE80211_MAX_SSID_LEN) 253062306a36Sopenharmony_ci brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n", 253162306a36Sopenharmony_ci ext_join_params->ssid_le.SSID, ssid_len); 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci /* Set up join scan parameters */ 253462306a36Sopenharmony_ci ext_join_params->scan_le.scan_type = -1; 253562306a36Sopenharmony_ci ext_join_params->scan_le.home_time = cpu_to_le32(-1); 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci if (sme->bssid) 253862306a36Sopenharmony_ci memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN); 253962306a36Sopenharmony_ci else 254062306a36Sopenharmony_ci eth_broadcast_addr(ext_join_params->assoc_le.bssid); 254162306a36Sopenharmony_ci 254262306a36Sopenharmony_ci if (cfg->channel) { 254362306a36Sopenharmony_ci ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1); 254462306a36Sopenharmony_ci 254562306a36Sopenharmony_ci ext_join_params->assoc_le.chanspec_list[0] = 254662306a36Sopenharmony_ci cpu_to_le16(chanspec); 254762306a36Sopenharmony_ci /* Increase dwell time to receive probe response or detect 254862306a36Sopenharmony_ci * beacon from target AP at a noisy air only during connect 254962306a36Sopenharmony_ci * command. 255062306a36Sopenharmony_ci */ 255162306a36Sopenharmony_ci ext_join_params->scan_le.active_time = 255262306a36Sopenharmony_ci cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS); 255362306a36Sopenharmony_ci ext_join_params->scan_le.passive_time = 255462306a36Sopenharmony_ci cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS); 255562306a36Sopenharmony_ci /* To sync with presence period of VSDB GO send probe request 255662306a36Sopenharmony_ci * more frequently. Probe request will be stopped when it gets 255762306a36Sopenharmony_ci * probe response from target AP/GO. 255862306a36Sopenharmony_ci */ 255962306a36Sopenharmony_ci ext_join_params->scan_le.nprobes = 256062306a36Sopenharmony_ci cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS / 256162306a36Sopenharmony_ci BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS); 256262306a36Sopenharmony_ci } else { 256362306a36Sopenharmony_ci ext_join_params->scan_le.active_time = cpu_to_le32(-1); 256462306a36Sopenharmony_ci ext_join_params->scan_le.passive_time = cpu_to_le32(-1); 256562306a36Sopenharmony_ci ext_join_params->scan_le.nprobes = cpu_to_le32(-1); 256662306a36Sopenharmony_ci } 256762306a36Sopenharmony_ci 256862306a36Sopenharmony_ci brcmf_set_join_pref(ifp, &sme->bss_select); 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params, 257162306a36Sopenharmony_ci join_params_size); 257262306a36Sopenharmony_ci kfree(ext_join_params); 257362306a36Sopenharmony_ci if (!err) 257462306a36Sopenharmony_ci /* This is it. join command worked, we are done */ 257562306a36Sopenharmony_ci goto done; 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci /* join command failed, fallback to set ssid */ 257862306a36Sopenharmony_ci memset(&join_params, 0, sizeof(join_params)); 257962306a36Sopenharmony_ci join_params_size = sizeof(join_params.ssid_le); 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len); 258262306a36Sopenharmony_ci join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len); 258362306a36Sopenharmony_ci 258462306a36Sopenharmony_ci if (sme->bssid) 258562306a36Sopenharmony_ci memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN); 258662306a36Sopenharmony_ci else 258762306a36Sopenharmony_ci eth_broadcast_addr(join_params.params_le.bssid); 258862306a36Sopenharmony_ci 258962306a36Sopenharmony_ci if (cfg->channel) { 259062306a36Sopenharmony_ci join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec); 259162306a36Sopenharmony_ci join_params.params_le.chanspec_num = cpu_to_le32(1); 259262306a36Sopenharmony_ci join_params_size += sizeof(join_params.params_le); 259362306a36Sopenharmony_ci } 259462306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, 259562306a36Sopenharmony_ci &join_params, join_params_size); 259662306a36Sopenharmony_ci if (err) 259762306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_SET_SSID failed (%d)\n", err); 259862306a36Sopenharmony_ci 259962306a36Sopenharmony_cidone: 260062306a36Sopenharmony_ci if (err) 260162306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); 260262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 260362306a36Sopenharmony_ci return err; 260462306a36Sopenharmony_ci} 260562306a36Sopenharmony_ci 260662306a36Sopenharmony_cistatic s32 260762306a36Sopenharmony_cibrcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev, 260862306a36Sopenharmony_ci u16 reason_code) 260962306a36Sopenharmony_ci{ 261062306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 261162306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 261262306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 261362306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 261462306a36Sopenharmony_ci struct brcmf_scb_val_le scbval; 261562306a36Sopenharmony_ci s32 err = 0; 261662306a36Sopenharmony_ci 261762306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code); 261862306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 261962306a36Sopenharmony_ci return -EIO; 262062306a36Sopenharmony_ci 262162306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); 262262306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state); 262362306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state); 262462306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state); 262562306a36Sopenharmony_ci cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL); 262662306a36Sopenharmony_ci 262762306a36Sopenharmony_ci memcpy(&scbval.ea, &profile->bssid, ETH_ALEN); 262862306a36Sopenharmony_ci scbval.val = cpu_to_le32(reason_code); 262962306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC, 263062306a36Sopenharmony_ci &scbval, sizeof(scbval)); 263162306a36Sopenharmony_ci if (err) 263262306a36Sopenharmony_ci bphy_err(drvr, "error (%d)\n", err); 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 263562306a36Sopenharmony_ci return err; 263662306a36Sopenharmony_ci} 263762306a36Sopenharmony_ci 263862306a36Sopenharmony_cistatic s32 263962306a36Sopenharmony_cibrcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, 264062306a36Sopenharmony_ci enum nl80211_tx_power_setting type, s32 mbm) 264162306a36Sopenharmony_ci{ 264262306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 264362306a36Sopenharmony_ci struct net_device *ndev = cfg_to_ndev(cfg); 264462306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 264562306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 264662306a36Sopenharmony_ci s32 err; 264762306a36Sopenharmony_ci s32 disable; 264862306a36Sopenharmony_ci u32 qdbm = 127; 264962306a36Sopenharmony_ci 265062306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm); 265162306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 265262306a36Sopenharmony_ci return -EIO; 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci switch (type) { 265562306a36Sopenharmony_ci case NL80211_TX_POWER_AUTOMATIC: 265662306a36Sopenharmony_ci break; 265762306a36Sopenharmony_ci case NL80211_TX_POWER_LIMITED: 265862306a36Sopenharmony_ci case NL80211_TX_POWER_FIXED: 265962306a36Sopenharmony_ci if (mbm < 0) { 266062306a36Sopenharmony_ci bphy_err(drvr, "TX_POWER_FIXED - dbm is negative\n"); 266162306a36Sopenharmony_ci err = -EINVAL; 266262306a36Sopenharmony_ci goto done; 266362306a36Sopenharmony_ci } 266462306a36Sopenharmony_ci qdbm = MBM_TO_DBM(4 * mbm); 266562306a36Sopenharmony_ci if (qdbm > 127) 266662306a36Sopenharmony_ci qdbm = 127; 266762306a36Sopenharmony_ci qdbm |= WL_TXPWR_OVERRIDE; 266862306a36Sopenharmony_ci break; 266962306a36Sopenharmony_ci default: 267062306a36Sopenharmony_ci bphy_err(drvr, "Unsupported type %d\n", type); 267162306a36Sopenharmony_ci err = -EINVAL; 267262306a36Sopenharmony_ci goto done; 267362306a36Sopenharmony_ci } 267462306a36Sopenharmony_ci /* Make sure radio is off or on as far as software is concerned */ 267562306a36Sopenharmony_ci disable = WL_RADIO_SW_DISABLE << 16; 267662306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable); 267762306a36Sopenharmony_ci if (err) 267862306a36Sopenharmony_ci bphy_err(drvr, "WLC_SET_RADIO error (%d)\n", err); 267962306a36Sopenharmony_ci 268062306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm); 268162306a36Sopenharmony_ci if (err) 268262306a36Sopenharmony_ci bphy_err(drvr, "qtxpower error (%d)\n", err); 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_cidone: 268562306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE); 268662306a36Sopenharmony_ci return err; 268762306a36Sopenharmony_ci} 268862306a36Sopenharmony_ci 268962306a36Sopenharmony_cistatic s32 269062306a36Sopenharmony_cibrcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev, 269162306a36Sopenharmony_ci s32 *dbm) 269262306a36Sopenharmony_ci{ 269362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 269462306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif = wdev_to_vif(wdev); 269562306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 269662306a36Sopenharmony_ci s32 qdbm = 0; 269762306a36Sopenharmony_ci s32 err; 269862306a36Sopenharmony_ci 269962306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 270062306a36Sopenharmony_ci if (!check_vif_up(vif)) 270162306a36Sopenharmony_ci return -EIO; 270262306a36Sopenharmony_ci 270362306a36Sopenharmony_ci err = brcmf_fil_iovar_int_get(vif->ifp, "qtxpower", &qdbm); 270462306a36Sopenharmony_ci if (err) { 270562306a36Sopenharmony_ci bphy_err(drvr, "error (%d)\n", err); 270662306a36Sopenharmony_ci goto done; 270762306a36Sopenharmony_ci } 270862306a36Sopenharmony_ci *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4; 270962306a36Sopenharmony_ci 271062306a36Sopenharmony_cidone: 271162306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm); 271262306a36Sopenharmony_ci return err; 271362306a36Sopenharmony_ci} 271462306a36Sopenharmony_ci 271562306a36Sopenharmony_cistatic s32 271662306a36Sopenharmony_cibrcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev, 271762306a36Sopenharmony_ci int link_id, u8 key_idx, bool unicast, 271862306a36Sopenharmony_ci bool multicast) 271962306a36Sopenharmony_ci{ 272062306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 272162306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 272262306a36Sopenharmony_ci u32 index; 272362306a36Sopenharmony_ci u32 wsec; 272462306a36Sopenharmony_ci s32 err = 0; 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 272762306a36Sopenharmony_ci brcmf_dbg(CONN, "key index (%d)\n", key_idx); 272862306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 272962306a36Sopenharmony_ci return -EIO; 273062306a36Sopenharmony_ci 273162306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); 273262306a36Sopenharmony_ci if (err) { 273362306a36Sopenharmony_ci bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err); 273462306a36Sopenharmony_ci goto done; 273562306a36Sopenharmony_ci } 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci if (wsec & WEP_ENABLED) { 273862306a36Sopenharmony_ci /* Just select a new current key */ 273962306a36Sopenharmony_ci index = key_idx; 274062306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, 274162306a36Sopenharmony_ci BRCMF_C_SET_KEY_PRIMARY, index); 274262306a36Sopenharmony_ci if (err) 274362306a36Sopenharmony_ci bphy_err(drvr, "error (%d)\n", err); 274462306a36Sopenharmony_ci } 274562306a36Sopenharmony_cidone: 274662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 274762306a36Sopenharmony_ci return err; 274862306a36Sopenharmony_ci} 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_cistatic s32 275162306a36Sopenharmony_cibrcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev, 275262306a36Sopenharmony_ci int link_id, u8 key_idx, bool pairwise, 275362306a36Sopenharmony_ci const u8 *mac_addr) 275462306a36Sopenharmony_ci{ 275562306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 275662306a36Sopenharmony_ci struct brcmf_wsec_key *key; 275762306a36Sopenharmony_ci s32 err; 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 276062306a36Sopenharmony_ci brcmf_dbg(CONN, "key index (%d)\n", key_idx); 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 276362306a36Sopenharmony_ci return -EIO; 276462306a36Sopenharmony_ci 276562306a36Sopenharmony_ci if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { 276662306a36Sopenharmony_ci /* we ignore this key index in this case */ 276762306a36Sopenharmony_ci return -EINVAL; 276862306a36Sopenharmony_ci } 276962306a36Sopenharmony_ci 277062306a36Sopenharmony_ci key = &ifp->vif->profile.key[key_idx]; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci if (key->algo == CRYPTO_ALGO_OFF) { 277362306a36Sopenharmony_ci brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n"); 277462306a36Sopenharmony_ci return -EINVAL; 277562306a36Sopenharmony_ci } 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci memset(key, 0, sizeof(*key)); 277862306a36Sopenharmony_ci key->index = (u32)key_idx; 277962306a36Sopenharmony_ci key->flags = BRCMF_PRIMARY_KEY; 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci /* Clear the key/index */ 278262306a36Sopenharmony_ci err = send_key_to_dongle(ifp, key); 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 278562306a36Sopenharmony_ci return err; 278662306a36Sopenharmony_ci} 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_cistatic s32 278962306a36Sopenharmony_cibrcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev, 279062306a36Sopenharmony_ci int link_id, u8 key_idx, bool pairwise, 279162306a36Sopenharmony_ci const u8 *mac_addr, struct key_params *params) 279262306a36Sopenharmony_ci{ 279362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 279462306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 279562306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 279662306a36Sopenharmony_ci struct brcmf_wsec_key *key; 279762306a36Sopenharmony_ci s32 val; 279862306a36Sopenharmony_ci s32 wsec; 279962306a36Sopenharmony_ci s32 err; 280062306a36Sopenharmony_ci u8 keybuf[8]; 280162306a36Sopenharmony_ci bool ext_key; 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 280462306a36Sopenharmony_ci brcmf_dbg(CONN, "key index (%d)\n", key_idx); 280562306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 280662306a36Sopenharmony_ci return -EIO; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) { 280962306a36Sopenharmony_ci /* we ignore this key index in this case */ 281062306a36Sopenharmony_ci bphy_err(drvr, "invalid key index (%d)\n", key_idx); 281162306a36Sopenharmony_ci return -EINVAL; 281262306a36Sopenharmony_ci } 281362306a36Sopenharmony_ci 281462306a36Sopenharmony_ci if (params->key_len == 0) 281562306a36Sopenharmony_ci return brcmf_cfg80211_del_key(wiphy, ndev, -1, key_idx, 281662306a36Sopenharmony_ci pairwise, mac_addr); 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci if (params->key_len > sizeof(key->data)) { 281962306a36Sopenharmony_ci bphy_err(drvr, "Too long key length (%u)\n", params->key_len); 282062306a36Sopenharmony_ci return -EINVAL; 282162306a36Sopenharmony_ci } 282262306a36Sopenharmony_ci 282362306a36Sopenharmony_ci ext_key = false; 282462306a36Sopenharmony_ci if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) && 282562306a36Sopenharmony_ci (params->cipher != WLAN_CIPHER_SUITE_WEP104)) { 282662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr); 282762306a36Sopenharmony_ci ext_key = true; 282862306a36Sopenharmony_ci } 282962306a36Sopenharmony_ci 283062306a36Sopenharmony_ci key = &ifp->vif->profile.key[key_idx]; 283162306a36Sopenharmony_ci memset(key, 0, sizeof(*key)); 283262306a36Sopenharmony_ci if ((ext_key) && (!is_multicast_ether_addr(mac_addr))) 283362306a36Sopenharmony_ci memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN); 283462306a36Sopenharmony_ci key->len = params->key_len; 283562306a36Sopenharmony_ci key->index = key_idx; 283662306a36Sopenharmony_ci memcpy(key->data, params->key, key->len); 283762306a36Sopenharmony_ci if (!ext_key) 283862306a36Sopenharmony_ci key->flags = BRCMF_PRIMARY_KEY; 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci if (params->seq && params->seq_len == 6) { 284162306a36Sopenharmony_ci /* rx iv */ 284262306a36Sopenharmony_ci u8 *ivptr; 284362306a36Sopenharmony_ci 284462306a36Sopenharmony_ci ivptr = (u8 *)params->seq; 284562306a36Sopenharmony_ci key->rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) | 284662306a36Sopenharmony_ci (ivptr[3] << 8) | ivptr[2]; 284762306a36Sopenharmony_ci key->rxiv.lo = (ivptr[1] << 8) | ivptr[0]; 284862306a36Sopenharmony_ci key->iv_initialized = true; 284962306a36Sopenharmony_ci } 285062306a36Sopenharmony_ci 285162306a36Sopenharmony_ci switch (params->cipher) { 285262306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP40: 285362306a36Sopenharmony_ci key->algo = CRYPTO_ALGO_WEP1; 285462306a36Sopenharmony_ci val = WEP_ENABLED; 285562306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); 285662306a36Sopenharmony_ci break; 285762306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_WEP104: 285862306a36Sopenharmony_ci key->algo = CRYPTO_ALGO_WEP128; 285962306a36Sopenharmony_ci val = WEP_ENABLED; 286062306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); 286162306a36Sopenharmony_ci break; 286262306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_TKIP: 286362306a36Sopenharmony_ci if (!brcmf_is_apmode(ifp->vif)) { 286462306a36Sopenharmony_ci brcmf_dbg(CONN, "Swapping RX/TX MIC key\n"); 286562306a36Sopenharmony_ci memcpy(keybuf, &key->data[24], sizeof(keybuf)); 286662306a36Sopenharmony_ci memcpy(&key->data[24], &key->data[16], sizeof(keybuf)); 286762306a36Sopenharmony_ci memcpy(&key->data[16], keybuf, sizeof(keybuf)); 286862306a36Sopenharmony_ci } 286962306a36Sopenharmony_ci key->algo = CRYPTO_ALGO_TKIP; 287062306a36Sopenharmony_ci val = TKIP_ENABLED; 287162306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); 287262306a36Sopenharmony_ci break; 287362306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_AES_CMAC: 287462306a36Sopenharmony_ci key->algo = CRYPTO_ALGO_AES_CCM; 287562306a36Sopenharmony_ci val = AES_ENABLED; 287662306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); 287762306a36Sopenharmony_ci break; 287862306a36Sopenharmony_ci case WLAN_CIPHER_SUITE_CCMP: 287962306a36Sopenharmony_ci key->algo = CRYPTO_ALGO_AES_CCM; 288062306a36Sopenharmony_ci val = AES_ENABLED; 288162306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n"); 288262306a36Sopenharmony_ci break; 288362306a36Sopenharmony_ci default: 288462306a36Sopenharmony_ci bphy_err(drvr, "Invalid cipher (0x%x)\n", params->cipher); 288562306a36Sopenharmony_ci err = -EINVAL; 288662306a36Sopenharmony_ci goto done; 288762306a36Sopenharmony_ci } 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci err = send_key_to_dongle(ifp, key); 289062306a36Sopenharmony_ci if (ext_key || err) 289162306a36Sopenharmony_ci goto done; 289262306a36Sopenharmony_ci 289362306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); 289462306a36Sopenharmony_ci if (err) { 289562306a36Sopenharmony_ci bphy_err(drvr, "get wsec error (%d)\n", err); 289662306a36Sopenharmony_ci goto done; 289762306a36Sopenharmony_ci } 289862306a36Sopenharmony_ci wsec |= val; 289962306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); 290062306a36Sopenharmony_ci if (err) { 290162306a36Sopenharmony_ci bphy_err(drvr, "set wsec error (%d)\n", err); 290262306a36Sopenharmony_ci goto done; 290362306a36Sopenharmony_ci } 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_cidone: 290662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 290762306a36Sopenharmony_ci return err; 290862306a36Sopenharmony_ci} 290962306a36Sopenharmony_ci 291062306a36Sopenharmony_cistatic s32 291162306a36Sopenharmony_cibrcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev, 291262306a36Sopenharmony_ci int link_id, u8 key_idx, bool pairwise, 291362306a36Sopenharmony_ci const u8 *mac_addr, void *cookie, 291462306a36Sopenharmony_ci void (*callback)(void *cookie, 291562306a36Sopenharmony_ci struct key_params *params)) 291662306a36Sopenharmony_ci{ 291762306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 291862306a36Sopenharmony_ci struct key_params params; 291962306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 292062306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 292162306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 292262306a36Sopenharmony_ci struct brcmf_cfg80211_security *sec; 292362306a36Sopenharmony_ci s32 wsec; 292462306a36Sopenharmony_ci s32 err = 0; 292562306a36Sopenharmony_ci 292662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 292762306a36Sopenharmony_ci brcmf_dbg(CONN, "key index (%d)\n", key_idx); 292862306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 292962306a36Sopenharmony_ci return -EIO; 293062306a36Sopenharmony_ci 293162306a36Sopenharmony_ci memset(¶ms, 0, sizeof(params)); 293262306a36Sopenharmony_ci 293362306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); 293462306a36Sopenharmony_ci if (err) { 293562306a36Sopenharmony_ci bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err); 293662306a36Sopenharmony_ci /* Ignore this error, may happen during DISASSOC */ 293762306a36Sopenharmony_ci err = -EAGAIN; 293862306a36Sopenharmony_ci goto done; 293962306a36Sopenharmony_ci } 294062306a36Sopenharmony_ci if (wsec & WEP_ENABLED) { 294162306a36Sopenharmony_ci sec = &profile->sec; 294262306a36Sopenharmony_ci if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) { 294362306a36Sopenharmony_ci params.cipher = WLAN_CIPHER_SUITE_WEP40; 294462306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n"); 294562306a36Sopenharmony_ci } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) { 294662306a36Sopenharmony_ci params.cipher = WLAN_CIPHER_SUITE_WEP104; 294762306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n"); 294862306a36Sopenharmony_ci } 294962306a36Sopenharmony_ci } else if (wsec & TKIP_ENABLED) { 295062306a36Sopenharmony_ci params.cipher = WLAN_CIPHER_SUITE_TKIP; 295162306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n"); 295262306a36Sopenharmony_ci } else if (wsec & AES_ENABLED) { 295362306a36Sopenharmony_ci params.cipher = WLAN_CIPHER_SUITE_AES_CMAC; 295462306a36Sopenharmony_ci brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n"); 295562306a36Sopenharmony_ci } else { 295662306a36Sopenharmony_ci bphy_err(drvr, "Invalid algo (0x%x)\n", wsec); 295762306a36Sopenharmony_ci err = -EINVAL; 295862306a36Sopenharmony_ci goto done; 295962306a36Sopenharmony_ci } 296062306a36Sopenharmony_ci callback(cookie, ¶ms); 296162306a36Sopenharmony_ci 296262306a36Sopenharmony_cidone: 296362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 296462306a36Sopenharmony_ci return err; 296562306a36Sopenharmony_ci} 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_cistatic s32 296862306a36Sopenharmony_cibrcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy, 296962306a36Sopenharmony_ci struct net_device *ndev, int link_id, 297062306a36Sopenharmony_ci u8 key_idx) 297162306a36Sopenharmony_ci{ 297262306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 297362306a36Sopenharmony_ci 297462306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx); 297562306a36Sopenharmony_ci 297662306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) 297762306a36Sopenharmony_ci return 0; 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci brcmf_dbg(INFO, "Not supported\n"); 298062306a36Sopenharmony_ci 298162306a36Sopenharmony_ci return -EOPNOTSUPP; 298262306a36Sopenharmony_ci} 298362306a36Sopenharmony_ci 298462306a36Sopenharmony_cistatic void 298562306a36Sopenharmony_cibrcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp) 298662306a36Sopenharmony_ci{ 298762306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 298862306a36Sopenharmony_ci s32 err; 298962306a36Sopenharmony_ci u8 key_idx; 299062306a36Sopenharmony_ci struct brcmf_wsec_key *key; 299162306a36Sopenharmony_ci s32 wsec; 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_ci for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) { 299462306a36Sopenharmony_ci key = &ifp->vif->profile.key[key_idx]; 299562306a36Sopenharmony_ci if ((key->algo == CRYPTO_ALGO_WEP1) || 299662306a36Sopenharmony_ci (key->algo == CRYPTO_ALGO_WEP128)) 299762306a36Sopenharmony_ci break; 299862306a36Sopenharmony_ci } 299962306a36Sopenharmony_ci if (key_idx == BRCMF_MAX_DEFAULT_KEYS) 300062306a36Sopenharmony_ci return; 300162306a36Sopenharmony_ci 300262306a36Sopenharmony_ci err = send_key_to_dongle(ifp, key); 300362306a36Sopenharmony_ci if (err) { 300462306a36Sopenharmony_ci bphy_err(drvr, "Setting WEP key failed (%d)\n", err); 300562306a36Sopenharmony_ci return; 300662306a36Sopenharmony_ci } 300762306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec); 300862306a36Sopenharmony_ci if (err) { 300962306a36Sopenharmony_ci bphy_err(drvr, "get wsec error (%d)\n", err); 301062306a36Sopenharmony_ci return; 301162306a36Sopenharmony_ci } 301262306a36Sopenharmony_ci wsec |= WEP_ENABLED; 301362306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); 301462306a36Sopenharmony_ci if (err) 301562306a36Sopenharmony_ci bphy_err(drvr, "set wsec error (%d)\n", err); 301662306a36Sopenharmony_ci} 301762306a36Sopenharmony_ci 301862306a36Sopenharmony_cistatic void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si) 301962306a36Sopenharmony_ci{ 302062306a36Sopenharmony_ci struct nl80211_sta_flag_update *sfu; 302162306a36Sopenharmony_ci 302262306a36Sopenharmony_ci brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags); 302362306a36Sopenharmony_ci si->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS); 302462306a36Sopenharmony_ci sfu = &si->sta_flags; 302562306a36Sopenharmony_ci sfu->mask = BIT(NL80211_STA_FLAG_WME) | 302662306a36Sopenharmony_ci BIT(NL80211_STA_FLAG_AUTHENTICATED) | 302762306a36Sopenharmony_ci BIT(NL80211_STA_FLAG_ASSOCIATED) | 302862306a36Sopenharmony_ci BIT(NL80211_STA_FLAG_AUTHORIZED); 302962306a36Sopenharmony_ci if (fw_sta_flags & BRCMF_STA_WME) 303062306a36Sopenharmony_ci sfu->set |= BIT(NL80211_STA_FLAG_WME); 303162306a36Sopenharmony_ci if (fw_sta_flags & BRCMF_STA_AUTHE) 303262306a36Sopenharmony_ci sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED); 303362306a36Sopenharmony_ci if (fw_sta_flags & BRCMF_STA_ASSOC) 303462306a36Sopenharmony_ci sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED); 303562306a36Sopenharmony_ci if (fw_sta_flags & BRCMF_STA_AUTHO) 303662306a36Sopenharmony_ci sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED); 303762306a36Sopenharmony_ci} 303862306a36Sopenharmony_ci 303962306a36Sopenharmony_cistatic void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si) 304062306a36Sopenharmony_ci{ 304162306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 304262306a36Sopenharmony_ci struct { 304362306a36Sopenharmony_ci __le32 len; 304462306a36Sopenharmony_ci struct brcmf_bss_info_le bss_le; 304562306a36Sopenharmony_ci } *buf; 304662306a36Sopenharmony_ci u16 capability; 304762306a36Sopenharmony_ci int err; 304862306a36Sopenharmony_ci 304962306a36Sopenharmony_ci buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); 305062306a36Sopenharmony_ci if (!buf) 305162306a36Sopenharmony_ci return; 305262306a36Sopenharmony_ci 305362306a36Sopenharmony_ci buf->len = cpu_to_le32(WL_BSS_INFO_MAX); 305462306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf, 305562306a36Sopenharmony_ci WL_BSS_INFO_MAX); 305662306a36Sopenharmony_ci if (err) { 305762306a36Sopenharmony_ci bphy_err(drvr, "Failed to get bss info (%d)\n", err); 305862306a36Sopenharmony_ci goto out_kfree; 305962306a36Sopenharmony_ci } 306062306a36Sopenharmony_ci si->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM); 306162306a36Sopenharmony_ci si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period); 306262306a36Sopenharmony_ci si->bss_param.dtim_period = buf->bss_le.dtim_period; 306362306a36Sopenharmony_ci capability = le16_to_cpu(buf->bss_le.capability); 306462306a36Sopenharmony_ci if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT) 306562306a36Sopenharmony_ci si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT; 306662306a36Sopenharmony_ci if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE) 306762306a36Sopenharmony_ci si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE; 306862306a36Sopenharmony_ci if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) 306962306a36Sopenharmony_ci si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME; 307062306a36Sopenharmony_ci 307162306a36Sopenharmony_ciout_kfree: 307262306a36Sopenharmony_ci kfree(buf); 307362306a36Sopenharmony_ci} 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_cistatic s32 307662306a36Sopenharmony_cibrcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp, 307762306a36Sopenharmony_ci struct station_info *sinfo) 307862306a36Sopenharmony_ci{ 307962306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 308062306a36Sopenharmony_ci struct brcmf_scb_val_le scbval; 308162306a36Sopenharmony_ci struct brcmf_pktcnt_le pktcnt; 308262306a36Sopenharmony_ci s32 err; 308362306a36Sopenharmony_ci u32 rate; 308462306a36Sopenharmony_ci u32 rssi; 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci /* Get the current tx rate */ 308762306a36Sopenharmony_ci err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate); 308862306a36Sopenharmony_ci if (err < 0) { 308962306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_GET_RATE error (%d)\n", err); 309062306a36Sopenharmony_ci return err; 309162306a36Sopenharmony_ci } 309262306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 309362306a36Sopenharmony_ci sinfo->txrate.legacy = rate * 5; 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci memset(&scbval, 0, sizeof(scbval)); 309662306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval, 309762306a36Sopenharmony_ci sizeof(scbval)); 309862306a36Sopenharmony_ci if (err) { 309962306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_GET_RSSI error (%d)\n", err); 310062306a36Sopenharmony_ci return err; 310162306a36Sopenharmony_ci } 310262306a36Sopenharmony_ci rssi = le32_to_cpu(scbval.val); 310362306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 310462306a36Sopenharmony_ci sinfo->signal = rssi; 310562306a36Sopenharmony_ci 310662306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt, 310762306a36Sopenharmony_ci sizeof(pktcnt)); 310862306a36Sopenharmony_ci if (err) { 310962306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err); 311062306a36Sopenharmony_ci return err; 311162306a36Sopenharmony_ci } 311262306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS) | 311362306a36Sopenharmony_ci BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) | 311462306a36Sopenharmony_ci BIT_ULL(NL80211_STA_INFO_TX_PACKETS) | 311562306a36Sopenharmony_ci BIT_ULL(NL80211_STA_INFO_TX_FAILED); 311662306a36Sopenharmony_ci sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt); 311762306a36Sopenharmony_ci sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt); 311862306a36Sopenharmony_ci sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt); 311962306a36Sopenharmony_ci sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt); 312062306a36Sopenharmony_ci 312162306a36Sopenharmony_ci return 0; 312262306a36Sopenharmony_ci} 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_cistatic s32 312562306a36Sopenharmony_cibrcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev, 312662306a36Sopenharmony_ci const u8 *mac, struct station_info *sinfo) 312762306a36Sopenharmony_ci{ 312862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 312962306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 313062306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 313162306a36Sopenharmony_ci struct brcmf_scb_val_le scb_val; 313262306a36Sopenharmony_ci s32 err = 0; 313362306a36Sopenharmony_ci struct brcmf_sta_info_le sta_info_le; 313462306a36Sopenharmony_ci u32 sta_flags; 313562306a36Sopenharmony_ci u32 is_tdls_peer; 313662306a36Sopenharmony_ci s32 total_rssi_avg = 0; 313762306a36Sopenharmony_ci s32 total_rssi = 0; 313862306a36Sopenharmony_ci s32 count_rssi = 0; 313962306a36Sopenharmony_ci int rssi; 314062306a36Sopenharmony_ci u32 i; 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac); 314362306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 314462306a36Sopenharmony_ci return -EIO; 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci if (brcmf_is_ibssmode(ifp->vif)) 314762306a36Sopenharmony_ci return brcmf_cfg80211_get_station_ibss(ifp, sinfo); 314862306a36Sopenharmony_ci 314962306a36Sopenharmony_ci memset(&sta_info_le, 0, sizeof(sta_info_le)); 315062306a36Sopenharmony_ci memcpy(&sta_info_le, mac, ETH_ALEN); 315162306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info", 315262306a36Sopenharmony_ci &sta_info_le, 315362306a36Sopenharmony_ci sizeof(sta_info_le)); 315462306a36Sopenharmony_ci is_tdls_peer = !err; 315562306a36Sopenharmony_ci if (err) { 315662306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "sta_info", 315762306a36Sopenharmony_ci &sta_info_le, 315862306a36Sopenharmony_ci sizeof(sta_info_le)); 315962306a36Sopenharmony_ci if (err < 0) { 316062306a36Sopenharmony_ci bphy_err(drvr, "GET STA INFO failed, %d\n", err); 316162306a36Sopenharmony_ci goto done; 316262306a36Sopenharmony_ci } 316362306a36Sopenharmony_ci } 316462306a36Sopenharmony_ci brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver)); 316562306a36Sopenharmony_ci sinfo->filled = BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME); 316662306a36Sopenharmony_ci sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000; 316762306a36Sopenharmony_ci sta_flags = le32_to_cpu(sta_info_le.flags); 316862306a36Sopenharmony_ci brcmf_convert_sta_flags(sta_flags, sinfo); 316962306a36Sopenharmony_ci sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER); 317062306a36Sopenharmony_ci if (is_tdls_peer) 317162306a36Sopenharmony_ci sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER); 317262306a36Sopenharmony_ci else 317362306a36Sopenharmony_ci sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER); 317462306a36Sopenharmony_ci if (sta_flags & BRCMF_STA_ASSOC) { 317562306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME); 317662306a36Sopenharmony_ci sinfo->connected_time = le32_to_cpu(sta_info_le.in); 317762306a36Sopenharmony_ci brcmf_fill_bss_param(ifp, sinfo); 317862306a36Sopenharmony_ci } 317962306a36Sopenharmony_ci if (sta_flags & BRCMF_STA_SCBSTATS) { 318062306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED); 318162306a36Sopenharmony_ci sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures); 318262306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS); 318362306a36Sopenharmony_ci sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts); 318462306a36Sopenharmony_ci sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts); 318562306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS); 318662306a36Sopenharmony_ci sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts); 318762306a36Sopenharmony_ci sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts); 318862306a36Sopenharmony_ci if (sinfo->tx_packets) { 318962306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE); 319062306a36Sopenharmony_ci sinfo->txrate.legacy = 319162306a36Sopenharmony_ci le32_to_cpu(sta_info_le.tx_rate) / 100; 319262306a36Sopenharmony_ci } 319362306a36Sopenharmony_ci if (sinfo->rx_packets) { 319462306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE); 319562306a36Sopenharmony_ci sinfo->rxrate.legacy = 319662306a36Sopenharmony_ci le32_to_cpu(sta_info_le.rx_rate) / 100; 319762306a36Sopenharmony_ci } 319862306a36Sopenharmony_ci if (le16_to_cpu(sta_info_le.ver) >= 4) { 319962306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES); 320062306a36Sopenharmony_ci sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes); 320162306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES); 320262306a36Sopenharmony_ci sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes); 320362306a36Sopenharmony_ci } 320462306a36Sopenharmony_ci for (i = 0; i < BRCMF_ANT_MAX; i++) { 320562306a36Sopenharmony_ci if (sta_info_le.rssi[i] == 0 || 320662306a36Sopenharmony_ci sta_info_le.rx_lastpkt_rssi[i] == 0) 320762306a36Sopenharmony_ci continue; 320862306a36Sopenharmony_ci sinfo->chains |= BIT(count_rssi); 320962306a36Sopenharmony_ci sinfo->chain_signal[count_rssi] = 321062306a36Sopenharmony_ci sta_info_le.rx_lastpkt_rssi[i]; 321162306a36Sopenharmony_ci sinfo->chain_signal_avg[count_rssi] = 321262306a36Sopenharmony_ci sta_info_le.rssi[i]; 321362306a36Sopenharmony_ci total_rssi += sta_info_le.rx_lastpkt_rssi[i]; 321462306a36Sopenharmony_ci total_rssi_avg += sta_info_le.rssi[i]; 321562306a36Sopenharmony_ci count_rssi++; 321662306a36Sopenharmony_ci } 321762306a36Sopenharmony_ci if (count_rssi) { 321862306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 321962306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG); 322062306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL); 322162306a36Sopenharmony_ci sinfo->filled |= 322262306a36Sopenharmony_ci BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG); 322362306a36Sopenharmony_ci sinfo->signal = total_rssi / count_rssi; 322462306a36Sopenharmony_ci sinfo->signal_avg = total_rssi_avg / count_rssi; 322562306a36Sopenharmony_ci } else if (test_bit(BRCMF_VIF_STATUS_CONNECTED, 322662306a36Sopenharmony_ci &ifp->vif->sme_state)) { 322762306a36Sopenharmony_ci memset(&scb_val, 0, sizeof(scb_val)); 322862306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, 322962306a36Sopenharmony_ci &scb_val, sizeof(scb_val)); 323062306a36Sopenharmony_ci if (err) { 323162306a36Sopenharmony_ci bphy_err(drvr, "Could not get rssi (%d)\n", 323262306a36Sopenharmony_ci err); 323362306a36Sopenharmony_ci goto done; 323462306a36Sopenharmony_ci } else { 323562306a36Sopenharmony_ci rssi = le32_to_cpu(scb_val.val); 323662306a36Sopenharmony_ci sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL); 323762306a36Sopenharmony_ci sinfo->signal = rssi; 323862306a36Sopenharmony_ci brcmf_dbg(CONN, "RSSI %d dBm\n", rssi); 323962306a36Sopenharmony_ci } 324062306a36Sopenharmony_ci } 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_cidone: 324362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 324462306a36Sopenharmony_ci return err; 324562306a36Sopenharmony_ci} 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_cistatic int 324862306a36Sopenharmony_cibrcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev, 324962306a36Sopenharmony_ci int idx, u8 *mac, struct station_info *sinfo) 325062306a36Sopenharmony_ci{ 325162306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 325262306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 325362306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 325462306a36Sopenharmony_ci s32 err; 325562306a36Sopenharmony_ci 325662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter, idx %d\n", idx); 325762306a36Sopenharmony_ci 325862306a36Sopenharmony_ci if (idx == 0) { 325962306a36Sopenharmony_ci cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST); 326062306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST, 326162306a36Sopenharmony_ci &cfg->assoclist, 326262306a36Sopenharmony_ci sizeof(cfg->assoclist)); 326362306a36Sopenharmony_ci if (err) { 326462306a36Sopenharmony_ci /* GET_ASSOCLIST unsupported by firmware of older chips */ 326562306a36Sopenharmony_ci if (err == -EBADE) 326662306a36Sopenharmony_ci bphy_info_once(drvr, "BRCMF_C_GET_ASSOCLIST unsupported\n"); 326762306a36Sopenharmony_ci else 326862306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_GET_ASSOCLIST failed, err=%d\n", 326962306a36Sopenharmony_ci err); 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci cfg->assoclist.count = 0; 327262306a36Sopenharmony_ci return -EOPNOTSUPP; 327362306a36Sopenharmony_ci } 327462306a36Sopenharmony_ci } 327562306a36Sopenharmony_ci if (idx < le32_to_cpu(cfg->assoclist.count)) { 327662306a36Sopenharmony_ci memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN); 327762306a36Sopenharmony_ci return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo); 327862306a36Sopenharmony_ci } 327962306a36Sopenharmony_ci return -ENOENT; 328062306a36Sopenharmony_ci} 328162306a36Sopenharmony_ci 328262306a36Sopenharmony_cistatic s32 328362306a36Sopenharmony_cibrcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev, 328462306a36Sopenharmony_ci bool enabled, s32 timeout) 328562306a36Sopenharmony_ci{ 328662306a36Sopenharmony_ci s32 pm; 328762306a36Sopenharmony_ci s32 err = 0; 328862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 328962306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 329062306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 329162306a36Sopenharmony_ci 329262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ci /* 329562306a36Sopenharmony_ci * Powersave enable/disable request is coming from the 329662306a36Sopenharmony_ci * cfg80211 even before the interface is up. In that 329762306a36Sopenharmony_ci * scenario, driver will be storing the power save 329862306a36Sopenharmony_ci * preference in cfg struct to apply this to 329962306a36Sopenharmony_ci * FW later while initializing the dongle 330062306a36Sopenharmony_ci */ 330162306a36Sopenharmony_ci cfg->pwr_save = enabled; 330262306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) { 330362306a36Sopenharmony_ci 330462306a36Sopenharmony_ci brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n"); 330562306a36Sopenharmony_ci goto done; 330662306a36Sopenharmony_ci } 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci pm = enabled ? PM_FAST : PM_OFF; 330962306a36Sopenharmony_ci /* Do not enable the power save after assoc if it is a p2p interface */ 331062306a36Sopenharmony_ci if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) { 331162306a36Sopenharmony_ci brcmf_dbg(INFO, "Do not enable power save for P2P clients\n"); 331262306a36Sopenharmony_ci pm = PM_OFF; 331362306a36Sopenharmony_ci } 331462306a36Sopenharmony_ci brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled")); 331562306a36Sopenharmony_ci 331662306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm); 331762306a36Sopenharmony_ci if (err) { 331862306a36Sopenharmony_ci if (err == -ENODEV) 331962306a36Sopenharmony_ci bphy_err(drvr, "net_device is not ready yet\n"); 332062306a36Sopenharmony_ci else 332162306a36Sopenharmony_ci bphy_err(drvr, "error (%d)\n", err); 332262306a36Sopenharmony_ci } 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret", 332562306a36Sopenharmony_ci min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS)); 332662306a36Sopenharmony_ci if (err) 332762306a36Sopenharmony_ci bphy_err(drvr, "Unable to set pm timeout, (%d)\n", err); 332862306a36Sopenharmony_ci 332962306a36Sopenharmony_cidone: 333062306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 333162306a36Sopenharmony_ci return err; 333262306a36Sopenharmony_ci} 333362306a36Sopenharmony_ci 333462306a36Sopenharmony_cistatic s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg, 333562306a36Sopenharmony_ci struct brcmf_bss_info_le *bi) 333662306a36Sopenharmony_ci{ 333762306a36Sopenharmony_ci struct wiphy *wiphy = cfg_to_wiphy(cfg); 333862306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 333962306a36Sopenharmony_ci struct cfg80211_bss *bss; 334062306a36Sopenharmony_ci enum nl80211_band band; 334162306a36Sopenharmony_ci struct brcmu_chan ch; 334262306a36Sopenharmony_ci u16 channel; 334362306a36Sopenharmony_ci u32 freq; 334462306a36Sopenharmony_ci u16 notify_capability; 334562306a36Sopenharmony_ci u16 notify_interval; 334662306a36Sopenharmony_ci u8 *notify_ie; 334762306a36Sopenharmony_ci size_t notify_ielen; 334862306a36Sopenharmony_ci struct cfg80211_inform_bss bss_data = {}; 334962306a36Sopenharmony_ci 335062306a36Sopenharmony_ci if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) { 335162306a36Sopenharmony_ci bphy_err(drvr, "Bss info is larger than buffer. Discarding\n"); 335262306a36Sopenharmony_ci return -EINVAL; 335362306a36Sopenharmony_ci } 335462306a36Sopenharmony_ci 335562306a36Sopenharmony_ci if (!bi->ctl_ch) { 335662306a36Sopenharmony_ci ch.chspec = le16_to_cpu(bi->chanspec); 335762306a36Sopenharmony_ci cfg->d11inf.decchspec(&ch); 335862306a36Sopenharmony_ci bi->ctl_ch = ch.control_ch_num; 335962306a36Sopenharmony_ci } 336062306a36Sopenharmony_ci channel = bi->ctl_ch; 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci if (channel <= CH_MAX_2G_CHANNEL) 336362306a36Sopenharmony_ci band = NL80211_BAND_2GHZ; 336462306a36Sopenharmony_ci else 336562306a36Sopenharmony_ci band = NL80211_BAND_5GHZ; 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci freq = ieee80211_channel_to_frequency(channel, band); 336862306a36Sopenharmony_ci bss_data.chan = ieee80211_get_channel(wiphy, freq); 336962306a36Sopenharmony_ci bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20; 337062306a36Sopenharmony_ci bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime()); 337162306a36Sopenharmony_ci 337262306a36Sopenharmony_ci notify_capability = le16_to_cpu(bi->capability); 337362306a36Sopenharmony_ci notify_interval = le16_to_cpu(bi->beacon_period); 337462306a36Sopenharmony_ci notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); 337562306a36Sopenharmony_ci notify_ielen = le32_to_cpu(bi->ie_length); 337662306a36Sopenharmony_ci bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100; 337762306a36Sopenharmony_ci 337862306a36Sopenharmony_ci brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID); 337962306a36Sopenharmony_ci brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq); 338062306a36Sopenharmony_ci brcmf_dbg(CONN, "Capability: %X\n", notify_capability); 338162306a36Sopenharmony_ci brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval); 338262306a36Sopenharmony_ci brcmf_dbg(CONN, "Signal: %d\n", bss_data.signal); 338362306a36Sopenharmony_ci 338462306a36Sopenharmony_ci bss = cfg80211_inform_bss_data(wiphy, &bss_data, 338562306a36Sopenharmony_ci CFG80211_BSS_FTYPE_UNKNOWN, 338662306a36Sopenharmony_ci (const u8 *)bi->BSSID, 338762306a36Sopenharmony_ci 0, notify_capability, 338862306a36Sopenharmony_ci notify_interval, notify_ie, 338962306a36Sopenharmony_ci notify_ielen, GFP_KERNEL); 339062306a36Sopenharmony_ci 339162306a36Sopenharmony_ci if (!bss) 339262306a36Sopenharmony_ci return -ENOMEM; 339362306a36Sopenharmony_ci 339462306a36Sopenharmony_ci cfg80211_put_bss(wiphy, bss); 339562306a36Sopenharmony_ci 339662306a36Sopenharmony_ci return 0; 339762306a36Sopenharmony_ci} 339862306a36Sopenharmony_ci 339962306a36Sopenharmony_cistatic struct brcmf_bss_info_le * 340062306a36Sopenharmony_cinext_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss) 340162306a36Sopenharmony_ci{ 340262306a36Sopenharmony_ci if (bss == NULL) 340362306a36Sopenharmony_ci return list->bss_info_le; 340462306a36Sopenharmony_ci return (struct brcmf_bss_info_le *)((unsigned long)bss + 340562306a36Sopenharmony_ci le32_to_cpu(bss->length)); 340662306a36Sopenharmony_ci} 340762306a36Sopenharmony_ci 340862306a36Sopenharmony_cistatic s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg) 340962306a36Sopenharmony_ci{ 341062306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 341162306a36Sopenharmony_ci struct brcmf_scan_results *bss_list; 341262306a36Sopenharmony_ci struct brcmf_bss_info_le *bi = NULL; /* must be initialized */ 341362306a36Sopenharmony_ci s32 err = 0; 341462306a36Sopenharmony_ci int i; 341562306a36Sopenharmony_ci 341662306a36Sopenharmony_ci bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf; 341762306a36Sopenharmony_ci if (bss_list->count != 0 && 341862306a36Sopenharmony_ci bss_list->version != BRCMF_BSS_INFO_VERSION) { 341962306a36Sopenharmony_ci bphy_err(drvr, "Version %d != WL_BSS_INFO_VERSION\n", 342062306a36Sopenharmony_ci bss_list->version); 342162306a36Sopenharmony_ci return -EOPNOTSUPP; 342262306a36Sopenharmony_ci } 342362306a36Sopenharmony_ci brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count); 342462306a36Sopenharmony_ci for (i = 0; i < bss_list->count; i++) { 342562306a36Sopenharmony_ci bi = next_bss_le(bss_list, bi); 342662306a36Sopenharmony_ci err = brcmf_inform_single_bss(cfg, bi); 342762306a36Sopenharmony_ci if (err) 342862306a36Sopenharmony_ci break; 342962306a36Sopenharmony_ci } 343062306a36Sopenharmony_ci return err; 343162306a36Sopenharmony_ci} 343262306a36Sopenharmony_ci 343362306a36Sopenharmony_cistatic s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg, 343462306a36Sopenharmony_ci struct net_device *ndev, const u8 *bssid) 343562306a36Sopenharmony_ci{ 343662306a36Sopenharmony_ci struct wiphy *wiphy = cfg_to_wiphy(cfg); 343762306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 343862306a36Sopenharmony_ci struct ieee80211_channel *notify_channel; 343962306a36Sopenharmony_ci struct brcmf_bss_info_le *bi = NULL; 344062306a36Sopenharmony_ci struct ieee80211_supported_band *band; 344162306a36Sopenharmony_ci struct cfg80211_bss *bss; 344262306a36Sopenharmony_ci struct brcmu_chan ch; 344362306a36Sopenharmony_ci u8 *buf = NULL; 344462306a36Sopenharmony_ci s32 err = 0; 344562306a36Sopenharmony_ci u32 freq; 344662306a36Sopenharmony_ci u16 notify_capability; 344762306a36Sopenharmony_ci u16 notify_interval; 344862306a36Sopenharmony_ci u8 *notify_ie; 344962306a36Sopenharmony_ci size_t notify_ielen; 345062306a36Sopenharmony_ci s32 notify_signal; 345162306a36Sopenharmony_ci 345262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_ci buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); 345562306a36Sopenharmony_ci if (buf == NULL) { 345662306a36Sopenharmony_ci err = -ENOMEM; 345762306a36Sopenharmony_ci goto CleanUp; 345862306a36Sopenharmony_ci } 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); 346162306a36Sopenharmony_ci 346262306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO, 346362306a36Sopenharmony_ci buf, WL_BSS_INFO_MAX); 346462306a36Sopenharmony_ci if (err) { 346562306a36Sopenharmony_ci bphy_err(drvr, "WLC_GET_BSS_INFO failed: %d\n", err); 346662306a36Sopenharmony_ci goto CleanUp; 346762306a36Sopenharmony_ci } 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci bi = (struct brcmf_bss_info_le *)(buf + 4); 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_ci ch.chspec = le16_to_cpu(bi->chanspec); 347262306a36Sopenharmony_ci cfg->d11inf.decchspec(&ch); 347362306a36Sopenharmony_ci 347462306a36Sopenharmony_ci if (ch.band == BRCMU_CHAN_BAND_2G) 347562306a36Sopenharmony_ci band = wiphy->bands[NL80211_BAND_2GHZ]; 347662306a36Sopenharmony_ci else 347762306a36Sopenharmony_ci band = wiphy->bands[NL80211_BAND_5GHZ]; 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band); 348062306a36Sopenharmony_ci cfg->channel = freq; 348162306a36Sopenharmony_ci notify_channel = ieee80211_get_channel(wiphy, freq); 348262306a36Sopenharmony_ci 348362306a36Sopenharmony_ci notify_capability = le16_to_cpu(bi->capability); 348462306a36Sopenharmony_ci notify_interval = le16_to_cpu(bi->beacon_period); 348562306a36Sopenharmony_ci notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset); 348662306a36Sopenharmony_ci notify_ielen = le32_to_cpu(bi->ie_length); 348762306a36Sopenharmony_ci notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100; 348862306a36Sopenharmony_ci 348962306a36Sopenharmony_ci brcmf_dbg(CONN, "channel: %d(%d)\n", ch.control_ch_num, freq); 349062306a36Sopenharmony_ci brcmf_dbg(CONN, "capability: %X\n", notify_capability); 349162306a36Sopenharmony_ci brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval); 349262306a36Sopenharmony_ci brcmf_dbg(CONN, "signal: %d\n", notify_signal); 349362306a36Sopenharmony_ci 349462306a36Sopenharmony_ci bss = cfg80211_inform_bss(wiphy, notify_channel, 349562306a36Sopenharmony_ci CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0, 349662306a36Sopenharmony_ci notify_capability, notify_interval, 349762306a36Sopenharmony_ci notify_ie, notify_ielen, notify_signal, 349862306a36Sopenharmony_ci GFP_KERNEL); 349962306a36Sopenharmony_ci 350062306a36Sopenharmony_ci if (!bss) { 350162306a36Sopenharmony_ci err = -ENOMEM; 350262306a36Sopenharmony_ci goto CleanUp; 350362306a36Sopenharmony_ci } 350462306a36Sopenharmony_ci 350562306a36Sopenharmony_ci cfg80211_put_bss(wiphy, bss); 350662306a36Sopenharmony_ci 350762306a36Sopenharmony_ciCleanUp: 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci kfree(buf); 351062306a36Sopenharmony_ci 351162306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 351262306a36Sopenharmony_ci 351362306a36Sopenharmony_ci return err; 351462306a36Sopenharmony_ci} 351562306a36Sopenharmony_ci 351662306a36Sopenharmony_cistatic s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg, 351762306a36Sopenharmony_ci struct brcmf_if *ifp) 351862306a36Sopenharmony_ci{ 351962306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 352062306a36Sopenharmony_ci struct brcmf_bss_info_le *bi = NULL; 352162306a36Sopenharmony_ci s32 err = 0; 352262306a36Sopenharmony_ci 352362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 352462306a36Sopenharmony_ci if (brcmf_is_ibssmode(ifp->vif)) 352562306a36Sopenharmony_ci return err; 352662306a36Sopenharmony_ci 352762306a36Sopenharmony_ci *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX); 352862306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, 352962306a36Sopenharmony_ci cfg->extra_buf, WL_EXTRA_BUF_MAX); 353062306a36Sopenharmony_ci if (err) { 353162306a36Sopenharmony_ci bphy_err(drvr, "Could not get bss info %d\n", err); 353262306a36Sopenharmony_ci goto update_bss_info_out; 353362306a36Sopenharmony_ci } 353462306a36Sopenharmony_ci bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4); 353562306a36Sopenharmony_ci err = brcmf_inform_single_bss(cfg, bi); 353662306a36Sopenharmony_ci 353762306a36Sopenharmony_ciupdate_bss_info_out: 353862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit"); 353962306a36Sopenharmony_ci return err; 354062306a36Sopenharmony_ci} 354162306a36Sopenharmony_ci 354262306a36Sopenharmony_civoid brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg) 354362306a36Sopenharmony_ci{ 354462306a36Sopenharmony_ci struct escan_info *escan = &cfg->escan_info; 354562306a36Sopenharmony_ci 354662306a36Sopenharmony_ci set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); 354762306a36Sopenharmony_ci if (cfg->int_escan_map || cfg->scan_request) { 354862306a36Sopenharmony_ci escan->escan_state = WL_ESCAN_STATE_IDLE; 354962306a36Sopenharmony_ci brcmf_notify_escan_complete(cfg, escan->ifp, true, true); 355062306a36Sopenharmony_ci } 355162306a36Sopenharmony_ci clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); 355262306a36Sopenharmony_ci clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status); 355362306a36Sopenharmony_ci} 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_cistatic void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work) 355662306a36Sopenharmony_ci{ 355762306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = 355862306a36Sopenharmony_ci container_of(work, struct brcmf_cfg80211_info, 355962306a36Sopenharmony_ci escan_timeout_work); 356062306a36Sopenharmony_ci 356162306a36Sopenharmony_ci brcmf_inform_bss(cfg); 356262306a36Sopenharmony_ci brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true); 356362306a36Sopenharmony_ci} 356462306a36Sopenharmony_ci 356562306a36Sopenharmony_cistatic void brcmf_escan_timeout(struct timer_list *t) 356662306a36Sopenharmony_ci{ 356762306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = 356862306a36Sopenharmony_ci from_timer(cfg, t, escan_timeout); 356962306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_ci if (cfg->int_escan_map || cfg->scan_request) { 357262306a36Sopenharmony_ci bphy_err(drvr, "timer expired\n"); 357362306a36Sopenharmony_ci schedule_work(&cfg->escan_timeout_work); 357462306a36Sopenharmony_ci } 357562306a36Sopenharmony_ci} 357662306a36Sopenharmony_ci 357762306a36Sopenharmony_cistatic s32 357862306a36Sopenharmony_cibrcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg, 357962306a36Sopenharmony_ci struct brcmf_bss_info_le *bss, 358062306a36Sopenharmony_ci struct brcmf_bss_info_le *bss_info_le) 358162306a36Sopenharmony_ci{ 358262306a36Sopenharmony_ci struct brcmu_chan ch_bss, ch_bss_info_le; 358362306a36Sopenharmony_ci 358462306a36Sopenharmony_ci ch_bss.chspec = le16_to_cpu(bss->chanspec); 358562306a36Sopenharmony_ci cfg->d11inf.decchspec(&ch_bss); 358662306a36Sopenharmony_ci ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec); 358762306a36Sopenharmony_ci cfg->d11inf.decchspec(&ch_bss_info_le); 358862306a36Sopenharmony_ci 358962306a36Sopenharmony_ci if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) && 359062306a36Sopenharmony_ci ch_bss.band == ch_bss_info_le.band && 359162306a36Sopenharmony_ci bss_info_le->SSID_len == bss->SSID_len && 359262306a36Sopenharmony_ci !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) { 359362306a36Sopenharmony_ci if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 359462306a36Sopenharmony_ci (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) { 359562306a36Sopenharmony_ci s16 bss_rssi = le16_to_cpu(bss->RSSI); 359662306a36Sopenharmony_ci s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI); 359762306a36Sopenharmony_ci 359862306a36Sopenharmony_ci /* preserve max RSSI if the measurements are 359962306a36Sopenharmony_ci * both on-channel or both off-channel 360062306a36Sopenharmony_ci */ 360162306a36Sopenharmony_ci if (bss_info_rssi > bss_rssi) 360262306a36Sopenharmony_ci bss->RSSI = bss_info_le->RSSI; 360362306a36Sopenharmony_ci } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) && 360462306a36Sopenharmony_ci (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) { 360562306a36Sopenharmony_ci /* preserve the on-channel rssi measurement 360662306a36Sopenharmony_ci * if the new measurement is off channel 360762306a36Sopenharmony_ci */ 360862306a36Sopenharmony_ci bss->RSSI = bss_info_le->RSSI; 360962306a36Sopenharmony_ci bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL; 361062306a36Sopenharmony_ci } 361162306a36Sopenharmony_ci return 1; 361262306a36Sopenharmony_ci } 361362306a36Sopenharmony_ci return 0; 361462306a36Sopenharmony_ci} 361562306a36Sopenharmony_ci 361662306a36Sopenharmony_cistatic s32 361762306a36Sopenharmony_cibrcmf_cfg80211_escan_handler(struct brcmf_if *ifp, 361862306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 361962306a36Sopenharmony_ci{ 362062306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 362162306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = drvr->config; 362262306a36Sopenharmony_ci s32 status; 362362306a36Sopenharmony_ci struct brcmf_escan_result_le *escan_result_le; 362462306a36Sopenharmony_ci u32 escan_buflen; 362562306a36Sopenharmony_ci struct brcmf_bss_info_le *bss_info_le; 362662306a36Sopenharmony_ci struct brcmf_bss_info_le *bss = NULL; 362762306a36Sopenharmony_ci u32 bi_length; 362862306a36Sopenharmony_ci struct brcmf_scan_results *list; 362962306a36Sopenharmony_ci u32 i; 363062306a36Sopenharmony_ci bool aborted; 363162306a36Sopenharmony_ci 363262306a36Sopenharmony_ci status = e->status; 363362306a36Sopenharmony_ci 363462306a36Sopenharmony_ci if (status == BRCMF_E_STATUS_ABORT) 363562306a36Sopenharmony_ci goto exit; 363662306a36Sopenharmony_ci 363762306a36Sopenharmony_ci if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { 363862306a36Sopenharmony_ci bphy_err(drvr, "scan not ready, bsscfgidx=%d\n", 363962306a36Sopenharmony_ci ifp->bsscfgidx); 364062306a36Sopenharmony_ci return -EPERM; 364162306a36Sopenharmony_ci } 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci if (status == BRCMF_E_STATUS_PARTIAL) { 364462306a36Sopenharmony_ci brcmf_dbg(SCAN, "ESCAN Partial result\n"); 364562306a36Sopenharmony_ci if (e->datalen < sizeof(*escan_result_le)) { 364662306a36Sopenharmony_ci bphy_err(drvr, "invalid event data length\n"); 364762306a36Sopenharmony_ci goto exit; 364862306a36Sopenharmony_ci } 364962306a36Sopenharmony_ci escan_result_le = (struct brcmf_escan_result_le *) data; 365062306a36Sopenharmony_ci if (!escan_result_le) { 365162306a36Sopenharmony_ci bphy_err(drvr, "Invalid escan result (NULL pointer)\n"); 365262306a36Sopenharmony_ci goto exit; 365362306a36Sopenharmony_ci } 365462306a36Sopenharmony_ci escan_buflen = le32_to_cpu(escan_result_le->buflen); 365562306a36Sopenharmony_ci if (escan_buflen > BRCMF_ESCAN_BUF_SIZE || 365662306a36Sopenharmony_ci escan_buflen > e->datalen || 365762306a36Sopenharmony_ci escan_buflen < sizeof(*escan_result_le)) { 365862306a36Sopenharmony_ci bphy_err(drvr, "Invalid escan buffer length: %d\n", 365962306a36Sopenharmony_ci escan_buflen); 366062306a36Sopenharmony_ci goto exit; 366162306a36Sopenharmony_ci } 366262306a36Sopenharmony_ci if (le16_to_cpu(escan_result_le->bss_count) != 1) { 366362306a36Sopenharmony_ci bphy_err(drvr, "Invalid bss_count %d: ignoring\n", 366462306a36Sopenharmony_ci escan_result_le->bss_count); 366562306a36Sopenharmony_ci goto exit; 366662306a36Sopenharmony_ci } 366762306a36Sopenharmony_ci bss_info_le = &escan_result_le->bss_info_le; 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le)) 367062306a36Sopenharmony_ci goto exit; 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ci if (!cfg->int_escan_map && !cfg->scan_request) { 367362306a36Sopenharmony_ci brcmf_dbg(SCAN, "result without cfg80211 request\n"); 367462306a36Sopenharmony_ci goto exit; 367562306a36Sopenharmony_ci } 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci bi_length = le32_to_cpu(bss_info_le->length); 367862306a36Sopenharmony_ci if (bi_length != escan_buflen - WL_ESCAN_RESULTS_FIXED_SIZE) { 367962306a36Sopenharmony_ci bphy_err(drvr, "Ignoring invalid bss_info length: %d\n", 368062306a36Sopenharmony_ci bi_length); 368162306a36Sopenharmony_ci goto exit; 368262306a36Sopenharmony_ci } 368362306a36Sopenharmony_ci 368462306a36Sopenharmony_ci if (!(cfg_to_wiphy(cfg)->interface_modes & 368562306a36Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC))) { 368662306a36Sopenharmony_ci if (le16_to_cpu(bss_info_le->capability) & 368762306a36Sopenharmony_ci WLAN_CAPABILITY_IBSS) { 368862306a36Sopenharmony_ci bphy_err(drvr, "Ignoring IBSS result\n"); 368962306a36Sopenharmony_ci goto exit; 369062306a36Sopenharmony_ci } 369162306a36Sopenharmony_ci } 369262306a36Sopenharmony_ci 369362306a36Sopenharmony_ci list = (struct brcmf_scan_results *) 369462306a36Sopenharmony_ci cfg->escan_info.escan_buf; 369562306a36Sopenharmony_ci if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) { 369662306a36Sopenharmony_ci bphy_err(drvr, "Buffer is too small: ignoring\n"); 369762306a36Sopenharmony_ci goto exit; 369862306a36Sopenharmony_ci } 369962306a36Sopenharmony_ci 370062306a36Sopenharmony_ci for (i = 0; i < list->count; i++) { 370162306a36Sopenharmony_ci bss = bss ? (struct brcmf_bss_info_le *) 370262306a36Sopenharmony_ci ((unsigned char *)bss + 370362306a36Sopenharmony_ci le32_to_cpu(bss->length)) : list->bss_info_le; 370462306a36Sopenharmony_ci if (brcmf_compare_update_same_bss(cfg, bss, 370562306a36Sopenharmony_ci bss_info_le)) 370662306a36Sopenharmony_ci goto exit; 370762306a36Sopenharmony_ci } 370862306a36Sopenharmony_ci memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le, 370962306a36Sopenharmony_ci bi_length); 371062306a36Sopenharmony_ci list->version = le32_to_cpu(bss_info_le->version); 371162306a36Sopenharmony_ci list->buflen += bi_length; 371262306a36Sopenharmony_ci list->count++; 371362306a36Sopenharmony_ci } else { 371462306a36Sopenharmony_ci cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; 371562306a36Sopenharmony_ci if (brcmf_p2p_scan_finding_common_channel(cfg, NULL)) 371662306a36Sopenharmony_ci goto exit; 371762306a36Sopenharmony_ci if (cfg->int_escan_map || cfg->scan_request) { 371862306a36Sopenharmony_ci brcmf_inform_bss(cfg); 371962306a36Sopenharmony_ci aborted = status != BRCMF_E_STATUS_SUCCESS; 372062306a36Sopenharmony_ci brcmf_notify_escan_complete(cfg, ifp, aborted, false); 372162306a36Sopenharmony_ci } else 372262306a36Sopenharmony_ci brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n", 372362306a36Sopenharmony_ci status); 372462306a36Sopenharmony_ci } 372562306a36Sopenharmony_ciexit: 372662306a36Sopenharmony_ci return 0; 372762306a36Sopenharmony_ci} 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_cistatic void brcmf_init_escan(struct brcmf_cfg80211_info *cfg) 373062306a36Sopenharmony_ci{ 373162306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT, 373262306a36Sopenharmony_ci brcmf_cfg80211_escan_handler); 373362306a36Sopenharmony_ci cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE; 373462306a36Sopenharmony_ci /* Init scan_timeout timer */ 373562306a36Sopenharmony_ci timer_setup(&cfg->escan_timeout, brcmf_escan_timeout, 0); 373662306a36Sopenharmony_ci INIT_WORK(&cfg->escan_timeout_work, 373762306a36Sopenharmony_ci brcmf_cfg80211_escan_timeout_worker); 373862306a36Sopenharmony_ci} 373962306a36Sopenharmony_ci 374062306a36Sopenharmony_cistatic struct cfg80211_scan_request * 374162306a36Sopenharmony_cibrcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) { 374262306a36Sopenharmony_ci struct cfg80211_scan_request *req; 374362306a36Sopenharmony_ci size_t req_size; 374462306a36Sopenharmony_ci 374562306a36Sopenharmony_ci req_size = sizeof(*req) + 374662306a36Sopenharmony_ci n_netinfo * sizeof(req->channels[0]) + 374762306a36Sopenharmony_ci n_netinfo * sizeof(*req->ssids); 374862306a36Sopenharmony_ci 374962306a36Sopenharmony_ci req = kzalloc(req_size, GFP_KERNEL); 375062306a36Sopenharmony_ci if (req) { 375162306a36Sopenharmony_ci req->wiphy = wiphy; 375262306a36Sopenharmony_ci req->ssids = (void *)(&req->channels[0]) + 375362306a36Sopenharmony_ci n_netinfo * sizeof(req->channels[0]); 375462306a36Sopenharmony_ci } 375562306a36Sopenharmony_ci return req; 375662306a36Sopenharmony_ci} 375762306a36Sopenharmony_ci 375862306a36Sopenharmony_cistatic int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req, 375962306a36Sopenharmony_ci u8 *ssid, u8 ssid_len, u8 channel) 376062306a36Sopenharmony_ci{ 376162306a36Sopenharmony_ci struct ieee80211_channel *chan; 376262306a36Sopenharmony_ci enum nl80211_band band; 376362306a36Sopenharmony_ci int freq, i; 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci if (channel <= CH_MAX_2G_CHANNEL) 376662306a36Sopenharmony_ci band = NL80211_BAND_2GHZ; 376762306a36Sopenharmony_ci else 376862306a36Sopenharmony_ci band = NL80211_BAND_5GHZ; 376962306a36Sopenharmony_ci 377062306a36Sopenharmony_ci freq = ieee80211_channel_to_frequency(channel, band); 377162306a36Sopenharmony_ci if (!freq) 377262306a36Sopenharmony_ci return -EINVAL; 377362306a36Sopenharmony_ci 377462306a36Sopenharmony_ci chan = ieee80211_get_channel(req->wiphy, freq); 377562306a36Sopenharmony_ci if (!chan) 377662306a36Sopenharmony_ci return -EINVAL; 377762306a36Sopenharmony_ci 377862306a36Sopenharmony_ci for (i = 0; i < req->n_channels; i++) { 377962306a36Sopenharmony_ci if (req->channels[i] == chan) 378062306a36Sopenharmony_ci break; 378162306a36Sopenharmony_ci } 378262306a36Sopenharmony_ci if (i == req->n_channels) { 378362306a36Sopenharmony_ci req->n_channels++; 378462306a36Sopenharmony_ci req->channels[i] = chan; 378562306a36Sopenharmony_ci } 378662306a36Sopenharmony_ci 378762306a36Sopenharmony_ci for (i = 0; i < req->n_ssids; i++) { 378862306a36Sopenharmony_ci if (req->ssids[i].ssid_len == ssid_len && 378962306a36Sopenharmony_ci !memcmp(req->ssids[i].ssid, ssid, ssid_len)) 379062306a36Sopenharmony_ci break; 379162306a36Sopenharmony_ci } 379262306a36Sopenharmony_ci if (i == req->n_ssids) { 379362306a36Sopenharmony_ci memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len); 379462306a36Sopenharmony_ci req->ssids[req->n_ssids++].ssid_len = ssid_len; 379562306a36Sopenharmony_ci } 379662306a36Sopenharmony_ci return 0; 379762306a36Sopenharmony_ci} 379862306a36Sopenharmony_ci 379962306a36Sopenharmony_cistatic int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap, 380062306a36Sopenharmony_ci struct cfg80211_scan_request *request) 380162306a36Sopenharmony_ci{ 380262306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = ifp->drvr->config; 380362306a36Sopenharmony_ci int err; 380462306a36Sopenharmony_ci 380562306a36Sopenharmony_ci if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) { 380662306a36Sopenharmony_ci if (cfg->int_escan_map) 380762306a36Sopenharmony_ci brcmf_dbg(SCAN, "aborting internal scan: map=%u\n", 380862306a36Sopenharmony_ci cfg->int_escan_map); 380962306a36Sopenharmony_ci /* Abort any on-going scan */ 381062306a36Sopenharmony_ci brcmf_abort_scanning(cfg); 381162306a36Sopenharmony_ci } 381262306a36Sopenharmony_ci 381362306a36Sopenharmony_ci brcmf_dbg(SCAN, "start internal scan: map=%u\n", fwmap); 381462306a36Sopenharmony_ci set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); 381562306a36Sopenharmony_ci cfg->escan_info.run = brcmf_run_escan; 381662306a36Sopenharmony_ci err = brcmf_do_escan(ifp, request); 381762306a36Sopenharmony_ci if (err) { 381862306a36Sopenharmony_ci clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status); 381962306a36Sopenharmony_ci return err; 382062306a36Sopenharmony_ci } 382162306a36Sopenharmony_ci cfg->int_escan_map = fwmap; 382262306a36Sopenharmony_ci return 0; 382362306a36Sopenharmony_ci} 382462306a36Sopenharmony_ci 382562306a36Sopenharmony_cistatic struct brcmf_pno_net_info_le * 382662306a36Sopenharmony_cibrcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1) 382762306a36Sopenharmony_ci{ 382862306a36Sopenharmony_ci struct brcmf_pno_scanresults_v2_le *pfn_v2; 382962306a36Sopenharmony_ci struct brcmf_pno_net_info_le *netinfo; 383062306a36Sopenharmony_ci 383162306a36Sopenharmony_ci switch (pfn_v1->version) { 383262306a36Sopenharmony_ci default: 383362306a36Sopenharmony_ci WARN_ON(1); 383462306a36Sopenharmony_ci fallthrough; 383562306a36Sopenharmony_ci case cpu_to_le32(1): 383662306a36Sopenharmony_ci netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1); 383762306a36Sopenharmony_ci break; 383862306a36Sopenharmony_ci case cpu_to_le32(2): 383962306a36Sopenharmony_ci pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1; 384062306a36Sopenharmony_ci netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1); 384162306a36Sopenharmony_ci break; 384262306a36Sopenharmony_ci } 384362306a36Sopenharmony_ci 384462306a36Sopenharmony_ci return netinfo; 384562306a36Sopenharmony_ci} 384662306a36Sopenharmony_ci 384762306a36Sopenharmony_ci/* PFN result doesn't have all the info which are required by the supplicant 384862306a36Sopenharmony_ci * (For e.g IEs) Do a target Escan so that sched scan results are reported 384962306a36Sopenharmony_ci * via wl_inform_single_bss in the required format. Escan does require the 385062306a36Sopenharmony_ci * scan request in the form of cfg80211_scan_request. For timebeing, create 385162306a36Sopenharmony_ci * cfg80211_scan_request one out of the received PNO event. 385262306a36Sopenharmony_ci */ 385362306a36Sopenharmony_cistatic s32 385462306a36Sopenharmony_cibrcmf_notify_sched_scan_results(struct brcmf_if *ifp, 385562306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 385662306a36Sopenharmony_ci{ 385762306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 385862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = drvr->config; 385962306a36Sopenharmony_ci struct brcmf_pno_net_info_le *netinfo, *netinfo_start; 386062306a36Sopenharmony_ci struct cfg80211_scan_request *request = NULL; 386162306a36Sopenharmony_ci struct wiphy *wiphy = cfg_to_wiphy(cfg); 386262306a36Sopenharmony_ci int i, err = 0; 386362306a36Sopenharmony_ci struct brcmf_pno_scanresults_le *pfn_result; 386462306a36Sopenharmony_ci u32 bucket_map; 386562306a36Sopenharmony_ci u32 result_count; 386662306a36Sopenharmony_ci u32 status; 386762306a36Sopenharmony_ci u32 datalen; 386862306a36Sopenharmony_ci 386962306a36Sopenharmony_ci brcmf_dbg(SCAN, "Enter\n"); 387062306a36Sopenharmony_ci 387162306a36Sopenharmony_ci if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) { 387262306a36Sopenharmony_ci brcmf_dbg(SCAN, "Event data to small. Ignore\n"); 387362306a36Sopenharmony_ci return 0; 387462306a36Sopenharmony_ci } 387562306a36Sopenharmony_ci 387662306a36Sopenharmony_ci if (e->event_code == BRCMF_E_PFN_NET_LOST) { 387762306a36Sopenharmony_ci brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n"); 387862306a36Sopenharmony_ci return 0; 387962306a36Sopenharmony_ci } 388062306a36Sopenharmony_ci 388162306a36Sopenharmony_ci pfn_result = (struct brcmf_pno_scanresults_le *)data; 388262306a36Sopenharmony_ci result_count = le32_to_cpu(pfn_result->count); 388362306a36Sopenharmony_ci status = le32_to_cpu(pfn_result->status); 388462306a36Sopenharmony_ci 388562306a36Sopenharmony_ci /* PFN event is limited to fit 512 bytes so we may get 388662306a36Sopenharmony_ci * multiple NET_FOUND events. For now place a warning here. 388762306a36Sopenharmony_ci */ 388862306a36Sopenharmony_ci WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE); 388962306a36Sopenharmony_ci brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count); 389062306a36Sopenharmony_ci if (!result_count) { 389162306a36Sopenharmony_ci bphy_err(drvr, "FALSE PNO Event. (pfn_count == 0)\n"); 389262306a36Sopenharmony_ci goto out_err; 389362306a36Sopenharmony_ci } 389462306a36Sopenharmony_ci 389562306a36Sopenharmony_ci netinfo_start = brcmf_get_netinfo_array(pfn_result); 389662306a36Sopenharmony_ci datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result); 389762306a36Sopenharmony_ci if (datalen < result_count * sizeof(*netinfo)) { 389862306a36Sopenharmony_ci bphy_err(drvr, "insufficient event data\n"); 389962306a36Sopenharmony_ci goto out_err; 390062306a36Sopenharmony_ci } 390162306a36Sopenharmony_ci 390262306a36Sopenharmony_ci request = brcmf_alloc_internal_escan_request(wiphy, 390362306a36Sopenharmony_ci result_count); 390462306a36Sopenharmony_ci if (!request) { 390562306a36Sopenharmony_ci err = -ENOMEM; 390662306a36Sopenharmony_ci goto out_err; 390762306a36Sopenharmony_ci } 390862306a36Sopenharmony_ci 390962306a36Sopenharmony_ci bucket_map = 0; 391062306a36Sopenharmony_ci for (i = 0; i < result_count; i++) { 391162306a36Sopenharmony_ci netinfo = &netinfo_start[i]; 391262306a36Sopenharmony_ci 391362306a36Sopenharmony_ci if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN) 391462306a36Sopenharmony_ci netinfo->SSID_len = IEEE80211_MAX_SSID_LEN; 391562306a36Sopenharmony_ci brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n", 391662306a36Sopenharmony_ci netinfo->SSID, netinfo->channel); 391762306a36Sopenharmony_ci bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo); 391862306a36Sopenharmony_ci err = brcmf_internal_escan_add_info(request, 391962306a36Sopenharmony_ci netinfo->SSID, 392062306a36Sopenharmony_ci netinfo->SSID_len, 392162306a36Sopenharmony_ci netinfo->channel); 392262306a36Sopenharmony_ci if (err) 392362306a36Sopenharmony_ci goto out_err; 392462306a36Sopenharmony_ci } 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci if (!bucket_map) 392762306a36Sopenharmony_ci goto free_req; 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci err = brcmf_start_internal_escan(ifp, bucket_map, request); 393062306a36Sopenharmony_ci if (!err) 393162306a36Sopenharmony_ci goto free_req; 393262306a36Sopenharmony_ci 393362306a36Sopenharmony_ciout_err: 393462306a36Sopenharmony_ci cfg80211_sched_scan_stopped(wiphy, 0); 393562306a36Sopenharmony_cifree_req: 393662306a36Sopenharmony_ci kfree(request); 393762306a36Sopenharmony_ci return err; 393862306a36Sopenharmony_ci} 393962306a36Sopenharmony_ci 394062306a36Sopenharmony_cistatic int 394162306a36Sopenharmony_cibrcmf_cfg80211_sched_scan_start(struct wiphy *wiphy, 394262306a36Sopenharmony_ci struct net_device *ndev, 394362306a36Sopenharmony_ci struct cfg80211_sched_scan_request *req) 394462306a36Sopenharmony_ci{ 394562306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 394662306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 394762306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 394862306a36Sopenharmony_ci 394962306a36Sopenharmony_ci brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n", 395062306a36Sopenharmony_ci req->n_match_sets, req->n_ssids); 395162306a36Sopenharmony_ci 395262306a36Sopenharmony_ci if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) { 395362306a36Sopenharmony_ci bphy_err(drvr, "Scanning suppressed: status=%lu\n", 395462306a36Sopenharmony_ci cfg->scan_status); 395562306a36Sopenharmony_ci return -EAGAIN; 395662306a36Sopenharmony_ci } 395762306a36Sopenharmony_ci 395862306a36Sopenharmony_ci if (req->n_match_sets <= 0) { 395962306a36Sopenharmony_ci brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n", 396062306a36Sopenharmony_ci req->n_match_sets); 396162306a36Sopenharmony_ci return -EINVAL; 396262306a36Sopenharmony_ci } 396362306a36Sopenharmony_ci 396462306a36Sopenharmony_ci return brcmf_pno_start_sched_scan(ifp, req); 396562306a36Sopenharmony_ci} 396662306a36Sopenharmony_ci 396762306a36Sopenharmony_cistatic int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy, 396862306a36Sopenharmony_ci struct net_device *ndev, u64 reqid) 396962306a36Sopenharmony_ci{ 397062306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 397162306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 397262306a36Sopenharmony_ci 397362306a36Sopenharmony_ci brcmf_dbg(SCAN, "enter\n"); 397462306a36Sopenharmony_ci brcmf_pno_stop_sched_scan(ifp, reqid); 397562306a36Sopenharmony_ci if (cfg->int_escan_map) 397662306a36Sopenharmony_ci brcmf_notify_escan_complete(cfg, ifp, true, true); 397762306a36Sopenharmony_ci return 0; 397862306a36Sopenharmony_ci} 397962306a36Sopenharmony_ci 398062306a36Sopenharmony_cistatic __always_inline void brcmf_delay(u32 ms) 398162306a36Sopenharmony_ci{ 398262306a36Sopenharmony_ci if (ms < 1000 / HZ) { 398362306a36Sopenharmony_ci cond_resched(); 398462306a36Sopenharmony_ci mdelay(ms); 398562306a36Sopenharmony_ci } else { 398662306a36Sopenharmony_ci msleep(ms); 398762306a36Sopenharmony_ci } 398862306a36Sopenharmony_ci} 398962306a36Sopenharmony_ci 399062306a36Sopenharmony_cistatic s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4], 399162306a36Sopenharmony_ci u8 *pattern, u32 patternsize, u8 *mask, 399262306a36Sopenharmony_ci u32 packet_offset) 399362306a36Sopenharmony_ci{ 399462306a36Sopenharmony_ci struct brcmf_fil_wowl_pattern_le *filter; 399562306a36Sopenharmony_ci u32 masksize; 399662306a36Sopenharmony_ci u32 patternoffset; 399762306a36Sopenharmony_ci u8 *buf; 399862306a36Sopenharmony_ci u32 bufsize; 399962306a36Sopenharmony_ci s32 ret; 400062306a36Sopenharmony_ci 400162306a36Sopenharmony_ci masksize = (patternsize + 7) / 8; 400262306a36Sopenharmony_ci patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize; 400362306a36Sopenharmony_ci 400462306a36Sopenharmony_ci bufsize = sizeof(*filter) + patternsize + masksize; 400562306a36Sopenharmony_ci buf = kzalloc(bufsize, GFP_KERNEL); 400662306a36Sopenharmony_ci if (!buf) 400762306a36Sopenharmony_ci return -ENOMEM; 400862306a36Sopenharmony_ci filter = (struct brcmf_fil_wowl_pattern_le *)buf; 400962306a36Sopenharmony_ci 401062306a36Sopenharmony_ci memcpy(filter->cmd, cmd, 4); 401162306a36Sopenharmony_ci filter->masksize = cpu_to_le32(masksize); 401262306a36Sopenharmony_ci filter->offset = cpu_to_le32(packet_offset); 401362306a36Sopenharmony_ci filter->patternoffset = cpu_to_le32(patternoffset); 401462306a36Sopenharmony_ci filter->patternsize = cpu_to_le32(patternsize); 401562306a36Sopenharmony_ci filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP); 401662306a36Sopenharmony_ci 401762306a36Sopenharmony_ci if ((mask) && (masksize)) 401862306a36Sopenharmony_ci memcpy(buf + sizeof(*filter), mask, masksize); 401962306a36Sopenharmony_ci if ((pattern) && (patternsize)) 402062306a36Sopenharmony_ci memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize); 402162306a36Sopenharmony_ci 402262306a36Sopenharmony_ci ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize); 402362306a36Sopenharmony_ci 402462306a36Sopenharmony_ci kfree(buf); 402562306a36Sopenharmony_ci return ret; 402662306a36Sopenharmony_ci} 402762306a36Sopenharmony_ci 402862306a36Sopenharmony_cistatic s32 402962306a36Sopenharmony_cibrcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e, 403062306a36Sopenharmony_ci void *data) 403162306a36Sopenharmony_ci{ 403262306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 403362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = drvr->config; 403462306a36Sopenharmony_ci struct brcmf_pno_scanresults_le *pfn_result; 403562306a36Sopenharmony_ci struct brcmf_pno_net_info_le *netinfo; 403662306a36Sopenharmony_ci 403762306a36Sopenharmony_ci brcmf_dbg(SCAN, "Enter\n"); 403862306a36Sopenharmony_ci 403962306a36Sopenharmony_ci if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) { 404062306a36Sopenharmony_ci brcmf_dbg(SCAN, "Event data to small. Ignore\n"); 404162306a36Sopenharmony_ci return 0; 404262306a36Sopenharmony_ci } 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ci pfn_result = (struct brcmf_pno_scanresults_le *)data; 404562306a36Sopenharmony_ci 404662306a36Sopenharmony_ci if (e->event_code == BRCMF_E_PFN_NET_LOST) { 404762306a36Sopenharmony_ci brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n"); 404862306a36Sopenharmony_ci return 0; 404962306a36Sopenharmony_ci } 405062306a36Sopenharmony_ci 405162306a36Sopenharmony_ci if (le32_to_cpu(pfn_result->count) < 1) { 405262306a36Sopenharmony_ci bphy_err(drvr, "Invalid result count, expected 1 (%d)\n", 405362306a36Sopenharmony_ci le32_to_cpu(pfn_result->count)); 405462306a36Sopenharmony_ci return -EINVAL; 405562306a36Sopenharmony_ci } 405662306a36Sopenharmony_ci 405762306a36Sopenharmony_ci netinfo = brcmf_get_netinfo_array(pfn_result); 405862306a36Sopenharmony_ci if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN) 405962306a36Sopenharmony_ci netinfo->SSID_len = IEEE80211_MAX_SSID_LEN; 406062306a36Sopenharmony_ci memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len); 406162306a36Sopenharmony_ci cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len; 406262306a36Sopenharmony_ci cfg->wowl.nd->n_channels = 1; 406362306a36Sopenharmony_ci cfg->wowl.nd->channels[0] = 406462306a36Sopenharmony_ci ieee80211_channel_to_frequency(netinfo->channel, 406562306a36Sopenharmony_ci netinfo->channel <= CH_MAX_2G_CHANNEL ? 406662306a36Sopenharmony_ci NL80211_BAND_2GHZ : NL80211_BAND_5GHZ); 406762306a36Sopenharmony_ci cfg->wowl.nd_info->n_matches = 1; 406862306a36Sopenharmony_ci cfg->wowl.nd_info->matches[0] = cfg->wowl.nd; 406962306a36Sopenharmony_ci 407062306a36Sopenharmony_ci /* Inform (the resume task) that the net detect information was recvd */ 407162306a36Sopenharmony_ci cfg->wowl.nd_data_completed = true; 407262306a36Sopenharmony_ci wake_up(&cfg->wowl.nd_data_wait); 407362306a36Sopenharmony_ci 407462306a36Sopenharmony_ci return 0; 407562306a36Sopenharmony_ci} 407662306a36Sopenharmony_ci 407762306a36Sopenharmony_ci#ifdef CONFIG_PM 407862306a36Sopenharmony_ci 407962306a36Sopenharmony_cistatic void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) 408062306a36Sopenharmony_ci{ 408162306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 408262306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 408362306a36Sopenharmony_ci struct brcmf_wowl_wakeind_le wake_ind_le; 408462306a36Sopenharmony_ci struct cfg80211_wowlan_wakeup wakeup_data; 408562306a36Sopenharmony_ci struct cfg80211_wowlan_wakeup *wakeup; 408662306a36Sopenharmony_ci u32 wakeind; 408762306a36Sopenharmony_ci s32 err; 408862306a36Sopenharmony_ci int timeout; 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le, 409162306a36Sopenharmony_ci sizeof(wake_ind_le)); 409262306a36Sopenharmony_ci if (err) { 409362306a36Sopenharmony_ci bphy_err(drvr, "Get wowl_wakeind failed, err = %d\n", err); 409462306a36Sopenharmony_ci return; 409562306a36Sopenharmony_ci } 409662306a36Sopenharmony_ci 409762306a36Sopenharmony_ci wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind); 409862306a36Sopenharmony_ci if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | 409962306a36Sopenharmony_ci BRCMF_WOWL_RETR | BRCMF_WOWL_NET | 410062306a36Sopenharmony_ci BRCMF_WOWL_PFN_FOUND)) { 410162306a36Sopenharmony_ci wakeup = &wakeup_data; 410262306a36Sopenharmony_ci memset(&wakeup_data, 0, sizeof(wakeup_data)); 410362306a36Sopenharmony_ci wakeup_data.pattern_idx = -1; 410462306a36Sopenharmony_ci 410562306a36Sopenharmony_ci if (wakeind & BRCMF_WOWL_MAGIC) { 410662306a36Sopenharmony_ci brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n"); 410762306a36Sopenharmony_ci wakeup_data.magic_pkt = true; 410862306a36Sopenharmony_ci } 410962306a36Sopenharmony_ci if (wakeind & BRCMF_WOWL_DIS) { 411062306a36Sopenharmony_ci brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n"); 411162306a36Sopenharmony_ci wakeup_data.disconnect = true; 411262306a36Sopenharmony_ci } 411362306a36Sopenharmony_ci if (wakeind & BRCMF_WOWL_BCN) { 411462306a36Sopenharmony_ci brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n"); 411562306a36Sopenharmony_ci wakeup_data.disconnect = true; 411662306a36Sopenharmony_ci } 411762306a36Sopenharmony_ci if (wakeind & BRCMF_WOWL_RETR) { 411862306a36Sopenharmony_ci brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n"); 411962306a36Sopenharmony_ci wakeup_data.disconnect = true; 412062306a36Sopenharmony_ci } 412162306a36Sopenharmony_ci if (wakeind & BRCMF_WOWL_NET) { 412262306a36Sopenharmony_ci brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n"); 412362306a36Sopenharmony_ci /* For now always map to pattern 0, no API to get 412462306a36Sopenharmony_ci * correct information available at the moment. 412562306a36Sopenharmony_ci */ 412662306a36Sopenharmony_ci wakeup_data.pattern_idx = 0; 412762306a36Sopenharmony_ci } 412862306a36Sopenharmony_ci if (wakeind & BRCMF_WOWL_PFN_FOUND) { 412962306a36Sopenharmony_ci brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n"); 413062306a36Sopenharmony_ci timeout = wait_event_timeout(cfg->wowl.nd_data_wait, 413162306a36Sopenharmony_ci cfg->wowl.nd_data_completed, 413262306a36Sopenharmony_ci BRCMF_ND_INFO_TIMEOUT); 413362306a36Sopenharmony_ci if (!timeout) 413462306a36Sopenharmony_ci bphy_err(drvr, "No result for wowl net detect\n"); 413562306a36Sopenharmony_ci else 413662306a36Sopenharmony_ci wakeup_data.net_detect = cfg->wowl.nd_info; 413762306a36Sopenharmony_ci } 413862306a36Sopenharmony_ci if (wakeind & BRCMF_WOWL_GTK_FAILURE) { 413962306a36Sopenharmony_ci brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n"); 414062306a36Sopenharmony_ci wakeup_data.gtk_rekey_failure = true; 414162306a36Sopenharmony_ci } 414262306a36Sopenharmony_ci } else { 414362306a36Sopenharmony_ci wakeup = NULL; 414462306a36Sopenharmony_ci } 414562306a36Sopenharmony_ci cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL); 414662306a36Sopenharmony_ci} 414762306a36Sopenharmony_ci 414862306a36Sopenharmony_ci#else 414962306a36Sopenharmony_ci 415062306a36Sopenharmony_cistatic void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp) 415162306a36Sopenharmony_ci{ 415262306a36Sopenharmony_ci} 415362306a36Sopenharmony_ci 415462306a36Sopenharmony_ci#endif /* CONFIG_PM */ 415562306a36Sopenharmony_ci 415662306a36Sopenharmony_cistatic s32 brcmf_cfg80211_resume(struct wiphy *wiphy) 415762306a36Sopenharmony_ci{ 415862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 415962306a36Sopenharmony_ci struct net_device *ndev = cfg_to_ndev(cfg); 416062306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 416162306a36Sopenharmony_ci 416262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 416362306a36Sopenharmony_ci 416462306a36Sopenharmony_ci if (cfg->wowl.active) { 416562306a36Sopenharmony_ci brcmf_report_wowl_wakeind(wiphy, ifp); 416662306a36Sopenharmony_ci brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0); 416762306a36Sopenharmony_ci brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0); 416862306a36Sopenharmony_ci if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) 416962306a36Sopenharmony_ci brcmf_configure_arp_nd_offload(ifp, true); 417062306a36Sopenharmony_ci brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, 417162306a36Sopenharmony_ci cfg->wowl.pre_pmmode); 417262306a36Sopenharmony_ci cfg->wowl.active = false; 417362306a36Sopenharmony_ci if (cfg->wowl.nd_enabled) { 417462306a36Sopenharmony_ci brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0); 417562306a36Sopenharmony_ci brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND); 417662306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, 417762306a36Sopenharmony_ci brcmf_notify_sched_scan_results); 417862306a36Sopenharmony_ci cfg->wowl.nd_enabled = false; 417962306a36Sopenharmony_ci } 418062306a36Sopenharmony_ci } 418162306a36Sopenharmony_ci return 0; 418262306a36Sopenharmony_ci} 418362306a36Sopenharmony_ci 418462306a36Sopenharmony_cistatic void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg, 418562306a36Sopenharmony_ci struct brcmf_if *ifp, 418662306a36Sopenharmony_ci struct cfg80211_wowlan *wowl) 418762306a36Sopenharmony_ci{ 418862306a36Sopenharmony_ci u32 wowl_config; 418962306a36Sopenharmony_ci struct brcmf_wowl_wakeind_le wowl_wakeind; 419062306a36Sopenharmony_ci u32 i; 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Suspend, wowl config.\n"); 419362306a36Sopenharmony_ci 419462306a36Sopenharmony_ci if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND)) 419562306a36Sopenharmony_ci brcmf_configure_arp_nd_offload(ifp, false); 419662306a36Sopenharmony_ci brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode); 419762306a36Sopenharmony_ci brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX); 419862306a36Sopenharmony_ci 419962306a36Sopenharmony_ci wowl_config = 0; 420062306a36Sopenharmony_ci if (wowl->disconnect) 420162306a36Sopenharmony_ci wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR; 420262306a36Sopenharmony_ci if (wowl->magic_pkt) 420362306a36Sopenharmony_ci wowl_config |= BRCMF_WOWL_MAGIC; 420462306a36Sopenharmony_ci if ((wowl->patterns) && (wowl->n_patterns)) { 420562306a36Sopenharmony_ci wowl_config |= BRCMF_WOWL_NET; 420662306a36Sopenharmony_ci for (i = 0; i < wowl->n_patterns; i++) { 420762306a36Sopenharmony_ci brcmf_config_wowl_pattern(ifp, "add", 420862306a36Sopenharmony_ci (u8 *)wowl->patterns[i].pattern, 420962306a36Sopenharmony_ci wowl->patterns[i].pattern_len, 421062306a36Sopenharmony_ci (u8 *)wowl->patterns[i].mask, 421162306a36Sopenharmony_ci wowl->patterns[i].pkt_offset); 421262306a36Sopenharmony_ci } 421362306a36Sopenharmony_ci } 421462306a36Sopenharmony_ci if (wowl->nd_config) { 421562306a36Sopenharmony_ci brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev, 421662306a36Sopenharmony_ci wowl->nd_config); 421762306a36Sopenharmony_ci wowl_config |= BRCMF_WOWL_PFN_FOUND; 421862306a36Sopenharmony_ci 421962306a36Sopenharmony_ci cfg->wowl.nd_data_completed = false; 422062306a36Sopenharmony_ci cfg->wowl.nd_enabled = true; 422162306a36Sopenharmony_ci /* Now reroute the event for PFN to the wowl function. */ 422262306a36Sopenharmony_ci brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND); 422362306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, 422462306a36Sopenharmony_ci brcmf_wowl_nd_results); 422562306a36Sopenharmony_ci } 422662306a36Sopenharmony_ci if (wowl->gtk_rekey_failure) 422762306a36Sopenharmony_ci wowl_config |= BRCMF_WOWL_GTK_FAILURE; 422862306a36Sopenharmony_ci if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state)) 422962306a36Sopenharmony_ci wowl_config |= BRCMF_WOWL_UNASSOC; 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci memcpy(&wowl_wakeind, "clear", 6); 423262306a36Sopenharmony_ci brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", &wowl_wakeind, 423362306a36Sopenharmony_ci sizeof(wowl_wakeind)); 423462306a36Sopenharmony_ci brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config); 423562306a36Sopenharmony_ci brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1); 423662306a36Sopenharmony_ci brcmf_bus_wowl_config(cfg->pub->bus_if, true); 423762306a36Sopenharmony_ci cfg->wowl.active = true; 423862306a36Sopenharmony_ci} 423962306a36Sopenharmony_ci 424062306a36Sopenharmony_cistatic int brcmf_keepalive_start(struct brcmf_if *ifp, unsigned int interval) 424162306a36Sopenharmony_ci{ 424262306a36Sopenharmony_ci struct brcmf_mkeep_alive_pkt_le kalive = {0}; 424362306a36Sopenharmony_ci int ret = 0; 424462306a36Sopenharmony_ci 424562306a36Sopenharmony_ci /* Configure Null function/data keepalive */ 424662306a36Sopenharmony_ci kalive.version = cpu_to_le16(1); 424762306a36Sopenharmony_ci kalive.period_msec = cpu_to_le32(interval * MSEC_PER_SEC); 424862306a36Sopenharmony_ci kalive.len_bytes = cpu_to_le16(0); 424962306a36Sopenharmony_ci kalive.keep_alive_id = 0; 425062306a36Sopenharmony_ci 425162306a36Sopenharmony_ci ret = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", &kalive, sizeof(kalive)); 425262306a36Sopenharmony_ci if (ret) 425362306a36Sopenharmony_ci brcmf_err("keep-alive packet config failed, ret=%d\n", ret); 425462306a36Sopenharmony_ci 425562306a36Sopenharmony_ci return ret; 425662306a36Sopenharmony_ci} 425762306a36Sopenharmony_ci 425862306a36Sopenharmony_cistatic s32 brcmf_cfg80211_suspend(struct wiphy *wiphy, 425962306a36Sopenharmony_ci struct cfg80211_wowlan *wowl) 426062306a36Sopenharmony_ci{ 426162306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 426262306a36Sopenharmony_ci struct net_device *ndev = cfg_to_ndev(cfg); 426362306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 426462306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 426562306a36Sopenharmony_ci 426662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 426762306a36Sopenharmony_ci 426862306a36Sopenharmony_ci /* if the primary net_device is not READY there is nothing 426962306a36Sopenharmony_ci * we can do but pray resume goes smoothly. 427062306a36Sopenharmony_ci */ 427162306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 427262306a36Sopenharmony_ci goto exit; 427362306a36Sopenharmony_ci 427462306a36Sopenharmony_ci /* Stop scheduled scan */ 427562306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) 427662306a36Sopenharmony_ci brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0); 427762306a36Sopenharmony_ci 427862306a36Sopenharmony_ci /* end any scanning */ 427962306a36Sopenharmony_ci if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) 428062306a36Sopenharmony_ci brcmf_abort_scanning(cfg); 428162306a36Sopenharmony_ci 428262306a36Sopenharmony_ci if (wowl == NULL) { 428362306a36Sopenharmony_ci brcmf_bus_wowl_config(cfg->pub->bus_if, false); 428462306a36Sopenharmony_ci list_for_each_entry(vif, &cfg->vif_list, list) { 428562306a36Sopenharmony_ci if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) 428662306a36Sopenharmony_ci continue; 428762306a36Sopenharmony_ci /* While going to suspend if associated with AP 428862306a36Sopenharmony_ci * disassociate from AP to save power while system is 428962306a36Sopenharmony_ci * in suspended state 429062306a36Sopenharmony_ci */ 429162306a36Sopenharmony_ci brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED, true); 429262306a36Sopenharmony_ci /* Make sure WPA_Supplicant receives all the event 429362306a36Sopenharmony_ci * generated due to DISASSOC call to the fw to keep 429462306a36Sopenharmony_ci * the state fw and WPA_Supplicant state consistent 429562306a36Sopenharmony_ci */ 429662306a36Sopenharmony_ci brcmf_delay(500); 429762306a36Sopenharmony_ci } 429862306a36Sopenharmony_ci /* Configure MPC */ 429962306a36Sopenharmony_ci brcmf_set_mpc(ifp, 1); 430062306a36Sopenharmony_ci 430162306a36Sopenharmony_ci } else { 430262306a36Sopenharmony_ci /* Configure WOWL paramaters */ 430362306a36Sopenharmony_ci brcmf_configure_wowl(cfg, ifp, wowl); 430462306a36Sopenharmony_ci 430562306a36Sopenharmony_ci /* Prevent disassociation due to inactivity with keep-alive */ 430662306a36Sopenharmony_ci brcmf_keepalive_start(ifp, 30); 430762306a36Sopenharmony_ci } 430862306a36Sopenharmony_ci 430962306a36Sopenharmony_ciexit: 431062306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 431162306a36Sopenharmony_ci /* clear any scanning activity */ 431262306a36Sopenharmony_ci cfg->scan_status = 0; 431362306a36Sopenharmony_ci return 0; 431462306a36Sopenharmony_ci} 431562306a36Sopenharmony_ci 431662306a36Sopenharmony_cistatic s32 431762306a36Sopenharmony_cibrcmf_pmksa_v3_op(struct brcmf_if *ifp, struct cfg80211_pmksa *pmksa, 431862306a36Sopenharmony_ci bool alive) 431962306a36Sopenharmony_ci{ 432062306a36Sopenharmony_ci struct brcmf_pmk_op_v3_le *pmk_op; 432162306a36Sopenharmony_ci int length = offsetof(struct brcmf_pmk_op_v3_le, pmk); 432262306a36Sopenharmony_ci int ret; 432362306a36Sopenharmony_ci 432462306a36Sopenharmony_ci pmk_op = kzalloc(sizeof(*pmk_op), GFP_KERNEL); 432562306a36Sopenharmony_ci if (!pmk_op) 432662306a36Sopenharmony_ci return -ENOMEM; 432762306a36Sopenharmony_ci 432862306a36Sopenharmony_ci pmk_op->version = cpu_to_le16(BRCMF_PMKSA_VER_3); 432962306a36Sopenharmony_ci 433062306a36Sopenharmony_ci if (!pmksa) { 433162306a36Sopenharmony_ci /* Flush operation, operate on entire list */ 433262306a36Sopenharmony_ci pmk_op->count = cpu_to_le16(0); 433362306a36Sopenharmony_ci } else { 433462306a36Sopenharmony_ci /* Single PMK operation */ 433562306a36Sopenharmony_ci pmk_op->count = cpu_to_le16(1); 433662306a36Sopenharmony_ci length += sizeof(struct brcmf_pmksa_v3); 433762306a36Sopenharmony_ci memcpy(pmk_op->pmk[0].bssid, pmksa->bssid, ETH_ALEN); 433862306a36Sopenharmony_ci memcpy(pmk_op->pmk[0].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); 433962306a36Sopenharmony_ci pmk_op->pmk[0].pmkid_len = WLAN_PMKID_LEN; 434062306a36Sopenharmony_ci pmk_op->pmk[0].time_left = cpu_to_le32(alive ? BRCMF_PMKSA_NO_EXPIRY : 0); 434162306a36Sopenharmony_ci } 434262306a36Sopenharmony_ci 434362306a36Sopenharmony_ci pmk_op->length = cpu_to_le16(length); 434462306a36Sopenharmony_ci 434562306a36Sopenharmony_ci ret = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_op, sizeof(*pmk_op)); 434662306a36Sopenharmony_ci kfree(pmk_op); 434762306a36Sopenharmony_ci return ret; 434862306a36Sopenharmony_ci} 434962306a36Sopenharmony_ci 435062306a36Sopenharmony_cistatic __used s32 435162306a36Sopenharmony_cibrcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp) 435262306a36Sopenharmony_ci{ 435362306a36Sopenharmony_ci struct brcmf_pmk_list_le *pmk_list; 435462306a36Sopenharmony_ci int i; 435562306a36Sopenharmony_ci u32 npmk; 435662306a36Sopenharmony_ci 435762306a36Sopenharmony_ci pmk_list = &cfg->pmk_list; 435862306a36Sopenharmony_ci npmk = le32_to_cpu(pmk_list->npmk); 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci brcmf_dbg(CONN, "No of elements %d\n", npmk); 436162306a36Sopenharmony_ci for (i = 0; i < npmk; i++) 436262306a36Sopenharmony_ci brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid); 436362306a36Sopenharmony_ci 436462306a36Sopenharmony_ci return brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list, 436562306a36Sopenharmony_ci sizeof(*pmk_list)); 436662306a36Sopenharmony_ci} 436762306a36Sopenharmony_ci 436862306a36Sopenharmony_cistatic s32 436962306a36Sopenharmony_cibrcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev, 437062306a36Sopenharmony_ci struct cfg80211_pmksa *pmksa) 437162306a36Sopenharmony_ci{ 437262306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 437362306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 437462306a36Sopenharmony_ci struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0]; 437562306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 437662306a36Sopenharmony_ci s32 err; 437762306a36Sopenharmony_ci u32 npmk, i; 437862306a36Sopenharmony_ci 437962306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 438062306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 438162306a36Sopenharmony_ci return -EIO; 438262306a36Sopenharmony_ci 438362306a36Sopenharmony_ci brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmksa->bssid); 438462306a36Sopenharmony_ci brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmksa->pmkid); 438562306a36Sopenharmony_ci 438662306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3)) 438762306a36Sopenharmony_ci return brcmf_pmksa_v3_op(ifp, pmksa, true); 438862306a36Sopenharmony_ci 438962306a36Sopenharmony_ci /* TODO: implement PMKID_V2 */ 439062306a36Sopenharmony_ci 439162306a36Sopenharmony_ci npmk = le32_to_cpu(cfg->pmk_list.npmk); 439262306a36Sopenharmony_ci for (i = 0; i < npmk; i++) 439362306a36Sopenharmony_ci if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN)) 439462306a36Sopenharmony_ci break; 439562306a36Sopenharmony_ci if (i < BRCMF_MAXPMKID) { 439662306a36Sopenharmony_ci memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN); 439762306a36Sopenharmony_ci memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN); 439862306a36Sopenharmony_ci if (i == npmk) { 439962306a36Sopenharmony_ci npmk++; 440062306a36Sopenharmony_ci cfg->pmk_list.npmk = cpu_to_le32(npmk); 440162306a36Sopenharmony_ci } 440262306a36Sopenharmony_ci } else { 440362306a36Sopenharmony_ci bphy_err(drvr, "Too many PMKSA entries cached %d\n", npmk); 440462306a36Sopenharmony_ci return -EINVAL; 440562306a36Sopenharmony_ci } 440662306a36Sopenharmony_ci 440762306a36Sopenharmony_ci err = brcmf_update_pmklist(cfg, ifp); 440862306a36Sopenharmony_ci 440962306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 441062306a36Sopenharmony_ci return err; 441162306a36Sopenharmony_ci} 441262306a36Sopenharmony_ci 441362306a36Sopenharmony_cistatic s32 441462306a36Sopenharmony_cibrcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev, 441562306a36Sopenharmony_ci struct cfg80211_pmksa *pmksa) 441662306a36Sopenharmony_ci{ 441762306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 441862306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 441962306a36Sopenharmony_ci struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0]; 442062306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 442162306a36Sopenharmony_ci s32 err; 442262306a36Sopenharmony_ci u32 npmk, i; 442362306a36Sopenharmony_ci 442462306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 442562306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 442662306a36Sopenharmony_ci return -EIO; 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_ci brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid); 442962306a36Sopenharmony_ci 443062306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3)) 443162306a36Sopenharmony_ci return brcmf_pmksa_v3_op(ifp, pmksa, false); 443262306a36Sopenharmony_ci 443362306a36Sopenharmony_ci /* TODO: implement PMKID_V2 */ 443462306a36Sopenharmony_ci 443562306a36Sopenharmony_ci npmk = le32_to_cpu(cfg->pmk_list.npmk); 443662306a36Sopenharmony_ci for (i = 0; i < npmk; i++) 443762306a36Sopenharmony_ci if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN)) 443862306a36Sopenharmony_ci break; 443962306a36Sopenharmony_ci 444062306a36Sopenharmony_ci if ((npmk > 0) && (i < npmk)) { 444162306a36Sopenharmony_ci for (; i < (npmk - 1); i++) { 444262306a36Sopenharmony_ci memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN); 444362306a36Sopenharmony_ci memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid, 444462306a36Sopenharmony_ci WLAN_PMKID_LEN); 444562306a36Sopenharmony_ci } 444662306a36Sopenharmony_ci memset(&pmk[i], 0, sizeof(*pmk)); 444762306a36Sopenharmony_ci cfg->pmk_list.npmk = cpu_to_le32(npmk - 1); 444862306a36Sopenharmony_ci } else { 444962306a36Sopenharmony_ci bphy_err(drvr, "Cache entry not found\n"); 445062306a36Sopenharmony_ci return -EINVAL; 445162306a36Sopenharmony_ci } 445262306a36Sopenharmony_ci 445362306a36Sopenharmony_ci err = brcmf_update_pmklist(cfg, ifp); 445462306a36Sopenharmony_ci 445562306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 445662306a36Sopenharmony_ci return err; 445762306a36Sopenharmony_ci 445862306a36Sopenharmony_ci} 445962306a36Sopenharmony_ci 446062306a36Sopenharmony_cistatic s32 446162306a36Sopenharmony_cibrcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev) 446262306a36Sopenharmony_ci{ 446362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 446462306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 446562306a36Sopenharmony_ci s32 err; 446662306a36Sopenharmony_ci 446762306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 446862306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 446962306a36Sopenharmony_ci return -EIO; 447062306a36Sopenharmony_ci 447162306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3)) 447262306a36Sopenharmony_ci return brcmf_pmksa_v3_op(ifp, NULL, false); 447362306a36Sopenharmony_ci 447462306a36Sopenharmony_ci /* TODO: implement PMKID_V2 */ 447562306a36Sopenharmony_ci 447662306a36Sopenharmony_ci memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list)); 447762306a36Sopenharmony_ci err = brcmf_update_pmklist(cfg, ifp); 447862306a36Sopenharmony_ci 447962306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 448062306a36Sopenharmony_ci return err; 448162306a36Sopenharmony_ci 448262306a36Sopenharmony_ci} 448362306a36Sopenharmony_ci 448462306a36Sopenharmony_cistatic s32 brcmf_configure_opensecurity(struct brcmf_if *ifp) 448562306a36Sopenharmony_ci{ 448662306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 448762306a36Sopenharmony_ci s32 err; 448862306a36Sopenharmony_ci s32 wpa_val; 448962306a36Sopenharmony_ci 449062306a36Sopenharmony_ci /* set auth */ 449162306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0); 449262306a36Sopenharmony_ci if (err < 0) { 449362306a36Sopenharmony_ci bphy_err(drvr, "auth error %d\n", err); 449462306a36Sopenharmony_ci return err; 449562306a36Sopenharmony_ci } 449662306a36Sopenharmony_ci /* set wsec */ 449762306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0); 449862306a36Sopenharmony_ci if (err < 0) { 449962306a36Sopenharmony_ci bphy_err(drvr, "wsec error %d\n", err); 450062306a36Sopenharmony_ci return err; 450162306a36Sopenharmony_ci } 450262306a36Sopenharmony_ci /* set upper-layer auth */ 450362306a36Sopenharmony_ci if (brcmf_is_ibssmode(ifp->vif)) 450462306a36Sopenharmony_ci wpa_val = WPA_AUTH_NONE; 450562306a36Sopenharmony_ci else 450662306a36Sopenharmony_ci wpa_val = WPA_AUTH_DISABLED; 450762306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_val); 450862306a36Sopenharmony_ci if (err < 0) { 450962306a36Sopenharmony_ci bphy_err(drvr, "wpa_auth error %d\n", err); 451062306a36Sopenharmony_ci return err; 451162306a36Sopenharmony_ci } 451262306a36Sopenharmony_ci 451362306a36Sopenharmony_ci return 0; 451462306a36Sopenharmony_ci} 451562306a36Sopenharmony_ci 451662306a36Sopenharmony_cistatic bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie) 451762306a36Sopenharmony_ci{ 451862306a36Sopenharmony_ci if (is_rsn_ie) 451962306a36Sopenharmony_ci return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0); 452062306a36Sopenharmony_ci 452162306a36Sopenharmony_ci return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0); 452262306a36Sopenharmony_ci} 452362306a36Sopenharmony_ci 452462306a36Sopenharmony_cistatic s32 452562306a36Sopenharmony_cibrcmf_configure_wpaie(struct brcmf_if *ifp, 452662306a36Sopenharmony_ci const struct brcmf_vs_tlv *wpa_ie, 452762306a36Sopenharmony_ci bool is_rsn_ie) 452862306a36Sopenharmony_ci{ 452962306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 453062306a36Sopenharmony_ci u32 auth = 0; /* d11 open authentication */ 453162306a36Sopenharmony_ci u16 count; 453262306a36Sopenharmony_ci s32 err = 0; 453362306a36Sopenharmony_ci s32 len; 453462306a36Sopenharmony_ci u32 i; 453562306a36Sopenharmony_ci u32 wsec; 453662306a36Sopenharmony_ci u32 pval = 0; 453762306a36Sopenharmony_ci u32 gval = 0; 453862306a36Sopenharmony_ci u32 wpa_auth = 0; 453962306a36Sopenharmony_ci u32 offset; 454062306a36Sopenharmony_ci u8 *data; 454162306a36Sopenharmony_ci u16 rsn_cap; 454262306a36Sopenharmony_ci u32 wme_bss_disable; 454362306a36Sopenharmony_ci u32 mfp; 454462306a36Sopenharmony_ci 454562306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 454662306a36Sopenharmony_ci if (wpa_ie == NULL) 454762306a36Sopenharmony_ci goto exit; 454862306a36Sopenharmony_ci 454962306a36Sopenharmony_ci len = wpa_ie->len + TLV_HDR_LEN; 455062306a36Sopenharmony_ci data = (u8 *)wpa_ie; 455162306a36Sopenharmony_ci offset = TLV_HDR_LEN; 455262306a36Sopenharmony_ci if (!is_rsn_ie) 455362306a36Sopenharmony_ci offset += VS_IE_FIXED_HDR_LEN; 455462306a36Sopenharmony_ci else 455562306a36Sopenharmony_ci offset += WPA_IE_VERSION_LEN; 455662306a36Sopenharmony_ci 455762306a36Sopenharmony_ci /* check for multicast cipher suite */ 455862306a36Sopenharmony_ci if (offset + WPA_IE_MIN_OUI_LEN > len) { 455962306a36Sopenharmony_ci err = -EINVAL; 456062306a36Sopenharmony_ci bphy_err(drvr, "no multicast cipher suite\n"); 456162306a36Sopenharmony_ci goto exit; 456262306a36Sopenharmony_ci } 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ci if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { 456562306a36Sopenharmony_ci err = -EINVAL; 456662306a36Sopenharmony_ci bphy_err(drvr, "ivalid OUI\n"); 456762306a36Sopenharmony_ci goto exit; 456862306a36Sopenharmony_ci } 456962306a36Sopenharmony_ci offset += TLV_OUI_LEN; 457062306a36Sopenharmony_ci 457162306a36Sopenharmony_ci /* pick up multicast cipher */ 457262306a36Sopenharmony_ci switch (data[offset]) { 457362306a36Sopenharmony_ci case WPA_CIPHER_NONE: 457462306a36Sopenharmony_ci gval = 0; 457562306a36Sopenharmony_ci break; 457662306a36Sopenharmony_ci case WPA_CIPHER_WEP_40: 457762306a36Sopenharmony_ci case WPA_CIPHER_WEP_104: 457862306a36Sopenharmony_ci gval = WEP_ENABLED; 457962306a36Sopenharmony_ci break; 458062306a36Sopenharmony_ci case WPA_CIPHER_TKIP: 458162306a36Sopenharmony_ci gval = TKIP_ENABLED; 458262306a36Sopenharmony_ci break; 458362306a36Sopenharmony_ci case WPA_CIPHER_AES_CCM: 458462306a36Sopenharmony_ci gval = AES_ENABLED; 458562306a36Sopenharmony_ci break; 458662306a36Sopenharmony_ci default: 458762306a36Sopenharmony_ci err = -EINVAL; 458862306a36Sopenharmony_ci bphy_err(drvr, "Invalid multi cast cipher info\n"); 458962306a36Sopenharmony_ci goto exit; 459062306a36Sopenharmony_ci } 459162306a36Sopenharmony_ci 459262306a36Sopenharmony_ci offset++; 459362306a36Sopenharmony_ci /* walk thru unicast cipher list and pick up what we recognize */ 459462306a36Sopenharmony_ci count = data[offset] + (data[offset + 1] << 8); 459562306a36Sopenharmony_ci offset += WPA_IE_SUITE_COUNT_LEN; 459662306a36Sopenharmony_ci /* Check for unicast suite(s) */ 459762306a36Sopenharmony_ci if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { 459862306a36Sopenharmony_ci err = -EINVAL; 459962306a36Sopenharmony_ci bphy_err(drvr, "no unicast cipher suite\n"); 460062306a36Sopenharmony_ci goto exit; 460162306a36Sopenharmony_ci } 460262306a36Sopenharmony_ci for (i = 0; i < count; i++) { 460362306a36Sopenharmony_ci if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { 460462306a36Sopenharmony_ci err = -EINVAL; 460562306a36Sopenharmony_ci bphy_err(drvr, "ivalid OUI\n"); 460662306a36Sopenharmony_ci goto exit; 460762306a36Sopenharmony_ci } 460862306a36Sopenharmony_ci offset += TLV_OUI_LEN; 460962306a36Sopenharmony_ci switch (data[offset]) { 461062306a36Sopenharmony_ci case WPA_CIPHER_NONE: 461162306a36Sopenharmony_ci break; 461262306a36Sopenharmony_ci case WPA_CIPHER_WEP_40: 461362306a36Sopenharmony_ci case WPA_CIPHER_WEP_104: 461462306a36Sopenharmony_ci pval |= WEP_ENABLED; 461562306a36Sopenharmony_ci break; 461662306a36Sopenharmony_ci case WPA_CIPHER_TKIP: 461762306a36Sopenharmony_ci pval |= TKIP_ENABLED; 461862306a36Sopenharmony_ci break; 461962306a36Sopenharmony_ci case WPA_CIPHER_AES_CCM: 462062306a36Sopenharmony_ci pval |= AES_ENABLED; 462162306a36Sopenharmony_ci break; 462262306a36Sopenharmony_ci default: 462362306a36Sopenharmony_ci bphy_err(drvr, "Invalid unicast security info\n"); 462462306a36Sopenharmony_ci } 462562306a36Sopenharmony_ci offset++; 462662306a36Sopenharmony_ci } 462762306a36Sopenharmony_ci /* walk thru auth management suite list and pick up what we recognize */ 462862306a36Sopenharmony_ci count = data[offset] + (data[offset + 1] << 8); 462962306a36Sopenharmony_ci offset += WPA_IE_SUITE_COUNT_LEN; 463062306a36Sopenharmony_ci /* Check for auth key management suite(s) */ 463162306a36Sopenharmony_ci if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) { 463262306a36Sopenharmony_ci err = -EINVAL; 463362306a36Sopenharmony_ci bphy_err(drvr, "no auth key mgmt suite\n"); 463462306a36Sopenharmony_ci goto exit; 463562306a36Sopenharmony_ci } 463662306a36Sopenharmony_ci for (i = 0; i < count; i++) { 463762306a36Sopenharmony_ci if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) { 463862306a36Sopenharmony_ci err = -EINVAL; 463962306a36Sopenharmony_ci bphy_err(drvr, "ivalid OUI\n"); 464062306a36Sopenharmony_ci goto exit; 464162306a36Sopenharmony_ci } 464262306a36Sopenharmony_ci offset += TLV_OUI_LEN; 464362306a36Sopenharmony_ci switch (data[offset]) { 464462306a36Sopenharmony_ci case RSN_AKM_NONE: 464562306a36Sopenharmony_ci brcmf_dbg(TRACE, "RSN_AKM_NONE\n"); 464662306a36Sopenharmony_ci wpa_auth |= WPA_AUTH_NONE; 464762306a36Sopenharmony_ci break; 464862306a36Sopenharmony_ci case RSN_AKM_UNSPECIFIED: 464962306a36Sopenharmony_ci brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n"); 465062306a36Sopenharmony_ci is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) : 465162306a36Sopenharmony_ci (wpa_auth |= WPA_AUTH_UNSPECIFIED); 465262306a36Sopenharmony_ci break; 465362306a36Sopenharmony_ci case RSN_AKM_PSK: 465462306a36Sopenharmony_ci brcmf_dbg(TRACE, "RSN_AKM_PSK\n"); 465562306a36Sopenharmony_ci is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) : 465662306a36Sopenharmony_ci (wpa_auth |= WPA_AUTH_PSK); 465762306a36Sopenharmony_ci break; 465862306a36Sopenharmony_ci case RSN_AKM_SHA256_PSK: 465962306a36Sopenharmony_ci brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n"); 466062306a36Sopenharmony_ci wpa_auth |= WPA2_AUTH_PSK_SHA256; 466162306a36Sopenharmony_ci break; 466262306a36Sopenharmony_ci case RSN_AKM_SHA256_1X: 466362306a36Sopenharmony_ci brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n"); 466462306a36Sopenharmony_ci wpa_auth |= WPA2_AUTH_1X_SHA256; 466562306a36Sopenharmony_ci break; 466662306a36Sopenharmony_ci case RSN_AKM_SAE: 466762306a36Sopenharmony_ci brcmf_dbg(TRACE, "RSN_AKM_SAE\n"); 466862306a36Sopenharmony_ci wpa_auth |= WPA3_AUTH_SAE_PSK; 466962306a36Sopenharmony_ci break; 467062306a36Sopenharmony_ci default: 467162306a36Sopenharmony_ci bphy_err(drvr, "Invalid key mgmt info\n"); 467262306a36Sopenharmony_ci } 467362306a36Sopenharmony_ci offset++; 467462306a36Sopenharmony_ci } 467562306a36Sopenharmony_ci 467662306a36Sopenharmony_ci mfp = BRCMF_MFP_NONE; 467762306a36Sopenharmony_ci if (is_rsn_ie) { 467862306a36Sopenharmony_ci wme_bss_disable = 1; 467962306a36Sopenharmony_ci if ((offset + RSN_CAP_LEN) <= len) { 468062306a36Sopenharmony_ci rsn_cap = data[offset] + (data[offset + 1] << 8); 468162306a36Sopenharmony_ci if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK) 468262306a36Sopenharmony_ci wme_bss_disable = 0; 468362306a36Sopenharmony_ci if (rsn_cap & RSN_CAP_MFPR_MASK) { 468462306a36Sopenharmony_ci brcmf_dbg(TRACE, "MFP Required\n"); 468562306a36Sopenharmony_ci mfp = BRCMF_MFP_REQUIRED; 468662306a36Sopenharmony_ci /* Firmware only supports mfp required in 468762306a36Sopenharmony_ci * combination with WPA2_AUTH_PSK_SHA256, 468862306a36Sopenharmony_ci * WPA2_AUTH_1X_SHA256, or WPA3_AUTH_SAE_PSK. 468962306a36Sopenharmony_ci */ 469062306a36Sopenharmony_ci if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 | 469162306a36Sopenharmony_ci WPA2_AUTH_1X_SHA256 | 469262306a36Sopenharmony_ci WPA3_AUTH_SAE_PSK))) { 469362306a36Sopenharmony_ci err = -EINVAL; 469462306a36Sopenharmony_ci goto exit; 469562306a36Sopenharmony_ci } 469662306a36Sopenharmony_ci /* Firmware has requirement that WPA2_AUTH_PSK/ 469762306a36Sopenharmony_ci * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI 469862306a36Sopenharmony_ci * is to be included in the rsn ie. 469962306a36Sopenharmony_ci */ 470062306a36Sopenharmony_ci if (wpa_auth & WPA2_AUTH_PSK_SHA256) 470162306a36Sopenharmony_ci wpa_auth |= WPA2_AUTH_PSK; 470262306a36Sopenharmony_ci else if (wpa_auth & WPA2_AUTH_1X_SHA256) 470362306a36Sopenharmony_ci wpa_auth |= WPA2_AUTH_UNSPECIFIED; 470462306a36Sopenharmony_ci } else if (rsn_cap & RSN_CAP_MFPC_MASK) { 470562306a36Sopenharmony_ci brcmf_dbg(TRACE, "MFP Capable\n"); 470662306a36Sopenharmony_ci mfp = BRCMF_MFP_CAPABLE; 470762306a36Sopenharmony_ci } 470862306a36Sopenharmony_ci } 470962306a36Sopenharmony_ci offset += RSN_CAP_LEN; 471062306a36Sopenharmony_ci /* set wme_bss_disable to sync RSN Capabilities */ 471162306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable", 471262306a36Sopenharmony_ci wme_bss_disable); 471362306a36Sopenharmony_ci if (err < 0) { 471462306a36Sopenharmony_ci bphy_err(drvr, "wme_bss_disable error %d\n", err); 471562306a36Sopenharmony_ci goto exit; 471662306a36Sopenharmony_ci } 471762306a36Sopenharmony_ci 471862306a36Sopenharmony_ci /* Skip PMKID cnt as it is know to be 0 for AP. */ 471962306a36Sopenharmony_ci offset += RSN_PMKID_COUNT_LEN; 472062306a36Sopenharmony_ci 472162306a36Sopenharmony_ci /* See if there is BIP wpa suite left for MFP */ 472262306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) && 472362306a36Sopenharmony_ci ((offset + WPA_IE_MIN_OUI_LEN) <= len)) { 472462306a36Sopenharmony_ci err = brcmf_fil_bsscfg_data_set(ifp, "bip", 472562306a36Sopenharmony_ci &data[offset], 472662306a36Sopenharmony_ci WPA_IE_MIN_OUI_LEN); 472762306a36Sopenharmony_ci if (err < 0) { 472862306a36Sopenharmony_ci bphy_err(drvr, "bip error %d\n", err); 472962306a36Sopenharmony_ci goto exit; 473062306a36Sopenharmony_ci } 473162306a36Sopenharmony_ci } 473262306a36Sopenharmony_ci } 473362306a36Sopenharmony_ci /* FOR WPS , set SES_OW_ENABLED */ 473462306a36Sopenharmony_ci wsec = (pval | gval | SES_OW_ENABLED); 473562306a36Sopenharmony_ci 473662306a36Sopenharmony_ci /* set auth */ 473762306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth); 473862306a36Sopenharmony_ci if (err < 0) { 473962306a36Sopenharmony_ci bphy_err(drvr, "auth error %d\n", err); 474062306a36Sopenharmony_ci goto exit; 474162306a36Sopenharmony_ci } 474262306a36Sopenharmony_ci /* set wsec */ 474362306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec); 474462306a36Sopenharmony_ci if (err < 0) { 474562306a36Sopenharmony_ci bphy_err(drvr, "wsec error %d\n", err); 474662306a36Sopenharmony_ci goto exit; 474762306a36Sopenharmony_ci } 474862306a36Sopenharmony_ci /* Configure MFP, this needs to go after wsec otherwise the wsec command 474962306a36Sopenharmony_ci * will overwrite the values set by MFP 475062306a36Sopenharmony_ci */ 475162306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) { 475262306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp); 475362306a36Sopenharmony_ci if (err < 0) { 475462306a36Sopenharmony_ci bphy_err(drvr, "mfp error %d\n", err); 475562306a36Sopenharmony_ci goto exit; 475662306a36Sopenharmony_ci } 475762306a36Sopenharmony_ci } 475862306a36Sopenharmony_ci /* set upper-layer auth */ 475962306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth); 476062306a36Sopenharmony_ci if (err < 0) { 476162306a36Sopenharmony_ci bphy_err(drvr, "wpa_auth error %d\n", err); 476262306a36Sopenharmony_ci goto exit; 476362306a36Sopenharmony_ci } 476462306a36Sopenharmony_ci 476562306a36Sopenharmony_ciexit: 476662306a36Sopenharmony_ci return err; 476762306a36Sopenharmony_ci} 476862306a36Sopenharmony_ci 476962306a36Sopenharmony_cistatic s32 477062306a36Sopenharmony_cibrcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len, 477162306a36Sopenharmony_ci struct parsed_vndr_ies *vndr_ies) 477262306a36Sopenharmony_ci{ 477362306a36Sopenharmony_ci struct brcmf_vs_tlv *vndrie; 477462306a36Sopenharmony_ci struct brcmf_tlv *ie; 477562306a36Sopenharmony_ci struct parsed_vndr_ie_info *parsed_info; 477662306a36Sopenharmony_ci s32 remaining_len; 477762306a36Sopenharmony_ci 477862306a36Sopenharmony_ci remaining_len = (s32)vndr_ie_len; 477962306a36Sopenharmony_ci memset(vndr_ies, 0, sizeof(*vndr_ies)); 478062306a36Sopenharmony_ci 478162306a36Sopenharmony_ci ie = (struct brcmf_tlv *)vndr_ie_buf; 478262306a36Sopenharmony_ci while (ie) { 478362306a36Sopenharmony_ci if (ie->id != WLAN_EID_VENDOR_SPECIFIC) 478462306a36Sopenharmony_ci goto next; 478562306a36Sopenharmony_ci vndrie = (struct brcmf_vs_tlv *)ie; 478662306a36Sopenharmony_ci /* len should be bigger than OUI length + one */ 478762306a36Sopenharmony_ci if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) { 478862306a36Sopenharmony_ci brcmf_err("invalid vndr ie. length is too small %d\n", 478962306a36Sopenharmony_ci vndrie->len); 479062306a36Sopenharmony_ci goto next; 479162306a36Sopenharmony_ci } 479262306a36Sopenharmony_ci /* if wpa or wme ie, do not add ie */ 479362306a36Sopenharmony_ci if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) && 479462306a36Sopenharmony_ci ((vndrie->oui_type == WPA_OUI_TYPE) || 479562306a36Sopenharmony_ci (vndrie->oui_type == WME_OUI_TYPE))) { 479662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n"); 479762306a36Sopenharmony_ci goto next; 479862306a36Sopenharmony_ci } 479962306a36Sopenharmony_ci 480062306a36Sopenharmony_ci parsed_info = &vndr_ies->ie_info[vndr_ies->count]; 480162306a36Sopenharmony_ci 480262306a36Sopenharmony_ci /* save vndr ie information */ 480362306a36Sopenharmony_ci parsed_info->ie_ptr = (char *)vndrie; 480462306a36Sopenharmony_ci parsed_info->ie_len = vndrie->len + TLV_HDR_LEN; 480562306a36Sopenharmony_ci memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie)); 480662306a36Sopenharmony_ci 480762306a36Sopenharmony_ci vndr_ies->count++; 480862306a36Sopenharmony_ci 480962306a36Sopenharmony_ci brcmf_dbg(TRACE, "** OUI %3ph, type 0x%02x\n", 481062306a36Sopenharmony_ci parsed_info->vndrie.oui, 481162306a36Sopenharmony_ci parsed_info->vndrie.oui_type); 481262306a36Sopenharmony_ci 481362306a36Sopenharmony_ci if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT) 481462306a36Sopenharmony_ci break; 481562306a36Sopenharmony_cinext: 481662306a36Sopenharmony_ci remaining_len -= (ie->len + TLV_HDR_LEN); 481762306a36Sopenharmony_ci if (remaining_len <= TLV_HDR_LEN) 481862306a36Sopenharmony_ci ie = NULL; 481962306a36Sopenharmony_ci else 482062306a36Sopenharmony_ci ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len + 482162306a36Sopenharmony_ci TLV_HDR_LEN); 482262306a36Sopenharmony_ci } 482362306a36Sopenharmony_ci return 0; 482462306a36Sopenharmony_ci} 482562306a36Sopenharmony_ci 482662306a36Sopenharmony_cistatic u32 482762306a36Sopenharmony_cibrcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd) 482862306a36Sopenharmony_ci{ 482962306a36Sopenharmony_ci strscpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN); 483062306a36Sopenharmony_ci 483162306a36Sopenharmony_ci put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]); 483262306a36Sopenharmony_ci 483362306a36Sopenharmony_ci put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]); 483462306a36Sopenharmony_ci 483562306a36Sopenharmony_ci memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len); 483662306a36Sopenharmony_ci 483762306a36Sopenharmony_ci return ie_len + VNDR_IE_HDR_SIZE; 483862306a36Sopenharmony_ci} 483962306a36Sopenharmony_ci 484062306a36Sopenharmony_cis32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag, 484162306a36Sopenharmony_ci const u8 *vndr_ie_buf, u32 vndr_ie_len) 484262306a36Sopenharmony_ci{ 484362306a36Sopenharmony_ci struct brcmf_pub *drvr; 484462306a36Sopenharmony_ci struct brcmf_if *ifp; 484562306a36Sopenharmony_ci struct vif_saved_ie *saved_ie; 484662306a36Sopenharmony_ci s32 err = 0; 484762306a36Sopenharmony_ci u8 *iovar_ie_buf; 484862306a36Sopenharmony_ci u8 *curr_ie_buf; 484962306a36Sopenharmony_ci u8 *mgmt_ie_buf = NULL; 485062306a36Sopenharmony_ci int mgmt_ie_buf_len; 485162306a36Sopenharmony_ci u32 *mgmt_ie_len; 485262306a36Sopenharmony_ci u32 del_add_ie_buf_len = 0; 485362306a36Sopenharmony_ci u32 total_ie_buf_len = 0; 485462306a36Sopenharmony_ci u32 parsed_ie_buf_len = 0; 485562306a36Sopenharmony_ci struct parsed_vndr_ies old_vndr_ies; 485662306a36Sopenharmony_ci struct parsed_vndr_ies new_vndr_ies; 485762306a36Sopenharmony_ci struct parsed_vndr_ie_info *vndrie_info; 485862306a36Sopenharmony_ci s32 i; 485962306a36Sopenharmony_ci u8 *ptr; 486062306a36Sopenharmony_ci int remained_buf_len; 486162306a36Sopenharmony_ci 486262306a36Sopenharmony_ci if (!vif) 486362306a36Sopenharmony_ci return -ENODEV; 486462306a36Sopenharmony_ci ifp = vif->ifp; 486562306a36Sopenharmony_ci drvr = ifp->drvr; 486662306a36Sopenharmony_ci saved_ie = &vif->saved_ie; 486762306a36Sopenharmony_ci 486862306a36Sopenharmony_ci brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx, 486962306a36Sopenharmony_ci pktflag); 487062306a36Sopenharmony_ci iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); 487162306a36Sopenharmony_ci if (!iovar_ie_buf) 487262306a36Sopenharmony_ci return -ENOMEM; 487362306a36Sopenharmony_ci curr_ie_buf = iovar_ie_buf; 487462306a36Sopenharmony_ci switch (pktflag) { 487562306a36Sopenharmony_ci case BRCMF_VNDR_IE_PRBREQ_FLAG: 487662306a36Sopenharmony_ci mgmt_ie_buf = saved_ie->probe_req_ie; 487762306a36Sopenharmony_ci mgmt_ie_len = &saved_ie->probe_req_ie_len; 487862306a36Sopenharmony_ci mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie); 487962306a36Sopenharmony_ci break; 488062306a36Sopenharmony_ci case BRCMF_VNDR_IE_PRBRSP_FLAG: 488162306a36Sopenharmony_ci mgmt_ie_buf = saved_ie->probe_res_ie; 488262306a36Sopenharmony_ci mgmt_ie_len = &saved_ie->probe_res_ie_len; 488362306a36Sopenharmony_ci mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie); 488462306a36Sopenharmony_ci break; 488562306a36Sopenharmony_ci case BRCMF_VNDR_IE_BEACON_FLAG: 488662306a36Sopenharmony_ci mgmt_ie_buf = saved_ie->beacon_ie; 488762306a36Sopenharmony_ci mgmt_ie_len = &saved_ie->beacon_ie_len; 488862306a36Sopenharmony_ci mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie); 488962306a36Sopenharmony_ci break; 489062306a36Sopenharmony_ci case BRCMF_VNDR_IE_ASSOCREQ_FLAG: 489162306a36Sopenharmony_ci mgmt_ie_buf = saved_ie->assoc_req_ie; 489262306a36Sopenharmony_ci mgmt_ie_len = &saved_ie->assoc_req_ie_len; 489362306a36Sopenharmony_ci mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie); 489462306a36Sopenharmony_ci break; 489562306a36Sopenharmony_ci case BRCMF_VNDR_IE_ASSOCRSP_FLAG: 489662306a36Sopenharmony_ci mgmt_ie_buf = saved_ie->assoc_res_ie; 489762306a36Sopenharmony_ci mgmt_ie_len = &saved_ie->assoc_res_ie_len; 489862306a36Sopenharmony_ci mgmt_ie_buf_len = sizeof(saved_ie->assoc_res_ie); 489962306a36Sopenharmony_ci break; 490062306a36Sopenharmony_ci default: 490162306a36Sopenharmony_ci err = -EPERM; 490262306a36Sopenharmony_ci bphy_err(drvr, "not suitable type\n"); 490362306a36Sopenharmony_ci goto exit; 490462306a36Sopenharmony_ci } 490562306a36Sopenharmony_ci 490662306a36Sopenharmony_ci if (vndr_ie_len > mgmt_ie_buf_len) { 490762306a36Sopenharmony_ci err = -ENOMEM; 490862306a36Sopenharmony_ci bphy_err(drvr, "extra IE size too big\n"); 490962306a36Sopenharmony_ci goto exit; 491062306a36Sopenharmony_ci } 491162306a36Sopenharmony_ci 491262306a36Sopenharmony_ci /* parse and save new vndr_ie in curr_ie_buff before comparing it */ 491362306a36Sopenharmony_ci if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) { 491462306a36Sopenharmony_ci ptr = curr_ie_buf; 491562306a36Sopenharmony_ci brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies); 491662306a36Sopenharmony_ci for (i = 0; i < new_vndr_ies.count; i++) { 491762306a36Sopenharmony_ci vndrie_info = &new_vndr_ies.ie_info[i]; 491862306a36Sopenharmony_ci memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr, 491962306a36Sopenharmony_ci vndrie_info->ie_len); 492062306a36Sopenharmony_ci parsed_ie_buf_len += vndrie_info->ie_len; 492162306a36Sopenharmony_ci } 492262306a36Sopenharmony_ci } 492362306a36Sopenharmony_ci 492462306a36Sopenharmony_ci if (mgmt_ie_buf && *mgmt_ie_len) { 492562306a36Sopenharmony_ci if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) && 492662306a36Sopenharmony_ci (memcmp(mgmt_ie_buf, curr_ie_buf, 492762306a36Sopenharmony_ci parsed_ie_buf_len) == 0)) { 492862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n"); 492962306a36Sopenharmony_ci goto exit; 493062306a36Sopenharmony_ci } 493162306a36Sopenharmony_ci 493262306a36Sopenharmony_ci /* parse old vndr_ie */ 493362306a36Sopenharmony_ci brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies); 493462306a36Sopenharmony_ci 493562306a36Sopenharmony_ci /* make a command to delete old ie */ 493662306a36Sopenharmony_ci for (i = 0; i < old_vndr_ies.count; i++) { 493762306a36Sopenharmony_ci vndrie_info = &old_vndr_ies.ie_info[i]; 493862306a36Sopenharmony_ci 493962306a36Sopenharmony_ci brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%3ph\n", 494062306a36Sopenharmony_ci vndrie_info->vndrie.id, 494162306a36Sopenharmony_ci vndrie_info->vndrie.len, 494262306a36Sopenharmony_ci vndrie_info->vndrie.oui); 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, 494562306a36Sopenharmony_ci vndrie_info->ie_ptr, 494662306a36Sopenharmony_ci vndrie_info->ie_len, 494762306a36Sopenharmony_ci "del"); 494862306a36Sopenharmony_ci curr_ie_buf += del_add_ie_buf_len; 494962306a36Sopenharmony_ci total_ie_buf_len += del_add_ie_buf_len; 495062306a36Sopenharmony_ci } 495162306a36Sopenharmony_ci } 495262306a36Sopenharmony_ci 495362306a36Sopenharmony_ci *mgmt_ie_len = 0; 495462306a36Sopenharmony_ci /* Add if there is any extra IE */ 495562306a36Sopenharmony_ci if (mgmt_ie_buf && parsed_ie_buf_len) { 495662306a36Sopenharmony_ci ptr = mgmt_ie_buf; 495762306a36Sopenharmony_ci 495862306a36Sopenharmony_ci remained_buf_len = mgmt_ie_buf_len; 495962306a36Sopenharmony_ci 496062306a36Sopenharmony_ci /* make a command to add new ie */ 496162306a36Sopenharmony_ci for (i = 0; i < new_vndr_ies.count; i++) { 496262306a36Sopenharmony_ci vndrie_info = &new_vndr_ies.ie_info[i]; 496362306a36Sopenharmony_ci 496462306a36Sopenharmony_ci /* verify remained buf size before copy data */ 496562306a36Sopenharmony_ci if (remained_buf_len < (vndrie_info->vndrie.len + 496662306a36Sopenharmony_ci VNDR_IE_VSIE_OFFSET)) { 496762306a36Sopenharmony_ci bphy_err(drvr, "no space in mgmt_ie_buf: len left %d", 496862306a36Sopenharmony_ci remained_buf_len); 496962306a36Sopenharmony_ci break; 497062306a36Sopenharmony_ci } 497162306a36Sopenharmony_ci remained_buf_len -= (vndrie_info->ie_len + 497262306a36Sopenharmony_ci VNDR_IE_VSIE_OFFSET); 497362306a36Sopenharmony_ci 497462306a36Sopenharmony_ci brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%3ph\n", 497562306a36Sopenharmony_ci vndrie_info->vndrie.id, 497662306a36Sopenharmony_ci vndrie_info->vndrie.len, 497762306a36Sopenharmony_ci vndrie_info->vndrie.oui); 497862306a36Sopenharmony_ci 497962306a36Sopenharmony_ci del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag, 498062306a36Sopenharmony_ci vndrie_info->ie_ptr, 498162306a36Sopenharmony_ci vndrie_info->ie_len, 498262306a36Sopenharmony_ci "add"); 498362306a36Sopenharmony_ci 498462306a36Sopenharmony_ci /* save the parsed IE in wl struct */ 498562306a36Sopenharmony_ci memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr, 498662306a36Sopenharmony_ci vndrie_info->ie_len); 498762306a36Sopenharmony_ci *mgmt_ie_len += vndrie_info->ie_len; 498862306a36Sopenharmony_ci 498962306a36Sopenharmony_ci curr_ie_buf += del_add_ie_buf_len; 499062306a36Sopenharmony_ci total_ie_buf_len += del_add_ie_buf_len; 499162306a36Sopenharmony_ci } 499262306a36Sopenharmony_ci } 499362306a36Sopenharmony_ci if (total_ie_buf_len) { 499462306a36Sopenharmony_ci err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf, 499562306a36Sopenharmony_ci total_ie_buf_len); 499662306a36Sopenharmony_ci if (err) 499762306a36Sopenharmony_ci bphy_err(drvr, "vndr ie set error : %d\n", err); 499862306a36Sopenharmony_ci } 499962306a36Sopenharmony_ci 500062306a36Sopenharmony_ciexit: 500162306a36Sopenharmony_ci kfree(iovar_ie_buf); 500262306a36Sopenharmony_ci return err; 500362306a36Sopenharmony_ci} 500462306a36Sopenharmony_ci 500562306a36Sopenharmony_cis32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif) 500662306a36Sopenharmony_ci{ 500762306a36Sopenharmony_ci static const s32 pktflags[] = { 500862306a36Sopenharmony_ci BRCMF_VNDR_IE_PRBREQ_FLAG, 500962306a36Sopenharmony_ci BRCMF_VNDR_IE_PRBRSP_FLAG, 501062306a36Sopenharmony_ci BRCMF_VNDR_IE_BEACON_FLAG 501162306a36Sopenharmony_ci }; 501262306a36Sopenharmony_ci int i; 501362306a36Sopenharmony_ci 501462306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(pktflags); i++) 501562306a36Sopenharmony_ci brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0); 501662306a36Sopenharmony_ci 501762306a36Sopenharmony_ci memset(&vif->saved_ie, 0, sizeof(vif->saved_ie)); 501862306a36Sopenharmony_ci return 0; 501962306a36Sopenharmony_ci} 502062306a36Sopenharmony_ci 502162306a36Sopenharmony_cistatic s32 502262306a36Sopenharmony_cibrcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif, 502362306a36Sopenharmony_ci struct cfg80211_beacon_data *beacon) 502462306a36Sopenharmony_ci{ 502562306a36Sopenharmony_ci struct brcmf_pub *drvr = vif->ifp->drvr; 502662306a36Sopenharmony_ci s32 err; 502762306a36Sopenharmony_ci 502862306a36Sopenharmony_ci /* Set Beacon IEs to FW */ 502962306a36Sopenharmony_ci err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG, 503062306a36Sopenharmony_ci beacon->tail, beacon->tail_len); 503162306a36Sopenharmony_ci if (err) { 503262306a36Sopenharmony_ci bphy_err(drvr, "Set Beacon IE Failed\n"); 503362306a36Sopenharmony_ci return err; 503462306a36Sopenharmony_ci } 503562306a36Sopenharmony_ci brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n"); 503662306a36Sopenharmony_ci 503762306a36Sopenharmony_ci /* Set Probe Response IEs to FW */ 503862306a36Sopenharmony_ci err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG, 503962306a36Sopenharmony_ci beacon->proberesp_ies, 504062306a36Sopenharmony_ci beacon->proberesp_ies_len); 504162306a36Sopenharmony_ci if (err) 504262306a36Sopenharmony_ci bphy_err(drvr, "Set Probe Resp IE Failed\n"); 504362306a36Sopenharmony_ci else 504462306a36Sopenharmony_ci brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n"); 504562306a36Sopenharmony_ci 504662306a36Sopenharmony_ci /* Set Assoc Response IEs to FW */ 504762306a36Sopenharmony_ci err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG, 504862306a36Sopenharmony_ci beacon->assocresp_ies, 504962306a36Sopenharmony_ci beacon->assocresp_ies_len); 505062306a36Sopenharmony_ci if (err) 505162306a36Sopenharmony_ci brcmf_err("Set Assoc Resp IE Failed\n"); 505262306a36Sopenharmony_ci else 505362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc Resp\n"); 505462306a36Sopenharmony_ci 505562306a36Sopenharmony_ci return err; 505662306a36Sopenharmony_ci} 505762306a36Sopenharmony_ci 505862306a36Sopenharmony_cistatic s32 505962306a36Sopenharmony_cibrcmf_parse_configure_security(struct brcmf_if *ifp, 506062306a36Sopenharmony_ci struct cfg80211_ap_settings *settings, 506162306a36Sopenharmony_ci enum nl80211_iftype dev_role) 506262306a36Sopenharmony_ci{ 506362306a36Sopenharmony_ci const struct brcmf_tlv *rsn_ie; 506462306a36Sopenharmony_ci const struct brcmf_vs_tlv *wpa_ie; 506562306a36Sopenharmony_ci s32 err = 0; 506662306a36Sopenharmony_ci 506762306a36Sopenharmony_ci /* find the RSN_IE */ 506862306a36Sopenharmony_ci rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, 506962306a36Sopenharmony_ci settings->beacon.tail_len, WLAN_EID_RSN); 507062306a36Sopenharmony_ci 507162306a36Sopenharmony_ci /* find the WPA_IE */ 507262306a36Sopenharmony_ci wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail, 507362306a36Sopenharmony_ci settings->beacon.tail_len); 507462306a36Sopenharmony_ci 507562306a36Sopenharmony_ci if (wpa_ie || rsn_ie) { 507662306a36Sopenharmony_ci brcmf_dbg(TRACE, "WPA(2) IE is found\n"); 507762306a36Sopenharmony_ci if (wpa_ie) { 507862306a36Sopenharmony_ci /* WPA IE */ 507962306a36Sopenharmony_ci err = brcmf_configure_wpaie(ifp, wpa_ie, false); 508062306a36Sopenharmony_ci if (err < 0) 508162306a36Sopenharmony_ci return err; 508262306a36Sopenharmony_ci } else { 508362306a36Sopenharmony_ci struct brcmf_vs_tlv *tmp_ie; 508462306a36Sopenharmony_ci 508562306a36Sopenharmony_ci tmp_ie = (struct brcmf_vs_tlv *)rsn_ie; 508662306a36Sopenharmony_ci 508762306a36Sopenharmony_ci /* RSN IE */ 508862306a36Sopenharmony_ci err = brcmf_configure_wpaie(ifp, tmp_ie, true); 508962306a36Sopenharmony_ci if (err < 0) 509062306a36Sopenharmony_ci return err; 509162306a36Sopenharmony_ci } 509262306a36Sopenharmony_ci } else { 509362306a36Sopenharmony_ci brcmf_dbg(TRACE, "No WPA(2) IEs found\n"); 509462306a36Sopenharmony_ci brcmf_configure_opensecurity(ifp); 509562306a36Sopenharmony_ci } 509662306a36Sopenharmony_ci 509762306a36Sopenharmony_ci return err; 509862306a36Sopenharmony_ci} 509962306a36Sopenharmony_ci 510062306a36Sopenharmony_cistatic s32 510162306a36Sopenharmony_cibrcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev, 510262306a36Sopenharmony_ci struct cfg80211_ap_settings *settings) 510362306a36Sopenharmony_ci{ 510462306a36Sopenharmony_ci s32 ie_offset; 510562306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 510662306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 510762306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 510862306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 510962306a36Sopenharmony_ci struct cfg80211_crypto_settings *crypto = &settings->crypto; 511062306a36Sopenharmony_ci const struct brcmf_tlv *ssid_ie; 511162306a36Sopenharmony_ci const struct brcmf_tlv *country_ie; 511262306a36Sopenharmony_ci struct brcmf_ssid_le ssid_le; 511362306a36Sopenharmony_ci s32 err = -EPERM; 511462306a36Sopenharmony_ci struct brcmf_join_params join_params; 511562306a36Sopenharmony_ci enum nl80211_iftype dev_role; 511662306a36Sopenharmony_ci struct brcmf_fil_bss_enable_le bss_enable; 511762306a36Sopenharmony_ci u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef); 511862306a36Sopenharmony_ci bool mbss; 511962306a36Sopenharmony_ci int is_11d; 512062306a36Sopenharmony_ci bool supports_11d; 512162306a36Sopenharmony_ci 512262306a36Sopenharmony_ci brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n", 512362306a36Sopenharmony_ci settings->chandef.chan->hw_value, 512462306a36Sopenharmony_ci settings->chandef.center_freq1, settings->chandef.width, 512562306a36Sopenharmony_ci settings->beacon_interval, settings->dtim_period); 512662306a36Sopenharmony_ci brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n", 512762306a36Sopenharmony_ci settings->ssid, settings->ssid_len, settings->auth_type, 512862306a36Sopenharmony_ci settings->inactivity_timeout); 512962306a36Sopenharmony_ci dev_role = ifp->vif->wdev.iftype; 513062306a36Sopenharmony_ci mbss = ifp->vif->mbss; 513162306a36Sopenharmony_ci 513262306a36Sopenharmony_ci /* store current 11d setting */ 513362306a36Sopenharmony_ci if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY, 513462306a36Sopenharmony_ci &ifp->vif->is_11d)) { 513562306a36Sopenharmony_ci is_11d = supports_11d = false; 513662306a36Sopenharmony_ci } else { 513762306a36Sopenharmony_ci country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail, 513862306a36Sopenharmony_ci settings->beacon.tail_len, 513962306a36Sopenharmony_ci WLAN_EID_COUNTRY); 514062306a36Sopenharmony_ci is_11d = country_ie ? 1 : 0; 514162306a36Sopenharmony_ci supports_11d = true; 514262306a36Sopenharmony_ci } 514362306a36Sopenharmony_ci 514462306a36Sopenharmony_ci memset(&ssid_le, 0, sizeof(ssid_le)); 514562306a36Sopenharmony_ci if (settings->ssid == NULL || settings->ssid_len == 0) { 514662306a36Sopenharmony_ci ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN; 514762306a36Sopenharmony_ci ssid_ie = brcmf_parse_tlvs( 514862306a36Sopenharmony_ci (u8 *)&settings->beacon.head[ie_offset], 514962306a36Sopenharmony_ci settings->beacon.head_len - ie_offset, 515062306a36Sopenharmony_ci WLAN_EID_SSID); 515162306a36Sopenharmony_ci if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN) 515262306a36Sopenharmony_ci return -EINVAL; 515362306a36Sopenharmony_ci 515462306a36Sopenharmony_ci memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len); 515562306a36Sopenharmony_ci ssid_le.SSID_len = cpu_to_le32(ssid_ie->len); 515662306a36Sopenharmony_ci brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID); 515762306a36Sopenharmony_ci } else { 515862306a36Sopenharmony_ci memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len); 515962306a36Sopenharmony_ci ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len); 516062306a36Sopenharmony_ci } 516162306a36Sopenharmony_ci 516262306a36Sopenharmony_ci if (!mbss) { 516362306a36Sopenharmony_ci brcmf_set_mpc(ifp, 0); 516462306a36Sopenharmony_ci brcmf_configure_arp_nd_offload(ifp, false); 516562306a36Sopenharmony_ci } 516662306a36Sopenharmony_ci 516762306a36Sopenharmony_ci /* Parameters shared by all radio interfaces */ 516862306a36Sopenharmony_ci if (!mbss) { 516962306a36Sopenharmony_ci if ((supports_11d) && (is_11d != ifp->vif->is_11d)) { 517062306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, 517162306a36Sopenharmony_ci is_11d); 517262306a36Sopenharmony_ci if (err < 0) { 517362306a36Sopenharmony_ci bphy_err(drvr, "Regulatory Set Error, %d\n", 517462306a36Sopenharmony_ci err); 517562306a36Sopenharmony_ci goto exit; 517662306a36Sopenharmony_ci } 517762306a36Sopenharmony_ci } 517862306a36Sopenharmony_ci if (settings->beacon_interval) { 517962306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, 518062306a36Sopenharmony_ci settings->beacon_interval); 518162306a36Sopenharmony_ci if (err < 0) { 518262306a36Sopenharmony_ci bphy_err(drvr, "Beacon Interval Set Error, %d\n", 518362306a36Sopenharmony_ci err); 518462306a36Sopenharmony_ci goto exit; 518562306a36Sopenharmony_ci } 518662306a36Sopenharmony_ci } 518762306a36Sopenharmony_ci if (settings->dtim_period) { 518862306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD, 518962306a36Sopenharmony_ci settings->dtim_period); 519062306a36Sopenharmony_ci if (err < 0) { 519162306a36Sopenharmony_ci bphy_err(drvr, "DTIM Interval Set Error, %d\n", 519262306a36Sopenharmony_ci err); 519362306a36Sopenharmony_ci goto exit; 519462306a36Sopenharmony_ci } 519562306a36Sopenharmony_ci } 519662306a36Sopenharmony_ci 519762306a36Sopenharmony_ci if ((dev_role == NL80211_IFTYPE_AP) && 519862306a36Sopenharmony_ci ((ifp->ifidx == 0) || 519962306a36Sopenharmony_ci (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB) && 520062306a36Sopenharmony_ci !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)))) { 520162306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); 520262306a36Sopenharmony_ci if (err < 0) { 520362306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_DOWN error %d\n", 520462306a36Sopenharmony_ci err); 520562306a36Sopenharmony_ci goto exit; 520662306a36Sopenharmony_ci } 520762306a36Sopenharmony_ci brcmf_fil_iovar_int_set(ifp, "apsta", 0); 520862306a36Sopenharmony_ci } 520962306a36Sopenharmony_ci 521062306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1); 521162306a36Sopenharmony_ci if (err < 0) { 521262306a36Sopenharmony_ci bphy_err(drvr, "SET INFRA error %d\n", err); 521362306a36Sopenharmony_ci goto exit; 521462306a36Sopenharmony_ci } 521562306a36Sopenharmony_ci } else if (WARN_ON(supports_11d && (is_11d != ifp->vif->is_11d))) { 521662306a36Sopenharmony_ci /* Multiple-BSS should use same 11d configuration */ 521762306a36Sopenharmony_ci err = -EINVAL; 521862306a36Sopenharmony_ci goto exit; 521962306a36Sopenharmony_ci } 522062306a36Sopenharmony_ci 522162306a36Sopenharmony_ci /* Interface specific setup */ 522262306a36Sopenharmony_ci if (dev_role == NL80211_IFTYPE_AP) { 522362306a36Sopenharmony_ci if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss)) 522462306a36Sopenharmony_ci brcmf_fil_iovar_int_set(ifp, "mbss", 1); 522562306a36Sopenharmony_ci 522662306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1); 522762306a36Sopenharmony_ci if (err < 0) { 522862306a36Sopenharmony_ci bphy_err(drvr, "setting AP mode failed %d\n", 522962306a36Sopenharmony_ci err); 523062306a36Sopenharmony_ci goto exit; 523162306a36Sopenharmony_ci } 523262306a36Sopenharmony_ci if (!mbss) { 523362306a36Sopenharmony_ci /* Firmware 10.x requires setting channel after enabling 523462306a36Sopenharmony_ci * AP and before bringing interface up. 523562306a36Sopenharmony_ci */ 523662306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); 523762306a36Sopenharmony_ci if (err < 0) { 523862306a36Sopenharmony_ci bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n", 523962306a36Sopenharmony_ci chanspec, err); 524062306a36Sopenharmony_ci goto exit; 524162306a36Sopenharmony_ci } 524262306a36Sopenharmony_ci } 524362306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); 524462306a36Sopenharmony_ci if (err < 0) { 524562306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err); 524662306a36Sopenharmony_ci goto exit; 524762306a36Sopenharmony_ci } 524862306a36Sopenharmony_ci 524962306a36Sopenharmony_ci if (crypto->psk) { 525062306a36Sopenharmony_ci brcmf_dbg(INFO, "using PSK offload\n"); 525162306a36Sopenharmony_ci profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_PSK); 525262306a36Sopenharmony_ci err = brcmf_set_pmk(ifp, crypto->psk, 525362306a36Sopenharmony_ci BRCMF_WSEC_MAX_PSK_LEN); 525462306a36Sopenharmony_ci if (err < 0) 525562306a36Sopenharmony_ci goto exit; 525662306a36Sopenharmony_ci } 525762306a36Sopenharmony_ci if (crypto->sae_pwd) { 525862306a36Sopenharmony_ci brcmf_dbg(INFO, "using SAE offload\n"); 525962306a36Sopenharmony_ci profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_SAE); 526062306a36Sopenharmony_ci err = brcmf_set_sae_password(ifp, crypto->sae_pwd, 526162306a36Sopenharmony_ci crypto->sae_pwd_len); 526262306a36Sopenharmony_ci if (err < 0) 526362306a36Sopenharmony_ci goto exit; 526462306a36Sopenharmony_ci } 526562306a36Sopenharmony_ci if (profile->use_fwauth == 0) 526662306a36Sopenharmony_ci profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE); 526762306a36Sopenharmony_ci 526862306a36Sopenharmony_ci err = brcmf_parse_configure_security(ifp, settings, 526962306a36Sopenharmony_ci NL80211_IFTYPE_AP); 527062306a36Sopenharmony_ci if (err < 0) { 527162306a36Sopenharmony_ci bphy_err(drvr, "brcmf_parse_configure_security error\n"); 527262306a36Sopenharmony_ci goto exit; 527362306a36Sopenharmony_ci } 527462306a36Sopenharmony_ci 527562306a36Sopenharmony_ci /* On DOWN the firmware removes the WEP keys, reconfigure 527662306a36Sopenharmony_ci * them if they were set. 527762306a36Sopenharmony_ci */ 527862306a36Sopenharmony_ci brcmf_cfg80211_reconfigure_wep(ifp); 527962306a36Sopenharmony_ci 528062306a36Sopenharmony_ci memset(&join_params, 0, sizeof(join_params)); 528162306a36Sopenharmony_ci /* join parameters starts with ssid */ 528262306a36Sopenharmony_ci memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le)); 528362306a36Sopenharmony_ci /* create softap */ 528462306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, 528562306a36Sopenharmony_ci &join_params, sizeof(join_params)); 528662306a36Sopenharmony_ci if (err < 0) { 528762306a36Sopenharmony_ci bphy_err(drvr, "SET SSID error (%d)\n", err); 528862306a36Sopenharmony_ci goto exit; 528962306a36Sopenharmony_ci } 529062306a36Sopenharmony_ci 529162306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "closednet", 529262306a36Sopenharmony_ci settings->hidden_ssid); 529362306a36Sopenharmony_ci if (err) { 529462306a36Sopenharmony_ci bphy_err(drvr, "%s closednet error (%d)\n", 529562306a36Sopenharmony_ci settings->hidden_ssid ? 529662306a36Sopenharmony_ci "enabled" : "disabled", 529762306a36Sopenharmony_ci err); 529862306a36Sopenharmony_ci goto exit; 529962306a36Sopenharmony_ci } 530062306a36Sopenharmony_ci 530162306a36Sopenharmony_ci brcmf_dbg(TRACE, "AP mode configuration complete\n"); 530262306a36Sopenharmony_ci } else if (dev_role == NL80211_IFTYPE_P2P_GO) { 530362306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec); 530462306a36Sopenharmony_ci if (err < 0) { 530562306a36Sopenharmony_ci bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n", 530662306a36Sopenharmony_ci chanspec, err); 530762306a36Sopenharmony_ci goto exit; 530862306a36Sopenharmony_ci } 530962306a36Sopenharmony_ci 531062306a36Sopenharmony_ci err = brcmf_parse_configure_security(ifp, settings, 531162306a36Sopenharmony_ci NL80211_IFTYPE_P2P_GO); 531262306a36Sopenharmony_ci if (err < 0) { 531362306a36Sopenharmony_ci brcmf_err("brcmf_parse_configure_security error\n"); 531462306a36Sopenharmony_ci goto exit; 531562306a36Sopenharmony_ci } 531662306a36Sopenharmony_ci 531762306a36Sopenharmony_ci err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le, 531862306a36Sopenharmony_ci sizeof(ssid_le)); 531962306a36Sopenharmony_ci if (err < 0) { 532062306a36Sopenharmony_ci bphy_err(drvr, "setting ssid failed %d\n", err); 532162306a36Sopenharmony_ci goto exit; 532262306a36Sopenharmony_ci } 532362306a36Sopenharmony_ci bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); 532462306a36Sopenharmony_ci bss_enable.enable = cpu_to_le32(1); 532562306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, 532662306a36Sopenharmony_ci sizeof(bss_enable)); 532762306a36Sopenharmony_ci if (err < 0) { 532862306a36Sopenharmony_ci bphy_err(drvr, "bss_enable config failed %d\n", err); 532962306a36Sopenharmony_ci goto exit; 533062306a36Sopenharmony_ci } 533162306a36Sopenharmony_ci 533262306a36Sopenharmony_ci brcmf_dbg(TRACE, "GO mode configuration complete\n"); 533362306a36Sopenharmony_ci } else { 533462306a36Sopenharmony_ci WARN_ON(1); 533562306a36Sopenharmony_ci } 533662306a36Sopenharmony_ci 533762306a36Sopenharmony_ci brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon); 533862306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); 533962306a36Sopenharmony_ci brcmf_net_setcarrier(ifp, true); 534062306a36Sopenharmony_ci 534162306a36Sopenharmony_ciexit: 534262306a36Sopenharmony_ci if ((err) && (!mbss)) { 534362306a36Sopenharmony_ci brcmf_set_mpc(ifp, 1); 534462306a36Sopenharmony_ci brcmf_configure_arp_nd_offload(ifp, true); 534562306a36Sopenharmony_ci } 534662306a36Sopenharmony_ci return err; 534762306a36Sopenharmony_ci} 534862306a36Sopenharmony_ci 534962306a36Sopenharmony_cistatic int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev, 535062306a36Sopenharmony_ci unsigned int link_id) 535162306a36Sopenharmony_ci{ 535262306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 535362306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 535462306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 535562306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 535662306a36Sopenharmony_ci s32 err; 535762306a36Sopenharmony_ci struct brcmf_fil_bss_enable_le bss_enable; 535862306a36Sopenharmony_ci struct brcmf_join_params join_params; 535962306a36Sopenharmony_ci 536062306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 536162306a36Sopenharmony_ci 536262306a36Sopenharmony_ci if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) { 536362306a36Sopenharmony_ci /* Due to most likely deauths outstanding we sleep */ 536462306a36Sopenharmony_ci /* first to make sure they get processed by fw. */ 536562306a36Sopenharmony_ci msleep(400); 536662306a36Sopenharmony_ci 536762306a36Sopenharmony_ci if (profile->use_fwauth != BIT(BRCMF_PROFILE_FWAUTH_NONE)) { 536862306a36Sopenharmony_ci if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_PSK)) 536962306a36Sopenharmony_ci brcmf_set_pmk(ifp, NULL, 0); 537062306a36Sopenharmony_ci if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_SAE)) 537162306a36Sopenharmony_ci brcmf_set_sae_password(ifp, NULL, 0); 537262306a36Sopenharmony_ci profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE); 537362306a36Sopenharmony_ci } 537462306a36Sopenharmony_ci 537562306a36Sopenharmony_ci if (ifp->vif->mbss) { 537662306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); 537762306a36Sopenharmony_ci return err; 537862306a36Sopenharmony_ci } 537962306a36Sopenharmony_ci 538062306a36Sopenharmony_ci /* First BSS doesn't get a full reset */ 538162306a36Sopenharmony_ci if (ifp->bsscfgidx == 0) 538262306a36Sopenharmony_ci brcmf_fil_iovar_int_set(ifp, "closednet", 0); 538362306a36Sopenharmony_ci 538462306a36Sopenharmony_ci memset(&join_params, 0, sizeof(join_params)); 538562306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID, 538662306a36Sopenharmony_ci &join_params, sizeof(join_params)); 538762306a36Sopenharmony_ci if (err < 0) 538862306a36Sopenharmony_ci bphy_err(drvr, "SET SSID error (%d)\n", err); 538962306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1); 539062306a36Sopenharmony_ci if (err < 0) 539162306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err); 539262306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0); 539362306a36Sopenharmony_ci if (err < 0) 539462306a36Sopenharmony_ci bphy_err(drvr, "setting AP mode failed %d\n", err); 539562306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) 539662306a36Sopenharmony_ci brcmf_fil_iovar_int_set(ifp, "mbss", 0); 539762306a36Sopenharmony_ci brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY, 539862306a36Sopenharmony_ci ifp->vif->is_11d); 539962306a36Sopenharmony_ci /* Bring device back up so it can be used again */ 540062306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); 540162306a36Sopenharmony_ci if (err < 0) 540262306a36Sopenharmony_ci bphy_err(drvr, "BRCMF_C_UP error %d\n", err); 540362306a36Sopenharmony_ci 540462306a36Sopenharmony_ci brcmf_vif_clear_mgmt_ies(ifp->vif); 540562306a36Sopenharmony_ci } else { 540662306a36Sopenharmony_ci bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx); 540762306a36Sopenharmony_ci bss_enable.enable = cpu_to_le32(0); 540862306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable, 540962306a36Sopenharmony_ci sizeof(bss_enable)); 541062306a36Sopenharmony_ci if (err < 0) 541162306a36Sopenharmony_ci bphy_err(drvr, "bss_enable config failed %d\n", err); 541262306a36Sopenharmony_ci } 541362306a36Sopenharmony_ci brcmf_set_mpc(ifp, 1); 541462306a36Sopenharmony_ci brcmf_configure_arp_nd_offload(ifp, true); 541562306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state); 541662306a36Sopenharmony_ci brcmf_net_setcarrier(ifp, false); 541762306a36Sopenharmony_ci 541862306a36Sopenharmony_ci return err; 541962306a36Sopenharmony_ci} 542062306a36Sopenharmony_ci 542162306a36Sopenharmony_cistatic s32 542262306a36Sopenharmony_cibrcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev, 542362306a36Sopenharmony_ci struct cfg80211_beacon_data *info) 542462306a36Sopenharmony_ci{ 542562306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 542662306a36Sopenharmony_ci 542762306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 542862306a36Sopenharmony_ci 542962306a36Sopenharmony_ci return brcmf_config_ap_mgmt_ie(ifp->vif, info); 543062306a36Sopenharmony_ci} 543162306a36Sopenharmony_ci 543262306a36Sopenharmony_cistatic int 543362306a36Sopenharmony_cibrcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev, 543462306a36Sopenharmony_ci struct station_del_parameters *params) 543562306a36Sopenharmony_ci{ 543662306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 543762306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 543862306a36Sopenharmony_ci struct brcmf_scb_val_le scbval; 543962306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 544062306a36Sopenharmony_ci s32 err; 544162306a36Sopenharmony_ci 544262306a36Sopenharmony_ci if (!params->mac) 544362306a36Sopenharmony_ci return -EFAULT; 544462306a36Sopenharmony_ci 544562306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter %pM\n", params->mac); 544662306a36Sopenharmony_ci 544762306a36Sopenharmony_ci if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif) 544862306a36Sopenharmony_ci ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp; 544962306a36Sopenharmony_ci if (!check_vif_up(ifp->vif)) 545062306a36Sopenharmony_ci return -EIO; 545162306a36Sopenharmony_ci 545262306a36Sopenharmony_ci memcpy(&scbval.ea, params->mac, ETH_ALEN); 545362306a36Sopenharmony_ci scbval.val = cpu_to_le32(params->reason_code); 545462306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON, 545562306a36Sopenharmony_ci &scbval, sizeof(scbval)); 545662306a36Sopenharmony_ci if (err) 545762306a36Sopenharmony_ci bphy_err(drvr, "SCB_DEAUTHENTICATE_FOR_REASON failed %d\n", 545862306a36Sopenharmony_ci err); 545962306a36Sopenharmony_ci 546062306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 546162306a36Sopenharmony_ci return err; 546262306a36Sopenharmony_ci} 546362306a36Sopenharmony_ci 546462306a36Sopenharmony_cistatic int 546562306a36Sopenharmony_cibrcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev, 546662306a36Sopenharmony_ci const u8 *mac, struct station_parameters *params) 546762306a36Sopenharmony_ci{ 546862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 546962306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 547062306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 547162306a36Sopenharmony_ci s32 err; 547262306a36Sopenharmony_ci 547362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac, 547462306a36Sopenharmony_ci params->sta_flags_mask, params->sta_flags_set); 547562306a36Sopenharmony_ci 547662306a36Sopenharmony_ci /* Ignore all 00 MAC */ 547762306a36Sopenharmony_ci if (is_zero_ether_addr(mac)) 547862306a36Sopenharmony_ci return 0; 547962306a36Sopenharmony_ci 548062306a36Sopenharmony_ci if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED))) 548162306a36Sopenharmony_ci return 0; 548262306a36Sopenharmony_ci 548362306a36Sopenharmony_ci if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED)) 548462306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE, 548562306a36Sopenharmony_ci (void *)mac, ETH_ALEN); 548662306a36Sopenharmony_ci else 548762306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE, 548862306a36Sopenharmony_ci (void *)mac, ETH_ALEN); 548962306a36Sopenharmony_ci if (err < 0) 549062306a36Sopenharmony_ci bphy_err(drvr, "Setting SCB (de-)authorize failed, %d\n", err); 549162306a36Sopenharmony_ci 549262306a36Sopenharmony_ci return err; 549362306a36Sopenharmony_ci} 549462306a36Sopenharmony_ci 549562306a36Sopenharmony_cistatic void 549662306a36Sopenharmony_cibrcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy, 549762306a36Sopenharmony_ci struct wireless_dev *wdev, 549862306a36Sopenharmony_ci struct mgmt_frame_regs *upd) 549962306a36Sopenharmony_ci{ 550062306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 550162306a36Sopenharmony_ci 550262306a36Sopenharmony_ci vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); 550362306a36Sopenharmony_ci 550462306a36Sopenharmony_ci vif->mgmt_rx_reg = upd->interface_stypes; 550562306a36Sopenharmony_ci} 550662306a36Sopenharmony_ci 550762306a36Sopenharmony_ci 550862306a36Sopenharmony_cistatic int 550962306a36Sopenharmony_cibrcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev, 551062306a36Sopenharmony_ci struct cfg80211_mgmt_tx_params *params, u64 *cookie) 551162306a36Sopenharmony_ci{ 551262306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 551362306a36Sopenharmony_ci struct ieee80211_channel *chan = params->chan; 551462306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 551562306a36Sopenharmony_ci const u8 *buf = params->buf; 551662306a36Sopenharmony_ci size_t len = params->len; 551762306a36Sopenharmony_ci const struct ieee80211_mgmt *mgmt; 551862306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 551962306a36Sopenharmony_ci s32 err = 0; 552062306a36Sopenharmony_ci s32 ie_offset; 552162306a36Sopenharmony_ci s32 ie_len; 552262306a36Sopenharmony_ci struct brcmf_fil_action_frame_le *action_frame; 552362306a36Sopenharmony_ci struct brcmf_fil_af_params_le *af_params; 552462306a36Sopenharmony_ci bool ack; 552562306a36Sopenharmony_ci s32 chan_nr; 552662306a36Sopenharmony_ci u32 freq; 552762306a36Sopenharmony_ci 552862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 552962306a36Sopenharmony_ci 553062306a36Sopenharmony_ci *cookie = 0; 553162306a36Sopenharmony_ci 553262306a36Sopenharmony_ci mgmt = (const struct ieee80211_mgmt *)buf; 553362306a36Sopenharmony_ci 553462306a36Sopenharmony_ci if (!ieee80211_is_mgmt(mgmt->frame_control)) { 553562306a36Sopenharmony_ci bphy_err(drvr, "Driver only allows MGMT packet type\n"); 553662306a36Sopenharmony_ci return -EPERM; 553762306a36Sopenharmony_ci } 553862306a36Sopenharmony_ci 553962306a36Sopenharmony_ci vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); 554062306a36Sopenharmony_ci 554162306a36Sopenharmony_ci if (ieee80211_is_probe_resp(mgmt->frame_control)) { 554262306a36Sopenharmony_ci /* Right now the only reason to get a probe response */ 554362306a36Sopenharmony_ci /* is for p2p listen response or for p2p GO from */ 554462306a36Sopenharmony_ci /* wpa_supplicant. Unfortunately the probe is send */ 554562306a36Sopenharmony_ci /* on primary ndev, while dongle wants it on the p2p */ 554662306a36Sopenharmony_ci /* vif. Since this is only reason for a probe */ 554762306a36Sopenharmony_ci /* response to be sent, the vif is taken from cfg. */ 554862306a36Sopenharmony_ci /* If ever desired to send proberesp for non p2p */ 554962306a36Sopenharmony_ci /* response then data should be checked for */ 555062306a36Sopenharmony_ci /* "DIRECT-". Note in future supplicant will take */ 555162306a36Sopenharmony_ci /* dedicated p2p wdev to do this and then this 'hack'*/ 555262306a36Sopenharmony_ci /* is not needed anymore. */ 555362306a36Sopenharmony_ci ie_offset = DOT11_MGMT_HDR_LEN + 555462306a36Sopenharmony_ci DOT11_BCN_PRB_FIXED_LEN; 555562306a36Sopenharmony_ci ie_len = len - ie_offset; 555662306a36Sopenharmony_ci if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) 555762306a36Sopenharmony_ci vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; 555862306a36Sopenharmony_ci err = brcmf_vif_set_mgmt_ie(vif, 555962306a36Sopenharmony_ci BRCMF_VNDR_IE_PRBRSP_FLAG, 556062306a36Sopenharmony_ci &buf[ie_offset], 556162306a36Sopenharmony_ci ie_len); 556262306a36Sopenharmony_ci cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true, 556362306a36Sopenharmony_ci GFP_KERNEL); 556462306a36Sopenharmony_ci } else if (ieee80211_is_action(mgmt->frame_control)) { 556562306a36Sopenharmony_ci if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) { 556662306a36Sopenharmony_ci bphy_err(drvr, "invalid action frame length\n"); 556762306a36Sopenharmony_ci err = -EINVAL; 556862306a36Sopenharmony_ci goto exit; 556962306a36Sopenharmony_ci } 557062306a36Sopenharmony_ci af_params = kzalloc(sizeof(*af_params), GFP_KERNEL); 557162306a36Sopenharmony_ci if (af_params == NULL) { 557262306a36Sopenharmony_ci bphy_err(drvr, "unable to allocate frame\n"); 557362306a36Sopenharmony_ci err = -ENOMEM; 557462306a36Sopenharmony_ci goto exit; 557562306a36Sopenharmony_ci } 557662306a36Sopenharmony_ci action_frame = &af_params->action_frame; 557762306a36Sopenharmony_ci /* Add the packet Id */ 557862306a36Sopenharmony_ci action_frame->packet_id = cpu_to_le32(*cookie); 557962306a36Sopenharmony_ci /* Add BSSID */ 558062306a36Sopenharmony_ci memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN); 558162306a36Sopenharmony_ci memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN); 558262306a36Sopenharmony_ci /* Add the length exepted for 802.11 header */ 558362306a36Sopenharmony_ci action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN); 558462306a36Sopenharmony_ci /* Add the channel. Use the one specified as parameter if any or 558562306a36Sopenharmony_ci * the current one (got from the firmware) otherwise 558662306a36Sopenharmony_ci */ 558762306a36Sopenharmony_ci if (chan) 558862306a36Sopenharmony_ci freq = chan->center_freq; 558962306a36Sopenharmony_ci else 559062306a36Sopenharmony_ci brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL, 559162306a36Sopenharmony_ci &freq); 559262306a36Sopenharmony_ci chan_nr = ieee80211_frequency_to_channel(freq); 559362306a36Sopenharmony_ci af_params->channel = cpu_to_le32(chan_nr); 559462306a36Sopenharmony_ci af_params->dwell_time = cpu_to_le32(params->wait); 559562306a36Sopenharmony_ci memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN], 559662306a36Sopenharmony_ci le16_to_cpu(action_frame->len)); 559762306a36Sopenharmony_ci 559862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n", 559962306a36Sopenharmony_ci *cookie, le16_to_cpu(action_frame->len), freq); 560062306a36Sopenharmony_ci 560162306a36Sopenharmony_ci ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg), 560262306a36Sopenharmony_ci af_params); 560362306a36Sopenharmony_ci 560462306a36Sopenharmony_ci cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack, 560562306a36Sopenharmony_ci GFP_KERNEL); 560662306a36Sopenharmony_ci kfree(af_params); 560762306a36Sopenharmony_ci } else { 560862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control); 560962306a36Sopenharmony_ci brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len); 561062306a36Sopenharmony_ci } 561162306a36Sopenharmony_ci 561262306a36Sopenharmony_ciexit: 561362306a36Sopenharmony_ci return err; 561462306a36Sopenharmony_ci} 561562306a36Sopenharmony_ci 561662306a36Sopenharmony_cistatic int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy, 561762306a36Sopenharmony_ci struct net_device *ndev, 561862306a36Sopenharmony_ci s32 rssi_low, s32 rssi_high) 561962306a36Sopenharmony_ci{ 562062306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 562162306a36Sopenharmony_ci struct brcmf_if *ifp; 562262306a36Sopenharmony_ci int err = 0; 562362306a36Sopenharmony_ci 562462306a36Sopenharmony_ci brcmf_dbg(TRACE, "low=%d high=%d", rssi_low, rssi_high); 562562306a36Sopenharmony_ci 562662306a36Sopenharmony_ci ifp = netdev_priv(ndev); 562762306a36Sopenharmony_ci vif = ifp->vif; 562862306a36Sopenharmony_ci 562962306a36Sopenharmony_ci if (rssi_low != vif->cqm_rssi_low || rssi_high != vif->cqm_rssi_high) { 563062306a36Sopenharmony_ci /* The firmware will send an event when the RSSI is less than or 563162306a36Sopenharmony_ci * equal to a configured level and the previous RSSI event was 563262306a36Sopenharmony_ci * less than or equal to a different level. Set a third level 563362306a36Sopenharmony_ci * so that we also detect the transition from rssi <= rssi_high 563462306a36Sopenharmony_ci * to rssi > rssi_high. 563562306a36Sopenharmony_ci */ 563662306a36Sopenharmony_ci struct brcmf_rssi_event_le config = { 563762306a36Sopenharmony_ci .rate_limit_msec = cpu_to_le32(0), 563862306a36Sopenharmony_ci .rssi_level_num = 3, 563962306a36Sopenharmony_ci .rssi_levels = { 564062306a36Sopenharmony_ci clamp_val(rssi_low, S8_MIN, S8_MAX - 2), 564162306a36Sopenharmony_ci clamp_val(rssi_high, S8_MIN + 1, S8_MAX - 1), 564262306a36Sopenharmony_ci S8_MAX, 564362306a36Sopenharmony_ci }, 564462306a36Sopenharmony_ci }; 564562306a36Sopenharmony_ci 564662306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &config, 564762306a36Sopenharmony_ci sizeof(config)); 564862306a36Sopenharmony_ci if (err) { 564962306a36Sopenharmony_ci err = -EINVAL; 565062306a36Sopenharmony_ci } else { 565162306a36Sopenharmony_ci vif->cqm_rssi_low = rssi_low; 565262306a36Sopenharmony_ci vif->cqm_rssi_high = rssi_high; 565362306a36Sopenharmony_ci } 565462306a36Sopenharmony_ci } 565562306a36Sopenharmony_ci 565662306a36Sopenharmony_ci return err; 565762306a36Sopenharmony_ci} 565862306a36Sopenharmony_ci 565962306a36Sopenharmony_cistatic int 566062306a36Sopenharmony_cibrcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy, 566162306a36Sopenharmony_ci struct wireless_dev *wdev, 566262306a36Sopenharmony_ci u64 cookie) 566362306a36Sopenharmony_ci{ 566462306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 566562306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 566662306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 566762306a36Sopenharmony_ci int err = 0; 566862306a36Sopenharmony_ci 566962306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter p2p listen cancel\n"); 567062306a36Sopenharmony_ci 567162306a36Sopenharmony_ci vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif; 567262306a36Sopenharmony_ci if (vif == NULL) { 567362306a36Sopenharmony_ci bphy_err(drvr, "No p2p device available for probe response\n"); 567462306a36Sopenharmony_ci err = -ENODEV; 567562306a36Sopenharmony_ci goto exit; 567662306a36Sopenharmony_ci } 567762306a36Sopenharmony_ci brcmf_p2p_cancel_remain_on_channel(vif->ifp); 567862306a36Sopenharmony_ciexit: 567962306a36Sopenharmony_ci return err; 568062306a36Sopenharmony_ci} 568162306a36Sopenharmony_ci 568262306a36Sopenharmony_cistatic int brcmf_cfg80211_get_channel(struct wiphy *wiphy, 568362306a36Sopenharmony_ci struct wireless_dev *wdev, 568462306a36Sopenharmony_ci unsigned int link_id, 568562306a36Sopenharmony_ci struct cfg80211_chan_def *chandef) 568662306a36Sopenharmony_ci{ 568762306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 568862306a36Sopenharmony_ci struct net_device *ndev = wdev->netdev; 568962306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 569062306a36Sopenharmony_ci struct brcmu_chan ch; 569162306a36Sopenharmony_ci enum nl80211_band band = 0; 569262306a36Sopenharmony_ci enum nl80211_chan_width width = 0; 569362306a36Sopenharmony_ci u32 chanspec; 569462306a36Sopenharmony_ci int freq, err; 569562306a36Sopenharmony_ci 569662306a36Sopenharmony_ci if (!ndev || drvr->bus_if->state != BRCMF_BUS_UP) 569762306a36Sopenharmony_ci return -ENODEV; 569862306a36Sopenharmony_ci 569962306a36Sopenharmony_ci err = brcmf_fil_iovar_int_get(netdev_priv(ndev), "chanspec", &chanspec); 570062306a36Sopenharmony_ci if (err) { 570162306a36Sopenharmony_ci bphy_err(drvr, "chanspec failed (%d)\n", err); 570262306a36Sopenharmony_ci return err; 570362306a36Sopenharmony_ci } 570462306a36Sopenharmony_ci 570562306a36Sopenharmony_ci ch.chspec = chanspec; 570662306a36Sopenharmony_ci cfg->d11inf.decchspec(&ch); 570762306a36Sopenharmony_ci 570862306a36Sopenharmony_ci switch (ch.band) { 570962306a36Sopenharmony_ci case BRCMU_CHAN_BAND_2G: 571062306a36Sopenharmony_ci band = NL80211_BAND_2GHZ; 571162306a36Sopenharmony_ci break; 571262306a36Sopenharmony_ci case BRCMU_CHAN_BAND_5G: 571362306a36Sopenharmony_ci band = NL80211_BAND_5GHZ; 571462306a36Sopenharmony_ci break; 571562306a36Sopenharmony_ci } 571662306a36Sopenharmony_ci 571762306a36Sopenharmony_ci switch (ch.bw) { 571862306a36Sopenharmony_ci case BRCMU_CHAN_BW_80: 571962306a36Sopenharmony_ci width = NL80211_CHAN_WIDTH_80; 572062306a36Sopenharmony_ci break; 572162306a36Sopenharmony_ci case BRCMU_CHAN_BW_40: 572262306a36Sopenharmony_ci width = NL80211_CHAN_WIDTH_40; 572362306a36Sopenharmony_ci break; 572462306a36Sopenharmony_ci case BRCMU_CHAN_BW_20: 572562306a36Sopenharmony_ci width = NL80211_CHAN_WIDTH_20; 572662306a36Sopenharmony_ci break; 572762306a36Sopenharmony_ci case BRCMU_CHAN_BW_80P80: 572862306a36Sopenharmony_ci width = NL80211_CHAN_WIDTH_80P80; 572962306a36Sopenharmony_ci break; 573062306a36Sopenharmony_ci case BRCMU_CHAN_BW_160: 573162306a36Sopenharmony_ci width = NL80211_CHAN_WIDTH_160; 573262306a36Sopenharmony_ci break; 573362306a36Sopenharmony_ci } 573462306a36Sopenharmony_ci 573562306a36Sopenharmony_ci freq = ieee80211_channel_to_frequency(ch.control_ch_num, band); 573662306a36Sopenharmony_ci chandef->chan = ieee80211_get_channel(wiphy, freq); 573762306a36Sopenharmony_ci chandef->width = width; 573862306a36Sopenharmony_ci chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band); 573962306a36Sopenharmony_ci chandef->center_freq2 = 0; 574062306a36Sopenharmony_ci 574162306a36Sopenharmony_ci return 0; 574262306a36Sopenharmony_ci} 574362306a36Sopenharmony_ci 574462306a36Sopenharmony_cistatic int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy, 574562306a36Sopenharmony_ci struct wireless_dev *wdev, 574662306a36Sopenharmony_ci enum nl80211_crit_proto_id proto, 574762306a36Sopenharmony_ci u16 duration) 574862306a36Sopenharmony_ci{ 574962306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 575062306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 575162306a36Sopenharmony_ci 575262306a36Sopenharmony_ci vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); 575362306a36Sopenharmony_ci 575462306a36Sopenharmony_ci /* only DHCP support for now */ 575562306a36Sopenharmony_ci if (proto != NL80211_CRIT_PROTO_DHCP) 575662306a36Sopenharmony_ci return -EINVAL; 575762306a36Sopenharmony_ci 575862306a36Sopenharmony_ci /* suppress and abort scanning */ 575962306a36Sopenharmony_ci set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); 576062306a36Sopenharmony_ci brcmf_abort_scanning(cfg); 576162306a36Sopenharmony_ci 576262306a36Sopenharmony_ci return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration); 576362306a36Sopenharmony_ci} 576462306a36Sopenharmony_ci 576562306a36Sopenharmony_cistatic void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy, 576662306a36Sopenharmony_ci struct wireless_dev *wdev) 576762306a36Sopenharmony_ci{ 576862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 576962306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 577062306a36Sopenharmony_ci 577162306a36Sopenharmony_ci vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev); 577262306a36Sopenharmony_ci 577362306a36Sopenharmony_ci brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0); 577462306a36Sopenharmony_ci clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status); 577562306a36Sopenharmony_ci} 577662306a36Sopenharmony_ci 577762306a36Sopenharmony_cistatic s32 577862306a36Sopenharmony_cibrcmf_notify_tdls_peer_event(struct brcmf_if *ifp, 577962306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 578062306a36Sopenharmony_ci{ 578162306a36Sopenharmony_ci switch (e->reason) { 578262306a36Sopenharmony_ci case BRCMF_E_REASON_TDLS_PEER_DISCOVERED: 578362306a36Sopenharmony_ci brcmf_dbg(TRACE, "TDLS Peer Discovered\n"); 578462306a36Sopenharmony_ci break; 578562306a36Sopenharmony_ci case BRCMF_E_REASON_TDLS_PEER_CONNECTED: 578662306a36Sopenharmony_ci brcmf_dbg(TRACE, "TDLS Peer Connected\n"); 578762306a36Sopenharmony_ci brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); 578862306a36Sopenharmony_ci break; 578962306a36Sopenharmony_ci case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED: 579062306a36Sopenharmony_ci brcmf_dbg(TRACE, "TDLS Peer Disconnected\n"); 579162306a36Sopenharmony_ci brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); 579262306a36Sopenharmony_ci break; 579362306a36Sopenharmony_ci } 579462306a36Sopenharmony_ci 579562306a36Sopenharmony_ci return 0; 579662306a36Sopenharmony_ci} 579762306a36Sopenharmony_ci 579862306a36Sopenharmony_cistatic int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper) 579962306a36Sopenharmony_ci{ 580062306a36Sopenharmony_ci int ret; 580162306a36Sopenharmony_ci 580262306a36Sopenharmony_ci switch (oper) { 580362306a36Sopenharmony_ci case NL80211_TDLS_DISCOVERY_REQ: 580462306a36Sopenharmony_ci ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY; 580562306a36Sopenharmony_ci break; 580662306a36Sopenharmony_ci case NL80211_TDLS_SETUP: 580762306a36Sopenharmony_ci ret = BRCMF_TDLS_MANUAL_EP_CREATE; 580862306a36Sopenharmony_ci break; 580962306a36Sopenharmony_ci case NL80211_TDLS_TEARDOWN: 581062306a36Sopenharmony_ci ret = BRCMF_TDLS_MANUAL_EP_DELETE; 581162306a36Sopenharmony_ci break; 581262306a36Sopenharmony_ci default: 581362306a36Sopenharmony_ci brcmf_err("unsupported operation: %d\n", oper); 581462306a36Sopenharmony_ci ret = -EOPNOTSUPP; 581562306a36Sopenharmony_ci } 581662306a36Sopenharmony_ci return ret; 581762306a36Sopenharmony_ci} 581862306a36Sopenharmony_ci 581962306a36Sopenharmony_cistatic int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy, 582062306a36Sopenharmony_ci struct net_device *ndev, const u8 *peer, 582162306a36Sopenharmony_ci enum nl80211_tdls_operation oper) 582262306a36Sopenharmony_ci{ 582362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 582462306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 582562306a36Sopenharmony_ci struct brcmf_if *ifp; 582662306a36Sopenharmony_ci struct brcmf_tdls_iovar_le info; 582762306a36Sopenharmony_ci int ret = 0; 582862306a36Sopenharmony_ci 582962306a36Sopenharmony_ci ret = brcmf_convert_nl80211_tdls_oper(oper); 583062306a36Sopenharmony_ci if (ret < 0) 583162306a36Sopenharmony_ci return ret; 583262306a36Sopenharmony_ci 583362306a36Sopenharmony_ci ifp = netdev_priv(ndev); 583462306a36Sopenharmony_ci memset(&info, 0, sizeof(info)); 583562306a36Sopenharmony_ci info.mode = (u8)ret; 583662306a36Sopenharmony_ci if (peer) 583762306a36Sopenharmony_ci memcpy(info.ea, peer, ETH_ALEN); 583862306a36Sopenharmony_ci 583962306a36Sopenharmony_ci ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint", 584062306a36Sopenharmony_ci &info, sizeof(info)); 584162306a36Sopenharmony_ci if (ret < 0) 584262306a36Sopenharmony_ci bphy_err(drvr, "tdls_endpoint iovar failed: ret=%d\n", ret); 584362306a36Sopenharmony_ci 584462306a36Sopenharmony_ci return ret; 584562306a36Sopenharmony_ci} 584662306a36Sopenharmony_ci 584762306a36Sopenharmony_cistatic int 584862306a36Sopenharmony_cibrcmf_cfg80211_update_conn_params(struct wiphy *wiphy, 584962306a36Sopenharmony_ci struct net_device *ndev, 585062306a36Sopenharmony_ci struct cfg80211_connect_params *sme, 585162306a36Sopenharmony_ci u32 changed) 585262306a36Sopenharmony_ci{ 585362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 585462306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 585562306a36Sopenharmony_ci struct brcmf_if *ifp; 585662306a36Sopenharmony_ci int err; 585762306a36Sopenharmony_ci 585862306a36Sopenharmony_ci if (!(changed & UPDATE_ASSOC_IES)) 585962306a36Sopenharmony_ci return 0; 586062306a36Sopenharmony_ci 586162306a36Sopenharmony_ci ifp = netdev_priv(ndev); 586262306a36Sopenharmony_ci err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG, 586362306a36Sopenharmony_ci sme->ie, sme->ie_len); 586462306a36Sopenharmony_ci if (err) 586562306a36Sopenharmony_ci bphy_err(drvr, "Set Assoc REQ IE Failed\n"); 586662306a36Sopenharmony_ci else 586762306a36Sopenharmony_ci brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n"); 586862306a36Sopenharmony_ci 586962306a36Sopenharmony_ci return err; 587062306a36Sopenharmony_ci} 587162306a36Sopenharmony_ci 587262306a36Sopenharmony_ci#ifdef CONFIG_PM 587362306a36Sopenharmony_cistatic int 587462306a36Sopenharmony_cibrcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev, 587562306a36Sopenharmony_ci struct cfg80211_gtk_rekey_data *gtk) 587662306a36Sopenharmony_ci{ 587762306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 587862306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 587962306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 588062306a36Sopenharmony_ci struct brcmf_gtk_keyinfo_le gtk_le; 588162306a36Sopenharmony_ci int ret; 588262306a36Sopenharmony_ci 588362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx); 588462306a36Sopenharmony_ci 588562306a36Sopenharmony_ci memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck)); 588662306a36Sopenharmony_ci memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek)); 588762306a36Sopenharmony_ci memcpy(gtk_le.replay_counter, gtk->replay_ctr, 588862306a36Sopenharmony_ci sizeof(gtk_le.replay_counter)); 588962306a36Sopenharmony_ci 589062306a36Sopenharmony_ci ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", >k_le, 589162306a36Sopenharmony_ci sizeof(gtk_le)); 589262306a36Sopenharmony_ci if (ret < 0) 589362306a36Sopenharmony_ci bphy_err(drvr, "gtk_key_info iovar failed: ret=%d\n", ret); 589462306a36Sopenharmony_ci 589562306a36Sopenharmony_ci return ret; 589662306a36Sopenharmony_ci} 589762306a36Sopenharmony_ci#endif 589862306a36Sopenharmony_ci 589962306a36Sopenharmony_cistatic int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev, 590062306a36Sopenharmony_ci const struct cfg80211_pmk_conf *conf) 590162306a36Sopenharmony_ci{ 590262306a36Sopenharmony_ci struct brcmf_if *ifp; 590362306a36Sopenharmony_ci 590462306a36Sopenharmony_ci brcmf_dbg(TRACE, "enter\n"); 590562306a36Sopenharmony_ci 590662306a36Sopenharmony_ci /* expect using firmware supplicant for 1X */ 590762306a36Sopenharmony_ci ifp = netdev_priv(dev); 590862306a36Sopenharmony_ci if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) 590962306a36Sopenharmony_ci return -EINVAL; 591062306a36Sopenharmony_ci 591162306a36Sopenharmony_ci if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN) 591262306a36Sopenharmony_ci return -ERANGE; 591362306a36Sopenharmony_ci 591462306a36Sopenharmony_ci return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len); 591562306a36Sopenharmony_ci} 591662306a36Sopenharmony_ci 591762306a36Sopenharmony_cistatic int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev, 591862306a36Sopenharmony_ci const u8 *aa) 591962306a36Sopenharmony_ci{ 592062306a36Sopenharmony_ci struct brcmf_if *ifp; 592162306a36Sopenharmony_ci 592262306a36Sopenharmony_ci brcmf_dbg(TRACE, "enter\n"); 592362306a36Sopenharmony_ci ifp = netdev_priv(dev); 592462306a36Sopenharmony_ci if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X)) 592562306a36Sopenharmony_ci return -EINVAL; 592662306a36Sopenharmony_ci 592762306a36Sopenharmony_ci return brcmf_set_pmk(ifp, NULL, 0); 592862306a36Sopenharmony_ci} 592962306a36Sopenharmony_ci 593062306a36Sopenharmony_cistatic struct cfg80211_ops brcmf_cfg80211_ops = { 593162306a36Sopenharmony_ci .add_virtual_intf = brcmf_cfg80211_add_iface, 593262306a36Sopenharmony_ci .del_virtual_intf = brcmf_cfg80211_del_iface, 593362306a36Sopenharmony_ci .change_virtual_intf = brcmf_cfg80211_change_iface, 593462306a36Sopenharmony_ci .scan = brcmf_cfg80211_scan, 593562306a36Sopenharmony_ci .set_wiphy_params = brcmf_cfg80211_set_wiphy_params, 593662306a36Sopenharmony_ci .join_ibss = brcmf_cfg80211_join_ibss, 593762306a36Sopenharmony_ci .leave_ibss = brcmf_cfg80211_leave_ibss, 593862306a36Sopenharmony_ci .get_station = brcmf_cfg80211_get_station, 593962306a36Sopenharmony_ci .dump_station = brcmf_cfg80211_dump_station, 594062306a36Sopenharmony_ci .set_tx_power = brcmf_cfg80211_set_tx_power, 594162306a36Sopenharmony_ci .get_tx_power = brcmf_cfg80211_get_tx_power, 594262306a36Sopenharmony_ci .add_key = brcmf_cfg80211_add_key, 594362306a36Sopenharmony_ci .del_key = brcmf_cfg80211_del_key, 594462306a36Sopenharmony_ci .get_key = brcmf_cfg80211_get_key, 594562306a36Sopenharmony_ci .set_default_key = brcmf_cfg80211_config_default_key, 594662306a36Sopenharmony_ci .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key, 594762306a36Sopenharmony_ci .set_power_mgmt = brcmf_cfg80211_set_power_mgmt, 594862306a36Sopenharmony_ci .connect = brcmf_cfg80211_connect, 594962306a36Sopenharmony_ci .disconnect = brcmf_cfg80211_disconnect, 595062306a36Sopenharmony_ci .suspend = brcmf_cfg80211_suspend, 595162306a36Sopenharmony_ci .resume = brcmf_cfg80211_resume, 595262306a36Sopenharmony_ci .set_pmksa = brcmf_cfg80211_set_pmksa, 595362306a36Sopenharmony_ci .del_pmksa = brcmf_cfg80211_del_pmksa, 595462306a36Sopenharmony_ci .flush_pmksa = brcmf_cfg80211_flush_pmksa, 595562306a36Sopenharmony_ci .start_ap = brcmf_cfg80211_start_ap, 595662306a36Sopenharmony_ci .stop_ap = brcmf_cfg80211_stop_ap, 595762306a36Sopenharmony_ci .change_beacon = brcmf_cfg80211_change_beacon, 595862306a36Sopenharmony_ci .del_station = brcmf_cfg80211_del_station, 595962306a36Sopenharmony_ci .change_station = brcmf_cfg80211_change_station, 596062306a36Sopenharmony_ci .sched_scan_start = brcmf_cfg80211_sched_scan_start, 596162306a36Sopenharmony_ci .sched_scan_stop = brcmf_cfg80211_sched_scan_stop, 596262306a36Sopenharmony_ci .update_mgmt_frame_registrations = 596362306a36Sopenharmony_ci brcmf_cfg80211_update_mgmt_frame_registrations, 596462306a36Sopenharmony_ci .mgmt_tx = brcmf_cfg80211_mgmt_tx, 596562306a36Sopenharmony_ci .set_cqm_rssi_range_config = brcmf_cfg80211_set_cqm_rssi_range_config, 596662306a36Sopenharmony_ci .remain_on_channel = brcmf_p2p_remain_on_channel, 596762306a36Sopenharmony_ci .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel, 596862306a36Sopenharmony_ci .get_channel = brcmf_cfg80211_get_channel, 596962306a36Sopenharmony_ci .start_p2p_device = brcmf_p2p_start_device, 597062306a36Sopenharmony_ci .stop_p2p_device = brcmf_p2p_stop_device, 597162306a36Sopenharmony_ci .crit_proto_start = brcmf_cfg80211_crit_proto_start, 597262306a36Sopenharmony_ci .crit_proto_stop = brcmf_cfg80211_crit_proto_stop, 597362306a36Sopenharmony_ci .tdls_oper = brcmf_cfg80211_tdls_oper, 597462306a36Sopenharmony_ci .update_connect_params = brcmf_cfg80211_update_conn_params, 597562306a36Sopenharmony_ci .set_pmk = brcmf_cfg80211_set_pmk, 597662306a36Sopenharmony_ci .del_pmk = brcmf_cfg80211_del_pmk, 597762306a36Sopenharmony_ci}; 597862306a36Sopenharmony_ci 597962306a36Sopenharmony_cistruct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings) 598062306a36Sopenharmony_ci{ 598162306a36Sopenharmony_ci struct cfg80211_ops *ops; 598262306a36Sopenharmony_ci 598362306a36Sopenharmony_ci ops = kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops), 598462306a36Sopenharmony_ci GFP_KERNEL); 598562306a36Sopenharmony_ci 598662306a36Sopenharmony_ci if (ops && settings->roamoff) 598762306a36Sopenharmony_ci ops->update_connect_params = NULL; 598862306a36Sopenharmony_ci 598962306a36Sopenharmony_ci return ops; 599062306a36Sopenharmony_ci} 599162306a36Sopenharmony_ci 599262306a36Sopenharmony_cistruct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg, 599362306a36Sopenharmony_ci enum nl80211_iftype type) 599462306a36Sopenharmony_ci{ 599562306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif_walk; 599662306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 599762306a36Sopenharmony_ci bool mbss; 599862306a36Sopenharmony_ci struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); 599962306a36Sopenharmony_ci 600062306a36Sopenharmony_ci brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n", 600162306a36Sopenharmony_ci sizeof(*vif)); 600262306a36Sopenharmony_ci vif = kzalloc(sizeof(*vif), GFP_KERNEL); 600362306a36Sopenharmony_ci if (!vif) 600462306a36Sopenharmony_ci return ERR_PTR(-ENOMEM); 600562306a36Sopenharmony_ci 600662306a36Sopenharmony_ci vif->wdev.wiphy = cfg->wiphy; 600762306a36Sopenharmony_ci vif->wdev.iftype = type; 600862306a36Sopenharmony_ci 600962306a36Sopenharmony_ci brcmf_init_prof(&vif->profile); 601062306a36Sopenharmony_ci 601162306a36Sopenharmony_ci if (type == NL80211_IFTYPE_AP && 601262306a36Sopenharmony_ci brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) { 601362306a36Sopenharmony_ci mbss = false; 601462306a36Sopenharmony_ci list_for_each_entry(vif_walk, &cfg->vif_list, list) { 601562306a36Sopenharmony_ci if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) { 601662306a36Sopenharmony_ci mbss = true; 601762306a36Sopenharmony_ci break; 601862306a36Sopenharmony_ci } 601962306a36Sopenharmony_ci } 602062306a36Sopenharmony_ci vif->mbss = mbss; 602162306a36Sopenharmony_ci } 602262306a36Sopenharmony_ci 602362306a36Sopenharmony_ci list_add_tail(&vif->list, &cfg->vif_list); 602462306a36Sopenharmony_ci return vif; 602562306a36Sopenharmony_ci} 602662306a36Sopenharmony_ci 602762306a36Sopenharmony_civoid brcmf_free_vif(struct brcmf_cfg80211_vif *vif) 602862306a36Sopenharmony_ci{ 602962306a36Sopenharmony_ci list_del(&vif->list); 603062306a36Sopenharmony_ci kfree(vif); 603162306a36Sopenharmony_ci} 603262306a36Sopenharmony_ci 603362306a36Sopenharmony_civoid brcmf_cfg80211_free_netdev(struct net_device *ndev) 603462306a36Sopenharmony_ci{ 603562306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 603662306a36Sopenharmony_ci struct brcmf_if *ifp; 603762306a36Sopenharmony_ci 603862306a36Sopenharmony_ci ifp = netdev_priv(ndev); 603962306a36Sopenharmony_ci vif = ifp->vif; 604062306a36Sopenharmony_ci 604162306a36Sopenharmony_ci if (vif) 604262306a36Sopenharmony_ci brcmf_free_vif(vif); 604362306a36Sopenharmony_ci} 604462306a36Sopenharmony_ci 604562306a36Sopenharmony_cistatic bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif, 604662306a36Sopenharmony_ci const struct brcmf_event_msg *e) 604762306a36Sopenharmony_ci{ 604862306a36Sopenharmony_ci u32 event = e->event_code; 604962306a36Sopenharmony_ci u32 status = e->status; 605062306a36Sopenharmony_ci 605162306a36Sopenharmony_ci if ((vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_PSK || 605262306a36Sopenharmony_ci vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_SAE) && 605362306a36Sopenharmony_ci event == BRCMF_E_PSK_SUP && 605462306a36Sopenharmony_ci status == BRCMF_E_STATUS_FWSUP_COMPLETED) 605562306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); 605662306a36Sopenharmony_ci if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) { 605762306a36Sopenharmony_ci brcmf_dbg(CONN, "Processing set ssid\n"); 605862306a36Sopenharmony_ci memcpy(vif->profile.bssid, e->addr, ETH_ALEN); 605962306a36Sopenharmony_ci if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK && 606062306a36Sopenharmony_ci vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE) 606162306a36Sopenharmony_ci return true; 606262306a36Sopenharmony_ci 606362306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); 606462306a36Sopenharmony_ci } 606562306a36Sopenharmony_ci 606662306a36Sopenharmony_ci if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) && 606762306a36Sopenharmony_ci test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) { 606862306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); 606962306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); 607062306a36Sopenharmony_ci return true; 607162306a36Sopenharmony_ci } 607262306a36Sopenharmony_ci return false; 607362306a36Sopenharmony_ci} 607462306a36Sopenharmony_ci 607562306a36Sopenharmony_cistatic bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif, 607662306a36Sopenharmony_ci const struct brcmf_event_msg *e) 607762306a36Sopenharmony_ci{ 607862306a36Sopenharmony_ci u32 event = e->event_code; 607962306a36Sopenharmony_ci u16 flags = e->flags; 608062306a36Sopenharmony_ci 608162306a36Sopenharmony_ci if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) || 608262306a36Sopenharmony_ci (event == BRCMF_E_DISASSOC_IND) || 608362306a36Sopenharmony_ci ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) { 608462306a36Sopenharmony_ci brcmf_dbg(CONN, "Processing link down\n"); 608562306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state); 608662306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state); 608762306a36Sopenharmony_ci return true; 608862306a36Sopenharmony_ci } 608962306a36Sopenharmony_ci return false; 609062306a36Sopenharmony_ci} 609162306a36Sopenharmony_ci 609262306a36Sopenharmony_cistatic bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg, 609362306a36Sopenharmony_ci const struct brcmf_event_msg *e) 609462306a36Sopenharmony_ci{ 609562306a36Sopenharmony_ci u32 event = e->event_code; 609662306a36Sopenharmony_ci u32 status = e->status; 609762306a36Sopenharmony_ci 609862306a36Sopenharmony_ci if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) { 609962306a36Sopenharmony_ci brcmf_dbg(CONN, "Processing Link %s & no network found\n", 610062306a36Sopenharmony_ci e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down"); 610162306a36Sopenharmony_ci return true; 610262306a36Sopenharmony_ci } 610362306a36Sopenharmony_ci 610462306a36Sopenharmony_ci if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) { 610562306a36Sopenharmony_ci brcmf_dbg(CONN, "Processing connecting & no network found\n"); 610662306a36Sopenharmony_ci return true; 610762306a36Sopenharmony_ci } 610862306a36Sopenharmony_ci 610962306a36Sopenharmony_ci if (event == BRCMF_E_PSK_SUP && 611062306a36Sopenharmony_ci status != BRCMF_E_STATUS_FWSUP_COMPLETED) { 611162306a36Sopenharmony_ci brcmf_dbg(CONN, "Processing failed supplicant state: %u\n", 611262306a36Sopenharmony_ci status); 611362306a36Sopenharmony_ci return true; 611462306a36Sopenharmony_ci } 611562306a36Sopenharmony_ci 611662306a36Sopenharmony_ci return false; 611762306a36Sopenharmony_ci} 611862306a36Sopenharmony_ci 611962306a36Sopenharmony_cistatic void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg) 612062306a36Sopenharmony_ci{ 612162306a36Sopenharmony_ci struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); 612262306a36Sopenharmony_ci 612362306a36Sopenharmony_ci kfree(conn_info->req_ie); 612462306a36Sopenharmony_ci conn_info->req_ie = NULL; 612562306a36Sopenharmony_ci conn_info->req_ie_len = 0; 612662306a36Sopenharmony_ci kfree(conn_info->resp_ie); 612762306a36Sopenharmony_ci conn_info->resp_ie = NULL; 612862306a36Sopenharmony_ci conn_info->resp_ie_len = 0; 612962306a36Sopenharmony_ci} 613062306a36Sopenharmony_ci 613162306a36Sopenharmony_ciu8 brcmf_map_prio_to_prec(void *config, u8 prio) 613262306a36Sopenharmony_ci{ 613362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config; 613462306a36Sopenharmony_ci 613562306a36Sopenharmony_ci if (!cfg) 613662306a36Sopenharmony_ci return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ? 613762306a36Sopenharmony_ci (prio ^ 2) : prio; 613862306a36Sopenharmony_ci 613962306a36Sopenharmony_ci /* For those AC(s) with ACM flag set to 1, convert its 4-level priority 614062306a36Sopenharmony_ci * to an 8-level precedence which is the same as BE's 614162306a36Sopenharmony_ci */ 614262306a36Sopenharmony_ci if (prio > PRIO_8021D_EE && 614362306a36Sopenharmony_ci cfg->ac_priority[prio] == cfg->ac_priority[PRIO_8021D_BE]) 614462306a36Sopenharmony_ci return cfg->ac_priority[prio] * 2; 614562306a36Sopenharmony_ci 614662306a36Sopenharmony_ci /* Conversion of 4-level priority to 8-level precedence */ 614762306a36Sopenharmony_ci if (prio == PRIO_8021D_BE || prio == PRIO_8021D_BK || 614862306a36Sopenharmony_ci prio == PRIO_8021D_CL || prio == PRIO_8021D_VO) 614962306a36Sopenharmony_ci return cfg->ac_priority[prio] * 2; 615062306a36Sopenharmony_ci else 615162306a36Sopenharmony_ci return cfg->ac_priority[prio] * 2 + 1; 615262306a36Sopenharmony_ci} 615362306a36Sopenharmony_ci 615462306a36Sopenharmony_ciu8 brcmf_map_prio_to_aci(void *config, u8 prio) 615562306a36Sopenharmony_ci{ 615662306a36Sopenharmony_ci /* Prio here refers to the 802.1d priority in range of 0 to 7. 615762306a36Sopenharmony_ci * ACI here refers to the WLAN AC Index in range of 0 to 3. 615862306a36Sopenharmony_ci * This function will return ACI corresponding to input prio. 615962306a36Sopenharmony_ci */ 616062306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config; 616162306a36Sopenharmony_ci 616262306a36Sopenharmony_ci if (cfg) 616362306a36Sopenharmony_ci return cfg->ac_priority[prio]; 616462306a36Sopenharmony_ci 616562306a36Sopenharmony_ci return prio; 616662306a36Sopenharmony_ci} 616762306a36Sopenharmony_ci 616862306a36Sopenharmony_cistatic void brcmf_init_wmm_prio(u8 *priority) 616962306a36Sopenharmony_ci{ 617062306a36Sopenharmony_ci /* Initialize AC priority array to default 617162306a36Sopenharmony_ci * 802.1d priority as per following table: 617262306a36Sopenharmony_ci * 802.1d prio 0,3 maps to BE 617362306a36Sopenharmony_ci * 802.1d prio 1,2 maps to BK 617462306a36Sopenharmony_ci * 802.1d prio 4,5 maps to VI 617562306a36Sopenharmony_ci * 802.1d prio 6,7 maps to VO 617662306a36Sopenharmony_ci */ 617762306a36Sopenharmony_ci priority[0] = BRCMF_FWS_FIFO_AC_BE; 617862306a36Sopenharmony_ci priority[3] = BRCMF_FWS_FIFO_AC_BE; 617962306a36Sopenharmony_ci priority[1] = BRCMF_FWS_FIFO_AC_BK; 618062306a36Sopenharmony_ci priority[2] = BRCMF_FWS_FIFO_AC_BK; 618162306a36Sopenharmony_ci priority[4] = BRCMF_FWS_FIFO_AC_VI; 618262306a36Sopenharmony_ci priority[5] = BRCMF_FWS_FIFO_AC_VI; 618362306a36Sopenharmony_ci priority[6] = BRCMF_FWS_FIFO_AC_VO; 618462306a36Sopenharmony_ci priority[7] = BRCMF_FWS_FIFO_AC_VO; 618562306a36Sopenharmony_ci} 618662306a36Sopenharmony_ci 618762306a36Sopenharmony_cistatic void brcmf_wifi_prioritize_acparams(const 618862306a36Sopenharmony_ci struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority) 618962306a36Sopenharmony_ci{ 619062306a36Sopenharmony_ci u8 aci; 619162306a36Sopenharmony_ci u8 aifsn; 619262306a36Sopenharmony_ci u8 ecwmin; 619362306a36Sopenharmony_ci u8 ecwmax; 619462306a36Sopenharmony_ci u8 acm; 619562306a36Sopenharmony_ci u8 ranking_basis[EDCF_AC_COUNT]; 619662306a36Sopenharmony_ci u8 aci_prio[EDCF_AC_COUNT]; /* AC_BE, AC_BK, AC_VI, AC_VO */ 619762306a36Sopenharmony_ci u8 index; 619862306a36Sopenharmony_ci 619962306a36Sopenharmony_ci for (aci = 0; aci < EDCF_AC_COUNT; aci++, acp++) { 620062306a36Sopenharmony_ci aifsn = acp->ACI & EDCF_AIFSN_MASK; 620162306a36Sopenharmony_ci acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0; 620262306a36Sopenharmony_ci ecwmin = acp->ECW & EDCF_ECWMIN_MASK; 620362306a36Sopenharmony_ci ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT; 620462306a36Sopenharmony_ci brcmf_dbg(CONN, "ACI %d aifsn %d acm %d ecwmin %d ecwmax %d\n", 620562306a36Sopenharmony_ci aci, aifsn, acm, ecwmin, ecwmax); 620662306a36Sopenharmony_ci /* Default AC_VO will be the lowest ranking value */ 620762306a36Sopenharmony_ci ranking_basis[aci] = aifsn + ecwmin + ecwmax; 620862306a36Sopenharmony_ci /* Initialise priority starting at 0 (AC_BE) */ 620962306a36Sopenharmony_ci aci_prio[aci] = 0; 621062306a36Sopenharmony_ci 621162306a36Sopenharmony_ci /* If ACM is set, STA can't use this AC as per 802.11. 621262306a36Sopenharmony_ci * Change the ranking to BE 621362306a36Sopenharmony_ci */ 621462306a36Sopenharmony_ci if (aci != AC_BE && aci != AC_BK && acm == 1) 621562306a36Sopenharmony_ci ranking_basis[aci] = ranking_basis[AC_BE]; 621662306a36Sopenharmony_ci } 621762306a36Sopenharmony_ci 621862306a36Sopenharmony_ci /* Ranking method which works for AC priority 621962306a36Sopenharmony_ci * swapping when values for cwmin, cwmax and aifsn are varied 622062306a36Sopenharmony_ci * Compare each aci_prio against each other aci_prio 622162306a36Sopenharmony_ci */ 622262306a36Sopenharmony_ci for (aci = 0; aci < EDCF_AC_COUNT; aci++) { 622362306a36Sopenharmony_ci for (index = 0; index < EDCF_AC_COUNT; index++) { 622462306a36Sopenharmony_ci if (index != aci) { 622562306a36Sopenharmony_ci /* Smaller ranking value has higher priority, 622662306a36Sopenharmony_ci * so increment priority for each ACI which has 622762306a36Sopenharmony_ci * a higher ranking value 622862306a36Sopenharmony_ci */ 622962306a36Sopenharmony_ci if (ranking_basis[aci] < ranking_basis[index]) 623062306a36Sopenharmony_ci aci_prio[aci]++; 623162306a36Sopenharmony_ci } 623262306a36Sopenharmony_ci } 623362306a36Sopenharmony_ci } 623462306a36Sopenharmony_ci 623562306a36Sopenharmony_ci /* By now, aci_prio[] will be in range of 0 to 3. 623662306a36Sopenharmony_ci * Use ACI prio to get the new priority value for 623762306a36Sopenharmony_ci * each 802.1d traffic type, in this range. 623862306a36Sopenharmony_ci */ 623962306a36Sopenharmony_ci if (!(aci_prio[AC_BE] == aci_prio[AC_BK] && 624062306a36Sopenharmony_ci aci_prio[AC_BK] == aci_prio[AC_VI] && 624162306a36Sopenharmony_ci aci_prio[AC_VI] == aci_prio[AC_VO])) { 624262306a36Sopenharmony_ci /* 802.1d 0,3 maps to BE */ 624362306a36Sopenharmony_ci priority[0] = aci_prio[AC_BE]; 624462306a36Sopenharmony_ci priority[3] = aci_prio[AC_BE]; 624562306a36Sopenharmony_ci 624662306a36Sopenharmony_ci /* 802.1d 1,2 maps to BK */ 624762306a36Sopenharmony_ci priority[1] = aci_prio[AC_BK]; 624862306a36Sopenharmony_ci priority[2] = aci_prio[AC_BK]; 624962306a36Sopenharmony_ci 625062306a36Sopenharmony_ci /* 802.1d 4,5 maps to VO */ 625162306a36Sopenharmony_ci priority[4] = aci_prio[AC_VI]; 625262306a36Sopenharmony_ci priority[5] = aci_prio[AC_VI]; 625362306a36Sopenharmony_ci 625462306a36Sopenharmony_ci /* 802.1d 6,7 maps to VO */ 625562306a36Sopenharmony_ci priority[6] = aci_prio[AC_VO]; 625662306a36Sopenharmony_ci priority[7] = aci_prio[AC_VO]; 625762306a36Sopenharmony_ci } else { 625862306a36Sopenharmony_ci /* Initialize to default priority */ 625962306a36Sopenharmony_ci brcmf_init_wmm_prio(priority); 626062306a36Sopenharmony_ci } 626162306a36Sopenharmony_ci 626262306a36Sopenharmony_ci brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n", 626362306a36Sopenharmony_ci priority[0], priority[1], priority[2], priority[3]); 626462306a36Sopenharmony_ci 626562306a36Sopenharmony_ci brcmf_dbg(CONN, "Adj prio VI 4->%d, VI 5->%d, VO 6->%d, VO 7->%d\n", 626662306a36Sopenharmony_ci priority[4], priority[5], priority[6], priority[7]); 626762306a36Sopenharmony_ci} 626862306a36Sopenharmony_ci 626962306a36Sopenharmony_cistatic s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg, 627062306a36Sopenharmony_ci struct brcmf_if *ifp) 627162306a36Sopenharmony_ci{ 627262306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 627362306a36Sopenharmony_ci struct brcmf_cfg80211_assoc_ielen_le *assoc_info; 627462306a36Sopenharmony_ci struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); 627562306a36Sopenharmony_ci struct brcmf_cfg80211_edcf_acparam edcf_acparam_info[EDCF_AC_COUNT]; 627662306a36Sopenharmony_ci u32 req_len; 627762306a36Sopenharmony_ci u32 resp_len; 627862306a36Sopenharmony_ci s32 err = 0; 627962306a36Sopenharmony_ci 628062306a36Sopenharmony_ci brcmf_clear_assoc_ies(cfg); 628162306a36Sopenharmony_ci 628262306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "assoc_info", 628362306a36Sopenharmony_ci cfg->extra_buf, WL_ASSOC_INFO_MAX); 628462306a36Sopenharmony_ci if (err) { 628562306a36Sopenharmony_ci bphy_err(drvr, "could not get assoc info (%d)\n", err); 628662306a36Sopenharmony_ci return err; 628762306a36Sopenharmony_ci } 628862306a36Sopenharmony_ci assoc_info = 628962306a36Sopenharmony_ci (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf; 629062306a36Sopenharmony_ci req_len = le32_to_cpu(assoc_info->req_len); 629162306a36Sopenharmony_ci resp_len = le32_to_cpu(assoc_info->resp_len); 629262306a36Sopenharmony_ci if (req_len > WL_EXTRA_BUF_MAX || resp_len > WL_EXTRA_BUF_MAX) { 629362306a36Sopenharmony_ci bphy_err(drvr, "invalid lengths in assoc info: req %u resp %u\n", 629462306a36Sopenharmony_ci req_len, resp_len); 629562306a36Sopenharmony_ci return -EINVAL; 629662306a36Sopenharmony_ci } 629762306a36Sopenharmony_ci if (req_len) { 629862306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies", 629962306a36Sopenharmony_ci cfg->extra_buf, 630062306a36Sopenharmony_ci WL_ASSOC_INFO_MAX); 630162306a36Sopenharmony_ci if (err) { 630262306a36Sopenharmony_ci bphy_err(drvr, "could not get assoc req (%d)\n", err); 630362306a36Sopenharmony_ci return err; 630462306a36Sopenharmony_ci } 630562306a36Sopenharmony_ci conn_info->req_ie_len = req_len; 630662306a36Sopenharmony_ci conn_info->req_ie = 630762306a36Sopenharmony_ci kmemdup(cfg->extra_buf, conn_info->req_ie_len, 630862306a36Sopenharmony_ci GFP_KERNEL); 630962306a36Sopenharmony_ci if (!conn_info->req_ie) 631062306a36Sopenharmony_ci conn_info->req_ie_len = 0; 631162306a36Sopenharmony_ci } else { 631262306a36Sopenharmony_ci conn_info->req_ie_len = 0; 631362306a36Sopenharmony_ci conn_info->req_ie = NULL; 631462306a36Sopenharmony_ci } 631562306a36Sopenharmony_ci if (resp_len) { 631662306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies", 631762306a36Sopenharmony_ci cfg->extra_buf, 631862306a36Sopenharmony_ci WL_ASSOC_INFO_MAX); 631962306a36Sopenharmony_ci if (err) { 632062306a36Sopenharmony_ci bphy_err(drvr, "could not get assoc resp (%d)\n", err); 632162306a36Sopenharmony_ci return err; 632262306a36Sopenharmony_ci } 632362306a36Sopenharmony_ci conn_info->resp_ie_len = resp_len; 632462306a36Sopenharmony_ci conn_info->resp_ie = 632562306a36Sopenharmony_ci kmemdup(cfg->extra_buf, conn_info->resp_ie_len, 632662306a36Sopenharmony_ci GFP_KERNEL); 632762306a36Sopenharmony_ci if (!conn_info->resp_ie) 632862306a36Sopenharmony_ci conn_info->resp_ie_len = 0; 632962306a36Sopenharmony_ci 633062306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "wme_ac_sta", 633162306a36Sopenharmony_ci edcf_acparam_info, 633262306a36Sopenharmony_ci sizeof(edcf_acparam_info)); 633362306a36Sopenharmony_ci if (err) { 633462306a36Sopenharmony_ci brcmf_err("could not get wme_ac_sta (%d)\n", err); 633562306a36Sopenharmony_ci return err; 633662306a36Sopenharmony_ci } 633762306a36Sopenharmony_ci 633862306a36Sopenharmony_ci brcmf_wifi_prioritize_acparams(edcf_acparam_info, 633962306a36Sopenharmony_ci cfg->ac_priority); 634062306a36Sopenharmony_ci } else { 634162306a36Sopenharmony_ci conn_info->resp_ie_len = 0; 634262306a36Sopenharmony_ci conn_info->resp_ie = NULL; 634362306a36Sopenharmony_ci } 634462306a36Sopenharmony_ci brcmf_dbg(CONN, "req len (%d) resp len (%d)\n", 634562306a36Sopenharmony_ci conn_info->req_ie_len, conn_info->resp_ie_len); 634662306a36Sopenharmony_ci 634762306a36Sopenharmony_ci return err; 634862306a36Sopenharmony_ci} 634962306a36Sopenharmony_ci 635062306a36Sopenharmony_cistatic s32 635162306a36Sopenharmony_cibrcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg, 635262306a36Sopenharmony_ci struct net_device *ndev, 635362306a36Sopenharmony_ci const struct brcmf_event_msg *e) 635462306a36Sopenharmony_ci{ 635562306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 635662306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 635762306a36Sopenharmony_ci struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); 635862306a36Sopenharmony_ci struct wiphy *wiphy = cfg_to_wiphy(cfg); 635962306a36Sopenharmony_ci struct ieee80211_channel *notify_channel = NULL; 636062306a36Sopenharmony_ci struct ieee80211_supported_band *band; 636162306a36Sopenharmony_ci struct brcmf_bss_info_le *bi; 636262306a36Sopenharmony_ci struct brcmu_chan ch; 636362306a36Sopenharmony_ci struct cfg80211_roam_info roam_info = {}; 636462306a36Sopenharmony_ci u32 freq; 636562306a36Sopenharmony_ci s32 err = 0; 636662306a36Sopenharmony_ci u8 *buf; 636762306a36Sopenharmony_ci 636862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 636962306a36Sopenharmony_ci 637062306a36Sopenharmony_ci brcmf_get_assoc_ies(cfg, ifp); 637162306a36Sopenharmony_ci memcpy(profile->bssid, e->addr, ETH_ALEN); 637262306a36Sopenharmony_ci brcmf_update_bss_info(cfg, ifp); 637362306a36Sopenharmony_ci 637462306a36Sopenharmony_ci buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL); 637562306a36Sopenharmony_ci if (buf == NULL) { 637662306a36Sopenharmony_ci err = -ENOMEM; 637762306a36Sopenharmony_ci goto done; 637862306a36Sopenharmony_ci } 637962306a36Sopenharmony_ci 638062306a36Sopenharmony_ci /* data sent to dongle has to be little endian */ 638162306a36Sopenharmony_ci *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX); 638262306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, 638362306a36Sopenharmony_ci buf, WL_BSS_INFO_MAX); 638462306a36Sopenharmony_ci 638562306a36Sopenharmony_ci if (err) 638662306a36Sopenharmony_ci goto done; 638762306a36Sopenharmony_ci 638862306a36Sopenharmony_ci bi = (struct brcmf_bss_info_le *)(buf + 4); 638962306a36Sopenharmony_ci ch.chspec = le16_to_cpu(bi->chanspec); 639062306a36Sopenharmony_ci cfg->d11inf.decchspec(&ch); 639162306a36Sopenharmony_ci 639262306a36Sopenharmony_ci if (ch.band == BRCMU_CHAN_BAND_2G) 639362306a36Sopenharmony_ci band = wiphy->bands[NL80211_BAND_2GHZ]; 639462306a36Sopenharmony_ci else 639562306a36Sopenharmony_ci band = wiphy->bands[NL80211_BAND_5GHZ]; 639662306a36Sopenharmony_ci 639762306a36Sopenharmony_ci freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band); 639862306a36Sopenharmony_ci notify_channel = ieee80211_get_channel(wiphy, freq); 639962306a36Sopenharmony_ci 640062306a36Sopenharmony_cidone: 640162306a36Sopenharmony_ci kfree(buf); 640262306a36Sopenharmony_ci 640362306a36Sopenharmony_ci roam_info.links[0].channel = notify_channel; 640462306a36Sopenharmony_ci roam_info.links[0].bssid = profile->bssid; 640562306a36Sopenharmony_ci roam_info.req_ie = conn_info->req_ie; 640662306a36Sopenharmony_ci roam_info.req_ie_len = conn_info->req_ie_len; 640762306a36Sopenharmony_ci roam_info.resp_ie = conn_info->resp_ie; 640862306a36Sopenharmony_ci roam_info.resp_ie_len = conn_info->resp_ie_len; 640962306a36Sopenharmony_ci 641062306a36Sopenharmony_ci cfg80211_roamed(ndev, &roam_info, GFP_KERNEL); 641162306a36Sopenharmony_ci brcmf_dbg(CONN, "Report roaming result\n"); 641262306a36Sopenharmony_ci 641362306a36Sopenharmony_ci if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) { 641462306a36Sopenharmony_ci cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL); 641562306a36Sopenharmony_ci brcmf_dbg(CONN, "Report port authorized\n"); 641662306a36Sopenharmony_ci } 641762306a36Sopenharmony_ci 641862306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state); 641962306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 642062306a36Sopenharmony_ci return err; 642162306a36Sopenharmony_ci} 642262306a36Sopenharmony_ci 642362306a36Sopenharmony_cistatic s32 642462306a36Sopenharmony_cibrcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg, 642562306a36Sopenharmony_ci struct net_device *ndev, const struct brcmf_event_msg *e, 642662306a36Sopenharmony_ci bool completed) 642762306a36Sopenharmony_ci{ 642862306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 642962306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 643062306a36Sopenharmony_ci struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg); 643162306a36Sopenharmony_ci struct cfg80211_connect_resp_params conn_params; 643262306a36Sopenharmony_ci 643362306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter\n"); 643462306a36Sopenharmony_ci 643562306a36Sopenharmony_ci if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING, 643662306a36Sopenharmony_ci &ifp->vif->sme_state)) { 643762306a36Sopenharmony_ci memset(&conn_params, 0, sizeof(conn_params)); 643862306a36Sopenharmony_ci if (completed) { 643962306a36Sopenharmony_ci brcmf_get_assoc_ies(cfg, ifp); 644062306a36Sopenharmony_ci brcmf_update_bss_info(cfg, ifp); 644162306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_CONNECTED, 644262306a36Sopenharmony_ci &ifp->vif->sme_state); 644362306a36Sopenharmony_ci conn_params.status = WLAN_STATUS_SUCCESS; 644462306a36Sopenharmony_ci } else { 644562306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, 644662306a36Sopenharmony_ci &ifp->vif->sme_state); 644762306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, 644862306a36Sopenharmony_ci &ifp->vif->sme_state); 644962306a36Sopenharmony_ci conn_params.status = WLAN_STATUS_AUTH_TIMEOUT; 645062306a36Sopenharmony_ci } 645162306a36Sopenharmony_ci conn_params.links[0].bssid = profile->bssid; 645262306a36Sopenharmony_ci conn_params.req_ie = conn_info->req_ie; 645362306a36Sopenharmony_ci conn_params.req_ie_len = conn_info->req_ie_len; 645462306a36Sopenharmony_ci conn_params.resp_ie = conn_info->resp_ie; 645562306a36Sopenharmony_ci conn_params.resp_ie_len = conn_info->resp_ie_len; 645662306a36Sopenharmony_ci cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL); 645762306a36Sopenharmony_ci brcmf_dbg(CONN, "Report connect result - connection %s\n", 645862306a36Sopenharmony_ci completed ? "succeeded" : "failed"); 645962306a36Sopenharmony_ci } 646062306a36Sopenharmony_ci brcmf_dbg(TRACE, "Exit\n"); 646162306a36Sopenharmony_ci return 0; 646262306a36Sopenharmony_ci} 646362306a36Sopenharmony_ci 646462306a36Sopenharmony_cistatic s32 646562306a36Sopenharmony_cibrcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg, 646662306a36Sopenharmony_ci struct net_device *ndev, 646762306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 646862306a36Sopenharmony_ci{ 646962306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 647062306a36Sopenharmony_ci static int generation; 647162306a36Sopenharmony_ci u32 event = e->event_code; 647262306a36Sopenharmony_ci u32 reason = e->reason; 647362306a36Sopenharmony_ci struct station_info *sinfo; 647462306a36Sopenharmony_ci 647562306a36Sopenharmony_ci brcmf_dbg(CONN, "event %s (%u), reason %d\n", 647662306a36Sopenharmony_ci brcmf_fweh_event_name(event), event, reason); 647762306a36Sopenharmony_ci if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS && 647862306a36Sopenharmony_ci ndev != cfg_to_ndev(cfg)) { 647962306a36Sopenharmony_ci brcmf_dbg(CONN, "AP mode link down\n"); 648062306a36Sopenharmony_ci complete(&cfg->vif_disabled); 648162306a36Sopenharmony_ci return 0; 648262306a36Sopenharmony_ci } 648362306a36Sopenharmony_ci 648462306a36Sopenharmony_ci if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) && 648562306a36Sopenharmony_ci (reason == BRCMF_E_STATUS_SUCCESS)) { 648662306a36Sopenharmony_ci if (!data) { 648762306a36Sopenharmony_ci bphy_err(drvr, "No IEs present in ASSOC/REASSOC_IND\n"); 648862306a36Sopenharmony_ci return -EINVAL; 648962306a36Sopenharmony_ci } 649062306a36Sopenharmony_ci 649162306a36Sopenharmony_ci sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL); 649262306a36Sopenharmony_ci if (!sinfo) 649362306a36Sopenharmony_ci return -ENOMEM; 649462306a36Sopenharmony_ci 649562306a36Sopenharmony_ci sinfo->assoc_req_ies = data; 649662306a36Sopenharmony_ci sinfo->assoc_req_ies_len = e->datalen; 649762306a36Sopenharmony_ci generation++; 649862306a36Sopenharmony_ci sinfo->generation = generation; 649962306a36Sopenharmony_ci cfg80211_new_sta(ndev, e->addr, sinfo, GFP_KERNEL); 650062306a36Sopenharmony_ci 650162306a36Sopenharmony_ci kfree(sinfo); 650262306a36Sopenharmony_ci } else if ((event == BRCMF_E_DISASSOC_IND) || 650362306a36Sopenharmony_ci (event == BRCMF_E_DEAUTH_IND) || 650462306a36Sopenharmony_ci (event == BRCMF_E_DEAUTH)) { 650562306a36Sopenharmony_ci cfg80211_del_sta(ndev, e->addr, GFP_KERNEL); 650662306a36Sopenharmony_ci } 650762306a36Sopenharmony_ci return 0; 650862306a36Sopenharmony_ci} 650962306a36Sopenharmony_ci 651062306a36Sopenharmony_cistatic s32 651162306a36Sopenharmony_cibrcmf_notify_connect_status(struct brcmf_if *ifp, 651262306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 651362306a36Sopenharmony_ci{ 651462306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = ifp->drvr->config; 651562306a36Sopenharmony_ci struct net_device *ndev = ifp->ndev; 651662306a36Sopenharmony_ci struct brcmf_cfg80211_profile *profile = &ifp->vif->profile; 651762306a36Sopenharmony_ci struct ieee80211_channel *chan; 651862306a36Sopenharmony_ci s32 err = 0; 651962306a36Sopenharmony_ci 652062306a36Sopenharmony_ci if ((e->event_code == BRCMF_E_DEAUTH) || 652162306a36Sopenharmony_ci (e->event_code == BRCMF_E_DEAUTH_IND) || 652262306a36Sopenharmony_ci (e->event_code == BRCMF_E_DISASSOC_IND) || 652362306a36Sopenharmony_ci ((e->event_code == BRCMF_E_LINK) && (!e->flags))) { 652462306a36Sopenharmony_ci brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr); 652562306a36Sopenharmony_ci } 652662306a36Sopenharmony_ci 652762306a36Sopenharmony_ci if (brcmf_is_apmode(ifp->vif)) { 652862306a36Sopenharmony_ci err = brcmf_notify_connect_status_ap(cfg, ndev, e, data); 652962306a36Sopenharmony_ci } else if (brcmf_is_linkup(ifp->vif, e)) { 653062306a36Sopenharmony_ci brcmf_dbg(CONN, "Linkup\n"); 653162306a36Sopenharmony_ci if (brcmf_is_ibssmode(ifp->vif)) { 653262306a36Sopenharmony_ci brcmf_inform_ibss(cfg, ndev, e->addr); 653362306a36Sopenharmony_ci chan = ieee80211_get_channel(cfg->wiphy, cfg->channel); 653462306a36Sopenharmony_ci memcpy(profile->bssid, e->addr, ETH_ALEN); 653562306a36Sopenharmony_ci cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL); 653662306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_CONNECTING, 653762306a36Sopenharmony_ci &ifp->vif->sme_state); 653862306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_CONNECTED, 653962306a36Sopenharmony_ci &ifp->vif->sme_state); 654062306a36Sopenharmony_ci } else 654162306a36Sopenharmony_ci brcmf_bss_connect_done(cfg, ndev, e, true); 654262306a36Sopenharmony_ci brcmf_net_setcarrier(ifp, true); 654362306a36Sopenharmony_ci } else if (brcmf_is_linkdown(ifp->vif, e)) { 654462306a36Sopenharmony_ci brcmf_dbg(CONN, "Linkdown\n"); 654562306a36Sopenharmony_ci if (!brcmf_is_ibssmode(ifp->vif) && 654662306a36Sopenharmony_ci (test_bit(BRCMF_VIF_STATUS_CONNECTED, 654762306a36Sopenharmony_ci &ifp->vif->sme_state) || 654862306a36Sopenharmony_ci test_bit(BRCMF_VIF_STATUS_CONNECTING, 654962306a36Sopenharmony_ci &ifp->vif->sme_state))) { 655062306a36Sopenharmony_ci if (test_bit(BRCMF_VIF_STATUS_CONNECTED, 655162306a36Sopenharmony_ci &ifp->vif->sme_state) && 655262306a36Sopenharmony_ci memcmp(profile->bssid, e->addr, ETH_ALEN)) 655362306a36Sopenharmony_ci return err; 655462306a36Sopenharmony_ci 655562306a36Sopenharmony_ci brcmf_bss_connect_done(cfg, ndev, e, false); 655662306a36Sopenharmony_ci brcmf_link_down(ifp->vif, 655762306a36Sopenharmony_ci brcmf_map_fw_linkdown_reason(e), 655862306a36Sopenharmony_ci e->event_code & 655962306a36Sopenharmony_ci (BRCMF_E_DEAUTH_IND | 656062306a36Sopenharmony_ci BRCMF_E_DISASSOC_IND) 656162306a36Sopenharmony_ci ? false : true); 656262306a36Sopenharmony_ci brcmf_init_prof(ndev_to_prof(ndev)); 656362306a36Sopenharmony_ci if (ndev != cfg_to_ndev(cfg)) 656462306a36Sopenharmony_ci complete(&cfg->vif_disabled); 656562306a36Sopenharmony_ci brcmf_net_setcarrier(ifp, false); 656662306a36Sopenharmony_ci } 656762306a36Sopenharmony_ci } else if (brcmf_is_nonetwork(cfg, e)) { 656862306a36Sopenharmony_ci if (brcmf_is_ibssmode(ifp->vif)) 656962306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_CONNECTING, 657062306a36Sopenharmony_ci &ifp->vif->sme_state); 657162306a36Sopenharmony_ci else 657262306a36Sopenharmony_ci brcmf_bss_connect_done(cfg, ndev, e, false); 657362306a36Sopenharmony_ci } 657462306a36Sopenharmony_ci 657562306a36Sopenharmony_ci return err; 657662306a36Sopenharmony_ci} 657762306a36Sopenharmony_ci 657862306a36Sopenharmony_cistatic s32 657962306a36Sopenharmony_cibrcmf_notify_roaming_status(struct brcmf_if *ifp, 658062306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 658162306a36Sopenharmony_ci{ 658262306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = ifp->drvr->config; 658362306a36Sopenharmony_ci u32 event = e->event_code; 658462306a36Sopenharmony_ci u32 status = e->status; 658562306a36Sopenharmony_ci 658662306a36Sopenharmony_ci if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) { 658762306a36Sopenharmony_ci if (test_bit(BRCMF_VIF_STATUS_CONNECTED, 658862306a36Sopenharmony_ci &ifp->vif->sme_state)) { 658962306a36Sopenharmony_ci brcmf_bss_roaming_done(cfg, ifp->ndev, e); 659062306a36Sopenharmony_ci } else { 659162306a36Sopenharmony_ci brcmf_bss_connect_done(cfg, ifp->ndev, e, true); 659262306a36Sopenharmony_ci brcmf_net_setcarrier(ifp, true); 659362306a36Sopenharmony_ci } 659462306a36Sopenharmony_ci } 659562306a36Sopenharmony_ci 659662306a36Sopenharmony_ci return 0; 659762306a36Sopenharmony_ci} 659862306a36Sopenharmony_ci 659962306a36Sopenharmony_cistatic s32 660062306a36Sopenharmony_cibrcmf_notify_mic_status(struct brcmf_if *ifp, 660162306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 660262306a36Sopenharmony_ci{ 660362306a36Sopenharmony_ci u16 flags = e->flags; 660462306a36Sopenharmony_ci enum nl80211_key_type key_type; 660562306a36Sopenharmony_ci 660662306a36Sopenharmony_ci if (flags & BRCMF_EVENT_MSG_GROUP) 660762306a36Sopenharmony_ci key_type = NL80211_KEYTYPE_GROUP; 660862306a36Sopenharmony_ci else 660962306a36Sopenharmony_ci key_type = NL80211_KEYTYPE_PAIRWISE; 661062306a36Sopenharmony_ci 661162306a36Sopenharmony_ci cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1, 661262306a36Sopenharmony_ci NULL, GFP_KERNEL); 661362306a36Sopenharmony_ci 661462306a36Sopenharmony_ci return 0; 661562306a36Sopenharmony_ci} 661662306a36Sopenharmony_ci 661762306a36Sopenharmony_cistatic s32 brcmf_notify_rssi(struct brcmf_if *ifp, 661862306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 661962306a36Sopenharmony_ci{ 662062306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif = ifp->vif; 662162306a36Sopenharmony_ci struct brcmf_rssi_be *info = data; 662262306a36Sopenharmony_ci s32 rssi, snr = 0, noise = 0; 662362306a36Sopenharmony_ci s32 low, high, last; 662462306a36Sopenharmony_ci 662562306a36Sopenharmony_ci if (e->datalen >= sizeof(*info)) { 662662306a36Sopenharmony_ci rssi = be32_to_cpu(info->rssi); 662762306a36Sopenharmony_ci snr = be32_to_cpu(info->snr); 662862306a36Sopenharmony_ci noise = be32_to_cpu(info->noise); 662962306a36Sopenharmony_ci } else if (e->datalen >= sizeof(rssi)) { 663062306a36Sopenharmony_ci rssi = be32_to_cpu(*(__be32 *)data); 663162306a36Sopenharmony_ci } else { 663262306a36Sopenharmony_ci brcmf_err("insufficient RSSI event data\n"); 663362306a36Sopenharmony_ci return 0; 663462306a36Sopenharmony_ci } 663562306a36Sopenharmony_ci 663662306a36Sopenharmony_ci low = vif->cqm_rssi_low; 663762306a36Sopenharmony_ci high = vif->cqm_rssi_high; 663862306a36Sopenharmony_ci last = vif->cqm_rssi_last; 663962306a36Sopenharmony_ci 664062306a36Sopenharmony_ci brcmf_dbg(TRACE, "rssi=%d snr=%d noise=%d low=%d high=%d last=%d\n", 664162306a36Sopenharmony_ci rssi, snr, noise, low, high, last); 664262306a36Sopenharmony_ci 664362306a36Sopenharmony_ci vif->cqm_rssi_last = rssi; 664462306a36Sopenharmony_ci 664562306a36Sopenharmony_ci if (rssi <= low || rssi == 0) { 664662306a36Sopenharmony_ci brcmf_dbg(INFO, "LOW rssi=%d\n", rssi); 664762306a36Sopenharmony_ci cfg80211_cqm_rssi_notify(ifp->ndev, 664862306a36Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, 664962306a36Sopenharmony_ci rssi, GFP_KERNEL); 665062306a36Sopenharmony_ci } else if (rssi > high) { 665162306a36Sopenharmony_ci brcmf_dbg(INFO, "HIGH rssi=%d\n", rssi); 665262306a36Sopenharmony_ci cfg80211_cqm_rssi_notify(ifp->ndev, 665362306a36Sopenharmony_ci NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, 665462306a36Sopenharmony_ci rssi, GFP_KERNEL); 665562306a36Sopenharmony_ci } 665662306a36Sopenharmony_ci 665762306a36Sopenharmony_ci return 0; 665862306a36Sopenharmony_ci} 665962306a36Sopenharmony_ci 666062306a36Sopenharmony_cistatic s32 brcmf_notify_vif_event(struct brcmf_if *ifp, 666162306a36Sopenharmony_ci const struct brcmf_event_msg *e, void *data) 666262306a36Sopenharmony_ci{ 666362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = ifp->drvr->config; 666462306a36Sopenharmony_ci struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data; 666562306a36Sopenharmony_ci struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; 666662306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 666762306a36Sopenharmony_ci 666862306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n", 666962306a36Sopenharmony_ci ifevent->action, ifevent->flags, ifevent->ifidx, 667062306a36Sopenharmony_ci ifevent->bsscfgidx); 667162306a36Sopenharmony_ci 667262306a36Sopenharmony_ci spin_lock(&event->vif_event_lock); 667362306a36Sopenharmony_ci event->action = ifevent->action; 667462306a36Sopenharmony_ci vif = event->vif; 667562306a36Sopenharmony_ci 667662306a36Sopenharmony_ci switch (ifevent->action) { 667762306a36Sopenharmony_ci case BRCMF_E_IF_ADD: 667862306a36Sopenharmony_ci /* waiting process may have timed out */ 667962306a36Sopenharmony_ci if (!cfg->vif_event.vif) { 668062306a36Sopenharmony_ci spin_unlock(&event->vif_event_lock); 668162306a36Sopenharmony_ci return -EBADF; 668262306a36Sopenharmony_ci } 668362306a36Sopenharmony_ci 668462306a36Sopenharmony_ci ifp->vif = vif; 668562306a36Sopenharmony_ci vif->ifp = ifp; 668662306a36Sopenharmony_ci if (ifp->ndev) { 668762306a36Sopenharmony_ci vif->wdev.netdev = ifp->ndev; 668862306a36Sopenharmony_ci ifp->ndev->ieee80211_ptr = &vif->wdev; 668962306a36Sopenharmony_ci SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy)); 669062306a36Sopenharmony_ci } 669162306a36Sopenharmony_ci spin_unlock(&event->vif_event_lock); 669262306a36Sopenharmony_ci wake_up(&event->vif_wq); 669362306a36Sopenharmony_ci return 0; 669462306a36Sopenharmony_ci 669562306a36Sopenharmony_ci case BRCMF_E_IF_DEL: 669662306a36Sopenharmony_ci spin_unlock(&event->vif_event_lock); 669762306a36Sopenharmony_ci /* event may not be upon user request */ 669862306a36Sopenharmony_ci if (brcmf_cfg80211_vif_event_armed(cfg)) 669962306a36Sopenharmony_ci wake_up(&event->vif_wq); 670062306a36Sopenharmony_ci return 0; 670162306a36Sopenharmony_ci 670262306a36Sopenharmony_ci case BRCMF_E_IF_CHANGE: 670362306a36Sopenharmony_ci spin_unlock(&event->vif_event_lock); 670462306a36Sopenharmony_ci wake_up(&event->vif_wq); 670562306a36Sopenharmony_ci return 0; 670662306a36Sopenharmony_ci 670762306a36Sopenharmony_ci default: 670862306a36Sopenharmony_ci spin_unlock(&event->vif_event_lock); 670962306a36Sopenharmony_ci break; 671062306a36Sopenharmony_ci } 671162306a36Sopenharmony_ci return -EINVAL; 671262306a36Sopenharmony_ci} 671362306a36Sopenharmony_ci 671462306a36Sopenharmony_cistatic void brcmf_init_conf(struct brcmf_cfg80211_conf *conf) 671562306a36Sopenharmony_ci{ 671662306a36Sopenharmony_ci conf->frag_threshold = (u32)-1; 671762306a36Sopenharmony_ci conf->rts_threshold = (u32)-1; 671862306a36Sopenharmony_ci conf->retry_short = (u32)-1; 671962306a36Sopenharmony_ci conf->retry_long = (u32)-1; 672062306a36Sopenharmony_ci} 672162306a36Sopenharmony_ci 672262306a36Sopenharmony_cistatic void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg) 672362306a36Sopenharmony_ci{ 672462306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_LINK, 672562306a36Sopenharmony_ci brcmf_notify_connect_status); 672662306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND, 672762306a36Sopenharmony_ci brcmf_notify_connect_status); 672862306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH, 672962306a36Sopenharmony_ci brcmf_notify_connect_status); 673062306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND, 673162306a36Sopenharmony_ci brcmf_notify_connect_status); 673262306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND, 673362306a36Sopenharmony_ci brcmf_notify_connect_status); 673462306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND, 673562306a36Sopenharmony_ci brcmf_notify_connect_status); 673662306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM, 673762306a36Sopenharmony_ci brcmf_notify_roaming_status); 673862306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR, 673962306a36Sopenharmony_ci brcmf_notify_mic_status); 674062306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID, 674162306a36Sopenharmony_ci brcmf_notify_connect_status); 674262306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND, 674362306a36Sopenharmony_ci brcmf_notify_sched_scan_results); 674462306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_IF, 674562306a36Sopenharmony_ci brcmf_notify_vif_event); 674662306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG, 674762306a36Sopenharmony_ci brcmf_p2p_notify_rx_mgmt_p2p_probereq); 674862306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE, 674962306a36Sopenharmony_ci brcmf_p2p_notify_listen_complete); 675062306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX, 675162306a36Sopenharmony_ci brcmf_p2p_notify_action_frame_rx); 675262306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE, 675362306a36Sopenharmony_ci brcmf_p2p_notify_action_tx_complete); 675462306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE, 675562306a36Sopenharmony_ci brcmf_p2p_notify_action_tx_complete); 675662306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP, 675762306a36Sopenharmony_ci brcmf_notify_connect_status); 675862306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi); 675962306a36Sopenharmony_ci} 676062306a36Sopenharmony_ci 676162306a36Sopenharmony_cistatic void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg) 676262306a36Sopenharmony_ci{ 676362306a36Sopenharmony_ci kfree(cfg->conf); 676462306a36Sopenharmony_ci cfg->conf = NULL; 676562306a36Sopenharmony_ci kfree(cfg->extra_buf); 676662306a36Sopenharmony_ci cfg->extra_buf = NULL; 676762306a36Sopenharmony_ci kfree(cfg->wowl.nd); 676862306a36Sopenharmony_ci cfg->wowl.nd = NULL; 676962306a36Sopenharmony_ci kfree(cfg->wowl.nd_info); 677062306a36Sopenharmony_ci cfg->wowl.nd_info = NULL; 677162306a36Sopenharmony_ci kfree(cfg->escan_info.escan_buf); 677262306a36Sopenharmony_ci cfg->escan_info.escan_buf = NULL; 677362306a36Sopenharmony_ci} 677462306a36Sopenharmony_ci 677562306a36Sopenharmony_cistatic s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg) 677662306a36Sopenharmony_ci{ 677762306a36Sopenharmony_ci cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL); 677862306a36Sopenharmony_ci if (!cfg->conf) 677962306a36Sopenharmony_ci goto init_priv_mem_out; 678062306a36Sopenharmony_ci cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL); 678162306a36Sopenharmony_ci if (!cfg->extra_buf) 678262306a36Sopenharmony_ci goto init_priv_mem_out; 678362306a36Sopenharmony_ci cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL); 678462306a36Sopenharmony_ci if (!cfg->wowl.nd) 678562306a36Sopenharmony_ci goto init_priv_mem_out; 678662306a36Sopenharmony_ci cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) + 678762306a36Sopenharmony_ci sizeof(struct cfg80211_wowlan_nd_match *), 678862306a36Sopenharmony_ci GFP_KERNEL); 678962306a36Sopenharmony_ci if (!cfg->wowl.nd_info) 679062306a36Sopenharmony_ci goto init_priv_mem_out; 679162306a36Sopenharmony_ci cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL); 679262306a36Sopenharmony_ci if (!cfg->escan_info.escan_buf) 679362306a36Sopenharmony_ci goto init_priv_mem_out; 679462306a36Sopenharmony_ci 679562306a36Sopenharmony_ci return 0; 679662306a36Sopenharmony_ci 679762306a36Sopenharmony_ciinit_priv_mem_out: 679862306a36Sopenharmony_ci brcmf_deinit_priv_mem(cfg); 679962306a36Sopenharmony_ci 680062306a36Sopenharmony_ci return -ENOMEM; 680162306a36Sopenharmony_ci} 680262306a36Sopenharmony_ci 680362306a36Sopenharmony_cistatic s32 wl_init_priv(struct brcmf_cfg80211_info *cfg) 680462306a36Sopenharmony_ci{ 680562306a36Sopenharmony_ci s32 err = 0; 680662306a36Sopenharmony_ci 680762306a36Sopenharmony_ci cfg->scan_request = NULL; 680862306a36Sopenharmony_ci cfg->pwr_save = true; 680962306a36Sopenharmony_ci cfg->dongle_up = false; /* dongle is not up yet */ 681062306a36Sopenharmony_ci err = brcmf_init_priv_mem(cfg); 681162306a36Sopenharmony_ci if (err) 681262306a36Sopenharmony_ci return err; 681362306a36Sopenharmony_ci brcmf_register_event_handlers(cfg); 681462306a36Sopenharmony_ci mutex_init(&cfg->usr_sync); 681562306a36Sopenharmony_ci brcmf_init_escan(cfg); 681662306a36Sopenharmony_ci brcmf_init_conf(cfg->conf); 681762306a36Sopenharmony_ci brcmf_init_wmm_prio(cfg->ac_priority); 681862306a36Sopenharmony_ci init_completion(&cfg->vif_disabled); 681962306a36Sopenharmony_ci return err; 682062306a36Sopenharmony_ci} 682162306a36Sopenharmony_ci 682262306a36Sopenharmony_cistatic void wl_deinit_priv(struct brcmf_cfg80211_info *cfg) 682362306a36Sopenharmony_ci{ 682462306a36Sopenharmony_ci cfg->dongle_up = false; /* dongle down */ 682562306a36Sopenharmony_ci brcmf_abort_scanning(cfg); 682662306a36Sopenharmony_ci brcmf_deinit_priv_mem(cfg); 682762306a36Sopenharmony_ci brcmf_clear_assoc_ies(cfg); 682862306a36Sopenharmony_ci} 682962306a36Sopenharmony_ci 683062306a36Sopenharmony_cistatic void init_vif_event(struct brcmf_cfg80211_vif_event *event) 683162306a36Sopenharmony_ci{ 683262306a36Sopenharmony_ci init_waitqueue_head(&event->vif_wq); 683362306a36Sopenharmony_ci spin_lock_init(&event->vif_event_lock); 683462306a36Sopenharmony_ci} 683562306a36Sopenharmony_ci 683662306a36Sopenharmony_cistatic s32 brcmf_dongle_roam(struct brcmf_if *ifp) 683762306a36Sopenharmony_ci{ 683862306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 683962306a36Sopenharmony_ci s32 err; 684062306a36Sopenharmony_ci u32 bcn_timeout; 684162306a36Sopenharmony_ci __le32 roamtrigger[2]; 684262306a36Sopenharmony_ci __le32 roam_delta[2]; 684362306a36Sopenharmony_ci 684462306a36Sopenharmony_ci /* Configure beacon timeout value based upon roaming setting */ 684562306a36Sopenharmony_ci if (ifp->drvr->settings->roamoff) 684662306a36Sopenharmony_ci bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF; 684762306a36Sopenharmony_ci else 684862306a36Sopenharmony_ci bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON; 684962306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout); 685062306a36Sopenharmony_ci if (err) { 685162306a36Sopenharmony_ci bphy_err(drvr, "bcn_timeout error (%d)\n", err); 685262306a36Sopenharmony_ci goto roam_setup_done; 685362306a36Sopenharmony_ci } 685462306a36Sopenharmony_ci 685562306a36Sopenharmony_ci /* Enable/Disable built-in roaming to allow supplicant to take care of 685662306a36Sopenharmony_ci * roaming. 685762306a36Sopenharmony_ci */ 685862306a36Sopenharmony_ci brcmf_dbg(INFO, "Internal Roaming = %s\n", 685962306a36Sopenharmony_ci ifp->drvr->settings->roamoff ? "Off" : "On"); 686062306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "roam_off", 686162306a36Sopenharmony_ci ifp->drvr->settings->roamoff); 686262306a36Sopenharmony_ci if (err) { 686362306a36Sopenharmony_ci bphy_err(drvr, "roam_off error (%d)\n", err); 686462306a36Sopenharmony_ci goto roam_setup_done; 686562306a36Sopenharmony_ci } 686662306a36Sopenharmony_ci 686762306a36Sopenharmony_ci roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL); 686862306a36Sopenharmony_ci roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL); 686962306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER, 687062306a36Sopenharmony_ci (void *)roamtrigger, sizeof(roamtrigger)); 687162306a36Sopenharmony_ci if (err) 687262306a36Sopenharmony_ci bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err); 687362306a36Sopenharmony_ci 687462306a36Sopenharmony_ci roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA); 687562306a36Sopenharmony_ci roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL); 687662306a36Sopenharmony_ci err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA, 687762306a36Sopenharmony_ci (void *)roam_delta, sizeof(roam_delta)); 687862306a36Sopenharmony_ci if (err) 687962306a36Sopenharmony_ci bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err); 688062306a36Sopenharmony_ci 688162306a36Sopenharmony_ci return 0; 688262306a36Sopenharmony_ci 688362306a36Sopenharmony_ciroam_setup_done: 688462306a36Sopenharmony_ci return err; 688562306a36Sopenharmony_ci} 688662306a36Sopenharmony_ci 688762306a36Sopenharmony_cistatic s32 688862306a36Sopenharmony_cibrcmf_dongle_scantime(struct brcmf_if *ifp) 688962306a36Sopenharmony_ci{ 689062306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 689162306a36Sopenharmony_ci s32 err = 0; 689262306a36Sopenharmony_ci 689362306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME, 689462306a36Sopenharmony_ci BRCMF_SCAN_CHANNEL_TIME); 689562306a36Sopenharmony_ci if (err) { 689662306a36Sopenharmony_ci bphy_err(drvr, "Scan assoc time error (%d)\n", err); 689762306a36Sopenharmony_ci goto dongle_scantime_out; 689862306a36Sopenharmony_ci } 689962306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME, 690062306a36Sopenharmony_ci BRCMF_SCAN_UNASSOC_TIME); 690162306a36Sopenharmony_ci if (err) { 690262306a36Sopenharmony_ci bphy_err(drvr, "Scan unassoc time error (%d)\n", err); 690362306a36Sopenharmony_ci goto dongle_scantime_out; 690462306a36Sopenharmony_ci } 690562306a36Sopenharmony_ci 690662306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME, 690762306a36Sopenharmony_ci BRCMF_SCAN_PASSIVE_TIME); 690862306a36Sopenharmony_ci if (err) { 690962306a36Sopenharmony_ci bphy_err(drvr, "Scan passive time error (%d)\n", err); 691062306a36Sopenharmony_ci goto dongle_scantime_out; 691162306a36Sopenharmony_ci } 691262306a36Sopenharmony_ci 691362306a36Sopenharmony_cidongle_scantime_out: 691462306a36Sopenharmony_ci return err; 691562306a36Sopenharmony_ci} 691662306a36Sopenharmony_ci 691762306a36Sopenharmony_cistatic void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel, 691862306a36Sopenharmony_ci struct brcmu_chan *ch) 691962306a36Sopenharmony_ci{ 692062306a36Sopenharmony_ci u32 ht40_flag; 692162306a36Sopenharmony_ci 692262306a36Sopenharmony_ci ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40; 692362306a36Sopenharmony_ci if (ch->sb == BRCMU_CHAN_SB_U) { 692462306a36Sopenharmony_ci if (ht40_flag == IEEE80211_CHAN_NO_HT40) 692562306a36Sopenharmony_ci channel->flags &= ~IEEE80211_CHAN_NO_HT40; 692662306a36Sopenharmony_ci channel->flags |= IEEE80211_CHAN_NO_HT40PLUS; 692762306a36Sopenharmony_ci } else { 692862306a36Sopenharmony_ci /* It should be one of 692962306a36Sopenharmony_ci * IEEE80211_CHAN_NO_HT40 or 693062306a36Sopenharmony_ci * IEEE80211_CHAN_NO_HT40PLUS 693162306a36Sopenharmony_ci */ 693262306a36Sopenharmony_ci channel->flags &= ~IEEE80211_CHAN_NO_HT40; 693362306a36Sopenharmony_ci if (ht40_flag == IEEE80211_CHAN_NO_HT40) 693462306a36Sopenharmony_ci channel->flags |= IEEE80211_CHAN_NO_HT40MINUS; 693562306a36Sopenharmony_ci } 693662306a36Sopenharmony_ci} 693762306a36Sopenharmony_ci 693862306a36Sopenharmony_cistatic int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg, 693962306a36Sopenharmony_ci u32 bw_cap[]) 694062306a36Sopenharmony_ci{ 694162306a36Sopenharmony_ci struct wiphy *wiphy = cfg_to_wiphy(cfg); 694262306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 694362306a36Sopenharmony_ci struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); 694462306a36Sopenharmony_ci struct ieee80211_supported_band *band; 694562306a36Sopenharmony_ci struct ieee80211_channel *channel; 694662306a36Sopenharmony_ci struct brcmf_chanspec_list *list; 694762306a36Sopenharmony_ci struct brcmu_chan ch; 694862306a36Sopenharmony_ci int err; 694962306a36Sopenharmony_ci u8 *pbuf; 695062306a36Sopenharmony_ci u32 i, j; 695162306a36Sopenharmony_ci u32 total; 695262306a36Sopenharmony_ci u32 chaninfo; 695362306a36Sopenharmony_ci 695462306a36Sopenharmony_ci pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); 695562306a36Sopenharmony_ci 695662306a36Sopenharmony_ci if (pbuf == NULL) 695762306a36Sopenharmony_ci return -ENOMEM; 695862306a36Sopenharmony_ci 695962306a36Sopenharmony_ci list = (struct brcmf_chanspec_list *)pbuf; 696062306a36Sopenharmony_ci 696162306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, 696262306a36Sopenharmony_ci BRCMF_DCMD_MEDLEN); 696362306a36Sopenharmony_ci if (err) { 696462306a36Sopenharmony_ci bphy_err(drvr, "get chanspecs error (%d)\n", err); 696562306a36Sopenharmony_ci goto fail_pbuf; 696662306a36Sopenharmony_ci } 696762306a36Sopenharmony_ci 696862306a36Sopenharmony_ci band = wiphy->bands[NL80211_BAND_2GHZ]; 696962306a36Sopenharmony_ci if (band) 697062306a36Sopenharmony_ci for (i = 0; i < band->n_channels; i++) 697162306a36Sopenharmony_ci band->channels[i].flags = IEEE80211_CHAN_DISABLED; 697262306a36Sopenharmony_ci band = wiphy->bands[NL80211_BAND_5GHZ]; 697362306a36Sopenharmony_ci if (band) 697462306a36Sopenharmony_ci for (i = 0; i < band->n_channels; i++) 697562306a36Sopenharmony_ci band->channels[i].flags = IEEE80211_CHAN_DISABLED; 697662306a36Sopenharmony_ci 697762306a36Sopenharmony_ci total = le32_to_cpu(list->count); 697862306a36Sopenharmony_ci if (total > BRCMF_MAX_CHANSPEC_LIST) { 697962306a36Sopenharmony_ci bphy_err(drvr, "Invalid count of channel Spec. (%u)\n", 698062306a36Sopenharmony_ci total); 698162306a36Sopenharmony_ci err = -EINVAL; 698262306a36Sopenharmony_ci goto fail_pbuf; 698362306a36Sopenharmony_ci } 698462306a36Sopenharmony_ci 698562306a36Sopenharmony_ci for (i = 0; i < total; i++) { 698662306a36Sopenharmony_ci ch.chspec = (u16)le32_to_cpu(list->element[i]); 698762306a36Sopenharmony_ci cfg->d11inf.decchspec(&ch); 698862306a36Sopenharmony_ci 698962306a36Sopenharmony_ci if (ch.band == BRCMU_CHAN_BAND_2G) { 699062306a36Sopenharmony_ci band = wiphy->bands[NL80211_BAND_2GHZ]; 699162306a36Sopenharmony_ci } else if (ch.band == BRCMU_CHAN_BAND_5G) { 699262306a36Sopenharmony_ci band = wiphy->bands[NL80211_BAND_5GHZ]; 699362306a36Sopenharmony_ci } else { 699462306a36Sopenharmony_ci bphy_err(drvr, "Invalid channel Spec. 0x%x.\n", 699562306a36Sopenharmony_ci ch.chspec); 699662306a36Sopenharmony_ci continue; 699762306a36Sopenharmony_ci } 699862306a36Sopenharmony_ci if (!band) 699962306a36Sopenharmony_ci continue; 700062306a36Sopenharmony_ci if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) && 700162306a36Sopenharmony_ci ch.bw == BRCMU_CHAN_BW_40) 700262306a36Sopenharmony_ci continue; 700362306a36Sopenharmony_ci if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) && 700462306a36Sopenharmony_ci ch.bw == BRCMU_CHAN_BW_80) 700562306a36Sopenharmony_ci continue; 700662306a36Sopenharmony_ci 700762306a36Sopenharmony_ci channel = NULL; 700862306a36Sopenharmony_ci for (j = 0; j < band->n_channels; j++) { 700962306a36Sopenharmony_ci if (band->channels[j].hw_value == ch.control_ch_num) { 701062306a36Sopenharmony_ci channel = &band->channels[j]; 701162306a36Sopenharmony_ci break; 701262306a36Sopenharmony_ci } 701362306a36Sopenharmony_ci } 701462306a36Sopenharmony_ci if (!channel) { 701562306a36Sopenharmony_ci /* It seems firmware supports some channel we never 701662306a36Sopenharmony_ci * considered. Something new in IEEE standard? 701762306a36Sopenharmony_ci */ 701862306a36Sopenharmony_ci bphy_err(drvr, "Ignoring unexpected firmware channel %d\n", 701962306a36Sopenharmony_ci ch.control_ch_num); 702062306a36Sopenharmony_ci continue; 702162306a36Sopenharmony_ci } 702262306a36Sopenharmony_ci 702362306a36Sopenharmony_ci if (channel->orig_flags & IEEE80211_CHAN_DISABLED) 702462306a36Sopenharmony_ci continue; 702562306a36Sopenharmony_ci 702662306a36Sopenharmony_ci /* assuming the chanspecs order is HT20, 702762306a36Sopenharmony_ci * HT40 upper, HT40 lower, and VHT80. 702862306a36Sopenharmony_ci */ 702962306a36Sopenharmony_ci switch (ch.bw) { 703062306a36Sopenharmony_ci case BRCMU_CHAN_BW_160: 703162306a36Sopenharmony_ci channel->flags &= ~IEEE80211_CHAN_NO_160MHZ; 703262306a36Sopenharmony_ci break; 703362306a36Sopenharmony_ci case BRCMU_CHAN_BW_80: 703462306a36Sopenharmony_ci channel->flags &= ~IEEE80211_CHAN_NO_80MHZ; 703562306a36Sopenharmony_ci break; 703662306a36Sopenharmony_ci case BRCMU_CHAN_BW_40: 703762306a36Sopenharmony_ci brcmf_update_bw40_channel_flag(channel, &ch); 703862306a36Sopenharmony_ci break; 703962306a36Sopenharmony_ci default: 704062306a36Sopenharmony_ci wiphy_warn(wiphy, "Firmware reported unsupported bandwidth %d\n", 704162306a36Sopenharmony_ci ch.bw); 704262306a36Sopenharmony_ci fallthrough; 704362306a36Sopenharmony_ci case BRCMU_CHAN_BW_20: 704462306a36Sopenharmony_ci /* enable the channel and disable other bandwidths 704562306a36Sopenharmony_ci * for now as mentioned order assure they are enabled 704662306a36Sopenharmony_ci * for subsequent chanspecs. 704762306a36Sopenharmony_ci */ 704862306a36Sopenharmony_ci channel->flags = IEEE80211_CHAN_NO_HT40 | 704962306a36Sopenharmony_ci IEEE80211_CHAN_NO_80MHZ | 705062306a36Sopenharmony_ci IEEE80211_CHAN_NO_160MHZ; 705162306a36Sopenharmony_ci ch.bw = BRCMU_CHAN_BW_20; 705262306a36Sopenharmony_ci cfg->d11inf.encchspec(&ch); 705362306a36Sopenharmony_ci chaninfo = ch.chspec; 705462306a36Sopenharmony_ci err = brcmf_fil_bsscfg_int_get(ifp, "per_chan_info", 705562306a36Sopenharmony_ci &chaninfo); 705662306a36Sopenharmony_ci if (!err) { 705762306a36Sopenharmony_ci if (chaninfo & WL_CHAN_RADAR) 705862306a36Sopenharmony_ci channel->flags |= 705962306a36Sopenharmony_ci (IEEE80211_CHAN_RADAR | 706062306a36Sopenharmony_ci IEEE80211_CHAN_NO_IR); 706162306a36Sopenharmony_ci if (chaninfo & WL_CHAN_PASSIVE) 706262306a36Sopenharmony_ci channel->flags |= 706362306a36Sopenharmony_ci IEEE80211_CHAN_NO_IR; 706462306a36Sopenharmony_ci } 706562306a36Sopenharmony_ci } 706662306a36Sopenharmony_ci } 706762306a36Sopenharmony_ci 706862306a36Sopenharmony_cifail_pbuf: 706962306a36Sopenharmony_ci kfree(pbuf); 707062306a36Sopenharmony_ci return err; 707162306a36Sopenharmony_ci} 707262306a36Sopenharmony_ci 707362306a36Sopenharmony_cistatic int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg) 707462306a36Sopenharmony_ci{ 707562306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 707662306a36Sopenharmony_ci struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); 707762306a36Sopenharmony_ci struct ieee80211_supported_band *band; 707862306a36Sopenharmony_ci struct brcmf_fil_bwcap_le band_bwcap; 707962306a36Sopenharmony_ci struct brcmf_chanspec_list *list; 708062306a36Sopenharmony_ci u8 *pbuf; 708162306a36Sopenharmony_ci u32 val; 708262306a36Sopenharmony_ci int err; 708362306a36Sopenharmony_ci struct brcmu_chan ch; 708462306a36Sopenharmony_ci u32 num_chan; 708562306a36Sopenharmony_ci int i, j; 708662306a36Sopenharmony_ci 708762306a36Sopenharmony_ci /* verify support for bw_cap command */ 708862306a36Sopenharmony_ci val = WLC_BAND_5G; 708962306a36Sopenharmony_ci err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &val); 709062306a36Sopenharmony_ci 709162306a36Sopenharmony_ci if (!err) { 709262306a36Sopenharmony_ci /* only set 2G bandwidth using bw_cap command */ 709362306a36Sopenharmony_ci band_bwcap.band = cpu_to_le32(WLC_BAND_2G); 709462306a36Sopenharmony_ci band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ); 709562306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap, 709662306a36Sopenharmony_ci sizeof(band_bwcap)); 709762306a36Sopenharmony_ci } else { 709862306a36Sopenharmony_ci brcmf_dbg(INFO, "fallback to mimo_bw_cap\n"); 709962306a36Sopenharmony_ci val = WLC_N_BW_40ALL; 710062306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val); 710162306a36Sopenharmony_ci } 710262306a36Sopenharmony_ci 710362306a36Sopenharmony_ci if (!err) { 710462306a36Sopenharmony_ci /* update channel info in 2G band */ 710562306a36Sopenharmony_ci pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL); 710662306a36Sopenharmony_ci 710762306a36Sopenharmony_ci if (pbuf == NULL) 710862306a36Sopenharmony_ci return -ENOMEM; 710962306a36Sopenharmony_ci 711062306a36Sopenharmony_ci ch.band = BRCMU_CHAN_BAND_2G; 711162306a36Sopenharmony_ci ch.bw = BRCMU_CHAN_BW_40; 711262306a36Sopenharmony_ci ch.sb = BRCMU_CHAN_SB_NONE; 711362306a36Sopenharmony_ci ch.chnum = 0; 711462306a36Sopenharmony_ci cfg->d11inf.encchspec(&ch); 711562306a36Sopenharmony_ci 711662306a36Sopenharmony_ci /* pass encoded chanspec in query */ 711762306a36Sopenharmony_ci *(__le16 *)pbuf = cpu_to_le16(ch.chspec); 711862306a36Sopenharmony_ci 711962306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf, 712062306a36Sopenharmony_ci BRCMF_DCMD_MEDLEN); 712162306a36Sopenharmony_ci if (err) { 712262306a36Sopenharmony_ci bphy_err(drvr, "get chanspecs error (%d)\n", err); 712362306a36Sopenharmony_ci kfree(pbuf); 712462306a36Sopenharmony_ci return err; 712562306a36Sopenharmony_ci } 712662306a36Sopenharmony_ci 712762306a36Sopenharmony_ci band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ]; 712862306a36Sopenharmony_ci list = (struct brcmf_chanspec_list *)pbuf; 712962306a36Sopenharmony_ci num_chan = le32_to_cpu(list->count); 713062306a36Sopenharmony_ci if (num_chan > BRCMF_MAX_CHANSPEC_LIST) { 713162306a36Sopenharmony_ci bphy_err(drvr, "Invalid count of channel Spec. (%u)\n", 713262306a36Sopenharmony_ci num_chan); 713362306a36Sopenharmony_ci kfree(pbuf); 713462306a36Sopenharmony_ci return -EINVAL; 713562306a36Sopenharmony_ci } 713662306a36Sopenharmony_ci 713762306a36Sopenharmony_ci for (i = 0; i < num_chan; i++) { 713862306a36Sopenharmony_ci ch.chspec = (u16)le32_to_cpu(list->element[i]); 713962306a36Sopenharmony_ci cfg->d11inf.decchspec(&ch); 714062306a36Sopenharmony_ci if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G)) 714162306a36Sopenharmony_ci continue; 714262306a36Sopenharmony_ci if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40)) 714362306a36Sopenharmony_ci continue; 714462306a36Sopenharmony_ci for (j = 0; j < band->n_channels; j++) { 714562306a36Sopenharmony_ci if (band->channels[j].hw_value == ch.control_ch_num) 714662306a36Sopenharmony_ci break; 714762306a36Sopenharmony_ci } 714862306a36Sopenharmony_ci if (WARN_ON(j == band->n_channels)) 714962306a36Sopenharmony_ci continue; 715062306a36Sopenharmony_ci 715162306a36Sopenharmony_ci brcmf_update_bw40_channel_flag(&band->channels[j], &ch); 715262306a36Sopenharmony_ci } 715362306a36Sopenharmony_ci kfree(pbuf); 715462306a36Sopenharmony_ci } 715562306a36Sopenharmony_ci return err; 715662306a36Sopenharmony_ci} 715762306a36Sopenharmony_ci 715862306a36Sopenharmony_cistatic void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[]) 715962306a36Sopenharmony_ci{ 716062306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 716162306a36Sopenharmony_ci u32 band, mimo_bwcap; 716262306a36Sopenharmony_ci int err; 716362306a36Sopenharmony_ci 716462306a36Sopenharmony_ci band = WLC_BAND_2G; 716562306a36Sopenharmony_ci err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); 716662306a36Sopenharmony_ci if (!err) { 716762306a36Sopenharmony_ci bw_cap[NL80211_BAND_2GHZ] = band; 716862306a36Sopenharmony_ci band = WLC_BAND_5G; 716962306a36Sopenharmony_ci err = brcmf_fil_iovar_int_get(ifp, "bw_cap", &band); 717062306a36Sopenharmony_ci if (!err) { 717162306a36Sopenharmony_ci bw_cap[NL80211_BAND_5GHZ] = band; 717262306a36Sopenharmony_ci return; 717362306a36Sopenharmony_ci } 717462306a36Sopenharmony_ci WARN_ON(1); 717562306a36Sopenharmony_ci return; 717662306a36Sopenharmony_ci } 717762306a36Sopenharmony_ci brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n"); 717862306a36Sopenharmony_ci mimo_bwcap = 0; 717962306a36Sopenharmony_ci err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap); 718062306a36Sopenharmony_ci if (err) 718162306a36Sopenharmony_ci /* assume 20MHz if firmware does not give a clue */ 718262306a36Sopenharmony_ci mimo_bwcap = WLC_N_BW_20ALL; 718362306a36Sopenharmony_ci 718462306a36Sopenharmony_ci switch (mimo_bwcap) { 718562306a36Sopenharmony_ci case WLC_N_BW_40ALL: 718662306a36Sopenharmony_ci bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT; 718762306a36Sopenharmony_ci fallthrough; 718862306a36Sopenharmony_ci case WLC_N_BW_20IN2G_40IN5G: 718962306a36Sopenharmony_ci bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT; 719062306a36Sopenharmony_ci fallthrough; 719162306a36Sopenharmony_ci case WLC_N_BW_20ALL: 719262306a36Sopenharmony_ci bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT; 719362306a36Sopenharmony_ci bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT; 719462306a36Sopenharmony_ci break; 719562306a36Sopenharmony_ci default: 719662306a36Sopenharmony_ci bphy_err(drvr, "invalid mimo_bw_cap value\n"); 719762306a36Sopenharmony_ci } 719862306a36Sopenharmony_ci} 719962306a36Sopenharmony_ci 720062306a36Sopenharmony_cistatic void brcmf_update_ht_cap(struct ieee80211_supported_band *band, 720162306a36Sopenharmony_ci u32 bw_cap[2], u32 nchain) 720262306a36Sopenharmony_ci{ 720362306a36Sopenharmony_ci band->ht_cap.ht_supported = true; 720462306a36Sopenharmony_ci if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) { 720562306a36Sopenharmony_ci band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40; 720662306a36Sopenharmony_ci band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; 720762306a36Sopenharmony_ci } 720862306a36Sopenharmony_ci band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20; 720962306a36Sopenharmony_ci band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40; 721062306a36Sopenharmony_ci band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K; 721162306a36Sopenharmony_ci band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16; 721262306a36Sopenharmony_ci memset(band->ht_cap.mcs.rx_mask, 0xff, nchain); 721362306a36Sopenharmony_ci band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED; 721462306a36Sopenharmony_ci} 721562306a36Sopenharmony_ci 721662306a36Sopenharmony_cistatic __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp) 721762306a36Sopenharmony_ci{ 721862306a36Sopenharmony_ci u16 mcs_map; 721962306a36Sopenharmony_ci int i; 722062306a36Sopenharmony_ci 722162306a36Sopenharmony_ci for (i = 0, mcs_map = 0xFFFF; i < nchain; i++) 722262306a36Sopenharmony_ci mcs_map = (mcs_map << 2) | supp; 722362306a36Sopenharmony_ci 722462306a36Sopenharmony_ci return cpu_to_le16(mcs_map); 722562306a36Sopenharmony_ci} 722662306a36Sopenharmony_ci 722762306a36Sopenharmony_cistatic void brcmf_update_vht_cap(struct ieee80211_supported_band *band, 722862306a36Sopenharmony_ci u32 bw_cap[2], u32 nchain, u32 txstreams, 722962306a36Sopenharmony_ci u32 txbf_bfe_cap, u32 txbf_bfr_cap) 723062306a36Sopenharmony_ci{ 723162306a36Sopenharmony_ci __le16 mcs_map; 723262306a36Sopenharmony_ci 723362306a36Sopenharmony_ci /* not allowed in 2.4G band */ 723462306a36Sopenharmony_ci if (band->band == NL80211_BAND_2GHZ) 723562306a36Sopenharmony_ci return; 723662306a36Sopenharmony_ci 723762306a36Sopenharmony_ci band->vht_cap.vht_supported = true; 723862306a36Sopenharmony_ci /* 80MHz is mandatory */ 723962306a36Sopenharmony_ci band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80; 724062306a36Sopenharmony_ci if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) { 724162306a36Sopenharmony_ci band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ; 724262306a36Sopenharmony_ci band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160; 724362306a36Sopenharmony_ci } 724462306a36Sopenharmony_ci /* all support 256-QAM */ 724562306a36Sopenharmony_ci mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9); 724662306a36Sopenharmony_ci band->vht_cap.vht_mcs.rx_mcs_map = mcs_map; 724762306a36Sopenharmony_ci band->vht_cap.vht_mcs.tx_mcs_map = mcs_map; 724862306a36Sopenharmony_ci 724962306a36Sopenharmony_ci /* Beamforming support information */ 725062306a36Sopenharmony_ci if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP) 725162306a36Sopenharmony_ci band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE; 725262306a36Sopenharmony_ci if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP) 725362306a36Sopenharmony_ci band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE; 725462306a36Sopenharmony_ci if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP) 725562306a36Sopenharmony_ci band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE; 725662306a36Sopenharmony_ci if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP) 725762306a36Sopenharmony_ci band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE; 725862306a36Sopenharmony_ci 725962306a36Sopenharmony_ci if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) { 726062306a36Sopenharmony_ci band->vht_cap.cap |= 726162306a36Sopenharmony_ci (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT); 726262306a36Sopenharmony_ci band->vht_cap.cap |= ((txstreams - 1) << 726362306a36Sopenharmony_ci IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT); 726462306a36Sopenharmony_ci band->vht_cap.cap |= 726562306a36Sopenharmony_ci IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB; 726662306a36Sopenharmony_ci } 726762306a36Sopenharmony_ci} 726862306a36Sopenharmony_ci 726962306a36Sopenharmony_cistatic int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg) 727062306a36Sopenharmony_ci{ 727162306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 727262306a36Sopenharmony_ci struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0); 727362306a36Sopenharmony_ci struct wiphy *wiphy = cfg_to_wiphy(cfg); 727462306a36Sopenharmony_ci u32 nmode = 0; 727562306a36Sopenharmony_ci u32 vhtmode = 0; 727662306a36Sopenharmony_ci u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT }; 727762306a36Sopenharmony_ci u32 rxchain; 727862306a36Sopenharmony_ci u32 nchain; 727962306a36Sopenharmony_ci int err; 728062306a36Sopenharmony_ci s32 i; 728162306a36Sopenharmony_ci struct ieee80211_supported_band *band; 728262306a36Sopenharmony_ci u32 txstreams = 0; 728362306a36Sopenharmony_ci u32 txbf_bfe_cap = 0; 728462306a36Sopenharmony_ci u32 txbf_bfr_cap = 0; 728562306a36Sopenharmony_ci 728662306a36Sopenharmony_ci (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode); 728762306a36Sopenharmony_ci err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode); 728862306a36Sopenharmony_ci if (err) { 728962306a36Sopenharmony_ci bphy_err(drvr, "nmode error (%d)\n", err); 729062306a36Sopenharmony_ci } else { 729162306a36Sopenharmony_ci brcmf_get_bwcap(ifp, bw_cap); 729262306a36Sopenharmony_ci } 729362306a36Sopenharmony_ci brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n", 729462306a36Sopenharmony_ci nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ], 729562306a36Sopenharmony_ci bw_cap[NL80211_BAND_5GHZ]); 729662306a36Sopenharmony_ci 729762306a36Sopenharmony_ci err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain); 729862306a36Sopenharmony_ci if (err) { 729962306a36Sopenharmony_ci /* rxchain unsupported by firmware of older chips */ 730062306a36Sopenharmony_ci if (err == -EBADE) 730162306a36Sopenharmony_ci bphy_info_once(drvr, "rxchain unsupported\n"); 730262306a36Sopenharmony_ci else 730362306a36Sopenharmony_ci bphy_err(drvr, "rxchain error (%d)\n", err); 730462306a36Sopenharmony_ci 730562306a36Sopenharmony_ci nchain = 1; 730662306a36Sopenharmony_ci } else { 730762306a36Sopenharmony_ci for (nchain = 0; rxchain; nchain++) 730862306a36Sopenharmony_ci rxchain = rxchain & (rxchain - 1); 730962306a36Sopenharmony_ci } 731062306a36Sopenharmony_ci brcmf_dbg(INFO, "nchain=%d\n", nchain); 731162306a36Sopenharmony_ci 731262306a36Sopenharmony_ci err = brcmf_construct_chaninfo(cfg, bw_cap); 731362306a36Sopenharmony_ci if (err) { 731462306a36Sopenharmony_ci bphy_err(drvr, "brcmf_construct_chaninfo failed (%d)\n", err); 731562306a36Sopenharmony_ci return err; 731662306a36Sopenharmony_ci } 731762306a36Sopenharmony_ci 731862306a36Sopenharmony_ci if (vhtmode) { 731962306a36Sopenharmony_ci (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams); 732062306a36Sopenharmony_ci (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap", 732162306a36Sopenharmony_ci &txbf_bfe_cap); 732262306a36Sopenharmony_ci (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap", 732362306a36Sopenharmony_ci &txbf_bfr_cap); 732462306a36Sopenharmony_ci } 732562306a36Sopenharmony_ci 732662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) { 732762306a36Sopenharmony_ci band = wiphy->bands[i]; 732862306a36Sopenharmony_ci if (band == NULL) 732962306a36Sopenharmony_ci continue; 733062306a36Sopenharmony_ci 733162306a36Sopenharmony_ci if (nmode) 733262306a36Sopenharmony_ci brcmf_update_ht_cap(band, bw_cap, nchain); 733362306a36Sopenharmony_ci if (vhtmode) 733462306a36Sopenharmony_ci brcmf_update_vht_cap(band, bw_cap, nchain, txstreams, 733562306a36Sopenharmony_ci txbf_bfe_cap, txbf_bfr_cap); 733662306a36Sopenharmony_ci } 733762306a36Sopenharmony_ci 733862306a36Sopenharmony_ci return 0; 733962306a36Sopenharmony_ci} 734062306a36Sopenharmony_ci 734162306a36Sopenharmony_cistatic const struct ieee80211_txrx_stypes 734262306a36Sopenharmony_cibrcmf_txrx_stypes[NUM_NL80211_IFTYPES] = { 734362306a36Sopenharmony_ci [NL80211_IFTYPE_STATION] = { 734462306a36Sopenharmony_ci .tx = 0xffff, 734562306a36Sopenharmony_ci .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 734662306a36Sopenharmony_ci BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 734762306a36Sopenharmony_ci }, 734862306a36Sopenharmony_ci [NL80211_IFTYPE_P2P_CLIENT] = { 734962306a36Sopenharmony_ci .tx = 0xffff, 735062306a36Sopenharmony_ci .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 735162306a36Sopenharmony_ci BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 735262306a36Sopenharmony_ci }, 735362306a36Sopenharmony_ci [NL80211_IFTYPE_P2P_GO] = { 735462306a36Sopenharmony_ci .tx = 0xffff, 735562306a36Sopenharmony_ci .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | 735662306a36Sopenharmony_ci BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | 735762306a36Sopenharmony_ci BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | 735862306a36Sopenharmony_ci BIT(IEEE80211_STYPE_DISASSOC >> 4) | 735962306a36Sopenharmony_ci BIT(IEEE80211_STYPE_AUTH >> 4) | 736062306a36Sopenharmony_ci BIT(IEEE80211_STYPE_DEAUTH >> 4) | 736162306a36Sopenharmony_ci BIT(IEEE80211_STYPE_ACTION >> 4) 736262306a36Sopenharmony_ci }, 736362306a36Sopenharmony_ci [NL80211_IFTYPE_P2P_DEVICE] = { 736462306a36Sopenharmony_ci .tx = 0xffff, 736562306a36Sopenharmony_ci .rx = BIT(IEEE80211_STYPE_ACTION >> 4) | 736662306a36Sopenharmony_ci BIT(IEEE80211_STYPE_PROBE_REQ >> 4) 736762306a36Sopenharmony_ci }, 736862306a36Sopenharmony_ci [NL80211_IFTYPE_AP] = { 736962306a36Sopenharmony_ci .tx = 0xffff, 737062306a36Sopenharmony_ci .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) | 737162306a36Sopenharmony_ci BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) | 737262306a36Sopenharmony_ci BIT(IEEE80211_STYPE_PROBE_REQ >> 4) | 737362306a36Sopenharmony_ci BIT(IEEE80211_STYPE_DISASSOC >> 4) | 737462306a36Sopenharmony_ci BIT(IEEE80211_STYPE_AUTH >> 4) | 737562306a36Sopenharmony_ci BIT(IEEE80211_STYPE_DEAUTH >> 4) | 737662306a36Sopenharmony_ci BIT(IEEE80211_STYPE_ACTION >> 4) 737762306a36Sopenharmony_ci } 737862306a36Sopenharmony_ci}; 737962306a36Sopenharmony_ci 738062306a36Sopenharmony_ci/** 738162306a36Sopenharmony_ci * brcmf_setup_ifmodes() - determine interface modes and combinations. 738262306a36Sopenharmony_ci * 738362306a36Sopenharmony_ci * @wiphy: wiphy object. 738462306a36Sopenharmony_ci * @ifp: interface object needed for feat module api. 738562306a36Sopenharmony_ci * 738662306a36Sopenharmony_ci * The interface modes and combinations are determined dynamically here 738762306a36Sopenharmony_ci * based on firmware functionality. 738862306a36Sopenharmony_ci * 738962306a36Sopenharmony_ci * no p2p and no mbss: 739062306a36Sopenharmony_ci * 739162306a36Sopenharmony_ci * #STA <= 1, #AP <= 1, channels = 1, 2 total 739262306a36Sopenharmony_ci * 739362306a36Sopenharmony_ci * no p2p and mbss: 739462306a36Sopenharmony_ci * 739562306a36Sopenharmony_ci * #STA <= 1, #AP <= 1, channels = 1, 2 total 739662306a36Sopenharmony_ci * #AP <= 4, matching BI, channels = 1, 4 total 739762306a36Sopenharmony_ci * 739862306a36Sopenharmony_ci * no p2p and rsdb: 739962306a36Sopenharmony_ci * #STA <= 1, #AP <= 2, channels = 2, 4 total 740062306a36Sopenharmony_ci * 740162306a36Sopenharmony_ci * p2p, no mchan, and mbss: 740262306a36Sopenharmony_ci * 740362306a36Sopenharmony_ci * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total 740462306a36Sopenharmony_ci * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total 740562306a36Sopenharmony_ci * #AP <= 4, matching BI, channels = 1, 4 total 740662306a36Sopenharmony_ci * 740762306a36Sopenharmony_ci * p2p, mchan, and mbss: 740862306a36Sopenharmony_ci * 740962306a36Sopenharmony_ci * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total 741062306a36Sopenharmony_ci * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total 741162306a36Sopenharmony_ci * #AP <= 4, matching BI, channels = 1, 4 total 741262306a36Sopenharmony_ci * 741362306a36Sopenharmony_ci * p2p, rsdb, and no mbss: 741462306a36Sopenharmony_ci * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2, 741562306a36Sopenharmony_ci * channels = 2, 4 total 741662306a36Sopenharmony_ci */ 741762306a36Sopenharmony_cistatic int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp) 741862306a36Sopenharmony_ci{ 741962306a36Sopenharmony_ci struct ieee80211_iface_combination *combo = NULL; 742062306a36Sopenharmony_ci struct ieee80211_iface_limit *c0_limits = NULL; 742162306a36Sopenharmony_ci struct ieee80211_iface_limit *p2p_limits = NULL; 742262306a36Sopenharmony_ci struct ieee80211_iface_limit *mbss_limits = NULL; 742362306a36Sopenharmony_ci bool mon_flag, mbss, p2p, rsdb, mchan; 742462306a36Sopenharmony_ci int i, c, n_combos, n_limits; 742562306a36Sopenharmony_ci 742662306a36Sopenharmony_ci mon_flag = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FLAG); 742762306a36Sopenharmony_ci mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS); 742862306a36Sopenharmony_ci p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P); 742962306a36Sopenharmony_ci rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB); 743062306a36Sopenharmony_ci mchan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN); 743162306a36Sopenharmony_ci 743262306a36Sopenharmony_ci n_combos = 1 + !!(p2p && !rsdb) + !!mbss; 743362306a36Sopenharmony_ci combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL); 743462306a36Sopenharmony_ci if (!combo) 743562306a36Sopenharmony_ci goto err; 743662306a36Sopenharmony_ci 743762306a36Sopenharmony_ci wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) | 743862306a36Sopenharmony_ci BIT(NL80211_IFTYPE_ADHOC) | 743962306a36Sopenharmony_ci BIT(NL80211_IFTYPE_AP); 744062306a36Sopenharmony_ci if (mon_flag) 744162306a36Sopenharmony_ci wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR); 744262306a36Sopenharmony_ci if (p2p) 744362306a36Sopenharmony_ci wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) | 744462306a36Sopenharmony_ci BIT(NL80211_IFTYPE_P2P_GO) | 744562306a36Sopenharmony_ci BIT(NL80211_IFTYPE_P2P_DEVICE); 744662306a36Sopenharmony_ci 744762306a36Sopenharmony_ci c = 0; 744862306a36Sopenharmony_ci i = 0; 744962306a36Sopenharmony_ci n_limits = 1 + mon_flag + (p2p ? 2 : 0) + (rsdb || !p2p); 745062306a36Sopenharmony_ci c0_limits = kcalloc(n_limits, sizeof(*c0_limits), GFP_KERNEL); 745162306a36Sopenharmony_ci if (!c0_limits) 745262306a36Sopenharmony_ci goto err; 745362306a36Sopenharmony_ci 745462306a36Sopenharmony_ci combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan)); 745562306a36Sopenharmony_ci c0_limits[i].max = 1 + (p2p && mchan); 745662306a36Sopenharmony_ci c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION); 745762306a36Sopenharmony_ci if (mon_flag) { 745862306a36Sopenharmony_ci c0_limits[i].max = 1; 745962306a36Sopenharmony_ci c0_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR); 746062306a36Sopenharmony_ci } 746162306a36Sopenharmony_ci if (p2p) { 746262306a36Sopenharmony_ci c0_limits[i].max = 1; 746362306a36Sopenharmony_ci c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); 746462306a36Sopenharmony_ci c0_limits[i].max = 1 + rsdb; 746562306a36Sopenharmony_ci c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) | 746662306a36Sopenharmony_ci BIT(NL80211_IFTYPE_P2P_GO); 746762306a36Sopenharmony_ci } 746862306a36Sopenharmony_ci if (p2p && rsdb) { 746962306a36Sopenharmony_ci c0_limits[i].max = 2; 747062306a36Sopenharmony_ci c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); 747162306a36Sopenharmony_ci combo[c].max_interfaces = 4; 747262306a36Sopenharmony_ci } else if (p2p) { 747362306a36Sopenharmony_ci combo[c].max_interfaces = i; 747462306a36Sopenharmony_ci } else if (rsdb) { 747562306a36Sopenharmony_ci c0_limits[i].max = 2; 747662306a36Sopenharmony_ci c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); 747762306a36Sopenharmony_ci combo[c].max_interfaces = 3; 747862306a36Sopenharmony_ci } else { 747962306a36Sopenharmony_ci c0_limits[i].max = 1; 748062306a36Sopenharmony_ci c0_limits[i++].types = BIT(NL80211_IFTYPE_AP); 748162306a36Sopenharmony_ci combo[c].max_interfaces = i; 748262306a36Sopenharmony_ci } 748362306a36Sopenharmony_ci combo[c].n_limits = i; 748462306a36Sopenharmony_ci combo[c].limits = c0_limits; 748562306a36Sopenharmony_ci 748662306a36Sopenharmony_ci if (p2p && !rsdb) { 748762306a36Sopenharmony_ci c++; 748862306a36Sopenharmony_ci i = 0; 748962306a36Sopenharmony_ci p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL); 749062306a36Sopenharmony_ci if (!p2p_limits) 749162306a36Sopenharmony_ci goto err; 749262306a36Sopenharmony_ci p2p_limits[i].max = 1; 749362306a36Sopenharmony_ci p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION); 749462306a36Sopenharmony_ci p2p_limits[i].max = 1; 749562306a36Sopenharmony_ci p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP); 749662306a36Sopenharmony_ci p2p_limits[i].max = 1; 749762306a36Sopenharmony_ci p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT); 749862306a36Sopenharmony_ci p2p_limits[i].max = 1; 749962306a36Sopenharmony_ci p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE); 750062306a36Sopenharmony_ci combo[c].num_different_channels = 1; 750162306a36Sopenharmony_ci combo[c].max_interfaces = i; 750262306a36Sopenharmony_ci combo[c].n_limits = i; 750362306a36Sopenharmony_ci combo[c].limits = p2p_limits; 750462306a36Sopenharmony_ci } 750562306a36Sopenharmony_ci 750662306a36Sopenharmony_ci if (mbss) { 750762306a36Sopenharmony_ci c++; 750862306a36Sopenharmony_ci i = 0; 750962306a36Sopenharmony_ci n_limits = 1 + mon_flag; 751062306a36Sopenharmony_ci mbss_limits = kcalloc(n_limits, sizeof(*mbss_limits), 751162306a36Sopenharmony_ci GFP_KERNEL); 751262306a36Sopenharmony_ci if (!mbss_limits) 751362306a36Sopenharmony_ci goto err; 751462306a36Sopenharmony_ci mbss_limits[i].max = 4; 751562306a36Sopenharmony_ci mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP); 751662306a36Sopenharmony_ci if (mon_flag) { 751762306a36Sopenharmony_ci mbss_limits[i].max = 1; 751862306a36Sopenharmony_ci mbss_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR); 751962306a36Sopenharmony_ci } 752062306a36Sopenharmony_ci combo[c].beacon_int_infra_match = true; 752162306a36Sopenharmony_ci combo[c].num_different_channels = 1; 752262306a36Sopenharmony_ci combo[c].max_interfaces = 4 + mon_flag; 752362306a36Sopenharmony_ci combo[c].n_limits = i; 752462306a36Sopenharmony_ci combo[c].limits = mbss_limits; 752562306a36Sopenharmony_ci } 752662306a36Sopenharmony_ci 752762306a36Sopenharmony_ci wiphy->n_iface_combinations = n_combos; 752862306a36Sopenharmony_ci wiphy->iface_combinations = combo; 752962306a36Sopenharmony_ci return 0; 753062306a36Sopenharmony_ci 753162306a36Sopenharmony_cierr: 753262306a36Sopenharmony_ci kfree(c0_limits); 753362306a36Sopenharmony_ci kfree(p2p_limits); 753462306a36Sopenharmony_ci kfree(mbss_limits); 753562306a36Sopenharmony_ci kfree(combo); 753662306a36Sopenharmony_ci return -ENOMEM; 753762306a36Sopenharmony_ci} 753862306a36Sopenharmony_ci 753962306a36Sopenharmony_ci#ifdef CONFIG_PM 754062306a36Sopenharmony_cistatic const struct wiphy_wowlan_support brcmf_wowlan_support = { 754162306a36Sopenharmony_ci .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT, 754262306a36Sopenharmony_ci .n_patterns = BRCMF_WOWL_MAXPATTERNS, 754362306a36Sopenharmony_ci .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE, 754462306a36Sopenharmony_ci .pattern_min_len = 1, 754562306a36Sopenharmony_ci .max_pkt_offset = 1500, 754662306a36Sopenharmony_ci}; 754762306a36Sopenharmony_ci#endif 754862306a36Sopenharmony_ci 754962306a36Sopenharmony_cistatic void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp) 755062306a36Sopenharmony_ci{ 755162306a36Sopenharmony_ci#ifdef CONFIG_PM 755262306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 755362306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 755462306a36Sopenharmony_ci struct wiphy_wowlan_support *wowl; 755562306a36Sopenharmony_ci 755662306a36Sopenharmony_ci wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support), 755762306a36Sopenharmony_ci GFP_KERNEL); 755862306a36Sopenharmony_ci if (!wowl) { 755962306a36Sopenharmony_ci bphy_err(drvr, "only support basic wowlan features\n"); 756062306a36Sopenharmony_ci wiphy->wowlan = &brcmf_wowlan_support; 756162306a36Sopenharmony_ci return; 756262306a36Sopenharmony_ci } 756362306a36Sopenharmony_ci 756462306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { 756562306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) { 756662306a36Sopenharmony_ci wowl->flags |= WIPHY_WOWLAN_NET_DETECT; 756762306a36Sopenharmony_ci wowl->max_nd_match_sets = BRCMF_PNO_MAX_PFN_COUNT; 756862306a36Sopenharmony_ci init_waitqueue_head(&cfg->wowl.nd_data_wait); 756962306a36Sopenharmony_ci } 757062306a36Sopenharmony_ci } 757162306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) { 757262306a36Sopenharmony_ci wowl->flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY; 757362306a36Sopenharmony_ci wowl->flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE; 757462306a36Sopenharmony_ci } 757562306a36Sopenharmony_ci 757662306a36Sopenharmony_ci wiphy->wowlan = wowl; 757762306a36Sopenharmony_ci#endif 757862306a36Sopenharmony_ci} 757962306a36Sopenharmony_ci 758062306a36Sopenharmony_cistatic int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp) 758162306a36Sopenharmony_ci{ 758262306a36Sopenharmony_ci struct brcmf_pub *drvr = ifp->drvr; 758362306a36Sopenharmony_ci const struct ieee80211_iface_combination *combo; 758462306a36Sopenharmony_ci struct ieee80211_supported_band *band; 758562306a36Sopenharmony_ci u16 max_interfaces = 0; 758662306a36Sopenharmony_ci bool gscan; 758762306a36Sopenharmony_ci __le32 bandlist[3]; 758862306a36Sopenharmony_ci u32 n_bands; 758962306a36Sopenharmony_ci int err, i; 759062306a36Sopenharmony_ci 759162306a36Sopenharmony_ci wiphy->max_scan_ssids = WL_NUM_SCAN_MAX; 759262306a36Sopenharmony_ci wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX; 759362306a36Sopenharmony_ci wiphy->max_num_pmkids = BRCMF_MAXPMKID; 759462306a36Sopenharmony_ci 759562306a36Sopenharmony_ci err = brcmf_setup_ifmodes(wiphy, ifp); 759662306a36Sopenharmony_ci if (err) 759762306a36Sopenharmony_ci return err; 759862306a36Sopenharmony_ci 759962306a36Sopenharmony_ci for (i = 0, combo = wiphy->iface_combinations; 760062306a36Sopenharmony_ci i < wiphy->n_iface_combinations; i++, combo++) { 760162306a36Sopenharmony_ci max_interfaces = max(max_interfaces, combo->max_interfaces); 760262306a36Sopenharmony_ci } 760362306a36Sopenharmony_ci 760462306a36Sopenharmony_ci for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses); 760562306a36Sopenharmony_ci i++) { 760662306a36Sopenharmony_ci u8 *addr = drvr->addresses[i].addr; 760762306a36Sopenharmony_ci 760862306a36Sopenharmony_ci memcpy(addr, drvr->mac, ETH_ALEN); 760962306a36Sopenharmony_ci if (i) { 761062306a36Sopenharmony_ci addr[0] |= BIT(1); 761162306a36Sopenharmony_ci addr[ETH_ALEN - 1] ^= i; 761262306a36Sopenharmony_ci } 761362306a36Sopenharmony_ci } 761462306a36Sopenharmony_ci wiphy->addresses = drvr->addresses; 761562306a36Sopenharmony_ci wiphy->n_addresses = i; 761662306a36Sopenharmony_ci 761762306a36Sopenharmony_ci wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM; 761862306a36Sopenharmony_ci wiphy->cipher_suites = brcmf_cipher_suites; 761962306a36Sopenharmony_ci wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites); 762062306a36Sopenharmony_ci if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) 762162306a36Sopenharmony_ci wiphy->n_cipher_suites--; 762262306a36Sopenharmony_ci wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) | 762362306a36Sopenharmony_ci BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) | 762462306a36Sopenharmony_ci BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST); 762562306a36Sopenharmony_ci 762662306a36Sopenharmony_ci wiphy->flags |= WIPHY_FLAG_NETNS_OK | 762762306a36Sopenharmony_ci WIPHY_FLAG_PS_ON_BY_DEFAULT | 762862306a36Sopenharmony_ci WIPHY_FLAG_HAVE_AP_SME | 762962306a36Sopenharmony_ci WIPHY_FLAG_OFFCHAN_TX | 763062306a36Sopenharmony_ci WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL; 763162306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) 763262306a36Sopenharmony_ci wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS; 763362306a36Sopenharmony_ci if (!ifp->drvr->settings->roamoff) 763462306a36Sopenharmony_ci wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM; 763562306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) { 763662306a36Sopenharmony_ci wiphy_ext_feature_set(wiphy, 763762306a36Sopenharmony_ci NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK); 763862306a36Sopenharmony_ci wiphy_ext_feature_set(wiphy, 763962306a36Sopenharmony_ci NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X); 764062306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE)) 764162306a36Sopenharmony_ci wiphy_ext_feature_set(wiphy, 764262306a36Sopenharmony_ci NL80211_EXT_FEATURE_SAE_OFFLOAD); 764362306a36Sopenharmony_ci } 764462306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWAUTH)) { 764562306a36Sopenharmony_ci wiphy_ext_feature_set(wiphy, 764662306a36Sopenharmony_ci NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK); 764762306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE)) 764862306a36Sopenharmony_ci wiphy_ext_feature_set(wiphy, 764962306a36Sopenharmony_ci NL80211_EXT_FEATURE_SAE_OFFLOAD_AP); 765062306a36Sopenharmony_ci } 765162306a36Sopenharmony_ci wiphy->mgmt_stypes = brcmf_txrx_stypes; 765262306a36Sopenharmony_ci wiphy->max_remain_on_channel_duration = 5000; 765362306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) { 765462306a36Sopenharmony_ci gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN); 765562306a36Sopenharmony_ci brcmf_pno_wiphy_params(wiphy, gscan); 765662306a36Sopenharmony_ci } 765762306a36Sopenharmony_ci /* vendor commands/events support */ 765862306a36Sopenharmony_ci wiphy->vendor_commands = brcmf_vendor_cmds; 765962306a36Sopenharmony_ci wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1; 766062306a36Sopenharmony_ci 766162306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL)) 766262306a36Sopenharmony_ci brcmf_wiphy_wowl_params(wiphy, ifp); 766362306a36Sopenharmony_ci err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist, 766462306a36Sopenharmony_ci sizeof(bandlist)); 766562306a36Sopenharmony_ci if (err) { 766662306a36Sopenharmony_ci bphy_err(drvr, "could not obtain band info: err=%d\n", err); 766762306a36Sopenharmony_ci return err; 766862306a36Sopenharmony_ci } 766962306a36Sopenharmony_ci /* first entry in bandlist is number of bands */ 767062306a36Sopenharmony_ci n_bands = le32_to_cpu(bandlist[0]); 767162306a36Sopenharmony_ci for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) { 767262306a36Sopenharmony_ci if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) { 767362306a36Sopenharmony_ci band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz), 767462306a36Sopenharmony_ci GFP_KERNEL); 767562306a36Sopenharmony_ci if (!band) 767662306a36Sopenharmony_ci return -ENOMEM; 767762306a36Sopenharmony_ci 767862306a36Sopenharmony_ci band->channels = kmemdup(&__wl_2ghz_channels, 767962306a36Sopenharmony_ci sizeof(__wl_2ghz_channels), 768062306a36Sopenharmony_ci GFP_KERNEL); 768162306a36Sopenharmony_ci if (!band->channels) { 768262306a36Sopenharmony_ci kfree(band); 768362306a36Sopenharmony_ci return -ENOMEM; 768462306a36Sopenharmony_ci } 768562306a36Sopenharmony_ci 768662306a36Sopenharmony_ci band->n_channels = ARRAY_SIZE(__wl_2ghz_channels); 768762306a36Sopenharmony_ci wiphy->bands[NL80211_BAND_2GHZ] = band; 768862306a36Sopenharmony_ci } 768962306a36Sopenharmony_ci if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) { 769062306a36Sopenharmony_ci band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz), 769162306a36Sopenharmony_ci GFP_KERNEL); 769262306a36Sopenharmony_ci if (!band) 769362306a36Sopenharmony_ci return -ENOMEM; 769462306a36Sopenharmony_ci 769562306a36Sopenharmony_ci band->channels = kmemdup(&__wl_5ghz_channels, 769662306a36Sopenharmony_ci sizeof(__wl_5ghz_channels), 769762306a36Sopenharmony_ci GFP_KERNEL); 769862306a36Sopenharmony_ci if (!band->channels) { 769962306a36Sopenharmony_ci kfree(band); 770062306a36Sopenharmony_ci return -ENOMEM; 770162306a36Sopenharmony_ci } 770262306a36Sopenharmony_ci 770362306a36Sopenharmony_ci band->n_channels = ARRAY_SIZE(__wl_5ghz_channels); 770462306a36Sopenharmony_ci wiphy->bands[NL80211_BAND_5GHZ] = band; 770562306a36Sopenharmony_ci } 770662306a36Sopenharmony_ci } 770762306a36Sopenharmony_ci 770862306a36Sopenharmony_ci if (wiphy->bands[NL80211_BAND_5GHZ] && 770962306a36Sopenharmony_ci brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DOT11H)) 771062306a36Sopenharmony_ci wiphy_ext_feature_set(wiphy, 771162306a36Sopenharmony_ci NL80211_EXT_FEATURE_DFS_OFFLOAD); 771262306a36Sopenharmony_ci 771362306a36Sopenharmony_ci wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST); 771462306a36Sopenharmony_ci 771562306a36Sopenharmony_ci wiphy_read_of_freq_limits(wiphy); 771662306a36Sopenharmony_ci 771762306a36Sopenharmony_ci return 0; 771862306a36Sopenharmony_ci} 771962306a36Sopenharmony_ci 772062306a36Sopenharmony_cistatic s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg) 772162306a36Sopenharmony_ci{ 772262306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 772362306a36Sopenharmony_ci struct net_device *ndev; 772462306a36Sopenharmony_ci struct wireless_dev *wdev; 772562306a36Sopenharmony_ci struct brcmf_if *ifp; 772662306a36Sopenharmony_ci s32 power_mode; 772762306a36Sopenharmony_ci s32 err = 0; 772862306a36Sopenharmony_ci 772962306a36Sopenharmony_ci if (cfg->dongle_up) 773062306a36Sopenharmony_ci return err; 773162306a36Sopenharmony_ci 773262306a36Sopenharmony_ci ndev = cfg_to_ndev(cfg); 773362306a36Sopenharmony_ci wdev = ndev->ieee80211_ptr; 773462306a36Sopenharmony_ci ifp = netdev_priv(ndev); 773562306a36Sopenharmony_ci 773662306a36Sopenharmony_ci /* make sure RF is ready for work */ 773762306a36Sopenharmony_ci brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0); 773862306a36Sopenharmony_ci 773962306a36Sopenharmony_ci brcmf_dongle_scantime(ifp); 774062306a36Sopenharmony_ci 774162306a36Sopenharmony_ci power_mode = cfg->pwr_save ? PM_FAST : PM_OFF; 774262306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode); 774362306a36Sopenharmony_ci if (err) 774462306a36Sopenharmony_ci goto default_conf_out; 774562306a36Sopenharmony_ci brcmf_dbg(INFO, "power save set to %s\n", 774662306a36Sopenharmony_ci (power_mode ? "enabled" : "disabled")); 774762306a36Sopenharmony_ci 774862306a36Sopenharmony_ci err = brcmf_dongle_roam(ifp); 774962306a36Sopenharmony_ci if (err) 775062306a36Sopenharmony_ci goto default_conf_out; 775162306a36Sopenharmony_ci err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype, 775262306a36Sopenharmony_ci NULL); 775362306a36Sopenharmony_ci if (err) 775462306a36Sopenharmony_ci goto default_conf_out; 775562306a36Sopenharmony_ci 775662306a36Sopenharmony_ci brcmf_configure_arp_nd_offload(ifp, true); 775762306a36Sopenharmony_ci 775862306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_FAKEFRAG, 1); 775962306a36Sopenharmony_ci if (err) { 776062306a36Sopenharmony_ci bphy_err(drvr, "failed to set frameburst mode\n"); 776162306a36Sopenharmony_ci goto default_conf_out; 776262306a36Sopenharmony_ci } 776362306a36Sopenharmony_ci 776462306a36Sopenharmony_ci cfg->dongle_up = true; 776562306a36Sopenharmony_cidefault_conf_out: 776662306a36Sopenharmony_ci 776762306a36Sopenharmony_ci return err; 776862306a36Sopenharmony_ci 776962306a36Sopenharmony_ci} 777062306a36Sopenharmony_ci 777162306a36Sopenharmony_cistatic s32 __brcmf_cfg80211_up(struct brcmf_if *ifp) 777262306a36Sopenharmony_ci{ 777362306a36Sopenharmony_ci set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); 777462306a36Sopenharmony_ci 777562306a36Sopenharmony_ci return brcmf_config_dongle(ifp->drvr->config); 777662306a36Sopenharmony_ci} 777762306a36Sopenharmony_ci 777862306a36Sopenharmony_cistatic s32 __brcmf_cfg80211_down(struct brcmf_if *ifp) 777962306a36Sopenharmony_ci{ 778062306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = ifp->drvr->config; 778162306a36Sopenharmony_ci 778262306a36Sopenharmony_ci /* 778362306a36Sopenharmony_ci * While going down, if associated with AP disassociate 778462306a36Sopenharmony_ci * from AP to save power 778562306a36Sopenharmony_ci */ 778662306a36Sopenharmony_ci if (check_vif_up(ifp->vif)) { 778762306a36Sopenharmony_ci brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED, true); 778862306a36Sopenharmony_ci 778962306a36Sopenharmony_ci /* Make sure WPA_Supplicant receives all the event 779062306a36Sopenharmony_ci generated due to DISASSOC call to the fw to keep 779162306a36Sopenharmony_ci the state fw and WPA_Supplicant state consistent 779262306a36Sopenharmony_ci */ 779362306a36Sopenharmony_ci brcmf_delay(500); 779462306a36Sopenharmony_ci } 779562306a36Sopenharmony_ci 779662306a36Sopenharmony_ci brcmf_abort_scanning(cfg); 779762306a36Sopenharmony_ci clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state); 779862306a36Sopenharmony_ci 779962306a36Sopenharmony_ci return 0; 780062306a36Sopenharmony_ci} 780162306a36Sopenharmony_ci 780262306a36Sopenharmony_cis32 brcmf_cfg80211_up(struct net_device *ndev) 780362306a36Sopenharmony_ci{ 780462306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 780562306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = ifp->drvr->config; 780662306a36Sopenharmony_ci s32 err = 0; 780762306a36Sopenharmony_ci 780862306a36Sopenharmony_ci mutex_lock(&cfg->usr_sync); 780962306a36Sopenharmony_ci err = __brcmf_cfg80211_up(ifp); 781062306a36Sopenharmony_ci mutex_unlock(&cfg->usr_sync); 781162306a36Sopenharmony_ci 781262306a36Sopenharmony_ci return err; 781362306a36Sopenharmony_ci} 781462306a36Sopenharmony_ci 781562306a36Sopenharmony_cis32 brcmf_cfg80211_down(struct net_device *ndev) 781662306a36Sopenharmony_ci{ 781762306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(ndev); 781862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = ifp->drvr->config; 781962306a36Sopenharmony_ci s32 err = 0; 782062306a36Sopenharmony_ci 782162306a36Sopenharmony_ci mutex_lock(&cfg->usr_sync); 782262306a36Sopenharmony_ci err = __brcmf_cfg80211_down(ifp); 782362306a36Sopenharmony_ci mutex_unlock(&cfg->usr_sync); 782462306a36Sopenharmony_ci 782562306a36Sopenharmony_ci return err; 782662306a36Sopenharmony_ci} 782762306a36Sopenharmony_ci 782862306a36Sopenharmony_cienum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp) 782962306a36Sopenharmony_ci{ 783062306a36Sopenharmony_ci struct wireless_dev *wdev = &ifp->vif->wdev; 783162306a36Sopenharmony_ci 783262306a36Sopenharmony_ci return wdev->iftype; 783362306a36Sopenharmony_ci} 783462306a36Sopenharmony_ci 783562306a36Sopenharmony_cibool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg, 783662306a36Sopenharmony_ci unsigned long state) 783762306a36Sopenharmony_ci{ 783862306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 783962306a36Sopenharmony_ci 784062306a36Sopenharmony_ci list_for_each_entry(vif, &cfg->vif_list, list) { 784162306a36Sopenharmony_ci if (test_bit(state, &vif->sme_state)) 784262306a36Sopenharmony_ci return true; 784362306a36Sopenharmony_ci } 784462306a36Sopenharmony_ci return false; 784562306a36Sopenharmony_ci} 784662306a36Sopenharmony_ci 784762306a36Sopenharmony_cistatic inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event, 784862306a36Sopenharmony_ci u8 action) 784962306a36Sopenharmony_ci{ 785062306a36Sopenharmony_ci u8 evt_action; 785162306a36Sopenharmony_ci 785262306a36Sopenharmony_ci spin_lock(&event->vif_event_lock); 785362306a36Sopenharmony_ci evt_action = event->action; 785462306a36Sopenharmony_ci spin_unlock(&event->vif_event_lock); 785562306a36Sopenharmony_ci return evt_action == action; 785662306a36Sopenharmony_ci} 785762306a36Sopenharmony_ci 785862306a36Sopenharmony_civoid brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg, 785962306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif) 786062306a36Sopenharmony_ci{ 786162306a36Sopenharmony_ci struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; 786262306a36Sopenharmony_ci 786362306a36Sopenharmony_ci spin_lock(&event->vif_event_lock); 786462306a36Sopenharmony_ci event->vif = vif; 786562306a36Sopenharmony_ci event->action = 0; 786662306a36Sopenharmony_ci spin_unlock(&event->vif_event_lock); 786762306a36Sopenharmony_ci} 786862306a36Sopenharmony_ci 786962306a36Sopenharmony_cibool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg) 787062306a36Sopenharmony_ci{ 787162306a36Sopenharmony_ci struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; 787262306a36Sopenharmony_ci bool armed; 787362306a36Sopenharmony_ci 787462306a36Sopenharmony_ci spin_lock(&event->vif_event_lock); 787562306a36Sopenharmony_ci armed = event->vif != NULL; 787662306a36Sopenharmony_ci spin_unlock(&event->vif_event_lock); 787762306a36Sopenharmony_ci 787862306a36Sopenharmony_ci return armed; 787962306a36Sopenharmony_ci} 788062306a36Sopenharmony_ci 788162306a36Sopenharmony_ciint brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg, 788262306a36Sopenharmony_ci u8 action, ulong timeout) 788362306a36Sopenharmony_ci{ 788462306a36Sopenharmony_ci struct brcmf_cfg80211_vif_event *event = &cfg->vif_event; 788562306a36Sopenharmony_ci 788662306a36Sopenharmony_ci return wait_event_timeout(event->vif_wq, 788762306a36Sopenharmony_ci vif_event_equals(event, action), timeout); 788862306a36Sopenharmony_ci} 788962306a36Sopenharmony_ci 789062306a36Sopenharmony_cistatic bool brmcf_use_iso3166_ccode_fallback(struct brcmf_pub *drvr) 789162306a36Sopenharmony_ci{ 789262306a36Sopenharmony_ci if (drvr->settings->trivial_ccode_map) 789362306a36Sopenharmony_ci return true; 789462306a36Sopenharmony_ci 789562306a36Sopenharmony_ci switch (drvr->bus_if->chip) { 789662306a36Sopenharmony_ci case BRCM_CC_43430_CHIP_ID: 789762306a36Sopenharmony_ci case BRCM_CC_4345_CHIP_ID: 789862306a36Sopenharmony_ci case BRCM_CC_4356_CHIP_ID: 789962306a36Sopenharmony_ci case BRCM_CC_43602_CHIP_ID: 790062306a36Sopenharmony_ci return true; 790162306a36Sopenharmony_ci default: 790262306a36Sopenharmony_ci return false; 790362306a36Sopenharmony_ci } 790462306a36Sopenharmony_ci} 790562306a36Sopenharmony_ci 790662306a36Sopenharmony_cistatic s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2], 790762306a36Sopenharmony_ci struct brcmf_fil_country_le *ccreq) 790862306a36Sopenharmony_ci{ 790962306a36Sopenharmony_ci struct brcmfmac_pd_cc *country_codes; 791062306a36Sopenharmony_ci struct brcmfmac_pd_cc_entry *cc; 791162306a36Sopenharmony_ci s32 found_index; 791262306a36Sopenharmony_ci int i; 791362306a36Sopenharmony_ci 791462306a36Sopenharmony_ci if ((alpha2[0] == ccreq->country_abbrev[0]) && 791562306a36Sopenharmony_ci (alpha2[1] == ccreq->country_abbrev[1])) { 791662306a36Sopenharmony_ci brcmf_dbg(TRACE, "Country code already set\n"); 791762306a36Sopenharmony_ci return -EAGAIN; 791862306a36Sopenharmony_ci } 791962306a36Sopenharmony_ci 792062306a36Sopenharmony_ci country_codes = drvr->settings->country_codes; 792162306a36Sopenharmony_ci if (!country_codes) { 792262306a36Sopenharmony_ci if (brmcf_use_iso3166_ccode_fallback(drvr)) { 792362306a36Sopenharmony_ci brcmf_dbg(TRACE, "No country codes configured for device, using ISO3166 code and 0 rev\n"); 792462306a36Sopenharmony_ci memset(ccreq, 0, sizeof(*ccreq)); 792562306a36Sopenharmony_ci ccreq->country_abbrev[0] = alpha2[0]; 792662306a36Sopenharmony_ci ccreq->country_abbrev[1] = alpha2[1]; 792762306a36Sopenharmony_ci ccreq->ccode[0] = alpha2[0]; 792862306a36Sopenharmony_ci ccreq->ccode[1] = alpha2[1]; 792962306a36Sopenharmony_ci return 0; 793062306a36Sopenharmony_ci } 793162306a36Sopenharmony_ci 793262306a36Sopenharmony_ci brcmf_dbg(TRACE, "No country codes configured for device\n"); 793362306a36Sopenharmony_ci return -EINVAL; 793462306a36Sopenharmony_ci } 793562306a36Sopenharmony_ci 793662306a36Sopenharmony_ci found_index = -1; 793762306a36Sopenharmony_ci for (i = 0; i < country_codes->table_size; i++) { 793862306a36Sopenharmony_ci cc = &country_codes->table[i]; 793962306a36Sopenharmony_ci if ((cc->iso3166[0] == '\0') && (found_index == -1)) 794062306a36Sopenharmony_ci found_index = i; 794162306a36Sopenharmony_ci if ((cc->iso3166[0] == alpha2[0]) && 794262306a36Sopenharmony_ci (cc->iso3166[1] == alpha2[1])) { 794362306a36Sopenharmony_ci found_index = i; 794462306a36Sopenharmony_ci break; 794562306a36Sopenharmony_ci } 794662306a36Sopenharmony_ci } 794762306a36Sopenharmony_ci if (found_index == -1) { 794862306a36Sopenharmony_ci brcmf_dbg(TRACE, "No country code match found\n"); 794962306a36Sopenharmony_ci return -EINVAL; 795062306a36Sopenharmony_ci } 795162306a36Sopenharmony_ci memset(ccreq, 0, sizeof(*ccreq)); 795262306a36Sopenharmony_ci ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev); 795362306a36Sopenharmony_ci memcpy(ccreq->ccode, country_codes->table[found_index].cc, 795462306a36Sopenharmony_ci BRCMF_COUNTRY_BUF_SZ); 795562306a36Sopenharmony_ci ccreq->country_abbrev[0] = alpha2[0]; 795662306a36Sopenharmony_ci ccreq->country_abbrev[1] = alpha2[1]; 795762306a36Sopenharmony_ci ccreq->country_abbrev[2] = 0; 795862306a36Sopenharmony_ci 795962306a36Sopenharmony_ci return 0; 796062306a36Sopenharmony_ci} 796162306a36Sopenharmony_ci 796262306a36Sopenharmony_cistatic int 796362306a36Sopenharmony_cibrcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey) 796462306a36Sopenharmony_ci{ 796562306a36Sopenharmony_ci int i; 796662306a36Sopenharmony_ci char *token; 796762306a36Sopenharmony_ci char delim[] = "\n "; 796862306a36Sopenharmony_ci unsigned long val; 796962306a36Sopenharmony_ci int err = 0; 797062306a36Sopenharmony_ci 797162306a36Sopenharmony_ci token = strsep(&buf, delim); 797262306a36Sopenharmony_ci while (token) { 797362306a36Sopenharmony_ci if (!strcmp(token, "OBSS")) { 797462306a36Sopenharmony_ci for (i = 0; i < OBSS_TOKEN_IDX; i++) 797562306a36Sopenharmony_ci token = strsep(&buf, delim); 797662306a36Sopenharmony_ci err = kstrtoul(token, 10, &val); 797762306a36Sopenharmony_ci if (err) 797862306a36Sopenharmony_ci break; 797962306a36Sopenharmony_ci survey->obss = val; 798062306a36Sopenharmony_ci } 798162306a36Sopenharmony_ci 798262306a36Sopenharmony_ci if (!strcmp(token, "IBSS")) { 798362306a36Sopenharmony_ci for (i = 0; i < IBSS_TOKEN_IDX; i++) 798462306a36Sopenharmony_ci token = strsep(&buf, delim); 798562306a36Sopenharmony_ci err = kstrtoul(token, 10, &val); 798662306a36Sopenharmony_ci if (err) 798762306a36Sopenharmony_ci break; 798862306a36Sopenharmony_ci survey->ibss = val; 798962306a36Sopenharmony_ci } 799062306a36Sopenharmony_ci 799162306a36Sopenharmony_ci if (!strcmp(token, "TXDur")) { 799262306a36Sopenharmony_ci for (i = 0; i < TX_TOKEN_IDX; i++) 799362306a36Sopenharmony_ci token = strsep(&buf, delim); 799462306a36Sopenharmony_ci err = kstrtoul(token, 10, &val); 799562306a36Sopenharmony_ci if (err) 799662306a36Sopenharmony_ci break; 799762306a36Sopenharmony_ci survey->tx = val; 799862306a36Sopenharmony_ci } 799962306a36Sopenharmony_ci 800062306a36Sopenharmony_ci if (!strcmp(token, "Category")) { 800162306a36Sopenharmony_ci for (i = 0; i < CTG_TOKEN_IDX; i++) 800262306a36Sopenharmony_ci token = strsep(&buf, delim); 800362306a36Sopenharmony_ci err = kstrtoul(token, 10, &val); 800462306a36Sopenharmony_ci if (err) 800562306a36Sopenharmony_ci break; 800662306a36Sopenharmony_ci survey->no_ctg = val; 800762306a36Sopenharmony_ci } 800862306a36Sopenharmony_ci 800962306a36Sopenharmony_ci if (!strcmp(token, "Packet")) { 801062306a36Sopenharmony_ci for (i = 0; i < PKT_TOKEN_IDX; i++) 801162306a36Sopenharmony_ci token = strsep(&buf, delim); 801262306a36Sopenharmony_ci err = kstrtoul(token, 10, &val); 801362306a36Sopenharmony_ci if (err) 801462306a36Sopenharmony_ci break; 801562306a36Sopenharmony_ci survey->no_pckt = val; 801662306a36Sopenharmony_ci } 801762306a36Sopenharmony_ci 801862306a36Sopenharmony_ci if (!strcmp(token, "Opp(time):")) { 801962306a36Sopenharmony_ci for (i = 0; i < IDLE_TOKEN_IDX; i++) 802062306a36Sopenharmony_ci token = strsep(&buf, delim); 802162306a36Sopenharmony_ci err = kstrtoul(token, 10, &val); 802262306a36Sopenharmony_ci if (err) 802362306a36Sopenharmony_ci break; 802462306a36Sopenharmony_ci survey->idle = val; 802562306a36Sopenharmony_ci } 802662306a36Sopenharmony_ci 802762306a36Sopenharmony_ci token = strsep(&buf, delim); 802862306a36Sopenharmony_ci } 802962306a36Sopenharmony_ci 803062306a36Sopenharmony_ci return err; 803162306a36Sopenharmony_ci} 803262306a36Sopenharmony_ci 803362306a36Sopenharmony_cistatic int 803462306a36Sopenharmony_cibrcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req, 803562306a36Sopenharmony_ci struct brcmf_dump_survey *survey) 803662306a36Sopenharmony_ci{ 803762306a36Sopenharmony_ci struct cca_stats_n_flags *results; 803862306a36Sopenharmony_ci char *buf; 803962306a36Sopenharmony_ci int err; 804062306a36Sopenharmony_ci 804162306a36Sopenharmony_ci buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL); 804262306a36Sopenharmony_ci if (!buf) 804362306a36Sopenharmony_ci return -ENOMEM; 804462306a36Sopenharmony_ci 804562306a36Sopenharmony_ci memcpy(buf, &req, sizeof(struct cca_msrmnt_query)); 804662306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "dump_obss", 804762306a36Sopenharmony_ci buf, BRCMF_DCMD_MEDLEN); 804862306a36Sopenharmony_ci if (err) { 804962306a36Sopenharmony_ci brcmf_err("dump_obss error (%d)\n", err); 805062306a36Sopenharmony_ci err = -EINVAL; 805162306a36Sopenharmony_ci goto exit; 805262306a36Sopenharmony_ci } 805362306a36Sopenharmony_ci results = (struct cca_stats_n_flags *)(buf); 805462306a36Sopenharmony_ci 805562306a36Sopenharmony_ci if (req.msrmnt_query) 805662306a36Sopenharmony_ci brcmf_parse_dump_obss(results->buf, survey); 805762306a36Sopenharmony_ci 805862306a36Sopenharmony_ciexit: 805962306a36Sopenharmony_ci kfree(buf); 806062306a36Sopenharmony_ci return err; 806162306a36Sopenharmony_ci} 806262306a36Sopenharmony_ci 806362306a36Sopenharmony_cistatic s32 806462306a36Sopenharmony_cibrcmf_set_channel(struct brcmf_cfg80211_info *cfg, struct ieee80211_channel *chan) 806562306a36Sopenharmony_ci{ 806662306a36Sopenharmony_ci u16 chspec = 0; 806762306a36Sopenharmony_ci int err = 0; 806862306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); 806962306a36Sopenharmony_ci 807062306a36Sopenharmony_ci if (chan->flags & IEEE80211_CHAN_DISABLED) 807162306a36Sopenharmony_ci return -EINVAL; 807262306a36Sopenharmony_ci 807362306a36Sopenharmony_ci /* set_channel */ 807462306a36Sopenharmony_ci chspec = channel_to_chanspec(&cfg->d11inf, chan); 807562306a36Sopenharmony_ci if (chspec != INVCHANSPEC) { 807662306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec); 807762306a36Sopenharmony_ci if (err) { 807862306a36Sopenharmony_ci brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err); 807962306a36Sopenharmony_ci err = -EINVAL; 808062306a36Sopenharmony_ci } 808162306a36Sopenharmony_ci } else { 808262306a36Sopenharmony_ci brcmf_err("failed to convert host chanspec to fw chanspec\n"); 808362306a36Sopenharmony_ci err = -EINVAL; 808462306a36Sopenharmony_ci } 808562306a36Sopenharmony_ci 808662306a36Sopenharmony_ci return err; 808762306a36Sopenharmony_ci} 808862306a36Sopenharmony_ci 808962306a36Sopenharmony_cistatic int 809062306a36Sopenharmony_cibrcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev, 809162306a36Sopenharmony_ci int idx, struct survey_info *info) 809262306a36Sopenharmony_ci{ 809362306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 809462306a36Sopenharmony_ci struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg)); 809562306a36Sopenharmony_ci struct brcmf_dump_survey survey = {}; 809662306a36Sopenharmony_ci struct ieee80211_supported_band *band; 809762306a36Sopenharmony_ci enum nl80211_band band_id; 809862306a36Sopenharmony_ci struct cca_msrmnt_query req; 809962306a36Sopenharmony_ci u32 noise; 810062306a36Sopenharmony_ci int err; 810162306a36Sopenharmony_ci 810262306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx); 810362306a36Sopenharmony_ci 810462306a36Sopenharmony_ci /* Do not run survey when VIF in CONNECTING / CONNECTED states */ 810562306a36Sopenharmony_ci if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) || 810662306a36Sopenharmony_ci (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) { 810762306a36Sopenharmony_ci return -EBUSY; 810862306a36Sopenharmony_ci } 810962306a36Sopenharmony_ci 811062306a36Sopenharmony_ci for (band_id = 0; band_id < NUM_NL80211_BANDS; band_id++) { 811162306a36Sopenharmony_ci band = wiphy->bands[band_id]; 811262306a36Sopenharmony_ci if (!band) 811362306a36Sopenharmony_ci continue; 811462306a36Sopenharmony_ci if (idx >= band->n_channels) { 811562306a36Sopenharmony_ci idx -= band->n_channels; 811662306a36Sopenharmony_ci continue; 811762306a36Sopenharmony_ci } 811862306a36Sopenharmony_ci 811962306a36Sopenharmony_ci info->channel = &band->channels[idx]; 812062306a36Sopenharmony_ci break; 812162306a36Sopenharmony_ci } 812262306a36Sopenharmony_ci if (band_id == NUM_NL80211_BANDS) 812362306a36Sopenharmony_ci return -ENOENT; 812462306a36Sopenharmony_ci 812562306a36Sopenharmony_ci /* Setting current channel to the requested channel */ 812662306a36Sopenharmony_ci info->filled = 0; 812762306a36Sopenharmony_ci if (brcmf_set_channel(cfg, info->channel)) 812862306a36Sopenharmony_ci return 0; 812962306a36Sopenharmony_ci 813062306a36Sopenharmony_ci /* Disable mpc */ 813162306a36Sopenharmony_ci brcmf_set_mpc(ifp, 0); 813262306a36Sopenharmony_ci 813362306a36Sopenharmony_ci /* Set interface up, explicitly. */ 813462306a36Sopenharmony_ci err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1); 813562306a36Sopenharmony_ci if (err) { 813662306a36Sopenharmony_ci brcmf_err("set interface up failed, err = %d\n", err); 813762306a36Sopenharmony_ci goto exit; 813862306a36Sopenharmony_ci } 813962306a36Sopenharmony_ci 814062306a36Sopenharmony_ci /* Get noise value */ 814162306a36Sopenharmony_ci err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise); 814262306a36Sopenharmony_ci if (err) { 814362306a36Sopenharmony_ci brcmf_err("Get Phy Noise failed, use dummy value\n"); 814462306a36Sopenharmony_ci noise = CHAN_NOISE_DUMMY; 814562306a36Sopenharmony_ci } 814662306a36Sopenharmony_ci 814762306a36Sopenharmony_ci /* Start Measurement for obss stats on current channel */ 814862306a36Sopenharmony_ci req.msrmnt_query = 0; 814962306a36Sopenharmony_ci req.time_req = ACS_MSRMNT_DELAY; 815062306a36Sopenharmony_ci err = brcmf_dump_obss(ifp, req, &survey); 815162306a36Sopenharmony_ci if (err) 815262306a36Sopenharmony_ci goto exit; 815362306a36Sopenharmony_ci 815462306a36Sopenharmony_ci /* Add 10 ms for IOVAR completion */ 815562306a36Sopenharmony_ci msleep(ACS_MSRMNT_DELAY + 10); 815662306a36Sopenharmony_ci 815762306a36Sopenharmony_ci /* Issue IOVAR to collect measurement results */ 815862306a36Sopenharmony_ci req.msrmnt_query = 1; 815962306a36Sopenharmony_ci err = brcmf_dump_obss(ifp, req, &survey); 816062306a36Sopenharmony_ci if (err) 816162306a36Sopenharmony_ci goto exit; 816262306a36Sopenharmony_ci 816362306a36Sopenharmony_ci info->noise = noise; 816462306a36Sopenharmony_ci info->time = ACS_MSRMNT_DELAY; 816562306a36Sopenharmony_ci info->time_busy = ACS_MSRMNT_DELAY - survey.idle; 816662306a36Sopenharmony_ci info->time_rx = survey.obss + survey.ibss + survey.no_ctg + 816762306a36Sopenharmony_ci survey.no_pckt; 816862306a36Sopenharmony_ci info->time_tx = survey.tx; 816962306a36Sopenharmony_ci info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME | 817062306a36Sopenharmony_ci SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX | 817162306a36Sopenharmony_ci SURVEY_INFO_TIME_TX; 817262306a36Sopenharmony_ci 817362306a36Sopenharmony_ci brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n", 817462306a36Sopenharmony_ci ieee80211_frequency_to_channel(info->channel->center_freq), 817562306a36Sopenharmony_ci ACS_MSRMNT_DELAY); 817662306a36Sopenharmony_ci brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n", 817762306a36Sopenharmony_ci info->noise, info->time_busy, info->time_rx, info->time_tx); 817862306a36Sopenharmony_ci 817962306a36Sopenharmony_ciexit: 818062306a36Sopenharmony_ci if (!brcmf_is_apmode(ifp->vif)) 818162306a36Sopenharmony_ci brcmf_set_mpc(ifp, 1); 818262306a36Sopenharmony_ci return err; 818362306a36Sopenharmony_ci} 818462306a36Sopenharmony_ci 818562306a36Sopenharmony_cistatic void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy, 818662306a36Sopenharmony_ci struct regulatory_request *req) 818762306a36Sopenharmony_ci{ 818862306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy); 818962306a36Sopenharmony_ci struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0); 819062306a36Sopenharmony_ci struct brcmf_pub *drvr = cfg->pub; 819162306a36Sopenharmony_ci struct brcmf_fil_country_le ccreq; 819262306a36Sopenharmony_ci s32 err; 819362306a36Sopenharmony_ci int i; 819462306a36Sopenharmony_ci 819562306a36Sopenharmony_ci /* The country code gets set to "00" by default at boot, ignore */ 819662306a36Sopenharmony_ci if (req->alpha2[0] == '0' && req->alpha2[1] == '0') 819762306a36Sopenharmony_ci return; 819862306a36Sopenharmony_ci 819962306a36Sopenharmony_ci /* ignore non-ISO3166 country codes */ 820062306a36Sopenharmony_ci for (i = 0; i < 2; i++) 820162306a36Sopenharmony_ci if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') { 820262306a36Sopenharmony_ci bphy_err(drvr, "not an ISO3166 code (0x%02x 0x%02x)\n", 820362306a36Sopenharmony_ci req->alpha2[0], req->alpha2[1]); 820462306a36Sopenharmony_ci return; 820562306a36Sopenharmony_ci } 820662306a36Sopenharmony_ci 820762306a36Sopenharmony_ci brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator, 820862306a36Sopenharmony_ci req->alpha2[0], req->alpha2[1]); 820962306a36Sopenharmony_ci 821062306a36Sopenharmony_ci err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq)); 821162306a36Sopenharmony_ci if (err) { 821262306a36Sopenharmony_ci bphy_err(drvr, "Country code iovar returned err = %d\n", err); 821362306a36Sopenharmony_ci return; 821462306a36Sopenharmony_ci } 821562306a36Sopenharmony_ci 821662306a36Sopenharmony_ci err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq); 821762306a36Sopenharmony_ci if (err) 821862306a36Sopenharmony_ci return; 821962306a36Sopenharmony_ci 822062306a36Sopenharmony_ci err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq)); 822162306a36Sopenharmony_ci if (err) { 822262306a36Sopenharmony_ci bphy_err(drvr, "Firmware rejected country setting\n"); 822362306a36Sopenharmony_ci return; 822462306a36Sopenharmony_ci } 822562306a36Sopenharmony_ci brcmf_setup_wiphybands(cfg); 822662306a36Sopenharmony_ci} 822762306a36Sopenharmony_ci 822862306a36Sopenharmony_cistatic void brcmf_free_wiphy(struct wiphy *wiphy) 822962306a36Sopenharmony_ci{ 823062306a36Sopenharmony_ci int i; 823162306a36Sopenharmony_ci 823262306a36Sopenharmony_ci if (!wiphy) 823362306a36Sopenharmony_ci return; 823462306a36Sopenharmony_ci 823562306a36Sopenharmony_ci if (wiphy->iface_combinations) { 823662306a36Sopenharmony_ci for (i = 0; i < wiphy->n_iface_combinations; i++) 823762306a36Sopenharmony_ci kfree(wiphy->iface_combinations[i].limits); 823862306a36Sopenharmony_ci } 823962306a36Sopenharmony_ci kfree(wiphy->iface_combinations); 824062306a36Sopenharmony_ci if (wiphy->bands[NL80211_BAND_2GHZ]) { 824162306a36Sopenharmony_ci kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels); 824262306a36Sopenharmony_ci kfree(wiphy->bands[NL80211_BAND_2GHZ]); 824362306a36Sopenharmony_ci } 824462306a36Sopenharmony_ci if (wiphy->bands[NL80211_BAND_5GHZ]) { 824562306a36Sopenharmony_ci kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels); 824662306a36Sopenharmony_ci kfree(wiphy->bands[NL80211_BAND_5GHZ]); 824762306a36Sopenharmony_ci } 824862306a36Sopenharmony_ci#if IS_ENABLED(CONFIG_PM) 824962306a36Sopenharmony_ci if (wiphy->wowlan != &brcmf_wowlan_support) 825062306a36Sopenharmony_ci kfree(wiphy->wowlan); 825162306a36Sopenharmony_ci#endif 825262306a36Sopenharmony_ci} 825362306a36Sopenharmony_ci 825462306a36Sopenharmony_cistruct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr, 825562306a36Sopenharmony_ci struct cfg80211_ops *ops, 825662306a36Sopenharmony_ci bool p2pdev_forced) 825762306a36Sopenharmony_ci{ 825862306a36Sopenharmony_ci struct wiphy *wiphy = drvr->wiphy; 825962306a36Sopenharmony_ci struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev; 826062306a36Sopenharmony_ci struct brcmf_cfg80211_info *cfg; 826162306a36Sopenharmony_ci struct brcmf_cfg80211_vif *vif; 826262306a36Sopenharmony_ci struct brcmf_if *ifp; 826362306a36Sopenharmony_ci s32 err = 0; 826462306a36Sopenharmony_ci s32 io_type; 826562306a36Sopenharmony_ci u16 *cap = NULL; 826662306a36Sopenharmony_ci 826762306a36Sopenharmony_ci if (!ndev) { 826862306a36Sopenharmony_ci bphy_err(drvr, "ndev is invalid\n"); 826962306a36Sopenharmony_ci return NULL; 827062306a36Sopenharmony_ci } 827162306a36Sopenharmony_ci 827262306a36Sopenharmony_ci cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 827362306a36Sopenharmony_ci if (!cfg) { 827462306a36Sopenharmony_ci bphy_err(drvr, "Could not allocate wiphy device\n"); 827562306a36Sopenharmony_ci return NULL; 827662306a36Sopenharmony_ci } 827762306a36Sopenharmony_ci 827862306a36Sopenharmony_ci cfg->wiphy = wiphy; 827962306a36Sopenharmony_ci cfg->pub = drvr; 828062306a36Sopenharmony_ci init_vif_event(&cfg->vif_event); 828162306a36Sopenharmony_ci INIT_LIST_HEAD(&cfg->vif_list); 828262306a36Sopenharmony_ci 828362306a36Sopenharmony_ci vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION); 828462306a36Sopenharmony_ci if (IS_ERR(vif)) 828562306a36Sopenharmony_ci goto wiphy_out; 828662306a36Sopenharmony_ci 828762306a36Sopenharmony_ci ifp = netdev_priv(ndev); 828862306a36Sopenharmony_ci vif->ifp = ifp; 828962306a36Sopenharmony_ci vif->wdev.netdev = ndev; 829062306a36Sopenharmony_ci ndev->ieee80211_ptr = &vif->wdev; 829162306a36Sopenharmony_ci SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy)); 829262306a36Sopenharmony_ci 829362306a36Sopenharmony_ci err = wl_init_priv(cfg); 829462306a36Sopenharmony_ci if (err) { 829562306a36Sopenharmony_ci bphy_err(drvr, "Failed to init iwm_priv (%d)\n", err); 829662306a36Sopenharmony_ci brcmf_free_vif(vif); 829762306a36Sopenharmony_ci goto wiphy_out; 829862306a36Sopenharmony_ci } 829962306a36Sopenharmony_ci ifp->vif = vif; 830062306a36Sopenharmony_ci 830162306a36Sopenharmony_ci /* determine d11 io type before wiphy setup */ 830262306a36Sopenharmony_ci err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type); 830362306a36Sopenharmony_ci if (err) { 830462306a36Sopenharmony_ci bphy_err(drvr, "Failed to get D11 version (%d)\n", err); 830562306a36Sopenharmony_ci goto priv_out; 830662306a36Sopenharmony_ci } 830762306a36Sopenharmony_ci cfg->d11inf.io_type = (u8)io_type; 830862306a36Sopenharmony_ci brcmu_d11_attach(&cfg->d11inf); 830962306a36Sopenharmony_ci 831062306a36Sopenharmony_ci /* regulatory notifer below needs access to cfg so 831162306a36Sopenharmony_ci * assign it now. 831262306a36Sopenharmony_ci */ 831362306a36Sopenharmony_ci drvr->config = cfg; 831462306a36Sopenharmony_ci 831562306a36Sopenharmony_ci err = brcmf_setup_wiphy(wiphy, ifp); 831662306a36Sopenharmony_ci if (err < 0) 831762306a36Sopenharmony_ci goto priv_out; 831862306a36Sopenharmony_ci 831962306a36Sopenharmony_ci brcmf_dbg(INFO, "Registering custom regulatory\n"); 832062306a36Sopenharmony_ci wiphy->reg_notifier = brcmf_cfg80211_reg_notifier; 832162306a36Sopenharmony_ci wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG; 832262306a36Sopenharmony_ci wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom); 832362306a36Sopenharmony_ci 832462306a36Sopenharmony_ci /* firmware defaults to 40MHz disabled in 2G band. We signal 832562306a36Sopenharmony_ci * cfg80211 here that we do and have it decide we can enable 832662306a36Sopenharmony_ci * it. But first check if device does support 2G operation. 832762306a36Sopenharmony_ci */ 832862306a36Sopenharmony_ci if (wiphy->bands[NL80211_BAND_2GHZ]) { 832962306a36Sopenharmony_ci cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap; 833062306a36Sopenharmony_ci *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40; 833162306a36Sopenharmony_ci } 833262306a36Sopenharmony_ci#ifdef CONFIG_PM 833362306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) 833462306a36Sopenharmony_ci ops->set_rekey_data = brcmf_cfg80211_set_rekey_data; 833562306a36Sopenharmony_ci#endif 833662306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS)) 833762306a36Sopenharmony_ci ops->dump_survey = brcmf_cfg80211_dump_survey; 833862306a36Sopenharmony_ci 833962306a36Sopenharmony_ci err = wiphy_register(wiphy); 834062306a36Sopenharmony_ci if (err < 0) { 834162306a36Sopenharmony_ci bphy_err(drvr, "Could not register wiphy device (%d)\n", err); 834262306a36Sopenharmony_ci goto priv_out; 834362306a36Sopenharmony_ci } 834462306a36Sopenharmony_ci 834562306a36Sopenharmony_ci err = brcmf_setup_wiphybands(cfg); 834662306a36Sopenharmony_ci if (err) { 834762306a36Sopenharmony_ci bphy_err(drvr, "Setting wiphy bands failed (%d)\n", err); 834862306a36Sopenharmony_ci goto wiphy_unreg_out; 834962306a36Sopenharmony_ci } 835062306a36Sopenharmony_ci 835162306a36Sopenharmony_ci /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(), 835262306a36Sopenharmony_ci * setup 40MHz in 2GHz band and enable OBSS scanning. 835362306a36Sopenharmony_ci */ 835462306a36Sopenharmony_ci if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) { 835562306a36Sopenharmony_ci err = brcmf_enable_bw40_2g(cfg); 835662306a36Sopenharmony_ci if (!err) 835762306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "obss_coex", 835862306a36Sopenharmony_ci BRCMF_OBSS_COEX_AUTO); 835962306a36Sopenharmony_ci else 836062306a36Sopenharmony_ci *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40; 836162306a36Sopenharmony_ci } 836262306a36Sopenharmony_ci 836362306a36Sopenharmony_ci err = brcmf_fweh_activate_events(ifp); 836462306a36Sopenharmony_ci if (err) { 836562306a36Sopenharmony_ci bphy_err(drvr, "FWEH activation failed (%d)\n", err); 836662306a36Sopenharmony_ci goto wiphy_unreg_out; 836762306a36Sopenharmony_ci } 836862306a36Sopenharmony_ci 836962306a36Sopenharmony_ci err = brcmf_p2p_attach(cfg, p2pdev_forced); 837062306a36Sopenharmony_ci if (err) { 837162306a36Sopenharmony_ci bphy_err(drvr, "P2P initialisation failed (%d)\n", err); 837262306a36Sopenharmony_ci goto wiphy_unreg_out; 837362306a36Sopenharmony_ci } 837462306a36Sopenharmony_ci err = brcmf_btcoex_attach(cfg); 837562306a36Sopenharmony_ci if (err) { 837662306a36Sopenharmony_ci bphy_err(drvr, "BT-coex initialisation failed (%d)\n", err); 837762306a36Sopenharmony_ci brcmf_p2p_detach(&cfg->p2p); 837862306a36Sopenharmony_ci goto wiphy_unreg_out; 837962306a36Sopenharmony_ci } 838062306a36Sopenharmony_ci err = brcmf_pno_attach(cfg); 838162306a36Sopenharmony_ci if (err) { 838262306a36Sopenharmony_ci bphy_err(drvr, "PNO initialisation failed (%d)\n", err); 838362306a36Sopenharmony_ci brcmf_btcoex_detach(cfg); 838462306a36Sopenharmony_ci brcmf_p2p_detach(&cfg->p2p); 838562306a36Sopenharmony_ci goto wiphy_unreg_out; 838662306a36Sopenharmony_ci } 838762306a36Sopenharmony_ci 838862306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) { 838962306a36Sopenharmony_ci err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1); 839062306a36Sopenharmony_ci if (err) { 839162306a36Sopenharmony_ci brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err); 839262306a36Sopenharmony_ci wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS; 839362306a36Sopenharmony_ci } else { 839462306a36Sopenharmony_ci brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT, 839562306a36Sopenharmony_ci brcmf_notify_tdls_peer_event); 839662306a36Sopenharmony_ci } 839762306a36Sopenharmony_ci } 839862306a36Sopenharmony_ci 839962306a36Sopenharmony_ci /* (re-) activate FWEH event handling */ 840062306a36Sopenharmony_ci err = brcmf_fweh_activate_events(ifp); 840162306a36Sopenharmony_ci if (err) { 840262306a36Sopenharmony_ci bphy_err(drvr, "FWEH activation failed (%d)\n", err); 840362306a36Sopenharmony_ci goto detach; 840462306a36Sopenharmony_ci } 840562306a36Sopenharmony_ci 840662306a36Sopenharmony_ci /* Fill in some of the advertised nl80211 supported features */ 840762306a36Sopenharmony_ci if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) { 840862306a36Sopenharmony_ci wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR; 840962306a36Sopenharmony_ci#ifdef CONFIG_PM 841062306a36Sopenharmony_ci if (wiphy->wowlan && 841162306a36Sopenharmony_ci wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT) 841262306a36Sopenharmony_ci wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR; 841362306a36Sopenharmony_ci#endif 841462306a36Sopenharmony_ci } 841562306a36Sopenharmony_ci 841662306a36Sopenharmony_ci return cfg; 841762306a36Sopenharmony_ci 841862306a36Sopenharmony_cidetach: 841962306a36Sopenharmony_ci brcmf_pno_detach(cfg); 842062306a36Sopenharmony_ci brcmf_btcoex_detach(cfg); 842162306a36Sopenharmony_ci brcmf_p2p_detach(&cfg->p2p); 842262306a36Sopenharmony_ciwiphy_unreg_out: 842362306a36Sopenharmony_ci wiphy_unregister(cfg->wiphy); 842462306a36Sopenharmony_cipriv_out: 842562306a36Sopenharmony_ci wl_deinit_priv(cfg); 842662306a36Sopenharmony_ci brcmf_free_vif(vif); 842762306a36Sopenharmony_ci ifp->vif = NULL; 842862306a36Sopenharmony_ciwiphy_out: 842962306a36Sopenharmony_ci brcmf_free_wiphy(wiphy); 843062306a36Sopenharmony_ci kfree(cfg); 843162306a36Sopenharmony_ci return NULL; 843262306a36Sopenharmony_ci} 843362306a36Sopenharmony_ci 843462306a36Sopenharmony_civoid brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg) 843562306a36Sopenharmony_ci{ 843662306a36Sopenharmony_ci if (!cfg) 843762306a36Sopenharmony_ci return; 843862306a36Sopenharmony_ci 843962306a36Sopenharmony_ci brcmf_pno_detach(cfg); 844062306a36Sopenharmony_ci brcmf_btcoex_detach(cfg); 844162306a36Sopenharmony_ci wiphy_unregister(cfg->wiphy); 844262306a36Sopenharmony_ci wl_deinit_priv(cfg); 844362306a36Sopenharmony_ci cancel_work_sync(&cfg->escan_timeout_work); 844462306a36Sopenharmony_ci brcmf_free_wiphy(cfg->wiphy); 844562306a36Sopenharmony_ci kfree(cfg); 844662306a36Sopenharmony_ci} 8447