1daeccac2SArend van Spriel // SPDX-License-Identifier: ISC
205491d2cSKalle Valo /*
305491d2cSKalle Valo * Copyright (c) 2010 Broadcom Corporation
405491d2cSKalle Valo */
505491d2cSKalle Valo
605491d2cSKalle Valo /* Toplevel file. Relies on dhd_linux.c to send commands to the dongle. */
705491d2cSKalle Valo
805491d2cSKalle Valo #include <linux/kernel.h>
905491d2cSKalle Valo #include <linux/etherdevice.h>
1005491d2cSKalle Valo #include <linux/module.h>
1105491d2cSKalle Valo #include <linux/vmalloc.h>
1205491d2cSKalle Valo #include <net/cfg80211.h>
1305491d2cSKalle Valo #include <net/netlink.h>
1420f2c5faSRafał Miłecki #include <uapi/linux/if_arp.h>
1505491d2cSKalle Valo
1605491d2cSKalle Valo #include <brcmu_utils.h>
1705491d2cSKalle Valo #include <defs.h>
1805491d2cSKalle Valo #include <brcmu_wifi.h>
19a21bf90eSHans de Goede #include <brcm_hw_ids.h>
2005491d2cSKalle Valo #include "core.h"
2105491d2cSKalle Valo #include "debug.h"
2205491d2cSKalle Valo #include "tracepoint.h"
2305491d2cSKalle Valo #include "fwil_types.h"
2405491d2cSKalle Valo #include "p2p.h"
2505491d2cSKalle Valo #include "btcoex.h"
26ac55136fSArend Van Spriel #include "pno.h"
2792072e5fSSaravanan Shanmugham #include "fwsignal.h"
2805491d2cSKalle Valo #include "cfg80211.h"
2905491d2cSKalle Valo #include "feature.h"
3005491d2cSKalle Valo #include "fwil.h"
3105491d2cSKalle Valo #include "proto.h"
3205491d2cSKalle Valo #include "vendor.h"
3305491d2cSKalle Valo #include "bus.h"
3405491d2cSKalle Valo #include "common.h"
358d59a64cSHector Martin #include "fwvid.h"
3605491d2cSKalle Valo
3705491d2cSKalle Valo #define BRCMF_SCAN_IE_LEN_MAX 2048
3805491d2cSKalle Valo
3905491d2cSKalle Valo #define WPA_OUI "\x00\x50\xF2" /* WPA OUI */
4005491d2cSKalle Valo #define WPA_OUI_TYPE 1
4105491d2cSKalle Valo #define RSN_OUI "\x00\x0F\xAC" /* RSN OUI */
4205491d2cSKalle Valo #define WME_OUI_TYPE 2
4305491d2cSKalle Valo #define WPS_OUI_TYPE 4
4405491d2cSKalle Valo
4505491d2cSKalle Valo #define VS_IE_FIXED_HDR_LEN 6
4605491d2cSKalle Valo #define WPA_IE_VERSION_LEN 2
4705491d2cSKalle Valo #define WPA_IE_MIN_OUI_LEN 4
4805491d2cSKalle Valo #define WPA_IE_SUITE_COUNT_LEN 2
4905491d2cSKalle Valo
5005491d2cSKalle Valo #define WPA_CIPHER_NONE 0 /* None */
5105491d2cSKalle Valo #define WPA_CIPHER_WEP_40 1 /* WEP (40-bit) */
5205491d2cSKalle Valo #define WPA_CIPHER_TKIP 2 /* TKIP: default for WPA */
5305491d2cSKalle Valo #define WPA_CIPHER_AES_CCM 4 /* AES (CCM) */
5405491d2cSKalle Valo #define WPA_CIPHER_WEP_104 5 /* WEP (104-bit) */
5505491d2cSKalle Valo
5605491d2cSKalle Valo #define RSN_AKM_NONE 0 /* None (IBSS) */
5705491d2cSKalle Valo #define RSN_AKM_UNSPECIFIED 1 /* Over 802.1x */
5805491d2cSKalle Valo #define RSN_AKM_PSK 2 /* Pre-shared Key */
59240d61a9SHante Meuleman #define RSN_AKM_SHA256_1X 5 /* SHA256, 802.1X */
60240d61a9SHante Meuleman #define RSN_AKM_SHA256_PSK 6 /* SHA256, Pre-shared Key */
61d5f59c96SChung-Hsien Hsu #define RSN_AKM_SAE 8 /* SAE */
6205491d2cSKalle Valo #define RSN_CAP_LEN 2 /* Length of RSN capabilities */
63240d61a9SHante Meuleman #define RSN_CAP_PTK_REPLAY_CNTR_MASK (BIT(2) | BIT(3))
64240d61a9SHante Meuleman #define RSN_CAP_MFPR_MASK BIT(6)
65240d61a9SHante Meuleman #define RSN_CAP_MFPC_MASK BIT(7)
66240d61a9SHante Meuleman #define RSN_PMKID_COUNT_LEN 2
6705491d2cSKalle Valo
6805491d2cSKalle Valo #define VNDR_IE_CMD_LEN 4 /* length of the set command
6905491d2cSKalle Valo * string :"add", "del" (+ NUL)
7005491d2cSKalle Valo */
7105491d2cSKalle Valo #define VNDR_IE_COUNT_OFFSET 4
7205491d2cSKalle Valo #define VNDR_IE_PKTFLAG_OFFSET 8
7305491d2cSKalle Valo #define VNDR_IE_VSIE_OFFSET 12
7405491d2cSKalle Valo #define VNDR_IE_HDR_SIZE 12
7505491d2cSKalle Valo #define VNDR_IE_PARSE_LIMIT 5
7605491d2cSKalle Valo
7705491d2cSKalle Valo #define DOT11_MGMT_HDR_LEN 24 /* d11 management header len */
7805491d2cSKalle Valo #define DOT11_BCN_PRB_FIXED_LEN 12 /* beacon/probe fixed length */
7905491d2cSKalle Valo
8005491d2cSKalle Valo #define BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS 320
8105491d2cSKalle Valo #define BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS 400
8205491d2cSKalle Valo #define BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS 20
8305491d2cSKalle Valo
841678ba8eSHante Meuleman #define BRCMF_SCAN_CHANNEL_TIME 40
851678ba8eSHante Meuleman #define BRCMF_SCAN_UNASSOC_TIME 40
861678ba8eSHante Meuleman #define BRCMF_SCAN_PASSIVE_TIME 120
871678ba8eSHante Meuleman
883021ad9aSHante Meuleman #define BRCMF_ND_INFO_TIMEOUT msecs_to_jiffies(2000)
893021ad9aSHante Meuleman
903dc05ffbSNicolas Saenz Julienne #define BRCMF_PS_MAX_TIMEOUT_MS 2000
913dc05ffbSNicolas Saenz Julienne
926c04deaeSWright Feng /* Dump obss definitions */
9325076fe2SDouble Lo #define ACS_MSRMNT_DELAY 80
946c04deaeSWright Feng #define CHAN_NOISE_DUMMY (-80)
956c04deaeSWright Feng #define OBSS_TOKEN_IDX 15
966c04deaeSWright Feng #define IBSS_TOKEN_IDX 15
976c04deaeSWright Feng #define TX_TOKEN_IDX 14
986c04deaeSWright Feng #define CTG_TOKEN_IDX 13
996c04deaeSWright Feng #define PKT_TOKEN_IDX 15
1006c04deaeSWright Feng #define IDLE_TOKEN_IDX 12
1016c04deaeSWright Feng
10205491d2cSKalle Valo #define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
10305491d2cSKalle Valo (sizeof(struct brcmf_assoc_params_le) - sizeof(u16))
10405491d2cSKalle Valo
1054920ab13SMinsuk Kang #define BRCMF_MAX_CHANSPEC_LIST \
1064920ab13SMinsuk Kang (BRCMF_DCMD_MEDLEN / sizeof(__le32) - 1)
1074920ab13SMinsuk Kang
1086c04deaeSWright Feng struct brcmf_dump_survey {
1096c04deaeSWright Feng u32 obss;
1106c04deaeSWright Feng u32 ibss;
1116c04deaeSWright Feng u32 no_ctg;
1126c04deaeSWright Feng u32 no_pckt;
1136c04deaeSWright Feng u32 tx;
1146c04deaeSWright Feng u32 idle;
1156c04deaeSWright Feng };
1166c04deaeSWright Feng
1176c04deaeSWright Feng struct cca_stats_n_flags {
1186c04deaeSWright Feng u32 msrmnt_time; /* Time for Measurement (msec) */
1196c04deaeSWright Feng u32 msrmnt_done; /* flag set when measurement complete */
1206c04deaeSWright Feng char buf[1];
1216c04deaeSWright Feng };
1226c04deaeSWright Feng
1236c04deaeSWright Feng struct cca_msrmnt_query {
1246c04deaeSWright Feng u32 msrmnt_query;
1256c04deaeSWright Feng u32 time_req;
1266c04deaeSWright Feng };
1276c04deaeSWright Feng
check_vif_up(struct brcmf_cfg80211_vif * vif)12805491d2cSKalle Valo static bool check_vif_up(struct brcmf_cfg80211_vif *vif)
12905491d2cSKalle Valo {
13005491d2cSKalle Valo if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state)) {
13105491d2cSKalle Valo brcmf_dbg(INFO, "device is not ready : status (%lu)\n",
13205491d2cSKalle Valo vif->sme_state);
13305491d2cSKalle Valo return false;
13405491d2cSKalle Valo }
13505491d2cSKalle Valo return true;
13605491d2cSKalle Valo }
13705491d2cSKalle Valo
13805491d2cSKalle Valo #define RATE_TO_BASE100KBPS(rate) (((rate) * 10) / 2)
13905491d2cSKalle Valo #define RATETAB_ENT(_rateid, _flags) \
14005491d2cSKalle Valo { \
14105491d2cSKalle Valo .bitrate = RATE_TO_BASE100KBPS(_rateid), \
14205491d2cSKalle Valo .hw_value = (_rateid), \
14305491d2cSKalle Valo .flags = (_flags), \
14405491d2cSKalle Valo }
14505491d2cSKalle Valo
14605491d2cSKalle Valo static struct ieee80211_rate __wl_rates[] = {
14705491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_1M, 0),
14805491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
14905491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
15005491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
15105491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_6M, 0),
15205491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_9M, 0),
15305491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_12M, 0),
15405491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_18M, 0),
15505491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_24M, 0),
15605491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_36M, 0),
15705491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_48M, 0),
15805491d2cSKalle Valo RATETAB_ENT(BRCM_RATE_54M, 0),
15905491d2cSKalle Valo };
16005491d2cSKalle Valo
16105491d2cSKalle Valo #define wl_g_rates (__wl_rates + 0)
16205491d2cSKalle Valo #define wl_g_rates_size ARRAY_SIZE(__wl_rates)
16305491d2cSKalle Valo #define wl_a_rates (__wl_rates + 4)
16405491d2cSKalle Valo #define wl_a_rates_size (wl_g_rates_size - 4)
16505491d2cSKalle Valo
16605491d2cSKalle Valo #define CHAN2G(_channel, _freq) { \
16757fbcce3SJohannes Berg .band = NL80211_BAND_2GHZ, \
16805491d2cSKalle Valo .center_freq = (_freq), \
16905491d2cSKalle Valo .hw_value = (_channel), \
17005491d2cSKalle Valo .max_antenna_gain = 0, \
17105491d2cSKalle Valo .max_power = 30, \
17205491d2cSKalle Valo }
17305491d2cSKalle Valo
17405491d2cSKalle Valo #define CHAN5G(_channel) { \
17557fbcce3SJohannes Berg .band = NL80211_BAND_5GHZ, \
17605491d2cSKalle Valo .center_freq = 5000 + (5 * (_channel)), \
17705491d2cSKalle Valo .hw_value = (_channel), \
17805491d2cSKalle Valo .max_antenna_gain = 0, \
17905491d2cSKalle Valo .max_power = 30, \
18005491d2cSKalle Valo }
18105491d2cSKalle Valo
18205491d2cSKalle Valo static struct ieee80211_channel __wl_2ghz_channels[] = {
18305491d2cSKalle Valo CHAN2G(1, 2412), CHAN2G(2, 2417), CHAN2G(3, 2422), CHAN2G(4, 2427),
18405491d2cSKalle Valo CHAN2G(5, 2432), CHAN2G(6, 2437), CHAN2G(7, 2442), CHAN2G(8, 2447),
18505491d2cSKalle Valo CHAN2G(9, 2452), CHAN2G(10, 2457), CHAN2G(11, 2462), CHAN2G(12, 2467),
18605491d2cSKalle Valo CHAN2G(13, 2472), CHAN2G(14, 2484)
18705491d2cSKalle Valo };
18805491d2cSKalle Valo
18905491d2cSKalle Valo static struct ieee80211_channel __wl_5ghz_channels[] = {
19005491d2cSKalle Valo CHAN5G(34), CHAN5G(36), CHAN5G(38), CHAN5G(40), CHAN5G(42),
19105491d2cSKalle Valo CHAN5G(44), CHAN5G(46), CHAN5G(48), CHAN5G(52), CHAN5G(56),
19205491d2cSKalle Valo CHAN5G(60), CHAN5G(64), CHAN5G(100), CHAN5G(104), CHAN5G(108),
19305491d2cSKalle Valo CHAN5G(112), CHAN5G(116), CHAN5G(120), CHAN5G(124), CHAN5G(128),
19405491d2cSKalle Valo CHAN5G(132), CHAN5G(136), CHAN5G(140), CHAN5G(144), CHAN5G(149),
19505491d2cSKalle Valo CHAN5G(153), CHAN5G(157), CHAN5G(161), CHAN5G(165)
19605491d2cSKalle Valo };
19705491d2cSKalle Valo
19805491d2cSKalle Valo /* Band templates duplicated per wiphy. The channel info
19905491d2cSKalle Valo * above is added to the band during setup.
20005491d2cSKalle Valo */
20105491d2cSKalle Valo static const struct ieee80211_supported_band __wl_band_2ghz = {
20257fbcce3SJohannes Berg .band = NL80211_BAND_2GHZ,
20305491d2cSKalle Valo .bitrates = wl_g_rates,
20405491d2cSKalle Valo .n_bitrates = wl_g_rates_size,
20505491d2cSKalle Valo };
20605491d2cSKalle Valo
20705491d2cSKalle Valo static const struct ieee80211_supported_band __wl_band_5ghz = {
20857fbcce3SJohannes Berg .band = NL80211_BAND_5GHZ,
20905491d2cSKalle Valo .bitrates = wl_a_rates,
21005491d2cSKalle Valo .n_bitrates = wl_a_rates_size,
21105491d2cSKalle Valo };
21205491d2cSKalle Valo
21305491d2cSKalle Valo /* This is to override regulatory domains defined in cfg80211 module (reg.c)
21405491d2cSKalle Valo * By default world regulatory domain defined in reg.c puts the flags
21505491d2cSKalle Valo * NL80211_RRF_NO_IR for 5GHz channels (for * 36..48 and 149..165).
21605491d2cSKalle Valo * With respect to these flags, wpa_supplicant doesn't * start p2p
21705491d2cSKalle Valo * operations on 5GHz channels. All the changes in world regulatory
21805491d2cSKalle Valo * domain are to be done here.
21905491d2cSKalle Valo */
22005491d2cSKalle Valo static const struct ieee80211_regdomain brcmf_regdom = {
22105491d2cSKalle Valo .n_reg_rules = 4,
22205491d2cSKalle Valo .alpha2 = "99",
22305491d2cSKalle Valo .reg_rules = {
22405491d2cSKalle Valo /* IEEE 802.11b/g, channels 1..11 */
22505491d2cSKalle Valo REG_RULE(2412-10, 2472+10, 40, 6, 20, 0),
22605491d2cSKalle Valo /* If any */
22705491d2cSKalle Valo /* IEEE 802.11 channel 14 - Only JP enables
22805491d2cSKalle Valo * this and for 802.11b only
22905491d2cSKalle Valo */
23005491d2cSKalle Valo REG_RULE(2484-10, 2484+10, 20, 6, 20, 0),
23105491d2cSKalle Valo /* IEEE 802.11a, channel 36..64 */
232fa905092SArend van Spriel REG_RULE(5150-10, 5350+10, 160, 6, 20, 0),
23305491d2cSKalle Valo /* IEEE 802.11a, channel 100..165 */
234fa905092SArend van Spriel REG_RULE(5470-10, 5850+10, 160, 6, 20, 0), }
23505491d2cSKalle Valo };
23605491d2cSKalle Valo
237240d61a9SHante Meuleman /* Note: brcmf_cipher_suites is an array of int defining which cipher suites
238240d61a9SHante Meuleman * are supported. A pointer to this array and the number of entries is passed
239240d61a9SHante Meuleman * on to upper layers. AES_CMAC defines whether or not the driver supports MFP.
240240d61a9SHante Meuleman * So the cipher suite AES_CMAC has to be the last one in the array, and when
241240d61a9SHante Meuleman * device does not support MFP then the number of suites will be decreased by 1
242240d61a9SHante Meuleman */
243240d61a9SHante Meuleman static const u32 brcmf_cipher_suites[] = {
24405491d2cSKalle Valo WLAN_CIPHER_SUITE_WEP40,
24505491d2cSKalle Valo WLAN_CIPHER_SUITE_WEP104,
24605491d2cSKalle Valo WLAN_CIPHER_SUITE_TKIP,
24705491d2cSKalle Valo WLAN_CIPHER_SUITE_CCMP,
248240d61a9SHante Meuleman /* Keep as last entry: */
249240d61a9SHante Meuleman WLAN_CIPHER_SUITE_AES_CMAC
25005491d2cSKalle Valo };
25105491d2cSKalle Valo
25205491d2cSKalle Valo /* Vendor specific ie. id = 221, oui and type defines exact ie */
25305491d2cSKalle Valo struct brcmf_vs_tlv {
25405491d2cSKalle Valo u8 id;
25505491d2cSKalle Valo u8 len;
25605491d2cSKalle Valo u8 oui[3];
25705491d2cSKalle Valo u8 oui_type;
25805491d2cSKalle Valo };
25905491d2cSKalle Valo
26005491d2cSKalle Valo struct parsed_vndr_ie_info {
26105491d2cSKalle Valo u8 *ie_ptr;
26205491d2cSKalle Valo u32 ie_len; /* total length including id & length field */
26305491d2cSKalle Valo struct brcmf_vs_tlv vndrie;
26405491d2cSKalle Valo };
26505491d2cSKalle Valo
26605491d2cSKalle Valo struct parsed_vndr_ies {
26705491d2cSKalle Valo u32 count;
26805491d2cSKalle Valo struct parsed_vndr_ie_info ie_info[VNDR_IE_PARSE_LIMIT];
26905491d2cSKalle Valo };
27005491d2cSKalle Valo
2714388827bSWright Feng #define WL_INTERFACE_CREATE_VER_1 1
2724388827bSWright Feng #define WL_INTERFACE_CREATE_VER_2 2
2734388827bSWright Feng #define WL_INTERFACE_CREATE_VER_3 3
2744388827bSWright Feng #define WL_INTERFACE_CREATE_VER_MAX WL_INTERFACE_CREATE_VER_3
2754388827bSWright Feng
2762b5fb30fSWright Feng #define WL_INTERFACE_MAC_DONT_USE 0x0
2772b5fb30fSWright Feng #define WL_INTERFACE_MAC_USE 0x2
2782b5fb30fSWright Feng
2792b5fb30fSWright Feng #define WL_INTERFACE_CREATE_STA 0x0
2802b5fb30fSWright Feng #define WL_INTERFACE_CREATE_AP 0x1
2812b5fb30fSWright Feng
2824388827bSWright Feng struct wl_interface_create_v1 {
2834388827bSWright Feng u16 ver; /* structure version */
2844388827bSWright Feng u32 flags; /* flags for operation */
2854388827bSWright Feng u8 mac_addr[ETH_ALEN]; /* MAC address */
2864388827bSWright Feng u32 wlc_index; /* optional for wlc index */
2874388827bSWright Feng };
2884388827bSWright Feng
2894388827bSWright Feng struct wl_interface_create_v2 {
2904388827bSWright Feng u16 ver; /* structure version */
2914388827bSWright Feng u8 pad1[2];
2924388827bSWright Feng u32 flags; /* flags for operation */
2934388827bSWright Feng u8 mac_addr[ETH_ALEN]; /* MAC address */
2944388827bSWright Feng u8 iftype; /* type of interface created */
2954388827bSWright Feng u8 pad2;
2964388827bSWright Feng u32 wlc_index; /* optional for wlc index */
2974388827bSWright Feng };
2984388827bSWright Feng
2994388827bSWright Feng struct wl_interface_create_v3 {
3004388827bSWright Feng u16 ver; /* structure version */
3014388827bSWright Feng u16 len; /* length of structure + data */
3024388827bSWright Feng u16 fixed_len; /* length of structure */
3034388827bSWright Feng u8 iftype; /* type of interface created */
3044388827bSWright Feng u8 wlc_index; /* optional for wlc index */
3054388827bSWright Feng u32 flags; /* flags for operation */
3064388827bSWright Feng u8 mac_addr[ETH_ALEN]; /* MAC address */
3074388827bSWright Feng u8 bssid[ETH_ALEN]; /* optional for BSSID */
3084388827bSWright Feng u8 if_index; /* interface index request */
3094388827bSWright Feng u8 pad[3];
3104388827bSWright Feng u8 data[]; /* Optional for specific data */
3112b5fb30fSWright Feng };
3122b5fb30fSWright Feng
nl80211_band_to_fwil(enum nl80211_band band)3137705ba6fSArend van Spriel static u8 nl80211_band_to_fwil(enum nl80211_band band)
3147705ba6fSArend van Spriel {
3157705ba6fSArend van Spriel switch (band) {
3167705ba6fSArend van Spriel case NL80211_BAND_2GHZ:
3177705ba6fSArend van Spriel return WLC_BAND_2G;
3187705ba6fSArend van Spriel case NL80211_BAND_5GHZ:
3197705ba6fSArend van Spriel return WLC_BAND_5G;
3207705ba6fSArend van Spriel default:
3217705ba6fSArend van Spriel WARN_ON(1);
3227705ba6fSArend van Spriel break;
3237705ba6fSArend van Spriel }
3247705ba6fSArend van Spriel return 0;
3257705ba6fSArend van Spriel }
3267705ba6fSArend van Spriel
chandef_to_chanspec(struct brcmu_d11inf * d11inf,struct cfg80211_chan_def * ch)32705491d2cSKalle Valo static u16 chandef_to_chanspec(struct brcmu_d11inf *d11inf,
32805491d2cSKalle Valo struct cfg80211_chan_def *ch)
32905491d2cSKalle Valo {
33005491d2cSKalle Valo struct brcmu_chan ch_inf;
33105491d2cSKalle Valo s32 primary_offset;
33205491d2cSKalle Valo
33305491d2cSKalle Valo brcmf_dbg(TRACE, "chandef: control %d center %d width %d\n",
33405491d2cSKalle Valo ch->chan->center_freq, ch->center_freq1, ch->width);
33505491d2cSKalle Valo ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq1);
33636e8072eSRafał Miłecki primary_offset = ch->chan->center_freq - ch->center_freq1;
33705491d2cSKalle Valo switch (ch->width) {
33805491d2cSKalle Valo case NL80211_CHAN_WIDTH_20:
33905491d2cSKalle Valo case NL80211_CHAN_WIDTH_20_NOHT:
34005491d2cSKalle Valo ch_inf.bw = BRCMU_CHAN_BW_20;
34105491d2cSKalle Valo WARN_ON(primary_offset != 0);
34205491d2cSKalle Valo break;
34305491d2cSKalle Valo case NL80211_CHAN_WIDTH_40:
34405491d2cSKalle Valo ch_inf.bw = BRCMU_CHAN_BW_40;
34536e8072eSRafał Miłecki if (primary_offset > 0)
34605491d2cSKalle Valo ch_inf.sb = BRCMU_CHAN_SB_U;
34705491d2cSKalle Valo else
34805491d2cSKalle Valo ch_inf.sb = BRCMU_CHAN_SB_L;
34905491d2cSKalle Valo break;
35005491d2cSKalle Valo case NL80211_CHAN_WIDTH_80:
35105491d2cSKalle Valo ch_inf.bw = BRCMU_CHAN_BW_80;
35236e8072eSRafał Miłecki if (primary_offset == -30)
35305491d2cSKalle Valo ch_inf.sb = BRCMU_CHAN_SB_LL;
35436e8072eSRafał Miłecki else if (primary_offset == -10)
35505491d2cSKalle Valo ch_inf.sb = BRCMU_CHAN_SB_LU;
35636e8072eSRafał Miłecki else if (primary_offset == 10)
35736e8072eSRafał Miłecki ch_inf.sb = BRCMU_CHAN_SB_UL;
35836e8072eSRafał Miłecki else
35936e8072eSRafał Miłecki ch_inf.sb = BRCMU_CHAN_SB_UU;
36005491d2cSKalle Valo break;
36105491d2cSKalle Valo case NL80211_CHAN_WIDTH_160:
362f491645fSArend van Spriel ch_inf.bw = BRCMU_CHAN_BW_160;
363f491645fSArend van Spriel if (primary_offset == -70)
364f491645fSArend van Spriel ch_inf.sb = BRCMU_CHAN_SB_LLL;
365f491645fSArend van Spriel else if (primary_offset == -50)
366f491645fSArend van Spriel ch_inf.sb = BRCMU_CHAN_SB_LLU;
367f491645fSArend van Spriel else if (primary_offset == -30)
368f491645fSArend van Spriel ch_inf.sb = BRCMU_CHAN_SB_LUL;
369f491645fSArend van Spriel else if (primary_offset == -10)
370f491645fSArend van Spriel ch_inf.sb = BRCMU_CHAN_SB_LUU;
371f491645fSArend van Spriel else if (primary_offset == 10)
372f491645fSArend van Spriel ch_inf.sb = BRCMU_CHAN_SB_ULL;
373f491645fSArend van Spriel else if (primary_offset == 30)
374f491645fSArend van Spriel ch_inf.sb = BRCMU_CHAN_SB_ULU;
375f491645fSArend van Spriel else if (primary_offset == 50)
376f491645fSArend van Spriel ch_inf.sb = BRCMU_CHAN_SB_UUL;
377f491645fSArend van Spriel else
378f491645fSArend van Spriel ch_inf.sb = BRCMU_CHAN_SB_UUU;
379f491645fSArend van Spriel break;
380f491645fSArend van Spriel case NL80211_CHAN_WIDTH_80P80:
38105491d2cSKalle Valo case NL80211_CHAN_WIDTH_5:
38205491d2cSKalle Valo case NL80211_CHAN_WIDTH_10:
38305491d2cSKalle Valo default:
38405491d2cSKalle Valo WARN_ON_ONCE(1);
38505491d2cSKalle Valo }
38605491d2cSKalle Valo switch (ch->chan->band) {
38757fbcce3SJohannes Berg case NL80211_BAND_2GHZ:
38805491d2cSKalle Valo ch_inf.band = BRCMU_CHAN_BAND_2G;
38905491d2cSKalle Valo break;
39057fbcce3SJohannes Berg case NL80211_BAND_5GHZ:
39105491d2cSKalle Valo ch_inf.band = BRCMU_CHAN_BAND_5G;
39205491d2cSKalle Valo break;
39357fbcce3SJohannes Berg case NL80211_BAND_60GHZ:
39405491d2cSKalle Valo default:
39505491d2cSKalle Valo WARN_ON_ONCE(1);
39605491d2cSKalle Valo }
39705491d2cSKalle Valo d11inf->encchspec(&ch_inf);
39805491d2cSKalle Valo
399f491645fSArend van Spriel brcmf_dbg(TRACE, "chanspec: 0x%x\n", ch_inf.chspec);
40005491d2cSKalle Valo return ch_inf.chspec;
40105491d2cSKalle Valo }
40205491d2cSKalle Valo
channel_to_chanspec(struct brcmu_d11inf * d11inf,struct ieee80211_channel * ch)40305491d2cSKalle Valo u16 channel_to_chanspec(struct brcmu_d11inf *d11inf,
40405491d2cSKalle Valo struct ieee80211_channel *ch)
40505491d2cSKalle Valo {
40605491d2cSKalle Valo struct brcmu_chan ch_inf;
40705491d2cSKalle Valo
40805491d2cSKalle Valo ch_inf.chnum = ieee80211_frequency_to_channel(ch->center_freq);
40905491d2cSKalle Valo ch_inf.bw = BRCMU_CHAN_BW_20;
41005491d2cSKalle Valo d11inf->encchspec(&ch_inf);
41105491d2cSKalle Valo
41205491d2cSKalle Valo return ch_inf.chspec;
41305491d2cSKalle Valo }
41405491d2cSKalle Valo
41505491d2cSKalle Valo /* Traverse a string of 1-byte tag/1-byte length/variable-length value
41605491d2cSKalle Valo * triples, returning a pointer to the substring whose first element
41705491d2cSKalle Valo * matches tag
41805491d2cSKalle Valo */
419c8d87079SRafał Miłecki static const struct brcmf_tlv *
brcmf_parse_tlvs(const void * buf,int buflen,uint key)42005491d2cSKalle Valo brcmf_parse_tlvs(const void *buf, int buflen, uint key)
42105491d2cSKalle Valo {
42205491d2cSKalle Valo const struct brcmf_tlv *elt = buf;
42305491d2cSKalle Valo int totlen = buflen;
42405491d2cSKalle Valo
42505491d2cSKalle Valo /* find tagged parameter */
42605491d2cSKalle Valo while (totlen >= TLV_HDR_LEN) {
42705491d2cSKalle Valo int len = elt->len;
42805491d2cSKalle Valo
42905491d2cSKalle Valo /* validate remaining totlen */
43005491d2cSKalle Valo if ((elt->id == key) && (totlen >= (len + TLV_HDR_LEN)))
43105491d2cSKalle Valo return elt;
43205491d2cSKalle Valo
43305491d2cSKalle Valo elt = (struct brcmf_tlv *)((u8 *)elt + (len + TLV_HDR_LEN));
43405491d2cSKalle Valo totlen -= (len + TLV_HDR_LEN);
43505491d2cSKalle Valo }
43605491d2cSKalle Valo
43705491d2cSKalle Valo return NULL;
43805491d2cSKalle Valo }
43905491d2cSKalle Valo
44005491d2cSKalle Valo /* Is any of the tlvs the expected entry? If
44105491d2cSKalle Valo * not update the tlvs buffer pointer/length.
44205491d2cSKalle Valo */
44305491d2cSKalle Valo static bool
brcmf_tlv_has_ie(const u8 * ie,const u8 ** tlvs,u32 * tlvs_len,const u8 * oui,u32 oui_len,u8 type)44405491d2cSKalle Valo brcmf_tlv_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len,
44505491d2cSKalle Valo const u8 *oui, u32 oui_len, u8 type)
44605491d2cSKalle Valo {
44705491d2cSKalle Valo /* If the contents match the OUI and the type */
44805491d2cSKalle Valo if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
44905491d2cSKalle Valo !memcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
45005491d2cSKalle Valo type == ie[TLV_BODY_OFF + oui_len]) {
45105491d2cSKalle Valo return true;
45205491d2cSKalle Valo }
45305491d2cSKalle Valo
45405491d2cSKalle Valo if (tlvs == NULL)
45505491d2cSKalle Valo return false;
45605491d2cSKalle Valo /* point to the next ie */
45705491d2cSKalle Valo ie += ie[TLV_LEN_OFF] + TLV_HDR_LEN;
45805491d2cSKalle Valo /* calculate the length of the rest of the buffer */
45905491d2cSKalle Valo *tlvs_len -= (int)(ie - *tlvs);
46005491d2cSKalle Valo /* update the pointer to the start of the buffer */
46105491d2cSKalle Valo *tlvs = ie;
46205491d2cSKalle Valo
46305491d2cSKalle Valo return false;
46405491d2cSKalle Valo }
46505491d2cSKalle Valo
46605491d2cSKalle Valo static struct brcmf_vs_tlv *
brcmf_find_wpaie(const u8 * parse,u32 len)46705491d2cSKalle Valo brcmf_find_wpaie(const u8 *parse, u32 len)
46805491d2cSKalle Valo {
46905491d2cSKalle Valo const struct brcmf_tlv *ie;
47005491d2cSKalle Valo
47105491d2cSKalle Valo while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
47205491d2cSKalle Valo if (brcmf_tlv_has_ie((const u8 *)ie, &parse, &len,
47305491d2cSKalle Valo WPA_OUI, TLV_OUI_LEN, WPA_OUI_TYPE))
47405491d2cSKalle Valo return (struct brcmf_vs_tlv *)ie;
47505491d2cSKalle Valo }
47605491d2cSKalle Valo return NULL;
47705491d2cSKalle Valo }
47805491d2cSKalle Valo
47905491d2cSKalle Valo static struct brcmf_vs_tlv *
brcmf_find_wpsie(const u8 * parse,u32 len)48005491d2cSKalle Valo brcmf_find_wpsie(const u8 *parse, u32 len)
48105491d2cSKalle Valo {
48205491d2cSKalle Valo const struct brcmf_tlv *ie;
48305491d2cSKalle Valo
48405491d2cSKalle Valo while ((ie = brcmf_parse_tlvs(parse, len, WLAN_EID_VENDOR_SPECIFIC))) {
48505491d2cSKalle Valo if (brcmf_tlv_has_ie((u8 *)ie, &parse, &len,
48605491d2cSKalle Valo WPA_OUI, TLV_OUI_LEN, WPS_OUI_TYPE))
48705491d2cSKalle Valo return (struct brcmf_vs_tlv *)ie;
48805491d2cSKalle Valo }
48905491d2cSKalle Valo return NULL;
49005491d2cSKalle Valo }
49105491d2cSKalle Valo
brcmf_vif_change_validate(struct brcmf_cfg80211_info * cfg,struct brcmf_cfg80211_vif * vif,enum nl80211_iftype new_type)49205491d2cSKalle Valo static int brcmf_vif_change_validate(struct brcmf_cfg80211_info *cfg,
49305491d2cSKalle Valo struct brcmf_cfg80211_vif *vif,
49405491d2cSKalle Valo enum nl80211_iftype new_type)
49505491d2cSKalle Valo {
49605491d2cSKalle Valo struct brcmf_cfg80211_vif *pos;
497353c46acSArend van Spriel bool check_combos = false;
498353c46acSArend van Spriel int ret = 0;
499e227300cSPurushottam Kushwaha struct iface_combination_params params = {
500e227300cSPurushottam Kushwaha .num_different_channels = 1,
501e227300cSPurushottam Kushwaha };
50205491d2cSKalle Valo
50305491d2cSKalle Valo list_for_each_entry(pos, &cfg->vif_list, list)
504353c46acSArend van Spriel if (pos == vif) {
505e227300cSPurushottam Kushwaha params.iftype_num[new_type]++;
506353c46acSArend van Spriel } else {
507353c46acSArend van Spriel /* concurrent interfaces so need check combinations */
508353c46acSArend van Spriel check_combos = true;
509e227300cSPurushottam Kushwaha params.iftype_num[pos->wdev.iftype]++;
510353c46acSArend van Spriel }
51105491d2cSKalle Valo
512353c46acSArend van Spriel if (check_combos)
513e227300cSPurushottam Kushwaha ret = cfg80211_check_combinations(cfg->wiphy, ¶ms);
514353c46acSArend van Spriel
515353c46acSArend van Spriel return ret;
51605491d2cSKalle Valo }
51705491d2cSKalle Valo
brcmf_vif_add_validate(struct brcmf_cfg80211_info * cfg,enum nl80211_iftype new_type)51805491d2cSKalle Valo static int brcmf_vif_add_validate(struct brcmf_cfg80211_info *cfg,
51905491d2cSKalle Valo enum nl80211_iftype new_type)
52005491d2cSKalle Valo {
52105491d2cSKalle Valo struct brcmf_cfg80211_vif *pos;
522e227300cSPurushottam Kushwaha struct iface_combination_params params = {
523e227300cSPurushottam Kushwaha .num_different_channels = 1,
524e227300cSPurushottam Kushwaha };
52505491d2cSKalle Valo
52605491d2cSKalle Valo list_for_each_entry(pos, &cfg->vif_list, list)
527e227300cSPurushottam Kushwaha params.iftype_num[pos->wdev.iftype]++;
52805491d2cSKalle Valo
529e227300cSPurushottam Kushwaha params.iftype_num[new_type]++;
530e227300cSPurushottam Kushwaha return cfg80211_check_combinations(cfg->wiphy, ¶ms);
53105491d2cSKalle Valo }
53205491d2cSKalle Valo
convert_key_from_CPU(struct brcmf_wsec_key * key,struct brcmf_wsec_key_le * key_le)53305491d2cSKalle Valo static void convert_key_from_CPU(struct brcmf_wsec_key *key,
53405491d2cSKalle Valo struct brcmf_wsec_key_le *key_le)
53505491d2cSKalle Valo {
53605491d2cSKalle Valo key_le->index = cpu_to_le32(key->index);
53705491d2cSKalle Valo key_le->len = cpu_to_le32(key->len);
53805491d2cSKalle Valo key_le->algo = cpu_to_le32(key->algo);
53905491d2cSKalle Valo key_le->flags = cpu_to_le32(key->flags);
54005491d2cSKalle Valo key_le->rxiv.hi = cpu_to_le32(key->rxiv.hi);
54105491d2cSKalle Valo key_le->rxiv.lo = cpu_to_le16(key->rxiv.lo);
54205491d2cSKalle Valo key_le->iv_initialized = cpu_to_le32(key->iv_initialized);
54305491d2cSKalle Valo memcpy(key_le->data, key->data, sizeof(key->data));
54405491d2cSKalle Valo memcpy(key_le->ea, key->ea, sizeof(key->ea));
54505491d2cSKalle Valo }
54605491d2cSKalle Valo
54705491d2cSKalle Valo static int
send_key_to_dongle(struct brcmf_if * ifp,struct brcmf_wsec_key * key)54805491d2cSKalle Valo send_key_to_dongle(struct brcmf_if *ifp, struct brcmf_wsec_key *key)
54905491d2cSKalle Valo {
55016e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
55105491d2cSKalle Valo int err;
55205491d2cSKalle Valo struct brcmf_wsec_key_le key_le;
55305491d2cSKalle Valo
55405491d2cSKalle Valo convert_key_from_CPU(key, &key_le);
55505491d2cSKalle Valo
55605491d2cSKalle Valo brcmf_netdev_wait_pend8021x(ifp);
55705491d2cSKalle Valo
55805491d2cSKalle Valo err = brcmf_fil_bsscfg_data_set(ifp, "wsec_key", &key_le,
55905491d2cSKalle Valo sizeof(key_le));
56005491d2cSKalle Valo
56105491d2cSKalle Valo if (err)
56216e64676SRafał Miłecki bphy_err(drvr, "wsec_key error (%d)\n", err);
56305491d2cSKalle Valo return err;
56405491d2cSKalle Valo }
56505491d2cSKalle Valo
56605491d2cSKalle Valo static void
brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev * wdev)56705491d2cSKalle Valo brcmf_cfg80211_update_proto_addr_mode(struct wireless_dev *wdev)
56805491d2cSKalle Valo {
56905491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
57005491d2cSKalle Valo struct brcmf_if *ifp;
57105491d2cSKalle Valo
57205491d2cSKalle Valo vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
57305491d2cSKalle Valo ifp = vif->ifp;
57405491d2cSKalle Valo
57505491d2cSKalle Valo if ((wdev->iftype == NL80211_IFTYPE_ADHOC) ||
57605491d2cSKalle Valo (wdev->iftype == NL80211_IFTYPE_AP) ||
57705491d2cSKalle Valo (wdev->iftype == NL80211_IFTYPE_P2P_GO))
57805491d2cSKalle Valo brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
57905491d2cSKalle Valo ADDR_DIRECT);
58005491d2cSKalle Valo else
58105491d2cSKalle Valo brcmf_proto_configure_addr_mode(ifp->drvr, ifp->ifidx,
58205491d2cSKalle Valo ADDR_INDIRECT);
58305491d2cSKalle Valo }
58405491d2cSKalle Valo
brcmf_get_first_free_bsscfgidx(struct brcmf_pub * drvr)585d02fb8f1SRafał Miłecki static int brcmf_get_first_free_bsscfgidx(struct brcmf_pub *drvr)
586d02fb8f1SRafał Miłecki {
587d02fb8f1SRafał Miłecki int bsscfgidx;
588d02fb8f1SRafał Miłecki
589d02fb8f1SRafał Miłecki for (bsscfgidx = 0; bsscfgidx < BRCMF_MAX_IFS; bsscfgidx++) {
590d02fb8f1SRafał Miłecki /* bsscfgidx 1 is reserved for legacy P2P */
591d02fb8f1SRafał Miłecki if (bsscfgidx == 1)
592d02fb8f1SRafał Miłecki continue;
593d02fb8f1SRafał Miłecki if (!drvr->iflist[bsscfgidx])
594d02fb8f1SRafał Miłecki return bsscfgidx;
595d02fb8f1SRafał Miłecki }
596d02fb8f1SRafał Miłecki
597d02fb8f1SRafał Miłecki return -ENOMEM;
598d02fb8f1SRafał Miłecki }
599d02fb8f1SRafał Miłecki
brcmf_set_vif_sta_macaddr(struct brcmf_if * ifp,u8 * mac_addr)6004388827bSWright Feng static void brcmf_set_vif_sta_macaddr(struct brcmf_if *ifp, u8 *mac_addr)
6012b5fb30fSWright Feng {
6022b5fb30fSWright Feng u8 mac_idx = ifp->drvr->sta_mac_idx;
6032b5fb30fSWright Feng
6042b5fb30fSWright Feng /* set difference MAC address with locally administered bit */
6054388827bSWright Feng memcpy(mac_addr, ifp->mac_addr, ETH_ALEN);
6064388827bSWright Feng mac_addr[0] |= 0x02;
6074388827bSWright Feng mac_addr[3] ^= mac_idx ? 0xC0 : 0xA0;
6082b5fb30fSWright Feng mac_idx++;
6092b5fb30fSWright Feng mac_idx = mac_idx % 2;
6102b5fb30fSWright Feng ifp->drvr->sta_mac_idx = mac_idx;
6112b5fb30fSWright Feng }
6122b5fb30fSWright Feng
brcmf_cfg80211_request_sta_if(struct brcmf_if * ifp,u8 * macaddr)6132b5fb30fSWright Feng static int brcmf_cfg80211_request_sta_if(struct brcmf_if *ifp, u8 *macaddr)
6142b5fb30fSWright Feng {
6154388827bSWright Feng struct wl_interface_create_v1 iface_v1;
6164388827bSWright Feng struct wl_interface_create_v2 iface_v2;
6174388827bSWright Feng struct wl_interface_create_v3 iface_v3;
6184388827bSWright Feng u32 iface_create_ver;
6192b5fb30fSWright Feng int err;
6202b5fb30fSWright Feng
6214388827bSWright Feng /* interface_create version 1 */
6224388827bSWright Feng memset(&iface_v1, 0, sizeof(iface_v1));
6234388827bSWright Feng iface_v1.ver = WL_INTERFACE_CREATE_VER_1;
6244388827bSWright Feng iface_v1.flags = WL_INTERFACE_CREATE_STA |
6254388827bSWright Feng WL_INTERFACE_MAC_USE;
6264388827bSWright Feng if (!is_zero_ether_addr(macaddr))
6274388827bSWright Feng memcpy(iface_v1.mac_addr, macaddr, ETH_ALEN);
6284388827bSWright Feng else
6294388827bSWright Feng brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);
6302b5fb30fSWright Feng
6314388827bSWright Feng err = brcmf_fil_iovar_data_get(ifp, "interface_create",
6324388827bSWright Feng &iface_v1,
6334388827bSWright Feng sizeof(iface_v1));
6344388827bSWright Feng if (err) {
6354388827bSWright Feng brcmf_info("failed to create interface(v1), err=%d\n",
6364388827bSWright Feng err);
6372b5fb30fSWright Feng } else {
6384388827bSWright Feng brcmf_dbg(INFO, "interface created(v1)\n");
6394388827bSWright Feng return 0;
6402b5fb30fSWright Feng }
6412b5fb30fSWright Feng
6424388827bSWright Feng /* interface_create version 2 */
6434388827bSWright Feng memset(&iface_v2, 0, sizeof(iface_v2));
6444388827bSWright Feng iface_v2.ver = WL_INTERFACE_CREATE_VER_2;
6454388827bSWright Feng iface_v2.flags = WL_INTERFACE_MAC_USE;
6464388827bSWright Feng iface_v2.iftype = WL_INTERFACE_CREATE_STA;
6474388827bSWright Feng if (!is_zero_ether_addr(macaddr))
6484388827bSWright Feng memcpy(iface_v2.mac_addr, macaddr, ETH_ALEN);
6494388827bSWright Feng else
6504388827bSWright Feng brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);
6514388827bSWright Feng
6524388827bSWright Feng err = brcmf_fil_iovar_data_get(ifp, "interface_create",
6534388827bSWright Feng &iface_v2,
6544388827bSWright Feng sizeof(iface_v2));
6554388827bSWright Feng if (err) {
6564388827bSWright Feng brcmf_info("failed to create interface(v2), err=%d\n",
6574388827bSWright Feng err);
6584388827bSWright Feng } else {
6594388827bSWright Feng brcmf_dbg(INFO, "interface created(v2)\n");
6604388827bSWright Feng return 0;
6614388827bSWright Feng }
6624388827bSWright Feng
6634388827bSWright Feng /* interface_create version 3+ */
6644388827bSWright Feng /* get supported version from firmware side */
6654388827bSWright Feng iface_create_ver = 0;
666*fb1862ceSArend van Spriel err = brcmf_fil_bsscfg_int_query(ifp, "interface_create",
6674388827bSWright Feng &iface_create_ver);
6684388827bSWright Feng if (err) {
6694388827bSWright Feng brcmf_err("fail to get supported version, err=%d\n", err);
6704388827bSWright Feng return -EOPNOTSUPP;
6714388827bSWright Feng }
6724388827bSWright Feng
6734388827bSWright Feng switch (iface_create_ver) {
6744388827bSWright Feng case WL_INTERFACE_CREATE_VER_3:
6754388827bSWright Feng memset(&iface_v3, 0, sizeof(iface_v3));
6764388827bSWright Feng iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
6774388827bSWright Feng iface_v3.flags = WL_INTERFACE_MAC_USE;
6784388827bSWright Feng iface_v3.iftype = WL_INTERFACE_CREATE_STA;
6794388827bSWright Feng if (!is_zero_ether_addr(macaddr))
6804388827bSWright Feng memcpy(iface_v3.mac_addr, macaddr, ETH_ALEN);
6814388827bSWright Feng else
6824388827bSWright Feng brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);
6834388827bSWright Feng
6844388827bSWright Feng err = brcmf_fil_iovar_data_get(ifp, "interface_create",
6854388827bSWright Feng &iface_v3,
6864388827bSWright Feng sizeof(iface_v3));
6874388827bSWright Feng
6884388827bSWright Feng if (!err)
6894388827bSWright Feng brcmf_dbg(INFO, "interface created(v3)\n");
6904388827bSWright Feng break;
6914388827bSWright Feng default:
6924388827bSWright Feng brcmf_err("not support interface create(v%d)\n",
6934388827bSWright Feng iface_create_ver);
6944388827bSWright Feng err = -EOPNOTSUPP;
6954388827bSWright Feng break;
6964388827bSWright Feng }
6974388827bSWright Feng
6984388827bSWright Feng if (err) {
6994388827bSWright Feng brcmf_info("station interface creation failed (%d)\n",
7004388827bSWright Feng err);
7014388827bSWright Feng return -EIO;
7024388827bSWright Feng }
7034388827bSWright Feng
7044388827bSWright Feng return 0;
7052b5fb30fSWright Feng }
7062b5fb30fSWright Feng
brcmf_cfg80211_request_ap_if(struct brcmf_if * ifp)70705491d2cSKalle Valo static int brcmf_cfg80211_request_ap_if(struct brcmf_if *ifp)
70805491d2cSKalle Valo {
7091562bdefSPrasanna Kerekoppa struct wl_interface_create_v1 iface_v1;
7101562bdefSPrasanna Kerekoppa struct wl_interface_create_v2 iface_v2;
7111562bdefSPrasanna Kerekoppa struct wl_interface_create_v3 iface_v3;
7121562bdefSPrasanna Kerekoppa u32 iface_create_ver;
71316e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
71405491d2cSKalle Valo struct brcmf_mbss_ssid_le mbss_ssid_le;
71505491d2cSKalle Valo int bsscfgidx;
71605491d2cSKalle Valo int err;
71705491d2cSKalle Valo
7181562bdefSPrasanna Kerekoppa /* interface_create version 1 */
7191562bdefSPrasanna Kerekoppa memset(&iface_v1, 0, sizeof(iface_v1));
7201562bdefSPrasanna Kerekoppa iface_v1.ver = WL_INTERFACE_CREATE_VER_1;
7211562bdefSPrasanna Kerekoppa iface_v1.flags = WL_INTERFACE_CREATE_AP |
7221562bdefSPrasanna Kerekoppa WL_INTERFACE_MAC_USE;
7231562bdefSPrasanna Kerekoppa
7241562bdefSPrasanna Kerekoppa brcmf_set_vif_sta_macaddr(ifp, iface_v1.mac_addr);
7251562bdefSPrasanna Kerekoppa
7261562bdefSPrasanna Kerekoppa err = brcmf_fil_iovar_data_get(ifp, "interface_create",
7271562bdefSPrasanna Kerekoppa &iface_v1,
7281562bdefSPrasanna Kerekoppa sizeof(iface_v1));
7291562bdefSPrasanna Kerekoppa if (err) {
7301562bdefSPrasanna Kerekoppa brcmf_info("failed to create interface(v1), err=%d\n",
7311562bdefSPrasanna Kerekoppa err);
7321562bdefSPrasanna Kerekoppa } else {
7331562bdefSPrasanna Kerekoppa brcmf_dbg(INFO, "interface created(v1)\n");
7341562bdefSPrasanna Kerekoppa return 0;
7351562bdefSPrasanna Kerekoppa }
7361562bdefSPrasanna Kerekoppa
7371562bdefSPrasanna Kerekoppa /* interface_create version 2 */
7381562bdefSPrasanna Kerekoppa memset(&iface_v2, 0, sizeof(iface_v2));
7391562bdefSPrasanna Kerekoppa iface_v2.ver = WL_INTERFACE_CREATE_VER_2;
7401562bdefSPrasanna Kerekoppa iface_v2.flags = WL_INTERFACE_MAC_USE;
7411562bdefSPrasanna Kerekoppa iface_v2.iftype = WL_INTERFACE_CREATE_AP;
7421562bdefSPrasanna Kerekoppa
7431562bdefSPrasanna Kerekoppa brcmf_set_vif_sta_macaddr(ifp, iface_v2.mac_addr);
7441562bdefSPrasanna Kerekoppa
7451562bdefSPrasanna Kerekoppa err = brcmf_fil_iovar_data_get(ifp, "interface_create",
7461562bdefSPrasanna Kerekoppa &iface_v2,
7471562bdefSPrasanna Kerekoppa sizeof(iface_v2));
7481562bdefSPrasanna Kerekoppa if (err) {
7491562bdefSPrasanna Kerekoppa brcmf_info("failed to create interface(v2), err=%d\n",
7501562bdefSPrasanna Kerekoppa err);
7511562bdefSPrasanna Kerekoppa } else {
7521562bdefSPrasanna Kerekoppa brcmf_dbg(INFO, "interface created(v2)\n");
7531562bdefSPrasanna Kerekoppa return 0;
7541562bdefSPrasanna Kerekoppa }
7551562bdefSPrasanna Kerekoppa
7561562bdefSPrasanna Kerekoppa /* interface_create version 3+ */
7571562bdefSPrasanna Kerekoppa /* get supported version from firmware side */
7581562bdefSPrasanna Kerekoppa iface_create_ver = 0;
759*fb1862ceSArend van Spriel err = brcmf_fil_bsscfg_int_query(ifp, "interface_create",
7601562bdefSPrasanna Kerekoppa &iface_create_ver);
7611562bdefSPrasanna Kerekoppa if (err) {
7621562bdefSPrasanna Kerekoppa brcmf_err("fail to get supported version, err=%d\n", err);
7631562bdefSPrasanna Kerekoppa return -EOPNOTSUPP;
7641562bdefSPrasanna Kerekoppa }
7651562bdefSPrasanna Kerekoppa
7661562bdefSPrasanna Kerekoppa switch (iface_create_ver) {
7671562bdefSPrasanna Kerekoppa case WL_INTERFACE_CREATE_VER_3:
7681562bdefSPrasanna Kerekoppa memset(&iface_v3, 0, sizeof(iface_v3));
7691562bdefSPrasanna Kerekoppa iface_v3.ver = WL_INTERFACE_CREATE_VER_3;
7701562bdefSPrasanna Kerekoppa iface_v3.flags = WL_INTERFACE_MAC_USE;
7711562bdefSPrasanna Kerekoppa iface_v3.iftype = WL_INTERFACE_CREATE_AP;
7721562bdefSPrasanna Kerekoppa brcmf_set_vif_sta_macaddr(ifp, iface_v3.mac_addr);
7731562bdefSPrasanna Kerekoppa
7741562bdefSPrasanna Kerekoppa err = brcmf_fil_iovar_data_get(ifp, "interface_create",
7751562bdefSPrasanna Kerekoppa &iface_v3,
7761562bdefSPrasanna Kerekoppa sizeof(iface_v3));
7771562bdefSPrasanna Kerekoppa
7781562bdefSPrasanna Kerekoppa if (!err)
7791562bdefSPrasanna Kerekoppa brcmf_dbg(INFO, "interface created(v3)\n");
7801562bdefSPrasanna Kerekoppa break;
7811562bdefSPrasanna Kerekoppa default:
7821562bdefSPrasanna Kerekoppa brcmf_err("not support interface create(v%d)\n",
7831562bdefSPrasanna Kerekoppa iface_create_ver);
7841562bdefSPrasanna Kerekoppa err = -EOPNOTSUPP;
7851562bdefSPrasanna Kerekoppa break;
7861562bdefSPrasanna Kerekoppa }
7871562bdefSPrasanna Kerekoppa
7881562bdefSPrasanna Kerekoppa if (err) {
7891562bdefSPrasanna Kerekoppa brcmf_info("Does not support interface_create (%d)\n",
7901562bdefSPrasanna Kerekoppa err);
79105491d2cSKalle Valo memset(&mbss_ssid_le, 0, sizeof(mbss_ssid_le));
792d02fb8f1SRafał Miłecki bsscfgidx = brcmf_get_first_free_bsscfgidx(ifp->drvr);
79305491d2cSKalle Valo if (bsscfgidx < 0)
79405491d2cSKalle Valo return bsscfgidx;
79505491d2cSKalle Valo
79605491d2cSKalle Valo mbss_ssid_le.bsscfgidx = cpu_to_le32(bsscfgidx);
79705491d2cSKalle Valo mbss_ssid_le.SSID_len = cpu_to_le32(5);
79805491d2cSKalle Valo sprintf(mbss_ssid_le.SSID, "ssid%d", bsscfgidx);
79905491d2cSKalle Valo
80005491d2cSKalle Valo err = brcmf_fil_bsscfg_data_set(ifp, "bsscfg:ssid", &mbss_ssid_le,
80105491d2cSKalle Valo sizeof(mbss_ssid_le));
8021562bdefSPrasanna Kerekoppa
80305491d2cSKalle Valo if (err < 0)
80416e64676SRafał Miłecki bphy_err(drvr, "setting ssid failed %d\n", err);
8051562bdefSPrasanna Kerekoppa }
80605491d2cSKalle Valo
80705491d2cSKalle Valo return err;
80805491d2cSKalle Valo }
80905491d2cSKalle Valo
81005491d2cSKalle Valo /**
8112b5fb30fSWright Feng * brcmf_apsta_add_vif() - create a new AP or STA virtual interface
81205491d2cSKalle Valo *
81305491d2cSKalle Valo * @wiphy: wiphy device of new interface.
81405491d2cSKalle Valo * @name: name of the new interface.
8152b5fb30fSWright Feng * @params: contains mac address for AP or STA device.
8162b5fb30fSWright Feng * @type: interface type.
81705491d2cSKalle Valo */
81805491d2cSKalle Valo static
brcmf_apsta_add_vif(struct wiphy * wiphy,const char * name,struct vif_params * params,enum nl80211_iftype type)8192b5fb30fSWright Feng struct wireless_dev *brcmf_apsta_add_vif(struct wiphy *wiphy, const char *name,
8202b5fb30fSWright Feng struct vif_params *params,
8212b5fb30fSWright Feng enum nl80211_iftype type)
82205491d2cSKalle Valo {
82305491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
82405491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
82516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
82605491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
82705491d2cSKalle Valo int err;
82805491d2cSKalle Valo
8292b5fb30fSWright Feng if (type != NL80211_IFTYPE_STATION && type != NL80211_IFTYPE_AP)
8302b5fb30fSWright Feng return ERR_PTR(-EINVAL);
8312b5fb30fSWright Feng
83205491d2cSKalle Valo if (brcmf_cfg80211_vif_event_armed(cfg))
83305491d2cSKalle Valo return ERR_PTR(-EBUSY);
83405491d2cSKalle Valo
83505491d2cSKalle Valo brcmf_dbg(INFO, "Adding vif \"%s\"\n", name);
83605491d2cSKalle Valo
8372b5fb30fSWright Feng vif = brcmf_alloc_vif(cfg, type);
83805491d2cSKalle Valo if (IS_ERR(vif))
83905491d2cSKalle Valo return (struct wireless_dev *)vif;
84005491d2cSKalle Valo
84105491d2cSKalle Valo brcmf_cfg80211_arm_vif_event(cfg, vif);
84205491d2cSKalle Valo
8432b5fb30fSWright Feng if (type == NL80211_IFTYPE_STATION)
8442b5fb30fSWright Feng err = brcmf_cfg80211_request_sta_if(ifp, params->macaddr);
8452b5fb30fSWright Feng else
84605491d2cSKalle Valo err = brcmf_cfg80211_request_ap_if(ifp);
84705491d2cSKalle Valo if (err) {
84805491d2cSKalle Valo brcmf_cfg80211_arm_vif_event(cfg, NULL);
84905491d2cSKalle Valo goto fail;
85005491d2cSKalle Valo }
85105491d2cSKalle Valo
85205491d2cSKalle Valo /* wait for firmware event */
853a9eb0c4bSArend van Spriel err = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_ADD,
85463ce3d5dSArend van Spriel BRCMF_VIF_EVENT_TIMEOUT);
85505491d2cSKalle Valo brcmf_cfg80211_arm_vif_event(cfg, NULL);
85605491d2cSKalle Valo if (!err) {
85716e64676SRafał Miłecki bphy_err(drvr, "timeout occurred\n");
85805491d2cSKalle Valo err = -EIO;
85905491d2cSKalle Valo goto fail;
86005491d2cSKalle Valo }
86105491d2cSKalle Valo
86205491d2cSKalle Valo /* interface created in firmware */
86305491d2cSKalle Valo ifp = vif->ifp;
86405491d2cSKalle Valo if (!ifp) {
86516e64676SRafał Miłecki bphy_err(drvr, "no if pointer provided\n");
86605491d2cSKalle Valo err = -ENOENT;
86705491d2cSKalle Valo goto fail;
86805491d2cSKalle Valo }
86905491d2cSKalle Valo
87005491d2cSKalle Valo strncpy(ifp->ndev->name, name, sizeof(ifp->ndev->name) - 1);
87105491d2cSKalle Valo err = brcmf_net_attach(ifp, true);
87205491d2cSKalle Valo if (err) {
87316e64676SRafał Miłecki bphy_err(drvr, "Registering netdevice failed\n");
874dca2307eSArend Van Spriel free_netdev(ifp->ndev);
87505491d2cSKalle Valo goto fail;
87605491d2cSKalle Valo }
87705491d2cSKalle Valo
87805491d2cSKalle Valo return &ifp->vif->wdev;
87905491d2cSKalle Valo
88005491d2cSKalle Valo fail:
88105491d2cSKalle Valo brcmf_free_vif(vif);
88205491d2cSKalle Valo return ERR_PTR(err);
88305491d2cSKalle Valo }
88405491d2cSKalle Valo
brcmf_is_apmode(struct brcmf_cfg80211_vif * vif)88505491d2cSKalle Valo static bool brcmf_is_apmode(struct brcmf_cfg80211_vif *vif)
88605491d2cSKalle Valo {
88705491d2cSKalle Valo enum nl80211_iftype iftype;
88805491d2cSKalle Valo
88905491d2cSKalle Valo iftype = vif->wdev.iftype;
89005491d2cSKalle Valo return iftype == NL80211_IFTYPE_AP || iftype == NL80211_IFTYPE_P2P_GO;
89105491d2cSKalle Valo }
89205491d2cSKalle Valo
brcmf_is_ibssmode(struct brcmf_cfg80211_vif * vif)89305491d2cSKalle Valo static bool brcmf_is_ibssmode(struct brcmf_cfg80211_vif *vif)
89405491d2cSKalle Valo {
89505491d2cSKalle Valo return vif->wdev.iftype == NL80211_IFTYPE_ADHOC;
89605491d2cSKalle Valo }
89705491d2cSKalle Valo
89820f2c5faSRafał Miłecki /**
89920f2c5faSRafał Miłecki * brcmf_mon_add_vif() - create monitor mode virtual interface
90020f2c5faSRafał Miłecki *
90120f2c5faSRafał Miłecki * @wiphy: wiphy device of new interface.
90220f2c5faSRafał Miłecki * @name: name of the new interface.
90320f2c5faSRafał Miłecki */
brcmf_mon_add_vif(struct wiphy * wiphy,const char * name)90420f2c5faSRafał Miłecki static struct wireless_dev *brcmf_mon_add_vif(struct wiphy *wiphy,
90520f2c5faSRafał Miłecki const char *name)
90620f2c5faSRafał Miłecki {
90720f2c5faSRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
90820f2c5faSRafał Miłecki struct brcmf_cfg80211_vif *vif;
90920f2c5faSRafał Miłecki struct net_device *ndev;
91020f2c5faSRafał Miłecki struct brcmf_if *ifp;
91120f2c5faSRafał Miłecki int err;
91220f2c5faSRafał Miłecki
91320f2c5faSRafał Miłecki if (cfg->pub->mon_if) {
91420f2c5faSRafał Miłecki err = -EEXIST;
91520f2c5faSRafał Miłecki goto err_out;
91620f2c5faSRafał Miłecki }
91720f2c5faSRafał Miłecki
91820f2c5faSRafał Miłecki vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_MONITOR);
91920f2c5faSRafał Miłecki if (IS_ERR(vif)) {
92020f2c5faSRafał Miłecki err = PTR_ERR(vif);
92120f2c5faSRafał Miłecki goto err_out;
92220f2c5faSRafał Miłecki }
92320f2c5faSRafał Miłecki
92420f2c5faSRafał Miłecki ndev = alloc_netdev(sizeof(*ifp), name, NET_NAME_UNKNOWN, ether_setup);
92520f2c5faSRafał Miłecki if (!ndev) {
92620f2c5faSRafał Miłecki err = -ENOMEM;
92720f2c5faSRafał Miłecki goto err_free_vif;
92820f2c5faSRafał Miłecki }
92920f2c5faSRafał Miłecki ndev->type = ARPHRD_IEEE80211_RADIOTAP;
93020f2c5faSRafał Miłecki ndev->ieee80211_ptr = &vif->wdev;
93120f2c5faSRafał Miłecki ndev->needs_free_netdev = true;
93220f2c5faSRafał Miłecki ndev->priv_destructor = brcmf_cfg80211_free_netdev;
93320f2c5faSRafał Miłecki SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
93420f2c5faSRafał Miłecki
93520f2c5faSRafał Miłecki ifp = netdev_priv(ndev);
93620f2c5faSRafał Miłecki ifp->vif = vif;
93720f2c5faSRafał Miłecki ifp->ndev = ndev;
93820f2c5faSRafał Miłecki ifp->drvr = cfg->pub;
93920f2c5faSRafał Miłecki
94020f2c5faSRafał Miłecki vif->ifp = ifp;
94120f2c5faSRafał Miłecki vif->wdev.netdev = ndev;
94220f2c5faSRafał Miłecki
94320f2c5faSRafał Miłecki err = brcmf_net_mon_attach(ifp);
94420f2c5faSRafał Miłecki if (err) {
94520f2c5faSRafał Miłecki brcmf_err("Failed to attach %s device\n", ndev->name);
94620f2c5faSRafał Miłecki free_netdev(ndev);
94720f2c5faSRafał Miłecki goto err_free_vif;
94820f2c5faSRafał Miłecki }
94920f2c5faSRafał Miłecki
95020f2c5faSRafał Miłecki cfg->pub->mon_if = ifp;
95120f2c5faSRafał Miłecki
95220f2c5faSRafał Miłecki return &vif->wdev;
95320f2c5faSRafał Miłecki
95420f2c5faSRafał Miłecki err_free_vif:
95520f2c5faSRafał Miłecki brcmf_free_vif(vif);
95620f2c5faSRafał Miłecki err_out:
95720f2c5faSRafał Miłecki return ERR_PTR(err);
95820f2c5faSRafał Miłecki }
95920f2c5faSRafał Miłecki
brcmf_mon_del_vif(struct wiphy * wiphy,struct wireless_dev * wdev)96020f2c5faSRafał Miłecki static int brcmf_mon_del_vif(struct wiphy *wiphy, struct wireless_dev *wdev)
96120f2c5faSRafał Miłecki {
96220f2c5faSRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
96320f2c5faSRafał Miłecki struct net_device *ndev = wdev->netdev;
96420f2c5faSRafał Miłecki
96520f2c5faSRafał Miłecki ndev->netdev_ops->ndo_stop(ndev);
96620f2c5faSRafał Miłecki
96720f2c5faSRafał Miłecki brcmf_net_detach(ndev, true);
96820f2c5faSRafał Miłecki
96920f2c5faSRafał Miłecki cfg->pub->mon_if = NULL;
97020f2c5faSRafał Miłecki
97120f2c5faSRafał Miłecki return 0;
97220f2c5faSRafał Miłecki }
97320f2c5faSRafał Miłecki
brcmf_cfg80211_add_iface(struct wiphy * wiphy,const char * name,unsigned char name_assign_type,enum nl80211_iftype type,struct vif_params * params)97405491d2cSKalle Valo static struct wireless_dev *brcmf_cfg80211_add_iface(struct wiphy *wiphy,
97505491d2cSKalle Valo const char *name,
97605491d2cSKalle Valo unsigned char name_assign_type,
97705491d2cSKalle Valo enum nl80211_iftype type,
97805491d2cSKalle Valo struct vif_params *params)
97905491d2cSKalle Valo {
98016e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
98116e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
98205491d2cSKalle Valo struct wireless_dev *wdev;
98305491d2cSKalle Valo int err;
98405491d2cSKalle Valo
98505491d2cSKalle Valo brcmf_dbg(TRACE, "enter: %s type %d\n", name, type);
98605491d2cSKalle Valo err = brcmf_vif_add_validate(wiphy_to_cfg(wiphy), type);
98705491d2cSKalle Valo if (err) {
98816e64676SRafał Miłecki bphy_err(drvr, "iface validation failed: err=%d\n", err);
98905491d2cSKalle Valo return ERR_PTR(err);
99005491d2cSKalle Valo }
99105491d2cSKalle Valo switch (type) {
99205491d2cSKalle Valo case NL80211_IFTYPE_ADHOC:
99305491d2cSKalle Valo case NL80211_IFTYPE_AP_VLAN:
99405491d2cSKalle Valo case NL80211_IFTYPE_WDS:
99505491d2cSKalle Valo case NL80211_IFTYPE_MESH_POINT:
99605491d2cSKalle Valo return ERR_PTR(-EOPNOTSUPP);
99720f2c5faSRafał Miłecki case NL80211_IFTYPE_MONITOR:
99820f2c5faSRafał Miłecki return brcmf_mon_add_vif(wiphy, name);
9992b5fb30fSWright Feng case NL80211_IFTYPE_STATION:
100005491d2cSKalle Valo case NL80211_IFTYPE_AP:
10012b5fb30fSWright Feng wdev = brcmf_apsta_add_vif(wiphy, name, params, type);
10020cd33c20SRafał Miłecki break;
100305491d2cSKalle Valo case NL80211_IFTYPE_P2P_CLIENT:
100405491d2cSKalle Valo case NL80211_IFTYPE_P2P_GO:
100505491d2cSKalle Valo case NL80211_IFTYPE_P2P_DEVICE:
1006818a986eSJohannes Berg wdev = brcmf_p2p_add_vif(wiphy, name, name_assign_type, type, params);
10070cd33c20SRafał Miłecki break;
100805491d2cSKalle Valo case NL80211_IFTYPE_UNSPECIFIED:
100905491d2cSKalle Valo default:
101005491d2cSKalle Valo return ERR_PTR(-EINVAL);
101105491d2cSKalle Valo }
10120cd33c20SRafał Miłecki
10130cd33c20SRafał Miłecki if (IS_ERR(wdev))
101416e64676SRafał Miłecki bphy_err(drvr, "add iface %s type %d failed: err=%d\n", name,
10153ef005b8SRafał Miłecki type, (int)PTR_ERR(wdev));
10160cd33c20SRafał Miłecki else
10170cd33c20SRafał Miłecki brcmf_cfg80211_update_proto_addr_mode(wdev);
10180cd33c20SRafał Miłecki
10190cd33c20SRafał Miłecki return wdev;
102005491d2cSKalle Valo }
102105491d2cSKalle Valo
brcmf_scan_config_mpc(struct brcmf_if * ifp,int mpc)102205491d2cSKalle Valo static void brcmf_scan_config_mpc(struct brcmf_if *ifp, int mpc)
102305491d2cSKalle Valo {
102405491d2cSKalle Valo if (brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_NEED_MPC))
102505491d2cSKalle Valo brcmf_set_mpc(ifp, mpc);
102605491d2cSKalle Valo }
102705491d2cSKalle Valo
brcmf_set_mpc(struct brcmf_if * ifp,int mpc)102805491d2cSKalle Valo void brcmf_set_mpc(struct brcmf_if *ifp, int mpc)
102905491d2cSKalle Valo {
103016e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
103105491d2cSKalle Valo s32 err = 0;
103205491d2cSKalle Valo
103305491d2cSKalle Valo if (check_vif_up(ifp->vif)) {
103405491d2cSKalle Valo err = brcmf_fil_iovar_int_set(ifp, "mpc", mpc);
103505491d2cSKalle Valo if (err) {
103616e64676SRafał Miłecki bphy_err(drvr, "fail to set mpc\n");
103705491d2cSKalle Valo return;
103805491d2cSKalle Valo }
103905491d2cSKalle Valo brcmf_dbg(INFO, "MPC : %d\n", mpc);
104005491d2cSKalle Valo }
104105491d2cSKalle Valo }
104205491d2cSKalle Valo
brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le * params_v2_le,struct brcmf_scan_params_le * params_le)1043398ce273SHector Martin static void brcmf_scan_params_v2_to_v1(struct brcmf_scan_params_v2_le *params_v2_le,
1044398ce273SHector Martin struct brcmf_scan_params_le *params_le)
1045398ce273SHector Martin {
1046398ce273SHector Martin size_t params_size;
1047398ce273SHector Martin u32 ch;
1048398ce273SHector Martin int n_channels, n_ssids;
1049398ce273SHector Martin
1050398ce273SHector Martin memcpy(¶ms_le->ssid_le, ¶ms_v2_le->ssid_le,
1051398ce273SHector Martin sizeof(params_le->ssid_le));
1052398ce273SHector Martin memcpy(¶ms_le->bssid, ¶ms_v2_le->bssid,
1053398ce273SHector Martin sizeof(params_le->bssid));
1054398ce273SHector Martin
1055398ce273SHector Martin params_le->bss_type = params_v2_le->bss_type;
1056398ce273SHector Martin params_le->scan_type = le32_to_cpu(params_v2_le->scan_type);
1057398ce273SHector Martin params_le->nprobes = params_v2_le->nprobes;
1058398ce273SHector Martin params_le->active_time = params_v2_le->active_time;
1059398ce273SHector Martin params_le->passive_time = params_v2_le->passive_time;
1060398ce273SHector Martin params_le->home_time = params_v2_le->home_time;
1061398ce273SHector Martin params_le->channel_num = params_v2_le->channel_num;
1062398ce273SHector Martin
1063398ce273SHector Martin ch = le32_to_cpu(params_v2_le->channel_num);
1064398ce273SHector Martin n_channels = ch & BRCMF_SCAN_PARAMS_COUNT_MASK;
1065398ce273SHector Martin n_ssids = ch >> BRCMF_SCAN_PARAMS_NSSID_SHIFT;
1066398ce273SHector Martin
1067398ce273SHector Martin params_size = sizeof(u16) * n_channels;
1068398ce273SHector Martin if (n_ssids > 0) {
1069398ce273SHector Martin params_size = roundup(params_size, sizeof(u32));
1070398ce273SHector Martin params_size += sizeof(struct brcmf_ssid_le) * n_ssids;
1071398ce273SHector Martin }
1072398ce273SHector Martin
1073398ce273SHector Martin memcpy(¶ms_le->channel_list[0],
1074398ce273SHector Martin ¶ms_v2_le->channel_list[0], params_size);
1075398ce273SHector Martin }
1076398ce273SHector Martin
brcmf_escan_prep(struct brcmf_cfg80211_info * cfg,struct brcmf_scan_params_v2_le * params_le,struct cfg80211_scan_request * request)1077398ce273SHector Martin static void brcmf_escan_prep(struct brcmf_cfg80211_info *cfg,
1078398ce273SHector Martin struct brcmf_scan_params_v2_le *params_le,
1079398ce273SHector Martin struct cfg80211_scan_request *request)
1080398ce273SHector Martin {
1081398ce273SHector Martin u32 n_ssids;
1082398ce273SHector Martin u32 n_channels;
1083398ce273SHector Martin s32 i;
1084398ce273SHector Martin s32 offset;
1085398ce273SHector Martin u16 chanspec;
1086398ce273SHector Martin char *ptr;
1087398ce273SHector Martin int length;
1088398ce273SHector Martin struct brcmf_ssid_le ssid_le;
1089398ce273SHector Martin
1090398ce273SHector Martin eth_broadcast_addr(params_le->bssid);
1091398ce273SHector Martin
1092398ce273SHector Martin length = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
1093398ce273SHector Martin
1094398ce273SHector Martin params_le->version = cpu_to_le16(BRCMF_SCAN_PARAMS_VERSION_V2);
1095398ce273SHector Martin params_le->bss_type = DOT11_BSSTYPE_ANY;
1096398ce273SHector Martin params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_ACTIVE);
1097398ce273SHector Martin params_le->channel_num = 0;
1098398ce273SHector Martin params_le->nprobes = cpu_to_le32(-1);
1099398ce273SHector Martin params_le->active_time = cpu_to_le32(-1);
1100398ce273SHector Martin params_le->passive_time = cpu_to_le32(-1);
1101398ce273SHector Martin params_le->home_time = cpu_to_le32(-1);
1102398ce273SHector Martin memset(¶ms_le->ssid_le, 0, sizeof(params_le->ssid_le));
1103398ce273SHector Martin
1104398ce273SHector Martin /* Scan abort */
1105398ce273SHector Martin if (!request) {
1106398ce273SHector Martin length += sizeof(u16);
1107398ce273SHector Martin params_le->channel_num = cpu_to_le32(1);
1108398ce273SHector Martin params_le->channel_list[0] = cpu_to_le16(-1);
1109398ce273SHector Martin params_le->length = cpu_to_le16(length);
1110398ce273SHector Martin return;
1111398ce273SHector Martin }
1112398ce273SHector Martin
1113398ce273SHector Martin n_ssids = request->n_ssids;
1114398ce273SHector Martin n_channels = request->n_channels;
1115398ce273SHector Martin
1116398ce273SHector Martin /* Copy channel array if applicable */
1117398ce273SHector Martin brcmf_dbg(SCAN, "### List of channelspecs to scan ### %d\n",
1118398ce273SHector Martin n_channels);
1119398ce273SHector Martin if (n_channels > 0) {
1120398ce273SHector Martin length += roundup(sizeof(u16) * n_channels, sizeof(u32));
1121398ce273SHector Martin for (i = 0; i < n_channels; i++) {
1122398ce273SHector Martin chanspec = channel_to_chanspec(&cfg->d11inf,
1123398ce273SHector Martin request->channels[i]);
1124398ce273SHector Martin brcmf_dbg(SCAN, "Chan : %d, Channel spec: %x\n",
1125398ce273SHector Martin request->channels[i]->hw_value, chanspec);
1126398ce273SHector Martin params_le->channel_list[i] = cpu_to_le16(chanspec);
1127398ce273SHector Martin }
1128398ce273SHector Martin } else {
1129398ce273SHector Martin brcmf_dbg(SCAN, "Scanning all channels\n");
1130398ce273SHector Martin }
1131398ce273SHector Martin
1132398ce273SHector Martin /* Copy ssid array if applicable */
1133398ce273SHector Martin brcmf_dbg(SCAN, "### List of SSIDs to scan ### %d\n", n_ssids);
1134398ce273SHector Martin if (n_ssids > 0) {
1135398ce273SHector Martin offset = offsetof(struct brcmf_scan_params_v2_le, channel_list) +
1136398ce273SHector Martin n_channels * sizeof(u16);
1137398ce273SHector Martin offset = roundup(offset, sizeof(u32));
1138398ce273SHector Martin length += sizeof(ssid_le) * n_ssids,
1139398ce273SHector Martin ptr = (char *)params_le + offset;
1140398ce273SHector Martin for (i = 0; i < n_ssids; i++) {
1141398ce273SHector Martin memset(&ssid_le, 0, sizeof(ssid_le));
1142398ce273SHector Martin ssid_le.SSID_len =
1143398ce273SHector Martin cpu_to_le32(request->ssids[i].ssid_len);
1144398ce273SHector Martin memcpy(ssid_le.SSID, request->ssids[i].ssid,
1145398ce273SHector Martin request->ssids[i].ssid_len);
1146398ce273SHector Martin if (!ssid_le.SSID_len)
1147398ce273SHector Martin brcmf_dbg(SCAN, "%d: Broadcast scan\n", i);
1148398ce273SHector Martin else
1149398ce273SHector Martin brcmf_dbg(SCAN, "%d: scan for %.32s size=%d\n",
1150398ce273SHector Martin i, ssid_le.SSID, ssid_le.SSID_len);
1151398ce273SHector Martin memcpy(ptr, &ssid_le, sizeof(ssid_le));
1152398ce273SHector Martin ptr += sizeof(ssid_le);
1153398ce273SHector Martin }
1154398ce273SHector Martin } else {
1155398ce273SHector Martin brcmf_dbg(SCAN, "Performing passive scan\n");
1156398ce273SHector Martin params_le->scan_type = cpu_to_le32(BRCMF_SCANTYPE_PASSIVE);
1157398ce273SHector Martin }
1158398ce273SHector Martin params_le->length = cpu_to_le16(length);
1159398ce273SHector Martin /* Adding mask to channel numbers */
1160398ce273SHector Martin params_le->channel_num =
1161398ce273SHector Martin cpu_to_le32((n_ssids << BRCMF_SCAN_PARAMS_NSSID_SHIFT) |
1162398ce273SHector Martin (n_channels & BRCMF_SCAN_PARAMS_COUNT_MASK));
1163398ce273SHector Martin }
1164398ce273SHector Martin
brcmf_notify_escan_complete(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,bool aborted,bool fw_abort)116505491d2cSKalle Valo s32 brcmf_notify_escan_complete(struct brcmf_cfg80211_info *cfg,
116605491d2cSKalle Valo struct brcmf_if *ifp, bool aborted,
116705491d2cSKalle Valo bool fw_abort)
116805491d2cSKalle Valo {
116916e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
1170398ce273SHector Martin struct brcmf_scan_params_v2_le params_v2_le;
117105491d2cSKalle Valo struct cfg80211_scan_request *scan_request;
1172efc2c1faSArend Van Spriel u64 reqid;
1173efc2c1faSArend Van Spriel u32 bucket;
117405491d2cSKalle Valo s32 err = 0;
117505491d2cSKalle Valo
117605491d2cSKalle Valo brcmf_dbg(SCAN, "Enter\n");
117705491d2cSKalle Valo
117805491d2cSKalle Valo /* clear scan request, because the FW abort can cause a second call */
117905491d2cSKalle Valo /* to this functon and might cause a double cfg80211_scan_done */
118005491d2cSKalle Valo scan_request = cfg->scan_request;
118105491d2cSKalle Valo cfg->scan_request = NULL;
118205491d2cSKalle Valo
118319079484SZheng Wang timer_delete_sync(&cfg->escan_timeout);
118405491d2cSKalle Valo
118505491d2cSKalle Valo if (fw_abort) {
118605491d2cSKalle Valo /* Do a scan abort to stop the driver's scan engine */
118705491d2cSKalle Valo brcmf_dbg(SCAN, "ABORT scan in firmware\n");
1188398ce273SHector Martin
1189398ce273SHector Martin brcmf_escan_prep(cfg, ¶ms_v2_le, NULL);
1190398ce273SHector Martin
119105491d2cSKalle Valo /* E-Scan (or anyother type) can be aborted by SCAN */
1192398ce273SHector Martin if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
119305491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
1194398ce273SHector Martin ¶ms_v2_le,
1195398ce273SHector Martin sizeof(params_v2_le));
1196398ce273SHector Martin } else {
1197398ce273SHector Martin struct brcmf_scan_params_le params_le;
1198398ce273SHector Martin
1199398ce273SHector Martin brcmf_scan_params_v2_to_v1(¶ms_v2_le, ¶ms_le);
1200398ce273SHector Martin err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCAN,
1201398ce273SHector Martin ¶ms_le,
1202398ce273SHector Martin sizeof(params_le));
1203398ce273SHector Martin }
1204398ce273SHector Martin
120505491d2cSKalle Valo if (err)
120616e64676SRafał Miłecki bphy_err(drvr, "Scan abort failed\n");
120705491d2cSKalle Valo }
120805491d2cSKalle Valo
120905491d2cSKalle Valo brcmf_scan_config_mpc(ifp, 1);
121005491d2cSKalle Valo
121105491d2cSKalle Valo /*
1212fa85b30aSArend Van Spriel * e-scan can be initiated internally
121305491d2cSKalle Valo * which takes precedence.
121405491d2cSKalle Valo */
1215efc2c1faSArend Van Spriel if (cfg->int_escan_map) {
1216efc2c1faSArend Van Spriel brcmf_dbg(SCAN, "scheduled scan completed (%x)\n",
1217efc2c1faSArend Van Spriel cfg->int_escan_map);
1218efc2c1faSArend Van Spriel while (cfg->int_escan_map) {
1219efc2c1faSArend Van Spriel bucket = __ffs(cfg->int_escan_map);
1220efc2c1faSArend Van Spriel cfg->int_escan_map &= ~BIT(bucket);
1221efc2c1faSArend Van Spriel reqid = brcmf_pno_find_reqid_by_bucket(cfg->pno,
1222efc2c1faSArend Van Spriel bucket);
1223efc2c1faSArend Van Spriel if (!aborted) {
1224efc2c1faSArend Van Spriel brcmf_dbg(SCAN, "report results: reqid=%llu\n",
1225efc2c1faSArend Van Spriel reqid);
1226efc2c1faSArend Van Spriel cfg80211_sched_scan_results(cfg_to_wiphy(cfg),
1227efc2c1faSArend Van Spriel reqid);
1228efc2c1faSArend Van Spriel }
1229efc2c1faSArend Van Spriel }
123005491d2cSKalle Valo } else if (scan_request) {
12311d76250bSAvraham Stern struct cfg80211_scan_info info = {
12321d76250bSAvraham Stern .aborted = aborted,
12331d76250bSAvraham Stern };
12341d76250bSAvraham Stern
123505491d2cSKalle Valo brcmf_dbg(SCAN, "ESCAN Completed scan: %s\n",
123605491d2cSKalle Valo aborted ? "Aborted" : "Done");
12371d76250bSAvraham Stern cfg80211_scan_done(scan_request, &info);
123805491d2cSKalle Valo }
123905491d2cSKalle Valo if (!test_and_clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
124005491d2cSKalle Valo brcmf_dbg(SCAN, "Scan complete, probably P2P scan\n");
124105491d2cSKalle Valo
124205491d2cSKalle Valo return err;
124305491d2cSKalle Valo }
124405491d2cSKalle Valo
brcmf_cfg80211_del_apsta_iface(struct wiphy * wiphy,struct wireless_dev * wdev)12452b5fb30fSWright Feng static int brcmf_cfg80211_del_apsta_iface(struct wiphy *wiphy,
1246dba8fbc6SRafał Miłecki struct wireless_dev *wdev)
1247dba8fbc6SRafał Miłecki {
1248856d5a01SArend Van Spriel struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
1249dba8fbc6SRafał Miłecki struct net_device *ndev = wdev->netdev;
1250dba8fbc6SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
125116e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
1252dba8fbc6SRafał Miłecki int ret;
1253dba8fbc6SRafał Miłecki int err;
1254dba8fbc6SRafał Miłecki
1255dba8fbc6SRafał Miłecki brcmf_cfg80211_arm_vif_event(cfg, ifp->vif);
1256dba8fbc6SRafał Miłecki
1257dba8fbc6SRafał Miłecki err = brcmf_fil_bsscfg_data_set(ifp, "interface_remove", NULL, 0);
1258dba8fbc6SRafał Miłecki if (err) {
125916e64676SRafał Miłecki bphy_err(drvr, "interface_remove failed %d\n", err);
1260dba8fbc6SRafał Miłecki goto err_unarm;
1261dba8fbc6SRafał Miłecki }
1262dba8fbc6SRafał Miłecki
1263dba8fbc6SRafał Miłecki /* wait for firmware event */
1264dba8fbc6SRafał Miłecki ret = brcmf_cfg80211_wait_vif_event(cfg, BRCMF_E_IF_DEL,
1265dba8fbc6SRafał Miłecki BRCMF_VIF_EVENT_TIMEOUT);
1266dba8fbc6SRafał Miłecki if (!ret) {
126716e64676SRafał Miłecki bphy_err(drvr, "timeout occurred\n");
1268dba8fbc6SRafał Miłecki err = -EIO;
1269dba8fbc6SRafał Miłecki goto err_unarm;
1270dba8fbc6SRafał Miłecki }
1271dba8fbc6SRafał Miłecki
1272dba8fbc6SRafał Miłecki brcmf_remove_interface(ifp, true);
1273dba8fbc6SRafał Miłecki
1274dba8fbc6SRafał Miłecki err_unarm:
1275dba8fbc6SRafał Miłecki brcmf_cfg80211_arm_vif_event(cfg, NULL);
1276dba8fbc6SRafał Miłecki return err;
1277dba8fbc6SRafał Miłecki }
1278dba8fbc6SRafał Miłecki
127905491d2cSKalle Valo static
brcmf_cfg80211_del_iface(struct wiphy * wiphy,struct wireless_dev * wdev)128005491d2cSKalle Valo int brcmf_cfg80211_del_iface(struct wiphy *wiphy, struct wireless_dev *wdev)
128105491d2cSKalle Valo {
1282856d5a01SArend Van Spriel struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
128305491d2cSKalle Valo struct net_device *ndev = wdev->netdev;
128405491d2cSKalle Valo
1285dba8fbc6SRafał Miłecki if (ndev && ndev == cfg_to_ndev(cfg))
1286dba8fbc6SRafał Miłecki return -ENOTSUPP;
1287dba8fbc6SRafał Miłecki
128805491d2cSKalle Valo /* vif event pending in firmware */
128905491d2cSKalle Valo if (brcmf_cfg80211_vif_event_armed(cfg))
129005491d2cSKalle Valo return -EBUSY;
129105491d2cSKalle Valo
129205491d2cSKalle Valo if (ndev) {
129305491d2cSKalle Valo if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status) &&
129405491d2cSKalle Valo cfg->escan_info.ifp == netdev_priv(ndev))
129505491d2cSKalle Valo brcmf_notify_escan_complete(cfg, netdev_priv(ndev),
129605491d2cSKalle Valo true, true);
129705491d2cSKalle Valo
129805491d2cSKalle Valo brcmf_fil_iovar_int_set(netdev_priv(ndev), "mpc", 1);
129905491d2cSKalle Valo }
130005491d2cSKalle Valo
130105491d2cSKalle Valo switch (wdev->iftype) {
130205491d2cSKalle Valo case NL80211_IFTYPE_ADHOC:
130305491d2cSKalle Valo case NL80211_IFTYPE_AP_VLAN:
130405491d2cSKalle Valo case NL80211_IFTYPE_WDS:
130505491d2cSKalle Valo case NL80211_IFTYPE_MESH_POINT:
130605491d2cSKalle Valo return -EOPNOTSUPP;
130720f2c5faSRafał Miłecki case NL80211_IFTYPE_MONITOR:
130820f2c5faSRafał Miłecki return brcmf_mon_del_vif(wiphy, wdev);
13092b5fb30fSWright Feng case NL80211_IFTYPE_STATION:
1310dba8fbc6SRafał Miłecki case NL80211_IFTYPE_AP:
13112b5fb30fSWright Feng return brcmf_cfg80211_del_apsta_iface(wiphy, wdev);
131205491d2cSKalle Valo case NL80211_IFTYPE_P2P_CLIENT:
131305491d2cSKalle Valo case NL80211_IFTYPE_P2P_GO:
131405491d2cSKalle Valo case NL80211_IFTYPE_P2P_DEVICE:
131505491d2cSKalle Valo return brcmf_p2p_del_vif(wiphy, wdev);
131605491d2cSKalle Valo case NL80211_IFTYPE_UNSPECIFIED:
131705491d2cSKalle Valo default:
131805491d2cSKalle Valo return -EINVAL;
131905491d2cSKalle Valo }
132005491d2cSKalle Valo return -EOPNOTSUPP;
132105491d2cSKalle Valo }
132205491d2cSKalle Valo
132305491d2cSKalle Valo static s32
brcmf_cfg80211_change_iface(struct wiphy * wiphy,struct net_device * ndev,enum nl80211_iftype type,struct vif_params * params)132405491d2cSKalle Valo brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
1325818a986eSJohannes Berg enum nl80211_iftype type,
132605491d2cSKalle Valo struct vif_params *params)
132705491d2cSKalle Valo {
1328856d5a01SArend Van Spriel struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
132905491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
133005491d2cSKalle Valo struct brcmf_cfg80211_vif *vif = ifp->vif;
133116e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
133205491d2cSKalle Valo s32 infra = 0;
133305491d2cSKalle Valo s32 ap = 0;
133405491d2cSKalle Valo s32 err = 0;
133505491d2cSKalle Valo
133637a869ecSHante Meuleman brcmf_dbg(TRACE, "Enter, bsscfgidx=%d, type=%d\n", ifp->bsscfgidx,
133737a869ecSHante Meuleman type);
133805491d2cSKalle Valo
133905491d2cSKalle Valo /* WAR: There are a number of p2p interface related problems which
134005491d2cSKalle Valo * need to be handled initially (before doing the validate).
134105491d2cSKalle Valo * wpa_supplicant tends to do iface changes on p2p device/client/go
134205491d2cSKalle Valo * which are not always possible/allowed. However we need to return
134305491d2cSKalle Valo * OK otherwise the wpa_supplicant wont start. The situation differs
134405491d2cSKalle Valo * on configuration and setup (p2pon=1 module param). The first check
134505491d2cSKalle Valo * is to see if the request is a change to station for p2p iface.
134605491d2cSKalle Valo */
134705491d2cSKalle Valo if ((type == NL80211_IFTYPE_STATION) &&
134805491d2cSKalle Valo ((vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) ||
134905491d2cSKalle Valo (vif->wdev.iftype == NL80211_IFTYPE_P2P_GO) ||
135005491d2cSKalle Valo (vif->wdev.iftype == NL80211_IFTYPE_P2P_DEVICE))) {
135105491d2cSKalle Valo brcmf_dbg(TRACE, "Ignoring cmd for p2p if\n");
135205491d2cSKalle Valo /* Now depending on whether module param p2pon=1 was used the
135305491d2cSKalle Valo * response needs to be either 0 or EOPNOTSUPP. The reason is
135405491d2cSKalle Valo * that if p2pon=1 is used, but a newer supplicant is used then
135505491d2cSKalle Valo * we should return an error, as this combination wont work.
135605491d2cSKalle Valo * In other situations 0 is returned and supplicant will start
135705491d2cSKalle Valo * normally. It will give a trace in cfg80211, but it is the
135805491d2cSKalle Valo * only way to get it working. Unfortunately this will result
135905491d2cSKalle Valo * in situation where we wont support new supplicant in
136005491d2cSKalle Valo * combination with module param p2pon=1, but that is the way
136105491d2cSKalle Valo * it is. If the user tries this then unloading of driver might
136205491d2cSKalle Valo * fail/lock.
136305491d2cSKalle Valo */
136405491d2cSKalle Valo if (cfg->p2p.p2pdev_dynamically)
136505491d2cSKalle Valo return -EOPNOTSUPP;
136605491d2cSKalle Valo else
136705491d2cSKalle Valo return 0;
136805491d2cSKalle Valo }
136905491d2cSKalle Valo err = brcmf_vif_change_validate(wiphy_to_cfg(wiphy), vif, type);
137005491d2cSKalle Valo if (err) {
137116e64676SRafał Miłecki bphy_err(drvr, "iface validation failed: err=%d\n", err);
137205491d2cSKalle Valo return err;
137305491d2cSKalle Valo }
137405491d2cSKalle Valo switch (type) {
137505491d2cSKalle Valo case NL80211_IFTYPE_MONITOR:
137605491d2cSKalle Valo case NL80211_IFTYPE_WDS:
137716e64676SRafał Miłecki bphy_err(drvr, "type (%d) : currently we do not support this type\n",
137805491d2cSKalle Valo type);
137905491d2cSKalle Valo return -EOPNOTSUPP;
138005491d2cSKalle Valo case NL80211_IFTYPE_ADHOC:
138105491d2cSKalle Valo infra = 0;
138205491d2cSKalle Valo break;
138305491d2cSKalle Valo case NL80211_IFTYPE_STATION:
138405491d2cSKalle Valo infra = 1;
138505491d2cSKalle Valo break;
138605491d2cSKalle Valo case NL80211_IFTYPE_AP:
138705491d2cSKalle Valo case NL80211_IFTYPE_P2P_GO:
138805491d2cSKalle Valo ap = 1;
138905491d2cSKalle Valo break;
139005491d2cSKalle Valo default:
139105491d2cSKalle Valo err = -EINVAL;
139205491d2cSKalle Valo goto done;
139305491d2cSKalle Valo }
139405491d2cSKalle Valo
139505491d2cSKalle Valo if (ap) {
139605491d2cSKalle Valo if (type == NL80211_IFTYPE_P2P_GO) {
139705491d2cSKalle Valo brcmf_dbg(INFO, "IF Type = P2P GO\n");
139805491d2cSKalle Valo err = brcmf_p2p_ifchange(cfg, BRCMF_FIL_P2P_IF_GO);
139905491d2cSKalle Valo }
140005491d2cSKalle Valo if (!err) {
140105491d2cSKalle Valo brcmf_dbg(INFO, "IF Type = AP\n");
140205491d2cSKalle Valo }
140305491d2cSKalle Valo } else {
140405491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, infra);
140505491d2cSKalle Valo if (err) {
140616e64676SRafał Miłecki bphy_err(drvr, "WLC_SET_INFRA error (%d)\n", err);
140705491d2cSKalle Valo err = -EAGAIN;
140805491d2cSKalle Valo goto done;
140905491d2cSKalle Valo }
141005491d2cSKalle Valo brcmf_dbg(INFO, "IF Type = %s\n", brcmf_is_ibssmode(vif) ?
141105491d2cSKalle Valo "Adhoc" : "Infra");
141205491d2cSKalle Valo }
141305491d2cSKalle Valo ndev->ieee80211_ptr->iftype = type;
141405491d2cSKalle Valo
141505491d2cSKalle Valo brcmf_cfg80211_update_proto_addr_mode(&vif->wdev);
141605491d2cSKalle Valo
141705491d2cSKalle Valo done:
141805491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
141905491d2cSKalle Valo
142005491d2cSKalle Valo return err;
142105491d2cSKalle Valo }
142205491d2cSKalle Valo
142305491d2cSKalle Valo static s32
brcmf_run_escan(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,struct cfg80211_scan_request * request)142405491d2cSKalle Valo brcmf_run_escan(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp,
1425c4958106SHante Meuleman struct cfg80211_scan_request *request)
142605491d2cSKalle Valo {
142716e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
1428398ce273SHector Martin s32 params_size = BRCMF_SCAN_PARAMS_V2_FIXED_SIZE +
1429398ce273SHector Martin offsetof(struct brcmf_escan_params_le, params_v2_le);
143005491d2cSKalle Valo struct brcmf_escan_params_le *params;
143105491d2cSKalle Valo s32 err = 0;
143205491d2cSKalle Valo
143305491d2cSKalle Valo brcmf_dbg(SCAN, "E-SCAN START\n");
143405491d2cSKalle Valo
143505491d2cSKalle Valo if (request != NULL) {
143605491d2cSKalle Valo /* Allocate space for populating ssids in struct */
143705491d2cSKalle Valo params_size += sizeof(u32) * ((request->n_channels + 1) / 2);
143805491d2cSKalle Valo
143905491d2cSKalle Valo /* Allocate space for populating ssids in struct */
1440e9a6ca82SHante Meuleman params_size += sizeof(struct brcmf_ssid_le) * request->n_ssids;
144105491d2cSKalle Valo }
144205491d2cSKalle Valo
144305491d2cSKalle Valo params = kzalloc(params_size, GFP_KERNEL);
144405491d2cSKalle Valo if (!params) {
144505491d2cSKalle Valo err = -ENOMEM;
144605491d2cSKalle Valo goto exit;
144705491d2cSKalle Valo }
144805491d2cSKalle Valo BUG_ON(params_size + sizeof("escan") >= BRCMF_DCMD_MEDLEN);
1449398ce273SHector Martin brcmf_escan_prep(cfg, ¶ms->params_v2_le, request);
1450398ce273SHector Martin
1451398ce273SHector Martin params->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION_V2);
1452398ce273SHector Martin
1453398ce273SHector Martin if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_V2)) {
1454398ce273SHector Martin struct brcmf_escan_params_le *params_v1;
1455398ce273SHector Martin
1456398ce273SHector Martin params_size -= BRCMF_SCAN_PARAMS_V2_FIXED_SIZE;
1457398ce273SHector Martin params_size += BRCMF_SCAN_PARAMS_FIXED_SIZE;
1458398ce273SHector Martin params_v1 = kzalloc(params_size, GFP_KERNEL);
145907d69832SPetr Tesarik if (!params_v1) {
146007d69832SPetr Tesarik err = -ENOMEM;
146107d69832SPetr Tesarik goto exit_params;
146207d69832SPetr Tesarik }
1463398ce273SHector Martin params_v1->version = cpu_to_le32(BRCMF_ESCAN_REQ_VERSION);
1464398ce273SHector Martin brcmf_scan_params_v2_to_v1(¶ms->params_v2_le, ¶ms_v1->params_le);
1465398ce273SHector Martin kfree(params);
1466398ce273SHector Martin params = params_v1;
1467398ce273SHector Martin }
1468398ce273SHector Martin
1469c4958106SHante Meuleman params->action = cpu_to_le16(WL_ESCAN_ACTION_START);
147005491d2cSKalle Valo params->sync_id = cpu_to_le16(0x1234);
147105491d2cSKalle Valo
147205491d2cSKalle Valo err = brcmf_fil_iovar_data_set(ifp, "escan", params, params_size);
147305491d2cSKalle Valo if (err) {
147405491d2cSKalle Valo if (err == -EBUSY)
147505491d2cSKalle Valo brcmf_dbg(INFO, "system busy : escan canceled\n");
147605491d2cSKalle Valo else
147716e64676SRafał Miłecki bphy_err(drvr, "error (%d)\n", err);
147805491d2cSKalle Valo }
147905491d2cSKalle Valo
148007d69832SPetr Tesarik exit_params:
148105491d2cSKalle Valo kfree(params);
148205491d2cSKalle Valo exit:
148305491d2cSKalle Valo return err;
148405491d2cSKalle Valo }
148505491d2cSKalle Valo
148605491d2cSKalle Valo static s32
brcmf_do_escan(struct brcmf_if * ifp,struct cfg80211_scan_request * request)1487ab5981c8SArend Van Spriel brcmf_do_escan(struct brcmf_if *ifp, struct cfg80211_scan_request *request)
148805491d2cSKalle Valo {
1489ab5981c8SArend Van Spriel struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
149005491d2cSKalle Valo s32 err;
149105491d2cSKalle Valo struct brcmf_scan_results *results;
149205491d2cSKalle Valo struct escan_info *escan = &cfg->escan_info;
149305491d2cSKalle Valo
149405491d2cSKalle Valo brcmf_dbg(SCAN, "Enter\n");
149505491d2cSKalle Valo escan->ifp = ifp;
1496ab5981c8SArend Van Spriel escan->wiphy = cfg->wiphy;
149705491d2cSKalle Valo escan->escan_state = WL_ESCAN_STATE_SCANNING;
1498bbf35414SArend Van Spriel
149905491d2cSKalle Valo brcmf_scan_config_mpc(ifp, 0);
150005491d2cSKalle Valo results = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
150105491d2cSKalle Valo results->version = 0;
150205491d2cSKalle Valo results->count = 0;
150305491d2cSKalle Valo results->buflen = WL_ESCAN_RESULTS_FIXED_SIZE;
150405491d2cSKalle Valo
1505c4958106SHante Meuleman err = escan->run(cfg, ifp, request);
150605491d2cSKalle Valo if (err)
150705491d2cSKalle Valo brcmf_scan_config_mpc(ifp, 1);
150805491d2cSKalle Valo return err;
150905491d2cSKalle Valo }
151005491d2cSKalle Valo
151105491d2cSKalle Valo static s32
brcmf_cfg80211_scan(struct wiphy * wiphy,struct cfg80211_scan_request * request)1512588378f1SArend Van Spriel brcmf_cfg80211_scan(struct wiphy *wiphy, struct cfg80211_scan_request *request)
151305491d2cSKalle Valo {
151405491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
151516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
1516588378f1SArend Van Spriel struct brcmf_cfg80211_vif *vif;
1517588378f1SArend Van Spriel s32 err = 0;
151805491d2cSKalle Valo
1519588378f1SArend Van Spriel brcmf_dbg(TRACE, "Enter\n");
1520588378f1SArend Van Spriel vif = container_of(request->wdev, struct brcmf_cfg80211_vif, wdev);
1521588378f1SArend Van Spriel if (!check_vif_up(vif))
1522588378f1SArend Van Spriel return -EIO;
152305491d2cSKalle Valo
152405491d2cSKalle Valo if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
152516e64676SRafał Miłecki bphy_err(drvr, "Scanning already: status (%lu)\n",
15263ef005b8SRafał Miłecki cfg->scan_status);
152705491d2cSKalle Valo return -EAGAIN;
152805491d2cSKalle Valo }
152905491d2cSKalle Valo if (test_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status)) {
153016e64676SRafał Miłecki bphy_err(drvr, "Scanning being aborted: status (%lu)\n",
153105491d2cSKalle Valo cfg->scan_status);
153205491d2cSKalle Valo return -EAGAIN;
153305491d2cSKalle Valo }
153405491d2cSKalle Valo if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
153516e64676SRafał Miłecki bphy_err(drvr, "Scanning suppressed: status (%lu)\n",
153605491d2cSKalle Valo cfg->scan_status);
153705491d2cSKalle Valo return -EAGAIN;
153805491d2cSKalle Valo }
15398c6efda2SArend Van Spriel if (test_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state)) {
154016e64676SRafał Miłecki bphy_err(drvr, "Connecting: status (%lu)\n", vif->sme_state);
154105491d2cSKalle Valo return -EAGAIN;
154205491d2cSKalle Valo }
154305491d2cSKalle Valo
154405491d2cSKalle Valo /* If scan req comes for p2p0, send it over primary I/F */
154505491d2cSKalle Valo if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
154605491d2cSKalle Valo vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif;
154705491d2cSKalle Valo
1548588378f1SArend Van Spriel brcmf_dbg(SCAN, "START ESCAN\n");
1549588378f1SArend Van Spriel
155005491d2cSKalle Valo cfg->scan_request = request;
155105491d2cSKalle Valo set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
15528c6efda2SArend Van Spriel
155305491d2cSKalle Valo cfg->escan_info.run = brcmf_run_escan;
155405491d2cSKalle Valo err = brcmf_p2p_scan_prep(wiphy, request, vif);
155505491d2cSKalle Valo if (err)
155605491d2cSKalle Valo goto scan_out;
155705491d2cSKalle Valo
1558bd99a301SArend Van Spriel err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBREQ_FLAG,
1559bd99a301SArend Van Spriel request->ie, request->ie_len);
1560bd99a301SArend Van Spriel if (err)
1561bd99a301SArend Van Spriel goto scan_out;
1562bd99a301SArend Van Spriel
1563ab5981c8SArend Van Spriel err = brcmf_do_escan(vif->ifp, request);
156405491d2cSKalle Valo if (err)
156505491d2cSKalle Valo goto scan_out;
156605491d2cSKalle Valo
156705491d2cSKalle Valo /* Arm scan timeout timer */
1568df2d8388SArend Van Spriel mod_timer(&cfg->escan_timeout,
1569df2d8388SArend Van Spriel jiffies + msecs_to_jiffies(BRCMF_ESCAN_TIMER_INTERVAL_MS));
157005491d2cSKalle Valo
157105491d2cSKalle Valo return 0;
157205491d2cSKalle Valo
157305491d2cSKalle Valo scan_out:
157416e64676SRafał Miłecki bphy_err(drvr, "scan error (%d)\n", err);
157505491d2cSKalle Valo clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
157605491d2cSKalle Valo cfg->scan_request = NULL;
157705491d2cSKalle Valo return err;
157805491d2cSKalle Valo }
157905491d2cSKalle Valo
brcmf_set_rts(struct net_device * ndev,u32 rts_threshold)158005491d2cSKalle Valo static s32 brcmf_set_rts(struct net_device *ndev, u32 rts_threshold)
158105491d2cSKalle Valo {
15823ef005b8SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
158316e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
158405491d2cSKalle Valo s32 err = 0;
158505491d2cSKalle Valo
15863ef005b8SRafał Miłecki err = brcmf_fil_iovar_int_set(ifp, "rtsthresh", rts_threshold);
158705491d2cSKalle Valo if (err)
158816e64676SRafał Miłecki bphy_err(drvr, "Error (%d)\n", err);
158905491d2cSKalle Valo
159005491d2cSKalle Valo return err;
159105491d2cSKalle Valo }
159205491d2cSKalle Valo
brcmf_set_frag(struct net_device * ndev,u32 frag_threshold)159305491d2cSKalle Valo static s32 brcmf_set_frag(struct net_device *ndev, u32 frag_threshold)
159405491d2cSKalle Valo {
15953ef005b8SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
159616e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
159705491d2cSKalle Valo s32 err = 0;
159805491d2cSKalle Valo
15993ef005b8SRafał Miłecki err = brcmf_fil_iovar_int_set(ifp, "fragthresh",
160005491d2cSKalle Valo frag_threshold);
160105491d2cSKalle Valo if (err)
160216e64676SRafał Miłecki bphy_err(drvr, "Error (%d)\n", err);
160305491d2cSKalle Valo
160405491d2cSKalle Valo return err;
160505491d2cSKalle Valo }
160605491d2cSKalle Valo
brcmf_set_retry(struct net_device * ndev,u32 retry,bool l)160705491d2cSKalle Valo static s32 brcmf_set_retry(struct net_device *ndev, u32 retry, bool l)
160805491d2cSKalle Valo {
16093ef005b8SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
161016e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
161105491d2cSKalle Valo s32 err = 0;
161205491d2cSKalle Valo u32 cmd = (l ? BRCMF_C_SET_LRL : BRCMF_C_SET_SRL);
161305491d2cSKalle Valo
16143ef005b8SRafał Miłecki err = brcmf_fil_cmd_int_set(ifp, cmd, retry);
161505491d2cSKalle Valo if (err) {
161616e64676SRafał Miłecki bphy_err(drvr, "cmd (%d) , error (%d)\n", cmd, err);
161705491d2cSKalle Valo return err;
161805491d2cSKalle Valo }
161905491d2cSKalle Valo return err;
162005491d2cSKalle Valo }
162105491d2cSKalle Valo
brcmf_cfg80211_set_wiphy_params(struct wiphy * wiphy,u32 changed)162205491d2cSKalle Valo static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
162305491d2cSKalle Valo {
162405491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
162505491d2cSKalle Valo struct net_device *ndev = cfg_to_ndev(cfg);
162605491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
162705491d2cSKalle Valo s32 err = 0;
162805491d2cSKalle Valo
162905491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
163005491d2cSKalle Valo if (!check_vif_up(ifp->vif))
163105491d2cSKalle Valo return -EIO;
163205491d2cSKalle Valo
163305491d2cSKalle Valo if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
163405491d2cSKalle Valo (cfg->conf->rts_threshold != wiphy->rts_threshold)) {
163505491d2cSKalle Valo cfg->conf->rts_threshold = wiphy->rts_threshold;
163605491d2cSKalle Valo err = brcmf_set_rts(ndev, cfg->conf->rts_threshold);
163705491d2cSKalle Valo if (!err)
163805491d2cSKalle Valo goto done;
163905491d2cSKalle Valo }
164005491d2cSKalle Valo if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
164105491d2cSKalle Valo (cfg->conf->frag_threshold != wiphy->frag_threshold)) {
164205491d2cSKalle Valo cfg->conf->frag_threshold = wiphy->frag_threshold;
164305491d2cSKalle Valo err = brcmf_set_frag(ndev, cfg->conf->frag_threshold);
164405491d2cSKalle Valo if (!err)
164505491d2cSKalle Valo goto done;
164605491d2cSKalle Valo }
164705491d2cSKalle Valo if (changed & WIPHY_PARAM_RETRY_LONG
164805491d2cSKalle Valo && (cfg->conf->retry_long != wiphy->retry_long)) {
164905491d2cSKalle Valo cfg->conf->retry_long = wiphy->retry_long;
165005491d2cSKalle Valo err = brcmf_set_retry(ndev, cfg->conf->retry_long, true);
165105491d2cSKalle Valo if (!err)
165205491d2cSKalle Valo goto done;
165305491d2cSKalle Valo }
165405491d2cSKalle Valo if (changed & WIPHY_PARAM_RETRY_SHORT
165505491d2cSKalle Valo && (cfg->conf->retry_short != wiphy->retry_short)) {
165605491d2cSKalle Valo cfg->conf->retry_short = wiphy->retry_short;
165705491d2cSKalle Valo err = brcmf_set_retry(ndev, cfg->conf->retry_short, false);
165805491d2cSKalle Valo if (!err)
165905491d2cSKalle Valo goto done;
166005491d2cSKalle Valo }
166105491d2cSKalle Valo
166205491d2cSKalle Valo done:
166305491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
166405491d2cSKalle Valo return err;
166505491d2cSKalle Valo }
166605491d2cSKalle Valo
brcmf_init_prof(struct brcmf_cfg80211_profile * prof)166705491d2cSKalle Valo static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
166805491d2cSKalle Valo {
166905491d2cSKalle Valo memset(prof, 0, sizeof(*prof));
167005491d2cSKalle Valo }
167105491d2cSKalle Valo
brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg * e)167205491d2cSKalle Valo static u16 brcmf_map_fw_linkdown_reason(const struct brcmf_event_msg *e)
167305491d2cSKalle Valo {
167405491d2cSKalle Valo u16 reason;
167505491d2cSKalle Valo
167605491d2cSKalle Valo switch (e->event_code) {
167705491d2cSKalle Valo case BRCMF_E_DEAUTH:
167805491d2cSKalle Valo case BRCMF_E_DEAUTH_IND:
167905491d2cSKalle Valo case BRCMF_E_DISASSOC_IND:
168005491d2cSKalle Valo reason = e->reason;
168105491d2cSKalle Valo break;
168205491d2cSKalle Valo case BRCMF_E_LINK:
168305491d2cSKalle Valo default:
168405491d2cSKalle Valo reason = 0;
168505491d2cSKalle Valo break;
168605491d2cSKalle Valo }
168705491d2cSKalle Valo return reason;
168805491d2cSKalle Valo }
168905491d2cSKalle Valo
brcmf_set_wsec(struct brcmf_if * ifp,const u8 * key,u16 key_len,u16 flags)16908d59a64cSHector Martin int brcmf_set_wsec(struct brcmf_if *ifp, const u8 *key, u16 key_len, u16 flags)
1691b8a64f0eSArend van Spriel {
169216e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
1693b8a64f0eSArend van Spriel struct brcmf_wsec_pmk_le pmk;
169489b89e52SHector Martin int err;
1695b8a64f0eSArend van Spriel
16968d59a64cSHector Martin if (key_len > sizeof(pmk.key)) {
16978d59a64cSHector Martin bphy_err(drvr, "key must be less than %zu bytes\n",
16988d59a64cSHector Martin sizeof(pmk.key));
16998d59a64cSHector Martin return -EINVAL;
17008d59a64cSHector Martin }
17018d59a64cSHector Martin
170289b89e52SHector Martin memset(&pmk, 0, sizeof(pmk));
170389b89e52SHector Martin
17048d59a64cSHector Martin /* pass key material directly */
17058d59a64cSHector Martin pmk.key_len = cpu_to_le16(key_len);
17068d59a64cSHector Martin pmk.flags = cpu_to_le16(flags);
17078d59a64cSHector Martin memcpy(pmk.key, key, key_len);
1708b8a64f0eSArend van Spriel
17098d59a64cSHector Martin /* store key material in firmware */
1710b8a64f0eSArend van Spriel err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_WSEC_PMK,
1711b8a64f0eSArend van Spriel &pmk, sizeof(pmk));
1712b8a64f0eSArend van Spriel if (err < 0)
171316e64676SRafał Miłecki bphy_err(drvr, "failed to change PSK in firmware (len=%u)\n",
17148d59a64cSHector Martin key_len);
1715b8a64f0eSArend van Spriel
1716b8a64f0eSArend van Spriel return err;
1717b8a64f0eSArend van Spriel }
17188d59a64cSHector Martin BRCMF_EXPORT_SYMBOL_GPL(brcmf_set_wsec);
1719b8a64f0eSArend van Spriel
brcmf_set_pmk(struct brcmf_if * ifp,const u8 * pmk_data,u16 pmk_len)17208d59a64cSHector Martin static int brcmf_set_pmk(struct brcmf_if *ifp, const u8 *pmk_data, u16 pmk_len)
17213b1e0a7bSChung-Hsien Hsu {
17228d59a64cSHector Martin return brcmf_set_wsec(ifp, pmk_data, pmk_len, 0);
17233b1e0a7bSChung-Hsien Hsu }
17243b1e0a7bSChung-Hsien Hsu
brcmf_link_down(struct brcmf_cfg80211_vif * vif,u16 reason,bool locally_generated)17251b050d97SSoontak Lee static void brcmf_link_down(struct brcmf_cfg80211_vif *vif, u16 reason,
17261b050d97SSoontak Lee bool locally_generated)
172705491d2cSKalle Valo {
172805491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(vif->wdev.wiphy);
172916e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
17301ac11ae9SArend van Spriel bool bus_up = drvr->bus_if->state == BRCMF_BUS_UP;
173105491d2cSKalle Valo s32 err = 0;
173205491d2cSKalle Valo
173305491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
173405491d2cSKalle Valo
1735b0a79088SHante Meuleman if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTED, &vif->sme_state)) {
17361ac11ae9SArend van Spriel if (bus_up) {
173705491d2cSKalle Valo brcmf_dbg(INFO, "Call WLC_DISASSOC to stop excess roaming\n");
173805491d2cSKalle Valo err = brcmf_fil_cmd_data_set(vif->ifp,
173905491d2cSKalle Valo BRCMF_C_DISASSOC, NULL, 0);
17401ac11ae9SArend van Spriel if (err)
17411ac11ae9SArend van Spriel bphy_err(drvr, "WLC_DISASSOC failed (%d)\n",
17421ac11ae9SArend van Spriel err);
174305491d2cSKalle Valo }
17441ac11ae9SArend van Spriel
1745b0a79088SHante Meuleman if ((vif->wdev.iftype == NL80211_IFTYPE_STATION) ||
1746b0a79088SHante Meuleman (vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT))
174705491d2cSKalle Valo cfg80211_disconnected(vif->wdev.netdev, reason, NULL, 0,
17481b050d97SSoontak Lee locally_generated, GFP_KERNEL);
174905491d2cSKalle Valo }
175005491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_CONNECTING, &vif->sme_state);
175152617beeSWataru Gohda clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
175252617beeSWataru Gohda clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
175305491d2cSKalle Valo clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
175405491d2cSKalle Valo brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
1755b8a64f0eSArend van Spriel if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
17561ac11ae9SArend van Spriel if (bus_up)
1757b8a64f0eSArend van Spriel brcmf_set_pmk(vif->ifp, NULL, 0);
1758b8a64f0eSArend van Spriel vif->profile.use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
1759b8a64f0eSArend van Spriel }
176005491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
176105491d2cSKalle Valo }
176205491d2cSKalle Valo
176305491d2cSKalle Valo static s32
brcmf_cfg80211_join_ibss(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ibss_params * params)176405491d2cSKalle Valo brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *ndev,
176505491d2cSKalle Valo struct cfg80211_ibss_params *params)
176605491d2cSKalle Valo {
176705491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
176805491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
176905491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
177016e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
177105491d2cSKalle Valo struct brcmf_join_params join_params;
177205491d2cSKalle Valo size_t join_params_size = 0;
177305491d2cSKalle Valo s32 err = 0;
177405491d2cSKalle Valo s32 wsec = 0;
177505491d2cSKalle Valo s32 bcnprd;
177605491d2cSKalle Valo u16 chanspec;
1777e9a6ca82SHante Meuleman u32 ssid_len;
177805491d2cSKalle Valo
177905491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
178005491d2cSKalle Valo if (!check_vif_up(ifp->vif))
178105491d2cSKalle Valo return -EIO;
178205491d2cSKalle Valo
178305491d2cSKalle Valo if (params->ssid)
178405491d2cSKalle Valo brcmf_dbg(CONN, "SSID: %s\n", params->ssid);
178505491d2cSKalle Valo else {
178605491d2cSKalle Valo brcmf_dbg(CONN, "SSID: NULL, Not supported\n");
178705491d2cSKalle Valo return -EOPNOTSUPP;
178805491d2cSKalle Valo }
178905491d2cSKalle Valo
179005491d2cSKalle Valo set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
179105491d2cSKalle Valo
179205491d2cSKalle Valo if (params->bssid)
179305491d2cSKalle Valo brcmf_dbg(CONN, "BSSID: %pM\n", params->bssid);
179405491d2cSKalle Valo else
179505491d2cSKalle Valo brcmf_dbg(CONN, "No BSSID specified\n");
179605491d2cSKalle Valo
179705491d2cSKalle Valo if (params->chandef.chan)
179805491d2cSKalle Valo brcmf_dbg(CONN, "channel: %d\n",
179905491d2cSKalle Valo params->chandef.chan->center_freq);
180005491d2cSKalle Valo else
180105491d2cSKalle Valo brcmf_dbg(CONN, "no channel specified\n");
180205491d2cSKalle Valo
180305491d2cSKalle Valo if (params->channel_fixed)
180405491d2cSKalle Valo brcmf_dbg(CONN, "fixed channel required\n");
180505491d2cSKalle Valo else
180605491d2cSKalle Valo brcmf_dbg(CONN, "no fixed channel required\n");
180705491d2cSKalle Valo
180805491d2cSKalle Valo if (params->ie && params->ie_len)
180905491d2cSKalle Valo brcmf_dbg(CONN, "ie len: %d\n", params->ie_len);
181005491d2cSKalle Valo else
181105491d2cSKalle Valo brcmf_dbg(CONN, "no ie specified\n");
181205491d2cSKalle Valo
181305491d2cSKalle Valo if (params->beacon_interval)
181405491d2cSKalle Valo brcmf_dbg(CONN, "beacon interval: %d\n",
181505491d2cSKalle Valo params->beacon_interval);
181605491d2cSKalle Valo else
181705491d2cSKalle Valo brcmf_dbg(CONN, "no beacon interval specified\n");
181805491d2cSKalle Valo
181905491d2cSKalle Valo if (params->basic_rates)
182005491d2cSKalle Valo brcmf_dbg(CONN, "basic rates: %08X\n", params->basic_rates);
182105491d2cSKalle Valo else
182205491d2cSKalle Valo brcmf_dbg(CONN, "no basic rates specified\n");
182305491d2cSKalle Valo
182405491d2cSKalle Valo if (params->privacy)
182505491d2cSKalle Valo brcmf_dbg(CONN, "privacy required\n");
182605491d2cSKalle Valo else
182705491d2cSKalle Valo brcmf_dbg(CONN, "no privacy required\n");
182805491d2cSKalle Valo
182905491d2cSKalle Valo /* Configure Privacy for starter */
183005491d2cSKalle Valo if (params->privacy)
183105491d2cSKalle Valo wsec |= WEP_ENABLED;
183205491d2cSKalle Valo
183305491d2cSKalle Valo err = brcmf_fil_iovar_int_set(ifp, "wsec", wsec);
183405491d2cSKalle Valo if (err) {
183516e64676SRafał Miłecki bphy_err(drvr, "wsec failed (%d)\n", err);
183605491d2cSKalle Valo goto done;
183705491d2cSKalle Valo }
183805491d2cSKalle Valo
183905491d2cSKalle Valo /* Configure Beacon Interval for starter */
184005491d2cSKalle Valo if (params->beacon_interval)
184105491d2cSKalle Valo bcnprd = params->beacon_interval;
184205491d2cSKalle Valo else
184305491d2cSKalle Valo bcnprd = 100;
184405491d2cSKalle Valo
184505491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD, bcnprd);
184605491d2cSKalle Valo if (err) {
184716e64676SRafał Miłecki bphy_err(drvr, "WLC_SET_BCNPRD failed (%d)\n", err);
184805491d2cSKalle Valo goto done;
184905491d2cSKalle Valo }
185005491d2cSKalle Valo
185105491d2cSKalle Valo /* Configure required join parameter */
185205491d2cSKalle Valo memset(&join_params, 0, sizeof(struct brcmf_join_params));
185305491d2cSKalle Valo
185405491d2cSKalle Valo /* SSID */
1855e9a6ca82SHante Meuleman ssid_len = min_t(u32, params->ssid_len, IEEE80211_MAX_SSID_LEN);
1856e9a6ca82SHante Meuleman memcpy(join_params.ssid_le.SSID, params->ssid, ssid_len);
1857e9a6ca82SHante Meuleman join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
185805491d2cSKalle Valo join_params_size = sizeof(join_params.ssid_le);
185905491d2cSKalle Valo
186005491d2cSKalle Valo /* BSSID */
186105491d2cSKalle Valo if (params->bssid) {
186205491d2cSKalle Valo memcpy(join_params.params_le.bssid, params->bssid, ETH_ALEN);
1863e9a6ca82SHante Meuleman join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE;
186405491d2cSKalle Valo memcpy(profile->bssid, params->bssid, ETH_ALEN);
186505491d2cSKalle Valo } else {
186605491d2cSKalle Valo eth_broadcast_addr(join_params.params_le.bssid);
186705491d2cSKalle Valo eth_zero_addr(profile->bssid);
186805491d2cSKalle Valo }
186905491d2cSKalle Valo
187005491d2cSKalle Valo /* Channel */
187105491d2cSKalle Valo if (params->chandef.chan) {
187205491d2cSKalle Valo u32 target_channel;
187305491d2cSKalle Valo
187405491d2cSKalle Valo cfg->channel =
187505491d2cSKalle Valo ieee80211_frequency_to_channel(
187605491d2cSKalle Valo params->chandef.chan->center_freq);
187705491d2cSKalle Valo if (params->channel_fixed) {
187805491d2cSKalle Valo /* adding chanspec */
187905491d2cSKalle Valo chanspec = chandef_to_chanspec(&cfg->d11inf,
188005491d2cSKalle Valo ¶ms->chandef);
188105491d2cSKalle Valo join_params.params_le.chanspec_list[0] =
188205491d2cSKalle Valo cpu_to_le16(chanspec);
188305491d2cSKalle Valo join_params.params_le.chanspec_num = cpu_to_le32(1);
188405491d2cSKalle Valo join_params_size += sizeof(join_params.params_le);
188505491d2cSKalle Valo }
188605491d2cSKalle Valo
188705491d2cSKalle Valo /* set channel for starter */
188805491d2cSKalle Valo target_channel = cfg->channel;
188905491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_CHANNEL,
189005491d2cSKalle Valo target_channel);
189105491d2cSKalle Valo if (err) {
189216e64676SRafał Miłecki bphy_err(drvr, "WLC_SET_CHANNEL failed (%d)\n", err);
189305491d2cSKalle Valo goto done;
189405491d2cSKalle Valo }
189505491d2cSKalle Valo } else
189605491d2cSKalle Valo cfg->channel = 0;
189705491d2cSKalle Valo
189805491d2cSKalle Valo cfg->ibss_starter = false;
189905491d2cSKalle Valo
190005491d2cSKalle Valo
190105491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
190205491d2cSKalle Valo &join_params, join_params_size);
190305491d2cSKalle Valo if (err) {
190416e64676SRafał Miłecki bphy_err(drvr, "WLC_SET_SSID failed (%d)\n", err);
190505491d2cSKalle Valo goto done;
190605491d2cSKalle Valo }
190705491d2cSKalle Valo
190805491d2cSKalle Valo done:
190905491d2cSKalle Valo if (err)
191005491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
191105491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
191205491d2cSKalle Valo return err;
191305491d2cSKalle Valo }
191405491d2cSKalle Valo
191505491d2cSKalle Valo static s32
brcmf_cfg80211_leave_ibss(struct wiphy * wiphy,struct net_device * ndev)191605491d2cSKalle Valo brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *ndev)
191705491d2cSKalle Valo {
191805491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
191905491d2cSKalle Valo
192005491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
19216a98d64aSHante Meuleman if (!check_vif_up(ifp->vif)) {
19226a98d64aSHante Meuleman /* When driver is being unloaded, it can end up here. If an
19236a98d64aSHante Meuleman * error is returned then later on a debug trace in the wireless
19246a98d64aSHante Meuleman * core module will be printed. To avoid this 0 is returned.
19256a98d64aSHante Meuleman */
19266a98d64aSHante Meuleman return 0;
19276a98d64aSHante Meuleman }
192805491d2cSKalle Valo
19291b050d97SSoontak Lee brcmf_link_down(ifp->vif, WLAN_REASON_DEAUTH_LEAVING, true);
193042e0ed0dSHante Meuleman brcmf_net_setcarrier(ifp, false);
193105491d2cSKalle Valo
193205491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
193305491d2cSKalle Valo
193405491d2cSKalle Valo return 0;
193505491d2cSKalle Valo }
193605491d2cSKalle Valo
brcmf_set_wpa_version(struct net_device * ndev,struct cfg80211_connect_params * sme)193705491d2cSKalle Valo static s32 brcmf_set_wpa_version(struct net_device *ndev,
193805491d2cSKalle Valo struct cfg80211_connect_params *sme)
193905491d2cSKalle Valo {
19403ef005b8SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
194105491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
194216e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
194305491d2cSKalle Valo struct brcmf_cfg80211_security *sec;
194405491d2cSKalle Valo s32 val = 0;
194505491d2cSKalle Valo s32 err = 0;
194605491d2cSKalle Valo
194705491d2cSKalle Valo if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
194805491d2cSKalle Valo val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
194905491d2cSKalle Valo else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
195005491d2cSKalle Valo val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
19513b1e0a7bSChung-Hsien Hsu else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_3)
19523b1e0a7bSChung-Hsien Hsu val = WPA3_AUTH_SAE_PSK;
195305491d2cSKalle Valo else
195405491d2cSKalle Valo val = WPA_AUTH_DISABLED;
195505491d2cSKalle Valo brcmf_dbg(CONN, "setting wpa_auth to 0x%0x\n", val);
19563ef005b8SRafał Miłecki err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", val);
195705491d2cSKalle Valo if (err) {
195816e64676SRafał Miłecki bphy_err(drvr, "set wpa_auth failed (%d)\n", err);
195905491d2cSKalle Valo return err;
196005491d2cSKalle Valo }
196105491d2cSKalle Valo sec = &profile->sec;
196205491d2cSKalle Valo sec->wpa_versions = sme->crypto.wpa_versions;
196305491d2cSKalle Valo return err;
196405491d2cSKalle Valo }
196505491d2cSKalle Valo
brcmf_set_auth_type(struct net_device * ndev,struct cfg80211_connect_params * sme)196605491d2cSKalle Valo static s32 brcmf_set_auth_type(struct net_device *ndev,
196705491d2cSKalle Valo struct cfg80211_connect_params *sme)
196805491d2cSKalle Valo {
19693ef005b8SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
197005491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
197116e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
197205491d2cSKalle Valo struct brcmf_cfg80211_security *sec;
197305491d2cSKalle Valo s32 val = 0;
197405491d2cSKalle Valo s32 err = 0;
197505491d2cSKalle Valo
197605491d2cSKalle Valo switch (sme->auth_type) {
197705491d2cSKalle Valo case NL80211_AUTHTYPE_OPEN_SYSTEM:
197805491d2cSKalle Valo val = 0;
197905491d2cSKalle Valo brcmf_dbg(CONN, "open system\n");
198005491d2cSKalle Valo break;
198105491d2cSKalle Valo case NL80211_AUTHTYPE_SHARED_KEY:
198205491d2cSKalle Valo val = 1;
198305491d2cSKalle Valo brcmf_dbg(CONN, "shared key\n");
198405491d2cSKalle Valo break;
19853b1e0a7bSChung-Hsien Hsu case NL80211_AUTHTYPE_SAE:
19863b1e0a7bSChung-Hsien Hsu val = 3;
19873b1e0a7bSChung-Hsien Hsu brcmf_dbg(CONN, "SAE authentication\n");
19883b1e0a7bSChung-Hsien Hsu break;
198905491d2cSKalle Valo default:
199005491d2cSKalle Valo val = 2;
199192c31360SHante Meuleman brcmf_dbg(CONN, "automatic, auth type (%d)\n", sme->auth_type);
199205491d2cSKalle Valo break;
199305491d2cSKalle Valo }
199405491d2cSKalle Valo
19953ef005b8SRafał Miłecki err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);
199605491d2cSKalle Valo if (err) {
199716e64676SRafał Miłecki bphy_err(drvr, "set auth failed (%d)\n", err);
199805491d2cSKalle Valo return err;
199905491d2cSKalle Valo }
200005491d2cSKalle Valo sec = &profile->sec;
200105491d2cSKalle Valo sec->auth_type = sme->auth_type;
200205491d2cSKalle Valo return err;
200305491d2cSKalle Valo }
200405491d2cSKalle Valo
200505491d2cSKalle Valo static s32
brcmf_set_wsec_mode(struct net_device * ndev,struct cfg80211_connect_params * sme)200605491d2cSKalle Valo brcmf_set_wsec_mode(struct net_device *ndev,
2007240d61a9SHante Meuleman struct cfg80211_connect_params *sme)
200805491d2cSKalle Valo {
20093ef005b8SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
201005491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
201116e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
201205491d2cSKalle Valo struct brcmf_cfg80211_security *sec;
201305491d2cSKalle Valo s32 pval = 0;
201405491d2cSKalle Valo s32 gval = 0;
201505491d2cSKalle Valo s32 wsec;
201605491d2cSKalle Valo s32 err = 0;
201705491d2cSKalle Valo
201805491d2cSKalle Valo if (sme->crypto.n_ciphers_pairwise) {
201905491d2cSKalle Valo switch (sme->crypto.ciphers_pairwise[0]) {
202005491d2cSKalle Valo case WLAN_CIPHER_SUITE_WEP40:
202105491d2cSKalle Valo case WLAN_CIPHER_SUITE_WEP104:
202205491d2cSKalle Valo pval = WEP_ENABLED;
202305491d2cSKalle Valo break;
202405491d2cSKalle Valo case WLAN_CIPHER_SUITE_TKIP:
202505491d2cSKalle Valo pval = TKIP_ENABLED;
202605491d2cSKalle Valo break;
202705491d2cSKalle Valo case WLAN_CIPHER_SUITE_CCMP:
202805491d2cSKalle Valo pval = AES_ENABLED;
202905491d2cSKalle Valo break;
203005491d2cSKalle Valo case WLAN_CIPHER_SUITE_AES_CMAC:
203105491d2cSKalle Valo pval = AES_ENABLED;
203205491d2cSKalle Valo break;
203305491d2cSKalle Valo default:
203416e64676SRafał Miłecki bphy_err(drvr, "invalid cipher pairwise (%d)\n",
203505491d2cSKalle Valo sme->crypto.ciphers_pairwise[0]);
203605491d2cSKalle Valo return -EINVAL;
203705491d2cSKalle Valo }
203805491d2cSKalle Valo }
203905491d2cSKalle Valo if (sme->crypto.cipher_group) {
204005491d2cSKalle Valo switch (sme->crypto.cipher_group) {
204105491d2cSKalle Valo case WLAN_CIPHER_SUITE_WEP40:
204205491d2cSKalle Valo case WLAN_CIPHER_SUITE_WEP104:
204305491d2cSKalle Valo gval = WEP_ENABLED;
204405491d2cSKalle Valo break;
204505491d2cSKalle Valo case WLAN_CIPHER_SUITE_TKIP:
204605491d2cSKalle Valo gval = TKIP_ENABLED;
204705491d2cSKalle Valo break;
204805491d2cSKalle Valo case WLAN_CIPHER_SUITE_CCMP:
204905491d2cSKalle Valo gval = AES_ENABLED;
205005491d2cSKalle Valo break;
205105491d2cSKalle Valo case WLAN_CIPHER_SUITE_AES_CMAC:
205205491d2cSKalle Valo gval = AES_ENABLED;
205305491d2cSKalle Valo break;
205405491d2cSKalle Valo default:
205516e64676SRafał Miłecki bphy_err(drvr, "invalid cipher group (%d)\n",
205605491d2cSKalle Valo sme->crypto.cipher_group);
205705491d2cSKalle Valo return -EINVAL;
205805491d2cSKalle Valo }
205905491d2cSKalle Valo }
206005491d2cSKalle Valo
206105491d2cSKalle Valo brcmf_dbg(CONN, "pval (%d) gval (%d)\n", pval, gval);
206205491d2cSKalle Valo /* In case of privacy, but no security and WPS then simulate */
206305491d2cSKalle Valo /* setting AES. WPS-2.0 allows no security */
206405491d2cSKalle Valo if (brcmf_find_wpsie(sme->ie, sme->ie_len) && !pval && !gval &&
206505491d2cSKalle Valo sme->privacy)
206605491d2cSKalle Valo pval = AES_ENABLED;
206705491d2cSKalle Valo
206805491d2cSKalle Valo wsec = pval | gval;
20693ef005b8SRafał Miłecki err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
207005491d2cSKalle Valo if (err) {
207116e64676SRafał Miłecki bphy_err(drvr, "error (%d)\n", err);
207205491d2cSKalle Valo return err;
207305491d2cSKalle Valo }
207405491d2cSKalle Valo
207505491d2cSKalle Valo sec = &profile->sec;
207605491d2cSKalle Valo sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
207705491d2cSKalle Valo sec->cipher_group = sme->crypto.cipher_group;
207805491d2cSKalle Valo
207905491d2cSKalle Valo return err;
208005491d2cSKalle Valo }
208105491d2cSKalle Valo
208205491d2cSKalle Valo static s32
brcmf_set_key_mgmt(struct net_device * ndev,struct cfg80211_connect_params * sme)208305491d2cSKalle Valo brcmf_set_key_mgmt(struct net_device *ndev, struct cfg80211_connect_params *sme)
208405491d2cSKalle Valo {
2085240d61a9SHante Meuleman struct brcmf_if *ifp = netdev_priv(ndev);
20862526ff21SArend van Spriel struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
208716e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
2088240d61a9SHante Meuleman s32 val;
2089240d61a9SHante Meuleman s32 err;
2090240d61a9SHante Meuleman const struct brcmf_tlv *rsn_ie;
2091240d61a9SHante Meuleman const u8 *ie;
2092240d61a9SHante Meuleman u32 ie_len;
2093240d61a9SHante Meuleman u32 offset;
2094240d61a9SHante Meuleman u16 rsn_cap;
2095240d61a9SHante Meuleman u32 mfp;
2096240d61a9SHante Meuleman u16 count;
209705491d2cSKalle Valo
20982526ff21SArend van Spriel profile->use_fwsup = BRCMF_PROFILE_FWSUP_NONE;
2099be898fedSChung-Hsien Hsu profile->is_ft = false;
21002526ff21SArend van Spriel
2101240d61a9SHante Meuleman if (!sme->crypto.n_akm_suites)
2102240d61a9SHante Meuleman return 0;
2103240d61a9SHante Meuleman
2104*fb1862ceSArend van Spriel err = brcmf_fil_bsscfg_int_get(netdev_priv(ndev),
2105*fb1862ceSArend van Spriel "wpa_auth", &val);
210605491d2cSKalle Valo if (err) {
210716e64676SRafał Miłecki bphy_err(drvr, "could not get wpa_auth (%d)\n", err);
210805491d2cSKalle Valo return err;
210905491d2cSKalle Valo }
211005491d2cSKalle Valo if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
211105491d2cSKalle Valo switch (sme->crypto.akm_suites[0]) {
211205491d2cSKalle Valo case WLAN_AKM_SUITE_8021X:
211305491d2cSKalle Valo val = WPA_AUTH_UNSPECIFIED;
21142526ff21SArend van Spriel if (sme->want_1x)
21152526ff21SArend van Spriel profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
211605491d2cSKalle Valo break;
211705491d2cSKalle Valo case WLAN_AKM_SUITE_PSK:
211805491d2cSKalle Valo val = WPA_AUTH_PSK;
211905491d2cSKalle Valo break;
212005491d2cSKalle Valo default:
2121beca6bd9SJames Prestwood bphy_err(drvr, "invalid akm suite (%d)\n",
2122beca6bd9SJames Prestwood sme->crypto.akm_suites[0]);
212305491d2cSKalle Valo return -EINVAL;
212405491d2cSKalle Valo }
212505491d2cSKalle Valo } else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
212605491d2cSKalle Valo switch (sme->crypto.akm_suites[0]) {
212705491d2cSKalle Valo case WLAN_AKM_SUITE_8021X:
212805491d2cSKalle Valo val = WPA2_AUTH_UNSPECIFIED;
21292526ff21SArend van Spriel if (sme->want_1x)
21302526ff21SArend van Spriel profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
213105491d2cSKalle Valo break;
2132240d61a9SHante Meuleman case WLAN_AKM_SUITE_8021X_SHA256:
2133240d61a9SHante Meuleman val = WPA2_AUTH_1X_SHA256;
21342526ff21SArend van Spriel if (sme->want_1x)
21352526ff21SArend van Spriel profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
2136240d61a9SHante Meuleman break;
2137240d61a9SHante Meuleman case WLAN_AKM_SUITE_PSK_SHA256:
2138240d61a9SHante Meuleman val = WPA2_AUTH_PSK_SHA256;
2139240d61a9SHante Meuleman break;
214005491d2cSKalle Valo case WLAN_AKM_SUITE_PSK:
214105491d2cSKalle Valo val = WPA2_AUTH_PSK;
214205491d2cSKalle Valo break;
21434ad298daSChung-Hsien Hsu case WLAN_AKM_SUITE_FT_8021X:
21444ad298daSChung-Hsien Hsu val = WPA2_AUTH_UNSPECIFIED | WPA2_AUTH_FT;
2145be898fedSChung-Hsien Hsu profile->is_ft = true;
2146a858376cSChung-Hsien Hsu if (sme->want_1x)
2147a858376cSChung-Hsien Hsu profile->use_fwsup = BRCMF_PROFILE_FWSUP_1X;
21484ad298daSChung-Hsien Hsu break;
21494ad298daSChung-Hsien Hsu case WLAN_AKM_SUITE_FT_PSK:
21504ad298daSChung-Hsien Hsu val = WPA2_AUTH_PSK | WPA2_AUTH_FT;
2151be898fedSChung-Hsien Hsu profile->is_ft = true;
21524ad298daSChung-Hsien Hsu break;
215305491d2cSKalle Valo default:
2154beca6bd9SJames Prestwood bphy_err(drvr, "invalid akm suite (%d)\n",
2155beca6bd9SJames Prestwood sme->crypto.akm_suites[0]);
215605491d2cSKalle Valo return -EINVAL;
215705491d2cSKalle Valo }
21583b1e0a7bSChung-Hsien Hsu } else if (val & WPA3_AUTH_SAE_PSK) {
21593b1e0a7bSChung-Hsien Hsu switch (sme->crypto.akm_suites[0]) {
21603b1e0a7bSChung-Hsien Hsu case WLAN_AKM_SUITE_SAE:
21613b1e0a7bSChung-Hsien Hsu val = WPA3_AUTH_SAE_PSK;
2162b2fe11f0SChung-Hsien Hsu if (sme->crypto.sae_pwd) {
2163b2fe11f0SChung-Hsien Hsu brcmf_dbg(INFO, "using SAE offload\n");
2164b2fe11f0SChung-Hsien Hsu profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
2165b2fe11f0SChung-Hsien Hsu }
21663b1e0a7bSChung-Hsien Hsu break;
21674b51de06SPaweł Drewniak case WLAN_AKM_SUITE_FT_OVER_SAE:
21684b51de06SPaweł Drewniak val = WPA3_AUTH_SAE_PSK | WPA2_AUTH_FT;
21694b51de06SPaweł Drewniak profile->is_ft = true;
21704b51de06SPaweł Drewniak if (sme->crypto.sae_pwd) {
21714b51de06SPaweł Drewniak brcmf_dbg(INFO, "using SAE offload\n");
21724b51de06SPaweł Drewniak profile->use_fwsup = BRCMF_PROFILE_FWSUP_SAE;
21734b51de06SPaweł Drewniak }
21744b51de06SPaweł Drewniak break;
21753b1e0a7bSChung-Hsien Hsu default:
2176beca6bd9SJames Prestwood bphy_err(drvr, "invalid akm suite (%d)\n",
2177beca6bd9SJames Prestwood sme->crypto.akm_suites[0]);
21783b1e0a7bSChung-Hsien Hsu return -EINVAL;
21793b1e0a7bSChung-Hsien Hsu }
218005491d2cSKalle Valo }
218105491d2cSKalle Valo
21822526ff21SArend van Spriel if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X)
21832526ff21SArend van Spriel brcmf_dbg(INFO, "using 1X offload\n");
21842526ff21SArend van Spriel
2185240d61a9SHante Meuleman if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
2186240d61a9SHante Meuleman goto skip_mfp_config;
2187240d61a9SHante Meuleman /* The MFP mode (1 or 2) needs to be determined, parse IEs. The
2188240d61a9SHante Meuleman * IE will not be verified, just a quick search for MFP config
2189240d61a9SHante Meuleman */
2190240d61a9SHante Meuleman rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie, sme->ie_len,
2191240d61a9SHante Meuleman WLAN_EID_RSN);
2192240d61a9SHante Meuleman if (!rsn_ie)
2193240d61a9SHante Meuleman goto skip_mfp_config;
2194240d61a9SHante Meuleman ie = (const u8 *)rsn_ie;
2195240d61a9SHante Meuleman ie_len = rsn_ie->len + TLV_HDR_LEN;
2196240d61a9SHante Meuleman /* Skip unicast suite */
2197240d61a9SHante Meuleman offset = TLV_HDR_LEN + WPA_IE_VERSION_LEN + WPA_IE_MIN_OUI_LEN;
2198240d61a9SHante Meuleman if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
2199240d61a9SHante Meuleman goto skip_mfp_config;
2200240d61a9SHante Meuleman /* Skip multicast suite */
2201240d61a9SHante Meuleman count = ie[offset] + (ie[offset + 1] << 8);
2202240d61a9SHante Meuleman offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
2203240d61a9SHante Meuleman if (offset + WPA_IE_SUITE_COUNT_LEN >= ie_len)
2204240d61a9SHante Meuleman goto skip_mfp_config;
2205240d61a9SHante Meuleman /* Skip auth key management suite(s) */
2206240d61a9SHante Meuleman count = ie[offset] + (ie[offset + 1] << 8);
2207240d61a9SHante Meuleman offset += WPA_IE_SUITE_COUNT_LEN + (count * WPA_IE_MIN_OUI_LEN);
2208240d61a9SHante Meuleman if (offset + WPA_IE_SUITE_COUNT_LEN > ie_len)
2209240d61a9SHante Meuleman goto skip_mfp_config;
2210240d61a9SHante Meuleman /* Ready to read capabilities */
2211240d61a9SHante Meuleman mfp = BRCMF_MFP_NONE;
2212240d61a9SHante Meuleman rsn_cap = ie[offset] + (ie[offset + 1] << 8);
2213240d61a9SHante Meuleman if (rsn_cap & RSN_CAP_MFPR_MASK)
2214240d61a9SHante Meuleman mfp = BRCMF_MFP_REQUIRED;
2215240d61a9SHante Meuleman else if (rsn_cap & RSN_CAP_MFPC_MASK)
2216240d61a9SHante Meuleman mfp = BRCMF_MFP_CAPABLE;
2217240d61a9SHante Meuleman brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "mfp", mfp);
2218240d61a9SHante Meuleman
2219240d61a9SHante Meuleman skip_mfp_config:
222005491d2cSKalle Valo brcmf_dbg(CONN, "setting wpa_auth to %d\n", val);
2221240d61a9SHante Meuleman err = brcmf_fil_bsscfg_int_set(netdev_priv(ndev), "wpa_auth", val);
222205491d2cSKalle Valo if (err) {
222316e64676SRafał Miłecki bphy_err(drvr, "could not set wpa_auth (%d)\n", err);
222405491d2cSKalle Valo return err;
222505491d2cSKalle Valo }
222605491d2cSKalle Valo
222705491d2cSKalle Valo return err;
222805491d2cSKalle Valo }
222905491d2cSKalle Valo
223005491d2cSKalle Valo static s32
brcmf_set_sharedkey(struct net_device * ndev,struct cfg80211_connect_params * sme)223105491d2cSKalle Valo brcmf_set_sharedkey(struct net_device *ndev,
223205491d2cSKalle Valo struct cfg80211_connect_params *sme)
223305491d2cSKalle Valo {
22343ef005b8SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
223516e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
223605491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = ndev_to_prof(ndev);
223705491d2cSKalle Valo struct brcmf_cfg80211_security *sec;
223805491d2cSKalle Valo struct brcmf_wsec_key key;
223905491d2cSKalle Valo s32 val;
224005491d2cSKalle Valo s32 err = 0;
224105491d2cSKalle Valo
224205491d2cSKalle Valo brcmf_dbg(CONN, "key len (%d)\n", sme->key_len);
224305491d2cSKalle Valo
224405491d2cSKalle Valo if (sme->key_len == 0)
224505491d2cSKalle Valo return 0;
224605491d2cSKalle Valo
224705491d2cSKalle Valo sec = &profile->sec;
224805491d2cSKalle Valo brcmf_dbg(CONN, "wpa_versions 0x%x cipher_pairwise 0x%x\n",
224905491d2cSKalle Valo sec->wpa_versions, sec->cipher_pairwise);
225005491d2cSKalle Valo
22513b1e0a7bSChung-Hsien Hsu if (sec->wpa_versions & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2 |
22523b1e0a7bSChung-Hsien Hsu NL80211_WPA_VERSION_3))
225305491d2cSKalle Valo return 0;
225405491d2cSKalle Valo
225505491d2cSKalle Valo if (!(sec->cipher_pairwise &
225605491d2cSKalle Valo (WLAN_CIPHER_SUITE_WEP40 | WLAN_CIPHER_SUITE_WEP104)))
225705491d2cSKalle Valo return 0;
225805491d2cSKalle Valo
225905491d2cSKalle Valo memset(&key, 0, sizeof(key));
226005491d2cSKalle Valo key.len = (u32) sme->key_len;
226105491d2cSKalle Valo key.index = (u32) sme->key_idx;
226205491d2cSKalle Valo if (key.len > sizeof(key.data)) {
226316e64676SRafał Miłecki bphy_err(drvr, "Too long key length (%u)\n", key.len);
226405491d2cSKalle Valo return -EINVAL;
226505491d2cSKalle Valo }
226605491d2cSKalle Valo memcpy(key.data, sme->key, key.len);
226705491d2cSKalle Valo key.flags = BRCMF_PRIMARY_KEY;
226805491d2cSKalle Valo switch (sec->cipher_pairwise) {
226905491d2cSKalle Valo case WLAN_CIPHER_SUITE_WEP40:
227005491d2cSKalle Valo key.algo = CRYPTO_ALGO_WEP1;
227105491d2cSKalle Valo break;
227205491d2cSKalle Valo case WLAN_CIPHER_SUITE_WEP104:
227305491d2cSKalle Valo key.algo = CRYPTO_ALGO_WEP128;
227405491d2cSKalle Valo break;
227505491d2cSKalle Valo default:
227616e64676SRafał Miłecki bphy_err(drvr, "Invalid algorithm (%d)\n",
227705491d2cSKalle Valo sme->crypto.ciphers_pairwise[0]);
227805491d2cSKalle Valo return -EINVAL;
227905491d2cSKalle Valo }
228005491d2cSKalle Valo /* Set the new key/index */
228105491d2cSKalle Valo brcmf_dbg(CONN, "key length (%d) key index (%d) algo (%d)\n",
228205491d2cSKalle Valo key.len, key.index, key.algo);
228305491d2cSKalle Valo brcmf_dbg(CONN, "key \"%s\"\n", key.data);
22843ef005b8SRafał Miłecki err = send_key_to_dongle(ifp, &key);
228505491d2cSKalle Valo if (err)
228605491d2cSKalle Valo return err;
228705491d2cSKalle Valo
228805491d2cSKalle Valo if (sec->auth_type == NL80211_AUTHTYPE_SHARED_KEY) {
228905491d2cSKalle Valo brcmf_dbg(CONN, "set auth_type to shared key\n");
229005491d2cSKalle Valo val = WL_AUTH_SHARED_KEY; /* shared key */
22913ef005b8SRafał Miłecki err = brcmf_fil_bsscfg_int_set(ifp, "auth", val);
229205491d2cSKalle Valo if (err)
229316e64676SRafał Miłecki bphy_err(drvr, "set auth failed (%d)\n", err);
229405491d2cSKalle Valo }
229505491d2cSKalle Valo return err;
229605491d2cSKalle Valo }
229705491d2cSKalle Valo
229805491d2cSKalle Valo static
brcmf_war_auth_type(struct brcmf_if * ifp,enum nl80211_auth_type type)229905491d2cSKalle Valo enum nl80211_auth_type brcmf_war_auth_type(struct brcmf_if *ifp,
230005491d2cSKalle Valo enum nl80211_auth_type type)
230105491d2cSKalle Valo {
230205491d2cSKalle Valo if (type == NL80211_AUTHTYPE_AUTOMATIC &&
230305491d2cSKalle Valo brcmf_feat_is_quirk_enabled(ifp, BRCMF_FEAT_QUIRK_AUTO_AUTH)) {
230405491d2cSKalle Valo brcmf_dbg(CONN, "WAR: use OPEN instead of AUTO\n");
230505491d2cSKalle Valo type = NL80211_AUTHTYPE_OPEN_SYSTEM;
230605491d2cSKalle Valo }
230705491d2cSKalle Valo return type;
230805491d2cSKalle Valo }
230905491d2cSKalle Valo
brcmf_set_join_pref(struct brcmf_if * ifp,struct cfg80211_bss_selection * bss_select)23107705ba6fSArend van Spriel static void brcmf_set_join_pref(struct brcmf_if *ifp,
23117705ba6fSArend van Spriel struct cfg80211_bss_selection *bss_select)
23127705ba6fSArend van Spriel {
231316e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
23147705ba6fSArend van Spriel struct brcmf_join_pref_params join_pref_params[2];
23157705ba6fSArend van Spriel enum nl80211_band band;
23167705ba6fSArend van Spriel int err, i = 0;
23177705ba6fSArend van Spriel
23187705ba6fSArend van Spriel join_pref_params[i].len = 2;
23197705ba6fSArend van Spriel join_pref_params[i].rssi_gain = 0;
23207705ba6fSArend van Spriel
23217705ba6fSArend van Spriel if (bss_select->behaviour != NL80211_BSS_SELECT_ATTR_BAND_PREF)
23227705ba6fSArend van Spriel brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_ASSOC_PREFER, WLC_BAND_AUTO);
23237705ba6fSArend van Spriel
23247705ba6fSArend van Spriel switch (bss_select->behaviour) {
23257705ba6fSArend van Spriel case __NL80211_BSS_SELECT_ATTR_INVALID:
23267705ba6fSArend van Spriel brcmf_c_set_joinpref_default(ifp);
23277705ba6fSArend van Spriel return;
23287705ba6fSArend van Spriel case NL80211_BSS_SELECT_ATTR_BAND_PREF:
23297705ba6fSArend van Spriel join_pref_params[i].type = BRCMF_JOIN_PREF_BAND;
23307705ba6fSArend van Spriel band = bss_select->param.band_pref;
23317705ba6fSArend van Spriel join_pref_params[i].band = nl80211_band_to_fwil(band);
23327705ba6fSArend van Spriel i++;
23337705ba6fSArend van Spriel break;
23347705ba6fSArend van Spriel case NL80211_BSS_SELECT_ATTR_RSSI_ADJUST:
23357705ba6fSArend van Spriel join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI_DELTA;
23367705ba6fSArend van Spriel band = bss_select->param.adjust.band;
23377705ba6fSArend van Spriel join_pref_params[i].band = nl80211_band_to_fwil(band);
23387705ba6fSArend van Spriel join_pref_params[i].rssi_gain = bss_select->param.adjust.delta;
23397705ba6fSArend van Spriel i++;
23407705ba6fSArend van Spriel break;
23417705ba6fSArend van Spriel case NL80211_BSS_SELECT_ATTR_RSSI:
23427705ba6fSArend van Spriel default:
23437705ba6fSArend van Spriel break;
23447705ba6fSArend van Spriel }
23457705ba6fSArend van Spriel join_pref_params[i].type = BRCMF_JOIN_PREF_RSSI;
23467705ba6fSArend van Spriel join_pref_params[i].len = 2;
23477705ba6fSArend van Spriel join_pref_params[i].rssi_gain = 0;
23487705ba6fSArend van Spriel join_pref_params[i].band = 0;
23497705ba6fSArend van Spriel err = brcmf_fil_iovar_data_set(ifp, "join_pref", join_pref_params,
23507705ba6fSArend van Spriel sizeof(join_pref_params));
23517705ba6fSArend van Spriel if (err)
235216e64676SRafał Miłecki bphy_err(drvr, "Set join_pref error (%d)\n", err);
23537705ba6fSArend van Spriel }
23547705ba6fSArend van Spriel
235505491d2cSKalle Valo static s32
brcmf_cfg80211_connect(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme)235605491d2cSKalle Valo brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *ndev,
235705491d2cSKalle Valo struct cfg80211_connect_params *sme)
235805491d2cSKalle Valo {
235905491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
236005491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
23612526ff21SArend van Spriel struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
236205491d2cSKalle Valo struct ieee80211_channel *chan = sme->channel;
236316e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
236405491d2cSKalle Valo struct brcmf_join_params join_params;
236505491d2cSKalle Valo size_t join_params_size;
236605491d2cSKalle Valo const struct brcmf_tlv *rsn_ie;
236705491d2cSKalle Valo const struct brcmf_vs_tlv *wpa_ie;
236805491d2cSKalle Valo const void *ie;
236905491d2cSKalle Valo u32 ie_len;
237005491d2cSKalle Valo struct brcmf_ext_join_params_le *ext_join_params;
237105491d2cSKalle Valo u16 chanspec;
237205491d2cSKalle Valo s32 err = 0;
2373e9a6ca82SHante Meuleman u32 ssid_len;
237405491d2cSKalle Valo
237505491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
237605491d2cSKalle Valo if (!check_vif_up(ifp->vif))
237705491d2cSKalle Valo return -EIO;
237805491d2cSKalle Valo
237905491d2cSKalle Valo if (!sme->ssid) {
238016e64676SRafał Miłecki bphy_err(drvr, "Invalid ssid\n");
238105491d2cSKalle Valo return -EOPNOTSUPP;
238205491d2cSKalle Valo }
238305491d2cSKalle Valo
2384c81c1fd4SPrasanna Kerekoppa if (sme->channel_hint)
2385c81c1fd4SPrasanna Kerekoppa chan = sme->channel_hint;
2386c81c1fd4SPrasanna Kerekoppa
2387c81c1fd4SPrasanna Kerekoppa if (sme->bssid_hint)
2388c81c1fd4SPrasanna Kerekoppa sme->bssid = sme->bssid_hint;
2389c81c1fd4SPrasanna Kerekoppa
239005491d2cSKalle Valo if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif) {
239105491d2cSKalle Valo /* A normal (non P2P) connection request setup. */
239205491d2cSKalle Valo ie = NULL;
239305491d2cSKalle Valo ie_len = 0;
239405491d2cSKalle Valo /* find the WPA_IE */
239505491d2cSKalle Valo wpa_ie = brcmf_find_wpaie((u8 *)sme->ie, sme->ie_len);
239605491d2cSKalle Valo if (wpa_ie) {
239705491d2cSKalle Valo ie = wpa_ie;
239805491d2cSKalle Valo ie_len = wpa_ie->len + TLV_HDR_LEN;
239905491d2cSKalle Valo } else {
240005491d2cSKalle Valo /* find the RSN_IE */
240105491d2cSKalle Valo rsn_ie = brcmf_parse_tlvs((const u8 *)sme->ie,
240205491d2cSKalle Valo sme->ie_len,
240305491d2cSKalle Valo WLAN_EID_RSN);
240405491d2cSKalle Valo if (rsn_ie) {
240505491d2cSKalle Valo ie = rsn_ie;
240605491d2cSKalle Valo ie_len = rsn_ie->len + TLV_HDR_LEN;
240705491d2cSKalle Valo }
240805491d2cSKalle Valo }
240905491d2cSKalle Valo brcmf_fil_iovar_data_set(ifp, "wpaie", ie, ie_len);
241005491d2cSKalle Valo }
241105491d2cSKalle Valo
241205491d2cSKalle Valo err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
241305491d2cSKalle Valo sme->ie, sme->ie_len);
241405491d2cSKalle Valo if (err)
241516e64676SRafał Miłecki bphy_err(drvr, "Set Assoc REQ IE Failed\n");
241605491d2cSKalle Valo else
241705491d2cSKalle Valo brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
241805491d2cSKalle Valo
241905491d2cSKalle Valo set_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
242005491d2cSKalle Valo
242105491d2cSKalle Valo if (chan) {
242205491d2cSKalle Valo cfg->channel =
242305491d2cSKalle Valo ieee80211_frequency_to_channel(chan->center_freq);
242405491d2cSKalle Valo chanspec = channel_to_chanspec(&cfg->d11inf, chan);
242505491d2cSKalle Valo brcmf_dbg(CONN, "channel=%d, center_req=%d, chanspec=0x%04x\n",
242605491d2cSKalle Valo cfg->channel, chan->center_freq, chanspec);
242705491d2cSKalle Valo } else {
242805491d2cSKalle Valo cfg->channel = 0;
242905491d2cSKalle Valo chanspec = 0;
243005491d2cSKalle Valo }
243105491d2cSKalle Valo
243205491d2cSKalle Valo brcmf_dbg(INFO, "ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
243305491d2cSKalle Valo
243405491d2cSKalle Valo err = brcmf_set_wpa_version(ndev, sme);
243505491d2cSKalle Valo if (err) {
243616e64676SRafał Miłecki bphy_err(drvr, "wl_set_wpa_version failed (%d)\n", err);
243705491d2cSKalle Valo goto done;
243805491d2cSKalle Valo }
243905491d2cSKalle Valo
244005491d2cSKalle Valo sme->auth_type = brcmf_war_auth_type(ifp, sme->auth_type);
244105491d2cSKalle Valo err = brcmf_set_auth_type(ndev, sme);
244205491d2cSKalle Valo if (err) {
244316e64676SRafał Miłecki bphy_err(drvr, "wl_set_auth_type failed (%d)\n", err);
244405491d2cSKalle Valo goto done;
244505491d2cSKalle Valo }
244605491d2cSKalle Valo
2447240d61a9SHante Meuleman err = brcmf_set_wsec_mode(ndev, sme);
244805491d2cSKalle Valo if (err) {
244916e64676SRafał Miłecki bphy_err(drvr, "wl_set_set_cipher failed (%d)\n", err);
245005491d2cSKalle Valo goto done;
245105491d2cSKalle Valo }
245205491d2cSKalle Valo
245305491d2cSKalle Valo err = brcmf_set_key_mgmt(ndev, sme);
245405491d2cSKalle Valo if (err) {
245516e64676SRafał Miłecki bphy_err(drvr, "wl_set_key_mgmt failed (%d)\n", err);
245605491d2cSKalle Valo goto done;
245705491d2cSKalle Valo }
245805491d2cSKalle Valo
245905491d2cSKalle Valo err = brcmf_set_sharedkey(ndev, sme);
246005491d2cSKalle Valo if (err) {
246116e64676SRafał Miłecki bphy_err(drvr, "brcmf_set_sharedkey failed (%d)\n", err);
246205491d2cSKalle Valo goto done;
246305491d2cSKalle Valo }
246405491d2cSKalle Valo
24653b1e0a7bSChung-Hsien Hsu if (sme->crypto.psk &&
24663b1e0a7bSChung-Hsien Hsu profile->use_fwsup != BRCMF_PROFILE_FWSUP_SAE) {
24672526ff21SArend van Spriel if (WARN_ON(profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE)) {
24682526ff21SArend van Spriel err = -EINVAL;
24692526ff21SArend van Spriel goto done;
24702526ff21SArend van Spriel }
2471b8a64f0eSArend van Spriel brcmf_dbg(INFO, "using PSK offload\n");
24722526ff21SArend van Spriel profile->use_fwsup = BRCMF_PROFILE_FWSUP_PSK;
24732526ff21SArend van Spriel }
2474b8a64f0eSArend van Spriel
24752526ff21SArend van Spriel if (profile->use_fwsup != BRCMF_PROFILE_FWSUP_NONE) {
2476b8a64f0eSArend van Spriel /* enable firmware supplicant for this interface */
2477b8a64f0eSArend van Spriel err = brcmf_fil_iovar_int_set(ifp, "sup_wpa", 1);
2478b8a64f0eSArend van Spriel if (err < 0) {
247916e64676SRafał Miłecki bphy_err(drvr, "failed to enable fw supplicant\n");
2480b8a64f0eSArend van Spriel goto done;
2481b8a64f0eSArend van Spriel }
24822526ff21SArend van Spriel }
2483b8a64f0eSArend van Spriel
24843b1e0a7bSChung-Hsien Hsu if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_PSK)
2485b8a64f0eSArend van Spriel err = brcmf_set_pmk(ifp, sme->crypto.psk,
2486b8a64f0eSArend van Spriel BRCMF_WSEC_MAX_PSK_LEN);
24873b1e0a7bSChung-Hsien Hsu else if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_SAE) {
24883b1e0a7bSChung-Hsien Hsu /* clean up user-space RSNE */
248937ff144dSZhang Changzhong err = brcmf_fil_iovar_data_set(ifp, "wpaie", NULL, 0);
249037ff144dSZhang Changzhong if (err) {
24913b1e0a7bSChung-Hsien Hsu bphy_err(drvr, "failed to clean up user-space RSNE\n");
2492b8a64f0eSArend van Spriel goto done;
2493b8a64f0eSArend van Spriel }
24948d59a64cSHector Martin err = brcmf_fwvid_set_sae_password(ifp, &sme->crypto);
24953b1e0a7bSChung-Hsien Hsu if (!err && sme->crypto.psk)
24963b1e0a7bSChung-Hsien Hsu err = brcmf_set_pmk(ifp, sme->crypto.psk,
24973b1e0a7bSChung-Hsien Hsu BRCMF_WSEC_MAX_PSK_LEN);
24983b1e0a7bSChung-Hsien Hsu }
24993b1e0a7bSChung-Hsien Hsu if (err)
25003b1e0a7bSChung-Hsien Hsu goto done;
2501b8a64f0eSArend van Spriel
250205491d2cSKalle Valo /* Join with specific BSSID and cached SSID
250305491d2cSKalle Valo * If SSID is zero join based on BSSID only
250405491d2cSKalle Valo */
250505491d2cSKalle Valo join_params_size = offsetof(struct brcmf_ext_join_params_le, assoc_le) +
250605491d2cSKalle Valo offsetof(struct brcmf_assoc_params_le, chanspec_list);
250705491d2cSKalle Valo if (cfg->channel)
250805491d2cSKalle Valo join_params_size += sizeof(u16);
250984f23fb1SJakub Kicinski ext_join_params = kzalloc(sizeof(*ext_join_params), GFP_KERNEL);
251005491d2cSKalle Valo if (ext_join_params == NULL) {
251105491d2cSKalle Valo err = -ENOMEM;
251205491d2cSKalle Valo goto done;
251305491d2cSKalle Valo }
2514e9a6ca82SHante Meuleman ssid_len = min_t(u32, sme->ssid_len, IEEE80211_MAX_SSID_LEN);
2515e9a6ca82SHante Meuleman ext_join_params->ssid_le.SSID_len = cpu_to_le32(ssid_len);
2516e9a6ca82SHante Meuleman memcpy(&ext_join_params->ssid_le.SSID, sme->ssid, ssid_len);
2517e9a6ca82SHante Meuleman if (ssid_len < IEEE80211_MAX_SSID_LEN)
2518e9a6ca82SHante Meuleman brcmf_dbg(CONN, "SSID \"%s\", len (%d)\n",
2519e9a6ca82SHante Meuleman ext_join_params->ssid_le.SSID, ssid_len);
252005491d2cSKalle Valo
252105491d2cSKalle Valo /* Set up join scan parameters */
252205491d2cSKalle Valo ext_join_params->scan_le.scan_type = -1;
252305491d2cSKalle Valo ext_join_params->scan_le.home_time = cpu_to_le32(-1);
252405491d2cSKalle Valo
252505491d2cSKalle Valo if (sme->bssid)
252605491d2cSKalle Valo memcpy(&ext_join_params->assoc_le.bssid, sme->bssid, ETH_ALEN);
252705491d2cSKalle Valo else
252805491d2cSKalle Valo eth_broadcast_addr(ext_join_params->assoc_le.bssid);
252905491d2cSKalle Valo
253005491d2cSKalle Valo if (cfg->channel) {
253105491d2cSKalle Valo ext_join_params->assoc_le.chanspec_num = cpu_to_le32(1);
253205491d2cSKalle Valo
253305491d2cSKalle Valo ext_join_params->assoc_le.chanspec_list[0] =
253405491d2cSKalle Valo cpu_to_le16(chanspec);
253505491d2cSKalle Valo /* Increase dwell time to receive probe response or detect
253605491d2cSKalle Valo * beacon from target AP at a noisy air only during connect
253705491d2cSKalle Valo * command.
253805491d2cSKalle Valo */
253905491d2cSKalle Valo ext_join_params->scan_le.active_time =
254005491d2cSKalle Valo cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS);
254105491d2cSKalle Valo ext_join_params->scan_le.passive_time =
254205491d2cSKalle Valo cpu_to_le32(BRCMF_SCAN_JOIN_PASSIVE_DWELL_TIME_MS);
254305491d2cSKalle Valo /* To sync with presence period of VSDB GO send probe request
254405491d2cSKalle Valo * more frequently. Probe request will be stopped when it gets
254505491d2cSKalle Valo * probe response from target AP/GO.
254605491d2cSKalle Valo */
254705491d2cSKalle Valo ext_join_params->scan_le.nprobes =
254805491d2cSKalle Valo cpu_to_le32(BRCMF_SCAN_JOIN_ACTIVE_DWELL_TIME_MS /
254905491d2cSKalle Valo BRCMF_SCAN_JOIN_PROBE_INTERVAL_MS);
255005491d2cSKalle Valo } else {
255105491d2cSKalle Valo ext_join_params->scan_le.active_time = cpu_to_le32(-1);
255205491d2cSKalle Valo ext_join_params->scan_le.passive_time = cpu_to_le32(-1);
255305491d2cSKalle Valo ext_join_params->scan_le.nprobes = cpu_to_le32(-1);
255405491d2cSKalle Valo }
255505491d2cSKalle Valo
25567705ba6fSArend van Spriel brcmf_set_join_pref(ifp, &sme->bss_select);
25577705ba6fSArend van Spriel
255805491d2cSKalle Valo err = brcmf_fil_bsscfg_data_set(ifp, "join", ext_join_params,
255905491d2cSKalle Valo join_params_size);
256005491d2cSKalle Valo kfree(ext_join_params);
256105491d2cSKalle Valo if (!err)
256205491d2cSKalle Valo /* This is it. join command worked, we are done */
256305491d2cSKalle Valo goto done;
256405491d2cSKalle Valo
256505491d2cSKalle Valo /* join command failed, fallback to set ssid */
256605491d2cSKalle Valo memset(&join_params, 0, sizeof(join_params));
256705491d2cSKalle Valo join_params_size = sizeof(join_params.ssid_le);
256805491d2cSKalle Valo
2569e9a6ca82SHante Meuleman memcpy(&join_params.ssid_le.SSID, sme->ssid, ssid_len);
2570e9a6ca82SHante Meuleman join_params.ssid_le.SSID_len = cpu_to_le32(ssid_len);
257105491d2cSKalle Valo
257205491d2cSKalle Valo if (sme->bssid)
257305491d2cSKalle Valo memcpy(join_params.params_le.bssid, sme->bssid, ETH_ALEN);
257405491d2cSKalle Valo else
257505491d2cSKalle Valo eth_broadcast_addr(join_params.params_le.bssid);
257605491d2cSKalle Valo
257705491d2cSKalle Valo if (cfg->channel) {
257805491d2cSKalle Valo join_params.params_le.chanspec_list[0] = cpu_to_le16(chanspec);
257905491d2cSKalle Valo join_params.params_le.chanspec_num = cpu_to_le32(1);
258005491d2cSKalle Valo join_params_size += sizeof(join_params.params_le);
258105491d2cSKalle Valo }
258205491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
258305491d2cSKalle Valo &join_params, join_params_size);
258405491d2cSKalle Valo if (err)
258516e64676SRafał Miłecki bphy_err(drvr, "BRCMF_C_SET_SSID failed (%d)\n", err);
258605491d2cSKalle Valo
258705491d2cSKalle Valo done:
258805491d2cSKalle Valo if (err)
258905491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
259005491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
259105491d2cSKalle Valo return err;
259205491d2cSKalle Valo }
259305491d2cSKalle Valo
259405491d2cSKalle Valo static s32
brcmf_cfg80211_disconnect(struct wiphy * wiphy,struct net_device * ndev,u16 reason_code)259505491d2cSKalle Valo brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *ndev,
259605491d2cSKalle Valo u16 reason_code)
259705491d2cSKalle Valo {
259816e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
259905491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
260005491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
260116e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
260205491d2cSKalle Valo struct brcmf_scb_val_le scbval;
260305491d2cSKalle Valo s32 err = 0;
260405491d2cSKalle Valo
260505491d2cSKalle Valo brcmf_dbg(TRACE, "Enter. Reason code = %d\n", reason_code);
260605491d2cSKalle Valo if (!check_vif_up(ifp->vif))
260705491d2cSKalle Valo return -EIO;
260805491d2cSKalle Valo
260905491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
261005491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state);
261152617beeSWataru Gohda clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &ifp->vif->sme_state);
261252617beeSWataru Gohda clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &ifp->vif->sme_state);
261305491d2cSKalle Valo cfg80211_disconnected(ndev, reason_code, NULL, 0, true, GFP_KERNEL);
261405491d2cSKalle Valo
261505491d2cSKalle Valo memcpy(&scbval.ea, &profile->bssid, ETH_ALEN);
261605491d2cSKalle Valo scbval.val = cpu_to_le32(reason_code);
261705491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_DISASSOC,
261805491d2cSKalle Valo &scbval, sizeof(scbval));
261905491d2cSKalle Valo if (err)
262016e64676SRafał Miłecki bphy_err(drvr, "error (%d)\n", err);
262105491d2cSKalle Valo
262205491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
262305491d2cSKalle Valo return err;
262405491d2cSKalle Valo }
262505491d2cSKalle Valo
262605491d2cSKalle Valo static s32
brcmf_cfg80211_set_tx_power(struct wiphy * wiphy,struct wireless_dev * wdev,enum nl80211_tx_power_setting type,s32 mbm)262705491d2cSKalle Valo brcmf_cfg80211_set_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
262805491d2cSKalle Valo enum nl80211_tx_power_setting type, s32 mbm)
262905491d2cSKalle Valo {
263005491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
263105491d2cSKalle Valo struct net_device *ndev = cfg_to_ndev(cfg);
263205491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
263316e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
263405491d2cSKalle Valo s32 err;
263505491d2cSKalle Valo s32 disable;
263605491d2cSKalle Valo u32 qdbm = 127;
263705491d2cSKalle Valo
263805491d2cSKalle Valo brcmf_dbg(TRACE, "Enter %d %d\n", type, mbm);
263905491d2cSKalle Valo if (!check_vif_up(ifp->vif))
264005491d2cSKalle Valo return -EIO;
264105491d2cSKalle Valo
264205491d2cSKalle Valo switch (type) {
264305491d2cSKalle Valo case NL80211_TX_POWER_AUTOMATIC:
264405491d2cSKalle Valo break;
264505491d2cSKalle Valo case NL80211_TX_POWER_LIMITED:
264605491d2cSKalle Valo case NL80211_TX_POWER_FIXED:
264705491d2cSKalle Valo if (mbm < 0) {
264816e64676SRafał Miłecki bphy_err(drvr, "TX_POWER_FIXED - dbm is negative\n");
264905491d2cSKalle Valo err = -EINVAL;
265005491d2cSKalle Valo goto done;
265105491d2cSKalle Valo }
265205491d2cSKalle Valo qdbm = MBM_TO_DBM(4 * mbm);
265305491d2cSKalle Valo if (qdbm > 127)
265405491d2cSKalle Valo qdbm = 127;
265505491d2cSKalle Valo qdbm |= WL_TXPWR_OVERRIDE;
265605491d2cSKalle Valo break;
265705491d2cSKalle Valo default:
265816e64676SRafał Miłecki bphy_err(drvr, "Unsupported type %d\n", type);
265905491d2cSKalle Valo err = -EINVAL;
266005491d2cSKalle Valo goto done;
266105491d2cSKalle Valo }
266205491d2cSKalle Valo /* Make sure radio is off or on as far as software is concerned */
266305491d2cSKalle Valo disable = WL_RADIO_SW_DISABLE << 16;
266405491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_RADIO, disable);
266505491d2cSKalle Valo if (err)
266616e64676SRafał Miłecki bphy_err(drvr, "WLC_SET_RADIO error (%d)\n", err);
266705491d2cSKalle Valo
266805491d2cSKalle Valo err = brcmf_fil_iovar_int_set(ifp, "qtxpower", qdbm);
266905491d2cSKalle Valo if (err)
267016e64676SRafał Miłecki bphy_err(drvr, "qtxpower error (%d)\n", err);
267105491d2cSKalle Valo
267205491d2cSKalle Valo done:
267305491d2cSKalle Valo brcmf_dbg(TRACE, "Exit %d (qdbm)\n", qdbm & ~WL_TXPWR_OVERRIDE);
267405491d2cSKalle Valo return err;
267505491d2cSKalle Valo }
267605491d2cSKalle Valo
267705491d2cSKalle Valo static s32
brcmf_cfg80211_get_tx_power(struct wiphy * wiphy,struct wireless_dev * wdev,s32 * dbm)267805491d2cSKalle Valo brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, struct wireless_dev *wdev,
267905491d2cSKalle Valo s32 *dbm)
268005491d2cSKalle Valo {
268116e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
2682856d5a01SArend Van Spriel struct brcmf_cfg80211_vif *vif = wdev_to_vif(wdev);
268316e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
2684*fb1862ceSArend van Spriel s32 qdbm;
268505491d2cSKalle Valo s32 err;
268605491d2cSKalle Valo
268705491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
2688856d5a01SArend Van Spriel if (!check_vif_up(vif))
268905491d2cSKalle Valo return -EIO;
269005491d2cSKalle Valo
2691856d5a01SArend Van Spriel err = brcmf_fil_iovar_int_get(vif->ifp, "qtxpower", &qdbm);
269205491d2cSKalle Valo if (err) {
269316e64676SRafał Miłecki bphy_err(drvr, "error (%d)\n", err);
269405491d2cSKalle Valo goto done;
269505491d2cSKalle Valo }
269605491d2cSKalle Valo *dbm = (qdbm & ~WL_TXPWR_OVERRIDE) / 4;
269705491d2cSKalle Valo
269805491d2cSKalle Valo done:
269905491d2cSKalle Valo brcmf_dbg(TRACE, "Exit (0x%x %d)\n", qdbm, *dbm);
270005491d2cSKalle Valo return err;
270105491d2cSKalle Valo }
270205491d2cSKalle Valo
270305491d2cSKalle Valo static s32
brcmf_cfg80211_config_default_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool unicast,bool multicast)270405491d2cSKalle Valo brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *ndev,
2705e7a7b84eSVeerendranath Jakkam int link_id, u8 key_idx, bool unicast,
2706e7a7b84eSVeerendranath Jakkam bool multicast)
270705491d2cSKalle Valo {
270805491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
270916e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
271005491d2cSKalle Valo u32 index;
271105491d2cSKalle Valo u32 wsec;
271205491d2cSKalle Valo s32 err = 0;
271305491d2cSKalle Valo
271405491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
271505491d2cSKalle Valo brcmf_dbg(CONN, "key index (%d)\n", key_idx);
271605491d2cSKalle Valo if (!check_vif_up(ifp->vif))
271705491d2cSKalle Valo return -EIO;
271805491d2cSKalle Valo
271905491d2cSKalle Valo err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
272005491d2cSKalle Valo if (err) {
272116e64676SRafał Miłecki bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);
272205491d2cSKalle Valo goto done;
272305491d2cSKalle Valo }
272405491d2cSKalle Valo
272505491d2cSKalle Valo if (wsec & WEP_ENABLED) {
272605491d2cSKalle Valo /* Just select a new current key */
272705491d2cSKalle Valo index = key_idx;
272805491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp,
272905491d2cSKalle Valo BRCMF_C_SET_KEY_PRIMARY, index);
273005491d2cSKalle Valo if (err)
273116e64676SRafał Miłecki bphy_err(drvr, "error (%d)\n", err);
273205491d2cSKalle Valo }
273305491d2cSKalle Valo done:
273405491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
273505491d2cSKalle Valo return err;
273605491d2cSKalle Valo }
273705491d2cSKalle Valo
273805491d2cSKalle Valo static s32
brcmf_cfg80211_del_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr)2739219e0f74SHante Meuleman brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *ndev,
2740e7a7b84eSVeerendranath Jakkam int link_id, u8 key_idx, bool pairwise,
2741e7a7b84eSVeerendranath Jakkam const u8 *mac_addr)
274205491d2cSKalle Valo {
274305491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
2744240d61a9SHante Meuleman struct brcmf_wsec_key *key;
2745240d61a9SHante Meuleman s32 err;
2746219e0f74SHante Meuleman
2747219e0f74SHante Meuleman brcmf_dbg(TRACE, "Enter\n");
2748240d61a9SHante Meuleman brcmf_dbg(CONN, "key index (%d)\n", key_idx);
2749240d61a9SHante Meuleman
2750219e0f74SHante Meuleman if (!check_vif_up(ifp->vif))
2751219e0f74SHante Meuleman return -EIO;
2752219e0f74SHante Meuleman
2753219e0f74SHante Meuleman if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
2754219e0f74SHante Meuleman /* we ignore this key index in this case */
2755219e0f74SHante Meuleman return -EINVAL;
2756219e0f74SHante Meuleman }
275705491d2cSKalle Valo
2758240d61a9SHante Meuleman key = &ifp->vif->profile.key[key_idx];
2759219e0f74SHante Meuleman
2760240d61a9SHante Meuleman if (key->algo == CRYPTO_ALGO_OFF) {
2761240d61a9SHante Meuleman brcmf_dbg(CONN, "Ignore clearing of (never configured) key\n");
2762240d61a9SHante Meuleman return -EINVAL;
2763240d61a9SHante Meuleman }
2764219e0f74SHante Meuleman
2765240d61a9SHante Meuleman memset(key, 0, sizeof(*key));
2766240d61a9SHante Meuleman key->index = (u32)key_idx;
2767240d61a9SHante Meuleman key->flags = BRCMF_PRIMARY_KEY;
2768219e0f74SHante Meuleman
2769240d61a9SHante Meuleman /* Clear the key/index */
2770240d61a9SHante Meuleman err = send_key_to_dongle(ifp, key);
277105491d2cSKalle Valo
2772219e0f74SHante Meuleman brcmf_dbg(TRACE, "Exit\n");
277305491d2cSKalle Valo return err;
277405491d2cSKalle Valo }
277505491d2cSKalle Valo
277605491d2cSKalle Valo static s32
brcmf_cfg80211_add_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr,struct key_params * params)277705491d2cSKalle Valo brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *ndev,
2778e7a7b84eSVeerendranath Jakkam int link_id, u8 key_idx, bool pairwise,
2779e7a7b84eSVeerendranath Jakkam const u8 *mac_addr, struct key_params *params)
278005491d2cSKalle Valo {
278116e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
278205491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
278316e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
278405491d2cSKalle Valo struct brcmf_wsec_key *key;
278505491d2cSKalle Valo s32 val;
278605491d2cSKalle Valo s32 wsec;
2787219e0f74SHante Meuleman s32 err;
278805491d2cSKalle Valo u8 keybuf[8];
2789219e0f74SHante Meuleman bool ext_key;
279005491d2cSKalle Valo
279105491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
279205491d2cSKalle Valo brcmf_dbg(CONN, "key index (%d)\n", key_idx);
279305491d2cSKalle Valo if (!check_vif_up(ifp->vif))
279405491d2cSKalle Valo return -EIO;
279505491d2cSKalle Valo
279605491d2cSKalle Valo if (key_idx >= BRCMF_MAX_DEFAULT_KEYS) {
279705491d2cSKalle Valo /* we ignore this key index in this case */
279816e64676SRafał Miłecki bphy_err(drvr, "invalid key index (%d)\n", key_idx);
279905491d2cSKalle Valo return -EINVAL;
280005491d2cSKalle Valo }
280105491d2cSKalle Valo
2802219e0f74SHante Meuleman if (params->key_len == 0)
2803e7a7b84eSVeerendranath Jakkam return brcmf_cfg80211_del_key(wiphy, ndev, -1, key_idx,
2804e7a7b84eSVeerendranath Jakkam pairwise, mac_addr);
2805219e0f74SHante Meuleman
2806219e0f74SHante Meuleman if (params->key_len > sizeof(key->data)) {
280716e64676SRafał Miłecki bphy_err(drvr, "Too long key length (%u)\n", params->key_len);
2808219e0f74SHante Meuleman return -EINVAL;
2809219e0f74SHante Meuleman }
2810219e0f74SHante Meuleman
2811219e0f74SHante Meuleman ext_key = false;
2812219e0f74SHante Meuleman if (mac_addr && (params->cipher != WLAN_CIPHER_SUITE_WEP40) &&
281305491d2cSKalle Valo (params->cipher != WLAN_CIPHER_SUITE_WEP104)) {
2814219e0f74SHante Meuleman brcmf_dbg(TRACE, "Ext key, mac %pM", mac_addr);
2815219e0f74SHante Meuleman ext_key = true;
281605491d2cSKalle Valo }
281705491d2cSKalle Valo
281805491d2cSKalle Valo key = &ifp->vif->profile.key[key_idx];
281905491d2cSKalle Valo memset(key, 0, sizeof(*key));
2820219e0f74SHante Meuleman if ((ext_key) && (!is_multicast_ether_addr(mac_addr)))
2821219e0f74SHante Meuleman memcpy((char *)&key->ea, (void *)mac_addr, ETH_ALEN);
282205491d2cSKalle Valo key->len = params->key_len;
282305491d2cSKalle Valo key->index = key_idx;
282405491d2cSKalle Valo memcpy(key->data, params->key, key->len);
2825219e0f74SHante Meuleman if (!ext_key)
282605491d2cSKalle Valo key->flags = BRCMF_PRIMARY_KEY;
2827219e0f74SHante Meuleman
282878db077dSSoontak Lee if (params->seq && params->seq_len == 6) {
282978db077dSSoontak Lee /* rx iv */
283078db077dSSoontak Lee u8 *ivptr;
283178db077dSSoontak Lee
283278db077dSSoontak Lee ivptr = (u8 *)params->seq;
283378db077dSSoontak Lee key->rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
283478db077dSSoontak Lee (ivptr[3] << 8) | ivptr[2];
283578db077dSSoontak Lee key->rxiv.lo = (ivptr[1] << 8) | ivptr[0];
283678db077dSSoontak Lee key->iv_initialized = true;
283778db077dSSoontak Lee }
283878db077dSSoontak Lee
283905491d2cSKalle Valo switch (params->cipher) {
284005491d2cSKalle Valo case WLAN_CIPHER_SUITE_WEP40:
284105491d2cSKalle Valo key->algo = CRYPTO_ALGO_WEP1;
284205491d2cSKalle Valo val = WEP_ENABLED;
284305491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
284405491d2cSKalle Valo break;
284505491d2cSKalle Valo case WLAN_CIPHER_SUITE_WEP104:
284605491d2cSKalle Valo key->algo = CRYPTO_ALGO_WEP128;
284705491d2cSKalle Valo val = WEP_ENABLED;
284805491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
284905491d2cSKalle Valo break;
285005491d2cSKalle Valo case WLAN_CIPHER_SUITE_TKIP:
285105491d2cSKalle Valo if (!brcmf_is_apmode(ifp->vif)) {
285205491d2cSKalle Valo brcmf_dbg(CONN, "Swapping RX/TX MIC key\n");
285305491d2cSKalle Valo memcpy(keybuf, &key->data[24], sizeof(keybuf));
285405491d2cSKalle Valo memcpy(&key->data[24], &key->data[16], sizeof(keybuf));
285505491d2cSKalle Valo memcpy(&key->data[16], keybuf, sizeof(keybuf));
285605491d2cSKalle Valo }
285705491d2cSKalle Valo key->algo = CRYPTO_ALGO_TKIP;
285805491d2cSKalle Valo val = TKIP_ENABLED;
285905491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
286005491d2cSKalle Valo break;
286105491d2cSKalle Valo case WLAN_CIPHER_SUITE_AES_CMAC:
286205491d2cSKalle Valo key->algo = CRYPTO_ALGO_AES_CCM;
286305491d2cSKalle Valo val = AES_ENABLED;
286405491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
286505491d2cSKalle Valo break;
286605491d2cSKalle Valo case WLAN_CIPHER_SUITE_CCMP:
286705491d2cSKalle Valo key->algo = CRYPTO_ALGO_AES_CCM;
286805491d2cSKalle Valo val = AES_ENABLED;
286905491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_CCMP\n");
287005491d2cSKalle Valo break;
287105491d2cSKalle Valo default:
287216e64676SRafał Miłecki bphy_err(drvr, "Invalid cipher (0x%x)\n", params->cipher);
287305491d2cSKalle Valo err = -EINVAL;
287405491d2cSKalle Valo goto done;
287505491d2cSKalle Valo }
287605491d2cSKalle Valo
287705491d2cSKalle Valo err = send_key_to_dongle(ifp, key);
2878219e0f74SHante Meuleman if (ext_key || err)
287905491d2cSKalle Valo goto done;
288005491d2cSKalle Valo
288105491d2cSKalle Valo err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
288205491d2cSKalle Valo if (err) {
288316e64676SRafał Miłecki bphy_err(drvr, "get wsec error (%d)\n", err);
288405491d2cSKalle Valo goto done;
288505491d2cSKalle Valo }
288605491d2cSKalle Valo wsec |= val;
288705491d2cSKalle Valo err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
288805491d2cSKalle Valo if (err) {
288916e64676SRafał Miłecki bphy_err(drvr, "set wsec error (%d)\n", err);
289005491d2cSKalle Valo goto done;
289105491d2cSKalle Valo }
289205491d2cSKalle Valo
289305491d2cSKalle Valo done:
289405491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
289505491d2cSKalle Valo return err;
289605491d2cSKalle Valo }
289705491d2cSKalle Valo
289805491d2cSKalle Valo static s32
brcmf_cfg80211_get_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx,bool pairwise,const u8 * mac_addr,void * cookie,void (* callback)(void * cookie,struct key_params * params))2899e7a7b84eSVeerendranath Jakkam brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *ndev,
2900e7a7b84eSVeerendranath Jakkam int link_id, u8 key_idx, bool pairwise,
2901e7a7b84eSVeerendranath Jakkam const u8 *mac_addr, void *cookie,
2902240d61a9SHante Meuleman void (*callback)(void *cookie,
2903240d61a9SHante Meuleman struct key_params *params))
290405491d2cSKalle Valo {
290516e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
290605491d2cSKalle Valo struct key_params params;
290705491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
290805491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
290916e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
291005491d2cSKalle Valo struct brcmf_cfg80211_security *sec;
291105491d2cSKalle Valo s32 wsec;
291205491d2cSKalle Valo s32 err = 0;
291305491d2cSKalle Valo
291405491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
291505491d2cSKalle Valo brcmf_dbg(CONN, "key index (%d)\n", key_idx);
291605491d2cSKalle Valo if (!check_vif_up(ifp->vif))
291705491d2cSKalle Valo return -EIO;
291805491d2cSKalle Valo
291905491d2cSKalle Valo memset(¶ms, 0, sizeof(params));
292005491d2cSKalle Valo
292105491d2cSKalle Valo err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
292205491d2cSKalle Valo if (err) {
292316e64676SRafał Miłecki bphy_err(drvr, "WLC_GET_WSEC error (%d)\n", err);
292405491d2cSKalle Valo /* Ignore this error, may happen during DISASSOC */
292505491d2cSKalle Valo err = -EAGAIN;
292605491d2cSKalle Valo goto done;
292705491d2cSKalle Valo }
292805491d2cSKalle Valo if (wsec & WEP_ENABLED) {
292905491d2cSKalle Valo sec = &profile->sec;
293005491d2cSKalle Valo if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
293105491d2cSKalle Valo params.cipher = WLAN_CIPHER_SUITE_WEP40;
293205491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP40\n");
293305491d2cSKalle Valo } else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
293405491d2cSKalle Valo params.cipher = WLAN_CIPHER_SUITE_WEP104;
293505491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_WEP104\n");
293605491d2cSKalle Valo }
293705491d2cSKalle Valo } else if (wsec & TKIP_ENABLED) {
293805491d2cSKalle Valo params.cipher = WLAN_CIPHER_SUITE_TKIP;
293905491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_TKIP\n");
294005491d2cSKalle Valo } else if (wsec & AES_ENABLED) {
294105491d2cSKalle Valo params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
294205491d2cSKalle Valo brcmf_dbg(CONN, "WLAN_CIPHER_SUITE_AES_CMAC\n");
294305491d2cSKalle Valo } else {
294416e64676SRafał Miłecki bphy_err(drvr, "Invalid algo (0x%x)\n", wsec);
294505491d2cSKalle Valo err = -EINVAL;
294605491d2cSKalle Valo goto done;
294705491d2cSKalle Valo }
294805491d2cSKalle Valo callback(cookie, ¶ms);
294905491d2cSKalle Valo
295005491d2cSKalle Valo done:
295105491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
295205491d2cSKalle Valo return err;
295305491d2cSKalle Valo }
295405491d2cSKalle Valo
295505491d2cSKalle Valo static s32
brcmf_cfg80211_config_default_mgmt_key(struct wiphy * wiphy,struct net_device * ndev,int link_id,u8 key_idx)295605491d2cSKalle Valo brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
2957e7a7b84eSVeerendranath Jakkam struct net_device *ndev, int link_id,
2958e7a7b84eSVeerendranath Jakkam u8 key_idx)
295905491d2cSKalle Valo {
2960240d61a9SHante Meuleman struct brcmf_if *ifp = netdev_priv(ndev);
2961240d61a9SHante Meuleman
2962240d61a9SHante Meuleman brcmf_dbg(TRACE, "Enter key_idx %d\n", key_idx);
2963240d61a9SHante Meuleman
2964240d61a9SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
2965240d61a9SHante Meuleman return 0;
2966240d61a9SHante Meuleman
296705491d2cSKalle Valo brcmf_dbg(INFO, "Not supported\n");
296805491d2cSKalle Valo
296905491d2cSKalle Valo return -EOPNOTSUPP;
297005491d2cSKalle Valo }
297105491d2cSKalle Valo
297205491d2cSKalle Valo static void
brcmf_cfg80211_reconfigure_wep(struct brcmf_if * ifp)297305491d2cSKalle Valo brcmf_cfg80211_reconfigure_wep(struct brcmf_if *ifp)
297405491d2cSKalle Valo {
297516e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
297605491d2cSKalle Valo s32 err;
297705491d2cSKalle Valo u8 key_idx;
297805491d2cSKalle Valo struct brcmf_wsec_key *key;
297905491d2cSKalle Valo s32 wsec;
298005491d2cSKalle Valo
298105491d2cSKalle Valo for (key_idx = 0; key_idx < BRCMF_MAX_DEFAULT_KEYS; key_idx++) {
298205491d2cSKalle Valo key = &ifp->vif->profile.key[key_idx];
298305491d2cSKalle Valo if ((key->algo == CRYPTO_ALGO_WEP1) ||
298405491d2cSKalle Valo (key->algo == CRYPTO_ALGO_WEP128))
298505491d2cSKalle Valo break;
298605491d2cSKalle Valo }
298705491d2cSKalle Valo if (key_idx == BRCMF_MAX_DEFAULT_KEYS)
298805491d2cSKalle Valo return;
298905491d2cSKalle Valo
299005491d2cSKalle Valo err = send_key_to_dongle(ifp, key);
299105491d2cSKalle Valo if (err) {
299216e64676SRafał Miłecki bphy_err(drvr, "Setting WEP key failed (%d)\n", err);
299305491d2cSKalle Valo return;
299405491d2cSKalle Valo }
299505491d2cSKalle Valo err = brcmf_fil_bsscfg_int_get(ifp, "wsec", &wsec);
299605491d2cSKalle Valo if (err) {
299716e64676SRafał Miłecki bphy_err(drvr, "get wsec error (%d)\n", err);
299805491d2cSKalle Valo return;
299905491d2cSKalle Valo }
300005491d2cSKalle Valo wsec |= WEP_ENABLED;
300105491d2cSKalle Valo err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
300205491d2cSKalle Valo if (err)
300316e64676SRafał Miłecki bphy_err(drvr, "set wsec error (%d)\n", err);
300405491d2cSKalle Valo }
300505491d2cSKalle Valo
brcmf_convert_sta_flags(u32 fw_sta_flags,struct station_info * si)300605491d2cSKalle Valo static void brcmf_convert_sta_flags(u32 fw_sta_flags, struct station_info *si)
300705491d2cSKalle Valo {
300805491d2cSKalle Valo struct nl80211_sta_flag_update *sfu;
300905491d2cSKalle Valo
301005491d2cSKalle Valo brcmf_dbg(TRACE, "flags %08x\n", fw_sta_flags);
301122d0d2faSOmer Efrat si->filled |= BIT_ULL(NL80211_STA_INFO_STA_FLAGS);
301205491d2cSKalle Valo sfu = &si->sta_flags;
301305491d2cSKalle Valo sfu->mask = BIT(NL80211_STA_FLAG_WME) |
301405491d2cSKalle Valo BIT(NL80211_STA_FLAG_AUTHENTICATED) |
301505491d2cSKalle Valo BIT(NL80211_STA_FLAG_ASSOCIATED) |
301605491d2cSKalle Valo BIT(NL80211_STA_FLAG_AUTHORIZED);
301705491d2cSKalle Valo if (fw_sta_flags & BRCMF_STA_WME)
301805491d2cSKalle Valo sfu->set |= BIT(NL80211_STA_FLAG_WME);
301905491d2cSKalle Valo if (fw_sta_flags & BRCMF_STA_AUTHE)
302005491d2cSKalle Valo sfu->set |= BIT(NL80211_STA_FLAG_AUTHENTICATED);
302105491d2cSKalle Valo if (fw_sta_flags & BRCMF_STA_ASSOC)
302205491d2cSKalle Valo sfu->set |= BIT(NL80211_STA_FLAG_ASSOCIATED);
302305491d2cSKalle Valo if (fw_sta_flags & BRCMF_STA_AUTHO)
302405491d2cSKalle Valo sfu->set |= BIT(NL80211_STA_FLAG_AUTHORIZED);
302505491d2cSKalle Valo }
302605491d2cSKalle Valo
brcmf_fill_bss_param(struct brcmf_if * ifp,struct station_info * si)302705491d2cSKalle Valo static void brcmf_fill_bss_param(struct brcmf_if *ifp, struct station_info *si)
302805491d2cSKalle Valo {
302916e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
303005491d2cSKalle Valo struct {
303105491d2cSKalle Valo __le32 len;
303205491d2cSKalle Valo struct brcmf_bss_info_le bss_le;
303305491d2cSKalle Valo } *buf;
303405491d2cSKalle Valo u16 capability;
303505491d2cSKalle Valo int err;
303605491d2cSKalle Valo
303705491d2cSKalle Valo buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
303805491d2cSKalle Valo if (!buf)
303905491d2cSKalle Valo return;
304005491d2cSKalle Valo
304105491d2cSKalle Valo buf->len = cpu_to_le32(WL_BSS_INFO_MAX);
304205491d2cSKalle Valo err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO, buf,
304305491d2cSKalle Valo WL_BSS_INFO_MAX);
304405491d2cSKalle Valo if (err) {
304516e64676SRafał Miłecki bphy_err(drvr, "Failed to get bss info (%d)\n", err);
304623e9c128SRafał Miłecki goto out_kfree;
304705491d2cSKalle Valo }
304822d0d2faSOmer Efrat si->filled |= BIT_ULL(NL80211_STA_INFO_BSS_PARAM);
304905491d2cSKalle Valo si->bss_param.beacon_interval = le16_to_cpu(buf->bss_le.beacon_period);
305005491d2cSKalle Valo si->bss_param.dtim_period = buf->bss_le.dtim_period;
305105491d2cSKalle Valo capability = le16_to_cpu(buf->bss_le.capability);
305205491d2cSKalle Valo if (capability & IEEE80211_HT_STBC_PARAM_DUAL_CTS_PROT)
305305491d2cSKalle Valo si->bss_param.flags |= BSS_PARAM_FLAGS_CTS_PROT;
305405491d2cSKalle Valo if (capability & WLAN_CAPABILITY_SHORT_PREAMBLE)
305505491d2cSKalle Valo si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_PREAMBLE;
305605491d2cSKalle Valo if (capability & WLAN_CAPABILITY_SHORT_SLOT_TIME)
305705491d2cSKalle Valo si->bss_param.flags |= BSS_PARAM_FLAGS_SHORT_SLOT_TIME;
305823e9c128SRafał Miłecki
305923e9c128SRafał Miłecki out_kfree:
306023e9c128SRafał Miłecki kfree(buf);
306105491d2cSKalle Valo }
306205491d2cSKalle Valo
306305491d2cSKalle Valo static s32
brcmf_cfg80211_get_station_ibss(struct brcmf_if * ifp,struct station_info * sinfo)30643f5893d1SHante Meuleman brcmf_cfg80211_get_station_ibss(struct brcmf_if *ifp,
30653f5893d1SHante Meuleman struct station_info *sinfo)
30663f5893d1SHante Meuleman {
306716e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
30683f5893d1SHante Meuleman struct brcmf_scb_val_le scbval;
30693f5893d1SHante Meuleman struct brcmf_pktcnt_le pktcnt;
30703f5893d1SHante Meuleman s32 err;
3071*fb1862ceSArend van Spriel u32 rate;
30723f5893d1SHante Meuleman u32 rssi;
30733f5893d1SHante Meuleman
30743f5893d1SHante Meuleman /* Get the current tx rate */
30753f5893d1SHante Meuleman err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_RATE, &rate);
30763f5893d1SHante Meuleman if (err < 0) {
307716e64676SRafał Miłecki bphy_err(drvr, "BRCMF_C_GET_RATE error (%d)\n", err);
30783f5893d1SHante Meuleman return err;
30793f5893d1SHante Meuleman }
308022d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
30813f5893d1SHante Meuleman sinfo->txrate.legacy = rate * 5;
30823f5893d1SHante Meuleman
30833f5893d1SHante Meuleman memset(&scbval, 0, sizeof(scbval));
30843f5893d1SHante Meuleman err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI, &scbval,
30853f5893d1SHante Meuleman sizeof(scbval));
30863f5893d1SHante Meuleman if (err) {
308716e64676SRafał Miłecki bphy_err(drvr, "BRCMF_C_GET_RSSI error (%d)\n", err);
30883f5893d1SHante Meuleman return err;
30893f5893d1SHante Meuleman }
30903f5893d1SHante Meuleman rssi = le32_to_cpu(scbval.val);
309122d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
30923f5893d1SHante Meuleman sinfo->signal = rssi;
30933f5893d1SHante Meuleman
30943f5893d1SHante Meuleman err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_GET_PKTCNTS, &pktcnt,
30953f5893d1SHante Meuleman sizeof(pktcnt));
30963f5893d1SHante Meuleman if (err) {
309716e64676SRafał Miłecki bphy_err(drvr, "BRCMF_C_GET_GET_PKTCNTS error (%d)\n", err);
30983f5893d1SHante Meuleman return err;
30993f5893d1SHante Meuleman }
310022d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS) |
310122d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_RX_DROP_MISC) |
310222d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_TX_PACKETS) |
310322d0d2faSOmer Efrat BIT_ULL(NL80211_STA_INFO_TX_FAILED);
31043f5893d1SHante Meuleman sinfo->rx_packets = le32_to_cpu(pktcnt.rx_good_pkt);
31053f5893d1SHante Meuleman sinfo->rx_dropped_misc = le32_to_cpu(pktcnt.rx_bad_pkt);
31063f5893d1SHante Meuleman sinfo->tx_packets = le32_to_cpu(pktcnt.tx_good_pkt);
31073f5893d1SHante Meuleman sinfo->tx_failed = le32_to_cpu(pktcnt.tx_bad_pkt);
31083f5893d1SHante Meuleman
31093f5893d1SHante Meuleman return 0;
31103f5893d1SHante Meuleman }
31113f5893d1SHante Meuleman
31123f5893d1SHante Meuleman static s32
brcmf_cfg80211_get_station(struct wiphy * wiphy,struct net_device * ndev,const u8 * mac,struct station_info * sinfo)311305491d2cSKalle Valo brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *ndev,
311405491d2cSKalle Valo const u8 *mac, struct station_info *sinfo)
311505491d2cSKalle Valo {
311616e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
311705491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
311816e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
311994abd778SJaap Jan Meijer struct brcmf_scb_val_le scb_val;
312005491d2cSKalle Valo s32 err = 0;
312105491d2cSKalle Valo struct brcmf_sta_info_le sta_info_le;
312205491d2cSKalle Valo u32 sta_flags;
312305491d2cSKalle Valo u32 is_tdls_peer;
31249a159093SAlvin Šipraga s32 total_rssi_avg = 0;
31259a159093SAlvin Šipraga s32 total_rssi = 0;
31269a159093SAlvin Šipraga s32 count_rssi = 0;
312794abd778SJaap Jan Meijer int rssi;
312805491d2cSKalle Valo u32 i;
312905491d2cSKalle Valo
313005491d2cSKalle Valo brcmf_dbg(TRACE, "Enter, MAC %pM\n", mac);
313105491d2cSKalle Valo if (!check_vif_up(ifp->vif))
313205491d2cSKalle Valo return -EIO;
313305491d2cSKalle Valo
31343f5893d1SHante Meuleman if (brcmf_is_ibssmode(ifp->vif))
31353f5893d1SHante Meuleman return brcmf_cfg80211_get_station_ibss(ifp, sinfo);
31363f5893d1SHante Meuleman
313705491d2cSKalle Valo memset(&sta_info_le, 0, sizeof(sta_info_le));
313805491d2cSKalle Valo memcpy(&sta_info_le, mac, ETH_ALEN);
313905491d2cSKalle Valo err = brcmf_fil_iovar_data_get(ifp, "tdls_sta_info",
314005491d2cSKalle Valo &sta_info_le,
314105491d2cSKalle Valo sizeof(sta_info_le));
314205491d2cSKalle Valo is_tdls_peer = !err;
314305491d2cSKalle Valo if (err) {
314405491d2cSKalle Valo err = brcmf_fil_iovar_data_get(ifp, "sta_info",
314505491d2cSKalle Valo &sta_info_le,
314605491d2cSKalle Valo sizeof(sta_info_le));
314705491d2cSKalle Valo if (err < 0) {
314816e64676SRafał Miłecki bphy_err(drvr, "GET STA INFO failed, %d\n", err);
314905491d2cSKalle Valo goto done;
315005491d2cSKalle Valo }
315105491d2cSKalle Valo }
315205491d2cSKalle Valo brcmf_dbg(TRACE, "version %d\n", le16_to_cpu(sta_info_le.ver));
315322d0d2faSOmer Efrat sinfo->filled = BIT_ULL(NL80211_STA_INFO_INACTIVE_TIME);
315405491d2cSKalle Valo sinfo->inactive_time = le32_to_cpu(sta_info_le.idle) * 1000;
315505491d2cSKalle Valo sta_flags = le32_to_cpu(sta_info_le.flags);
315605491d2cSKalle Valo brcmf_convert_sta_flags(sta_flags, sinfo);
315705491d2cSKalle Valo sinfo->sta_flags.mask |= BIT(NL80211_STA_FLAG_TDLS_PEER);
315805491d2cSKalle Valo if (is_tdls_peer)
315905491d2cSKalle Valo sinfo->sta_flags.set |= BIT(NL80211_STA_FLAG_TDLS_PEER);
316005491d2cSKalle Valo else
316105491d2cSKalle Valo sinfo->sta_flags.set &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
316205491d2cSKalle Valo if (sta_flags & BRCMF_STA_ASSOC) {
316322d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CONNECTED_TIME);
316405491d2cSKalle Valo sinfo->connected_time = le32_to_cpu(sta_info_le.in);
316505491d2cSKalle Valo brcmf_fill_bss_param(ifp, sinfo);
316605491d2cSKalle Valo }
316705491d2cSKalle Valo if (sta_flags & BRCMF_STA_SCBSTATS) {
316822d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_FAILED);
316905491d2cSKalle Valo sinfo->tx_failed = le32_to_cpu(sta_info_le.tx_failures);
317022d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_PACKETS);
317105491d2cSKalle Valo sinfo->tx_packets = le32_to_cpu(sta_info_le.tx_pkts);
317205491d2cSKalle Valo sinfo->tx_packets += le32_to_cpu(sta_info_le.tx_mcast_pkts);
317322d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_PACKETS);
317405491d2cSKalle Valo sinfo->rx_packets = le32_to_cpu(sta_info_le.rx_ucast_pkts);
317505491d2cSKalle Valo sinfo->rx_packets += le32_to_cpu(sta_info_le.rx_mcast_pkts);
317605491d2cSKalle Valo if (sinfo->tx_packets) {
317722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BITRATE);
317805491d2cSKalle Valo sinfo->txrate.legacy =
317905491d2cSKalle Valo le32_to_cpu(sta_info_le.tx_rate) / 100;
318005491d2cSKalle Valo }
318105491d2cSKalle Valo if (sinfo->rx_packets) {
318222d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BITRATE);
318305491d2cSKalle Valo sinfo->rxrate.legacy =
318405491d2cSKalle Valo le32_to_cpu(sta_info_le.rx_rate) / 100;
318505491d2cSKalle Valo }
318605491d2cSKalle Valo if (le16_to_cpu(sta_info_le.ver) >= 4) {
318722d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_TX_BYTES);
318805491d2cSKalle Valo sinfo->tx_bytes = le64_to_cpu(sta_info_le.tx_tot_bytes);
318922d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_RX_BYTES);
319005491d2cSKalle Valo sinfo->rx_bytes = le64_to_cpu(sta_info_le.rx_tot_bytes);
319105491d2cSKalle Valo }
319205491d2cSKalle Valo for (i = 0; i < BRCMF_ANT_MAX; i++) {
31939a159093SAlvin Šipraga if (sta_info_le.rssi[i] == 0 ||
31949a159093SAlvin Šipraga sta_info_le.rx_lastpkt_rssi[i] == 0)
31959a159093SAlvin Šipraga continue;
3196feb45643SAlvin Šipraga sinfo->chains |= BIT(count_rssi);
31979a159093SAlvin Šipraga sinfo->chain_signal[count_rssi] =
31989a159093SAlvin Šipraga sta_info_le.rx_lastpkt_rssi[i];
319905491d2cSKalle Valo sinfo->chain_signal_avg[count_rssi] =
320005491d2cSKalle Valo sta_info_le.rssi[i];
32019a159093SAlvin Šipraga total_rssi += sta_info_le.rx_lastpkt_rssi[i];
32029a159093SAlvin Šipraga total_rssi_avg += sta_info_le.rssi[i];
320305491d2cSKalle Valo count_rssi++;
320405491d2cSKalle Valo }
320505491d2cSKalle Valo if (count_rssi) {
320622d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
32079a159093SAlvin Šipraga sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL_AVG);
32089a159093SAlvin Šipraga sinfo->filled |= BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL);
32099a159093SAlvin Šipraga sinfo->filled |=
32109a159093SAlvin Šipraga BIT_ULL(NL80211_STA_INFO_CHAIN_SIGNAL_AVG);
32119a159093SAlvin Šipraga sinfo->signal = total_rssi / count_rssi;
32129a159093SAlvin Šipraga sinfo->signal_avg = total_rssi_avg / count_rssi;
321394abd778SJaap Jan Meijer } else if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
321494abd778SJaap Jan Meijer &ifp->vif->sme_state)) {
321594abd778SJaap Jan Meijer memset(&scb_val, 0, sizeof(scb_val));
321694abd778SJaap Jan Meijer err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_RSSI,
321794abd778SJaap Jan Meijer &scb_val, sizeof(scb_val));
321894abd778SJaap Jan Meijer if (err) {
321916e64676SRafał Miłecki bphy_err(drvr, "Could not get rssi (%d)\n",
32203ef005b8SRafał Miłecki err);
322194abd778SJaap Jan Meijer goto done;
322294abd778SJaap Jan Meijer } else {
322394abd778SJaap Jan Meijer rssi = le32_to_cpu(scb_val.val);
322422d0d2faSOmer Efrat sinfo->filled |= BIT_ULL(NL80211_STA_INFO_SIGNAL);
322594abd778SJaap Jan Meijer sinfo->signal = rssi;
322694abd778SJaap Jan Meijer brcmf_dbg(CONN, "RSSI %d dBm\n", rssi);
322794abd778SJaap Jan Meijer }
322805491d2cSKalle Valo }
322905491d2cSKalle Valo }
323005491d2cSKalle Valo done:
323105491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
323205491d2cSKalle Valo return err;
323305491d2cSKalle Valo }
323405491d2cSKalle Valo
323505491d2cSKalle Valo static int
brcmf_cfg80211_dump_station(struct wiphy * wiphy,struct net_device * ndev,int idx,u8 * mac,struct station_info * sinfo)323605491d2cSKalle Valo brcmf_cfg80211_dump_station(struct wiphy *wiphy, struct net_device *ndev,
323705491d2cSKalle Valo int idx, u8 *mac, struct station_info *sinfo)
323805491d2cSKalle Valo {
323905491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
324005491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
324116e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
324205491d2cSKalle Valo s32 err;
324305491d2cSKalle Valo
324405491d2cSKalle Valo brcmf_dbg(TRACE, "Enter, idx %d\n", idx);
324505491d2cSKalle Valo
324605491d2cSKalle Valo if (idx == 0) {
324705491d2cSKalle Valo cfg->assoclist.count = cpu_to_le32(BRCMF_MAX_ASSOCLIST);
324805491d2cSKalle Valo err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_ASSOCLIST,
324905491d2cSKalle Valo &cfg->assoclist,
325005491d2cSKalle Valo sizeof(cfg->assoclist));
325105491d2cSKalle Valo if (err) {
325278f0a64fSDmitry Osipenko /* GET_ASSOCLIST unsupported by firmware of older chips */
325378f0a64fSDmitry Osipenko if (err == -EBADE)
325478f0a64fSDmitry Osipenko bphy_info_once(drvr, "BRCMF_C_GET_ASSOCLIST unsupported\n");
325578f0a64fSDmitry Osipenko else
325678f0a64fSDmitry Osipenko bphy_err(drvr, "BRCMF_C_GET_ASSOCLIST failed, err=%d\n",
325705491d2cSKalle Valo err);
325878f0a64fSDmitry Osipenko
325905491d2cSKalle Valo cfg->assoclist.count = 0;
326005491d2cSKalle Valo return -EOPNOTSUPP;
326105491d2cSKalle Valo }
326205491d2cSKalle Valo }
326305491d2cSKalle Valo if (idx < le32_to_cpu(cfg->assoclist.count)) {
326405491d2cSKalle Valo memcpy(mac, cfg->assoclist.mac[idx], ETH_ALEN);
326505491d2cSKalle Valo return brcmf_cfg80211_get_station(wiphy, ndev, mac, sinfo);
326605491d2cSKalle Valo }
326705491d2cSKalle Valo return -ENOENT;
326805491d2cSKalle Valo }
326905491d2cSKalle Valo
327005491d2cSKalle Valo static s32
brcmf_cfg80211_set_power_mgmt(struct wiphy * wiphy,struct net_device * ndev,bool enabled,s32 timeout)327105491d2cSKalle Valo brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *ndev,
327205491d2cSKalle Valo bool enabled, s32 timeout)
327305491d2cSKalle Valo {
327405491d2cSKalle Valo s32 pm;
327505491d2cSKalle Valo s32 err = 0;
327605491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
327705491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
327816e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
327905491d2cSKalle Valo
328005491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
328105491d2cSKalle Valo
328205491d2cSKalle Valo /*
328305491d2cSKalle Valo * Powersave enable/disable request is coming from the
328405491d2cSKalle Valo * cfg80211 even before the interface is up. In that
328505491d2cSKalle Valo * scenario, driver will be storing the power save
328605491d2cSKalle Valo * preference in cfg struct to apply this to
328705491d2cSKalle Valo * FW later while initializing the dongle
328805491d2cSKalle Valo */
328905491d2cSKalle Valo cfg->pwr_save = enabled;
329005491d2cSKalle Valo if (!check_vif_up(ifp->vif)) {
329105491d2cSKalle Valo
329205491d2cSKalle Valo brcmf_dbg(INFO, "Device is not ready, storing the value in cfg_info struct\n");
329305491d2cSKalle Valo goto done;
329405491d2cSKalle Valo }
329505491d2cSKalle Valo
329605491d2cSKalle Valo pm = enabled ? PM_FAST : PM_OFF;
329705491d2cSKalle Valo /* Do not enable the power save after assoc if it is a p2p interface */
329805491d2cSKalle Valo if (ifp->vif->wdev.iftype == NL80211_IFTYPE_P2P_CLIENT) {
329905491d2cSKalle Valo brcmf_dbg(INFO, "Do not enable power save for P2P clients\n");
330005491d2cSKalle Valo pm = PM_OFF;
330105491d2cSKalle Valo }
330205491d2cSKalle Valo brcmf_dbg(INFO, "power save %s\n", (pm ? "enabled" : "disabled"));
330305491d2cSKalle Valo
330405491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, pm);
330505491d2cSKalle Valo if (err) {
330605491d2cSKalle Valo if (err == -ENODEV)
330716e64676SRafał Miłecki bphy_err(drvr, "net_device is not ready yet\n");
330805491d2cSKalle Valo else
330916e64676SRafał Miłecki bphy_err(drvr, "error (%d)\n", err);
331005491d2cSKalle Valo }
33113dc05ffbSNicolas Saenz Julienne
33123dc05ffbSNicolas Saenz Julienne err = brcmf_fil_iovar_int_set(ifp, "pm2_sleep_ret",
33133dc05ffbSNicolas Saenz Julienne min_t(u32, timeout, BRCMF_PS_MAX_TIMEOUT_MS));
33143dc05ffbSNicolas Saenz Julienne if (err)
33153dc05ffbSNicolas Saenz Julienne bphy_err(drvr, "Unable to set pm timeout, (%d)\n", err);
33163dc05ffbSNicolas Saenz Julienne
331705491d2cSKalle Valo done:
331805491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
331905491d2cSKalle Valo return err;
332005491d2cSKalle Valo }
332105491d2cSKalle Valo
brcmf_inform_single_bss(struct brcmf_cfg80211_info * cfg,struct brcmf_bss_info_le * bi)332205491d2cSKalle Valo static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_info *cfg,
332305491d2cSKalle Valo struct brcmf_bss_info_le *bi)
332405491d2cSKalle Valo {
332505491d2cSKalle Valo struct wiphy *wiphy = cfg_to_wiphy(cfg);
332616e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
332705491d2cSKalle Valo struct cfg80211_bss *bss;
3328aed14219SFranky Lin enum nl80211_band band;
332905491d2cSKalle Valo struct brcmu_chan ch;
333005491d2cSKalle Valo u16 channel;
333105491d2cSKalle Valo u32 freq;
333205491d2cSKalle Valo u16 notify_capability;
333305491d2cSKalle Valo u16 notify_interval;
333405491d2cSKalle Valo u8 *notify_ie;
333505491d2cSKalle Valo size_t notify_ielen;
3336763ece85SFranky Lin struct cfg80211_inform_bss bss_data = {};
333705491d2cSKalle Valo
333805491d2cSKalle Valo if (le32_to_cpu(bi->length) > WL_BSS_INFO_MAX) {
333916e64676SRafał Miłecki bphy_err(drvr, "Bss info is larger than buffer. Discarding\n");
3340bd994491SRaveendran Somu return -EINVAL;
334105491d2cSKalle Valo }
334205491d2cSKalle Valo
334305491d2cSKalle Valo if (!bi->ctl_ch) {
334405491d2cSKalle Valo ch.chspec = le16_to_cpu(bi->chanspec);
334505491d2cSKalle Valo cfg->d11inf.decchspec(&ch);
33464712d88aSRafał Miłecki bi->ctl_ch = ch.control_ch_num;
334705491d2cSKalle Valo }
334805491d2cSKalle Valo channel = bi->ctl_ch;
334905491d2cSKalle Valo
335005491d2cSKalle Valo if (channel <= CH_MAX_2G_CHANNEL)
3351aed14219SFranky Lin band = NL80211_BAND_2GHZ;
335205491d2cSKalle Valo else
3353aed14219SFranky Lin band = NL80211_BAND_5GHZ;
335405491d2cSKalle Valo
3355aed14219SFranky Lin freq = ieee80211_channel_to_frequency(channel, band);
33567742fce4SFranky Lin bss_data.chan = ieee80211_get_channel(wiphy, freq);
33577742fce4SFranky Lin bss_data.scan_width = NL80211_BSS_CHAN_WIDTH_20;
33587742fce4SFranky Lin bss_data.boottime_ns = ktime_to_ns(ktime_get_boottime());
335905491d2cSKalle Valo
336005491d2cSKalle Valo notify_capability = le16_to_cpu(bi->capability);
336105491d2cSKalle Valo notify_interval = le16_to_cpu(bi->beacon_period);
336205491d2cSKalle Valo notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
336305491d2cSKalle Valo notify_ielen = le32_to_cpu(bi->ie_length);
33647742fce4SFranky Lin bss_data.signal = (s16)le16_to_cpu(bi->RSSI) * 100;
336505491d2cSKalle Valo
336605491d2cSKalle Valo brcmf_dbg(CONN, "bssid: %pM\n", bi->BSSID);
336705491d2cSKalle Valo brcmf_dbg(CONN, "Channel: %d(%d)\n", channel, freq);
336805491d2cSKalle Valo brcmf_dbg(CONN, "Capability: %X\n", notify_capability);
336905491d2cSKalle Valo brcmf_dbg(CONN, "Beacon interval: %d\n", notify_interval);
33707742fce4SFranky Lin brcmf_dbg(CONN, "Signal: %d\n", bss_data.signal);
337105491d2cSKalle Valo
33727742fce4SFranky Lin bss = cfg80211_inform_bss_data(wiphy, &bss_data,
337305491d2cSKalle Valo CFG80211_BSS_FTYPE_UNKNOWN,
337405491d2cSKalle Valo (const u8 *)bi->BSSID,
337505491d2cSKalle Valo 0, notify_capability,
337605491d2cSKalle Valo notify_interval, notify_ie,
33777742fce4SFranky Lin notify_ielen, GFP_KERNEL);
337805491d2cSKalle Valo
337905491d2cSKalle Valo if (!bss)
338005491d2cSKalle Valo return -ENOMEM;
338105491d2cSKalle Valo
338205491d2cSKalle Valo cfg80211_put_bss(wiphy, bss);
338305491d2cSKalle Valo
338405491d2cSKalle Valo return 0;
338505491d2cSKalle Valo }
338605491d2cSKalle Valo
338705491d2cSKalle Valo static struct brcmf_bss_info_le *
next_bss_le(struct brcmf_scan_results * list,struct brcmf_bss_info_le * bss)338805491d2cSKalle Valo next_bss_le(struct brcmf_scan_results *list, struct brcmf_bss_info_le *bss)
338905491d2cSKalle Valo {
339005491d2cSKalle Valo if (bss == NULL)
339105491d2cSKalle Valo return list->bss_info_le;
339205491d2cSKalle Valo return (struct brcmf_bss_info_le *)((unsigned long)bss +
339305491d2cSKalle Valo le32_to_cpu(bss->length));
339405491d2cSKalle Valo }
339505491d2cSKalle Valo
brcmf_inform_bss(struct brcmf_cfg80211_info * cfg)339605491d2cSKalle Valo static s32 brcmf_inform_bss(struct brcmf_cfg80211_info *cfg)
339705491d2cSKalle Valo {
339816e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
339905491d2cSKalle Valo struct brcmf_scan_results *bss_list;
340005491d2cSKalle Valo struct brcmf_bss_info_le *bi = NULL; /* must be initialized */
340105491d2cSKalle Valo s32 err = 0;
340205491d2cSKalle Valo int i;
340305491d2cSKalle Valo
340405491d2cSKalle Valo bss_list = (struct brcmf_scan_results *)cfg->escan_info.escan_buf;
340505491d2cSKalle Valo if (bss_list->count != 0 &&
340605491d2cSKalle Valo bss_list->version != BRCMF_BSS_INFO_VERSION) {
340716e64676SRafał Miłecki bphy_err(drvr, "Version %d != WL_BSS_INFO_VERSION\n",
340805491d2cSKalle Valo bss_list->version);
340905491d2cSKalle Valo return -EOPNOTSUPP;
341005491d2cSKalle Valo }
341105491d2cSKalle Valo brcmf_dbg(SCAN, "scanned AP count (%d)\n", bss_list->count);
341205491d2cSKalle Valo for (i = 0; i < bss_list->count; i++) {
341305491d2cSKalle Valo bi = next_bss_le(bss_list, bi);
341405491d2cSKalle Valo err = brcmf_inform_single_bss(cfg, bi);
341505491d2cSKalle Valo if (err)
341605491d2cSKalle Valo break;
341705491d2cSKalle Valo }
341805491d2cSKalle Valo return err;
341905491d2cSKalle Valo }
342005491d2cSKalle Valo
brcmf_inform_ibss(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const u8 * bssid)3421b0a79088SHante Meuleman static s32 brcmf_inform_ibss(struct brcmf_cfg80211_info *cfg,
342205491d2cSKalle Valo struct net_device *ndev, const u8 *bssid)
342305491d2cSKalle Valo {
342405491d2cSKalle Valo struct wiphy *wiphy = cfg_to_wiphy(cfg);
342516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
342605491d2cSKalle Valo struct ieee80211_channel *notify_channel;
342705491d2cSKalle Valo struct brcmf_bss_info_le *bi = NULL;
342805491d2cSKalle Valo struct ieee80211_supported_band *band;
342905491d2cSKalle Valo struct cfg80211_bss *bss;
343005491d2cSKalle Valo struct brcmu_chan ch;
343105491d2cSKalle Valo u8 *buf = NULL;
343205491d2cSKalle Valo s32 err = 0;
343305491d2cSKalle Valo u32 freq;
343405491d2cSKalle Valo u16 notify_capability;
343505491d2cSKalle Valo u16 notify_interval;
343605491d2cSKalle Valo u8 *notify_ie;
343705491d2cSKalle Valo size_t notify_ielen;
343805491d2cSKalle Valo s32 notify_signal;
343905491d2cSKalle Valo
344005491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
344105491d2cSKalle Valo
344205491d2cSKalle Valo buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
344305491d2cSKalle Valo if (buf == NULL) {
344405491d2cSKalle Valo err = -ENOMEM;
344505491d2cSKalle Valo goto CleanUp;
344605491d2cSKalle Valo }
344705491d2cSKalle Valo
344805491d2cSKalle Valo *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
344905491d2cSKalle Valo
345005491d2cSKalle Valo err = brcmf_fil_cmd_data_get(netdev_priv(ndev), BRCMF_C_GET_BSS_INFO,
345105491d2cSKalle Valo buf, WL_BSS_INFO_MAX);
345205491d2cSKalle Valo if (err) {
345316e64676SRafał Miłecki bphy_err(drvr, "WLC_GET_BSS_INFO failed: %d\n", err);
345405491d2cSKalle Valo goto CleanUp;
345505491d2cSKalle Valo }
345605491d2cSKalle Valo
345705491d2cSKalle Valo bi = (struct brcmf_bss_info_le *)(buf + 4);
345805491d2cSKalle Valo
345905491d2cSKalle Valo ch.chspec = le16_to_cpu(bi->chanspec);
346005491d2cSKalle Valo cfg->d11inf.decchspec(&ch);
346105491d2cSKalle Valo
346205491d2cSKalle Valo if (ch.band == BRCMU_CHAN_BAND_2G)
346357fbcce3SJohannes Berg band = wiphy->bands[NL80211_BAND_2GHZ];
346405491d2cSKalle Valo else
346557fbcce3SJohannes Berg band = wiphy->bands[NL80211_BAND_5GHZ];
346605491d2cSKalle Valo
34674712d88aSRafał Miłecki freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
3468b0a79088SHante Meuleman cfg->channel = freq;
346905491d2cSKalle Valo notify_channel = ieee80211_get_channel(wiphy, freq);
347005491d2cSKalle Valo
347105491d2cSKalle Valo notify_capability = le16_to_cpu(bi->capability);
347205491d2cSKalle Valo notify_interval = le16_to_cpu(bi->beacon_period);
347305491d2cSKalle Valo notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
347405491d2cSKalle Valo notify_ielen = le32_to_cpu(bi->ie_length);
347505491d2cSKalle Valo notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
347605491d2cSKalle Valo
34774712d88aSRafał Miłecki brcmf_dbg(CONN, "channel: %d(%d)\n", ch.control_ch_num, freq);
347805491d2cSKalle Valo brcmf_dbg(CONN, "capability: %X\n", notify_capability);
347905491d2cSKalle Valo brcmf_dbg(CONN, "beacon interval: %d\n", notify_interval);
348005491d2cSKalle Valo brcmf_dbg(CONN, "signal: %d\n", notify_signal);
348105491d2cSKalle Valo
348205491d2cSKalle Valo bss = cfg80211_inform_bss(wiphy, notify_channel,
348305491d2cSKalle Valo CFG80211_BSS_FTYPE_UNKNOWN, bssid, 0,
348405491d2cSKalle Valo notify_capability, notify_interval,
348505491d2cSKalle Valo notify_ie, notify_ielen, notify_signal,
348605491d2cSKalle Valo GFP_KERNEL);
348705491d2cSKalle Valo
348805491d2cSKalle Valo if (!bss) {
348905491d2cSKalle Valo err = -ENOMEM;
349005491d2cSKalle Valo goto CleanUp;
349105491d2cSKalle Valo }
349205491d2cSKalle Valo
349305491d2cSKalle Valo cfg80211_put_bss(wiphy, bss);
349405491d2cSKalle Valo
349505491d2cSKalle Valo CleanUp:
349605491d2cSKalle Valo
349705491d2cSKalle Valo kfree(buf);
349805491d2cSKalle Valo
349905491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
350005491d2cSKalle Valo
350105491d2cSKalle Valo return err;
350205491d2cSKalle Valo }
350305491d2cSKalle Valo
brcmf_update_bss_info(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)350405491d2cSKalle Valo static s32 brcmf_update_bss_info(struct brcmf_cfg80211_info *cfg,
350505491d2cSKalle Valo struct brcmf_if *ifp)
350605491d2cSKalle Valo {
350716e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
3508a373f38cSRamesh Rangavittal struct brcmf_bss_info_le *bi = NULL;
350905491d2cSKalle Valo s32 err = 0;
351005491d2cSKalle Valo
351105491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
351205491d2cSKalle Valo if (brcmf_is_ibssmode(ifp->vif))
351305491d2cSKalle Valo return err;
351405491d2cSKalle Valo
351505491d2cSKalle Valo *(__le32 *)cfg->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
351605491d2cSKalle Valo err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
351705491d2cSKalle Valo cfg->extra_buf, WL_EXTRA_BUF_MAX);
351805491d2cSKalle Valo if (err) {
351916e64676SRafał Miłecki bphy_err(drvr, "Could not get bss info %d\n", err);
352005491d2cSKalle Valo goto update_bss_info_out;
352105491d2cSKalle Valo }
352205491d2cSKalle Valo bi = (struct brcmf_bss_info_le *)(cfg->extra_buf + 4);
352305491d2cSKalle Valo err = brcmf_inform_single_bss(cfg, bi);
352405491d2cSKalle Valo
352505491d2cSKalle Valo update_bss_info_out:
352605491d2cSKalle Valo brcmf_dbg(TRACE, "Exit");
352705491d2cSKalle Valo return err;
352805491d2cSKalle Valo }
352905491d2cSKalle Valo
brcmf_abort_scanning(struct brcmf_cfg80211_info * cfg)353005491d2cSKalle Valo void brcmf_abort_scanning(struct brcmf_cfg80211_info *cfg)
353105491d2cSKalle Valo {
353205491d2cSKalle Valo struct escan_info *escan = &cfg->escan_info;
353305491d2cSKalle Valo
353405491d2cSKalle Valo set_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
3535efc2c1faSArend Van Spriel if (cfg->int_escan_map || cfg->scan_request) {
353605491d2cSKalle Valo escan->escan_state = WL_ESCAN_STATE_IDLE;
353705491d2cSKalle Valo brcmf_notify_escan_complete(cfg, escan->ifp, true, true);
353805491d2cSKalle Valo }
353905491d2cSKalle Valo clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
354005491d2cSKalle Valo clear_bit(BRCMF_SCAN_STATUS_ABORT, &cfg->scan_status);
354105491d2cSKalle Valo }
354205491d2cSKalle Valo
brcmf_cfg80211_escan_timeout_worker(struct work_struct * work)354305491d2cSKalle Valo static void brcmf_cfg80211_escan_timeout_worker(struct work_struct *work)
354405491d2cSKalle Valo {
354505491d2cSKalle Valo struct brcmf_cfg80211_info *cfg =
354605491d2cSKalle Valo container_of(work, struct brcmf_cfg80211_info,
354705491d2cSKalle Valo escan_timeout_work);
354805491d2cSKalle Valo
354905491d2cSKalle Valo brcmf_inform_bss(cfg);
355005491d2cSKalle Valo brcmf_notify_escan_complete(cfg, cfg->escan_info.ifp, true, true);
355105491d2cSKalle Valo }
355205491d2cSKalle Valo
brcmf_escan_timeout(struct timer_list * t)3553e99e88a9SKees Cook static void brcmf_escan_timeout(struct timer_list *t)
355405491d2cSKalle Valo {
355505491d2cSKalle Valo struct brcmf_cfg80211_info *cfg =
3556e99e88a9SKees Cook from_timer(cfg, t, escan_timeout);
355716e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
355805491d2cSKalle Valo
3559efc2c1faSArend Van Spriel if (cfg->int_escan_map || cfg->scan_request) {
356016e64676SRafał Miłecki bphy_err(drvr, "timer expired\n");
356105491d2cSKalle Valo schedule_work(&cfg->escan_timeout_work);
356205491d2cSKalle Valo }
356305491d2cSKalle Valo }
356405491d2cSKalle Valo
356505491d2cSKalle Valo static s32
brcmf_compare_update_same_bss(struct brcmf_cfg80211_info * cfg,struct brcmf_bss_info_le * bss,struct brcmf_bss_info_le * bss_info_le)356605491d2cSKalle Valo brcmf_compare_update_same_bss(struct brcmf_cfg80211_info *cfg,
356705491d2cSKalle Valo struct brcmf_bss_info_le *bss,
356805491d2cSKalle Valo struct brcmf_bss_info_le *bss_info_le)
356905491d2cSKalle Valo {
357005491d2cSKalle Valo struct brcmu_chan ch_bss, ch_bss_info_le;
357105491d2cSKalle Valo
357205491d2cSKalle Valo ch_bss.chspec = le16_to_cpu(bss->chanspec);
357305491d2cSKalle Valo cfg->d11inf.decchspec(&ch_bss);
357405491d2cSKalle Valo ch_bss_info_le.chspec = le16_to_cpu(bss_info_le->chanspec);
357505491d2cSKalle Valo cfg->d11inf.decchspec(&ch_bss_info_le);
357605491d2cSKalle Valo
357705491d2cSKalle Valo if (!memcmp(&bss_info_le->BSSID, &bss->BSSID, ETH_ALEN) &&
357805491d2cSKalle Valo ch_bss.band == ch_bss_info_le.band &&
357905491d2cSKalle Valo bss_info_le->SSID_len == bss->SSID_len &&
358005491d2cSKalle Valo !memcmp(bss_info_le->SSID, bss->SSID, bss_info_le->SSID_len)) {
358105491d2cSKalle Valo if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) ==
358205491d2cSKalle Valo (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL)) {
358305491d2cSKalle Valo s16 bss_rssi = le16_to_cpu(bss->RSSI);
358405491d2cSKalle Valo s16 bss_info_rssi = le16_to_cpu(bss_info_le->RSSI);
358505491d2cSKalle Valo
358605491d2cSKalle Valo /* preserve max RSSI if the measurements are
358705491d2cSKalle Valo * both on-channel or both off-channel
358805491d2cSKalle Valo */
358905491d2cSKalle Valo if (bss_info_rssi > bss_rssi)
359005491d2cSKalle Valo bss->RSSI = bss_info_le->RSSI;
359105491d2cSKalle Valo } else if ((bss->flags & BRCMF_BSS_RSSI_ON_CHANNEL) &&
359205491d2cSKalle Valo (bss_info_le->flags & BRCMF_BSS_RSSI_ON_CHANNEL) == 0) {
359305491d2cSKalle Valo /* preserve the on-channel rssi measurement
359405491d2cSKalle Valo * if the new measurement is off channel
359505491d2cSKalle Valo */
359605491d2cSKalle Valo bss->RSSI = bss_info_le->RSSI;
359705491d2cSKalle Valo bss->flags |= BRCMF_BSS_RSSI_ON_CHANNEL;
359805491d2cSKalle Valo }
359905491d2cSKalle Valo return 1;
360005491d2cSKalle Valo }
360105491d2cSKalle Valo return 0;
360205491d2cSKalle Valo }
360305491d2cSKalle Valo
360405491d2cSKalle Valo static s32
brcmf_cfg80211_escan_handler(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)360505491d2cSKalle Valo brcmf_cfg80211_escan_handler(struct brcmf_if *ifp,
360605491d2cSKalle Valo const struct brcmf_event_msg *e, void *data)
360705491d2cSKalle Valo {
360816e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
360916e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = drvr->config;
361005491d2cSKalle Valo s32 status;
361105491d2cSKalle Valo struct brcmf_escan_result_le *escan_result_le;
361217df6453SArend Van Spriel u32 escan_buflen;
361305491d2cSKalle Valo struct brcmf_bss_info_le *bss_info_le;
361405491d2cSKalle Valo struct brcmf_bss_info_le *bss = NULL;
361505491d2cSKalle Valo u32 bi_length;
361605491d2cSKalle Valo struct brcmf_scan_results *list;
361705491d2cSKalle Valo u32 i;
361805491d2cSKalle Valo bool aborted;
361905491d2cSKalle Valo
362005491d2cSKalle Valo status = e->status;
362105491d2cSKalle Valo
3622b9472a2eSHans de Goede if (status == BRCMF_E_STATUS_ABORT)
3623b9472a2eSHans de Goede goto exit;
3624b9472a2eSHans de Goede
362505491d2cSKalle Valo if (!test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
362616e64676SRafał Miłecki bphy_err(drvr, "scan not ready, bsscfgidx=%d\n",
36273ef005b8SRafał Miłecki ifp->bsscfgidx);
362805491d2cSKalle Valo return -EPERM;
362905491d2cSKalle Valo }
363005491d2cSKalle Valo
363105491d2cSKalle Valo if (status == BRCMF_E_STATUS_PARTIAL) {
363205491d2cSKalle Valo brcmf_dbg(SCAN, "ESCAN Partial result\n");
363317df6453SArend Van Spriel if (e->datalen < sizeof(*escan_result_le)) {
363416e64676SRafał Miłecki bphy_err(drvr, "invalid event data length\n");
363517df6453SArend Van Spriel goto exit;
363617df6453SArend Van Spriel }
363705491d2cSKalle Valo escan_result_le = (struct brcmf_escan_result_le *) data;
363805491d2cSKalle Valo if (!escan_result_le) {
363916e64676SRafał Miłecki bphy_err(drvr, "Invalid escan result (NULL pointer)\n");
364005491d2cSKalle Valo goto exit;
364105491d2cSKalle Valo }
364217df6453SArend Van Spriel escan_buflen = le32_to_cpu(escan_result_le->buflen);
364317df6453SArend Van Spriel if (escan_buflen > BRCMF_ESCAN_BUF_SIZE ||
364417df6453SArend Van Spriel escan_buflen > e->datalen ||
364517df6453SArend Van Spriel escan_buflen < sizeof(*escan_result_le)) {
364616e64676SRafał Miłecki bphy_err(drvr, "Invalid escan buffer length: %d\n",
364717df6453SArend Van Spriel escan_buflen);
364817df6453SArend Van Spriel goto exit;
364917df6453SArend Van Spriel }
365005491d2cSKalle Valo if (le16_to_cpu(escan_result_le->bss_count) != 1) {
365116e64676SRafał Miłecki bphy_err(drvr, "Invalid bss_count %d: ignoring\n",
365205491d2cSKalle Valo escan_result_le->bss_count);
365305491d2cSKalle Valo goto exit;
365405491d2cSKalle Valo }
365505491d2cSKalle Valo bss_info_le = &escan_result_le->bss_info_le;
365605491d2cSKalle Valo
365705491d2cSKalle Valo if (brcmf_p2p_scan_finding_common_channel(cfg, bss_info_le))
365805491d2cSKalle Valo goto exit;
365905491d2cSKalle Valo
3660efc2c1faSArend Van Spriel if (!cfg->int_escan_map && !cfg->scan_request) {
366105491d2cSKalle Valo brcmf_dbg(SCAN, "result without cfg80211 request\n");
366205491d2cSKalle Valo goto exit;
366305491d2cSKalle Valo }
366405491d2cSKalle Valo
366505491d2cSKalle Valo bi_length = le32_to_cpu(bss_info_le->length);
366617df6453SArend Van Spriel if (bi_length != escan_buflen - WL_ESCAN_RESULTS_FIXED_SIZE) {
366716e64676SRafał Miłecki bphy_err(drvr, "Ignoring invalid bss_info length: %d\n",
366805491d2cSKalle Valo bi_length);
366905491d2cSKalle Valo goto exit;
367005491d2cSKalle Valo }
367105491d2cSKalle Valo
367205491d2cSKalle Valo if (!(cfg_to_wiphy(cfg)->interface_modes &
367305491d2cSKalle Valo BIT(NL80211_IFTYPE_ADHOC))) {
367405491d2cSKalle Valo if (le16_to_cpu(bss_info_le->capability) &
367505491d2cSKalle Valo WLAN_CAPABILITY_IBSS) {
367616e64676SRafał Miłecki bphy_err(drvr, "Ignoring IBSS result\n");
367705491d2cSKalle Valo goto exit;
367805491d2cSKalle Valo }
367905491d2cSKalle Valo }
368005491d2cSKalle Valo
368105491d2cSKalle Valo list = (struct brcmf_scan_results *)
368205491d2cSKalle Valo cfg->escan_info.escan_buf;
3683d5367334SHante Meuleman if (bi_length > BRCMF_ESCAN_BUF_SIZE - list->buflen) {
368416e64676SRafał Miłecki bphy_err(drvr, "Buffer is too small: ignoring\n");
368505491d2cSKalle Valo goto exit;
368605491d2cSKalle Valo }
368705491d2cSKalle Valo
368805491d2cSKalle Valo for (i = 0; i < list->count; i++) {
368905491d2cSKalle Valo bss = bss ? (struct brcmf_bss_info_le *)
369005491d2cSKalle Valo ((unsigned char *)bss +
369105491d2cSKalle Valo le32_to_cpu(bss->length)) : list->bss_info_le;
369205491d2cSKalle Valo if (brcmf_compare_update_same_bss(cfg, bss,
369305491d2cSKalle Valo bss_info_le))
369405491d2cSKalle Valo goto exit;
369505491d2cSKalle Valo }
3696d5367334SHante Meuleman memcpy(&cfg->escan_info.escan_buf[list->buflen], bss_info_le,
3697d5367334SHante Meuleman bi_length);
369805491d2cSKalle Valo list->version = le32_to_cpu(bss_info_le->version);
369905491d2cSKalle Valo list->buflen += bi_length;
370005491d2cSKalle Valo list->count++;
370105491d2cSKalle Valo } else {
370205491d2cSKalle Valo cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
370305491d2cSKalle Valo if (brcmf_p2p_scan_finding_common_channel(cfg, NULL))
370405491d2cSKalle Valo goto exit;
3705efc2c1faSArend Van Spriel if (cfg->int_escan_map || cfg->scan_request) {
370605491d2cSKalle Valo brcmf_inform_bss(cfg);
370705491d2cSKalle Valo aborted = status != BRCMF_E_STATUS_SUCCESS;
370805491d2cSKalle Valo brcmf_notify_escan_complete(cfg, ifp, aborted, false);
370905491d2cSKalle Valo } else
371005491d2cSKalle Valo brcmf_dbg(SCAN, "Ignored scan complete result 0x%x\n",
371105491d2cSKalle Valo status);
371205491d2cSKalle Valo }
371305491d2cSKalle Valo exit:
371405491d2cSKalle Valo return 0;
371505491d2cSKalle Valo }
371605491d2cSKalle Valo
brcmf_init_escan(struct brcmf_cfg80211_info * cfg)371705491d2cSKalle Valo static void brcmf_init_escan(struct brcmf_cfg80211_info *cfg)
371805491d2cSKalle Valo {
371905491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_ESCAN_RESULT,
372005491d2cSKalle Valo brcmf_cfg80211_escan_handler);
372105491d2cSKalle Valo cfg->escan_info.escan_state = WL_ESCAN_STATE_IDLE;
372205491d2cSKalle Valo /* Init scan_timeout timer */
3723e99e88a9SKees Cook timer_setup(&cfg->escan_timeout, brcmf_escan_timeout, 0);
372405491d2cSKalle Valo INIT_WORK(&cfg->escan_timeout_work,
372505491d2cSKalle Valo brcmf_cfg80211_escan_timeout_worker);
372605491d2cSKalle Valo }
372705491d2cSKalle Valo
3728fa85b30aSArend Van Spriel static struct cfg80211_scan_request *
brcmf_alloc_internal_escan_request(struct wiphy * wiphy,u32 n_netinfo)3729fa85b30aSArend Van Spriel brcmf_alloc_internal_escan_request(struct wiphy *wiphy, u32 n_netinfo) {
3730fa85b30aSArend Van Spriel struct cfg80211_scan_request *req;
3731fa85b30aSArend Van Spriel size_t req_size;
3732fa85b30aSArend Van Spriel
3733fa85b30aSArend Van Spriel req_size = sizeof(*req) +
3734fa85b30aSArend Van Spriel n_netinfo * sizeof(req->channels[0]) +
3735fa85b30aSArend Van Spriel n_netinfo * sizeof(*req->ssids);
3736fa85b30aSArend Van Spriel
3737fa85b30aSArend Van Spriel req = kzalloc(req_size, GFP_KERNEL);
3738fa85b30aSArend Van Spriel if (req) {
3739fa85b30aSArend Van Spriel req->wiphy = wiphy;
3740fa85b30aSArend Van Spriel req->ssids = (void *)(&req->channels[0]) +
3741fa85b30aSArend Van Spriel n_netinfo * sizeof(req->channels[0]);
3742fa85b30aSArend Van Spriel }
3743fa85b30aSArend Van Spriel return req;
3744fa85b30aSArend Van Spriel }
3745fa85b30aSArend Van Spriel
brcmf_internal_escan_add_info(struct cfg80211_scan_request * req,u8 * ssid,u8 ssid_len,u8 channel)3746fa85b30aSArend Van Spriel static int brcmf_internal_escan_add_info(struct cfg80211_scan_request *req,
3747fa85b30aSArend Van Spriel u8 *ssid, u8 ssid_len, u8 channel)
3748fa85b30aSArend Van Spriel {
3749fa85b30aSArend Van Spriel struct ieee80211_channel *chan;
3750fa85b30aSArend Van Spriel enum nl80211_band band;
37516ea51fc7SArend Van Spriel int freq, i;
3752fa85b30aSArend Van Spriel
3753fa85b30aSArend Van Spriel if (channel <= CH_MAX_2G_CHANNEL)
3754fa85b30aSArend Van Spriel band = NL80211_BAND_2GHZ;
3755fa85b30aSArend Van Spriel else
3756fa85b30aSArend Van Spriel band = NL80211_BAND_5GHZ;
3757fa85b30aSArend Van Spriel
3758fa85b30aSArend Van Spriel freq = ieee80211_channel_to_frequency(channel, band);
3759fa85b30aSArend Van Spriel if (!freq)
3760fa85b30aSArend Van Spriel return -EINVAL;
3761fa85b30aSArend Van Spriel
3762fa85b30aSArend Van Spriel chan = ieee80211_get_channel(req->wiphy, freq);
3763fa85b30aSArend Van Spriel if (!chan)
3764fa85b30aSArend Van Spriel return -EINVAL;
3765fa85b30aSArend Van Spriel
37666ea51fc7SArend Van Spriel for (i = 0; i < req->n_channels; i++) {
37676ea51fc7SArend Van Spriel if (req->channels[i] == chan)
37686ea51fc7SArend Van Spriel break;
37696ea51fc7SArend Van Spriel }
37704571767dSKees Cook if (i == req->n_channels) {
37714571767dSKees Cook req->n_channels++;
37724571767dSKees Cook req->channels[i] = chan;
37734571767dSKees Cook }
37746ea51fc7SArend Van Spriel
37756ea51fc7SArend Van Spriel for (i = 0; i < req->n_ssids; i++) {
37766ea51fc7SArend Van Spriel if (req->ssids[i].ssid_len == ssid_len &&
37776ea51fc7SArend Van Spriel !memcmp(req->ssids[i].ssid, ssid, ssid_len))
37786ea51fc7SArend Van Spriel break;
37796ea51fc7SArend Van Spriel }
37806ea51fc7SArend Van Spriel if (i == req->n_ssids) {
3781fa85b30aSArend Van Spriel memcpy(req->ssids[req->n_ssids].ssid, ssid, ssid_len);
3782fa85b30aSArend Van Spriel req->ssids[req->n_ssids++].ssid_len = ssid_len;
37836ea51fc7SArend Van Spriel }
3784fa85b30aSArend Van Spriel return 0;
3785fa85b30aSArend Van Spriel }
3786fa85b30aSArend Van Spriel
brcmf_start_internal_escan(struct brcmf_if * ifp,u32 fwmap,struct cfg80211_scan_request * request)3787efc2c1faSArend Van Spriel static int brcmf_start_internal_escan(struct brcmf_if *ifp, u32 fwmap,
3788fa85b30aSArend Van Spriel struct cfg80211_scan_request *request)
3789fa85b30aSArend Van Spriel {
3790fa85b30aSArend Van Spriel struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
3791fa85b30aSArend Van Spriel int err;
3792fa85b30aSArend Van Spriel
3793fa85b30aSArend Van Spriel if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status)) {
3794efc2c1faSArend Van Spriel if (cfg->int_escan_map)
3795efc2c1faSArend Van Spriel brcmf_dbg(SCAN, "aborting internal scan: map=%u\n",
3796efc2c1faSArend Van Spriel cfg->int_escan_map);
3797fa85b30aSArend Van Spriel /* Abort any on-going scan */
3798fa85b30aSArend Van Spriel brcmf_abort_scanning(cfg);
3799fa85b30aSArend Van Spriel }
3800fa85b30aSArend Van Spriel
3801efc2c1faSArend Van Spriel brcmf_dbg(SCAN, "start internal scan: map=%u\n", fwmap);
3802fa85b30aSArend Van Spriel set_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3803fa85b30aSArend Van Spriel cfg->escan_info.run = brcmf_run_escan;
3804fa85b30aSArend Van Spriel err = brcmf_do_escan(ifp, request);
3805fa85b30aSArend Van Spriel if (err) {
3806fa85b30aSArend Van Spriel clear_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status);
3807fa85b30aSArend Van Spriel return err;
3808fa85b30aSArend Van Spriel }
3809efc2c1faSArend Van Spriel cfg->int_escan_map = fwmap;
3810fa85b30aSArend Van Spriel return 0;
3811fa85b30aSArend Van Spriel }
3812fa85b30aSArend Van Spriel
381353e3a80dSArend Van Spriel static struct brcmf_pno_net_info_le *
brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le * pfn_v1)381453e3a80dSArend Van Spriel brcmf_get_netinfo_array(struct brcmf_pno_scanresults_le *pfn_v1)
381553e3a80dSArend Van Spriel {
381653e3a80dSArend Van Spriel struct brcmf_pno_scanresults_v2_le *pfn_v2;
381753e3a80dSArend Van Spriel struct brcmf_pno_net_info_le *netinfo;
381853e3a80dSArend Van Spriel
381953e3a80dSArend Van Spriel switch (pfn_v1->version) {
382053e3a80dSArend Van Spriel default:
382153e3a80dSArend Van Spriel WARN_ON(1);
38220bde10dcSGustavo A. R. Silva fallthrough;
382353e3a80dSArend Van Spriel case cpu_to_le32(1):
382453e3a80dSArend Van Spriel netinfo = (struct brcmf_pno_net_info_le *)(pfn_v1 + 1);
382553e3a80dSArend Van Spriel break;
382653e3a80dSArend Van Spriel case cpu_to_le32(2):
382753e3a80dSArend Van Spriel pfn_v2 = (struct brcmf_pno_scanresults_v2_le *)pfn_v1;
382853e3a80dSArend Van Spriel netinfo = (struct brcmf_pno_net_info_le *)(pfn_v2 + 1);
382953e3a80dSArend Van Spriel break;
383053e3a80dSArend Van Spriel }
383153e3a80dSArend Van Spriel
383253e3a80dSArend Van Spriel return netinfo;
383353e3a80dSArend Van Spriel }
383453e3a80dSArend Van Spriel
38355419f7f1SHante Meuleman /* PFN result doesn't have all the info which are required by the supplicant
38365419f7f1SHante Meuleman * (For e.g IEs) Do a target Escan so that sched scan results are reported
38375419f7f1SHante Meuleman * via wl_inform_single_bss in the required format. Escan does require the
38385419f7f1SHante Meuleman * scan request in the form of cfg80211_scan_request. For timebeing, create
38395419f7f1SHante Meuleman * cfg80211_scan_request one out of the received PNO event.
38405419f7f1SHante Meuleman */
38415419f7f1SHante Meuleman static s32
brcmf_notify_sched_scan_results(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)38425419f7f1SHante Meuleman brcmf_notify_sched_scan_results(struct brcmf_if *ifp,
38435419f7f1SHante Meuleman const struct brcmf_event_msg *e, void *data)
38445419f7f1SHante Meuleman {
384516e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
384616e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = drvr->config;
38475419f7f1SHante Meuleman struct brcmf_pno_net_info_le *netinfo, *netinfo_start;
38485419f7f1SHante Meuleman struct cfg80211_scan_request *request = NULL;
38495419f7f1SHante Meuleman struct wiphy *wiphy = cfg_to_wiphy(cfg);
3850fa85b30aSArend Van Spriel int i, err = 0;
38515419f7f1SHante Meuleman struct brcmf_pno_scanresults_le *pfn_result;
3852efc2c1faSArend Van Spriel u32 bucket_map;
38535419f7f1SHante Meuleman u32 result_count;
38545419f7f1SHante Meuleman u32 status;
38554835f37eSArend Van Spriel u32 datalen;
38565419f7f1SHante Meuleman
38575419f7f1SHante Meuleman brcmf_dbg(SCAN, "Enter\n");
38585419f7f1SHante Meuleman
38590aedbcafSHante Meuleman if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
38600aedbcafSHante Meuleman brcmf_dbg(SCAN, "Event data to small. Ignore\n");
38610aedbcafSHante Meuleman return 0;
38620aedbcafSHante Meuleman }
38630aedbcafSHante Meuleman
38645419f7f1SHante Meuleman if (e->event_code == BRCMF_E_PFN_NET_LOST) {
38655419f7f1SHante Meuleman brcmf_dbg(SCAN, "PFN NET LOST event. Do Nothing\n");
38665419f7f1SHante Meuleman return 0;
38675419f7f1SHante Meuleman }
38685419f7f1SHante Meuleman
38695419f7f1SHante Meuleman pfn_result = (struct brcmf_pno_scanresults_le *)data;
38705419f7f1SHante Meuleman result_count = le32_to_cpu(pfn_result->count);
38715419f7f1SHante Meuleman status = le32_to_cpu(pfn_result->status);
38725419f7f1SHante Meuleman
38735419f7f1SHante Meuleman /* PFN event is limited to fit 512 bytes so we may get
38745419f7f1SHante Meuleman * multiple NET_FOUND events. For now place a warning here.
38755419f7f1SHante Meuleman */
38765419f7f1SHante Meuleman WARN_ON(status != BRCMF_PNO_SCAN_COMPLETE);
38775419f7f1SHante Meuleman brcmf_dbg(SCAN, "PFN NET FOUND event. count: %d\n", result_count);
3878fa85b30aSArend Van Spriel if (!result_count) {
387916e64676SRafał Miłecki bphy_err(drvr, "FALSE PNO Event. (pfn_count == 0)\n");
3880fa85b30aSArend Van Spriel goto out_err;
3881fa85b30aSArend Van Spriel }
38824835f37eSArend Van Spriel
38834835f37eSArend Van Spriel netinfo_start = brcmf_get_netinfo_array(pfn_result);
38844835f37eSArend Van Spriel datalen = e->datalen - ((void *)netinfo_start - (void *)pfn_result);
38854835f37eSArend Van Spriel if (datalen < result_count * sizeof(*netinfo)) {
388616e64676SRafał Miłecki bphy_err(drvr, "insufficient event data\n");
38874835f37eSArend Van Spriel goto out_err;
38884835f37eSArend Van Spriel }
38894835f37eSArend Van Spriel
3890fa85b30aSArend Van Spriel request = brcmf_alloc_internal_escan_request(wiphy,
3891fa85b30aSArend Van Spriel result_count);
3892fa85b30aSArend Van Spriel if (!request) {
38935419f7f1SHante Meuleman err = -ENOMEM;
38945419f7f1SHante Meuleman goto out_err;
38955419f7f1SHante Meuleman }
38965419f7f1SHante Meuleman
3897efc2c1faSArend Van Spriel bucket_map = 0;
38985419f7f1SHante Meuleman for (i = 0; i < result_count; i++) {
38995419f7f1SHante Meuleman netinfo = &netinfo_start[i];
39005419f7f1SHante Meuleman
39014835f37eSArend Van Spriel if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
39024835f37eSArend Van Spriel netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
3903fa85b30aSArend Van Spriel brcmf_dbg(SCAN, "SSID:%.32s Channel:%d\n",
39045419f7f1SHante Meuleman netinfo->SSID, netinfo->channel);
3905efc2c1faSArend Van Spriel bucket_map |= brcmf_pno_get_bucket_map(cfg->pno, netinfo);
3906fa85b30aSArend Van Spriel err = brcmf_internal_escan_add_info(request,
3907fa85b30aSArend Van Spriel netinfo->SSID,
3908fa85b30aSArend Van Spriel netinfo->SSID_len,
3909fa85b30aSArend Van Spriel netinfo->channel);
3910fa85b30aSArend Van Spriel if (err)
39115419f7f1SHante Meuleman goto out_err;
39125419f7f1SHante Meuleman }
39135419f7f1SHante Meuleman
3914efc2c1faSArend Van Spriel if (!bucket_map)
3915efc2c1faSArend Van Spriel goto free_req;
3916efc2c1faSArend Van Spriel
3917efc2c1faSArend Van Spriel err = brcmf_start_internal_escan(ifp, bucket_map, request);
3918fa85b30aSArend Van Spriel if (!err)
3919fa85b30aSArend Van Spriel goto free_req;
39205419f7f1SHante Meuleman
39215419f7f1SHante Meuleman out_err:
3922b34939b9SArend Van Spriel cfg80211_sched_scan_stopped(wiphy, 0);
3923fa85b30aSArend Van Spriel free_req:
3924fa85b30aSArend Van Spriel kfree(request);
39255419f7f1SHante Meuleman return err;
39265419f7f1SHante Meuleman }
39275419f7f1SHante Meuleman
39285419f7f1SHante Meuleman static int
brcmf_cfg80211_sched_scan_start(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_sched_scan_request * req)39295419f7f1SHante Meuleman brcmf_cfg80211_sched_scan_start(struct wiphy *wiphy,
39305419f7f1SHante Meuleman struct net_device *ndev,
39313e2e86abSArend Van Spriel struct cfg80211_sched_scan_request *req)
39325419f7f1SHante Meuleman {
3933856d5a01SArend Van Spriel struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
393416e64676SRafał Miłecki struct brcmf_if *ifp = netdev_priv(ndev);
393516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
39365419f7f1SHante Meuleman
3937efc2c1faSArend Van Spriel brcmf_dbg(SCAN, "Enter: n_match_sets=%d n_ssids=%d\n",
39383e2e86abSArend Van Spriel req->n_match_sets, req->n_ssids);
3939dfe5b0d5SArend Van Spriel
39405419f7f1SHante Meuleman if (test_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status)) {
394116e64676SRafał Miłecki bphy_err(drvr, "Scanning suppressed: status=%lu\n",
39425419f7f1SHante Meuleman cfg->scan_status);
39435419f7f1SHante Meuleman return -EAGAIN;
39445419f7f1SHante Meuleman }
39455419f7f1SHante Meuleman
39463e2e86abSArend Van Spriel if (req->n_match_sets <= 0) {
39473e2e86abSArend Van Spriel brcmf_dbg(SCAN, "invalid number of matchsets specified: %d\n",
39483e2e86abSArend Van Spriel req->n_match_sets);
39495419f7f1SHante Meuleman return -EINVAL;
39505419f7f1SHante Meuleman }
39515419f7f1SHante Meuleman
39523e48611dSArend Van Spriel return brcmf_pno_start_sched_scan(ifp, req);
39535419f7f1SHante Meuleman }
39545419f7f1SHante Meuleman
brcmf_cfg80211_sched_scan_stop(struct wiphy * wiphy,struct net_device * ndev,u64 reqid)39555419f7f1SHante Meuleman static int brcmf_cfg80211_sched_scan_stop(struct wiphy *wiphy,
39563a3ecf1dSArend Van Spriel struct net_device *ndev, u64 reqid)
39575419f7f1SHante Meuleman {
39585419f7f1SHante Meuleman struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
3959ac55136fSArend Van Spriel struct brcmf_if *ifp = netdev_priv(ndev);
39605419f7f1SHante Meuleman
39615419f7f1SHante Meuleman brcmf_dbg(SCAN, "enter\n");
3962efc2c1faSArend Van Spriel brcmf_pno_stop_sched_scan(ifp, reqid);
3963efc2c1faSArend Van Spriel if (cfg->int_escan_map)
3964ac55136fSArend Van Spriel brcmf_notify_escan_complete(cfg, ifp, true, true);
39655419f7f1SHante Meuleman return 0;
39665419f7f1SHante Meuleman }
39675419f7f1SHante Meuleman
brcmf_delay(u32 ms)396805491d2cSKalle Valo static __always_inline void brcmf_delay(u32 ms)
396905491d2cSKalle Valo {
397005491d2cSKalle Valo if (ms < 1000 / HZ) {
397105491d2cSKalle Valo cond_resched();
397205491d2cSKalle Valo mdelay(ms);
397305491d2cSKalle Valo } else {
397405491d2cSKalle Valo msleep(ms);
397505491d2cSKalle Valo }
397605491d2cSKalle Valo }
397705491d2cSKalle Valo
brcmf_config_wowl_pattern(struct brcmf_if * ifp,u8 cmd[4],u8 * pattern,u32 patternsize,u8 * mask,u32 packet_offset)397805491d2cSKalle Valo static s32 brcmf_config_wowl_pattern(struct brcmf_if *ifp, u8 cmd[4],
397905491d2cSKalle Valo u8 *pattern, u32 patternsize, u8 *mask,
398005491d2cSKalle Valo u32 packet_offset)
398105491d2cSKalle Valo {
398205491d2cSKalle Valo struct brcmf_fil_wowl_pattern_le *filter;
398305491d2cSKalle Valo u32 masksize;
398405491d2cSKalle Valo u32 patternoffset;
398505491d2cSKalle Valo u8 *buf;
398605491d2cSKalle Valo u32 bufsize;
398705491d2cSKalle Valo s32 ret;
398805491d2cSKalle Valo
398905491d2cSKalle Valo masksize = (patternsize + 7) / 8;
399005491d2cSKalle Valo patternoffset = sizeof(*filter) - sizeof(filter->cmd) + masksize;
399105491d2cSKalle Valo
399205491d2cSKalle Valo bufsize = sizeof(*filter) + patternsize + masksize;
399305491d2cSKalle Valo buf = kzalloc(bufsize, GFP_KERNEL);
399405491d2cSKalle Valo if (!buf)
399505491d2cSKalle Valo return -ENOMEM;
399605491d2cSKalle Valo filter = (struct brcmf_fil_wowl_pattern_le *)buf;
399705491d2cSKalle Valo
399805491d2cSKalle Valo memcpy(filter->cmd, cmd, 4);
399905491d2cSKalle Valo filter->masksize = cpu_to_le32(masksize);
400005491d2cSKalle Valo filter->offset = cpu_to_le32(packet_offset);
400105491d2cSKalle Valo filter->patternoffset = cpu_to_le32(patternoffset);
400205491d2cSKalle Valo filter->patternsize = cpu_to_le32(patternsize);
400305491d2cSKalle Valo filter->type = cpu_to_le32(BRCMF_WOWL_PATTERN_TYPE_BITMAP);
400405491d2cSKalle Valo
400505491d2cSKalle Valo if ((mask) && (masksize))
400605491d2cSKalle Valo memcpy(buf + sizeof(*filter), mask, masksize);
400705491d2cSKalle Valo if ((pattern) && (patternsize))
400805491d2cSKalle Valo memcpy(buf + sizeof(*filter) + masksize, pattern, patternsize);
400905491d2cSKalle Valo
401005491d2cSKalle Valo ret = brcmf_fil_iovar_data_set(ifp, "wowl_pattern", buf, bufsize);
401105491d2cSKalle Valo
401205491d2cSKalle Valo kfree(buf);
401305491d2cSKalle Valo return ret;
401405491d2cSKalle Valo }
401505491d2cSKalle Valo
40163021ad9aSHante Meuleman static s32
brcmf_wowl_nd_results(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)40173021ad9aSHante Meuleman brcmf_wowl_nd_results(struct brcmf_if *ifp, const struct brcmf_event_msg *e,
40183021ad9aSHante Meuleman void *data)
40193021ad9aSHante Meuleman {
402016e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
402116e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = drvr->config;
40223021ad9aSHante Meuleman struct brcmf_pno_scanresults_le *pfn_result;
40233021ad9aSHante Meuleman struct brcmf_pno_net_info_le *netinfo;
40243021ad9aSHante Meuleman
40253021ad9aSHante Meuleman brcmf_dbg(SCAN, "Enter\n");
40263021ad9aSHante Meuleman
40270aedbcafSHante Meuleman if (e->datalen < (sizeof(*pfn_result) + sizeof(*netinfo))) {
40280aedbcafSHante Meuleman brcmf_dbg(SCAN, "Event data to small. Ignore\n");
40290aedbcafSHante Meuleman return 0;
40300aedbcafSHante Meuleman }
40310aedbcafSHante Meuleman
40323021ad9aSHante Meuleman pfn_result = (struct brcmf_pno_scanresults_le *)data;
40333021ad9aSHante Meuleman
40343021ad9aSHante Meuleman if (e->event_code == BRCMF_E_PFN_NET_LOST) {
40353021ad9aSHante Meuleman brcmf_dbg(SCAN, "PFN NET LOST event. Ignore\n");
40363021ad9aSHante Meuleman return 0;
40373021ad9aSHante Meuleman }
40383021ad9aSHante Meuleman
40393021ad9aSHante Meuleman if (le32_to_cpu(pfn_result->count) < 1) {
404016e64676SRafał Miłecki bphy_err(drvr, "Invalid result count, expected 1 (%d)\n",
40413021ad9aSHante Meuleman le32_to_cpu(pfn_result->count));
40423021ad9aSHante Meuleman return -EINVAL;
40433021ad9aSHante Meuleman }
40443021ad9aSHante Meuleman
4045d29afe91SArend Van Spriel netinfo = brcmf_get_netinfo_array(pfn_result);
40461b5e2423SArend van Spriel if (netinfo->SSID_len > IEEE80211_MAX_SSID_LEN)
40471b5e2423SArend van Spriel netinfo->SSID_len = IEEE80211_MAX_SSID_LEN;
40483021ad9aSHante Meuleman memcpy(cfg->wowl.nd->ssid.ssid, netinfo->SSID, netinfo->SSID_len);
40493021ad9aSHante Meuleman cfg->wowl.nd->ssid.ssid_len = netinfo->SSID_len;
40503021ad9aSHante Meuleman cfg->wowl.nd->n_channels = 1;
40513021ad9aSHante Meuleman cfg->wowl.nd->channels[0] =
40523021ad9aSHante Meuleman ieee80211_channel_to_frequency(netinfo->channel,
40533021ad9aSHante Meuleman netinfo->channel <= CH_MAX_2G_CHANNEL ?
40543021ad9aSHante Meuleman NL80211_BAND_2GHZ : NL80211_BAND_5GHZ);
40553021ad9aSHante Meuleman cfg->wowl.nd_info->n_matches = 1;
40563021ad9aSHante Meuleman cfg->wowl.nd_info->matches[0] = cfg->wowl.nd;
40573021ad9aSHante Meuleman
40583021ad9aSHante Meuleman /* Inform (the resume task) that the net detect information was recvd */
40593021ad9aSHante Meuleman cfg->wowl.nd_data_completed = true;
40603021ad9aSHante Meuleman wake_up(&cfg->wowl.nd_data_wait);
40613021ad9aSHante Meuleman
40623021ad9aSHante Meuleman return 0;
40633021ad9aSHante Meuleman }
40643021ad9aSHante Meuleman
4065aeb64225SHante Meuleman #ifdef CONFIG_PM
4066aeb64225SHante Meuleman
brcmf_report_wowl_wakeind(struct wiphy * wiphy,struct brcmf_if * ifp)4067aeb64225SHante Meuleman static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
4068aeb64225SHante Meuleman {
40693021ad9aSHante Meuleman struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
407016e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
4071aeb64225SHante Meuleman struct brcmf_wowl_wakeind_le wake_ind_le;
4072aeb64225SHante Meuleman struct cfg80211_wowlan_wakeup wakeup_data;
4073aeb64225SHante Meuleman struct cfg80211_wowlan_wakeup *wakeup;
4074aeb64225SHante Meuleman u32 wakeind;
4075aeb64225SHante Meuleman s32 err;
40763021ad9aSHante Meuleman int timeout;
4077aeb64225SHante Meuleman
4078aeb64225SHante Meuleman err = brcmf_fil_iovar_data_get(ifp, "wowl_wakeind", &wake_ind_le,
4079aeb64225SHante Meuleman sizeof(wake_ind_le));
40803021ad9aSHante Meuleman if (err) {
408116e64676SRafał Miłecki bphy_err(drvr, "Get wowl_wakeind failed, err = %d\n", err);
4082aeb64225SHante Meuleman return;
4083aeb64225SHante Meuleman }
4084aeb64225SHante Meuleman
4085aeb64225SHante Meuleman wakeind = le32_to_cpu(wake_ind_le.ucode_wakeind);
4086aeb64225SHante Meuleman if (wakeind & (BRCMF_WOWL_MAGIC | BRCMF_WOWL_DIS | BRCMF_WOWL_BCN |
40873021ad9aSHante Meuleman BRCMF_WOWL_RETR | BRCMF_WOWL_NET |
40883021ad9aSHante Meuleman BRCMF_WOWL_PFN_FOUND)) {
4089aeb64225SHante Meuleman wakeup = &wakeup_data;
4090aeb64225SHante Meuleman memset(&wakeup_data, 0, sizeof(wakeup_data));
4091aeb64225SHante Meuleman wakeup_data.pattern_idx = -1;
4092aeb64225SHante Meuleman
4093aeb64225SHante Meuleman if (wakeind & BRCMF_WOWL_MAGIC) {
4094aeb64225SHante Meuleman brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_MAGIC\n");
4095aeb64225SHante Meuleman wakeup_data.magic_pkt = true;
4096aeb64225SHante Meuleman }
4097aeb64225SHante Meuleman if (wakeind & BRCMF_WOWL_DIS) {
4098aeb64225SHante Meuleman brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_DIS\n");
4099aeb64225SHante Meuleman wakeup_data.disconnect = true;
4100aeb64225SHante Meuleman }
4101aeb64225SHante Meuleman if (wakeind & BRCMF_WOWL_BCN) {
4102aeb64225SHante Meuleman brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_BCN\n");
4103aeb64225SHante Meuleman wakeup_data.disconnect = true;
4104aeb64225SHante Meuleman }
4105aeb64225SHante Meuleman if (wakeind & BRCMF_WOWL_RETR) {
4106aeb64225SHante Meuleman brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_RETR\n");
4107aeb64225SHante Meuleman wakeup_data.disconnect = true;
4108aeb64225SHante Meuleman }
4109aeb64225SHante Meuleman if (wakeind & BRCMF_WOWL_NET) {
4110aeb64225SHante Meuleman brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_NET\n");
4111aeb64225SHante Meuleman /* For now always map to pattern 0, no API to get
4112aeb64225SHante Meuleman * correct information available at the moment.
4113aeb64225SHante Meuleman */
4114aeb64225SHante Meuleman wakeup_data.pattern_idx = 0;
4115aeb64225SHante Meuleman }
41163021ad9aSHante Meuleman if (wakeind & BRCMF_WOWL_PFN_FOUND) {
41173021ad9aSHante Meuleman brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_PFN_FOUND\n");
41183021ad9aSHante Meuleman timeout = wait_event_timeout(cfg->wowl.nd_data_wait,
41193021ad9aSHante Meuleman cfg->wowl.nd_data_completed,
41203021ad9aSHante Meuleman BRCMF_ND_INFO_TIMEOUT);
41213021ad9aSHante Meuleman if (!timeout)
412216e64676SRafał Miłecki bphy_err(drvr, "No result for wowl net detect\n");
41233021ad9aSHante Meuleman else
41243021ad9aSHante Meuleman wakeup_data.net_detect = cfg->wowl.nd_info;
41253021ad9aSHante Meuleman }
41265c22fb85SHante Meuleman if (wakeind & BRCMF_WOWL_GTK_FAILURE) {
41275c22fb85SHante Meuleman brcmf_dbg(INFO, "WOWL Wake indicator: BRCMF_WOWL_GTK_FAILURE\n");
41285c22fb85SHante Meuleman wakeup_data.gtk_rekey_failure = true;
41295c22fb85SHante Meuleman }
4130aeb64225SHante Meuleman } else {
4131aeb64225SHante Meuleman wakeup = NULL;
4132aeb64225SHante Meuleman }
4133aeb64225SHante Meuleman cfg80211_report_wowlan_wakeup(&ifp->vif->wdev, wakeup, GFP_KERNEL);
4134aeb64225SHante Meuleman }
4135aeb64225SHante Meuleman
4136aeb64225SHante Meuleman #else
4137aeb64225SHante Meuleman
brcmf_report_wowl_wakeind(struct wiphy * wiphy,struct brcmf_if * ifp)4138aeb64225SHante Meuleman static void brcmf_report_wowl_wakeind(struct wiphy *wiphy, struct brcmf_if *ifp)
4139aeb64225SHante Meuleman {
4140aeb64225SHante Meuleman }
4141aeb64225SHante Meuleman
4142aeb64225SHante Meuleman #endif /* CONFIG_PM */
4143aeb64225SHante Meuleman
brcmf_cfg80211_resume(struct wiphy * wiphy)414405491d2cSKalle Valo static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
414505491d2cSKalle Valo {
414605491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
414705491d2cSKalle Valo struct net_device *ndev = cfg_to_ndev(cfg);
414805491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
414905491d2cSKalle Valo
415005491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
415105491d2cSKalle Valo
41523021ad9aSHante Meuleman if (cfg->wowl.active) {
4153aeb64225SHante Meuleman brcmf_report_wowl_wakeind(wiphy, ifp);
4154aeb64225SHante Meuleman brcmf_fil_iovar_int_set(ifp, "wowl_clear", 0);
4155aeb64225SHante Meuleman brcmf_config_wowl_pattern(ifp, "clr", NULL, 0, NULL, 0);
415673ef9e64SHante Meuleman if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
415752f22fb2SFranky Lin brcmf_configure_arp_nd_offload(ifp, true);
415805491d2cSKalle Valo brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM,
41593021ad9aSHante Meuleman cfg->wowl.pre_pmmode);
41603021ad9aSHante Meuleman cfg->wowl.active = false;
41613021ad9aSHante Meuleman if (cfg->wowl.nd_enabled) {
41623a3ecf1dSArend Van Spriel brcmf_cfg80211_sched_scan_stop(cfg->wiphy, ifp->ndev, 0);
41633021ad9aSHante Meuleman brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
41643021ad9aSHante Meuleman brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
41653021ad9aSHante Meuleman brcmf_notify_sched_scan_results);
41663021ad9aSHante Meuleman cfg->wowl.nd_enabled = false;
41673021ad9aSHante Meuleman }
416805491d2cSKalle Valo }
416905491d2cSKalle Valo return 0;
417005491d2cSKalle Valo }
417105491d2cSKalle Valo
brcmf_configure_wowl(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp,struct cfg80211_wowlan * wowl)417205491d2cSKalle Valo static void brcmf_configure_wowl(struct brcmf_cfg80211_info *cfg,
417305491d2cSKalle Valo struct brcmf_if *ifp,
417405491d2cSKalle Valo struct cfg80211_wowlan *wowl)
417505491d2cSKalle Valo {
417605491d2cSKalle Valo u32 wowl_config;
4177a7ed7828SHante Meuleman struct brcmf_wowl_wakeind_le wowl_wakeind;
417805491d2cSKalle Valo u32 i;
417905491d2cSKalle Valo
418005491d2cSKalle Valo brcmf_dbg(TRACE, "Suspend, wowl config.\n");
418105491d2cSKalle Valo
418273ef9e64SHante Meuleman if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ARP_ND))
418352f22fb2SFranky Lin brcmf_configure_arp_nd_offload(ifp, false);
41843021ad9aSHante Meuleman brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PM, &cfg->wowl.pre_pmmode);
418505491d2cSKalle Valo brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, PM_MAX);
418605491d2cSKalle Valo
418705491d2cSKalle Valo wowl_config = 0;
418805491d2cSKalle Valo if (wowl->disconnect)
418905491d2cSKalle Valo wowl_config = BRCMF_WOWL_DIS | BRCMF_WOWL_BCN | BRCMF_WOWL_RETR;
419005491d2cSKalle Valo if (wowl->magic_pkt)
419105491d2cSKalle Valo wowl_config |= BRCMF_WOWL_MAGIC;
419205491d2cSKalle Valo if ((wowl->patterns) && (wowl->n_patterns)) {
419305491d2cSKalle Valo wowl_config |= BRCMF_WOWL_NET;
419405491d2cSKalle Valo for (i = 0; i < wowl->n_patterns; i++) {
419505491d2cSKalle Valo brcmf_config_wowl_pattern(ifp, "add",
419605491d2cSKalle Valo (u8 *)wowl->patterns[i].pattern,
419705491d2cSKalle Valo wowl->patterns[i].pattern_len,
419805491d2cSKalle Valo (u8 *)wowl->patterns[i].mask,
419905491d2cSKalle Valo wowl->patterns[i].pkt_offset);
420005491d2cSKalle Valo }
420105491d2cSKalle Valo }
42023021ad9aSHante Meuleman if (wowl->nd_config) {
42033021ad9aSHante Meuleman brcmf_cfg80211_sched_scan_start(cfg->wiphy, ifp->ndev,
42043021ad9aSHante Meuleman wowl->nd_config);
42053021ad9aSHante Meuleman wowl_config |= BRCMF_WOWL_PFN_FOUND;
42063021ad9aSHante Meuleman
42073021ad9aSHante Meuleman cfg->wowl.nd_data_completed = false;
42083021ad9aSHante Meuleman cfg->wowl.nd_enabled = true;
42093021ad9aSHante Meuleman /* Now reroute the event for PFN to the wowl function. */
42103021ad9aSHante Meuleman brcmf_fweh_unregister(cfg->pub, BRCMF_E_PFN_NET_FOUND);
42113021ad9aSHante Meuleman brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
42123021ad9aSHante Meuleman brcmf_wowl_nd_results);
42133021ad9aSHante Meuleman }
42145c22fb85SHante Meuleman if (wowl->gtk_rekey_failure)
42155c22fb85SHante Meuleman wowl_config |= BRCMF_WOWL_GTK_FAILURE;
42163021ad9aSHante Meuleman if (!test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))
42173021ad9aSHante Meuleman wowl_config |= BRCMF_WOWL_UNASSOC;
42183021ad9aSHante Meuleman
4219a7ed7828SHante Meuleman memcpy(&wowl_wakeind, "clear", 6);
4220a7ed7828SHante Meuleman brcmf_fil_iovar_data_set(ifp, "wowl_wakeind", &wowl_wakeind,
4221a7ed7828SHante Meuleman sizeof(wowl_wakeind));
422205491d2cSKalle Valo brcmf_fil_iovar_int_set(ifp, "wowl", wowl_config);
422305491d2cSKalle Valo brcmf_fil_iovar_int_set(ifp, "wowl_activate", 1);
422405491d2cSKalle Valo brcmf_bus_wowl_config(cfg->pub->bus_if, true);
42253021ad9aSHante Meuleman cfg->wowl.active = true;
422605491d2cSKalle Valo }
422705491d2cSKalle Valo
brcmf_keepalive_start(struct brcmf_if * ifp,unsigned int interval)42287a6cfe28SLoic Poulain static int brcmf_keepalive_start(struct brcmf_if *ifp, unsigned int interval)
42297a6cfe28SLoic Poulain {
42307a6cfe28SLoic Poulain struct brcmf_mkeep_alive_pkt_le kalive = {0};
42317a6cfe28SLoic Poulain int ret = 0;
42327a6cfe28SLoic Poulain
42337a6cfe28SLoic Poulain /* Configure Null function/data keepalive */
42347a6cfe28SLoic Poulain kalive.version = cpu_to_le16(1);
42353db30b79SLoic Poulain kalive.period_msec = cpu_to_le32(interval * MSEC_PER_SEC);
42367a6cfe28SLoic Poulain kalive.len_bytes = cpu_to_le16(0);
42373db30b79SLoic Poulain kalive.keep_alive_id = 0;
42387a6cfe28SLoic Poulain
42397a6cfe28SLoic Poulain ret = brcmf_fil_iovar_data_set(ifp, "mkeep_alive", &kalive, sizeof(kalive));
42407a6cfe28SLoic Poulain if (ret)
42417a6cfe28SLoic Poulain brcmf_err("keep-alive packet config failed, ret=%d\n", ret);
42427a6cfe28SLoic Poulain
42437a6cfe28SLoic Poulain return ret;
42447a6cfe28SLoic Poulain }
42457a6cfe28SLoic Poulain
brcmf_cfg80211_suspend(struct wiphy * wiphy,struct cfg80211_wowlan * wowl)424605491d2cSKalle Valo static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
424705491d2cSKalle Valo struct cfg80211_wowlan *wowl)
424805491d2cSKalle Valo {
424905491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
425005491d2cSKalle Valo struct net_device *ndev = cfg_to_ndev(cfg);
425105491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
425205491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
425305491d2cSKalle Valo
425405491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
425505491d2cSKalle Valo
425605491d2cSKalle Valo /* if the primary net_device is not READY there is nothing
425705491d2cSKalle Valo * we can do but pray resume goes smoothly.
425805491d2cSKalle Valo */
425905491d2cSKalle Valo if (!check_vif_up(ifp->vif))
426005491d2cSKalle Valo goto exit;
426105491d2cSKalle Valo
42623021ad9aSHante Meuleman /* Stop scheduled scan */
42633021ad9aSHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO))
42643a3ecf1dSArend Van Spriel brcmf_cfg80211_sched_scan_stop(wiphy, ndev, 0);
42653021ad9aSHante Meuleman
426605491d2cSKalle Valo /* end any scanning */
426705491d2cSKalle Valo if (test_bit(BRCMF_SCAN_STATUS_BUSY, &cfg->scan_status))
426805491d2cSKalle Valo brcmf_abort_scanning(cfg);
426905491d2cSKalle Valo
427005491d2cSKalle Valo if (wowl == NULL) {
427105491d2cSKalle Valo brcmf_bus_wowl_config(cfg->pub->bus_if, false);
427205491d2cSKalle Valo list_for_each_entry(vif, &cfg->vif_list, list) {
427305491d2cSKalle Valo if (!test_bit(BRCMF_VIF_STATUS_READY, &vif->sme_state))
427405491d2cSKalle Valo continue;
427505491d2cSKalle Valo /* While going to suspend if associated with AP
427605491d2cSKalle Valo * disassociate from AP to save power while system is
427705491d2cSKalle Valo * in suspended state
427805491d2cSKalle Valo */
42791b050d97SSoontak Lee brcmf_link_down(vif, WLAN_REASON_UNSPECIFIED, true);
428005491d2cSKalle Valo /* Make sure WPA_Supplicant receives all the event
428105491d2cSKalle Valo * generated due to DISASSOC call to the fw to keep
428205491d2cSKalle Valo * the state fw and WPA_Supplicant state consistent
428305491d2cSKalle Valo */
428405491d2cSKalle Valo brcmf_delay(500);
428505491d2cSKalle Valo }
428605491d2cSKalle Valo /* Configure MPC */
428705491d2cSKalle Valo brcmf_set_mpc(ifp, 1);
428805491d2cSKalle Valo
428905491d2cSKalle Valo } else {
429005491d2cSKalle Valo /* Configure WOWL paramaters */
429105491d2cSKalle Valo brcmf_configure_wowl(cfg, ifp, wowl);
42927a6cfe28SLoic Poulain
42937a6cfe28SLoic Poulain /* Prevent disassociation due to inactivity with keep-alive */
42947a6cfe28SLoic Poulain brcmf_keepalive_start(ifp, 30);
429505491d2cSKalle Valo }
429605491d2cSKalle Valo
429705491d2cSKalle Valo exit:
429805491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
429905491d2cSKalle Valo /* clear any scanning activity */
430005491d2cSKalle Valo cfg->scan_status = 0;
430105491d2cSKalle Valo return 0;
430205491d2cSKalle Valo }
430305491d2cSKalle Valo
4304a96202acSHector Martin static s32
brcmf_pmksa_v3_op(struct brcmf_if * ifp,struct cfg80211_pmksa * pmksa,bool alive)4305a96202acSHector Martin brcmf_pmksa_v3_op(struct brcmf_if *ifp, struct cfg80211_pmksa *pmksa,
4306a96202acSHector Martin bool alive)
4307a96202acSHector Martin {
4308a96202acSHector Martin struct brcmf_pmk_op_v3_le *pmk_op;
4309a96202acSHector Martin int length = offsetof(struct brcmf_pmk_op_v3_le, pmk);
4310a96202acSHector Martin int ret;
4311a96202acSHector Martin
4312a96202acSHector Martin pmk_op = kzalloc(sizeof(*pmk_op), GFP_KERNEL);
4313df62e22cSDuoming Zhou if (!pmk_op)
4314df62e22cSDuoming Zhou return -ENOMEM;
4315df62e22cSDuoming Zhou
4316a96202acSHector Martin pmk_op->version = cpu_to_le16(BRCMF_PMKSA_VER_3);
4317a96202acSHector Martin
4318a96202acSHector Martin if (!pmksa) {
4319a96202acSHector Martin /* Flush operation, operate on entire list */
4320a96202acSHector Martin pmk_op->count = cpu_to_le16(0);
4321a96202acSHector Martin } else {
4322a96202acSHector Martin /* Single PMK operation */
4323a96202acSHector Martin pmk_op->count = cpu_to_le16(1);
4324a96202acSHector Martin length += sizeof(struct brcmf_pmksa_v3);
43254291f94fSJanne Grunau if (pmksa->bssid)
4326a96202acSHector Martin memcpy(pmk_op->pmk[0].bssid, pmksa->bssid, ETH_ALEN);
43274291f94fSJanne Grunau if (pmksa->pmkid) {
4328a96202acSHector Martin memcpy(pmk_op->pmk[0].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
4329a96202acSHector Martin pmk_op->pmk[0].pmkid_len = WLAN_PMKID_LEN;
43304291f94fSJanne Grunau }
43314291f94fSJanne Grunau if (pmksa->ssid && pmksa->ssid_len) {
43324291f94fSJanne Grunau memcpy(pmk_op->pmk[0].ssid.SSID, pmksa->ssid, pmksa->ssid_len);
43334291f94fSJanne Grunau pmk_op->pmk[0].ssid.SSID_len = pmksa->ssid_len;
43344291f94fSJanne Grunau }
4335a96202acSHector Martin pmk_op->pmk[0].time_left = cpu_to_le32(alive ? BRCMF_PMKSA_NO_EXPIRY : 0);
4336a96202acSHector Martin }
4337a96202acSHector Martin
4338a96202acSHector Martin pmk_op->length = cpu_to_le16(length);
4339a96202acSHector Martin
4340a96202acSHector Martin ret = brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_op, sizeof(*pmk_op));
4341a96202acSHector Martin kfree(pmk_op);
4342a96202acSHector Martin return ret;
4343a96202acSHector Martin }
4344a96202acSHector Martin
434505491d2cSKalle Valo static __used s32
brcmf_update_pmklist(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)43466c404f34SHante Meuleman brcmf_update_pmklist(struct brcmf_cfg80211_info *cfg, struct brcmf_if *ifp)
434705491d2cSKalle Valo {
43486c404f34SHante Meuleman struct brcmf_pmk_list_le *pmk_list;
43496c404f34SHante Meuleman int i;
43506c404f34SHante Meuleman u32 npmk;
435105491d2cSKalle Valo
43526c404f34SHante Meuleman pmk_list = &cfg->pmk_list;
43536c404f34SHante Meuleman npmk = le32_to_cpu(pmk_list->npmk);
435405491d2cSKalle Valo
43556c404f34SHante Meuleman brcmf_dbg(CONN, "No of elements %d\n", npmk);
43566c404f34SHante Meuleman for (i = 0; i < npmk; i++)
43576c404f34SHante Meuleman brcmf_dbg(CONN, "PMK[%d]: %pM\n", i, &pmk_list->pmk[i].bssid);
435805491d2cSKalle Valo
4359e56a7708SJinpeng Cui return brcmf_fil_iovar_data_set(ifp, "pmkid_info", pmk_list,
43606c404f34SHante Meuleman sizeof(*pmk_list));
436105491d2cSKalle Valo }
436205491d2cSKalle Valo
436305491d2cSKalle Valo static s32
brcmf_cfg80211_set_pmksa(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_pmksa * pmksa)436405491d2cSKalle Valo brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *ndev,
436505491d2cSKalle Valo struct cfg80211_pmksa *pmksa)
436605491d2cSKalle Valo {
436705491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
436805491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
43696c404f34SHante Meuleman struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
437016e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
43716c404f34SHante Meuleman s32 err;
43726c404f34SHante Meuleman u32 npmk, i;
437305491d2cSKalle Valo
437405491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
437505491d2cSKalle Valo if (!check_vif_up(ifp->vif))
437605491d2cSKalle Valo return -EIO;
437705491d2cSKalle Valo
4378a96202acSHector Martin brcmf_dbg(CONN, "set_pmksa - PMK bssid: %pM =\n", pmksa->bssid);
4379a96202acSHector Martin brcmf_dbg(CONN, "%*ph\n", WLAN_PMKID_LEN, pmksa->pmkid);
4380a96202acSHector Martin
4381a96202acSHector Martin if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4382a96202acSHector Martin return brcmf_pmksa_v3_op(ifp, pmksa, true);
4383a96202acSHector Martin
4384a96202acSHector Martin /* TODO: implement PMKID_V2 */
4385a96202acSHector Martin
43866c404f34SHante Meuleman npmk = le32_to_cpu(cfg->pmk_list.npmk);
43876c404f34SHante Meuleman for (i = 0; i < npmk; i++)
43886c404f34SHante Meuleman if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
438905491d2cSKalle Valo break;
43906c404f34SHante Meuleman if (i < BRCMF_MAXPMKID) {
43916c404f34SHante Meuleman memcpy(pmk[i].bssid, pmksa->bssid, ETH_ALEN);
43926c404f34SHante Meuleman memcpy(pmk[i].pmkid, pmksa->pmkid, WLAN_PMKID_LEN);
43936c404f34SHante Meuleman if (i == npmk) {
43946c404f34SHante Meuleman npmk++;
43956c404f34SHante Meuleman cfg->pmk_list.npmk = cpu_to_le32(npmk);
439605491d2cSKalle Valo }
43976c404f34SHante Meuleman } else {
439816e64676SRafał Miłecki bphy_err(drvr, "Too many PMKSA entries cached %d\n", npmk);
43996c404f34SHante Meuleman return -EINVAL;
44006c404f34SHante Meuleman }
440105491d2cSKalle Valo
44026c404f34SHante Meuleman err = brcmf_update_pmklist(cfg, ifp);
440305491d2cSKalle Valo
440405491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
440505491d2cSKalle Valo return err;
440605491d2cSKalle Valo }
440705491d2cSKalle Valo
440805491d2cSKalle Valo static s32
brcmf_cfg80211_del_pmksa(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_pmksa * pmksa)440905491d2cSKalle Valo brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *ndev,
441005491d2cSKalle Valo struct cfg80211_pmksa *pmksa)
441105491d2cSKalle Valo {
441205491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
441305491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
44146c404f34SHante Meuleman struct brcmf_pmksa *pmk = &cfg->pmk_list.pmk[0];
441516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
44166c404f34SHante Meuleman s32 err;
44176c404f34SHante Meuleman u32 npmk, i;
441805491d2cSKalle Valo
441905491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
442005491d2cSKalle Valo if (!check_vif_up(ifp->vif))
442105491d2cSKalle Valo return -EIO;
442205491d2cSKalle Valo
44237703773eSNicolas Iooss brcmf_dbg(CONN, "del_pmksa - PMK bssid = %pM\n", pmksa->bssid);
442405491d2cSKalle Valo
4425a96202acSHector Martin if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4426a96202acSHector Martin return brcmf_pmksa_v3_op(ifp, pmksa, false);
4427a96202acSHector Martin
4428a96202acSHector Martin /* TODO: implement PMKID_V2 */
4429a96202acSHector Martin
44306c404f34SHante Meuleman npmk = le32_to_cpu(cfg->pmk_list.npmk);
44316c404f34SHante Meuleman for (i = 0; i < npmk; i++)
44327703773eSNicolas Iooss if (!memcmp(pmksa->bssid, pmk[i].bssid, ETH_ALEN))
443305491d2cSKalle Valo break;
443405491d2cSKalle Valo
44356c404f34SHante Meuleman if ((npmk > 0) && (i < npmk)) {
44366c404f34SHante Meuleman for (; i < (npmk - 1); i++) {
44376c404f34SHante Meuleman memcpy(&pmk[i].bssid, &pmk[i + 1].bssid, ETH_ALEN);
44386c404f34SHante Meuleman memcpy(&pmk[i].pmkid, &pmk[i + 1].pmkid,
443905491d2cSKalle Valo WLAN_PMKID_LEN);
444005491d2cSKalle Valo }
44416c404f34SHante Meuleman memset(&pmk[i], 0, sizeof(*pmk));
44426c404f34SHante Meuleman cfg->pmk_list.npmk = cpu_to_le32(npmk - 1);
44436c404f34SHante Meuleman } else {
444416e64676SRafał Miłecki bphy_err(drvr, "Cache entry not found\n");
44456c404f34SHante Meuleman return -EINVAL;
44466c404f34SHante Meuleman }
444705491d2cSKalle Valo
44486c404f34SHante Meuleman err = brcmf_update_pmklist(cfg, ifp);
444905491d2cSKalle Valo
445005491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
445105491d2cSKalle Valo return err;
445205491d2cSKalle Valo
445305491d2cSKalle Valo }
445405491d2cSKalle Valo
445505491d2cSKalle Valo static s32
brcmf_cfg80211_flush_pmksa(struct wiphy * wiphy,struct net_device * ndev)445605491d2cSKalle Valo brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *ndev)
445705491d2cSKalle Valo {
445805491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
445905491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
44606c404f34SHante Meuleman s32 err;
446105491d2cSKalle Valo
446205491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
446305491d2cSKalle Valo if (!check_vif_up(ifp->vif))
446405491d2cSKalle Valo return -EIO;
446505491d2cSKalle Valo
4466a96202acSHector Martin if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PMKID_V3))
4467a96202acSHector Martin return brcmf_pmksa_v3_op(ifp, NULL, false);
4468a96202acSHector Martin
4469a96202acSHector Martin /* TODO: implement PMKID_V2 */
4470a96202acSHector Martin
44716c404f34SHante Meuleman memset(&cfg->pmk_list, 0, sizeof(cfg->pmk_list));
44726c404f34SHante Meuleman err = brcmf_update_pmklist(cfg, ifp);
447305491d2cSKalle Valo
447405491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
447505491d2cSKalle Valo return err;
447605491d2cSKalle Valo
447705491d2cSKalle Valo }
447805491d2cSKalle Valo
brcmf_configure_opensecurity(struct brcmf_if * ifp)447905491d2cSKalle Valo static s32 brcmf_configure_opensecurity(struct brcmf_if *ifp)
448005491d2cSKalle Valo {
448116e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
448205491d2cSKalle Valo s32 err;
4483fdfb0f94SWright Feng s32 wpa_val;
448405491d2cSKalle Valo
448505491d2cSKalle Valo /* set auth */
448605491d2cSKalle Valo err = brcmf_fil_bsscfg_int_set(ifp, "auth", 0);
448705491d2cSKalle Valo if (err < 0) {
448816e64676SRafał Miłecki bphy_err(drvr, "auth error %d\n", err);
448905491d2cSKalle Valo return err;
449005491d2cSKalle Valo }
449105491d2cSKalle Valo /* set wsec */
449205491d2cSKalle Valo err = brcmf_fil_bsscfg_int_set(ifp, "wsec", 0);
449305491d2cSKalle Valo if (err < 0) {
449416e64676SRafał Miłecki bphy_err(drvr, "wsec error %d\n", err);
449505491d2cSKalle Valo return err;
449605491d2cSKalle Valo }
449705491d2cSKalle Valo /* set upper-layer auth */
4498fdfb0f94SWright Feng if (brcmf_is_ibssmode(ifp->vif))
4499fdfb0f94SWright Feng wpa_val = WPA_AUTH_NONE;
4500fdfb0f94SWright Feng else
4501fdfb0f94SWright Feng wpa_val = WPA_AUTH_DISABLED;
4502fdfb0f94SWright Feng err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_val);
450305491d2cSKalle Valo if (err < 0) {
450416e64676SRafał Miłecki bphy_err(drvr, "wpa_auth error %d\n", err);
450505491d2cSKalle Valo return err;
450605491d2cSKalle Valo }
450705491d2cSKalle Valo
450805491d2cSKalle Valo return 0;
450905491d2cSKalle Valo }
451005491d2cSKalle Valo
brcmf_valid_wpa_oui(u8 * oui,bool is_rsn_ie)451105491d2cSKalle Valo static bool brcmf_valid_wpa_oui(u8 *oui, bool is_rsn_ie)
451205491d2cSKalle Valo {
451305491d2cSKalle Valo if (is_rsn_ie)
451405491d2cSKalle Valo return (memcmp(oui, RSN_OUI, TLV_OUI_LEN) == 0);
451505491d2cSKalle Valo
451605491d2cSKalle Valo return (memcmp(oui, WPA_OUI, TLV_OUI_LEN) == 0);
451705491d2cSKalle Valo }
451805491d2cSKalle Valo
451905491d2cSKalle Valo static s32
brcmf_configure_wpaie(struct brcmf_if * ifp,const struct brcmf_vs_tlv * wpa_ie,bool is_rsn_ie)452005491d2cSKalle Valo brcmf_configure_wpaie(struct brcmf_if *ifp,
452105491d2cSKalle Valo const struct brcmf_vs_tlv *wpa_ie,
452205491d2cSKalle Valo bool is_rsn_ie)
452305491d2cSKalle Valo {
452416e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
452505491d2cSKalle Valo u32 auth = 0; /* d11 open authentication */
452605491d2cSKalle Valo u16 count;
452705491d2cSKalle Valo s32 err = 0;
4528240d61a9SHante Meuleman s32 len;
452905491d2cSKalle Valo u32 i;
453005491d2cSKalle Valo u32 wsec;
453105491d2cSKalle Valo u32 pval = 0;
453205491d2cSKalle Valo u32 gval = 0;
453305491d2cSKalle Valo u32 wpa_auth = 0;
453405491d2cSKalle Valo u32 offset;
453505491d2cSKalle Valo u8 *data;
453605491d2cSKalle Valo u16 rsn_cap;
453705491d2cSKalle Valo u32 wme_bss_disable;
4538240d61a9SHante Meuleman u32 mfp;
453905491d2cSKalle Valo
454005491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
454105491d2cSKalle Valo if (wpa_ie == NULL)
454205491d2cSKalle Valo goto exit;
454305491d2cSKalle Valo
454405491d2cSKalle Valo len = wpa_ie->len + TLV_HDR_LEN;
454505491d2cSKalle Valo data = (u8 *)wpa_ie;
454605491d2cSKalle Valo offset = TLV_HDR_LEN;
454705491d2cSKalle Valo if (!is_rsn_ie)
454805491d2cSKalle Valo offset += VS_IE_FIXED_HDR_LEN;
454905491d2cSKalle Valo else
455005491d2cSKalle Valo offset += WPA_IE_VERSION_LEN;
455105491d2cSKalle Valo
455205491d2cSKalle Valo /* check for multicast cipher suite */
455305491d2cSKalle Valo if (offset + WPA_IE_MIN_OUI_LEN > len) {
455405491d2cSKalle Valo err = -EINVAL;
455516e64676SRafał Miłecki bphy_err(drvr, "no multicast cipher suite\n");
455605491d2cSKalle Valo goto exit;
455705491d2cSKalle Valo }
455805491d2cSKalle Valo
455905491d2cSKalle Valo if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
456005491d2cSKalle Valo err = -EINVAL;
456116e64676SRafał Miłecki bphy_err(drvr, "ivalid OUI\n");
456205491d2cSKalle Valo goto exit;
456305491d2cSKalle Valo }
456405491d2cSKalle Valo offset += TLV_OUI_LEN;
456505491d2cSKalle Valo
456605491d2cSKalle Valo /* pick up multicast cipher */
456705491d2cSKalle Valo switch (data[offset]) {
456805491d2cSKalle Valo case WPA_CIPHER_NONE:
456905491d2cSKalle Valo gval = 0;
457005491d2cSKalle Valo break;
457105491d2cSKalle Valo case WPA_CIPHER_WEP_40:
457205491d2cSKalle Valo case WPA_CIPHER_WEP_104:
457305491d2cSKalle Valo gval = WEP_ENABLED;
457405491d2cSKalle Valo break;
457505491d2cSKalle Valo case WPA_CIPHER_TKIP:
457605491d2cSKalle Valo gval = TKIP_ENABLED;
457705491d2cSKalle Valo break;
457805491d2cSKalle Valo case WPA_CIPHER_AES_CCM:
457905491d2cSKalle Valo gval = AES_ENABLED;
458005491d2cSKalle Valo break;
458105491d2cSKalle Valo default:
458205491d2cSKalle Valo err = -EINVAL;
458316e64676SRafał Miłecki bphy_err(drvr, "Invalid multi cast cipher info\n");
458405491d2cSKalle Valo goto exit;
458505491d2cSKalle Valo }
458605491d2cSKalle Valo
458705491d2cSKalle Valo offset++;
458805491d2cSKalle Valo /* walk thru unicast cipher list and pick up what we recognize */
458905491d2cSKalle Valo count = data[offset] + (data[offset + 1] << 8);
459005491d2cSKalle Valo offset += WPA_IE_SUITE_COUNT_LEN;
459105491d2cSKalle Valo /* Check for unicast suite(s) */
459205491d2cSKalle Valo if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
459305491d2cSKalle Valo err = -EINVAL;
459416e64676SRafał Miłecki bphy_err(drvr, "no unicast cipher suite\n");
459505491d2cSKalle Valo goto exit;
459605491d2cSKalle Valo }
459705491d2cSKalle Valo for (i = 0; i < count; i++) {
459805491d2cSKalle Valo if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
459905491d2cSKalle Valo err = -EINVAL;
460016e64676SRafał Miłecki bphy_err(drvr, "ivalid OUI\n");
460105491d2cSKalle Valo goto exit;
460205491d2cSKalle Valo }
460305491d2cSKalle Valo offset += TLV_OUI_LEN;
460405491d2cSKalle Valo switch (data[offset]) {
460505491d2cSKalle Valo case WPA_CIPHER_NONE:
460605491d2cSKalle Valo break;
460705491d2cSKalle Valo case WPA_CIPHER_WEP_40:
460805491d2cSKalle Valo case WPA_CIPHER_WEP_104:
460905491d2cSKalle Valo pval |= WEP_ENABLED;
461005491d2cSKalle Valo break;
461105491d2cSKalle Valo case WPA_CIPHER_TKIP:
461205491d2cSKalle Valo pval |= TKIP_ENABLED;
461305491d2cSKalle Valo break;
461405491d2cSKalle Valo case WPA_CIPHER_AES_CCM:
461505491d2cSKalle Valo pval |= AES_ENABLED;
461605491d2cSKalle Valo break;
461705491d2cSKalle Valo default:
461816e64676SRafał Miłecki bphy_err(drvr, "Invalid unicast security info\n");
461905491d2cSKalle Valo }
462005491d2cSKalle Valo offset++;
462105491d2cSKalle Valo }
462205491d2cSKalle Valo /* walk thru auth management suite list and pick up what we recognize */
462305491d2cSKalle Valo count = data[offset] + (data[offset + 1] << 8);
462405491d2cSKalle Valo offset += WPA_IE_SUITE_COUNT_LEN;
462505491d2cSKalle Valo /* Check for auth key management suite(s) */
462605491d2cSKalle Valo if (offset + (WPA_IE_MIN_OUI_LEN * count) > len) {
462705491d2cSKalle Valo err = -EINVAL;
462816e64676SRafał Miłecki bphy_err(drvr, "no auth key mgmt suite\n");
462905491d2cSKalle Valo goto exit;
463005491d2cSKalle Valo }
463105491d2cSKalle Valo for (i = 0; i < count; i++) {
463205491d2cSKalle Valo if (!brcmf_valid_wpa_oui(&data[offset], is_rsn_ie)) {
463305491d2cSKalle Valo err = -EINVAL;
463416e64676SRafał Miłecki bphy_err(drvr, "ivalid OUI\n");
463505491d2cSKalle Valo goto exit;
463605491d2cSKalle Valo }
463705491d2cSKalle Valo offset += TLV_OUI_LEN;
463805491d2cSKalle Valo switch (data[offset]) {
463905491d2cSKalle Valo case RSN_AKM_NONE:
464005491d2cSKalle Valo brcmf_dbg(TRACE, "RSN_AKM_NONE\n");
464105491d2cSKalle Valo wpa_auth |= WPA_AUTH_NONE;
464205491d2cSKalle Valo break;
464305491d2cSKalle Valo case RSN_AKM_UNSPECIFIED:
464405491d2cSKalle Valo brcmf_dbg(TRACE, "RSN_AKM_UNSPECIFIED\n");
464505491d2cSKalle Valo is_rsn_ie ? (wpa_auth |= WPA2_AUTH_UNSPECIFIED) :
464605491d2cSKalle Valo (wpa_auth |= WPA_AUTH_UNSPECIFIED);
464705491d2cSKalle Valo break;
464805491d2cSKalle Valo case RSN_AKM_PSK:
464905491d2cSKalle Valo brcmf_dbg(TRACE, "RSN_AKM_PSK\n");
465005491d2cSKalle Valo is_rsn_ie ? (wpa_auth |= WPA2_AUTH_PSK) :
465105491d2cSKalle Valo (wpa_auth |= WPA_AUTH_PSK);
465205491d2cSKalle Valo break;
4653240d61a9SHante Meuleman case RSN_AKM_SHA256_PSK:
4654240d61a9SHante Meuleman brcmf_dbg(TRACE, "RSN_AKM_MFP_PSK\n");
4655240d61a9SHante Meuleman wpa_auth |= WPA2_AUTH_PSK_SHA256;
4656240d61a9SHante Meuleman break;
4657240d61a9SHante Meuleman case RSN_AKM_SHA256_1X:
4658240d61a9SHante Meuleman brcmf_dbg(TRACE, "RSN_AKM_MFP_1X\n");
4659240d61a9SHante Meuleman wpa_auth |= WPA2_AUTH_1X_SHA256;
4660240d61a9SHante Meuleman break;
4661d5f59c96SChung-Hsien Hsu case RSN_AKM_SAE:
4662d5f59c96SChung-Hsien Hsu brcmf_dbg(TRACE, "RSN_AKM_SAE\n");
4663d5f59c96SChung-Hsien Hsu wpa_auth |= WPA3_AUTH_SAE_PSK;
4664d5f59c96SChung-Hsien Hsu break;
466505491d2cSKalle Valo default:
466616e64676SRafał Miłecki bphy_err(drvr, "Invalid key mgmt info\n");
466705491d2cSKalle Valo }
466805491d2cSKalle Valo offset++;
466905491d2cSKalle Valo }
467005491d2cSKalle Valo
4671240d61a9SHante Meuleman mfp = BRCMF_MFP_NONE;
467205491d2cSKalle Valo if (is_rsn_ie) {
467305491d2cSKalle Valo wme_bss_disable = 1;
467405491d2cSKalle Valo if ((offset + RSN_CAP_LEN) <= len) {
467505491d2cSKalle Valo rsn_cap = data[offset] + (data[offset + 1] << 8);
467605491d2cSKalle Valo if (rsn_cap & RSN_CAP_PTK_REPLAY_CNTR_MASK)
467705491d2cSKalle Valo wme_bss_disable = 0;
4678240d61a9SHante Meuleman if (rsn_cap & RSN_CAP_MFPR_MASK) {
4679240d61a9SHante Meuleman brcmf_dbg(TRACE, "MFP Required\n");
4680240d61a9SHante Meuleman mfp = BRCMF_MFP_REQUIRED;
4681240d61a9SHante Meuleman /* Firmware only supports mfp required in
4682d5f59c96SChung-Hsien Hsu * combination with WPA2_AUTH_PSK_SHA256,
4683d5f59c96SChung-Hsien Hsu * WPA2_AUTH_1X_SHA256, or WPA3_AUTH_SAE_PSK.
4684240d61a9SHante Meuleman */
4685240d61a9SHante Meuleman if (!(wpa_auth & (WPA2_AUTH_PSK_SHA256 |
4686d5f59c96SChung-Hsien Hsu WPA2_AUTH_1X_SHA256 |
4687d5f59c96SChung-Hsien Hsu WPA3_AUTH_SAE_PSK))) {
4688240d61a9SHante Meuleman err = -EINVAL;
4689240d61a9SHante Meuleman goto exit;
469005491d2cSKalle Valo }
4691240d61a9SHante Meuleman /* Firmware has requirement that WPA2_AUTH_PSK/
4692240d61a9SHante Meuleman * WPA2_AUTH_UNSPECIFIED be set, if SHA256 OUI
4693240d61a9SHante Meuleman * is to be included in the rsn ie.
4694240d61a9SHante Meuleman */
4695240d61a9SHante Meuleman if (wpa_auth & WPA2_AUTH_PSK_SHA256)
4696240d61a9SHante Meuleman wpa_auth |= WPA2_AUTH_PSK;
4697240d61a9SHante Meuleman else if (wpa_auth & WPA2_AUTH_1X_SHA256)
4698240d61a9SHante Meuleman wpa_auth |= WPA2_AUTH_UNSPECIFIED;
4699240d61a9SHante Meuleman } else if (rsn_cap & RSN_CAP_MFPC_MASK) {
4700240d61a9SHante Meuleman brcmf_dbg(TRACE, "MFP Capable\n");
4701240d61a9SHante Meuleman mfp = BRCMF_MFP_CAPABLE;
4702240d61a9SHante Meuleman }
4703240d61a9SHante Meuleman }
4704240d61a9SHante Meuleman offset += RSN_CAP_LEN;
470505491d2cSKalle Valo /* set wme_bss_disable to sync RSN Capabilities */
470605491d2cSKalle Valo err = brcmf_fil_bsscfg_int_set(ifp, "wme_bss_disable",
470705491d2cSKalle Valo wme_bss_disable);
470805491d2cSKalle Valo if (err < 0) {
470916e64676SRafał Miłecki bphy_err(drvr, "wme_bss_disable error %d\n", err);
471005491d2cSKalle Valo goto exit;
471105491d2cSKalle Valo }
4712240d61a9SHante Meuleman
4713240d61a9SHante Meuleman /* Skip PMKID cnt as it is know to be 0 for AP. */
4714240d61a9SHante Meuleman offset += RSN_PMKID_COUNT_LEN;
4715240d61a9SHante Meuleman
4716240d61a9SHante Meuleman /* See if there is BIP wpa suite left for MFP */
4717240d61a9SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP) &&
4718240d61a9SHante Meuleman ((offset + WPA_IE_MIN_OUI_LEN) <= len)) {
4719240d61a9SHante Meuleman err = brcmf_fil_bsscfg_data_set(ifp, "bip",
4720240d61a9SHante Meuleman &data[offset],
4721240d61a9SHante Meuleman WPA_IE_MIN_OUI_LEN);
4722240d61a9SHante Meuleman if (err < 0) {
472316e64676SRafał Miłecki bphy_err(drvr, "bip error %d\n", err);
4724240d61a9SHante Meuleman goto exit;
4725240d61a9SHante Meuleman }
4726240d61a9SHante Meuleman }
472705491d2cSKalle Valo }
472805491d2cSKalle Valo /* FOR WPS , set SES_OW_ENABLED */
472905491d2cSKalle Valo wsec = (pval | gval | SES_OW_ENABLED);
473005491d2cSKalle Valo
473105491d2cSKalle Valo /* set auth */
473205491d2cSKalle Valo err = brcmf_fil_bsscfg_int_set(ifp, "auth", auth);
473305491d2cSKalle Valo if (err < 0) {
473416e64676SRafał Miłecki bphy_err(drvr, "auth error %d\n", err);
473505491d2cSKalle Valo goto exit;
473605491d2cSKalle Valo }
473705491d2cSKalle Valo /* set wsec */
473805491d2cSKalle Valo err = brcmf_fil_bsscfg_int_set(ifp, "wsec", wsec);
473905491d2cSKalle Valo if (err < 0) {
474016e64676SRafał Miłecki bphy_err(drvr, "wsec error %d\n", err);
474105491d2cSKalle Valo goto exit;
474205491d2cSKalle Valo }
4743240d61a9SHante Meuleman /* Configure MFP, this needs to go after wsec otherwise the wsec command
4744240d61a9SHante Meuleman * will overwrite the values set by MFP
4745240d61a9SHante Meuleman */
4746240d61a9SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP)) {
4747240d61a9SHante Meuleman err = brcmf_fil_bsscfg_int_set(ifp, "mfp", mfp);
4748240d61a9SHante Meuleman if (err < 0) {
474916e64676SRafał Miłecki bphy_err(drvr, "mfp error %d\n", err);
4750240d61a9SHante Meuleman goto exit;
4751240d61a9SHante Meuleman }
4752240d61a9SHante Meuleman }
475305491d2cSKalle Valo /* set upper-layer auth */
475405491d2cSKalle Valo err = brcmf_fil_bsscfg_int_set(ifp, "wpa_auth", wpa_auth);
475505491d2cSKalle Valo if (err < 0) {
475616e64676SRafał Miłecki bphy_err(drvr, "wpa_auth error %d\n", err);
475705491d2cSKalle Valo goto exit;
475805491d2cSKalle Valo }
475905491d2cSKalle Valo
476005491d2cSKalle Valo exit:
476105491d2cSKalle Valo return err;
476205491d2cSKalle Valo }
476305491d2cSKalle Valo
476405491d2cSKalle Valo static s32
brcmf_parse_vndr_ies(const u8 * vndr_ie_buf,u32 vndr_ie_len,struct parsed_vndr_ies * vndr_ies)476505491d2cSKalle Valo brcmf_parse_vndr_ies(const u8 *vndr_ie_buf, u32 vndr_ie_len,
476605491d2cSKalle Valo struct parsed_vndr_ies *vndr_ies)
476705491d2cSKalle Valo {
476805491d2cSKalle Valo struct brcmf_vs_tlv *vndrie;
476905491d2cSKalle Valo struct brcmf_tlv *ie;
477005491d2cSKalle Valo struct parsed_vndr_ie_info *parsed_info;
477105491d2cSKalle Valo s32 remaining_len;
477205491d2cSKalle Valo
477305491d2cSKalle Valo remaining_len = (s32)vndr_ie_len;
477405491d2cSKalle Valo memset(vndr_ies, 0, sizeof(*vndr_ies));
477505491d2cSKalle Valo
477605491d2cSKalle Valo ie = (struct brcmf_tlv *)vndr_ie_buf;
477705491d2cSKalle Valo while (ie) {
477805491d2cSKalle Valo if (ie->id != WLAN_EID_VENDOR_SPECIFIC)
477905491d2cSKalle Valo goto next;
478005491d2cSKalle Valo vndrie = (struct brcmf_vs_tlv *)ie;
478105491d2cSKalle Valo /* len should be bigger than OUI length + one */
478205491d2cSKalle Valo if (vndrie->len < (VS_IE_FIXED_HDR_LEN - TLV_HDR_LEN + 1)) {
478305491d2cSKalle Valo brcmf_err("invalid vndr ie. length is too small %d\n",
478405491d2cSKalle Valo vndrie->len);
478505491d2cSKalle Valo goto next;
478605491d2cSKalle Valo }
478705491d2cSKalle Valo /* if wpa or wme ie, do not add ie */
478805491d2cSKalle Valo if (!memcmp(vndrie->oui, (u8 *)WPA_OUI, TLV_OUI_LEN) &&
478905491d2cSKalle Valo ((vndrie->oui_type == WPA_OUI_TYPE) ||
479005491d2cSKalle Valo (vndrie->oui_type == WME_OUI_TYPE))) {
479105491d2cSKalle Valo brcmf_dbg(TRACE, "Found WPA/WME oui. Do not add it\n");
479205491d2cSKalle Valo goto next;
479305491d2cSKalle Valo }
479405491d2cSKalle Valo
479505491d2cSKalle Valo parsed_info = &vndr_ies->ie_info[vndr_ies->count];
479605491d2cSKalle Valo
479705491d2cSKalle Valo /* save vndr ie information */
479805491d2cSKalle Valo parsed_info->ie_ptr = (char *)vndrie;
479905491d2cSKalle Valo parsed_info->ie_len = vndrie->len + TLV_HDR_LEN;
480005491d2cSKalle Valo memcpy(&parsed_info->vndrie, vndrie, sizeof(*vndrie));
480105491d2cSKalle Valo
480205491d2cSKalle Valo vndr_ies->count++;
480305491d2cSKalle Valo
48040e48b86dSAndy Shevchenko brcmf_dbg(TRACE, "** OUI %3ph, type 0x%02x\n",
48050e48b86dSAndy Shevchenko parsed_info->vndrie.oui,
480605491d2cSKalle Valo parsed_info->vndrie.oui_type);
480705491d2cSKalle Valo
480805491d2cSKalle Valo if (vndr_ies->count >= VNDR_IE_PARSE_LIMIT)
480905491d2cSKalle Valo break;
481005491d2cSKalle Valo next:
481105491d2cSKalle Valo remaining_len -= (ie->len + TLV_HDR_LEN);
481205491d2cSKalle Valo if (remaining_len <= TLV_HDR_LEN)
481305491d2cSKalle Valo ie = NULL;
481405491d2cSKalle Valo else
481505491d2cSKalle Valo ie = (struct brcmf_tlv *)(((u8 *)ie) + ie->len +
481605491d2cSKalle Valo TLV_HDR_LEN);
481705491d2cSKalle Valo }
481805491d2cSKalle Valo return 0;
481905491d2cSKalle Valo }
482005491d2cSKalle Valo
482105491d2cSKalle Valo static u32
brcmf_vndr_ie(u8 * iebuf,s32 pktflag,u8 * ie_ptr,u32 ie_len,s8 * add_del_cmd)482205491d2cSKalle Valo brcmf_vndr_ie(u8 *iebuf, s32 pktflag, u8 *ie_ptr, u32 ie_len, s8 *add_del_cmd)
482305491d2cSKalle Valo {
48245f42b382SXulin Sun strscpy(iebuf, add_del_cmd, VNDR_IE_CMD_LEN);
482505491d2cSKalle Valo
482605491d2cSKalle Valo put_unaligned_le32(1, &iebuf[VNDR_IE_COUNT_OFFSET]);
482705491d2cSKalle Valo
482805491d2cSKalle Valo put_unaligned_le32(pktflag, &iebuf[VNDR_IE_PKTFLAG_OFFSET]);
482905491d2cSKalle Valo
483005491d2cSKalle Valo memcpy(&iebuf[VNDR_IE_VSIE_OFFSET], ie_ptr, ie_len);
483105491d2cSKalle Valo
483205491d2cSKalle Valo return ie_len + VNDR_IE_HDR_SIZE;
483305491d2cSKalle Valo }
483405491d2cSKalle Valo
brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif * vif,s32 pktflag,const u8 * vndr_ie_buf,u32 vndr_ie_len)483505491d2cSKalle Valo s32 brcmf_vif_set_mgmt_ie(struct brcmf_cfg80211_vif *vif, s32 pktflag,
483605491d2cSKalle Valo const u8 *vndr_ie_buf, u32 vndr_ie_len)
483705491d2cSKalle Valo {
483816e64676SRafał Miłecki struct brcmf_pub *drvr;
483905491d2cSKalle Valo struct brcmf_if *ifp;
484005491d2cSKalle Valo struct vif_saved_ie *saved_ie;
484105491d2cSKalle Valo s32 err = 0;
484205491d2cSKalle Valo u8 *iovar_ie_buf;
484305491d2cSKalle Valo u8 *curr_ie_buf;
484405491d2cSKalle Valo u8 *mgmt_ie_buf = NULL;
484505491d2cSKalle Valo int mgmt_ie_buf_len;
484605491d2cSKalle Valo u32 *mgmt_ie_len;
484705491d2cSKalle Valo u32 del_add_ie_buf_len = 0;
484805491d2cSKalle Valo u32 total_ie_buf_len = 0;
484905491d2cSKalle Valo u32 parsed_ie_buf_len = 0;
485005491d2cSKalle Valo struct parsed_vndr_ies old_vndr_ies;
485105491d2cSKalle Valo struct parsed_vndr_ies new_vndr_ies;
485205491d2cSKalle Valo struct parsed_vndr_ie_info *vndrie_info;
485305491d2cSKalle Valo s32 i;
485405491d2cSKalle Valo u8 *ptr;
485505491d2cSKalle Valo int remained_buf_len;
485605491d2cSKalle Valo
485705491d2cSKalle Valo if (!vif)
485805491d2cSKalle Valo return -ENODEV;
485905491d2cSKalle Valo ifp = vif->ifp;
486016e64676SRafał Miłecki drvr = ifp->drvr;
486105491d2cSKalle Valo saved_ie = &vif->saved_ie;
486205491d2cSKalle Valo
486337a869ecSHante Meuleman brcmf_dbg(TRACE, "bsscfgidx %d, pktflag : 0x%02X\n", ifp->bsscfgidx,
486437a869ecSHante Meuleman pktflag);
486505491d2cSKalle Valo iovar_ie_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
486605491d2cSKalle Valo if (!iovar_ie_buf)
486705491d2cSKalle Valo return -ENOMEM;
486805491d2cSKalle Valo curr_ie_buf = iovar_ie_buf;
486905491d2cSKalle Valo switch (pktflag) {
487005491d2cSKalle Valo case BRCMF_VNDR_IE_PRBREQ_FLAG:
487105491d2cSKalle Valo mgmt_ie_buf = saved_ie->probe_req_ie;
487205491d2cSKalle Valo mgmt_ie_len = &saved_ie->probe_req_ie_len;
487305491d2cSKalle Valo mgmt_ie_buf_len = sizeof(saved_ie->probe_req_ie);
487405491d2cSKalle Valo break;
487505491d2cSKalle Valo case BRCMF_VNDR_IE_PRBRSP_FLAG:
487605491d2cSKalle Valo mgmt_ie_buf = saved_ie->probe_res_ie;
487705491d2cSKalle Valo mgmt_ie_len = &saved_ie->probe_res_ie_len;
487805491d2cSKalle Valo mgmt_ie_buf_len = sizeof(saved_ie->probe_res_ie);
487905491d2cSKalle Valo break;
488005491d2cSKalle Valo case BRCMF_VNDR_IE_BEACON_FLAG:
488105491d2cSKalle Valo mgmt_ie_buf = saved_ie->beacon_ie;
488205491d2cSKalle Valo mgmt_ie_len = &saved_ie->beacon_ie_len;
488305491d2cSKalle Valo mgmt_ie_buf_len = sizeof(saved_ie->beacon_ie);
488405491d2cSKalle Valo break;
488505491d2cSKalle Valo case BRCMF_VNDR_IE_ASSOCREQ_FLAG:
488605491d2cSKalle Valo mgmt_ie_buf = saved_ie->assoc_req_ie;
488705491d2cSKalle Valo mgmt_ie_len = &saved_ie->assoc_req_ie_len;
488805491d2cSKalle Valo mgmt_ie_buf_len = sizeof(saved_ie->assoc_req_ie);
488905491d2cSKalle Valo break;
48902719afcaSRyohei Kondo case BRCMF_VNDR_IE_ASSOCRSP_FLAG:
48912719afcaSRyohei Kondo mgmt_ie_buf = saved_ie->assoc_res_ie;
48922719afcaSRyohei Kondo mgmt_ie_len = &saved_ie->assoc_res_ie_len;
48932719afcaSRyohei Kondo mgmt_ie_buf_len = sizeof(saved_ie->assoc_res_ie);
48942719afcaSRyohei Kondo break;
489505491d2cSKalle Valo default:
489605491d2cSKalle Valo err = -EPERM;
489716e64676SRafał Miłecki bphy_err(drvr, "not suitable type\n");
489805491d2cSKalle Valo goto exit;
489905491d2cSKalle Valo }
490005491d2cSKalle Valo
490105491d2cSKalle Valo if (vndr_ie_len > mgmt_ie_buf_len) {
490205491d2cSKalle Valo err = -ENOMEM;
490316e64676SRafał Miłecki bphy_err(drvr, "extra IE size too big\n");
490405491d2cSKalle Valo goto exit;
490505491d2cSKalle Valo }
490605491d2cSKalle Valo
490705491d2cSKalle Valo /* parse and save new vndr_ie in curr_ie_buff before comparing it */
490805491d2cSKalle Valo if (vndr_ie_buf && vndr_ie_len && curr_ie_buf) {
490905491d2cSKalle Valo ptr = curr_ie_buf;
491005491d2cSKalle Valo brcmf_parse_vndr_ies(vndr_ie_buf, vndr_ie_len, &new_vndr_ies);
491105491d2cSKalle Valo for (i = 0; i < new_vndr_ies.count; i++) {
491205491d2cSKalle Valo vndrie_info = &new_vndr_ies.ie_info[i];
491305491d2cSKalle Valo memcpy(ptr + parsed_ie_buf_len, vndrie_info->ie_ptr,
491405491d2cSKalle Valo vndrie_info->ie_len);
491505491d2cSKalle Valo parsed_ie_buf_len += vndrie_info->ie_len;
491605491d2cSKalle Valo }
491705491d2cSKalle Valo }
491805491d2cSKalle Valo
491905491d2cSKalle Valo if (mgmt_ie_buf && *mgmt_ie_len) {
492005491d2cSKalle Valo if (parsed_ie_buf_len && (parsed_ie_buf_len == *mgmt_ie_len) &&
492105491d2cSKalle Valo (memcmp(mgmt_ie_buf, curr_ie_buf,
492205491d2cSKalle Valo parsed_ie_buf_len) == 0)) {
492305491d2cSKalle Valo brcmf_dbg(TRACE, "Previous mgmt IE equals to current IE\n");
492405491d2cSKalle Valo goto exit;
492505491d2cSKalle Valo }
492605491d2cSKalle Valo
492705491d2cSKalle Valo /* parse old vndr_ie */
492805491d2cSKalle Valo brcmf_parse_vndr_ies(mgmt_ie_buf, *mgmt_ie_len, &old_vndr_ies);
492905491d2cSKalle Valo
493005491d2cSKalle Valo /* make a command to delete old ie */
493105491d2cSKalle Valo for (i = 0; i < old_vndr_ies.count; i++) {
493205491d2cSKalle Valo vndrie_info = &old_vndr_ies.ie_info[i];
493305491d2cSKalle Valo
49340e48b86dSAndy Shevchenko brcmf_dbg(TRACE, "DEL ID : %d, Len: %d , OUI:%3ph\n",
493505491d2cSKalle Valo vndrie_info->vndrie.id,
493605491d2cSKalle Valo vndrie_info->vndrie.len,
49370e48b86dSAndy Shevchenko vndrie_info->vndrie.oui);
493805491d2cSKalle Valo
493905491d2cSKalle Valo del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
494005491d2cSKalle Valo vndrie_info->ie_ptr,
494105491d2cSKalle Valo vndrie_info->ie_len,
494205491d2cSKalle Valo "del");
494305491d2cSKalle Valo curr_ie_buf += del_add_ie_buf_len;
494405491d2cSKalle Valo total_ie_buf_len += del_add_ie_buf_len;
494505491d2cSKalle Valo }
494605491d2cSKalle Valo }
494705491d2cSKalle Valo
494805491d2cSKalle Valo *mgmt_ie_len = 0;
494905491d2cSKalle Valo /* Add if there is any extra IE */
495005491d2cSKalle Valo if (mgmt_ie_buf && parsed_ie_buf_len) {
495105491d2cSKalle Valo ptr = mgmt_ie_buf;
495205491d2cSKalle Valo
495305491d2cSKalle Valo remained_buf_len = mgmt_ie_buf_len;
495405491d2cSKalle Valo
495505491d2cSKalle Valo /* make a command to add new ie */
495605491d2cSKalle Valo for (i = 0; i < new_vndr_ies.count; i++) {
495705491d2cSKalle Valo vndrie_info = &new_vndr_ies.ie_info[i];
495805491d2cSKalle Valo
495905491d2cSKalle Valo /* verify remained buf size before copy data */
496005491d2cSKalle Valo if (remained_buf_len < (vndrie_info->vndrie.len +
496105491d2cSKalle Valo VNDR_IE_VSIE_OFFSET)) {
496216e64676SRafał Miłecki bphy_err(drvr, "no space in mgmt_ie_buf: len left %d",
496305491d2cSKalle Valo remained_buf_len);
496405491d2cSKalle Valo break;
496505491d2cSKalle Valo }
496605491d2cSKalle Valo remained_buf_len -= (vndrie_info->ie_len +
496705491d2cSKalle Valo VNDR_IE_VSIE_OFFSET);
496805491d2cSKalle Valo
49690e48b86dSAndy Shevchenko brcmf_dbg(TRACE, "ADDED ID : %d, Len: %d, OUI:%3ph\n",
497005491d2cSKalle Valo vndrie_info->vndrie.id,
497105491d2cSKalle Valo vndrie_info->vndrie.len,
49720e48b86dSAndy Shevchenko vndrie_info->vndrie.oui);
497305491d2cSKalle Valo
497405491d2cSKalle Valo del_add_ie_buf_len = brcmf_vndr_ie(curr_ie_buf, pktflag,
497505491d2cSKalle Valo vndrie_info->ie_ptr,
497605491d2cSKalle Valo vndrie_info->ie_len,
497705491d2cSKalle Valo "add");
497805491d2cSKalle Valo
497905491d2cSKalle Valo /* save the parsed IE in wl struct */
498005491d2cSKalle Valo memcpy(ptr + (*mgmt_ie_len), vndrie_info->ie_ptr,
498105491d2cSKalle Valo vndrie_info->ie_len);
498205491d2cSKalle Valo *mgmt_ie_len += vndrie_info->ie_len;
498305491d2cSKalle Valo
498405491d2cSKalle Valo curr_ie_buf += del_add_ie_buf_len;
498505491d2cSKalle Valo total_ie_buf_len += del_add_ie_buf_len;
498605491d2cSKalle Valo }
498705491d2cSKalle Valo }
498805491d2cSKalle Valo if (total_ie_buf_len) {
498905491d2cSKalle Valo err = brcmf_fil_bsscfg_data_set(ifp, "vndr_ie", iovar_ie_buf,
499005491d2cSKalle Valo total_ie_buf_len);
499105491d2cSKalle Valo if (err)
499216e64676SRafał Miłecki bphy_err(drvr, "vndr ie set error : %d\n", err);
499305491d2cSKalle Valo }
499405491d2cSKalle Valo
499505491d2cSKalle Valo exit:
499605491d2cSKalle Valo kfree(iovar_ie_buf);
499705491d2cSKalle Valo return err;
499805491d2cSKalle Valo }
499905491d2cSKalle Valo
brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif * vif)500005491d2cSKalle Valo s32 brcmf_vif_clear_mgmt_ies(struct brcmf_cfg80211_vif *vif)
500105491d2cSKalle Valo {
50022386f64cSColin Ian King static const s32 pktflags[] = {
500305491d2cSKalle Valo BRCMF_VNDR_IE_PRBREQ_FLAG,
500405491d2cSKalle Valo BRCMF_VNDR_IE_PRBRSP_FLAG,
500505491d2cSKalle Valo BRCMF_VNDR_IE_BEACON_FLAG
500605491d2cSKalle Valo };
500705491d2cSKalle Valo int i;
500805491d2cSKalle Valo
500905491d2cSKalle Valo for (i = 0; i < ARRAY_SIZE(pktflags); i++)
501005491d2cSKalle Valo brcmf_vif_set_mgmt_ie(vif, pktflags[i], NULL, 0);
501105491d2cSKalle Valo
501205491d2cSKalle Valo memset(&vif->saved_ie, 0, sizeof(vif->saved_ie));
501305491d2cSKalle Valo return 0;
501405491d2cSKalle Valo }
501505491d2cSKalle Valo
501605491d2cSKalle Valo static s32
brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif * vif,struct cfg80211_beacon_data * beacon)501705491d2cSKalle Valo brcmf_config_ap_mgmt_ie(struct brcmf_cfg80211_vif *vif,
501805491d2cSKalle Valo struct cfg80211_beacon_data *beacon)
501905491d2cSKalle Valo {
502016e64676SRafał Miłecki struct brcmf_pub *drvr = vif->ifp->drvr;
502105491d2cSKalle Valo s32 err;
502205491d2cSKalle Valo
502305491d2cSKalle Valo /* Set Beacon IEs to FW */
502405491d2cSKalle Valo err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_BEACON_FLAG,
502505491d2cSKalle Valo beacon->tail, beacon->tail_len);
502605491d2cSKalle Valo if (err) {
502716e64676SRafał Miłecki bphy_err(drvr, "Set Beacon IE Failed\n");
502805491d2cSKalle Valo return err;
502905491d2cSKalle Valo }
503005491d2cSKalle Valo brcmf_dbg(TRACE, "Applied Vndr IEs for Beacon\n");
503105491d2cSKalle Valo
503205491d2cSKalle Valo /* Set Probe Response IEs to FW */
503305491d2cSKalle Valo err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_PRBRSP_FLAG,
503405491d2cSKalle Valo beacon->proberesp_ies,
503505491d2cSKalle Valo beacon->proberesp_ies_len);
503605491d2cSKalle Valo if (err)
503716e64676SRafał Miłecki bphy_err(drvr, "Set Probe Resp IE Failed\n");
503805491d2cSKalle Valo else
503905491d2cSKalle Valo brcmf_dbg(TRACE, "Applied Vndr IEs for Probe Resp\n");
504005491d2cSKalle Valo
50412719afcaSRyohei Kondo /* Set Assoc Response IEs to FW */
50422719afcaSRyohei Kondo err = brcmf_vif_set_mgmt_ie(vif, BRCMF_VNDR_IE_ASSOCRSP_FLAG,
50432719afcaSRyohei Kondo beacon->assocresp_ies,
50442719afcaSRyohei Kondo beacon->assocresp_ies_len);
50452719afcaSRyohei Kondo if (err)
50462719afcaSRyohei Kondo brcmf_err("Set Assoc Resp IE Failed\n");
50472719afcaSRyohei Kondo else
50482719afcaSRyohei Kondo brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc Resp\n");
50492719afcaSRyohei Kondo
505005491d2cSKalle Valo return err;
505105491d2cSKalle Valo }
505205491d2cSKalle Valo
505305491d2cSKalle Valo static s32
brcmf_parse_configure_security(struct brcmf_if * ifp,struct cfg80211_ap_settings * settings,enum nl80211_iftype dev_role)5054b46f1546SJia-Shyr Chuang brcmf_parse_configure_security(struct brcmf_if *ifp,
5055b46f1546SJia-Shyr Chuang struct cfg80211_ap_settings *settings,
5056b46f1546SJia-Shyr Chuang enum nl80211_iftype dev_role)
5057b46f1546SJia-Shyr Chuang {
5058b46f1546SJia-Shyr Chuang const struct brcmf_tlv *rsn_ie;
5059b46f1546SJia-Shyr Chuang const struct brcmf_vs_tlv *wpa_ie;
5060b46f1546SJia-Shyr Chuang s32 err = 0;
5061b46f1546SJia-Shyr Chuang
5062b46f1546SJia-Shyr Chuang /* find the RSN_IE */
5063b46f1546SJia-Shyr Chuang rsn_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
5064b46f1546SJia-Shyr Chuang settings->beacon.tail_len, WLAN_EID_RSN);
5065b46f1546SJia-Shyr Chuang
5066b46f1546SJia-Shyr Chuang /* find the WPA_IE */
5067b46f1546SJia-Shyr Chuang wpa_ie = brcmf_find_wpaie((u8 *)settings->beacon.tail,
5068b46f1546SJia-Shyr Chuang settings->beacon.tail_len);
5069b46f1546SJia-Shyr Chuang
5070b46f1546SJia-Shyr Chuang if (wpa_ie || rsn_ie) {
5071b46f1546SJia-Shyr Chuang brcmf_dbg(TRACE, "WPA(2) IE is found\n");
5072b46f1546SJia-Shyr Chuang if (wpa_ie) {
5073b46f1546SJia-Shyr Chuang /* WPA IE */
5074b46f1546SJia-Shyr Chuang err = brcmf_configure_wpaie(ifp, wpa_ie, false);
5075b46f1546SJia-Shyr Chuang if (err < 0)
5076b46f1546SJia-Shyr Chuang return err;
5077b46f1546SJia-Shyr Chuang } else {
5078b46f1546SJia-Shyr Chuang struct brcmf_vs_tlv *tmp_ie;
5079b46f1546SJia-Shyr Chuang
5080b46f1546SJia-Shyr Chuang tmp_ie = (struct brcmf_vs_tlv *)rsn_ie;
5081b46f1546SJia-Shyr Chuang
5082b46f1546SJia-Shyr Chuang /* RSN IE */
5083b46f1546SJia-Shyr Chuang err = brcmf_configure_wpaie(ifp, tmp_ie, true);
5084b46f1546SJia-Shyr Chuang if (err < 0)
5085b46f1546SJia-Shyr Chuang return err;
5086b46f1546SJia-Shyr Chuang }
5087b46f1546SJia-Shyr Chuang } else {
5088b46f1546SJia-Shyr Chuang brcmf_dbg(TRACE, "No WPA(2) IEs found\n");
5089b46f1546SJia-Shyr Chuang brcmf_configure_opensecurity(ifp);
5090b46f1546SJia-Shyr Chuang }
5091b46f1546SJia-Shyr Chuang
5092b46f1546SJia-Shyr Chuang return err;
5093b46f1546SJia-Shyr Chuang }
5094b46f1546SJia-Shyr Chuang
5095b46f1546SJia-Shyr Chuang static s32
brcmf_cfg80211_start_ap(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_ap_settings * settings)509605491d2cSKalle Valo brcmf_cfg80211_start_ap(struct wiphy *wiphy, struct net_device *ndev,
509705491d2cSKalle Valo struct cfg80211_ap_settings *settings)
509805491d2cSKalle Valo {
509905491d2cSKalle Valo s32 ie_offset;
510005491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
510105491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
510216e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
5103787fb926SChung-Hsien Hsu struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
5104787fb926SChung-Hsien Hsu struct cfg80211_crypto_settings *crypto = &settings->crypto;
510505491d2cSKalle Valo const struct brcmf_tlv *ssid_ie;
510605491d2cSKalle Valo const struct brcmf_tlv *country_ie;
510705491d2cSKalle Valo struct brcmf_ssid_le ssid_le;
510805491d2cSKalle Valo s32 err = -EPERM;
510905491d2cSKalle Valo struct brcmf_join_params join_params;
511005491d2cSKalle Valo enum nl80211_iftype dev_role;
511105491d2cSKalle Valo struct brcmf_fil_bss_enable_le bss_enable;
51128707e08dSRafał Miłecki u16 chanspec = chandef_to_chanspec(&cfg->d11inf, &settings->chandef);
511305491d2cSKalle Valo bool mbss;
511405491d2cSKalle Valo int is_11d;
5115b3589dfeSHante Meuleman bool supports_11d;
511605491d2cSKalle Valo
511705491d2cSKalle Valo brcmf_dbg(TRACE, "ctrlchn=%d, center=%d, bw=%d, beacon_interval=%d, dtim_period=%d,\n",
511805491d2cSKalle Valo settings->chandef.chan->hw_value,
511905491d2cSKalle Valo settings->chandef.center_freq1, settings->chandef.width,
512005491d2cSKalle Valo settings->beacon_interval, settings->dtim_period);
512105491d2cSKalle Valo brcmf_dbg(TRACE, "ssid=%s(%zu), auth_type=%d, inactivity_timeout=%d\n",
512205491d2cSKalle Valo settings->ssid, settings->ssid_len, settings->auth_type,
512305491d2cSKalle Valo settings->inactivity_timeout);
512405491d2cSKalle Valo dev_role = ifp->vif->wdev.iftype;
512505491d2cSKalle Valo mbss = ifp->vif->mbss;
512605491d2cSKalle Valo
512705491d2cSKalle Valo /* store current 11d setting */
5128b3589dfeSHante Meuleman if (brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_REGULATORY,
5129b3589dfeSHante Meuleman &ifp->vif->is_11d)) {
5130d3532ea6SArnd Bergmann is_11d = supports_11d = false;
5131b3589dfeSHante Meuleman } else {
513205491d2cSKalle Valo country_ie = brcmf_parse_tlvs((u8 *)settings->beacon.tail,
513305491d2cSKalle Valo settings->beacon.tail_len,
513405491d2cSKalle Valo WLAN_EID_COUNTRY);
513505491d2cSKalle Valo is_11d = country_ie ? 1 : 0;
5136b3589dfeSHante Meuleman supports_11d = true;
5137b3589dfeSHante Meuleman }
513805491d2cSKalle Valo
513905491d2cSKalle Valo memset(&ssid_le, 0, sizeof(ssid_le));
514005491d2cSKalle Valo if (settings->ssid == NULL || settings->ssid_len == 0) {
514105491d2cSKalle Valo ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
514205491d2cSKalle Valo ssid_ie = brcmf_parse_tlvs(
514305491d2cSKalle Valo (u8 *)&settings->beacon.head[ie_offset],
514405491d2cSKalle Valo settings->beacon.head_len - ie_offset,
514505491d2cSKalle Valo WLAN_EID_SSID);
5146ded89912SArend Van Spriel if (!ssid_ie || ssid_ie->len > IEEE80211_MAX_SSID_LEN)
514705491d2cSKalle Valo return -EINVAL;
514805491d2cSKalle Valo
514905491d2cSKalle Valo memcpy(ssid_le.SSID, ssid_ie->data, ssid_ie->len);
515005491d2cSKalle Valo ssid_le.SSID_len = cpu_to_le32(ssid_ie->len);
515105491d2cSKalle Valo brcmf_dbg(TRACE, "SSID is (%s) in Head\n", ssid_le.SSID);
515205491d2cSKalle Valo } else {
515305491d2cSKalle Valo memcpy(ssid_le.SSID, settings->ssid, settings->ssid_len);
515405491d2cSKalle Valo ssid_le.SSID_len = cpu_to_le32((u32)settings->ssid_len);
515505491d2cSKalle Valo }
515605491d2cSKalle Valo
515705491d2cSKalle Valo if (!mbss) {
515805491d2cSKalle Valo brcmf_set_mpc(ifp, 0);
515952f22fb2SFranky Lin brcmf_configure_arp_nd_offload(ifp, false);
516005491d2cSKalle Valo }
516105491d2cSKalle Valo
51628707e08dSRafał Miłecki /* Parameters shared by all radio interfaces */
516305491d2cSKalle Valo if (!mbss) {
5164b3589dfeSHante Meuleman if ((supports_11d) && (is_11d != ifp->vif->is_11d)) {
516505491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
516605491d2cSKalle Valo is_11d);
516705491d2cSKalle Valo if (err < 0) {
516816e64676SRafał Miłecki bphy_err(drvr, "Regulatory Set Error, %d\n",
51693ef005b8SRafał Miłecki err);
517005491d2cSKalle Valo goto exit;
517105491d2cSKalle Valo }
517205491d2cSKalle Valo }
517305491d2cSKalle Valo if (settings->beacon_interval) {
517405491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_BCNPRD,
517505491d2cSKalle Valo settings->beacon_interval);
517605491d2cSKalle Valo if (err < 0) {
517716e64676SRafał Miłecki bphy_err(drvr, "Beacon Interval Set Error, %d\n",
517805491d2cSKalle Valo err);
517905491d2cSKalle Valo goto exit;
518005491d2cSKalle Valo }
518105491d2cSKalle Valo }
518205491d2cSKalle Valo if (settings->dtim_period) {
518305491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_DTIMPRD,
518405491d2cSKalle Valo settings->dtim_period);
518505491d2cSKalle Valo if (err < 0) {
518616e64676SRafał Miłecki bphy_err(drvr, "DTIM Interval Set Error, %d\n",
51873ef005b8SRafał Miłecki err);
518805491d2cSKalle Valo goto exit;
518905491d2cSKalle Valo }
519005491d2cSKalle Valo }
519105491d2cSKalle Valo
51928abffd81SHante Meuleman if ((dev_role == NL80211_IFTYPE_AP) &&
51938abffd81SHante Meuleman ((ifp->ifidx == 0) ||
5194774965f2SWright Feng (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB) &&
5195774965f2SWright Feng !brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN)))) {
519605491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
519705491d2cSKalle Valo if (err < 0) {
519816e64676SRafał Miłecki bphy_err(drvr, "BRCMF_C_DOWN error %d\n",
51993ef005b8SRafał Miłecki err);
520005491d2cSKalle Valo goto exit;
520105491d2cSKalle Valo }
520205491d2cSKalle Valo brcmf_fil_iovar_int_set(ifp, "apsta", 0);
520305491d2cSKalle Valo }
520405491d2cSKalle Valo
520505491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_INFRA, 1);
520605491d2cSKalle Valo if (err < 0) {
520716e64676SRafał Miłecki bphy_err(drvr, "SET INFRA error %d\n", err);
520805491d2cSKalle Valo goto exit;
520905491d2cSKalle Valo }
5210b3589dfeSHante Meuleman } else if (WARN_ON(supports_11d && (is_11d != ifp->vif->is_11d))) {
521105491d2cSKalle Valo /* Multiple-BSS should use same 11d configuration */
521205491d2cSKalle Valo err = -EINVAL;
521305491d2cSKalle Valo goto exit;
521405491d2cSKalle Valo }
52158707e08dSRafał Miłecki
52168707e08dSRafał Miłecki /* Interface specific setup */
521705491d2cSKalle Valo if (dev_role == NL80211_IFTYPE_AP) {
521805491d2cSKalle Valo if ((brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) && (!mbss))
521905491d2cSKalle Valo brcmf_fil_iovar_int_set(ifp, "mbss", 1);
522005491d2cSKalle Valo
522105491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 1);
522205491d2cSKalle Valo if (err < 0) {
522316e64676SRafał Miłecki bphy_err(drvr, "setting AP mode failed %d\n",
52243ef005b8SRafał Miłecki err);
522505491d2cSKalle Valo goto exit;
522605491d2cSKalle Valo }
52278707e08dSRafał Miłecki if (!mbss) {
52288707e08dSRafał Miłecki /* Firmware 10.x requires setting channel after enabling
52298707e08dSRafał Miłecki * AP and before bringing interface up.
52308707e08dSRafał Miłecki */
52318707e08dSRafał Miłecki err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
52328707e08dSRafał Miłecki if (err < 0) {
523316e64676SRafał Miłecki bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",
52348707e08dSRafał Miłecki chanspec, err);
52358707e08dSRafał Miłecki goto exit;
52368707e08dSRafał Miłecki }
52378707e08dSRafał Miłecki }
523805491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
523905491d2cSKalle Valo if (err < 0) {
524016e64676SRafał Miłecki bphy_err(drvr, "BRCMF_C_UP error (%d)\n", err);
524105491d2cSKalle Valo goto exit;
524205491d2cSKalle Valo }
5243b46f1546SJia-Shyr Chuang
5244787fb926SChung-Hsien Hsu if (crypto->psk) {
5245787fb926SChung-Hsien Hsu brcmf_dbg(INFO, "using PSK offload\n");
5246787fb926SChung-Hsien Hsu profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_PSK);
5247787fb926SChung-Hsien Hsu err = brcmf_set_pmk(ifp, crypto->psk,
5248787fb926SChung-Hsien Hsu BRCMF_WSEC_MAX_PSK_LEN);
5249787fb926SChung-Hsien Hsu if (err < 0)
5250787fb926SChung-Hsien Hsu goto exit;
5251787fb926SChung-Hsien Hsu }
5252d5f59c96SChung-Hsien Hsu if (crypto->sae_pwd) {
5253d5f59c96SChung-Hsien Hsu brcmf_dbg(INFO, "using SAE offload\n");
5254d5f59c96SChung-Hsien Hsu profile->use_fwauth |= BIT(BRCMF_PROFILE_FWAUTH_SAE);
52558d59a64cSHector Martin err = brcmf_fwvid_set_sae_password(ifp, crypto);
5256d5f59c96SChung-Hsien Hsu if (err < 0)
5257d5f59c96SChung-Hsien Hsu goto exit;
5258d5f59c96SChung-Hsien Hsu }
5259787fb926SChung-Hsien Hsu if (profile->use_fwauth == 0)
5260787fb926SChung-Hsien Hsu profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
5261787fb926SChung-Hsien Hsu
5262b46f1546SJia-Shyr Chuang err = brcmf_parse_configure_security(ifp, settings,
5263b46f1546SJia-Shyr Chuang NL80211_IFTYPE_AP);
5264b46f1546SJia-Shyr Chuang if (err < 0) {
5265b46f1546SJia-Shyr Chuang bphy_err(drvr, "brcmf_parse_configure_security error\n");
5266b46f1546SJia-Shyr Chuang goto exit;
5267b46f1546SJia-Shyr Chuang }
5268b46f1546SJia-Shyr Chuang
526905491d2cSKalle Valo /* On DOWN the firmware removes the WEP keys, reconfigure
527005491d2cSKalle Valo * them if they were set.
527105491d2cSKalle Valo */
527205491d2cSKalle Valo brcmf_cfg80211_reconfigure_wep(ifp);
527305491d2cSKalle Valo
527405491d2cSKalle Valo memset(&join_params, 0, sizeof(join_params));
527505491d2cSKalle Valo /* join parameters starts with ssid */
527605491d2cSKalle Valo memcpy(&join_params.ssid_le, &ssid_le, sizeof(ssid_le));
527705491d2cSKalle Valo /* create softap */
527805491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
527905491d2cSKalle Valo &join_params, sizeof(join_params));
528005491d2cSKalle Valo if (err < 0) {
528116e64676SRafał Miłecki bphy_err(drvr, "SET SSID error (%d)\n", err);
528205491d2cSKalle Valo goto exit;
528305491d2cSKalle Valo }
5284c940de10SRafał Miłecki
5285d9429d03SSoontak Lee err = brcmf_fil_iovar_int_set(ifp, "closednet",
5286d9429d03SSoontak Lee settings->hidden_ssid);
5287c940de10SRafał Miłecki if (err) {
5288d9429d03SSoontak Lee bphy_err(drvr, "%s closednet error (%d)\n",
5289d9429d03SSoontak Lee settings->hidden_ssid ?
5290d9429d03SSoontak Lee "enabled" : "disabled",
5291d9429d03SSoontak Lee err);
5292c940de10SRafał Miłecki goto exit;
5293c940de10SRafał Miłecki }
5294c940de10SRafał Miłecki
529505491d2cSKalle Valo brcmf_dbg(TRACE, "AP mode configuration complete\n");
52968707e08dSRafał Miłecki } else if (dev_role == NL80211_IFTYPE_P2P_GO) {
52978707e08dSRafał Miłecki err = brcmf_fil_iovar_int_set(ifp, "chanspec", chanspec);
52988707e08dSRafał Miłecki if (err < 0) {
529916e64676SRafał Miłecki bphy_err(drvr, "Set Channel failed: chspec=%d, %d\n",
53008707e08dSRafał Miłecki chanspec, err);
53018707e08dSRafał Miłecki goto exit;
53028707e08dSRafał Miłecki }
5303b46f1546SJia-Shyr Chuang
5304b46f1546SJia-Shyr Chuang err = brcmf_parse_configure_security(ifp, settings,
5305b46f1546SJia-Shyr Chuang NL80211_IFTYPE_P2P_GO);
5306b46f1546SJia-Shyr Chuang if (err < 0) {
5307b46f1546SJia-Shyr Chuang brcmf_err("brcmf_parse_configure_security error\n");
5308b46f1546SJia-Shyr Chuang goto exit;
5309b46f1546SJia-Shyr Chuang }
5310b46f1546SJia-Shyr Chuang
531105491d2cSKalle Valo err = brcmf_fil_bsscfg_data_set(ifp, "ssid", &ssid_le,
531205491d2cSKalle Valo sizeof(ssid_le));
531305491d2cSKalle Valo if (err < 0) {
531416e64676SRafał Miłecki bphy_err(drvr, "setting ssid failed %d\n", err);
531505491d2cSKalle Valo goto exit;
531605491d2cSKalle Valo }
531737a869ecSHante Meuleman bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
531805491d2cSKalle Valo bss_enable.enable = cpu_to_le32(1);
531905491d2cSKalle Valo err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
532005491d2cSKalle Valo sizeof(bss_enable));
532105491d2cSKalle Valo if (err < 0) {
532216e64676SRafał Miłecki bphy_err(drvr, "bss_enable config failed %d\n", err);
532305491d2cSKalle Valo goto exit;
532405491d2cSKalle Valo }
532505491d2cSKalle Valo
532605491d2cSKalle Valo brcmf_dbg(TRACE, "GO mode configuration complete\n");
53278707e08dSRafał Miłecki } else {
53288707e08dSRafał Miłecki WARN_ON(1);
532905491d2cSKalle Valo }
53308707e08dSRafał Miłecki
5331f25ba69cSWright Feng brcmf_config_ap_mgmt_ie(ifp->vif, &settings->beacon);
533205491d2cSKalle Valo set_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
533305491d2cSKalle Valo brcmf_net_setcarrier(ifp, true);
533405491d2cSKalle Valo
533505491d2cSKalle Valo exit:
533605491d2cSKalle Valo if ((err) && (!mbss)) {
533705491d2cSKalle Valo brcmf_set_mpc(ifp, 1);
533852f22fb2SFranky Lin brcmf_configure_arp_nd_offload(ifp, true);
533905491d2cSKalle Valo }
534005491d2cSKalle Valo return err;
534105491d2cSKalle Valo }
534205491d2cSKalle Valo
brcmf_cfg80211_stop_ap(struct wiphy * wiphy,struct net_device * ndev,unsigned int link_id)53437b0a0e3cSJohannes Berg static int brcmf_cfg80211_stop_ap(struct wiphy *wiphy, struct net_device *ndev,
53447b0a0e3cSJohannes Berg unsigned int link_id)
534505491d2cSKalle Valo {
534616e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
534705491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
534816e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
5349787fb926SChung-Hsien Hsu struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
535005491d2cSKalle Valo s32 err;
535105491d2cSKalle Valo struct brcmf_fil_bss_enable_le bss_enable;
535205491d2cSKalle Valo struct brcmf_join_params join_params;
535305491d2cSKalle Valo
535405491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
535505491d2cSKalle Valo
535605491d2cSKalle Valo if (ifp->vif->wdev.iftype == NL80211_IFTYPE_AP) {
535705491d2cSKalle Valo /* Due to most likely deauths outstanding we sleep */
535805491d2cSKalle Valo /* first to make sure they get processed by fw. */
535905491d2cSKalle Valo msleep(400);
536005491d2cSKalle Valo
5361787fb926SChung-Hsien Hsu if (profile->use_fwauth != BIT(BRCMF_PROFILE_FWAUTH_NONE)) {
53628d59a64cSHector Martin struct cfg80211_crypto_settings crypto = {};
53638d59a64cSHector Martin
5364787fb926SChung-Hsien Hsu if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_PSK))
5365787fb926SChung-Hsien Hsu brcmf_set_pmk(ifp, NULL, 0);
5366d5f59c96SChung-Hsien Hsu if (profile->use_fwauth & BIT(BRCMF_PROFILE_FWAUTH_SAE))
53678d59a64cSHector Martin brcmf_fwvid_set_sae_password(ifp, &crypto);
5368787fb926SChung-Hsien Hsu profile->use_fwauth = BIT(BRCMF_PROFILE_FWAUTH_NONE);
5369787fb926SChung-Hsien Hsu }
5370787fb926SChung-Hsien Hsu
537105491d2cSKalle Valo if (ifp->vif->mbss) {
537205491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
537305491d2cSKalle Valo return err;
537405491d2cSKalle Valo }
537505491d2cSKalle Valo
5376c940de10SRafał Miłecki /* First BSS doesn't get a full reset */
5377c940de10SRafał Miłecki if (ifp->bsscfgidx == 0)
5378c940de10SRafał Miłecki brcmf_fil_iovar_int_set(ifp, "closednet", 0);
5379c940de10SRafał Miłecki
538005491d2cSKalle Valo memset(&join_params, 0, sizeof(join_params));
538105491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SSID,
538205491d2cSKalle Valo &join_params, sizeof(join_params));
538305491d2cSKalle Valo if (err < 0)
538416e64676SRafał Miłecki bphy_err(drvr, "SET SSID error (%d)\n", err);
538505491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_DOWN, 1);
538605491d2cSKalle Valo if (err < 0)
538716e64676SRafał Miłecki bphy_err(drvr, "BRCMF_C_DOWN error %d\n", err);
538805491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_AP, 0);
538905491d2cSKalle Valo if (err < 0)
539016e64676SRafał Miłecki bphy_err(drvr, "setting AP mode failed %d\n", err);
539105491d2cSKalle Valo if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS))
539205491d2cSKalle Valo brcmf_fil_iovar_int_set(ifp, "mbss", 0);
5393b3589dfeSHante Meuleman brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_REGULATORY,
539405491d2cSKalle Valo ifp->vif->is_11d);
539505491d2cSKalle Valo /* Bring device back up so it can be used again */
539605491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
539705491d2cSKalle Valo if (err < 0)
539816e64676SRafał Miłecki bphy_err(drvr, "BRCMF_C_UP error %d\n", err);
5399f25ba69cSWright Feng
5400f25ba69cSWright Feng brcmf_vif_clear_mgmt_ies(ifp->vif);
540105491d2cSKalle Valo } else {
540237a869ecSHante Meuleman bss_enable.bsscfgidx = cpu_to_le32(ifp->bsscfgidx);
540305491d2cSKalle Valo bss_enable.enable = cpu_to_le32(0);
540405491d2cSKalle Valo err = brcmf_fil_iovar_data_set(ifp, "bss", &bss_enable,
540505491d2cSKalle Valo sizeof(bss_enable));
540605491d2cSKalle Valo if (err < 0)
540716e64676SRafał Miłecki bphy_err(drvr, "bss_enable config failed %d\n", err);
540805491d2cSKalle Valo }
540905491d2cSKalle Valo brcmf_set_mpc(ifp, 1);
541052f22fb2SFranky Lin brcmf_configure_arp_nd_offload(ifp, true);
541105491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_AP_CREATED, &ifp->vif->sme_state);
541205491d2cSKalle Valo brcmf_net_setcarrier(ifp, false);
541305491d2cSKalle Valo
541405491d2cSKalle Valo return err;
541505491d2cSKalle Valo }
541605491d2cSKalle Valo
541705491d2cSKalle Valo static s32
brcmf_cfg80211_change_beacon(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_beacon_data * info)541805491d2cSKalle Valo brcmf_cfg80211_change_beacon(struct wiphy *wiphy, struct net_device *ndev,
541905491d2cSKalle Valo struct cfg80211_beacon_data *info)
542005491d2cSKalle Valo {
542105491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
542205491d2cSKalle Valo
542305491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
542405491d2cSKalle Valo
5425e56a7708SJinpeng Cui return brcmf_config_ap_mgmt_ie(ifp->vif, info);
542605491d2cSKalle Valo }
542705491d2cSKalle Valo
542805491d2cSKalle Valo static int
brcmf_cfg80211_del_station(struct wiphy * wiphy,struct net_device * ndev,struct station_del_parameters * params)542905491d2cSKalle Valo brcmf_cfg80211_del_station(struct wiphy *wiphy, struct net_device *ndev,
543005491d2cSKalle Valo struct station_del_parameters *params)
543105491d2cSKalle Valo {
543205491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
543316e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
543405491d2cSKalle Valo struct brcmf_scb_val_le scbval;
543505491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
543605491d2cSKalle Valo s32 err;
543705491d2cSKalle Valo
543805491d2cSKalle Valo if (!params->mac)
543905491d2cSKalle Valo return -EFAULT;
544005491d2cSKalle Valo
544105491d2cSKalle Valo brcmf_dbg(TRACE, "Enter %pM\n", params->mac);
544205491d2cSKalle Valo
544305491d2cSKalle Valo if (ifp->vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif)
544405491d2cSKalle Valo ifp = cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif->ifp;
544505491d2cSKalle Valo if (!check_vif_up(ifp->vif))
544605491d2cSKalle Valo return -EIO;
544705491d2cSKalle Valo
544805491d2cSKalle Valo memcpy(&scbval.ea, params->mac, ETH_ALEN);
544905491d2cSKalle Valo scbval.val = cpu_to_le32(params->reason_code);
545005491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON,
545105491d2cSKalle Valo &scbval, sizeof(scbval));
545205491d2cSKalle Valo if (err)
545316e64676SRafał Miłecki bphy_err(drvr, "SCB_DEAUTHENTICATE_FOR_REASON failed %d\n",
54543ef005b8SRafał Miłecki err);
545505491d2cSKalle Valo
545605491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
545705491d2cSKalle Valo return err;
545805491d2cSKalle Valo }
545905491d2cSKalle Valo
546005491d2cSKalle Valo static int
brcmf_cfg80211_change_station(struct wiphy * wiphy,struct net_device * ndev,const u8 * mac,struct station_parameters * params)546105491d2cSKalle Valo brcmf_cfg80211_change_station(struct wiphy *wiphy, struct net_device *ndev,
546205491d2cSKalle Valo const u8 *mac, struct station_parameters *params)
546305491d2cSKalle Valo {
546416e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
546516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
546605491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
546705491d2cSKalle Valo s32 err;
546805491d2cSKalle Valo
546905491d2cSKalle Valo brcmf_dbg(TRACE, "Enter, MAC %pM, mask 0x%04x set 0x%04x\n", mac,
547005491d2cSKalle Valo params->sta_flags_mask, params->sta_flags_set);
547105491d2cSKalle Valo
547205491d2cSKalle Valo /* Ignore all 00 MAC */
547305491d2cSKalle Valo if (is_zero_ether_addr(mac))
547405491d2cSKalle Valo return 0;
547505491d2cSKalle Valo
547605491d2cSKalle Valo if (!(params->sta_flags_mask & BIT(NL80211_STA_FLAG_AUTHORIZED)))
547705491d2cSKalle Valo return 0;
547805491d2cSKalle Valo
547905491d2cSKalle Valo if (params->sta_flags_set & BIT(NL80211_STA_FLAG_AUTHORIZED))
548005491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_AUTHORIZE,
548105491d2cSKalle Valo (void *)mac, ETH_ALEN);
548205491d2cSKalle Valo else
548305491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_SCB_DEAUTHORIZE,
548405491d2cSKalle Valo (void *)mac, ETH_ALEN);
548505491d2cSKalle Valo if (err < 0)
548616e64676SRafał Miłecki bphy_err(drvr, "Setting SCB (de-)authorize failed, %d\n", err);
548705491d2cSKalle Valo
548805491d2cSKalle Valo return err;
548905491d2cSKalle Valo }
549005491d2cSKalle Valo
549105491d2cSKalle Valo static void
brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy * wiphy,struct wireless_dev * wdev,struct mgmt_frame_regs * upd)54926cd536feSJohannes Berg brcmf_cfg80211_update_mgmt_frame_registrations(struct wiphy *wiphy,
549305491d2cSKalle Valo struct wireless_dev *wdev,
54946cd536feSJohannes Berg struct mgmt_frame_regs *upd)
549505491d2cSKalle Valo {
549605491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
549705491d2cSKalle Valo
549805491d2cSKalle Valo vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
54996cd536feSJohannes Berg
55006cd536feSJohannes Berg vif->mgmt_rx_reg = upd->interface_stypes;
550105491d2cSKalle Valo }
550205491d2cSKalle Valo
550305491d2cSKalle Valo
550405491d2cSKalle Valo static int
brcmf_cfg80211_mgmt_tx(struct wiphy * wiphy,struct wireless_dev * wdev,struct cfg80211_mgmt_tx_params * params,u64 * cookie)550505491d2cSKalle Valo brcmf_cfg80211_mgmt_tx(struct wiphy *wiphy, struct wireless_dev *wdev,
550605491d2cSKalle Valo struct cfg80211_mgmt_tx_params *params, u64 *cookie)
550705491d2cSKalle Valo {
550805491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
550905491d2cSKalle Valo struct ieee80211_channel *chan = params->chan;
551016e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
551105491d2cSKalle Valo const u8 *buf = params->buf;
551205491d2cSKalle Valo size_t len = params->len;
551305491d2cSKalle Valo const struct ieee80211_mgmt *mgmt;
551405491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
551505491d2cSKalle Valo s32 err = 0;
551605491d2cSKalle Valo s32 ie_offset;
551705491d2cSKalle Valo s32 ie_len;
551805491d2cSKalle Valo struct brcmf_fil_action_frame_le *action_frame;
551905491d2cSKalle Valo struct brcmf_fil_af_params_le *af_params;
552005491d2cSKalle Valo bool ack;
552105491d2cSKalle Valo s32 chan_nr;
552205491d2cSKalle Valo u32 freq;
552305491d2cSKalle Valo
552405491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
552505491d2cSKalle Valo
552605491d2cSKalle Valo *cookie = 0;
552705491d2cSKalle Valo
552805491d2cSKalle Valo mgmt = (const struct ieee80211_mgmt *)buf;
552905491d2cSKalle Valo
553005491d2cSKalle Valo if (!ieee80211_is_mgmt(mgmt->frame_control)) {
553116e64676SRafał Miłecki bphy_err(drvr, "Driver only allows MGMT packet type\n");
553205491d2cSKalle Valo return -EPERM;
553305491d2cSKalle Valo }
553405491d2cSKalle Valo
553505491d2cSKalle Valo vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
553605491d2cSKalle Valo
553705491d2cSKalle Valo if (ieee80211_is_probe_resp(mgmt->frame_control)) {
553805491d2cSKalle Valo /* Right now the only reason to get a probe response */
553905491d2cSKalle Valo /* is for p2p listen response or for p2p GO from */
554005491d2cSKalle Valo /* wpa_supplicant. Unfortunately the probe is send */
554105491d2cSKalle Valo /* on primary ndev, while dongle wants it on the p2p */
554205491d2cSKalle Valo /* vif. Since this is only reason for a probe */
554305491d2cSKalle Valo /* response to be sent, the vif is taken from cfg. */
554405491d2cSKalle Valo /* If ever desired to send proberesp for non p2p */
554505491d2cSKalle Valo /* response then data should be checked for */
554605491d2cSKalle Valo /* "DIRECT-". Note in future supplicant will take */
554705491d2cSKalle Valo /* dedicated p2p wdev to do this and then this 'hack'*/
554805491d2cSKalle Valo /* is not needed anymore. */
554905491d2cSKalle Valo ie_offset = DOT11_MGMT_HDR_LEN +
555005491d2cSKalle Valo DOT11_BCN_PRB_FIXED_LEN;
555105491d2cSKalle Valo ie_len = len - ie_offset;
555205491d2cSKalle Valo if (vif == cfg->p2p.bss_idx[P2PAPI_BSSCFG_PRIMARY].vif)
555305491d2cSKalle Valo vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
555405491d2cSKalle Valo err = brcmf_vif_set_mgmt_ie(vif,
555505491d2cSKalle Valo BRCMF_VNDR_IE_PRBRSP_FLAG,
555605491d2cSKalle Valo &buf[ie_offset],
555705491d2cSKalle Valo ie_len);
555805491d2cSKalle Valo cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, true,
555905491d2cSKalle Valo GFP_KERNEL);
556005491d2cSKalle Valo } else if (ieee80211_is_action(mgmt->frame_control)) {
55618f44c9a4SArend van Spriel if (len > BRCMF_FIL_ACTION_FRAME_SIZE + DOT11_MGMT_HDR_LEN) {
556216e64676SRafał Miłecki bphy_err(drvr, "invalid action frame length\n");
55638f44c9a4SArend van Spriel err = -EINVAL;
55648f44c9a4SArend van Spriel goto exit;
55658f44c9a4SArend van Spriel }
556605491d2cSKalle Valo af_params = kzalloc(sizeof(*af_params), GFP_KERNEL);
556705491d2cSKalle Valo if (af_params == NULL) {
556816e64676SRafał Miłecki bphy_err(drvr, "unable to allocate frame\n");
556905491d2cSKalle Valo err = -ENOMEM;
557005491d2cSKalle Valo goto exit;
557105491d2cSKalle Valo }
557205491d2cSKalle Valo action_frame = &af_params->action_frame;
557305491d2cSKalle Valo /* Add the packet Id */
557405491d2cSKalle Valo action_frame->packet_id = cpu_to_le32(*cookie);
557505491d2cSKalle Valo /* Add BSSID */
557605491d2cSKalle Valo memcpy(&action_frame->da[0], &mgmt->da[0], ETH_ALEN);
557705491d2cSKalle Valo memcpy(&af_params->bssid[0], &mgmt->bssid[0], ETH_ALEN);
557805491d2cSKalle Valo /* Add the length exepted for 802.11 header */
557905491d2cSKalle Valo action_frame->len = cpu_to_le16(len - DOT11_MGMT_HDR_LEN);
558005491d2cSKalle Valo /* Add the channel. Use the one specified as parameter if any or
558105491d2cSKalle Valo * the current one (got from the firmware) otherwise
558205491d2cSKalle Valo */
558305491d2cSKalle Valo if (chan)
558405491d2cSKalle Valo freq = chan->center_freq;
558505491d2cSKalle Valo else
558605491d2cSKalle Valo brcmf_fil_cmd_int_get(vif->ifp, BRCMF_C_GET_CHANNEL,
558705491d2cSKalle Valo &freq);
558805491d2cSKalle Valo chan_nr = ieee80211_frequency_to_channel(freq);
558905491d2cSKalle Valo af_params->channel = cpu_to_le32(chan_nr);
5590ad96bc27SJoseph Chuang af_params->dwell_time = cpu_to_le32(params->wait);
559105491d2cSKalle Valo memcpy(action_frame->data, &buf[DOT11_MGMT_HDR_LEN],
559205491d2cSKalle Valo le16_to_cpu(action_frame->len));
559305491d2cSKalle Valo
559405491d2cSKalle Valo brcmf_dbg(TRACE, "Action frame, cookie=%lld, len=%d, freq=%d\n",
559505491d2cSKalle Valo *cookie, le16_to_cpu(action_frame->len), freq);
559605491d2cSKalle Valo
559705491d2cSKalle Valo ack = brcmf_p2p_send_action_frame(cfg, cfg_to_ndev(cfg),
559805491d2cSKalle Valo af_params);
559905491d2cSKalle Valo
560005491d2cSKalle Valo cfg80211_mgmt_tx_status(wdev, *cookie, buf, len, ack,
560105491d2cSKalle Valo GFP_KERNEL);
560205491d2cSKalle Valo kfree(af_params);
560305491d2cSKalle Valo } else {
560405491d2cSKalle Valo brcmf_dbg(TRACE, "Unhandled, fc=%04x!!\n", mgmt->frame_control);
56055b5e0928SAlexey Dobriyan brcmf_dbg_hex_dump(true, buf, len, "payload, len=%zu\n", len);
560605491d2cSKalle Valo }
560705491d2cSKalle Valo
560805491d2cSKalle Valo exit:
560905491d2cSKalle Valo return err;
561005491d2cSKalle Valo }
561105491d2cSKalle Valo
brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy * wiphy,struct net_device * ndev,s32 rssi_low,s32 rssi_high)56127dd56ea4SAlvin Šipraga static int brcmf_cfg80211_set_cqm_rssi_range_config(struct wiphy *wiphy,
56137dd56ea4SAlvin Šipraga struct net_device *ndev,
56147dd56ea4SAlvin Šipraga s32 rssi_low, s32 rssi_high)
56157dd56ea4SAlvin Šipraga {
56167dd56ea4SAlvin Šipraga struct brcmf_cfg80211_vif *vif;
56177dd56ea4SAlvin Šipraga struct brcmf_if *ifp;
56187dd56ea4SAlvin Šipraga int err = 0;
56197dd56ea4SAlvin Šipraga
56207dd56ea4SAlvin Šipraga brcmf_dbg(TRACE, "low=%d high=%d", rssi_low, rssi_high);
56217dd56ea4SAlvin Šipraga
56227dd56ea4SAlvin Šipraga ifp = netdev_priv(ndev);
56237dd56ea4SAlvin Šipraga vif = ifp->vif;
56247dd56ea4SAlvin Šipraga
56257dd56ea4SAlvin Šipraga if (rssi_low != vif->cqm_rssi_low || rssi_high != vif->cqm_rssi_high) {
56267dd56ea4SAlvin Šipraga /* The firmware will send an event when the RSSI is less than or
56277dd56ea4SAlvin Šipraga * equal to a configured level and the previous RSSI event was
56287dd56ea4SAlvin Šipraga * less than or equal to a different level. Set a third level
56297dd56ea4SAlvin Šipraga * so that we also detect the transition from rssi <= rssi_high
56307dd56ea4SAlvin Šipraga * to rssi > rssi_high.
56317dd56ea4SAlvin Šipraga */
56327dd56ea4SAlvin Šipraga struct brcmf_rssi_event_le config = {
56337dd56ea4SAlvin Šipraga .rate_limit_msec = cpu_to_le32(0),
56347dd56ea4SAlvin Šipraga .rssi_level_num = 3,
56357dd56ea4SAlvin Šipraga .rssi_levels = {
56367dd56ea4SAlvin Šipraga clamp_val(rssi_low, S8_MIN, S8_MAX - 2),
56377dd56ea4SAlvin Šipraga clamp_val(rssi_high, S8_MIN + 1, S8_MAX - 1),
56387dd56ea4SAlvin Šipraga S8_MAX,
56397dd56ea4SAlvin Šipraga },
56407dd56ea4SAlvin Šipraga };
56417dd56ea4SAlvin Šipraga
56427dd56ea4SAlvin Šipraga err = brcmf_fil_iovar_data_set(ifp, "rssi_event", &config,
56437dd56ea4SAlvin Šipraga sizeof(config));
56447dd56ea4SAlvin Šipraga if (err) {
56457dd56ea4SAlvin Šipraga err = -EINVAL;
56467dd56ea4SAlvin Šipraga } else {
56477dd56ea4SAlvin Šipraga vif->cqm_rssi_low = rssi_low;
56487dd56ea4SAlvin Šipraga vif->cqm_rssi_high = rssi_high;
56497dd56ea4SAlvin Šipraga }
56507dd56ea4SAlvin Šipraga }
56517dd56ea4SAlvin Šipraga
56527dd56ea4SAlvin Šipraga return err;
56537dd56ea4SAlvin Šipraga }
565405491d2cSKalle Valo
565505491d2cSKalle Valo static int
brcmf_cfg80211_cancel_remain_on_channel(struct wiphy * wiphy,struct wireless_dev * wdev,u64 cookie)565605491d2cSKalle Valo brcmf_cfg80211_cancel_remain_on_channel(struct wiphy *wiphy,
565705491d2cSKalle Valo struct wireless_dev *wdev,
565805491d2cSKalle Valo u64 cookie)
565905491d2cSKalle Valo {
566005491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
566116e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
566205491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
566305491d2cSKalle Valo int err = 0;
566405491d2cSKalle Valo
566505491d2cSKalle Valo brcmf_dbg(TRACE, "Enter p2p listen cancel\n");
566605491d2cSKalle Valo
566705491d2cSKalle Valo vif = cfg->p2p.bss_idx[P2PAPI_BSSCFG_DEVICE].vif;
566805491d2cSKalle Valo if (vif == NULL) {
566916e64676SRafał Miłecki bphy_err(drvr, "No p2p device available for probe response\n");
567005491d2cSKalle Valo err = -ENODEV;
567105491d2cSKalle Valo goto exit;
567205491d2cSKalle Valo }
567305491d2cSKalle Valo brcmf_p2p_cancel_remain_on_channel(vif->ifp);
567405491d2cSKalle Valo exit:
567505491d2cSKalle Valo return err;
567605491d2cSKalle Valo }
567705491d2cSKalle Valo
brcmf_cfg80211_get_channel(struct wiphy * wiphy,struct wireless_dev * wdev,unsigned int link_id,struct cfg80211_chan_def * chandef)5678ee6e7aa3SRafał Miłecki static int brcmf_cfg80211_get_channel(struct wiphy *wiphy,
5679ee6e7aa3SRafał Miłecki struct wireless_dev *wdev,
56807b0a0e3cSJohannes Berg unsigned int link_id,
5681ee6e7aa3SRafał Miłecki struct cfg80211_chan_def *chandef)
5682ee6e7aa3SRafał Miłecki {
5683ee6e7aa3SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
5684ee6e7aa3SRafał Miłecki struct net_device *ndev = wdev->netdev;
568516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
5686ee6e7aa3SRafał Miłecki struct brcmu_chan ch;
5687ee6e7aa3SRafał Miłecki enum nl80211_band band = 0;
5688ee6e7aa3SRafał Miłecki enum nl80211_chan_width width = 0;
5689ee6e7aa3SRafał Miłecki u32 chanspec;
5690ee6e7aa3SRafał Miłecki int freq, err;
5691ee6e7aa3SRafał Miłecki
56921ac11ae9SArend van Spriel if (!ndev || drvr->bus_if->state != BRCMF_BUS_UP)
5693ee6e7aa3SRafał Miłecki return -ENODEV;
5694ee6e7aa3SRafał Miłecki
56951ac11ae9SArend van Spriel err = brcmf_fil_iovar_int_get(netdev_priv(ndev), "chanspec", &chanspec);
5696ee6e7aa3SRafał Miłecki if (err) {
569716e64676SRafał Miłecki bphy_err(drvr, "chanspec failed (%d)\n", err);
5698ee6e7aa3SRafał Miłecki return err;
5699ee6e7aa3SRafał Miłecki }
5700ee6e7aa3SRafał Miłecki
5701ee6e7aa3SRafał Miłecki ch.chspec = chanspec;
5702ee6e7aa3SRafał Miłecki cfg->d11inf.decchspec(&ch);
5703ee6e7aa3SRafał Miłecki
5704ee6e7aa3SRafał Miłecki switch (ch.band) {
5705ee6e7aa3SRafał Miłecki case BRCMU_CHAN_BAND_2G:
5706ee6e7aa3SRafał Miłecki band = NL80211_BAND_2GHZ;
5707ee6e7aa3SRafał Miłecki break;
5708ee6e7aa3SRafał Miłecki case BRCMU_CHAN_BAND_5G:
5709ee6e7aa3SRafał Miłecki band = NL80211_BAND_5GHZ;
5710ee6e7aa3SRafał Miłecki break;
5711ee6e7aa3SRafał Miłecki }
5712ee6e7aa3SRafał Miłecki
5713ee6e7aa3SRafał Miłecki switch (ch.bw) {
5714ee6e7aa3SRafał Miłecki case BRCMU_CHAN_BW_80:
5715ee6e7aa3SRafał Miłecki width = NL80211_CHAN_WIDTH_80;
5716ee6e7aa3SRafał Miłecki break;
5717ee6e7aa3SRafał Miłecki case BRCMU_CHAN_BW_40:
5718ee6e7aa3SRafał Miłecki width = NL80211_CHAN_WIDTH_40;
5719ee6e7aa3SRafał Miłecki break;
5720ee6e7aa3SRafał Miłecki case BRCMU_CHAN_BW_20:
5721ee6e7aa3SRafał Miłecki width = NL80211_CHAN_WIDTH_20;
5722ee6e7aa3SRafał Miłecki break;
5723ee6e7aa3SRafał Miłecki case BRCMU_CHAN_BW_80P80:
5724ee6e7aa3SRafał Miłecki width = NL80211_CHAN_WIDTH_80P80;
5725ee6e7aa3SRafał Miłecki break;
5726ee6e7aa3SRafał Miłecki case BRCMU_CHAN_BW_160:
5727ee6e7aa3SRafał Miłecki width = NL80211_CHAN_WIDTH_160;
5728ee6e7aa3SRafał Miłecki break;
5729ee6e7aa3SRafał Miłecki }
5730ee6e7aa3SRafał Miłecki
5731ee6e7aa3SRafał Miłecki freq = ieee80211_channel_to_frequency(ch.control_ch_num, band);
5732ee6e7aa3SRafał Miłecki chandef->chan = ieee80211_get_channel(wiphy, freq);
5733ee6e7aa3SRafał Miłecki chandef->width = width;
5734ee6e7aa3SRafał Miłecki chandef->center_freq1 = ieee80211_channel_to_frequency(ch.chnum, band);
5735ee6e7aa3SRafał Miłecki chandef->center_freq2 = 0;
5736ee6e7aa3SRafał Miłecki
5737ee6e7aa3SRafał Miłecki return 0;
5738ee6e7aa3SRafał Miłecki }
5739ee6e7aa3SRafał Miłecki
brcmf_cfg80211_crit_proto_start(struct wiphy * wiphy,struct wireless_dev * wdev,enum nl80211_crit_proto_id proto,u16 duration)574005491d2cSKalle Valo static int brcmf_cfg80211_crit_proto_start(struct wiphy *wiphy,
574105491d2cSKalle Valo struct wireless_dev *wdev,
574205491d2cSKalle Valo enum nl80211_crit_proto_id proto,
574305491d2cSKalle Valo u16 duration)
574405491d2cSKalle Valo {
574505491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
574605491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
574705491d2cSKalle Valo
574805491d2cSKalle Valo vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
574905491d2cSKalle Valo
575005491d2cSKalle Valo /* only DHCP support for now */
575105491d2cSKalle Valo if (proto != NL80211_CRIT_PROTO_DHCP)
575205491d2cSKalle Valo return -EINVAL;
575305491d2cSKalle Valo
575405491d2cSKalle Valo /* suppress and abort scanning */
575505491d2cSKalle Valo set_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
575605491d2cSKalle Valo brcmf_abort_scanning(cfg);
575705491d2cSKalle Valo
575805491d2cSKalle Valo return brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_DISABLED, duration);
575905491d2cSKalle Valo }
576005491d2cSKalle Valo
brcmf_cfg80211_crit_proto_stop(struct wiphy * wiphy,struct wireless_dev * wdev)576105491d2cSKalle Valo static void brcmf_cfg80211_crit_proto_stop(struct wiphy *wiphy,
576205491d2cSKalle Valo struct wireless_dev *wdev)
576305491d2cSKalle Valo {
576405491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
576505491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
576605491d2cSKalle Valo
576705491d2cSKalle Valo vif = container_of(wdev, struct brcmf_cfg80211_vif, wdev);
576805491d2cSKalle Valo
576905491d2cSKalle Valo brcmf_btcoex_set_mode(vif, BRCMF_BTCOEX_ENABLED, 0);
577005491d2cSKalle Valo clear_bit(BRCMF_SCAN_STATUS_SUPPRESS, &cfg->scan_status);
577105491d2cSKalle Valo }
577205491d2cSKalle Valo
577305491d2cSKalle Valo static s32
brcmf_notify_tdls_peer_event(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)577405491d2cSKalle Valo brcmf_notify_tdls_peer_event(struct brcmf_if *ifp,
577505491d2cSKalle Valo const struct brcmf_event_msg *e, void *data)
577605491d2cSKalle Valo {
577705491d2cSKalle Valo switch (e->reason) {
577805491d2cSKalle Valo case BRCMF_E_REASON_TDLS_PEER_DISCOVERED:
577905491d2cSKalle Valo brcmf_dbg(TRACE, "TDLS Peer Discovered\n");
578005491d2cSKalle Valo break;
578105491d2cSKalle Valo case BRCMF_E_REASON_TDLS_PEER_CONNECTED:
578205491d2cSKalle Valo brcmf_dbg(TRACE, "TDLS Peer Connected\n");
578305491d2cSKalle Valo brcmf_proto_add_tdls_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
578405491d2cSKalle Valo break;
578505491d2cSKalle Valo case BRCMF_E_REASON_TDLS_PEER_DISCONNECTED:
578605491d2cSKalle Valo brcmf_dbg(TRACE, "TDLS Peer Disconnected\n");
578705491d2cSKalle Valo brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
578805491d2cSKalle Valo break;
578905491d2cSKalle Valo }
579005491d2cSKalle Valo
579105491d2cSKalle Valo return 0;
579205491d2cSKalle Valo }
579305491d2cSKalle Valo
brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)579405491d2cSKalle Valo static int brcmf_convert_nl80211_tdls_oper(enum nl80211_tdls_operation oper)
579505491d2cSKalle Valo {
579605491d2cSKalle Valo int ret;
579705491d2cSKalle Valo
579805491d2cSKalle Valo switch (oper) {
579905491d2cSKalle Valo case NL80211_TDLS_DISCOVERY_REQ:
580005491d2cSKalle Valo ret = BRCMF_TDLS_MANUAL_EP_DISCOVERY;
580105491d2cSKalle Valo break;
580205491d2cSKalle Valo case NL80211_TDLS_SETUP:
580305491d2cSKalle Valo ret = BRCMF_TDLS_MANUAL_EP_CREATE;
580405491d2cSKalle Valo break;
580505491d2cSKalle Valo case NL80211_TDLS_TEARDOWN:
580605491d2cSKalle Valo ret = BRCMF_TDLS_MANUAL_EP_DELETE;
580705491d2cSKalle Valo break;
580805491d2cSKalle Valo default:
580905491d2cSKalle Valo brcmf_err("unsupported operation: %d\n", oper);
581005491d2cSKalle Valo ret = -EOPNOTSUPP;
581105491d2cSKalle Valo }
581205491d2cSKalle Valo return ret;
581305491d2cSKalle Valo }
581405491d2cSKalle Valo
brcmf_cfg80211_tdls_oper(struct wiphy * wiphy,struct net_device * ndev,const u8 * peer,enum nl80211_tdls_operation oper)581505491d2cSKalle Valo static int brcmf_cfg80211_tdls_oper(struct wiphy *wiphy,
581605491d2cSKalle Valo struct net_device *ndev, const u8 *peer,
581705491d2cSKalle Valo enum nl80211_tdls_operation oper)
581805491d2cSKalle Valo {
581916e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
582016e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
582105491d2cSKalle Valo struct brcmf_if *ifp;
582205491d2cSKalle Valo struct brcmf_tdls_iovar_le info;
582305491d2cSKalle Valo int ret = 0;
582405491d2cSKalle Valo
582505491d2cSKalle Valo ret = brcmf_convert_nl80211_tdls_oper(oper);
582605491d2cSKalle Valo if (ret < 0)
582705491d2cSKalle Valo return ret;
582805491d2cSKalle Valo
582905491d2cSKalle Valo ifp = netdev_priv(ndev);
583005491d2cSKalle Valo memset(&info, 0, sizeof(info));
583105491d2cSKalle Valo info.mode = (u8)ret;
583205491d2cSKalle Valo if (peer)
583305491d2cSKalle Valo memcpy(info.ea, peer, ETH_ALEN);
583405491d2cSKalle Valo
583505491d2cSKalle Valo ret = brcmf_fil_iovar_data_set(ifp, "tdls_endpoint",
583605491d2cSKalle Valo &info, sizeof(info));
583705491d2cSKalle Valo if (ret < 0)
583816e64676SRafał Miłecki bphy_err(drvr, "tdls_endpoint iovar failed: ret=%d\n", ret);
583905491d2cSKalle Valo
584005491d2cSKalle Valo return ret;
584105491d2cSKalle Valo }
584205491d2cSKalle Valo
58432a2a5d18SArend Van Spriel static int
brcmf_cfg80211_update_conn_params(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_connect_params * sme,u32 changed)58442a2a5d18SArend Van Spriel brcmf_cfg80211_update_conn_params(struct wiphy *wiphy,
58452a2a5d18SArend Van Spriel struct net_device *ndev,
58462a2a5d18SArend Van Spriel struct cfg80211_connect_params *sme,
58472a2a5d18SArend Van Spriel u32 changed)
58482a2a5d18SArend Van Spriel {
584916e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
585016e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
58512a2a5d18SArend Van Spriel struct brcmf_if *ifp;
58522a2a5d18SArend Van Spriel int err;
58532a2a5d18SArend Van Spriel
58542a2a5d18SArend Van Spriel if (!(changed & UPDATE_ASSOC_IES))
58552a2a5d18SArend Van Spriel return 0;
58562a2a5d18SArend Van Spriel
58572a2a5d18SArend Van Spriel ifp = netdev_priv(ndev);
58582a2a5d18SArend Van Spriel err = brcmf_vif_set_mgmt_ie(ifp->vif, BRCMF_VNDR_IE_ASSOCREQ_FLAG,
58592a2a5d18SArend Van Spriel sme->ie, sme->ie_len);
58602a2a5d18SArend Van Spriel if (err)
586116e64676SRafał Miłecki bphy_err(drvr, "Set Assoc REQ IE Failed\n");
58622a2a5d18SArend Van Spriel else
58632a2a5d18SArend Van Spriel brcmf_dbg(TRACE, "Applied Vndr IEs for Assoc request\n");
58642a2a5d18SArend Van Spriel
58652a2a5d18SArend Van Spriel return err;
58662a2a5d18SArend Van Spriel }
58672a2a5d18SArend Van Spriel
58685c22fb85SHante Meuleman #ifdef CONFIG_PM
58695c22fb85SHante Meuleman static int
brcmf_cfg80211_set_rekey_data(struct wiphy * wiphy,struct net_device * ndev,struct cfg80211_gtk_rekey_data * gtk)58705c22fb85SHante Meuleman brcmf_cfg80211_set_rekey_data(struct wiphy *wiphy, struct net_device *ndev,
58715c22fb85SHante Meuleman struct cfg80211_gtk_rekey_data *gtk)
58725c22fb85SHante Meuleman {
587316e64676SRafał Miłecki struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
587416e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
58755c22fb85SHante Meuleman struct brcmf_if *ifp = netdev_priv(ndev);
58765c22fb85SHante Meuleman struct brcmf_gtk_keyinfo_le gtk_le;
58775c22fb85SHante Meuleman int ret;
58785c22fb85SHante Meuleman
58795c22fb85SHante Meuleman brcmf_dbg(TRACE, "Enter, bssidx=%d\n", ifp->bsscfgidx);
58805c22fb85SHante Meuleman
58815c22fb85SHante Meuleman memcpy(gtk_le.kck, gtk->kck, sizeof(gtk_le.kck));
58825c22fb85SHante Meuleman memcpy(gtk_le.kek, gtk->kek, sizeof(gtk_le.kek));
58835c22fb85SHante Meuleman memcpy(gtk_le.replay_counter, gtk->replay_ctr,
58845c22fb85SHante Meuleman sizeof(gtk_le.replay_counter));
58855c22fb85SHante Meuleman
58865c22fb85SHante Meuleman ret = brcmf_fil_iovar_data_set(ifp, "gtk_key_info", >k_le,
58875c22fb85SHante Meuleman sizeof(gtk_le));
58885c22fb85SHante Meuleman if (ret < 0)
588916e64676SRafał Miłecki bphy_err(drvr, "gtk_key_info iovar failed: ret=%d\n", ret);
58905c22fb85SHante Meuleman
58915c22fb85SHante Meuleman return ret;
58925c22fb85SHante Meuleman }
58935c22fb85SHante Meuleman #endif
58945c22fb85SHante Meuleman
brcmf_cfg80211_set_pmk(struct wiphy * wiphy,struct net_device * dev,const struct cfg80211_pmk_conf * conf)58952526ff21SArend van Spriel static int brcmf_cfg80211_set_pmk(struct wiphy *wiphy, struct net_device *dev,
58962526ff21SArend van Spriel const struct cfg80211_pmk_conf *conf)
58972526ff21SArend van Spriel {
58982526ff21SArend van Spriel struct brcmf_if *ifp;
58992526ff21SArend van Spriel
59002526ff21SArend van Spriel brcmf_dbg(TRACE, "enter\n");
59012526ff21SArend van Spriel
59022526ff21SArend van Spriel /* expect using firmware supplicant for 1X */
59032526ff21SArend van Spriel ifp = netdev_priv(dev);
59042526ff21SArend van Spriel if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
59052526ff21SArend van Spriel return -EINVAL;
59062526ff21SArend van Spriel
590764d1519eSJohannes Berg if (conf->pmk_len > BRCMF_WSEC_MAX_PSK_LEN)
590864d1519eSJohannes Berg return -ERANGE;
590964d1519eSJohannes Berg
59102526ff21SArend van Spriel return brcmf_set_pmk(ifp, conf->pmk, conf->pmk_len);
59112526ff21SArend van Spriel }
59122526ff21SArend van Spriel
brcmf_cfg80211_del_pmk(struct wiphy * wiphy,struct net_device * dev,const u8 * aa)59132526ff21SArend van Spriel static int brcmf_cfg80211_del_pmk(struct wiphy *wiphy, struct net_device *dev,
59142526ff21SArend van Spriel const u8 *aa)
59152526ff21SArend van Spriel {
59162526ff21SArend van Spriel struct brcmf_if *ifp;
59172526ff21SArend van Spriel
59182526ff21SArend van Spriel brcmf_dbg(TRACE, "enter\n");
59192526ff21SArend van Spriel ifp = netdev_priv(dev);
59202526ff21SArend van Spriel if (WARN_ON(ifp->vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_1X))
59212526ff21SArend van Spriel return -EINVAL;
59222526ff21SArend van Spriel
59232526ff21SArend van Spriel return brcmf_set_pmk(ifp, NULL, 0);
59242526ff21SArend van Spriel }
59252526ff21SArend van Spriel
59265c22fb85SHante Meuleman static struct cfg80211_ops brcmf_cfg80211_ops = {
592705491d2cSKalle Valo .add_virtual_intf = brcmf_cfg80211_add_iface,
592805491d2cSKalle Valo .del_virtual_intf = brcmf_cfg80211_del_iface,
592905491d2cSKalle Valo .change_virtual_intf = brcmf_cfg80211_change_iface,
593005491d2cSKalle Valo .scan = brcmf_cfg80211_scan,
593105491d2cSKalle Valo .set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
593205491d2cSKalle Valo .join_ibss = brcmf_cfg80211_join_ibss,
593305491d2cSKalle Valo .leave_ibss = brcmf_cfg80211_leave_ibss,
593405491d2cSKalle Valo .get_station = brcmf_cfg80211_get_station,
593505491d2cSKalle Valo .dump_station = brcmf_cfg80211_dump_station,
593605491d2cSKalle Valo .set_tx_power = brcmf_cfg80211_set_tx_power,
593705491d2cSKalle Valo .get_tx_power = brcmf_cfg80211_get_tx_power,
593805491d2cSKalle Valo .add_key = brcmf_cfg80211_add_key,
593905491d2cSKalle Valo .del_key = brcmf_cfg80211_del_key,
594005491d2cSKalle Valo .get_key = brcmf_cfg80211_get_key,
594105491d2cSKalle Valo .set_default_key = brcmf_cfg80211_config_default_key,
594205491d2cSKalle Valo .set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
594305491d2cSKalle Valo .set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
594405491d2cSKalle Valo .connect = brcmf_cfg80211_connect,
594505491d2cSKalle Valo .disconnect = brcmf_cfg80211_disconnect,
594605491d2cSKalle Valo .suspend = brcmf_cfg80211_suspend,
594705491d2cSKalle Valo .resume = brcmf_cfg80211_resume,
594805491d2cSKalle Valo .set_pmksa = brcmf_cfg80211_set_pmksa,
594905491d2cSKalle Valo .del_pmksa = brcmf_cfg80211_del_pmksa,
595005491d2cSKalle Valo .flush_pmksa = brcmf_cfg80211_flush_pmksa,
595105491d2cSKalle Valo .start_ap = brcmf_cfg80211_start_ap,
595205491d2cSKalle Valo .stop_ap = brcmf_cfg80211_stop_ap,
595305491d2cSKalle Valo .change_beacon = brcmf_cfg80211_change_beacon,
595405491d2cSKalle Valo .del_station = brcmf_cfg80211_del_station,
595505491d2cSKalle Valo .change_station = brcmf_cfg80211_change_station,
595605491d2cSKalle Valo .sched_scan_start = brcmf_cfg80211_sched_scan_start,
595705491d2cSKalle Valo .sched_scan_stop = brcmf_cfg80211_sched_scan_stop,
59586cd536feSJohannes Berg .update_mgmt_frame_registrations =
59596cd536feSJohannes Berg brcmf_cfg80211_update_mgmt_frame_registrations,
596005491d2cSKalle Valo .mgmt_tx = brcmf_cfg80211_mgmt_tx,
59617dd56ea4SAlvin Šipraga .set_cqm_rssi_range_config = brcmf_cfg80211_set_cqm_rssi_range_config,
596205491d2cSKalle Valo .remain_on_channel = brcmf_p2p_remain_on_channel,
596305491d2cSKalle Valo .cancel_remain_on_channel = brcmf_cfg80211_cancel_remain_on_channel,
5964ee6e7aa3SRafał Miłecki .get_channel = brcmf_cfg80211_get_channel,
596505491d2cSKalle Valo .start_p2p_device = brcmf_p2p_start_device,
596605491d2cSKalle Valo .stop_p2p_device = brcmf_p2p_stop_device,
596705491d2cSKalle Valo .crit_proto_start = brcmf_cfg80211_crit_proto_start,
596805491d2cSKalle Valo .crit_proto_stop = brcmf_cfg80211_crit_proto_stop,
596905491d2cSKalle Valo .tdls_oper = brcmf_cfg80211_tdls_oper,
59702a2a5d18SArend Van Spriel .update_connect_params = brcmf_cfg80211_update_conn_params,
59712526ff21SArend van Spriel .set_pmk = brcmf_cfg80211_set_pmk,
59722526ff21SArend van Spriel .del_pmk = brcmf_cfg80211_del_pmk,
597305491d2cSKalle Valo };
597405491d2cSKalle Valo
brcmf_cfg80211_get_ops(struct brcmf_mp_device * settings)59758c892df4SStijn Tintel struct cfg80211_ops *brcmf_cfg80211_get_ops(struct brcmf_mp_device *settings)
5976856d5a01SArend Van Spriel {
59778c892df4SStijn Tintel struct cfg80211_ops *ops;
59788c892df4SStijn Tintel
59798c892df4SStijn Tintel ops = kmemdup(&brcmf_cfg80211_ops, sizeof(brcmf_cfg80211_ops),
5980856d5a01SArend Van Spriel GFP_KERNEL);
59818c892df4SStijn Tintel
59828c892df4SStijn Tintel if (ops && settings->roamoff)
59838c892df4SStijn Tintel ops->update_connect_params = NULL;
59848c892df4SStijn Tintel
59858c892df4SStijn Tintel return ops;
5986856d5a01SArend Van Spriel }
5987856d5a01SArend Van Spriel
brcmf_alloc_vif(struct brcmf_cfg80211_info * cfg,enum nl80211_iftype type)598805491d2cSKalle Valo struct brcmf_cfg80211_vif *brcmf_alloc_vif(struct brcmf_cfg80211_info *cfg,
598926072330SRafał Miłecki enum nl80211_iftype type)
599005491d2cSKalle Valo {
599105491d2cSKalle Valo struct brcmf_cfg80211_vif *vif_walk;
599205491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
599305491d2cSKalle Valo bool mbss;
59942635853cSWright Feng struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
599505491d2cSKalle Valo
599605491d2cSKalle Valo brcmf_dbg(TRACE, "allocating virtual interface (size=%zu)\n",
599705491d2cSKalle Valo sizeof(*vif));
599805491d2cSKalle Valo vif = kzalloc(sizeof(*vif), GFP_KERNEL);
599905491d2cSKalle Valo if (!vif)
600005491d2cSKalle Valo return ERR_PTR(-ENOMEM);
600105491d2cSKalle Valo
600205491d2cSKalle Valo vif->wdev.wiphy = cfg->wiphy;
600305491d2cSKalle Valo vif->wdev.iftype = type;
600405491d2cSKalle Valo
600505491d2cSKalle Valo brcmf_init_prof(&vif->profile);
600605491d2cSKalle Valo
60072635853cSWright Feng if (type == NL80211_IFTYPE_AP &&
60082635853cSWright Feng brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS)) {
600905491d2cSKalle Valo mbss = false;
601005491d2cSKalle Valo list_for_each_entry(vif_walk, &cfg->vif_list, list) {
601105491d2cSKalle Valo if (vif_walk->wdev.iftype == NL80211_IFTYPE_AP) {
601205491d2cSKalle Valo mbss = true;
601305491d2cSKalle Valo break;
601405491d2cSKalle Valo }
601505491d2cSKalle Valo }
601605491d2cSKalle Valo vif->mbss = mbss;
601705491d2cSKalle Valo }
601805491d2cSKalle Valo
601905491d2cSKalle Valo list_add_tail(&vif->list, &cfg->vif_list);
602005491d2cSKalle Valo return vif;
602105491d2cSKalle Valo }
602205491d2cSKalle Valo
brcmf_free_vif(struct brcmf_cfg80211_vif * vif)602305491d2cSKalle Valo void brcmf_free_vif(struct brcmf_cfg80211_vif *vif)
602405491d2cSKalle Valo {
602505491d2cSKalle Valo list_del(&vif->list);
602605491d2cSKalle Valo kfree(vif);
602705491d2cSKalle Valo }
602805491d2cSKalle Valo
brcmf_cfg80211_free_netdev(struct net_device * ndev)602905491d2cSKalle Valo void brcmf_cfg80211_free_netdev(struct net_device *ndev)
603005491d2cSKalle Valo {
603105491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
603205491d2cSKalle Valo struct brcmf_if *ifp;
603305491d2cSKalle Valo
603405491d2cSKalle Valo ifp = netdev_priv(ndev);
603505491d2cSKalle Valo vif = ifp->vif;
603605491d2cSKalle Valo
603705491d2cSKalle Valo if (vif)
603805491d2cSKalle Valo brcmf_free_vif(vif);
603905491d2cSKalle Valo }
604005491d2cSKalle Valo
brcmf_is_linkup(struct brcmf_cfg80211_vif * vif,const struct brcmf_event_msg * e)6041b8a64f0eSArend van Spriel static bool brcmf_is_linkup(struct brcmf_cfg80211_vif *vif,
6042b8a64f0eSArend van Spriel const struct brcmf_event_msg *e)
604305491d2cSKalle Valo {
604405491d2cSKalle Valo u32 event = e->event_code;
604505491d2cSKalle Valo u32 status = e->status;
604605491d2cSKalle Valo
6047b2fe11f0SChung-Hsien Hsu if ((vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_PSK ||
6048b2fe11f0SChung-Hsien Hsu vif->profile.use_fwsup == BRCMF_PROFILE_FWSUP_SAE) &&
60492526ff21SArend van Spriel event == BRCMF_E_PSK_SUP &&
6050b8a64f0eSArend van Spriel status == BRCMF_E_STATUS_FWSUP_COMPLETED)
6051b8a64f0eSArend van Spriel set_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
605205491d2cSKalle Valo if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
605305491d2cSKalle Valo brcmf_dbg(CONN, "Processing set ssid\n");
6054b8a64f0eSArend van Spriel memcpy(vif->profile.bssid, e->addr, ETH_ALEN);
60553b1e0a7bSChung-Hsien Hsu if (vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_PSK &&
60563b1e0a7bSChung-Hsien Hsu vif->profile.use_fwsup != BRCMF_PROFILE_FWSUP_SAE)
605705491d2cSKalle Valo return true;
6058b8a64f0eSArend van Spriel
6059b8a64f0eSArend van Spriel set_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
606005491d2cSKalle Valo }
606105491d2cSKalle Valo
6062b8a64f0eSArend van Spriel if (test_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state) &&
6063b8a64f0eSArend van Spriel test_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state)) {
6064b8a64f0eSArend van Spriel clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
6065b8a64f0eSArend van Spriel clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
6066b8a64f0eSArend van Spriel return true;
6067b8a64f0eSArend van Spriel }
606805491d2cSKalle Valo return false;
606905491d2cSKalle Valo }
607005491d2cSKalle Valo
brcmf_is_linkdown(struct brcmf_cfg80211_vif * vif,const struct brcmf_event_msg * e)6071e862a3e4SLuca Pesce static bool brcmf_is_linkdown(struct brcmf_cfg80211_vif *vif,
6072e862a3e4SLuca Pesce const struct brcmf_event_msg *e)
607305491d2cSKalle Valo {
607405491d2cSKalle Valo u32 event = e->event_code;
607505491d2cSKalle Valo u16 flags = e->flags;
607605491d2cSKalle Valo
607705491d2cSKalle Valo if ((event == BRCMF_E_DEAUTH) || (event == BRCMF_E_DEAUTH_IND) ||
607805491d2cSKalle Valo (event == BRCMF_E_DISASSOC_IND) ||
607905491d2cSKalle Valo ((event == BRCMF_E_LINK) && (!(flags & BRCMF_EVENT_MSG_LINK)))) {
608005491d2cSKalle Valo brcmf_dbg(CONN, "Processing link down\n");
6081e862a3e4SLuca Pesce clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS, &vif->sme_state);
6082e862a3e4SLuca Pesce clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS, &vif->sme_state);
608305491d2cSKalle Valo return true;
608405491d2cSKalle Valo }
608505491d2cSKalle Valo return false;
608605491d2cSKalle Valo }
608705491d2cSKalle Valo
brcmf_is_nonetwork(struct brcmf_cfg80211_info * cfg,const struct brcmf_event_msg * e)608805491d2cSKalle Valo static bool brcmf_is_nonetwork(struct brcmf_cfg80211_info *cfg,
608905491d2cSKalle Valo const struct brcmf_event_msg *e)
609005491d2cSKalle Valo {
609105491d2cSKalle Valo u32 event = e->event_code;
609205491d2cSKalle Valo u32 status = e->status;
609305491d2cSKalle Valo
609405491d2cSKalle Valo if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
609505491d2cSKalle Valo brcmf_dbg(CONN, "Processing Link %s & no network found\n",
609605491d2cSKalle Valo e->flags & BRCMF_EVENT_MSG_LINK ? "up" : "down");
609705491d2cSKalle Valo return true;
609805491d2cSKalle Valo }
609905491d2cSKalle Valo
610005491d2cSKalle Valo if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
610105491d2cSKalle Valo brcmf_dbg(CONN, "Processing connecting & no network found\n");
610205491d2cSKalle Valo return true;
610305491d2cSKalle Valo }
610405491d2cSKalle Valo
6105b8a64f0eSArend van Spriel if (event == BRCMF_E_PSK_SUP &&
6106b8a64f0eSArend van Spriel status != BRCMF_E_STATUS_FWSUP_COMPLETED) {
6107b8a64f0eSArend van Spriel brcmf_dbg(CONN, "Processing failed supplicant state: %u\n",
6108b8a64f0eSArend van Spriel status);
6109b8a64f0eSArend van Spriel return true;
6110b8a64f0eSArend van Spriel }
6111b8a64f0eSArend van Spriel
611205491d2cSKalle Valo return false;
611305491d2cSKalle Valo }
611405491d2cSKalle Valo
brcmf_clear_assoc_ies(struct brcmf_cfg80211_info * cfg)611505491d2cSKalle Valo static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_info *cfg)
611605491d2cSKalle Valo {
611705491d2cSKalle Valo struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
611805491d2cSKalle Valo
611905491d2cSKalle Valo kfree(conn_info->req_ie);
612005491d2cSKalle Valo conn_info->req_ie = NULL;
612105491d2cSKalle Valo conn_info->req_ie_len = 0;
612205491d2cSKalle Valo kfree(conn_info->resp_ie);
612305491d2cSKalle Valo conn_info->resp_ie = NULL;
612405491d2cSKalle Valo conn_info->resp_ie_len = 0;
612505491d2cSKalle Valo }
612605491d2cSKalle Valo
brcmf_map_prio_to_prec(void * config,u8 prio)612792072e5fSSaravanan Shanmugham u8 brcmf_map_prio_to_prec(void *config, u8 prio)
612892072e5fSSaravanan Shanmugham {
612992072e5fSSaravanan Shanmugham struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
613092072e5fSSaravanan Shanmugham
613192072e5fSSaravanan Shanmugham if (!cfg)
613292072e5fSSaravanan Shanmugham return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
613392072e5fSSaravanan Shanmugham (prio ^ 2) : prio;
613492072e5fSSaravanan Shanmugham
613592072e5fSSaravanan Shanmugham /* For those AC(s) with ACM flag set to 1, convert its 4-level priority
613692072e5fSSaravanan Shanmugham * to an 8-level precedence which is the same as BE's
613792072e5fSSaravanan Shanmugham */
613892072e5fSSaravanan Shanmugham if (prio > PRIO_8021D_EE &&
613992072e5fSSaravanan Shanmugham cfg->ac_priority[prio] == cfg->ac_priority[PRIO_8021D_BE])
614092072e5fSSaravanan Shanmugham return cfg->ac_priority[prio] * 2;
614192072e5fSSaravanan Shanmugham
614292072e5fSSaravanan Shanmugham /* Conversion of 4-level priority to 8-level precedence */
614392072e5fSSaravanan Shanmugham if (prio == PRIO_8021D_BE || prio == PRIO_8021D_BK ||
614492072e5fSSaravanan Shanmugham prio == PRIO_8021D_CL || prio == PRIO_8021D_VO)
614592072e5fSSaravanan Shanmugham return cfg->ac_priority[prio] * 2;
614692072e5fSSaravanan Shanmugham else
614792072e5fSSaravanan Shanmugham return cfg->ac_priority[prio] * 2 + 1;
614892072e5fSSaravanan Shanmugham }
614992072e5fSSaravanan Shanmugham
brcmf_map_prio_to_aci(void * config,u8 prio)615092072e5fSSaravanan Shanmugham u8 brcmf_map_prio_to_aci(void *config, u8 prio)
615192072e5fSSaravanan Shanmugham {
615292072e5fSSaravanan Shanmugham /* Prio here refers to the 802.1d priority in range of 0 to 7.
615392072e5fSSaravanan Shanmugham * ACI here refers to the WLAN AC Index in range of 0 to 3.
615492072e5fSSaravanan Shanmugham * This function will return ACI corresponding to input prio.
615592072e5fSSaravanan Shanmugham */
615692072e5fSSaravanan Shanmugham struct brcmf_cfg80211_info *cfg = (struct brcmf_cfg80211_info *)config;
615792072e5fSSaravanan Shanmugham
615892072e5fSSaravanan Shanmugham if (cfg)
615992072e5fSSaravanan Shanmugham return cfg->ac_priority[prio];
616092072e5fSSaravanan Shanmugham
616192072e5fSSaravanan Shanmugham return prio;
616292072e5fSSaravanan Shanmugham }
616392072e5fSSaravanan Shanmugham
brcmf_init_wmm_prio(u8 * priority)616492072e5fSSaravanan Shanmugham static void brcmf_init_wmm_prio(u8 *priority)
616592072e5fSSaravanan Shanmugham {
616692072e5fSSaravanan Shanmugham /* Initialize AC priority array to default
616792072e5fSSaravanan Shanmugham * 802.1d priority as per following table:
616892072e5fSSaravanan Shanmugham * 802.1d prio 0,3 maps to BE
616992072e5fSSaravanan Shanmugham * 802.1d prio 1,2 maps to BK
617092072e5fSSaravanan Shanmugham * 802.1d prio 4,5 maps to VI
617192072e5fSSaravanan Shanmugham * 802.1d prio 6,7 maps to VO
617292072e5fSSaravanan Shanmugham */
617392072e5fSSaravanan Shanmugham priority[0] = BRCMF_FWS_FIFO_AC_BE;
617492072e5fSSaravanan Shanmugham priority[3] = BRCMF_FWS_FIFO_AC_BE;
617592072e5fSSaravanan Shanmugham priority[1] = BRCMF_FWS_FIFO_AC_BK;
617692072e5fSSaravanan Shanmugham priority[2] = BRCMF_FWS_FIFO_AC_BK;
617792072e5fSSaravanan Shanmugham priority[4] = BRCMF_FWS_FIFO_AC_VI;
617892072e5fSSaravanan Shanmugham priority[5] = BRCMF_FWS_FIFO_AC_VI;
617992072e5fSSaravanan Shanmugham priority[6] = BRCMF_FWS_FIFO_AC_VO;
618092072e5fSSaravanan Shanmugham priority[7] = BRCMF_FWS_FIFO_AC_VO;
618192072e5fSSaravanan Shanmugham }
618292072e5fSSaravanan Shanmugham
brcmf_wifi_prioritize_acparams(const struct brcmf_cfg80211_edcf_acparam * acp,u8 * priority)618392072e5fSSaravanan Shanmugham static void brcmf_wifi_prioritize_acparams(const
618492072e5fSSaravanan Shanmugham struct brcmf_cfg80211_edcf_acparam *acp, u8 *priority)
618592072e5fSSaravanan Shanmugham {
618692072e5fSSaravanan Shanmugham u8 aci;
618792072e5fSSaravanan Shanmugham u8 aifsn;
618892072e5fSSaravanan Shanmugham u8 ecwmin;
618992072e5fSSaravanan Shanmugham u8 ecwmax;
619092072e5fSSaravanan Shanmugham u8 acm;
619192072e5fSSaravanan Shanmugham u8 ranking_basis[EDCF_AC_COUNT];
619292072e5fSSaravanan Shanmugham u8 aci_prio[EDCF_AC_COUNT]; /* AC_BE, AC_BK, AC_VI, AC_VO */
619392072e5fSSaravanan Shanmugham u8 index;
619492072e5fSSaravanan Shanmugham
619592072e5fSSaravanan Shanmugham for (aci = 0; aci < EDCF_AC_COUNT; aci++, acp++) {
619692072e5fSSaravanan Shanmugham aifsn = acp->ACI & EDCF_AIFSN_MASK;
619792072e5fSSaravanan Shanmugham acm = (acp->ACI & EDCF_ACM_MASK) ? 1 : 0;
619892072e5fSSaravanan Shanmugham ecwmin = acp->ECW & EDCF_ECWMIN_MASK;
619992072e5fSSaravanan Shanmugham ecwmax = (acp->ECW & EDCF_ECWMAX_MASK) >> EDCF_ECWMAX_SHIFT;
620092072e5fSSaravanan Shanmugham brcmf_dbg(CONN, "ACI %d aifsn %d acm %d ecwmin %d ecwmax %d\n",
620192072e5fSSaravanan Shanmugham aci, aifsn, acm, ecwmin, ecwmax);
620292072e5fSSaravanan Shanmugham /* Default AC_VO will be the lowest ranking value */
620392072e5fSSaravanan Shanmugham ranking_basis[aci] = aifsn + ecwmin + ecwmax;
620492072e5fSSaravanan Shanmugham /* Initialise priority starting at 0 (AC_BE) */
620592072e5fSSaravanan Shanmugham aci_prio[aci] = 0;
620692072e5fSSaravanan Shanmugham
620792072e5fSSaravanan Shanmugham /* If ACM is set, STA can't use this AC as per 802.11.
620892072e5fSSaravanan Shanmugham * Change the ranking to BE
620992072e5fSSaravanan Shanmugham */
621092072e5fSSaravanan Shanmugham if (aci != AC_BE && aci != AC_BK && acm == 1)
621192072e5fSSaravanan Shanmugham ranking_basis[aci] = ranking_basis[AC_BE];
621292072e5fSSaravanan Shanmugham }
621392072e5fSSaravanan Shanmugham
621492072e5fSSaravanan Shanmugham /* Ranking method which works for AC priority
621592072e5fSSaravanan Shanmugham * swapping when values for cwmin, cwmax and aifsn are varied
621692072e5fSSaravanan Shanmugham * Compare each aci_prio against each other aci_prio
621792072e5fSSaravanan Shanmugham */
621892072e5fSSaravanan Shanmugham for (aci = 0; aci < EDCF_AC_COUNT; aci++) {
621992072e5fSSaravanan Shanmugham for (index = 0; index < EDCF_AC_COUNT; index++) {
622092072e5fSSaravanan Shanmugham if (index != aci) {
622192072e5fSSaravanan Shanmugham /* Smaller ranking value has higher priority,
622292072e5fSSaravanan Shanmugham * so increment priority for each ACI which has
622392072e5fSSaravanan Shanmugham * a higher ranking value
622492072e5fSSaravanan Shanmugham */
622592072e5fSSaravanan Shanmugham if (ranking_basis[aci] < ranking_basis[index])
622692072e5fSSaravanan Shanmugham aci_prio[aci]++;
622792072e5fSSaravanan Shanmugham }
622892072e5fSSaravanan Shanmugham }
622992072e5fSSaravanan Shanmugham }
623092072e5fSSaravanan Shanmugham
623192072e5fSSaravanan Shanmugham /* By now, aci_prio[] will be in range of 0 to 3.
623292072e5fSSaravanan Shanmugham * Use ACI prio to get the new priority value for
623392072e5fSSaravanan Shanmugham * each 802.1d traffic type, in this range.
623492072e5fSSaravanan Shanmugham */
623592072e5fSSaravanan Shanmugham if (!(aci_prio[AC_BE] == aci_prio[AC_BK] &&
623692072e5fSSaravanan Shanmugham aci_prio[AC_BK] == aci_prio[AC_VI] &&
623792072e5fSSaravanan Shanmugham aci_prio[AC_VI] == aci_prio[AC_VO])) {
623892072e5fSSaravanan Shanmugham /* 802.1d 0,3 maps to BE */
623992072e5fSSaravanan Shanmugham priority[0] = aci_prio[AC_BE];
624092072e5fSSaravanan Shanmugham priority[3] = aci_prio[AC_BE];
624192072e5fSSaravanan Shanmugham
624292072e5fSSaravanan Shanmugham /* 802.1d 1,2 maps to BK */
624392072e5fSSaravanan Shanmugham priority[1] = aci_prio[AC_BK];
624492072e5fSSaravanan Shanmugham priority[2] = aci_prio[AC_BK];
624592072e5fSSaravanan Shanmugham
624692072e5fSSaravanan Shanmugham /* 802.1d 4,5 maps to VO */
624792072e5fSSaravanan Shanmugham priority[4] = aci_prio[AC_VI];
624892072e5fSSaravanan Shanmugham priority[5] = aci_prio[AC_VI];
624992072e5fSSaravanan Shanmugham
625092072e5fSSaravanan Shanmugham /* 802.1d 6,7 maps to VO */
625192072e5fSSaravanan Shanmugham priority[6] = aci_prio[AC_VO];
625292072e5fSSaravanan Shanmugham priority[7] = aci_prio[AC_VO];
625392072e5fSSaravanan Shanmugham } else {
625492072e5fSSaravanan Shanmugham /* Initialize to default priority */
625592072e5fSSaravanan Shanmugham brcmf_init_wmm_prio(priority);
625692072e5fSSaravanan Shanmugham }
625792072e5fSSaravanan Shanmugham
625892072e5fSSaravanan Shanmugham brcmf_dbg(CONN, "Adj prio BE 0->%d, BK 1->%d, BK 2->%d, BE 3->%d\n",
625992072e5fSSaravanan Shanmugham priority[0], priority[1], priority[2], priority[3]);
626092072e5fSSaravanan Shanmugham
626192072e5fSSaravanan Shanmugham brcmf_dbg(CONN, "Adj prio VI 4->%d, VI 5->%d, VO 6->%d, VO 7->%d\n",
626292072e5fSSaravanan Shanmugham priority[4], priority[5], priority[6], priority[7]);
626392072e5fSSaravanan Shanmugham }
626492072e5fSSaravanan Shanmugham
brcmf_get_assoc_ies(struct brcmf_cfg80211_info * cfg,struct brcmf_if * ifp)626505491d2cSKalle Valo static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_info *cfg,
626605491d2cSKalle Valo struct brcmf_if *ifp)
626705491d2cSKalle Valo {
626816e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
626905491d2cSKalle Valo struct brcmf_cfg80211_assoc_ielen_le *assoc_info;
627005491d2cSKalle Valo struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
627192072e5fSSaravanan Shanmugham struct brcmf_cfg80211_edcf_acparam edcf_acparam_info[EDCF_AC_COUNT];
627205491d2cSKalle Valo u32 req_len;
627305491d2cSKalle Valo u32 resp_len;
627405491d2cSKalle Valo s32 err = 0;
627505491d2cSKalle Valo
627605491d2cSKalle Valo brcmf_clear_assoc_ies(cfg);
627705491d2cSKalle Valo
627805491d2cSKalle Valo err = brcmf_fil_iovar_data_get(ifp, "assoc_info",
627905491d2cSKalle Valo cfg->extra_buf, WL_ASSOC_INFO_MAX);
628005491d2cSKalle Valo if (err) {
628116e64676SRafał Miłecki bphy_err(drvr, "could not get assoc info (%d)\n", err);
628205491d2cSKalle Valo return err;
628305491d2cSKalle Valo }
628405491d2cSKalle Valo assoc_info =
628505491d2cSKalle Valo (struct brcmf_cfg80211_assoc_ielen_le *)cfg->extra_buf;
628605491d2cSKalle Valo req_len = le32_to_cpu(assoc_info->req_len);
628705491d2cSKalle Valo resp_len = le32_to_cpu(assoc_info->resp_len);
62880da40e01SJisoo Jang if (req_len > WL_EXTRA_BUF_MAX || resp_len > WL_EXTRA_BUF_MAX) {
62890da40e01SJisoo Jang bphy_err(drvr, "invalid lengths in assoc info: req %u resp %u\n",
62900da40e01SJisoo Jang req_len, resp_len);
62910da40e01SJisoo Jang return -EINVAL;
62920da40e01SJisoo Jang }
629305491d2cSKalle Valo if (req_len) {
629405491d2cSKalle Valo err = brcmf_fil_iovar_data_get(ifp, "assoc_req_ies",
629505491d2cSKalle Valo cfg->extra_buf,
629605491d2cSKalle Valo WL_ASSOC_INFO_MAX);
629705491d2cSKalle Valo if (err) {
629816e64676SRafał Miłecki bphy_err(drvr, "could not get assoc req (%d)\n", err);
629905491d2cSKalle Valo return err;
630005491d2cSKalle Valo }
630105491d2cSKalle Valo conn_info->req_ie_len = req_len;
630205491d2cSKalle Valo conn_info->req_ie =
630305491d2cSKalle Valo kmemdup(cfg->extra_buf, conn_info->req_ie_len,
630405491d2cSKalle Valo GFP_KERNEL);
630546953f97SKangjie Lu if (!conn_info->req_ie)
630646953f97SKangjie Lu conn_info->req_ie_len = 0;
630705491d2cSKalle Valo } else {
630805491d2cSKalle Valo conn_info->req_ie_len = 0;
630905491d2cSKalle Valo conn_info->req_ie = NULL;
631005491d2cSKalle Valo }
631105491d2cSKalle Valo if (resp_len) {
631205491d2cSKalle Valo err = brcmf_fil_iovar_data_get(ifp, "assoc_resp_ies",
631305491d2cSKalle Valo cfg->extra_buf,
631405491d2cSKalle Valo WL_ASSOC_INFO_MAX);
631505491d2cSKalle Valo if (err) {
631616e64676SRafał Miłecki bphy_err(drvr, "could not get assoc resp (%d)\n", err);
631705491d2cSKalle Valo return err;
631805491d2cSKalle Valo }
631905491d2cSKalle Valo conn_info->resp_ie_len = resp_len;
632005491d2cSKalle Valo conn_info->resp_ie =
632105491d2cSKalle Valo kmemdup(cfg->extra_buf, conn_info->resp_ie_len,
632205491d2cSKalle Valo GFP_KERNEL);
632346953f97SKangjie Lu if (!conn_info->resp_ie)
632446953f97SKangjie Lu conn_info->resp_ie_len = 0;
632592072e5fSSaravanan Shanmugham
632692072e5fSSaravanan Shanmugham err = brcmf_fil_iovar_data_get(ifp, "wme_ac_sta",
632792072e5fSSaravanan Shanmugham edcf_acparam_info,
632892072e5fSSaravanan Shanmugham sizeof(edcf_acparam_info));
632992072e5fSSaravanan Shanmugham if (err) {
633092072e5fSSaravanan Shanmugham brcmf_err("could not get wme_ac_sta (%d)\n", err);
633192072e5fSSaravanan Shanmugham return err;
633292072e5fSSaravanan Shanmugham }
633392072e5fSSaravanan Shanmugham
633492072e5fSSaravanan Shanmugham brcmf_wifi_prioritize_acparams(edcf_acparam_info,
633592072e5fSSaravanan Shanmugham cfg->ac_priority);
633605491d2cSKalle Valo } else {
633705491d2cSKalle Valo conn_info->resp_ie_len = 0;
633805491d2cSKalle Valo conn_info->resp_ie = NULL;
633905491d2cSKalle Valo }
634005491d2cSKalle Valo brcmf_dbg(CONN, "req len (%d) resp len (%d)\n",
634105491d2cSKalle Valo conn_info->req_ie_len, conn_info->resp_ie_len);
634205491d2cSKalle Valo
634305491d2cSKalle Valo return err;
634405491d2cSKalle Valo }
634505491d2cSKalle Valo
634605491d2cSKalle Valo static s32
brcmf_bss_roaming_done(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e)634705491d2cSKalle Valo brcmf_bss_roaming_done(struct brcmf_cfg80211_info *cfg,
634805491d2cSKalle Valo struct net_device *ndev,
634905491d2cSKalle Valo const struct brcmf_event_msg *e)
635005491d2cSKalle Valo {
635105491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
635205491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
635305491d2cSKalle Valo struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
635405491d2cSKalle Valo struct wiphy *wiphy = cfg_to_wiphy(cfg);
635505491d2cSKalle Valo struct ieee80211_channel *notify_channel = NULL;
635605491d2cSKalle Valo struct ieee80211_supported_band *band;
635705491d2cSKalle Valo struct brcmf_bss_info_le *bi;
635805491d2cSKalle Valo struct brcmu_chan ch;
635929ce6ecbSAvraham Stern struct cfg80211_roam_info roam_info = {};
636005491d2cSKalle Valo u32 freq;
636105491d2cSKalle Valo s32 err = 0;
636205491d2cSKalle Valo u8 *buf;
636305491d2cSKalle Valo
636405491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
636505491d2cSKalle Valo
636605491d2cSKalle Valo brcmf_get_assoc_ies(cfg, ifp);
636705491d2cSKalle Valo memcpy(profile->bssid, e->addr, ETH_ALEN);
636805491d2cSKalle Valo brcmf_update_bss_info(cfg, ifp);
636905491d2cSKalle Valo
637005491d2cSKalle Valo buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
637105491d2cSKalle Valo if (buf == NULL) {
637205491d2cSKalle Valo err = -ENOMEM;
637305491d2cSKalle Valo goto done;
637405491d2cSKalle Valo }
637505491d2cSKalle Valo
637605491d2cSKalle Valo /* data sent to dongle has to be little endian */
637705491d2cSKalle Valo *(__le32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
637805491d2cSKalle Valo err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BSS_INFO,
637905491d2cSKalle Valo buf, WL_BSS_INFO_MAX);
638005491d2cSKalle Valo
638105491d2cSKalle Valo if (err)
638205491d2cSKalle Valo goto done;
638305491d2cSKalle Valo
638405491d2cSKalle Valo bi = (struct brcmf_bss_info_le *)(buf + 4);
638505491d2cSKalle Valo ch.chspec = le16_to_cpu(bi->chanspec);
638605491d2cSKalle Valo cfg->d11inf.decchspec(&ch);
638705491d2cSKalle Valo
638805491d2cSKalle Valo if (ch.band == BRCMU_CHAN_BAND_2G)
638957fbcce3SJohannes Berg band = wiphy->bands[NL80211_BAND_2GHZ];
639005491d2cSKalle Valo else
639157fbcce3SJohannes Berg band = wiphy->bands[NL80211_BAND_5GHZ];
639205491d2cSKalle Valo
63934712d88aSRafał Miłecki freq = ieee80211_channel_to_frequency(ch.control_ch_num, band->band);
639405491d2cSKalle Valo notify_channel = ieee80211_get_channel(wiphy, freq);
639505491d2cSKalle Valo
639605491d2cSKalle Valo done:
639705491d2cSKalle Valo kfree(buf);
639829ce6ecbSAvraham Stern
6399efbabc11SVeerendranath Jakkam roam_info.links[0].channel = notify_channel;
6400efbabc11SVeerendranath Jakkam roam_info.links[0].bssid = profile->bssid;
640129ce6ecbSAvraham Stern roam_info.req_ie = conn_info->req_ie;
640229ce6ecbSAvraham Stern roam_info.req_ie_len = conn_info->req_ie_len;
640329ce6ecbSAvraham Stern roam_info.resp_ie = conn_info->resp_ie;
640429ce6ecbSAvraham Stern roam_info.resp_ie_len = conn_info->resp_ie_len;
640529ce6ecbSAvraham Stern
640629ce6ecbSAvraham Stern cfg80211_roamed(ndev, &roam_info, GFP_KERNEL);
640705491d2cSKalle Valo brcmf_dbg(CONN, "Report roaming result\n");
640805491d2cSKalle Valo
6409be898fedSChung-Hsien Hsu if (profile->use_fwsup == BRCMF_PROFILE_FWSUP_1X && profile->is_ft) {
64100ff57171SVinayak Yadawad cfg80211_port_authorized(ndev, profile->bssid, NULL, 0, GFP_KERNEL);
6411be898fedSChung-Hsien Hsu brcmf_dbg(CONN, "Report port authorized\n");
6412be898fedSChung-Hsien Hsu }
6413be898fedSChung-Hsien Hsu
641405491d2cSKalle Valo set_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state);
641505491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
641605491d2cSKalle Valo return err;
641705491d2cSKalle Valo }
641805491d2cSKalle Valo
641905491d2cSKalle Valo static s32
brcmf_bss_connect_done(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e,bool completed)642005491d2cSKalle Valo brcmf_bss_connect_done(struct brcmf_cfg80211_info *cfg,
642105491d2cSKalle Valo struct net_device *ndev, const struct brcmf_event_msg *e,
642205491d2cSKalle Valo bool completed)
642305491d2cSKalle Valo {
642405491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
642505491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
642605491d2cSKalle Valo struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg);
6427123fef3fSArend van Spriel struct cfg80211_connect_resp_params conn_params;
642805491d2cSKalle Valo
642905491d2cSKalle Valo brcmf_dbg(TRACE, "Enter\n");
643005491d2cSKalle Valo
643105491d2cSKalle Valo if (test_and_clear_bit(BRCMF_VIF_STATUS_CONNECTING,
643205491d2cSKalle Valo &ifp->vif->sme_state)) {
6433123fef3fSArend van Spriel memset(&conn_params, 0, sizeof(conn_params));
643405491d2cSKalle Valo if (completed) {
643505491d2cSKalle Valo brcmf_get_assoc_ies(cfg, ifp);
643605491d2cSKalle Valo brcmf_update_bss_info(cfg, ifp);
643705491d2cSKalle Valo set_bit(BRCMF_VIF_STATUS_CONNECTED,
643805491d2cSKalle Valo &ifp->vif->sme_state);
6439123fef3fSArend van Spriel conn_params.status = WLAN_STATUS_SUCCESS;
6440123fef3fSArend van Spriel } else {
644152617beeSWataru Gohda clear_bit(BRCMF_VIF_STATUS_EAP_SUCCESS,
644252617beeSWataru Gohda &ifp->vif->sme_state);
644352617beeSWataru Gohda clear_bit(BRCMF_VIF_STATUS_ASSOC_SUCCESS,
644452617beeSWataru Gohda &ifp->vif->sme_state);
6445123fef3fSArend van Spriel conn_params.status = WLAN_STATUS_AUTH_TIMEOUT;
644605491d2cSKalle Valo }
6447efbabc11SVeerendranath Jakkam conn_params.links[0].bssid = profile->bssid;
6448123fef3fSArend van Spriel conn_params.req_ie = conn_info->req_ie;
6449123fef3fSArend van Spriel conn_params.req_ie_len = conn_info->req_ie_len;
6450123fef3fSArend van Spriel conn_params.resp_ie = conn_info->resp_ie;
6451123fef3fSArend van Spriel conn_params.resp_ie_len = conn_info->resp_ie_len;
6452123fef3fSArend van Spriel cfg80211_connect_done(ndev, &conn_params, GFP_KERNEL);
645305491d2cSKalle Valo brcmf_dbg(CONN, "Report connect result - connection %s\n",
645405491d2cSKalle Valo completed ? "succeeded" : "failed");
645505491d2cSKalle Valo }
645605491d2cSKalle Valo brcmf_dbg(TRACE, "Exit\n");
645705491d2cSKalle Valo return 0;
645805491d2cSKalle Valo }
645905491d2cSKalle Valo
646005491d2cSKalle Valo static s32
brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info * cfg,struct net_device * ndev,const struct brcmf_event_msg * e,void * data)646105491d2cSKalle Valo brcmf_notify_connect_status_ap(struct brcmf_cfg80211_info *cfg,
646205491d2cSKalle Valo struct net_device *ndev,
646305491d2cSKalle Valo const struct brcmf_event_msg *e, void *data)
646405491d2cSKalle Valo {
646516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
646605491d2cSKalle Valo static int generation;
646705491d2cSKalle Valo u32 event = e->event_code;
646805491d2cSKalle Valo u32 reason = e->reason;
646941bd3d58SToke Høiland-Jørgensen struct station_info *sinfo;
647005491d2cSKalle Valo
6471e1c122d5SRafał Miłecki brcmf_dbg(CONN, "event %s (%u), reason %d\n",
6472e1c122d5SRafał Miłecki brcmf_fweh_event_name(event), event, reason);
647305491d2cSKalle Valo if (event == BRCMF_E_LINK && reason == BRCMF_E_REASON_LINK_BSSCFG_DIS &&
647405491d2cSKalle Valo ndev != cfg_to_ndev(cfg)) {
647505491d2cSKalle Valo brcmf_dbg(CONN, "AP mode link down\n");
647605491d2cSKalle Valo complete(&cfg->vif_disabled);
647705491d2cSKalle Valo return 0;
647805491d2cSKalle Valo }
647905491d2cSKalle Valo
648005491d2cSKalle Valo if (((event == BRCMF_E_ASSOC_IND) || (event == BRCMF_E_REASSOC_IND)) &&
648105491d2cSKalle Valo (reason == BRCMF_E_STATUS_SUCCESS)) {
648205491d2cSKalle Valo if (!data) {
648316e64676SRafał Miłecki bphy_err(drvr, "No IEs present in ASSOC/REASSOC_IND\n");
648405491d2cSKalle Valo return -EINVAL;
648505491d2cSKalle Valo }
648641bd3d58SToke Høiland-Jørgensen
648741bd3d58SToke Høiland-Jørgensen sinfo = kzalloc(sizeof(*sinfo), GFP_KERNEL);
648841bd3d58SToke Høiland-Jørgensen if (!sinfo)
648941bd3d58SToke Høiland-Jørgensen return -ENOMEM;
649041bd3d58SToke Høiland-Jørgensen
649141bd3d58SToke Høiland-Jørgensen sinfo->assoc_req_ies = data;
649241bd3d58SToke Høiland-Jørgensen sinfo->assoc_req_ies_len = e->datalen;
649305491d2cSKalle Valo generation++;
649441bd3d58SToke Høiland-Jørgensen sinfo->generation = generation;
649541bd3d58SToke Høiland-Jørgensen cfg80211_new_sta(ndev, e->addr, sinfo, GFP_KERNEL);
649641bd3d58SToke Høiland-Jørgensen
649741bd3d58SToke Høiland-Jørgensen kfree(sinfo);
649805491d2cSKalle Valo } else if ((event == BRCMF_E_DISASSOC_IND) ||
649905491d2cSKalle Valo (event == BRCMF_E_DEAUTH_IND) ||
650005491d2cSKalle Valo (event == BRCMF_E_DEAUTH)) {
650105491d2cSKalle Valo cfg80211_del_sta(ndev, e->addr, GFP_KERNEL);
650205491d2cSKalle Valo }
650305491d2cSKalle Valo return 0;
650405491d2cSKalle Valo }
650505491d2cSKalle Valo
650605491d2cSKalle Valo static s32
brcmf_notify_connect_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)650705491d2cSKalle Valo brcmf_notify_connect_status(struct brcmf_if *ifp,
650805491d2cSKalle Valo const struct brcmf_event_msg *e, void *data)
650905491d2cSKalle Valo {
651005491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
651105491d2cSKalle Valo struct net_device *ndev = ifp->ndev;
651205491d2cSKalle Valo struct brcmf_cfg80211_profile *profile = &ifp->vif->profile;
651305491d2cSKalle Valo struct ieee80211_channel *chan;
651405491d2cSKalle Valo s32 err = 0;
651505491d2cSKalle Valo
651605491d2cSKalle Valo if ((e->event_code == BRCMF_E_DEAUTH) ||
651705491d2cSKalle Valo (e->event_code == BRCMF_E_DEAUTH_IND) ||
651805491d2cSKalle Valo (e->event_code == BRCMF_E_DISASSOC_IND) ||
651905491d2cSKalle Valo ((e->event_code == BRCMF_E_LINK) && (!e->flags))) {
652005491d2cSKalle Valo brcmf_proto_delete_peer(ifp->drvr, ifp->ifidx, (u8 *)e->addr);
652105491d2cSKalle Valo }
652205491d2cSKalle Valo
652305491d2cSKalle Valo if (brcmf_is_apmode(ifp->vif)) {
652405491d2cSKalle Valo err = brcmf_notify_connect_status_ap(cfg, ndev, e, data);
6525b8a64f0eSArend van Spriel } else if (brcmf_is_linkup(ifp->vif, e)) {
652605491d2cSKalle Valo brcmf_dbg(CONN, "Linkup\n");
652705491d2cSKalle Valo if (brcmf_is_ibssmode(ifp->vif)) {
6528b0a79088SHante Meuleman brcmf_inform_ibss(cfg, ndev, e->addr);
652905491d2cSKalle Valo chan = ieee80211_get_channel(cfg->wiphy, cfg->channel);
653005491d2cSKalle Valo memcpy(profile->bssid, e->addr, ETH_ALEN);
653105491d2cSKalle Valo cfg80211_ibss_joined(ndev, e->addr, chan, GFP_KERNEL);
653205491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_CONNECTING,
653305491d2cSKalle Valo &ifp->vif->sme_state);
653405491d2cSKalle Valo set_bit(BRCMF_VIF_STATUS_CONNECTED,
653505491d2cSKalle Valo &ifp->vif->sme_state);
653605491d2cSKalle Valo } else
653705491d2cSKalle Valo brcmf_bss_connect_done(cfg, ndev, e, true);
653805491d2cSKalle Valo brcmf_net_setcarrier(ifp, true);
6539e862a3e4SLuca Pesce } else if (brcmf_is_linkdown(ifp->vif, e)) {
654005491d2cSKalle Valo brcmf_dbg(CONN, "Linkdown\n");
65410d9de08dSAble Liao if (!brcmf_is_ibssmode(ifp->vif) &&
654252617beeSWataru Gohda (test_bit(BRCMF_VIF_STATUS_CONNECTED,
654352617beeSWataru Gohda &ifp->vif->sme_state) ||
654452617beeSWataru Gohda test_bit(BRCMF_VIF_STATUS_CONNECTING,
654552617beeSWataru Gohda &ifp->vif->sme_state))) {
654652617beeSWataru Gohda if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
654752617beeSWataru Gohda &ifp->vif->sme_state) &&
654852617beeSWataru Gohda memcmp(profile->bssid, e->addr, ETH_ALEN))
65490d9de08dSAble Liao return err;
65500d9de08dSAble Liao
655105491d2cSKalle Valo brcmf_bss_connect_done(cfg, ndev, e, false);
655242e0ed0dSHante Meuleman brcmf_link_down(ifp->vif,
65531b050d97SSoontak Lee brcmf_map_fw_linkdown_reason(e),
65541b050d97SSoontak Lee e->event_code &
65551b050d97SSoontak Lee (BRCMF_E_DEAUTH_IND |
65561b050d97SSoontak Lee BRCMF_E_DISASSOC_IND)
65571b050d97SSoontak Lee ? false : true);
655805491d2cSKalle Valo brcmf_init_prof(ndev_to_prof(ndev));
655905491d2cSKalle Valo if (ndev != cfg_to_ndev(cfg))
656005491d2cSKalle Valo complete(&cfg->vif_disabled);
656105491d2cSKalle Valo brcmf_net_setcarrier(ifp, false);
656242e0ed0dSHante Meuleman }
656305491d2cSKalle Valo } else if (brcmf_is_nonetwork(cfg, e)) {
656405491d2cSKalle Valo if (brcmf_is_ibssmode(ifp->vif))
656505491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_CONNECTING,
656605491d2cSKalle Valo &ifp->vif->sme_state);
656705491d2cSKalle Valo else
656805491d2cSKalle Valo brcmf_bss_connect_done(cfg, ndev, e, false);
656905491d2cSKalle Valo }
657005491d2cSKalle Valo
657105491d2cSKalle Valo return err;
657205491d2cSKalle Valo }
657305491d2cSKalle Valo
657405491d2cSKalle Valo static s32
brcmf_notify_roaming_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)657505491d2cSKalle Valo brcmf_notify_roaming_status(struct brcmf_if *ifp,
657605491d2cSKalle Valo const struct brcmf_event_msg *e, void *data)
657705491d2cSKalle Valo {
657805491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
657905491d2cSKalle Valo u32 event = e->event_code;
658005491d2cSKalle Valo u32 status = e->status;
658105491d2cSKalle Valo
658205491d2cSKalle Valo if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
65838b943e36SChung-Hsien Hsu if (test_bit(BRCMF_VIF_STATUS_CONNECTED,
65848b943e36SChung-Hsien Hsu &ifp->vif->sme_state)) {
658505491d2cSKalle Valo brcmf_bss_roaming_done(cfg, ifp->ndev, e);
65868b943e36SChung-Hsien Hsu } else {
658705491d2cSKalle Valo brcmf_bss_connect_done(cfg, ifp->ndev, e, true);
65888b943e36SChung-Hsien Hsu brcmf_net_setcarrier(ifp, true);
65898b943e36SChung-Hsien Hsu }
659005491d2cSKalle Valo }
659105491d2cSKalle Valo
659205491d2cSKalle Valo return 0;
659305491d2cSKalle Valo }
659405491d2cSKalle Valo
659505491d2cSKalle Valo static s32
brcmf_notify_mic_status(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)659605491d2cSKalle Valo brcmf_notify_mic_status(struct brcmf_if *ifp,
659705491d2cSKalle Valo const struct brcmf_event_msg *e, void *data)
659805491d2cSKalle Valo {
659905491d2cSKalle Valo u16 flags = e->flags;
660005491d2cSKalle Valo enum nl80211_key_type key_type;
660105491d2cSKalle Valo
660205491d2cSKalle Valo if (flags & BRCMF_EVENT_MSG_GROUP)
660305491d2cSKalle Valo key_type = NL80211_KEYTYPE_GROUP;
660405491d2cSKalle Valo else
660505491d2cSKalle Valo key_type = NL80211_KEYTYPE_PAIRWISE;
660605491d2cSKalle Valo
660705491d2cSKalle Valo cfg80211_michael_mic_failure(ifp->ndev, (u8 *)&e->addr, key_type, -1,
660805491d2cSKalle Valo NULL, GFP_KERNEL);
660905491d2cSKalle Valo
661005491d2cSKalle Valo return 0;
661105491d2cSKalle Valo }
661205491d2cSKalle Valo
brcmf_notify_rssi(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)66137dd56ea4SAlvin Šipraga static s32 brcmf_notify_rssi(struct brcmf_if *ifp,
66147dd56ea4SAlvin Šipraga const struct brcmf_event_msg *e, void *data)
66157dd56ea4SAlvin Šipraga {
66167dd56ea4SAlvin Šipraga struct brcmf_cfg80211_vif *vif = ifp->vif;
66177dd56ea4SAlvin Šipraga struct brcmf_rssi_be *info = data;
6618ec52d77dSJohn Keeping s32 rssi, snr = 0, noise = 0;
66197dd56ea4SAlvin Šipraga s32 low, high, last;
66207dd56ea4SAlvin Šipraga
6621ec52d77dSJohn Keeping if (e->datalen >= sizeof(*info)) {
66227dd56ea4SAlvin Šipraga rssi = be32_to_cpu(info->rssi);
66237dd56ea4SAlvin Šipraga snr = be32_to_cpu(info->snr);
66247dd56ea4SAlvin Šipraga noise = be32_to_cpu(info->noise);
6625ec52d77dSJohn Keeping } else if (e->datalen >= sizeof(rssi)) {
6626ec52d77dSJohn Keeping rssi = be32_to_cpu(*(__be32 *)data);
6627ec52d77dSJohn Keeping } else {
6628ec52d77dSJohn Keeping brcmf_err("insufficient RSSI event data\n");
6629ec52d77dSJohn Keeping return 0;
6630ec52d77dSJohn Keeping }
66317dd56ea4SAlvin Šipraga
66327dd56ea4SAlvin Šipraga low = vif->cqm_rssi_low;
66337dd56ea4SAlvin Šipraga high = vif->cqm_rssi_high;
66347dd56ea4SAlvin Šipraga last = vif->cqm_rssi_last;
66357dd56ea4SAlvin Šipraga
66367dd56ea4SAlvin Šipraga brcmf_dbg(TRACE, "rssi=%d snr=%d noise=%d low=%d high=%d last=%d\n",
66377dd56ea4SAlvin Šipraga rssi, snr, noise, low, high, last);
66387dd56ea4SAlvin Šipraga
66397dd56ea4SAlvin Šipraga vif->cqm_rssi_last = rssi;
66407dd56ea4SAlvin Šipraga
66417dd56ea4SAlvin Šipraga if (rssi <= low || rssi == 0) {
66427dd56ea4SAlvin Šipraga brcmf_dbg(INFO, "LOW rssi=%d\n", rssi);
66437dd56ea4SAlvin Šipraga cfg80211_cqm_rssi_notify(ifp->ndev,
66447dd56ea4SAlvin Šipraga NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW,
66457dd56ea4SAlvin Šipraga rssi, GFP_KERNEL);
66467dd56ea4SAlvin Šipraga } else if (rssi > high) {
66477dd56ea4SAlvin Šipraga brcmf_dbg(INFO, "HIGH rssi=%d\n", rssi);
66487dd56ea4SAlvin Šipraga cfg80211_cqm_rssi_notify(ifp->ndev,
66497dd56ea4SAlvin Šipraga NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH,
66507dd56ea4SAlvin Šipraga rssi, GFP_KERNEL);
66517dd56ea4SAlvin Šipraga }
66527dd56ea4SAlvin Šipraga
66537dd56ea4SAlvin Šipraga return 0;
66547dd56ea4SAlvin Šipraga }
66557dd56ea4SAlvin Šipraga
brcmf_notify_vif_event(struct brcmf_if * ifp,const struct brcmf_event_msg * e,void * data)665605491d2cSKalle Valo static s32 brcmf_notify_vif_event(struct brcmf_if *ifp,
665705491d2cSKalle Valo const struct brcmf_event_msg *e, void *data)
665805491d2cSKalle Valo {
665905491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
666005491d2cSKalle Valo struct brcmf_if_event *ifevent = (struct brcmf_if_event *)data;
666105491d2cSKalle Valo struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
666205491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
666305491d2cSKalle Valo
666437a869ecSHante Meuleman brcmf_dbg(TRACE, "Enter: action %u flags %u ifidx %u bsscfgidx %u\n",
666505491d2cSKalle Valo ifevent->action, ifevent->flags, ifevent->ifidx,
666637a869ecSHante Meuleman ifevent->bsscfgidx);
666705491d2cSKalle Valo
6668b64abcb7Smhiramat@kernel.org spin_lock(&event->vif_event_lock);
666905491d2cSKalle Valo event->action = ifevent->action;
667005491d2cSKalle Valo vif = event->vif;
667105491d2cSKalle Valo
667205491d2cSKalle Valo switch (ifevent->action) {
667305491d2cSKalle Valo case BRCMF_E_IF_ADD:
667405491d2cSKalle Valo /* waiting process may have timed out */
667505491d2cSKalle Valo if (!cfg->vif_event.vif) {
6676b64abcb7Smhiramat@kernel.org spin_unlock(&event->vif_event_lock);
667705491d2cSKalle Valo return -EBADF;
667805491d2cSKalle Valo }
667905491d2cSKalle Valo
668005491d2cSKalle Valo ifp->vif = vif;
668105491d2cSKalle Valo vif->ifp = ifp;
668205491d2cSKalle Valo if (ifp->ndev) {
668305491d2cSKalle Valo vif->wdev.netdev = ifp->ndev;
668405491d2cSKalle Valo ifp->ndev->ieee80211_ptr = &vif->wdev;
668505491d2cSKalle Valo SET_NETDEV_DEV(ifp->ndev, wiphy_dev(cfg->wiphy));
668605491d2cSKalle Valo }
6687b64abcb7Smhiramat@kernel.org spin_unlock(&event->vif_event_lock);
668805491d2cSKalle Valo wake_up(&event->vif_wq);
668905491d2cSKalle Valo return 0;
669005491d2cSKalle Valo
669105491d2cSKalle Valo case BRCMF_E_IF_DEL:
6692b64abcb7Smhiramat@kernel.org spin_unlock(&event->vif_event_lock);
669305491d2cSKalle Valo /* event may not be upon user request */
669405491d2cSKalle Valo if (brcmf_cfg80211_vif_event_armed(cfg))
669505491d2cSKalle Valo wake_up(&event->vif_wq);
669605491d2cSKalle Valo return 0;
669705491d2cSKalle Valo
669805491d2cSKalle Valo case BRCMF_E_IF_CHANGE:
6699b64abcb7Smhiramat@kernel.org spin_unlock(&event->vif_event_lock);
670005491d2cSKalle Valo wake_up(&event->vif_wq);
670105491d2cSKalle Valo return 0;
670205491d2cSKalle Valo
670305491d2cSKalle Valo default:
6704b64abcb7Smhiramat@kernel.org spin_unlock(&event->vif_event_lock);
670505491d2cSKalle Valo break;
670605491d2cSKalle Valo }
670705491d2cSKalle Valo return -EINVAL;
670805491d2cSKalle Valo }
670905491d2cSKalle Valo
brcmf_init_conf(struct brcmf_cfg80211_conf * conf)671005491d2cSKalle Valo static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
671105491d2cSKalle Valo {
671205491d2cSKalle Valo conf->frag_threshold = (u32)-1;
671305491d2cSKalle Valo conf->rts_threshold = (u32)-1;
671405491d2cSKalle Valo conf->retry_short = (u32)-1;
671505491d2cSKalle Valo conf->retry_long = (u32)-1;
671605491d2cSKalle Valo }
671705491d2cSKalle Valo
brcmf_register_event_handlers(struct brcmf_cfg80211_info * cfg)671805491d2cSKalle Valo static void brcmf_register_event_handlers(struct brcmf_cfg80211_info *cfg)
671905491d2cSKalle Valo {
672005491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_LINK,
672105491d2cSKalle Valo brcmf_notify_connect_status);
672205491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH_IND,
672305491d2cSKalle Valo brcmf_notify_connect_status);
672405491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_DEAUTH,
672505491d2cSKalle Valo brcmf_notify_connect_status);
672605491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_DISASSOC_IND,
672705491d2cSKalle Valo brcmf_notify_connect_status);
672805491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_ASSOC_IND,
672905491d2cSKalle Valo brcmf_notify_connect_status);
673005491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_REASSOC_IND,
673105491d2cSKalle Valo brcmf_notify_connect_status);
673205491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_ROAM,
673305491d2cSKalle Valo brcmf_notify_roaming_status);
673405491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_MIC_ERROR,
673505491d2cSKalle Valo brcmf_notify_mic_status);
673605491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_SET_SSID,
673705491d2cSKalle Valo brcmf_notify_connect_status);
673805491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_PFN_NET_FOUND,
673905491d2cSKalle Valo brcmf_notify_sched_scan_results);
674005491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_IF,
674105491d2cSKalle Valo brcmf_notify_vif_event);
674205491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_PROBEREQ_MSG,
674305491d2cSKalle Valo brcmf_p2p_notify_rx_mgmt_p2p_probereq);
674405491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_P2P_DISC_LISTEN_COMPLETE,
674505491d2cSKalle Valo brcmf_p2p_notify_listen_complete);
674605491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_RX,
674705491d2cSKalle Valo brcmf_p2p_notify_action_frame_rx);
674805491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_COMPLETE,
674905491d2cSKalle Valo brcmf_p2p_notify_action_tx_complete);
675005491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE,
675105491d2cSKalle Valo brcmf_p2p_notify_action_tx_complete);
6752b8a64f0eSArend van Spriel brcmf_fweh_register(cfg->pub, BRCMF_E_PSK_SUP,
6753b8a64f0eSArend van Spriel brcmf_notify_connect_status);
67547dd56ea4SAlvin Šipraga brcmf_fweh_register(cfg->pub, BRCMF_E_RSSI, brcmf_notify_rssi);
675505491d2cSKalle Valo }
675605491d2cSKalle Valo
brcmf_deinit_priv_mem(struct brcmf_cfg80211_info * cfg)675705491d2cSKalle Valo static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_info *cfg)
675805491d2cSKalle Valo {
675905491d2cSKalle Valo kfree(cfg->conf);
676005491d2cSKalle Valo cfg->conf = NULL;
676105491d2cSKalle Valo kfree(cfg->extra_buf);
676205491d2cSKalle Valo cfg->extra_buf = NULL;
67633021ad9aSHante Meuleman kfree(cfg->wowl.nd);
67643021ad9aSHante Meuleman cfg->wowl.nd = NULL;
67653021ad9aSHante Meuleman kfree(cfg->wowl.nd_info);
67663021ad9aSHante Meuleman cfg->wowl.nd_info = NULL;
6767d5367334SHante Meuleman kfree(cfg->escan_info.escan_buf);
6768d5367334SHante Meuleman cfg->escan_info.escan_buf = NULL;
676905491d2cSKalle Valo }
677005491d2cSKalle Valo
brcmf_init_priv_mem(struct brcmf_cfg80211_info * cfg)677105491d2cSKalle Valo static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_info *cfg)
677205491d2cSKalle Valo {
677305491d2cSKalle Valo cfg->conf = kzalloc(sizeof(*cfg->conf), GFP_KERNEL);
677405491d2cSKalle Valo if (!cfg->conf)
677505491d2cSKalle Valo goto init_priv_mem_out;
677605491d2cSKalle Valo cfg->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
677705491d2cSKalle Valo if (!cfg->extra_buf)
677805491d2cSKalle Valo goto init_priv_mem_out;
67793021ad9aSHante Meuleman cfg->wowl.nd = kzalloc(sizeof(*cfg->wowl.nd) + sizeof(u32), GFP_KERNEL);
67803021ad9aSHante Meuleman if (!cfg->wowl.nd)
67813021ad9aSHante Meuleman goto init_priv_mem_out;
67823021ad9aSHante Meuleman cfg->wowl.nd_info = kzalloc(sizeof(*cfg->wowl.nd_info) +
67833021ad9aSHante Meuleman sizeof(struct cfg80211_wowlan_nd_match *),
67843021ad9aSHante Meuleman GFP_KERNEL);
67853021ad9aSHante Meuleman if (!cfg->wowl.nd_info)
67863021ad9aSHante Meuleman goto init_priv_mem_out;
6787d5367334SHante Meuleman cfg->escan_info.escan_buf = kzalloc(BRCMF_ESCAN_BUF_SIZE, GFP_KERNEL);
6788d5367334SHante Meuleman if (!cfg->escan_info.escan_buf)
6789d5367334SHante Meuleman goto init_priv_mem_out;
679005491d2cSKalle Valo
679105491d2cSKalle Valo return 0;
679205491d2cSKalle Valo
679305491d2cSKalle Valo init_priv_mem_out:
679405491d2cSKalle Valo brcmf_deinit_priv_mem(cfg);
679505491d2cSKalle Valo
679605491d2cSKalle Valo return -ENOMEM;
679705491d2cSKalle Valo }
679805491d2cSKalle Valo
wl_init_priv(struct brcmf_cfg80211_info * cfg)679905491d2cSKalle Valo static s32 wl_init_priv(struct brcmf_cfg80211_info *cfg)
680005491d2cSKalle Valo {
680105491d2cSKalle Valo s32 err = 0;
680205491d2cSKalle Valo
680305491d2cSKalle Valo cfg->scan_request = NULL;
680405491d2cSKalle Valo cfg->pwr_save = true;
680505491d2cSKalle Valo cfg->dongle_up = false; /* dongle is not up yet */
680605491d2cSKalle Valo err = brcmf_init_priv_mem(cfg);
680705491d2cSKalle Valo if (err)
680805491d2cSKalle Valo return err;
680905491d2cSKalle Valo brcmf_register_event_handlers(cfg);
681005491d2cSKalle Valo mutex_init(&cfg->usr_sync);
681105491d2cSKalle Valo brcmf_init_escan(cfg);
681205491d2cSKalle Valo brcmf_init_conf(cfg->conf);
681392072e5fSSaravanan Shanmugham brcmf_init_wmm_prio(cfg->ac_priority);
681405491d2cSKalle Valo init_completion(&cfg->vif_disabled);
681505491d2cSKalle Valo return err;
681605491d2cSKalle Valo }
681705491d2cSKalle Valo
wl_deinit_priv(struct brcmf_cfg80211_info * cfg)681805491d2cSKalle Valo static void wl_deinit_priv(struct brcmf_cfg80211_info *cfg)
681905491d2cSKalle Valo {
682005491d2cSKalle Valo cfg->dongle_up = false; /* dongle down */
682105491d2cSKalle Valo brcmf_abort_scanning(cfg);
682205491d2cSKalle Valo brcmf_deinit_priv_mem(cfg);
68235606aeaaSWataru Gohda brcmf_clear_assoc_ies(cfg);
682405491d2cSKalle Valo }
682505491d2cSKalle Valo
init_vif_event(struct brcmf_cfg80211_vif_event * event)682605491d2cSKalle Valo static void init_vif_event(struct brcmf_cfg80211_vif_event *event)
682705491d2cSKalle Valo {
682805491d2cSKalle Valo init_waitqueue_head(&event->vif_wq);
6829b64abcb7Smhiramat@kernel.org spin_lock_init(&event->vif_event_lock);
683005491d2cSKalle Valo }
683105491d2cSKalle Valo
brcmf_dongle_roam(struct brcmf_if * ifp)68321119e23eSHante Meuleman static s32 brcmf_dongle_roam(struct brcmf_if *ifp)
683305491d2cSKalle Valo {
683416e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
68351119e23eSHante Meuleman s32 err;
68361119e23eSHante Meuleman u32 bcn_timeout;
683705491d2cSKalle Valo __le32 roamtrigger[2];
683805491d2cSKalle Valo __le32 roam_delta[2];
683905491d2cSKalle Valo
68401119e23eSHante Meuleman /* Configure beacon timeout value based upon roaming setting */
68417d34b056SHante Meuleman if (ifp->drvr->settings->roamoff)
68421119e23eSHante Meuleman bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_OFF;
68431119e23eSHante Meuleman else
68441119e23eSHante Meuleman bcn_timeout = BRCMF_DEFAULT_BCN_TIMEOUT_ROAM_ON;
684505491d2cSKalle Valo err = brcmf_fil_iovar_int_set(ifp, "bcn_timeout", bcn_timeout);
684605491d2cSKalle Valo if (err) {
684716e64676SRafał Miłecki bphy_err(drvr, "bcn_timeout error (%d)\n", err);
68481119e23eSHante Meuleman goto roam_setup_done;
684905491d2cSKalle Valo }
685005491d2cSKalle Valo
68511119e23eSHante Meuleman /* Enable/Disable built-in roaming to allow supplicant to take care of
68521119e23eSHante Meuleman * roaming.
685305491d2cSKalle Valo */
685405491d2cSKalle Valo brcmf_dbg(INFO, "Internal Roaming = %s\n",
68557d34b056SHante Meuleman ifp->drvr->settings->roamoff ? "Off" : "On");
68567d34b056SHante Meuleman err = brcmf_fil_iovar_int_set(ifp, "roam_off",
68577d34b056SHante Meuleman ifp->drvr->settings->roamoff);
685805491d2cSKalle Valo if (err) {
685916e64676SRafał Miłecki bphy_err(drvr, "roam_off error (%d)\n", err);
68601119e23eSHante Meuleman goto roam_setup_done;
686105491d2cSKalle Valo }
686205491d2cSKalle Valo
686305491d2cSKalle Valo roamtrigger[0] = cpu_to_le32(WL_ROAM_TRIGGER_LEVEL);
686405491d2cSKalle Valo roamtrigger[1] = cpu_to_le32(BRCM_BAND_ALL);
686505491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_TRIGGER,
686605491d2cSKalle Valo (void *)roamtrigger, sizeof(roamtrigger));
6867c12c8913SSoeren Moch if (err)
686816e64676SRafał Miłecki bphy_err(drvr, "WLC_SET_ROAM_TRIGGER error (%d)\n", err);
686905491d2cSKalle Valo
687005491d2cSKalle Valo roam_delta[0] = cpu_to_le32(WL_ROAM_DELTA);
687105491d2cSKalle Valo roam_delta[1] = cpu_to_le32(BRCM_BAND_ALL);
687205491d2cSKalle Valo err = brcmf_fil_cmd_data_set(ifp, BRCMF_C_SET_ROAM_DELTA,
687305491d2cSKalle Valo (void *)roam_delta, sizeof(roam_delta));
6874c12c8913SSoeren Moch if (err)
687516e64676SRafał Miłecki bphy_err(drvr, "WLC_SET_ROAM_DELTA error (%d)\n", err);
6876c12c8913SSoeren Moch
6877c12c8913SSoeren Moch return 0;
687805491d2cSKalle Valo
68791119e23eSHante Meuleman roam_setup_done:
688005491d2cSKalle Valo return err;
688105491d2cSKalle Valo }
688205491d2cSKalle Valo
688305491d2cSKalle Valo static s32
brcmf_dongle_scantime(struct brcmf_if * ifp)68841678ba8eSHante Meuleman brcmf_dongle_scantime(struct brcmf_if *ifp)
688505491d2cSKalle Valo {
688616e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
688705491d2cSKalle Valo s32 err = 0;
688805491d2cSKalle Valo
688905491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_CHANNEL_TIME,
68901678ba8eSHante Meuleman BRCMF_SCAN_CHANNEL_TIME);
689105491d2cSKalle Valo if (err) {
689216e64676SRafał Miłecki bphy_err(drvr, "Scan assoc time error (%d)\n", err);
689305491d2cSKalle Valo goto dongle_scantime_out;
689405491d2cSKalle Valo }
689505491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_UNASSOC_TIME,
68961678ba8eSHante Meuleman BRCMF_SCAN_UNASSOC_TIME);
689705491d2cSKalle Valo if (err) {
689816e64676SRafał Miłecki bphy_err(drvr, "Scan unassoc time error (%d)\n", err);
689905491d2cSKalle Valo goto dongle_scantime_out;
690005491d2cSKalle Valo }
690105491d2cSKalle Valo
690205491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_SCAN_PASSIVE_TIME,
69031678ba8eSHante Meuleman BRCMF_SCAN_PASSIVE_TIME);
690405491d2cSKalle Valo if (err) {
690516e64676SRafał Miłecki bphy_err(drvr, "Scan passive time error (%d)\n", err);
690605491d2cSKalle Valo goto dongle_scantime_out;
690705491d2cSKalle Valo }
690805491d2cSKalle Valo
690905491d2cSKalle Valo dongle_scantime_out:
691005491d2cSKalle Valo return err;
691105491d2cSKalle Valo }
691205491d2cSKalle Valo
brcmf_update_bw40_channel_flag(struct ieee80211_channel * channel,struct brcmu_chan * ch)691305491d2cSKalle Valo static void brcmf_update_bw40_channel_flag(struct ieee80211_channel *channel,
691405491d2cSKalle Valo struct brcmu_chan *ch)
691505491d2cSKalle Valo {
691605491d2cSKalle Valo u32 ht40_flag;
691705491d2cSKalle Valo
691805491d2cSKalle Valo ht40_flag = channel->flags & IEEE80211_CHAN_NO_HT40;
691905491d2cSKalle Valo if (ch->sb == BRCMU_CHAN_SB_U) {
692005491d2cSKalle Valo if (ht40_flag == IEEE80211_CHAN_NO_HT40)
692105491d2cSKalle Valo channel->flags &= ~IEEE80211_CHAN_NO_HT40;
692205491d2cSKalle Valo channel->flags |= IEEE80211_CHAN_NO_HT40PLUS;
692305491d2cSKalle Valo } else {
692405491d2cSKalle Valo /* It should be one of
692505491d2cSKalle Valo * IEEE80211_CHAN_NO_HT40 or
692605491d2cSKalle Valo * IEEE80211_CHAN_NO_HT40PLUS
692705491d2cSKalle Valo */
692805491d2cSKalle Valo channel->flags &= ~IEEE80211_CHAN_NO_HT40;
692905491d2cSKalle Valo if (ht40_flag == IEEE80211_CHAN_NO_HT40)
693005491d2cSKalle Valo channel->flags |= IEEE80211_CHAN_NO_HT40MINUS;
693105491d2cSKalle Valo }
693205491d2cSKalle Valo }
693305491d2cSKalle Valo
brcmf_construct_chaninfo(struct brcmf_cfg80211_info * cfg,u32 bw_cap[])693405491d2cSKalle Valo static int brcmf_construct_chaninfo(struct brcmf_cfg80211_info *cfg,
693505491d2cSKalle Valo u32 bw_cap[])
693605491d2cSKalle Valo {
69373ef005b8SRafał Miłecki struct wiphy *wiphy = cfg_to_wiphy(cfg);
693816e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
693916e64676SRafał Miłecki struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
694005491d2cSKalle Valo struct ieee80211_supported_band *band;
694105491d2cSKalle Valo struct ieee80211_channel *channel;
694205491d2cSKalle Valo struct brcmf_chanspec_list *list;
694305491d2cSKalle Valo struct brcmu_chan ch;
694405491d2cSKalle Valo int err;
694505491d2cSKalle Valo u8 *pbuf;
694605491d2cSKalle Valo u32 i, j;
694705491d2cSKalle Valo u32 total;
694805491d2cSKalle Valo u32 chaninfo;
694905491d2cSKalle Valo
695005491d2cSKalle Valo pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
695105491d2cSKalle Valo
695205491d2cSKalle Valo if (pbuf == NULL)
695305491d2cSKalle Valo return -ENOMEM;
695405491d2cSKalle Valo
695505491d2cSKalle Valo list = (struct brcmf_chanspec_list *)pbuf;
695605491d2cSKalle Valo
695705491d2cSKalle Valo err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
695805491d2cSKalle Valo BRCMF_DCMD_MEDLEN);
695905491d2cSKalle Valo if (err) {
696016e64676SRafał Miłecki bphy_err(drvr, "get chanspecs error (%d)\n", err);
696105491d2cSKalle Valo goto fail_pbuf;
696205491d2cSKalle Valo }
696305491d2cSKalle Valo
696457fbcce3SJohannes Berg band = wiphy->bands[NL80211_BAND_2GHZ];
696505491d2cSKalle Valo if (band)
696605491d2cSKalle Valo for (i = 0; i < band->n_channels; i++)
696705491d2cSKalle Valo band->channels[i].flags = IEEE80211_CHAN_DISABLED;
696857fbcce3SJohannes Berg band = wiphy->bands[NL80211_BAND_5GHZ];
696905491d2cSKalle Valo if (band)
697005491d2cSKalle Valo for (i = 0; i < band->n_channels; i++)
697105491d2cSKalle Valo band->channels[i].flags = IEEE80211_CHAN_DISABLED;
697205491d2cSKalle Valo
697305491d2cSKalle Valo total = le32_to_cpu(list->count);
69744920ab13SMinsuk Kang if (total > BRCMF_MAX_CHANSPEC_LIST) {
69754920ab13SMinsuk Kang bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
69764920ab13SMinsuk Kang total);
69774920ab13SMinsuk Kang err = -EINVAL;
69784920ab13SMinsuk Kang goto fail_pbuf;
69794920ab13SMinsuk Kang }
69804920ab13SMinsuk Kang
698105491d2cSKalle Valo for (i = 0; i < total; i++) {
698205491d2cSKalle Valo ch.chspec = (u16)le32_to_cpu(list->element[i]);
698305491d2cSKalle Valo cfg->d11inf.decchspec(&ch);
698405491d2cSKalle Valo
698505491d2cSKalle Valo if (ch.band == BRCMU_CHAN_BAND_2G) {
698657fbcce3SJohannes Berg band = wiphy->bands[NL80211_BAND_2GHZ];
698705491d2cSKalle Valo } else if (ch.band == BRCMU_CHAN_BAND_5G) {
698857fbcce3SJohannes Berg band = wiphy->bands[NL80211_BAND_5GHZ];
698905491d2cSKalle Valo } else {
699016e64676SRafał Miłecki bphy_err(drvr, "Invalid channel Spec. 0x%x.\n",
69913ef005b8SRafał Miłecki ch.chspec);
699205491d2cSKalle Valo continue;
699305491d2cSKalle Valo }
699405491d2cSKalle Valo if (!band)
699505491d2cSKalle Valo continue;
699605491d2cSKalle Valo if (!(bw_cap[band->band] & WLC_BW_40MHZ_BIT) &&
699705491d2cSKalle Valo ch.bw == BRCMU_CHAN_BW_40)
699805491d2cSKalle Valo continue;
699905491d2cSKalle Valo if (!(bw_cap[band->band] & WLC_BW_80MHZ_BIT) &&
700005491d2cSKalle Valo ch.bw == BRCMU_CHAN_BW_80)
700105491d2cSKalle Valo continue;
700205491d2cSKalle Valo
700377c0d0cdSRafał Miłecki channel = NULL;
700405491d2cSKalle Valo for (j = 0; j < band->n_channels; j++) {
700577c0d0cdSRafał Miłecki if (band->channels[j].hw_value == ch.control_ch_num) {
700677c0d0cdSRafał Miłecki channel = &band->channels[j];
700705491d2cSKalle Valo break;
700805491d2cSKalle Valo }
700905491d2cSKalle Valo }
701077c0d0cdSRafał Miłecki if (!channel) {
701177c0d0cdSRafał Miłecki /* It seems firmware supports some channel we never
701277c0d0cdSRafał Miłecki * considered. Something new in IEEE standard?
701377c0d0cdSRafał Miłecki */
701416e64676SRafał Miłecki bphy_err(drvr, "Ignoring unexpected firmware channel %d\n",
701577c0d0cdSRafał Miłecki ch.control_ch_num);
701677c0d0cdSRafał Miłecki continue;
701777c0d0cdSRafał Miłecki }
701805491d2cSKalle Valo
70190f83ff69SRafał Miłecki if (channel->orig_flags & IEEE80211_CHAN_DISABLED)
70200f83ff69SRafał Miłecki continue;
70210f83ff69SRafał Miłecki
702205491d2cSKalle Valo /* assuming the chanspecs order is HT20,
702305491d2cSKalle Valo * HT40 upper, HT40 lower, and VHT80.
702405491d2cSKalle Valo */
7025f4e18329SRafał Miłecki switch (ch.bw) {
702630519cbeSRafał Miłecki case BRCMU_CHAN_BW_160:
702730519cbeSRafał Miłecki channel->flags &= ~IEEE80211_CHAN_NO_160MHZ;
702830519cbeSRafał Miłecki break;
7029f4e18329SRafał Miłecki case BRCMU_CHAN_BW_80:
703077c0d0cdSRafał Miłecki channel->flags &= ~IEEE80211_CHAN_NO_80MHZ;
7031f4e18329SRafał Miłecki break;
7032f4e18329SRafał Miłecki case BRCMU_CHAN_BW_40:
703377c0d0cdSRafał Miłecki brcmf_update_bw40_channel_flag(channel, &ch);
7034f4e18329SRafał Miłecki break;
7035f4e18329SRafał Miłecki default:
7036f4e18329SRafał Miłecki wiphy_warn(wiphy, "Firmware reported unsupported bandwidth %d\n",
7037f4e18329SRafał Miłecki ch.bw);
70380bde10dcSGustavo A. R. Silva fallthrough;
7039f4e18329SRafał Miłecki case BRCMU_CHAN_BW_20:
704005491d2cSKalle Valo /* enable the channel and disable other bandwidths
704105491d2cSKalle Valo * for now as mentioned order assure they are enabled
704205491d2cSKalle Valo * for subsequent chanspecs.
704305491d2cSKalle Valo */
704477c0d0cdSRafał Miłecki channel->flags = IEEE80211_CHAN_NO_HT40 |
7045d1fe6ad6SRafał Miłecki IEEE80211_CHAN_NO_80MHZ |
7046d1fe6ad6SRafał Miłecki IEEE80211_CHAN_NO_160MHZ;
704705491d2cSKalle Valo ch.bw = BRCMU_CHAN_BW_20;
704805491d2cSKalle Valo cfg->d11inf.encchspec(&ch);
704905491d2cSKalle Valo chaninfo = ch.chspec;
7050*fb1862ceSArend van Spriel err = brcmf_fil_bsscfg_int_query(ifp, "per_chan_info",
705105491d2cSKalle Valo &chaninfo);
705205491d2cSKalle Valo if (!err) {
705305491d2cSKalle Valo if (chaninfo & WL_CHAN_RADAR)
705477c0d0cdSRafał Miłecki channel->flags |=
705505491d2cSKalle Valo (IEEE80211_CHAN_RADAR |
705605491d2cSKalle Valo IEEE80211_CHAN_NO_IR);
705705491d2cSKalle Valo if (chaninfo & WL_CHAN_PASSIVE)
705877c0d0cdSRafał Miłecki channel->flags |=
705905491d2cSKalle Valo IEEE80211_CHAN_NO_IR;
706005491d2cSKalle Valo }
706105491d2cSKalle Valo }
706205491d2cSKalle Valo }
706305491d2cSKalle Valo
706405491d2cSKalle Valo fail_pbuf:
706505491d2cSKalle Valo kfree(pbuf);
706605491d2cSKalle Valo return err;
706705491d2cSKalle Valo }
706805491d2cSKalle Valo
brcmf_enable_bw40_2g(struct brcmf_cfg80211_info * cfg)706905491d2cSKalle Valo static int brcmf_enable_bw40_2g(struct brcmf_cfg80211_info *cfg)
707005491d2cSKalle Valo {
707116e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
707216e64676SRafał Miłecki struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
707305491d2cSKalle Valo struct ieee80211_supported_band *band;
707405491d2cSKalle Valo struct brcmf_fil_bwcap_le band_bwcap;
707505491d2cSKalle Valo struct brcmf_chanspec_list *list;
707605491d2cSKalle Valo u8 *pbuf;
707705491d2cSKalle Valo u32 val;
707805491d2cSKalle Valo int err;
707905491d2cSKalle Valo struct brcmu_chan ch;
708005491d2cSKalle Valo u32 num_chan;
708105491d2cSKalle Valo int i, j;
708205491d2cSKalle Valo
708305491d2cSKalle Valo /* verify support for bw_cap command */
708405491d2cSKalle Valo val = WLC_BAND_5G;
7085*fb1862ceSArend van Spriel err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &val);
708605491d2cSKalle Valo
708705491d2cSKalle Valo if (!err) {
708805491d2cSKalle Valo /* only set 2G bandwidth using bw_cap command */
708905491d2cSKalle Valo band_bwcap.band = cpu_to_le32(WLC_BAND_2G);
709005491d2cSKalle Valo band_bwcap.bw_cap = cpu_to_le32(WLC_BW_CAP_40MHZ);
709105491d2cSKalle Valo err = brcmf_fil_iovar_data_set(ifp, "bw_cap", &band_bwcap,
709205491d2cSKalle Valo sizeof(band_bwcap));
709305491d2cSKalle Valo } else {
709405491d2cSKalle Valo brcmf_dbg(INFO, "fallback to mimo_bw_cap\n");
709505491d2cSKalle Valo val = WLC_N_BW_40ALL;
709605491d2cSKalle Valo err = brcmf_fil_iovar_int_set(ifp, "mimo_bw_cap", val);
709705491d2cSKalle Valo }
709805491d2cSKalle Valo
709905491d2cSKalle Valo if (!err) {
710005491d2cSKalle Valo /* update channel info in 2G band */
710105491d2cSKalle Valo pbuf = kzalloc(BRCMF_DCMD_MEDLEN, GFP_KERNEL);
710205491d2cSKalle Valo
710305491d2cSKalle Valo if (pbuf == NULL)
710405491d2cSKalle Valo return -ENOMEM;
710505491d2cSKalle Valo
710605491d2cSKalle Valo ch.band = BRCMU_CHAN_BAND_2G;
710705491d2cSKalle Valo ch.bw = BRCMU_CHAN_BW_40;
710805491d2cSKalle Valo ch.sb = BRCMU_CHAN_SB_NONE;
710905491d2cSKalle Valo ch.chnum = 0;
711005491d2cSKalle Valo cfg->d11inf.encchspec(&ch);
711105491d2cSKalle Valo
711205491d2cSKalle Valo /* pass encoded chanspec in query */
711305491d2cSKalle Valo *(__le16 *)pbuf = cpu_to_le16(ch.chspec);
711405491d2cSKalle Valo
711505491d2cSKalle Valo err = brcmf_fil_iovar_data_get(ifp, "chanspecs", pbuf,
711605491d2cSKalle Valo BRCMF_DCMD_MEDLEN);
711705491d2cSKalle Valo if (err) {
711816e64676SRafał Miłecki bphy_err(drvr, "get chanspecs error (%d)\n", err);
711905491d2cSKalle Valo kfree(pbuf);
712005491d2cSKalle Valo return err;
712105491d2cSKalle Valo }
712205491d2cSKalle Valo
712357fbcce3SJohannes Berg band = cfg_to_wiphy(cfg)->bands[NL80211_BAND_2GHZ];
712405491d2cSKalle Valo list = (struct brcmf_chanspec_list *)pbuf;
712505491d2cSKalle Valo num_chan = le32_to_cpu(list->count);
71264920ab13SMinsuk Kang if (num_chan > BRCMF_MAX_CHANSPEC_LIST) {
71274920ab13SMinsuk Kang bphy_err(drvr, "Invalid count of channel Spec. (%u)\n",
71284920ab13SMinsuk Kang num_chan);
71294920ab13SMinsuk Kang kfree(pbuf);
71304920ab13SMinsuk Kang return -EINVAL;
71314920ab13SMinsuk Kang }
71324920ab13SMinsuk Kang
713305491d2cSKalle Valo for (i = 0; i < num_chan; i++) {
713405491d2cSKalle Valo ch.chspec = (u16)le32_to_cpu(list->element[i]);
713505491d2cSKalle Valo cfg->d11inf.decchspec(&ch);
713605491d2cSKalle Valo if (WARN_ON(ch.band != BRCMU_CHAN_BAND_2G))
713705491d2cSKalle Valo continue;
713805491d2cSKalle Valo if (WARN_ON(ch.bw != BRCMU_CHAN_BW_40))
713905491d2cSKalle Valo continue;
714005491d2cSKalle Valo for (j = 0; j < band->n_channels; j++) {
71414712d88aSRafał Miłecki if (band->channels[j].hw_value == ch.control_ch_num)
714205491d2cSKalle Valo break;
714305491d2cSKalle Valo }
714405491d2cSKalle Valo if (WARN_ON(j == band->n_channels))
714505491d2cSKalle Valo continue;
714605491d2cSKalle Valo
714705491d2cSKalle Valo brcmf_update_bw40_channel_flag(&band->channels[j], &ch);
714805491d2cSKalle Valo }
714905491d2cSKalle Valo kfree(pbuf);
715005491d2cSKalle Valo }
715105491d2cSKalle Valo return err;
715205491d2cSKalle Valo }
715305491d2cSKalle Valo
brcmf_get_bwcap(struct brcmf_if * ifp,u32 bw_cap[])715405491d2cSKalle Valo static void brcmf_get_bwcap(struct brcmf_if *ifp, u32 bw_cap[])
715505491d2cSKalle Valo {
715616e64676SRafał Miłecki struct brcmf_pub *drvr = ifp->drvr;
715705491d2cSKalle Valo u32 band, mimo_bwcap;
715805491d2cSKalle Valo int err;
715905491d2cSKalle Valo
716005491d2cSKalle Valo band = WLC_BAND_2G;
7161*fb1862ceSArend van Spriel err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);
716205491d2cSKalle Valo if (!err) {
716357fbcce3SJohannes Berg bw_cap[NL80211_BAND_2GHZ] = band;
716405491d2cSKalle Valo band = WLC_BAND_5G;
7165*fb1862ceSArend van Spriel err = brcmf_fil_iovar_int_query(ifp, "bw_cap", &band);
716605491d2cSKalle Valo if (!err) {
716757fbcce3SJohannes Berg bw_cap[NL80211_BAND_5GHZ] = band;
716805491d2cSKalle Valo return;
716905491d2cSKalle Valo }
717005491d2cSKalle Valo WARN_ON(1);
717105491d2cSKalle Valo return;
717205491d2cSKalle Valo }
717305491d2cSKalle Valo brcmf_dbg(INFO, "fallback to mimo_bw_cap info\n");
717405491d2cSKalle Valo err = brcmf_fil_iovar_int_get(ifp, "mimo_bw_cap", &mimo_bwcap);
717505491d2cSKalle Valo if (err)
717605491d2cSKalle Valo /* assume 20MHz if firmware does not give a clue */
717705491d2cSKalle Valo mimo_bwcap = WLC_N_BW_20ALL;
717805491d2cSKalle Valo
717905491d2cSKalle Valo switch (mimo_bwcap) {
718005491d2cSKalle Valo case WLC_N_BW_40ALL:
718157fbcce3SJohannes Berg bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_40MHZ_BIT;
71820bde10dcSGustavo A. R. Silva fallthrough;
718305491d2cSKalle Valo case WLC_N_BW_20IN2G_40IN5G:
718457fbcce3SJohannes Berg bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_40MHZ_BIT;
71850bde10dcSGustavo A. R. Silva fallthrough;
718605491d2cSKalle Valo case WLC_N_BW_20ALL:
718757fbcce3SJohannes Berg bw_cap[NL80211_BAND_2GHZ] |= WLC_BW_20MHZ_BIT;
718857fbcce3SJohannes Berg bw_cap[NL80211_BAND_5GHZ] |= WLC_BW_20MHZ_BIT;
718905491d2cSKalle Valo break;
719005491d2cSKalle Valo default:
719116e64676SRafał Miłecki bphy_err(drvr, "invalid mimo_bw_cap value\n");
719205491d2cSKalle Valo }
719305491d2cSKalle Valo }
719405491d2cSKalle Valo
brcmf_update_ht_cap(struct ieee80211_supported_band * band,u32 bw_cap[2],u32 nchain)719505491d2cSKalle Valo static void brcmf_update_ht_cap(struct ieee80211_supported_band *band,
719605491d2cSKalle Valo u32 bw_cap[2], u32 nchain)
719705491d2cSKalle Valo {
719805491d2cSKalle Valo band->ht_cap.ht_supported = true;
719905491d2cSKalle Valo if (bw_cap[band->band] & WLC_BW_40MHZ_BIT) {
720005491d2cSKalle Valo band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_40;
720105491d2cSKalle Valo band->ht_cap.cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
720205491d2cSKalle Valo }
720305491d2cSKalle Valo band->ht_cap.cap |= IEEE80211_HT_CAP_SGI_20;
720405491d2cSKalle Valo band->ht_cap.cap |= IEEE80211_HT_CAP_DSSSCCK40;
720505491d2cSKalle Valo band->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
720605491d2cSKalle Valo band->ht_cap.ampdu_density = IEEE80211_HT_MPDU_DENSITY_16;
720705491d2cSKalle Valo memset(band->ht_cap.mcs.rx_mask, 0xff, nchain);
720805491d2cSKalle Valo band->ht_cap.mcs.tx_params = IEEE80211_HT_MCS_TX_DEFINED;
720905491d2cSKalle Valo }
721005491d2cSKalle Valo
brcmf_get_mcs_map(u32 nchain,enum ieee80211_vht_mcs_support supp)721105491d2cSKalle Valo static __le16 brcmf_get_mcs_map(u32 nchain, enum ieee80211_vht_mcs_support supp)
721205491d2cSKalle Valo {
721305491d2cSKalle Valo u16 mcs_map;
721405491d2cSKalle Valo int i;
721505491d2cSKalle Valo
721605491d2cSKalle Valo for (i = 0, mcs_map = 0xFFFF; i < nchain; i++)
721705491d2cSKalle Valo mcs_map = (mcs_map << 2) | supp;
721805491d2cSKalle Valo
721905491d2cSKalle Valo return cpu_to_le16(mcs_map);
722005491d2cSKalle Valo }
722105491d2cSKalle Valo
brcmf_update_vht_cap(struct ieee80211_supported_band * band,u32 bw_cap[2],u32 nchain,u32 txstreams,u32 txbf_bfe_cap,u32 txbf_bfr_cap)722205491d2cSKalle Valo static void brcmf_update_vht_cap(struct ieee80211_supported_band *band,
72237bf65aa9SHante Meuleman u32 bw_cap[2], u32 nchain, u32 txstreams,
72247bf65aa9SHante Meuleman u32 txbf_bfe_cap, u32 txbf_bfr_cap)
722505491d2cSKalle Valo {
722605491d2cSKalle Valo __le16 mcs_map;
722705491d2cSKalle Valo
722805491d2cSKalle Valo /* not allowed in 2.4G band */
722957fbcce3SJohannes Berg if (band->band == NL80211_BAND_2GHZ)
723005491d2cSKalle Valo return;
723105491d2cSKalle Valo
723205491d2cSKalle Valo band->vht_cap.vht_supported = true;
723305491d2cSKalle Valo /* 80MHz is mandatory */
723405491d2cSKalle Valo band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_80;
723505491d2cSKalle Valo if (bw_cap[band->band] & WLC_BW_160MHZ_BIT) {
723605491d2cSKalle Valo band->vht_cap.cap |= IEEE80211_VHT_CAP_SUPP_CHAN_WIDTH_160MHZ;
723705491d2cSKalle Valo band->vht_cap.cap |= IEEE80211_VHT_CAP_SHORT_GI_160;
723805491d2cSKalle Valo }
723905491d2cSKalle Valo /* all support 256-QAM */
724005491d2cSKalle Valo mcs_map = brcmf_get_mcs_map(nchain, IEEE80211_VHT_MCS_SUPPORT_0_9);
724105491d2cSKalle Valo band->vht_cap.vht_mcs.rx_mcs_map = mcs_map;
724205491d2cSKalle Valo band->vht_cap.vht_mcs.tx_mcs_map = mcs_map;
72437bf65aa9SHante Meuleman
72447bf65aa9SHante Meuleman /* Beamforming support information */
72457bf65aa9SHante Meuleman if (txbf_bfe_cap & BRCMF_TXBF_SU_BFE_CAP)
72467bf65aa9SHante Meuleman band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMEE_CAPABLE;
72477bf65aa9SHante Meuleman if (txbf_bfe_cap & BRCMF_TXBF_MU_BFE_CAP)
72487bf65aa9SHante Meuleman band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMEE_CAPABLE;
72497bf65aa9SHante Meuleman if (txbf_bfr_cap & BRCMF_TXBF_SU_BFR_CAP)
72507bf65aa9SHante Meuleman band->vht_cap.cap |= IEEE80211_VHT_CAP_SU_BEAMFORMER_CAPABLE;
72517bf65aa9SHante Meuleman if (txbf_bfr_cap & BRCMF_TXBF_MU_BFR_CAP)
72527bf65aa9SHante Meuleman band->vht_cap.cap |= IEEE80211_VHT_CAP_MU_BEAMFORMER_CAPABLE;
72537bf65aa9SHante Meuleman
72547bf65aa9SHante Meuleman if ((txbf_bfe_cap || txbf_bfr_cap) && (txstreams > 1)) {
72557bf65aa9SHante Meuleman band->vht_cap.cap |=
72567bf65aa9SHante Meuleman (2 << IEEE80211_VHT_CAP_BEAMFORMEE_STS_SHIFT);
72577bf65aa9SHante Meuleman band->vht_cap.cap |= ((txstreams - 1) <<
72587bf65aa9SHante Meuleman IEEE80211_VHT_CAP_SOUNDING_DIMENSIONS_SHIFT);
72597bf65aa9SHante Meuleman band->vht_cap.cap |=
72607bf65aa9SHante Meuleman IEEE80211_VHT_CAP_VHT_LINK_ADAPTATION_VHT_MRQ_MFB;
72617bf65aa9SHante Meuleman }
726205491d2cSKalle Valo }
726305491d2cSKalle Valo
brcmf_setup_wiphybands(struct brcmf_cfg80211_info * cfg)7264856d5a01SArend Van Spriel static int brcmf_setup_wiphybands(struct brcmf_cfg80211_info *cfg)
726505491d2cSKalle Valo {
726616e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
726716e64676SRafał Miłecki struct brcmf_if *ifp = brcmf_get_ifp(drvr, 0);
72683ef005b8SRafał Miłecki struct wiphy *wiphy = cfg_to_wiphy(cfg);
7269*fb1862ceSArend van Spriel u32 nmode;
727005491d2cSKalle Valo u32 vhtmode = 0;
727105491d2cSKalle Valo u32 bw_cap[2] = { WLC_BW_20MHZ_BIT, WLC_BW_20MHZ_BIT };
7272*fb1862ceSArend van Spriel u32 rxchain;
727305491d2cSKalle Valo u32 nchain;
727405491d2cSKalle Valo int err;
727505491d2cSKalle Valo s32 i;
727605491d2cSKalle Valo struct ieee80211_supported_band *band;
72777bf65aa9SHante Meuleman u32 txstreams = 0;
72787bf65aa9SHante Meuleman u32 txbf_bfe_cap = 0;
72797bf65aa9SHante Meuleman u32 txbf_bfr_cap = 0;
728005491d2cSKalle Valo
728105491d2cSKalle Valo (void)brcmf_fil_iovar_int_get(ifp, "vhtmode", &vhtmode);
728205491d2cSKalle Valo err = brcmf_fil_iovar_int_get(ifp, "nmode", &nmode);
728305491d2cSKalle Valo if (err) {
728416e64676SRafał Miłecki bphy_err(drvr, "nmode error (%d)\n", err);
728505491d2cSKalle Valo } else {
728605491d2cSKalle Valo brcmf_get_bwcap(ifp, bw_cap);
728705491d2cSKalle Valo }
728805491d2cSKalle Valo brcmf_dbg(INFO, "nmode=%d, vhtmode=%d, bw_cap=(%d, %d)\n",
728957fbcce3SJohannes Berg nmode, vhtmode, bw_cap[NL80211_BAND_2GHZ],
729057fbcce3SJohannes Berg bw_cap[NL80211_BAND_5GHZ]);
729105491d2cSKalle Valo
729205491d2cSKalle Valo err = brcmf_fil_iovar_int_get(ifp, "rxchain", &rxchain);
729305491d2cSKalle Valo if (err) {
729478f0a64fSDmitry Osipenko /* rxchain unsupported by firmware of older chips */
729578f0a64fSDmitry Osipenko if (err == -EBADE)
729678f0a64fSDmitry Osipenko bphy_info_once(drvr, "rxchain unsupported\n");
729778f0a64fSDmitry Osipenko else
729816e64676SRafał Miłecki bphy_err(drvr, "rxchain error (%d)\n", err);
729978f0a64fSDmitry Osipenko
730005491d2cSKalle Valo nchain = 1;
730105491d2cSKalle Valo } else {
730205491d2cSKalle Valo for (nchain = 0; rxchain; nchain++)
730305491d2cSKalle Valo rxchain = rxchain & (rxchain - 1);
730405491d2cSKalle Valo }
730505491d2cSKalle Valo brcmf_dbg(INFO, "nchain=%d\n", nchain);
730605491d2cSKalle Valo
730705491d2cSKalle Valo err = brcmf_construct_chaninfo(cfg, bw_cap);
730805491d2cSKalle Valo if (err) {
730916e64676SRafał Miłecki bphy_err(drvr, "brcmf_construct_chaninfo failed (%d)\n", err);
731005491d2cSKalle Valo return err;
731105491d2cSKalle Valo }
731205491d2cSKalle Valo
73137bf65aa9SHante Meuleman if (vhtmode) {
73147bf65aa9SHante Meuleman (void)brcmf_fil_iovar_int_get(ifp, "txstreams", &txstreams);
73157bf65aa9SHante Meuleman (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfe_cap",
73167bf65aa9SHante Meuleman &txbf_bfe_cap);
73177bf65aa9SHante Meuleman (void)brcmf_fil_iovar_int_get(ifp, "txbf_bfr_cap",
73187bf65aa9SHante Meuleman &txbf_bfr_cap);
73197bf65aa9SHante Meuleman }
73207bf65aa9SHante Meuleman
732105491d2cSKalle Valo for (i = 0; i < ARRAY_SIZE(wiphy->bands); i++) {
732205491d2cSKalle Valo band = wiphy->bands[i];
732305491d2cSKalle Valo if (band == NULL)
732405491d2cSKalle Valo continue;
732505491d2cSKalle Valo
732605491d2cSKalle Valo if (nmode)
732705491d2cSKalle Valo brcmf_update_ht_cap(band, bw_cap, nchain);
732805491d2cSKalle Valo if (vhtmode)
73297bf65aa9SHante Meuleman brcmf_update_vht_cap(band, bw_cap, nchain, txstreams,
73307bf65aa9SHante Meuleman txbf_bfe_cap, txbf_bfr_cap);
733105491d2cSKalle Valo }
733205491d2cSKalle Valo
733305491d2cSKalle Valo return 0;
733405491d2cSKalle Valo }
733505491d2cSKalle Valo
733605491d2cSKalle Valo static const struct ieee80211_txrx_stypes
733705491d2cSKalle Valo brcmf_txrx_stypes[NUM_NL80211_IFTYPES] = {
733805491d2cSKalle Valo [NL80211_IFTYPE_STATION] = {
733905491d2cSKalle Valo .tx = 0xffff,
734005491d2cSKalle Valo .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
734105491d2cSKalle Valo BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
734205491d2cSKalle Valo },
734305491d2cSKalle Valo [NL80211_IFTYPE_P2P_CLIENT] = {
734405491d2cSKalle Valo .tx = 0xffff,
734505491d2cSKalle Valo .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
734605491d2cSKalle Valo BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
734705491d2cSKalle Valo },
734805491d2cSKalle Valo [NL80211_IFTYPE_P2P_GO] = {
734905491d2cSKalle Valo .tx = 0xffff,
735005491d2cSKalle Valo .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
735105491d2cSKalle Valo BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
735205491d2cSKalle Valo BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
735305491d2cSKalle Valo BIT(IEEE80211_STYPE_DISASSOC >> 4) |
735405491d2cSKalle Valo BIT(IEEE80211_STYPE_AUTH >> 4) |
735505491d2cSKalle Valo BIT(IEEE80211_STYPE_DEAUTH >> 4) |
735605491d2cSKalle Valo BIT(IEEE80211_STYPE_ACTION >> 4)
735705491d2cSKalle Valo },
735805491d2cSKalle Valo [NL80211_IFTYPE_P2P_DEVICE] = {
735905491d2cSKalle Valo .tx = 0xffff,
736005491d2cSKalle Valo .rx = BIT(IEEE80211_STYPE_ACTION >> 4) |
736105491d2cSKalle Valo BIT(IEEE80211_STYPE_PROBE_REQ >> 4)
7362861cb5ebSStefan Wahren },
7363861cb5ebSStefan Wahren [NL80211_IFTYPE_AP] = {
7364861cb5ebSStefan Wahren .tx = 0xffff,
7365861cb5ebSStefan Wahren .rx = BIT(IEEE80211_STYPE_ASSOC_REQ >> 4) |
7366861cb5ebSStefan Wahren BIT(IEEE80211_STYPE_REASSOC_REQ >> 4) |
7367861cb5ebSStefan Wahren BIT(IEEE80211_STYPE_PROBE_REQ >> 4) |
7368861cb5ebSStefan Wahren BIT(IEEE80211_STYPE_DISASSOC >> 4) |
7369861cb5ebSStefan Wahren BIT(IEEE80211_STYPE_AUTH >> 4) |
7370861cb5ebSStefan Wahren BIT(IEEE80211_STYPE_DEAUTH >> 4) |
7371861cb5ebSStefan Wahren BIT(IEEE80211_STYPE_ACTION >> 4)
737205491d2cSKalle Valo }
737305491d2cSKalle Valo };
737405491d2cSKalle Valo
737505491d2cSKalle Valo /**
737605491d2cSKalle Valo * brcmf_setup_ifmodes() - determine interface modes and combinations.
737705491d2cSKalle Valo *
737805491d2cSKalle Valo * @wiphy: wiphy object.
737905491d2cSKalle Valo * @ifp: interface object needed for feat module api.
738005491d2cSKalle Valo *
738105491d2cSKalle Valo * The interface modes and combinations are determined dynamically here
738205491d2cSKalle Valo * based on firmware functionality.
738305491d2cSKalle Valo *
738405491d2cSKalle Valo * no p2p and no mbss:
738505491d2cSKalle Valo *
738605491d2cSKalle Valo * #STA <= 1, #AP <= 1, channels = 1, 2 total
738705491d2cSKalle Valo *
738805491d2cSKalle Valo * no p2p and mbss:
738905491d2cSKalle Valo *
739005491d2cSKalle Valo * #STA <= 1, #AP <= 1, channels = 1, 2 total
739105491d2cSKalle Valo * #AP <= 4, matching BI, channels = 1, 4 total
739205491d2cSKalle Valo *
7393837482e6SWright Feng * no p2p and rsdb:
7394ec3428bbSWright Feng * #STA <= 1, #AP <= 2, channels = 2, 4 total
7395837482e6SWright Feng *
739605491d2cSKalle Valo * p2p, no mchan, and mbss:
739705491d2cSKalle Valo *
739805491d2cSKalle Valo * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 1, 3 total
739905491d2cSKalle Valo * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
740005491d2cSKalle Valo * #AP <= 4, matching BI, channels = 1, 4 total
740105491d2cSKalle Valo *
740205491d2cSKalle Valo * p2p, mchan, and mbss:
740305491d2cSKalle Valo *
74044388827bSWright Feng * #STA <= 2, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 1, channels = 2, 3 total
740505491d2cSKalle Valo * #STA <= 1, #P2P-DEV <= 1, #AP <= 1, #P2P-CL <= 1, channels = 1, 4 total
740605491d2cSKalle Valo * #AP <= 4, matching BI, channels = 1, 4 total
7407837482e6SWright Feng *
7408837482e6SWright Feng * p2p, rsdb, and no mbss:
7409ec3428bbSWright Feng * #STA <= 1, #P2P-DEV <= 1, #{P2P-CL, P2P-GO} <= 2, AP <= 2,
7410837482e6SWright Feng * channels = 2, 4 total
741105491d2cSKalle Valo */
brcmf_setup_ifmodes(struct wiphy * wiphy,struct brcmf_if * ifp)741205491d2cSKalle Valo static int brcmf_setup_ifmodes(struct wiphy *wiphy, struct brcmf_if *ifp)
741305491d2cSKalle Valo {
741405491d2cSKalle Valo struct ieee80211_iface_combination *combo = NULL;
741505491d2cSKalle Valo struct ieee80211_iface_limit *c0_limits = NULL;
741605491d2cSKalle Valo struct ieee80211_iface_limit *p2p_limits = NULL;
741705491d2cSKalle Valo struct ieee80211_iface_limit *mbss_limits = NULL;
741820f2c5faSRafał Miłecki bool mon_flag, mbss, p2p, rsdb, mchan;
741920f2c5faSRafał Miłecki int i, c, n_combos, n_limits;
742005491d2cSKalle Valo
742120f2c5faSRafał Miłecki mon_flag = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MONITOR_FLAG);
742205491d2cSKalle Valo mbss = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MBSS);
742305491d2cSKalle Valo p2p = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_P2P);
7424837482e6SWright Feng rsdb = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_RSDB);
742524332f80SRafał Miłecki mchan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MCHAN);
742605491d2cSKalle Valo
7427837482e6SWright Feng n_combos = 1 + !!(p2p && !rsdb) + !!mbss;
742805491d2cSKalle Valo combo = kcalloc(n_combos, sizeof(*combo), GFP_KERNEL);
742905491d2cSKalle Valo if (!combo)
743005491d2cSKalle Valo goto err;
743105491d2cSKalle Valo
743205491d2cSKalle Valo wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION) |
743305491d2cSKalle Valo BIT(NL80211_IFTYPE_ADHOC) |
743405491d2cSKalle Valo BIT(NL80211_IFTYPE_AP);
743520f2c5faSRafał Miłecki if (mon_flag)
743620f2c5faSRafał Miłecki wiphy->interface_modes |= BIT(NL80211_IFTYPE_MONITOR);
743724332f80SRafał Miłecki if (p2p)
743824332f80SRafał Miłecki wiphy->interface_modes |= BIT(NL80211_IFTYPE_P2P_CLIENT) |
743924332f80SRafał Miłecki BIT(NL80211_IFTYPE_P2P_GO) |
744024332f80SRafał Miłecki BIT(NL80211_IFTYPE_P2P_DEVICE);
744105491d2cSKalle Valo
744205491d2cSKalle Valo c = 0;
744305491d2cSKalle Valo i = 0;
744420f2c5faSRafał Miłecki n_limits = 1 + mon_flag + (p2p ? 2 : 0) + (rsdb || !p2p);
744520f2c5faSRafał Miłecki c0_limits = kcalloc(n_limits, sizeof(*c0_limits), GFP_KERNEL);
7446f568adacSRafał Miłecki if (!c0_limits)
7447f568adacSRafał Miłecki goto err;
744824332f80SRafał Miłecki
744924332f80SRafał Miłecki combo[c].num_different_channels = 1 + (rsdb || (p2p && mchan));
74504388827bSWright Feng c0_limits[i].max = 1 + (p2p && mchan);
745105491d2cSKalle Valo c0_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
745220f2c5faSRafał Miłecki if (mon_flag) {
745320f2c5faSRafał Miłecki c0_limits[i].max = 1;
745420f2c5faSRafał Miłecki c0_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);
745520f2c5faSRafał Miłecki }
745624332f80SRafał Miłecki if (p2p) {
7457837482e6SWright Feng c0_limits[i].max = 1;
7458837482e6SWright Feng c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
745924332f80SRafał Miłecki c0_limits[i].max = 1 + rsdb;
7460837482e6SWright Feng c0_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT) |
7461837482e6SWright Feng BIT(NL80211_IFTYPE_P2P_GO);
746224332f80SRafał Miłecki }
746324332f80SRafał Miłecki if (p2p && rsdb) {
7464837482e6SWright Feng c0_limits[i].max = 2;
7465837482e6SWright Feng c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7466ec3428bbSWright Feng combo[c].max_interfaces = 4;
7467837482e6SWright Feng } else if (p2p) {
7468837482e6SWright Feng combo[c].max_interfaces = i;
7469837482e6SWright Feng } else if (rsdb) {
7470837482e6SWright Feng c0_limits[i].max = 2;
7471837482e6SWright Feng c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
7472837482e6SWright Feng combo[c].max_interfaces = 3;
747305491d2cSKalle Valo } else {
7474837482e6SWright Feng c0_limits[i].max = 1;
747505491d2cSKalle Valo c0_limits[i++].types = BIT(NL80211_IFTYPE_AP);
747605491d2cSKalle Valo combo[c].max_interfaces = i;
7477837482e6SWright Feng }
747805491d2cSKalle Valo combo[c].n_limits = i;
747905491d2cSKalle Valo combo[c].limits = c0_limits;
748005491d2cSKalle Valo
7481837482e6SWright Feng if (p2p && !rsdb) {
748205491d2cSKalle Valo c++;
748305491d2cSKalle Valo i = 0;
7484f568adacSRafał Miłecki p2p_limits = kcalloc(4, sizeof(*p2p_limits), GFP_KERNEL);
7485f568adacSRafał Miłecki if (!p2p_limits)
7486f568adacSRafał Miłecki goto err;
748705491d2cSKalle Valo p2p_limits[i].max = 1;
748805491d2cSKalle Valo p2p_limits[i++].types = BIT(NL80211_IFTYPE_STATION);
748905491d2cSKalle Valo p2p_limits[i].max = 1;
749005491d2cSKalle Valo p2p_limits[i++].types = BIT(NL80211_IFTYPE_AP);
749105491d2cSKalle Valo p2p_limits[i].max = 1;
749205491d2cSKalle Valo p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_CLIENT);
749305491d2cSKalle Valo p2p_limits[i].max = 1;
749405491d2cSKalle Valo p2p_limits[i++].types = BIT(NL80211_IFTYPE_P2P_DEVICE);
7495f568adacSRafał Miłecki combo[c].num_different_channels = 1;
749605491d2cSKalle Valo combo[c].max_interfaces = i;
749705491d2cSKalle Valo combo[c].n_limits = i;
749805491d2cSKalle Valo combo[c].limits = p2p_limits;
749905491d2cSKalle Valo }
750005491d2cSKalle Valo
750105491d2cSKalle Valo if (mbss) {
750205491d2cSKalle Valo c++;
7503f568adacSRafał Miłecki i = 0;
750420f2c5faSRafał Miłecki n_limits = 1 + mon_flag;
750520f2c5faSRafał Miłecki mbss_limits = kcalloc(n_limits, sizeof(*mbss_limits),
750620f2c5faSRafał Miłecki GFP_KERNEL);
7507f568adacSRafał Miłecki if (!mbss_limits)
7508f568adacSRafał Miłecki goto err;
7509f568adacSRafał Miłecki mbss_limits[i].max = 4;
7510f568adacSRafał Miłecki mbss_limits[i++].types = BIT(NL80211_IFTYPE_AP);
751120f2c5faSRafał Miłecki if (mon_flag) {
751220f2c5faSRafał Miłecki mbss_limits[i].max = 1;
751320f2c5faSRafał Miłecki mbss_limits[i++].types = BIT(NL80211_IFTYPE_MONITOR);
751420f2c5faSRafał Miłecki }
751505491d2cSKalle Valo combo[c].beacon_int_infra_match = true;
751605491d2cSKalle Valo combo[c].num_different_channels = 1;
751720f2c5faSRafał Miłecki combo[c].max_interfaces = 4 + mon_flag;
7518f568adacSRafał Miłecki combo[c].n_limits = i;
751905491d2cSKalle Valo combo[c].limits = mbss_limits;
752005491d2cSKalle Valo }
7521f568adacSRafał Miłecki
752205491d2cSKalle Valo wiphy->n_iface_combinations = n_combos;
752305491d2cSKalle Valo wiphy->iface_combinations = combo;
752405491d2cSKalle Valo return 0;
752505491d2cSKalle Valo
752605491d2cSKalle Valo err:
752705491d2cSKalle Valo kfree(c0_limits);
752805491d2cSKalle Valo kfree(p2p_limits);
752905491d2cSKalle Valo kfree(mbss_limits);
753005491d2cSKalle Valo kfree(combo);
753105491d2cSKalle Valo return -ENOMEM;
753205491d2cSKalle Valo }
753305491d2cSKalle Valo
753405491d2cSKalle Valo #ifdef CONFIG_PM
75350b57010fSArend Van Spriel static const struct wiphy_wowlan_support brcmf_wowlan_support = {
753605491d2cSKalle Valo .flags = WIPHY_WOWLAN_MAGIC_PKT | WIPHY_WOWLAN_DISCONNECT,
753705491d2cSKalle Valo .n_patterns = BRCMF_WOWL_MAXPATTERNS,
753805491d2cSKalle Valo .pattern_max_len = BRCMF_WOWL_MAXPATTERNSIZE,
753905491d2cSKalle Valo .pattern_min_len = 1,
754005491d2cSKalle Valo .max_pkt_offset = 1500,
754105491d2cSKalle Valo };
754205491d2cSKalle Valo #endif
754305491d2cSKalle Valo
brcmf_wiphy_wowl_params(struct wiphy * wiphy,struct brcmf_if * ifp)75443021ad9aSHante Meuleman static void brcmf_wiphy_wowl_params(struct wiphy *wiphy, struct brcmf_if *ifp)
754505491d2cSKalle Valo {
754605491d2cSKalle Valo #ifdef CONFIG_PM
75473021ad9aSHante Meuleman struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
754816e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
75490b57010fSArend Van Spriel struct wiphy_wowlan_support *wowl;
75500b57010fSArend Van Spriel
75510b57010fSArend Van Spriel wowl = kmemdup(&brcmf_wowlan_support, sizeof(brcmf_wowlan_support),
75520b57010fSArend Van Spriel GFP_KERNEL);
75530b57010fSArend Van Spriel if (!wowl) {
755416e64676SRafał Miłecki bphy_err(drvr, "only support basic wowlan features\n");
75550b57010fSArend Van Spriel wiphy->wowlan = &brcmf_wowlan_support;
75560b57010fSArend Van Spriel return;
75570b57010fSArend Van Spriel }
75583021ad9aSHante Meuleman
75593021ad9aSHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
75605c22fb85SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_ND)) {
75610b57010fSArend Van Spriel wowl->flags |= WIPHY_WOWLAN_NET_DETECT;
75620b57010fSArend Van Spriel wowl->max_nd_match_sets = BRCMF_PNO_MAX_PFN_COUNT;
75633021ad9aSHante Meuleman init_waitqueue_head(&cfg->wowl.nd_data_wait);
75643021ad9aSHante Meuleman }
75653021ad9aSHante Meuleman }
75665c22fb85SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK)) {
75670b57010fSArend Van Spriel wowl->flags |= WIPHY_WOWLAN_SUPPORTS_GTK_REKEY;
75680b57010fSArend Van Spriel wowl->flags |= WIPHY_WOWLAN_GTK_REKEY_FAILURE;
75693021ad9aSHante Meuleman }
75705c22fb85SHante Meuleman
75710b57010fSArend Van Spriel wiphy->wowlan = wowl;
757205491d2cSKalle Valo #endif
757305491d2cSKalle Valo }
757405491d2cSKalle Valo
brcmf_setup_wiphy(struct wiphy * wiphy,struct brcmf_if * ifp)757505491d2cSKalle Valo static int brcmf_setup_wiphy(struct wiphy *wiphy, struct brcmf_if *ifp)
757605491d2cSKalle Valo {
757705491d2cSKalle Valo struct brcmf_pub *drvr = ifp->drvr;
757805491d2cSKalle Valo const struct ieee80211_iface_combination *combo;
757905491d2cSKalle Valo struct ieee80211_supported_band *band;
758005491d2cSKalle Valo u16 max_interfaces = 0;
758194ed6ffbSArend Van Spriel bool gscan;
758205491d2cSKalle Valo __le32 bandlist[3];
758305491d2cSKalle Valo u32 n_bands;
758405491d2cSKalle Valo int err, i;
758505491d2cSKalle Valo
758605491d2cSKalle Valo wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
758705491d2cSKalle Valo wiphy->max_scan_ie_len = BRCMF_SCAN_IE_LEN_MAX;
75886c404f34SHante Meuleman wiphy->max_num_pmkids = BRCMF_MAXPMKID;
758905491d2cSKalle Valo
759005491d2cSKalle Valo err = brcmf_setup_ifmodes(wiphy, ifp);
759105491d2cSKalle Valo if (err)
759205491d2cSKalle Valo return err;
759305491d2cSKalle Valo
759405491d2cSKalle Valo for (i = 0, combo = wiphy->iface_combinations;
759505491d2cSKalle Valo i < wiphy->n_iface_combinations; i++, combo++) {
759605491d2cSKalle Valo max_interfaces = max(max_interfaces, combo->max_interfaces);
759705491d2cSKalle Valo }
759805491d2cSKalle Valo
759905491d2cSKalle Valo for (i = 0; i < max_interfaces && i < ARRAY_SIZE(drvr->addresses);
760005491d2cSKalle Valo i++) {
760105491d2cSKalle Valo u8 *addr = drvr->addresses[i].addr;
760205491d2cSKalle Valo
760305491d2cSKalle Valo memcpy(addr, drvr->mac, ETH_ALEN);
760405491d2cSKalle Valo if (i) {
760505491d2cSKalle Valo addr[0] |= BIT(1);
760605491d2cSKalle Valo addr[ETH_ALEN - 1] ^= i;
760705491d2cSKalle Valo }
760805491d2cSKalle Valo }
760905491d2cSKalle Valo wiphy->addresses = drvr->addresses;
761005491d2cSKalle Valo wiphy->n_addresses = i;
761105491d2cSKalle Valo
761205491d2cSKalle Valo wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
7613240d61a9SHante Meuleman wiphy->cipher_suites = brcmf_cipher_suites;
7614240d61a9SHante Meuleman wiphy->n_cipher_suites = ARRAY_SIZE(brcmf_cipher_suites);
7615240d61a9SHante Meuleman if (!brcmf_feat_is_enabled(ifp, BRCMF_FEAT_MFP))
7616240d61a9SHante Meuleman wiphy->n_cipher_suites--;
76177705ba6fSArend van Spriel wiphy->bss_select_support = BIT(NL80211_BSS_SELECT_ATTR_RSSI) |
76187705ba6fSArend van Spriel BIT(NL80211_BSS_SELECT_ATTR_BAND_PREF) |
76197705ba6fSArend van Spriel BIT(NL80211_BSS_SELECT_ATTR_RSSI_ADJUST);
76207705ba6fSArend van Spriel
76210cc0236cSArend Van Spriel wiphy->flags |= WIPHY_FLAG_NETNS_OK |
76220cc0236cSArend Van Spriel WIPHY_FLAG_PS_ON_BY_DEFAULT |
76231204aa17SRafał Miłecki WIPHY_FLAG_HAVE_AP_SME |
762405491d2cSKalle Valo WIPHY_FLAG_OFFCHAN_TX |
7625a7b82d47SHante Meuleman WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL;
7626a7b82d47SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS))
7627a7b82d47SHante Meuleman wiphy->flags |= WIPHY_FLAG_SUPPORTS_TDLS;
76287d34b056SHante Meuleman if (!ifp->drvr->settings->roamoff)
762905491d2cSKalle Valo wiphy->flags |= WIPHY_FLAG_SUPPORTS_FW_ROAM;
76302526ff21SArend van Spriel if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWSUP)) {
7631b8a64f0eSArend van Spriel wiphy_ext_feature_set(wiphy,
7632b8a64f0eSArend van Spriel NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK);
76332526ff21SArend van Spriel wiphy_ext_feature_set(wiphy,
76342526ff21SArend van Spriel NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X);
76353b1e0a7bSChung-Hsien Hsu if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
76363b1e0a7bSChung-Hsien Hsu wiphy_ext_feature_set(wiphy,
76373b1e0a7bSChung-Hsien Hsu NL80211_EXT_FEATURE_SAE_OFFLOAD);
76382526ff21SArend van Spriel }
7639d5f59c96SChung-Hsien Hsu if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_FWAUTH)) {
7640787fb926SChung-Hsien Hsu wiphy_ext_feature_set(wiphy,
7641787fb926SChung-Hsien Hsu NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK);
7642d5f59c96SChung-Hsien Hsu if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SAE))
7643d5f59c96SChung-Hsien Hsu wiphy_ext_feature_set(wiphy,
7644d5f59c96SChung-Hsien Hsu NL80211_EXT_FEATURE_SAE_OFFLOAD_AP);
7645d5f59c96SChung-Hsien Hsu }
764605491d2cSKalle Valo wiphy->mgmt_stypes = brcmf_txrx_stypes;
764705491d2cSKalle Valo wiphy->max_remain_on_channel_duration = 5000;
764894ed6ffbSArend Van Spriel if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_PNO)) {
764994ed6ffbSArend Van Spriel gscan = brcmf_feat_is_enabled(ifp, BRCMF_FEAT_GSCAN);
765094ed6ffbSArend Van Spriel brcmf_pno_wiphy_params(wiphy, gscan);
765194ed6ffbSArend Van Spriel }
765205491d2cSKalle Valo /* vendor commands/events support */
765305491d2cSKalle Valo wiphy->vendor_commands = brcmf_vendor_cmds;
765405491d2cSKalle Valo wiphy->n_vendor_commands = BRCMF_VNDR_CMDS_LAST - 1;
765505491d2cSKalle Valo
765605491d2cSKalle Valo if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL))
76573021ad9aSHante Meuleman brcmf_wiphy_wowl_params(wiphy, ifp);
765805491d2cSKalle Valo err = brcmf_fil_cmd_data_get(ifp, BRCMF_C_GET_BANDLIST, &bandlist,
765905491d2cSKalle Valo sizeof(bandlist));
766005491d2cSKalle Valo if (err) {
766116e64676SRafał Miłecki bphy_err(drvr, "could not obtain band info: err=%d\n", err);
766205491d2cSKalle Valo return err;
766305491d2cSKalle Valo }
766405491d2cSKalle Valo /* first entry in bandlist is number of bands */
766505491d2cSKalle Valo n_bands = le32_to_cpu(bandlist[0]);
766605491d2cSKalle Valo for (i = 1; i <= n_bands && i < ARRAY_SIZE(bandlist); i++) {
766705491d2cSKalle Valo if (bandlist[i] == cpu_to_le32(WLC_BAND_2G)) {
766805491d2cSKalle Valo band = kmemdup(&__wl_band_2ghz, sizeof(__wl_band_2ghz),
766905491d2cSKalle Valo GFP_KERNEL);
767005491d2cSKalle Valo if (!band)
767105491d2cSKalle Valo return -ENOMEM;
767205491d2cSKalle Valo
767305491d2cSKalle Valo band->channels = kmemdup(&__wl_2ghz_channels,
767405491d2cSKalle Valo sizeof(__wl_2ghz_channels),
767505491d2cSKalle Valo GFP_KERNEL);
767605491d2cSKalle Valo if (!band->channels) {
767705491d2cSKalle Valo kfree(band);
767805491d2cSKalle Valo return -ENOMEM;
767905491d2cSKalle Valo }
768005491d2cSKalle Valo
768105491d2cSKalle Valo band->n_channels = ARRAY_SIZE(__wl_2ghz_channels);
768257fbcce3SJohannes Berg wiphy->bands[NL80211_BAND_2GHZ] = band;
768305491d2cSKalle Valo }
768405491d2cSKalle Valo if (bandlist[i] == cpu_to_le32(WLC_BAND_5G)) {
768505491d2cSKalle Valo band = kmemdup(&__wl_band_5ghz, sizeof(__wl_band_5ghz),
768605491d2cSKalle Valo GFP_KERNEL);
768705491d2cSKalle Valo if (!band)
768805491d2cSKalle Valo return -ENOMEM;
768905491d2cSKalle Valo
769005491d2cSKalle Valo band->channels = kmemdup(&__wl_5ghz_channels,
769105491d2cSKalle Valo sizeof(__wl_5ghz_channels),
769205491d2cSKalle Valo GFP_KERNEL);
769305491d2cSKalle Valo if (!band->channels) {
769405491d2cSKalle Valo kfree(band);
769505491d2cSKalle Valo return -ENOMEM;
769605491d2cSKalle Valo }
769705491d2cSKalle Valo
769805491d2cSKalle Valo band->n_channels = ARRAY_SIZE(__wl_5ghz_channels);
769957fbcce3SJohannes Berg wiphy->bands[NL80211_BAND_5GHZ] = band;
770005491d2cSKalle Valo }
770105491d2cSKalle Valo }
77020f83ff69SRafał Miłecki
7703011a56a3SArend van Spriel if (wiphy->bands[NL80211_BAND_5GHZ] &&
7704011a56a3SArend van Spriel brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DOT11H))
7705011a56a3SArend van Spriel wiphy_ext_feature_set(wiphy,
7706011a56a3SArend van Spriel NL80211_EXT_FEATURE_DFS_OFFLOAD);
7707011a56a3SArend van Spriel
77087dd56ea4SAlvin Šipraga wiphy_ext_feature_set(wiphy, NL80211_EXT_FEATURE_CQM_RSSI_LIST);
77097dd56ea4SAlvin Šipraga
77100f83ff69SRafał Miłecki wiphy_read_of_freq_limits(wiphy);
77110f83ff69SRafał Miłecki
7712ab99063fSRafał Miłecki return 0;
771305491d2cSKalle Valo }
771405491d2cSKalle Valo
brcmf_config_dongle(struct brcmf_cfg80211_info * cfg)771505491d2cSKalle Valo static s32 brcmf_config_dongle(struct brcmf_cfg80211_info *cfg)
771605491d2cSKalle Valo {
771716e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
771805491d2cSKalle Valo struct net_device *ndev;
771905491d2cSKalle Valo struct wireless_dev *wdev;
772005491d2cSKalle Valo struct brcmf_if *ifp;
772105491d2cSKalle Valo s32 power_mode;
772205491d2cSKalle Valo s32 err = 0;
772305491d2cSKalle Valo
772405491d2cSKalle Valo if (cfg->dongle_up)
772505491d2cSKalle Valo return err;
772605491d2cSKalle Valo
772705491d2cSKalle Valo ndev = cfg_to_ndev(cfg);
772805491d2cSKalle Valo wdev = ndev->ieee80211_ptr;
772905491d2cSKalle Valo ifp = netdev_priv(ndev);
773005491d2cSKalle Valo
773105491d2cSKalle Valo /* make sure RF is ready for work */
773205491d2cSKalle Valo brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
773305491d2cSKalle Valo
77341678ba8eSHante Meuleman brcmf_dongle_scantime(ifp);
773505491d2cSKalle Valo
773605491d2cSKalle Valo power_mode = cfg->pwr_save ? PM_FAST : PM_OFF;
773705491d2cSKalle Valo err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PM, power_mode);
773805491d2cSKalle Valo if (err)
773905491d2cSKalle Valo goto default_conf_out;
774005491d2cSKalle Valo brcmf_dbg(INFO, "power save set to %s\n",
774105491d2cSKalle Valo (power_mode ? "enabled" : "disabled"));
774205491d2cSKalle Valo
77431119e23eSHante Meuleman err = brcmf_dongle_roam(ifp);
774405491d2cSKalle Valo if (err)
774505491d2cSKalle Valo goto default_conf_out;
774605491d2cSKalle Valo err = brcmf_cfg80211_change_iface(wdev->wiphy, ndev, wdev->iftype,
7747818a986eSJohannes Berg NULL);
774805491d2cSKalle Valo if (err)
774905491d2cSKalle Valo goto default_conf_out;
775005491d2cSKalle Valo
775152f22fb2SFranky Lin brcmf_configure_arp_nd_offload(ifp, true);
775205491d2cSKalle Valo
7753a3bdc6deSWright Feng err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_FAKEFRAG, 1);
7754a3bdc6deSWright Feng if (err) {
775516e64676SRafał Miłecki bphy_err(drvr, "failed to set frameburst mode\n");
7756a3bdc6deSWright Feng goto default_conf_out;
7757a3bdc6deSWright Feng }
7758a3bdc6deSWright Feng
775905491d2cSKalle Valo cfg->dongle_up = true;
776005491d2cSKalle Valo default_conf_out:
776105491d2cSKalle Valo
776205491d2cSKalle Valo return err;
776305491d2cSKalle Valo
776405491d2cSKalle Valo }
776505491d2cSKalle Valo
__brcmf_cfg80211_up(struct brcmf_if * ifp)776605491d2cSKalle Valo static s32 __brcmf_cfg80211_up(struct brcmf_if *ifp)
776705491d2cSKalle Valo {
776805491d2cSKalle Valo set_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
776905491d2cSKalle Valo
777005491d2cSKalle Valo return brcmf_config_dongle(ifp->drvr->config);
777105491d2cSKalle Valo }
777205491d2cSKalle Valo
__brcmf_cfg80211_down(struct brcmf_if * ifp)777305491d2cSKalle Valo static s32 __brcmf_cfg80211_down(struct brcmf_if *ifp)
777405491d2cSKalle Valo {
777505491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
777605491d2cSKalle Valo
777705491d2cSKalle Valo /*
777805491d2cSKalle Valo * While going down, if associated with AP disassociate
777905491d2cSKalle Valo * from AP to save power
778005491d2cSKalle Valo */
778105491d2cSKalle Valo if (check_vif_up(ifp->vif)) {
77821b050d97SSoontak Lee brcmf_link_down(ifp->vif, WLAN_REASON_UNSPECIFIED, true);
778305491d2cSKalle Valo
778405491d2cSKalle Valo /* Make sure WPA_Supplicant receives all the event
778505491d2cSKalle Valo generated due to DISASSOC call to the fw to keep
778605491d2cSKalle Valo the state fw and WPA_Supplicant state consistent
778705491d2cSKalle Valo */
778805491d2cSKalle Valo brcmf_delay(500);
778905491d2cSKalle Valo }
779005491d2cSKalle Valo
779105491d2cSKalle Valo brcmf_abort_scanning(cfg);
779205491d2cSKalle Valo clear_bit(BRCMF_VIF_STATUS_READY, &ifp->vif->sme_state);
779305491d2cSKalle Valo
779405491d2cSKalle Valo return 0;
779505491d2cSKalle Valo }
779605491d2cSKalle Valo
brcmf_cfg80211_up(struct net_device * ndev)779705491d2cSKalle Valo s32 brcmf_cfg80211_up(struct net_device *ndev)
779805491d2cSKalle Valo {
779905491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
780005491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
780105491d2cSKalle Valo s32 err = 0;
780205491d2cSKalle Valo
780305491d2cSKalle Valo mutex_lock(&cfg->usr_sync);
780405491d2cSKalle Valo err = __brcmf_cfg80211_up(ifp);
780505491d2cSKalle Valo mutex_unlock(&cfg->usr_sync);
780605491d2cSKalle Valo
780705491d2cSKalle Valo return err;
780805491d2cSKalle Valo }
780905491d2cSKalle Valo
brcmf_cfg80211_down(struct net_device * ndev)781005491d2cSKalle Valo s32 brcmf_cfg80211_down(struct net_device *ndev)
781105491d2cSKalle Valo {
781205491d2cSKalle Valo struct brcmf_if *ifp = netdev_priv(ndev);
781305491d2cSKalle Valo struct brcmf_cfg80211_info *cfg = ifp->drvr->config;
781405491d2cSKalle Valo s32 err = 0;
781505491d2cSKalle Valo
781605491d2cSKalle Valo mutex_lock(&cfg->usr_sync);
781705491d2cSKalle Valo err = __brcmf_cfg80211_down(ifp);
781805491d2cSKalle Valo mutex_unlock(&cfg->usr_sync);
781905491d2cSKalle Valo
782005491d2cSKalle Valo return err;
782105491d2cSKalle Valo }
782205491d2cSKalle Valo
brcmf_cfg80211_get_iftype(struct brcmf_if * ifp)782305491d2cSKalle Valo enum nl80211_iftype brcmf_cfg80211_get_iftype(struct brcmf_if *ifp)
782405491d2cSKalle Valo {
782505491d2cSKalle Valo struct wireless_dev *wdev = &ifp->vif->wdev;
782605491d2cSKalle Valo
782705491d2cSKalle Valo return wdev->iftype;
782805491d2cSKalle Valo }
782905491d2cSKalle Valo
brcmf_get_vif_state_any(struct brcmf_cfg80211_info * cfg,unsigned long state)783005491d2cSKalle Valo bool brcmf_get_vif_state_any(struct brcmf_cfg80211_info *cfg,
783105491d2cSKalle Valo unsigned long state)
783205491d2cSKalle Valo {
783305491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
783405491d2cSKalle Valo
783505491d2cSKalle Valo list_for_each_entry(vif, &cfg->vif_list, list) {
783605491d2cSKalle Valo if (test_bit(state, &vif->sme_state))
783705491d2cSKalle Valo return true;
783805491d2cSKalle Valo }
783905491d2cSKalle Valo return false;
784005491d2cSKalle Valo }
784105491d2cSKalle Valo
vif_event_equals(struct brcmf_cfg80211_vif_event * event,u8 action)784205491d2cSKalle Valo static inline bool vif_event_equals(struct brcmf_cfg80211_vif_event *event,
784305491d2cSKalle Valo u8 action)
784405491d2cSKalle Valo {
784505491d2cSKalle Valo u8 evt_action;
784605491d2cSKalle Valo
7847b64abcb7Smhiramat@kernel.org spin_lock(&event->vif_event_lock);
784805491d2cSKalle Valo evt_action = event->action;
7849b64abcb7Smhiramat@kernel.org spin_unlock(&event->vif_event_lock);
785005491d2cSKalle Valo return evt_action == action;
785105491d2cSKalle Valo }
785205491d2cSKalle Valo
brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info * cfg,struct brcmf_cfg80211_vif * vif)785305491d2cSKalle Valo void brcmf_cfg80211_arm_vif_event(struct brcmf_cfg80211_info *cfg,
785405491d2cSKalle Valo struct brcmf_cfg80211_vif *vif)
785505491d2cSKalle Valo {
785605491d2cSKalle Valo struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
785705491d2cSKalle Valo
7858b64abcb7Smhiramat@kernel.org spin_lock(&event->vif_event_lock);
785905491d2cSKalle Valo event->vif = vif;
786005491d2cSKalle Valo event->action = 0;
7861b64abcb7Smhiramat@kernel.org spin_unlock(&event->vif_event_lock);
786205491d2cSKalle Valo }
786305491d2cSKalle Valo
brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info * cfg)786405491d2cSKalle Valo bool brcmf_cfg80211_vif_event_armed(struct brcmf_cfg80211_info *cfg)
786505491d2cSKalle Valo {
786605491d2cSKalle Valo struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
786705491d2cSKalle Valo bool armed;
786805491d2cSKalle Valo
7869b64abcb7Smhiramat@kernel.org spin_lock(&event->vif_event_lock);
787005491d2cSKalle Valo armed = event->vif != NULL;
7871b64abcb7Smhiramat@kernel.org spin_unlock(&event->vif_event_lock);
787205491d2cSKalle Valo
787305491d2cSKalle Valo return armed;
787405491d2cSKalle Valo }
7875a9eb0c4bSArend van Spriel
brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info * cfg,u8 action,ulong timeout)7876a9eb0c4bSArend van Spriel int brcmf_cfg80211_wait_vif_event(struct brcmf_cfg80211_info *cfg,
787705491d2cSKalle Valo u8 action, ulong timeout)
787805491d2cSKalle Valo {
787905491d2cSKalle Valo struct brcmf_cfg80211_vif_event *event = &cfg->vif_event;
788005491d2cSKalle Valo
788105491d2cSKalle Valo return wait_event_timeout(event->vif_wq,
788205491d2cSKalle Valo vif_event_equals(event, action), timeout);
788305491d2cSKalle Valo }
788405491d2cSKalle Valo
brmcf_use_iso3166_ccode_fallback(struct brcmf_pub * drvr)7885a21bf90eSHans de Goede static bool brmcf_use_iso3166_ccode_fallback(struct brcmf_pub *drvr)
7886a21bf90eSHans de Goede {
78875c54ab24SAlvin Šipraga if (drvr->settings->trivial_ccode_map)
78885c54ab24SAlvin Šipraga return true;
78895c54ab24SAlvin Šipraga
7890a21bf90eSHans de Goede switch (drvr->bus_if->chip) {
7891d173d020SHans de Goede case BRCM_CC_43430_CHIP_ID:
7892a21bf90eSHans de Goede case BRCM_CC_4345_CHIP_ID:
7893659fda7fSHans de Goede case BRCM_CC_4356_CHIP_ID:
789421947f3aSHamid Zamani case BRCM_CC_43602_CHIP_ID:
7895a21bf90eSHans de Goede return true;
7896a21bf90eSHans de Goede default:
7897a21bf90eSHans de Goede return false;
7898a21bf90eSHans de Goede }
7899a21bf90eSHans de Goede }
7900a21bf90eSHans de Goede
brcmf_translate_country_code(struct brcmf_pub * drvr,char alpha2[2],struct brcmf_fil_country_le * ccreq)790173345fd2SHante Meuleman static s32 brcmf_translate_country_code(struct brcmf_pub *drvr, char alpha2[2],
790273345fd2SHante Meuleman struct brcmf_fil_country_le *ccreq)
790373345fd2SHante Meuleman {
79044d792895SHante Meuleman struct brcmfmac_pd_cc *country_codes;
79054d792895SHante Meuleman struct brcmfmac_pd_cc_entry *cc;
790673345fd2SHante Meuleman s32 found_index;
790773345fd2SHante Meuleman int i;
790873345fd2SHante Meuleman
790973345fd2SHante Meuleman if ((alpha2[0] == ccreq->country_abbrev[0]) &&
791073345fd2SHante Meuleman (alpha2[1] == ccreq->country_abbrev[1])) {
791173345fd2SHante Meuleman brcmf_dbg(TRACE, "Country code already set\n");
791273345fd2SHante Meuleman return -EAGAIN;
791373345fd2SHante Meuleman }
791473345fd2SHante Meuleman
7915a21bf90eSHans de Goede country_codes = drvr->settings->country_codes;
7916a21bf90eSHans de Goede if (!country_codes) {
7917a21bf90eSHans de Goede if (brmcf_use_iso3166_ccode_fallback(drvr)) {
7918a21bf90eSHans de Goede brcmf_dbg(TRACE, "No country codes configured for device, using ISO3166 code and 0 rev\n");
7919a21bf90eSHans de Goede memset(ccreq, 0, sizeof(*ccreq));
7920a21bf90eSHans de Goede ccreq->country_abbrev[0] = alpha2[0];
7921a21bf90eSHans de Goede ccreq->country_abbrev[1] = alpha2[1];
7922a21bf90eSHans de Goede ccreq->ccode[0] = alpha2[0];
7923a21bf90eSHans de Goede ccreq->ccode[1] = alpha2[1];
7924a21bf90eSHans de Goede return 0;
7925a21bf90eSHans de Goede }
7926a21bf90eSHans de Goede
7927a21bf90eSHans de Goede brcmf_dbg(TRACE, "No country codes configured for device\n");
7928a21bf90eSHans de Goede return -EINVAL;
7929a21bf90eSHans de Goede }
7930a21bf90eSHans de Goede
793173345fd2SHante Meuleman found_index = -1;
793273345fd2SHante Meuleman for (i = 0; i < country_codes->table_size; i++) {
793373345fd2SHante Meuleman cc = &country_codes->table[i];
793473345fd2SHante Meuleman if ((cc->iso3166[0] == '\0') && (found_index == -1))
793573345fd2SHante Meuleman found_index = i;
793673345fd2SHante Meuleman if ((cc->iso3166[0] == alpha2[0]) &&
793773345fd2SHante Meuleman (cc->iso3166[1] == alpha2[1])) {
793873345fd2SHante Meuleman found_index = i;
793973345fd2SHante Meuleman break;
794073345fd2SHante Meuleman }
794173345fd2SHante Meuleman }
794273345fd2SHante Meuleman if (found_index == -1) {
794373345fd2SHante Meuleman brcmf_dbg(TRACE, "No country code match found\n");
794473345fd2SHante Meuleman return -EINVAL;
794573345fd2SHante Meuleman }
794673345fd2SHante Meuleman memset(ccreq, 0, sizeof(*ccreq));
794773345fd2SHante Meuleman ccreq->rev = cpu_to_le32(country_codes->table[found_index].rev);
794873345fd2SHante Meuleman memcpy(ccreq->ccode, country_codes->table[found_index].cc,
794973345fd2SHante Meuleman BRCMF_COUNTRY_BUF_SZ);
795073345fd2SHante Meuleman ccreq->country_abbrev[0] = alpha2[0];
795173345fd2SHante Meuleman ccreq->country_abbrev[1] = alpha2[1];
795273345fd2SHante Meuleman ccreq->country_abbrev[2] = 0;
795373345fd2SHante Meuleman
795473345fd2SHante Meuleman return 0;
795573345fd2SHante Meuleman }
795673345fd2SHante Meuleman
79576c04deaeSWright Feng static int
brcmf_parse_dump_obss(char * buf,struct brcmf_dump_survey * survey)79586c04deaeSWright Feng brcmf_parse_dump_obss(char *buf, struct brcmf_dump_survey *survey)
79596c04deaeSWright Feng {
79606c04deaeSWright Feng int i;
79616c04deaeSWright Feng char *token;
79626c04deaeSWright Feng char delim[] = "\n ";
79636c04deaeSWright Feng unsigned long val;
79646c04deaeSWright Feng int err = 0;
79656c04deaeSWright Feng
79666c04deaeSWright Feng token = strsep(&buf, delim);
79676c04deaeSWright Feng while (token) {
79686c04deaeSWright Feng if (!strcmp(token, "OBSS")) {
79696c04deaeSWright Feng for (i = 0; i < OBSS_TOKEN_IDX; i++)
79706c04deaeSWright Feng token = strsep(&buf, delim);
79716c04deaeSWright Feng err = kstrtoul(token, 10, &val);
79726c04deaeSWright Feng if (err)
79736c04deaeSWright Feng break;
79746c04deaeSWright Feng survey->obss = val;
79756c04deaeSWright Feng }
79766c04deaeSWright Feng
79776c04deaeSWright Feng if (!strcmp(token, "IBSS")) {
79786c04deaeSWright Feng for (i = 0; i < IBSS_TOKEN_IDX; i++)
79796c04deaeSWright Feng token = strsep(&buf, delim);
79806c04deaeSWright Feng err = kstrtoul(token, 10, &val);
79816c04deaeSWright Feng if (err)
79826c04deaeSWright Feng break;
79836c04deaeSWright Feng survey->ibss = val;
79846c04deaeSWright Feng }
79856c04deaeSWright Feng
79866c04deaeSWright Feng if (!strcmp(token, "TXDur")) {
79876c04deaeSWright Feng for (i = 0; i < TX_TOKEN_IDX; i++)
79886c04deaeSWright Feng token = strsep(&buf, delim);
79896c04deaeSWright Feng err = kstrtoul(token, 10, &val);
79906c04deaeSWright Feng if (err)
79916c04deaeSWright Feng break;
79926c04deaeSWright Feng survey->tx = val;
79936c04deaeSWright Feng }
79946c04deaeSWright Feng
79956c04deaeSWright Feng if (!strcmp(token, "Category")) {
79966c04deaeSWright Feng for (i = 0; i < CTG_TOKEN_IDX; i++)
79976c04deaeSWright Feng token = strsep(&buf, delim);
79986c04deaeSWright Feng err = kstrtoul(token, 10, &val);
79996c04deaeSWright Feng if (err)
80006c04deaeSWright Feng break;
80016c04deaeSWright Feng survey->no_ctg = val;
80026c04deaeSWright Feng }
80036c04deaeSWright Feng
80046c04deaeSWright Feng if (!strcmp(token, "Packet")) {
80056c04deaeSWright Feng for (i = 0; i < PKT_TOKEN_IDX; i++)
80066c04deaeSWright Feng token = strsep(&buf, delim);
80076c04deaeSWright Feng err = kstrtoul(token, 10, &val);
80086c04deaeSWright Feng if (err)
80096c04deaeSWright Feng break;
80106c04deaeSWright Feng survey->no_pckt = val;
80116c04deaeSWright Feng }
80126c04deaeSWright Feng
80136c04deaeSWright Feng if (!strcmp(token, "Opp(time):")) {
80146c04deaeSWright Feng for (i = 0; i < IDLE_TOKEN_IDX; i++)
80156c04deaeSWright Feng token = strsep(&buf, delim);
80166c04deaeSWright Feng err = kstrtoul(token, 10, &val);
80176c04deaeSWright Feng if (err)
80186c04deaeSWright Feng break;
80196c04deaeSWright Feng survey->idle = val;
80206c04deaeSWright Feng }
80216c04deaeSWright Feng
80226c04deaeSWright Feng token = strsep(&buf, delim);
80236c04deaeSWright Feng }
80246c04deaeSWright Feng
80256c04deaeSWright Feng return err;
80266c04deaeSWright Feng }
80276c04deaeSWright Feng
80286c04deaeSWright Feng static int
brcmf_dump_obss(struct brcmf_if * ifp,struct cca_msrmnt_query req,struct brcmf_dump_survey * survey)80296c04deaeSWright Feng brcmf_dump_obss(struct brcmf_if *ifp, struct cca_msrmnt_query req,
80306c04deaeSWright Feng struct brcmf_dump_survey *survey)
80316c04deaeSWright Feng {
80326c04deaeSWright Feng struct cca_stats_n_flags *results;
80336c04deaeSWright Feng char *buf;
80346c04deaeSWright Feng int err;
80356c04deaeSWright Feng
80366c04deaeSWright Feng buf = kzalloc(sizeof(char) * BRCMF_DCMD_MEDLEN, GFP_KERNEL);
8037216647e6SWright Feng if (!buf)
80386c04deaeSWright Feng return -ENOMEM;
80396c04deaeSWright Feng
80406c04deaeSWright Feng memcpy(buf, &req, sizeof(struct cca_msrmnt_query));
80416c04deaeSWright Feng err = brcmf_fil_iovar_data_get(ifp, "dump_obss",
80426c04deaeSWright Feng buf, BRCMF_DCMD_MEDLEN);
8043216647e6SWright Feng if (err) {
80446c04deaeSWright Feng brcmf_err("dump_obss error (%d)\n", err);
8045216647e6SWright Feng err = -EINVAL;
80466c04deaeSWright Feng goto exit;
80476c04deaeSWright Feng }
80486c04deaeSWright Feng results = (struct cca_stats_n_flags *)(buf);
80496c04deaeSWright Feng
80506c04deaeSWright Feng if (req.msrmnt_query)
80516c04deaeSWright Feng brcmf_parse_dump_obss(results->buf, survey);
80526c04deaeSWright Feng
80536c04deaeSWright Feng exit:
80546c04deaeSWright Feng kfree(buf);
8055216647e6SWright Feng return err;
80566c04deaeSWright Feng }
80576c04deaeSWright Feng
80586c04deaeSWright Feng static s32
brcmf_set_channel(struct brcmf_cfg80211_info * cfg,struct ieee80211_channel * chan)80595fac1858SArend van Spriel brcmf_set_channel(struct brcmf_cfg80211_info *cfg, struct ieee80211_channel *chan)
80606c04deaeSWright Feng {
80616c04deaeSWright Feng u16 chspec = 0;
80626c04deaeSWright Feng int err = 0;
80636c04deaeSWright Feng struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
80646c04deaeSWright Feng
8065e5d1ab1aSArend van Spriel if (chan->flags & IEEE80211_CHAN_DISABLED)
8066e5d1ab1aSArend van Spriel return -EINVAL;
8067e5d1ab1aSArend van Spriel
80686c04deaeSWright Feng /* set_channel */
80696c04deaeSWright Feng chspec = channel_to_chanspec(&cfg->d11inf, chan);
80706c04deaeSWright Feng if (chspec != INVCHANSPEC) {
80716c04deaeSWright Feng err = brcmf_fil_iovar_int_set(ifp, "chanspec", chspec);
80726c04deaeSWright Feng if (err) {
80736c04deaeSWright Feng brcmf_err("set chanspec 0x%04x fail, reason %d\n", chspec, err);
80746c04deaeSWright Feng err = -EINVAL;
80756c04deaeSWright Feng }
80766c04deaeSWright Feng } else {
80776c04deaeSWright Feng brcmf_err("failed to convert host chanspec to fw chanspec\n");
80786c04deaeSWright Feng err = -EINVAL;
80796c04deaeSWright Feng }
80806c04deaeSWright Feng
80816c04deaeSWright Feng return err;
80826c04deaeSWright Feng }
80836c04deaeSWright Feng
80846c04deaeSWright Feng static int
brcmf_cfg80211_dump_survey(struct wiphy * wiphy,struct net_device * ndev,int idx,struct survey_info * info)80856c04deaeSWright Feng brcmf_cfg80211_dump_survey(struct wiphy *wiphy, struct net_device *ndev,
80866c04deaeSWright Feng int idx, struct survey_info *info)
80876c04deaeSWright Feng {
80886c04deaeSWright Feng struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
80896c04deaeSWright Feng struct brcmf_if *ifp = netdev_priv(cfg_to_ndev(cfg));
80906c04deaeSWright Feng struct brcmf_dump_survey survey = {};
80916c04deaeSWright Feng struct ieee80211_supported_band *band;
8092aadb50d1SArend van Spriel enum nl80211_band band_id;
80936c04deaeSWright Feng struct cca_msrmnt_query req;
8094216647e6SWright Feng u32 noise;
80956c04deaeSWright Feng int err;
80966c04deaeSWright Feng
80976c04deaeSWright Feng brcmf_dbg(TRACE, "Enter: channel idx=%d\n", idx);
80986c04deaeSWright Feng
809962ccb2e6SRamesh Rangavittal /* Do not run survey when VIF in CONNECTING / CONNECTED states */
810062ccb2e6SRamesh Rangavittal if ((test_bit(BRCMF_VIF_STATUS_CONNECTING, &ifp->vif->sme_state)) ||
810162ccb2e6SRamesh Rangavittal (test_bit(BRCMF_VIF_STATUS_CONNECTED, &ifp->vif->sme_state))) {
810262ccb2e6SRamesh Rangavittal return -EBUSY;
810362ccb2e6SRamesh Rangavittal }
810462ccb2e6SRamesh Rangavittal
8105aadb50d1SArend van Spriel for (band_id = 0; band_id < NUM_NL80211_BANDS; band_id++) {
8106aadb50d1SArend van Spriel band = wiphy->bands[band_id];
8107aadb50d1SArend van Spriel if (!band)
8108aadb50d1SArend van Spriel continue;
8109aadb50d1SArend van Spriel if (idx >= band->n_channels) {
81106c04deaeSWright Feng idx -= band->n_channels;
8111aadb50d1SArend van Spriel continue;
81126c04deaeSWright Feng }
81136c04deaeSWright Feng
8114aadb50d1SArend van Spriel info->channel = &band->channels[idx];
8115aadb50d1SArend van Spriel break;
81166c04deaeSWright Feng }
8117aadb50d1SArend van Spriel if (band_id == NUM_NL80211_BANDS)
8118aadb50d1SArend van Spriel return -ENOENT;
81196c04deaeSWright Feng
81206c04deaeSWright Feng /* Setting current channel to the requested channel */
81216c04deaeSWright Feng info->filled = 0;
81225fac1858SArend van Spriel if (brcmf_set_channel(cfg, info->channel))
81236c04deaeSWright Feng return 0;
81246c04deaeSWright Feng
81256c04deaeSWright Feng /* Disable mpc */
8126216647e6SWright Feng brcmf_set_mpc(ifp, 0);
8127216647e6SWright Feng
81286c04deaeSWright Feng /* Set interface up, explicitly. */
8129216647e6SWright Feng err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 1);
81306c04deaeSWright Feng if (err) {
8131216647e6SWright Feng brcmf_err("set interface up failed, err = %d\n", err);
8132216647e6SWright Feng goto exit;
81336c04deaeSWright Feng }
81346c04deaeSWright Feng
81356c04deaeSWright Feng /* Get noise value */
81366c04deaeSWright Feng err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_PHY_NOISE, &noise);
81376c04deaeSWright Feng if (err) {
8138216647e6SWright Feng brcmf_err("Get Phy Noise failed, use dummy value\n");
81396c04deaeSWright Feng noise = CHAN_NOISE_DUMMY;
81406c04deaeSWright Feng }
81416c04deaeSWright Feng
81426c04deaeSWright Feng /* Start Measurement for obss stats on current channel */
81436c04deaeSWright Feng req.msrmnt_query = 0;
81446c04deaeSWright Feng req.time_req = ACS_MSRMNT_DELAY;
81456c04deaeSWright Feng err = brcmf_dump_obss(ifp, req, &survey);
81466c04deaeSWright Feng if (err)
81476c04deaeSWright Feng goto exit;
81486c04deaeSWright Feng
81496c04deaeSWright Feng /* Add 10 ms for IOVAR completion */
81506c04deaeSWright Feng msleep(ACS_MSRMNT_DELAY + 10);
81516c04deaeSWright Feng
81526c04deaeSWright Feng /* Issue IOVAR to collect measurement results */
81536c04deaeSWright Feng req.msrmnt_query = 1;
81546c04deaeSWright Feng err = brcmf_dump_obss(ifp, req, &survey);
8155216647e6SWright Feng if (err)
81566c04deaeSWright Feng goto exit;
81576c04deaeSWright Feng
81586c04deaeSWright Feng info->noise = noise;
81596c04deaeSWright Feng info->time = ACS_MSRMNT_DELAY;
81606c04deaeSWright Feng info->time_busy = ACS_MSRMNT_DELAY - survey.idle;
81616c04deaeSWright Feng info->time_rx = survey.obss + survey.ibss + survey.no_ctg +
81626c04deaeSWright Feng survey.no_pckt;
81636c04deaeSWright Feng info->time_tx = survey.tx;
81646c04deaeSWright Feng info->filled = SURVEY_INFO_NOISE_DBM | SURVEY_INFO_TIME |
81656c04deaeSWright Feng SURVEY_INFO_TIME_BUSY | SURVEY_INFO_TIME_RX |
81666c04deaeSWright Feng SURVEY_INFO_TIME_TX;
81676c04deaeSWright Feng
81686c04deaeSWright Feng brcmf_dbg(INFO, "OBSS dump: channel %d: survey duration %d\n",
8169e5d1ab1aSArend van Spriel ieee80211_frequency_to_channel(info->channel->center_freq),
81706c04deaeSWright Feng ACS_MSRMNT_DELAY);
81716c04deaeSWright Feng brcmf_dbg(INFO, "noise(%d) busy(%llu) rx(%llu) tx(%llu)\n",
81726c04deaeSWright Feng info->noise, info->time_busy, info->time_rx, info->time_tx);
81736c04deaeSWright Feng
81746c04deaeSWright Feng exit:
8175216647e6SWright Feng if (!brcmf_is_apmode(ifp->vif))
8176216647e6SWright Feng brcmf_set_mpc(ifp, 1);
81776c04deaeSWright Feng return err;
81786c04deaeSWright Feng }
81796c04deaeSWright Feng
brcmf_cfg80211_reg_notifier(struct wiphy * wiphy,struct regulatory_request * req)818005491d2cSKalle Valo static void brcmf_cfg80211_reg_notifier(struct wiphy *wiphy,
818105491d2cSKalle Valo struct regulatory_request *req)
818205491d2cSKalle Valo {
8183856d5a01SArend Van Spriel struct brcmf_cfg80211_info *cfg = wiphy_to_cfg(wiphy);
8184856d5a01SArend Van Spriel struct brcmf_if *ifp = brcmf_get_ifp(cfg->pub, 0);
818516e64676SRafał Miłecki struct brcmf_pub *drvr = cfg->pub;
818605491d2cSKalle Valo struct brcmf_fil_country_le ccreq;
818773345fd2SHante Meuleman s32 err;
818805491d2cSKalle Valo int i;
818905491d2cSKalle Valo
819026e53788SHans de Goede /* The country code gets set to "00" by default at boot, ignore */
819126e53788SHans de Goede if (req->alpha2[0] == '0' && req->alpha2[1] == '0')
819226e53788SHans de Goede return;
819326e53788SHans de Goede
819405491d2cSKalle Valo /* ignore non-ISO3166 country codes */
81959b9322dbSStefan Wahren for (i = 0; i < 2; i++)
819605491d2cSKalle Valo if (req->alpha2[i] < 'A' || req->alpha2[i] > 'Z') {
819716e64676SRafał Miłecki bphy_err(drvr, "not an ISO3166 code (0x%02x 0x%02x)\n",
819873345fd2SHante Meuleman req->alpha2[0], req->alpha2[1]);
819905491d2cSKalle Valo return;
820005491d2cSKalle Valo }
820173345fd2SHante Meuleman
820273345fd2SHante Meuleman brcmf_dbg(TRACE, "Enter: initiator=%d, alpha=%c%c\n", req->initiator,
820373345fd2SHante Meuleman req->alpha2[0], req->alpha2[1]);
820473345fd2SHante Meuleman
820573345fd2SHante Meuleman err = brcmf_fil_iovar_data_get(ifp, "country", &ccreq, sizeof(ccreq));
820673345fd2SHante Meuleman if (err) {
820716e64676SRafał Miłecki bphy_err(drvr, "Country code iovar returned err = %d\n", err);
820873345fd2SHante Meuleman return;
820973345fd2SHante Meuleman }
821073345fd2SHante Meuleman
821173345fd2SHante Meuleman err = brcmf_translate_country_code(ifp->drvr, req->alpha2, &ccreq);
821273345fd2SHante Meuleman if (err)
821373345fd2SHante Meuleman return;
821473345fd2SHante Meuleman
821573345fd2SHante Meuleman err = brcmf_fil_iovar_data_set(ifp, "country", &ccreq, sizeof(ccreq));
821673345fd2SHante Meuleman if (err) {
821716e64676SRafał Miłecki bphy_err(drvr, "Firmware rejected country setting\n");
821805491d2cSKalle Valo return;
821905491d2cSKalle Valo }
8220856d5a01SArend Van Spriel brcmf_setup_wiphybands(cfg);
822105491d2cSKalle Valo }
822205491d2cSKalle Valo
brcmf_free_wiphy(struct wiphy * wiphy)822305491d2cSKalle Valo static void brcmf_free_wiphy(struct wiphy *wiphy)
822405491d2cSKalle Valo {
822505491d2cSKalle Valo int i;
822605491d2cSKalle Valo
822705491d2cSKalle Valo if (!wiphy)
822805491d2cSKalle Valo return;
822905491d2cSKalle Valo
823005491d2cSKalle Valo if (wiphy->iface_combinations) {
823105491d2cSKalle Valo for (i = 0; i < wiphy->n_iface_combinations; i++)
823205491d2cSKalle Valo kfree(wiphy->iface_combinations[i].limits);
823305491d2cSKalle Valo }
823405491d2cSKalle Valo kfree(wiphy->iface_combinations);
823557fbcce3SJohannes Berg if (wiphy->bands[NL80211_BAND_2GHZ]) {
823657fbcce3SJohannes Berg kfree(wiphy->bands[NL80211_BAND_2GHZ]->channels);
823757fbcce3SJohannes Berg kfree(wiphy->bands[NL80211_BAND_2GHZ]);
823805491d2cSKalle Valo }
823957fbcce3SJohannes Berg if (wiphy->bands[NL80211_BAND_5GHZ]) {
824057fbcce3SJohannes Berg kfree(wiphy->bands[NL80211_BAND_5GHZ]->channels);
824157fbcce3SJohannes Berg kfree(wiphy->bands[NL80211_BAND_5GHZ]);
824205491d2cSKalle Valo }
82430b57010fSArend Van Spriel #if IS_ENABLED(CONFIG_PM)
82440b57010fSArend Van Spriel if (wiphy->wowlan != &brcmf_wowlan_support)
82450b57010fSArend Van Spriel kfree(wiphy->wowlan);
82460b57010fSArend Van Spriel #endif
824705491d2cSKalle Valo }
824805491d2cSKalle Valo
brcmf_cfg80211_attach(struct brcmf_pub * drvr,struct cfg80211_ops * ops,bool p2pdev_forced)824905491d2cSKalle Valo struct brcmf_cfg80211_info *brcmf_cfg80211_attach(struct brcmf_pub *drvr,
8250856d5a01SArend Van Spriel struct cfg80211_ops *ops,
825105491d2cSKalle Valo bool p2pdev_forced)
825205491d2cSKalle Valo {
8253856d5a01SArend Van Spriel struct wiphy *wiphy = drvr->wiphy;
825405491d2cSKalle Valo struct net_device *ndev = brcmf_get_ifp(drvr, 0)->ndev;
825505491d2cSKalle Valo struct brcmf_cfg80211_info *cfg;
825605491d2cSKalle Valo struct brcmf_cfg80211_vif *vif;
825705491d2cSKalle Valo struct brcmf_if *ifp;
825805491d2cSKalle Valo s32 err = 0;
825905491d2cSKalle Valo s32 io_type;
826005491d2cSKalle Valo u16 *cap = NULL;
826105491d2cSKalle Valo
826205491d2cSKalle Valo if (!ndev) {
826316e64676SRafał Miłecki bphy_err(drvr, "ndev is invalid\n");
826405491d2cSKalle Valo return NULL;
826505491d2cSKalle Valo }
826605491d2cSKalle Valo
8267856d5a01SArend Van Spriel cfg = kzalloc(sizeof(*cfg), GFP_KERNEL);
8268856d5a01SArend Van Spriel if (!cfg) {
826916e64676SRafał Miłecki bphy_err(drvr, "Could not allocate wiphy device\n");
8270856d5a01SArend Van Spriel return NULL;
827105491d2cSKalle Valo }
827205491d2cSKalle Valo
827305491d2cSKalle Valo cfg->wiphy = wiphy;
827405491d2cSKalle Valo cfg->pub = drvr;
827505491d2cSKalle Valo init_vif_event(&cfg->vif_event);
827605491d2cSKalle Valo INIT_LIST_HEAD(&cfg->vif_list);
827705491d2cSKalle Valo
827826072330SRafał Miłecki vif = brcmf_alloc_vif(cfg, NL80211_IFTYPE_STATION);
827905491d2cSKalle Valo if (IS_ERR(vif))
828005491d2cSKalle Valo goto wiphy_out;
828105491d2cSKalle Valo
8282856d5a01SArend Van Spriel ifp = netdev_priv(ndev);
828305491d2cSKalle Valo vif->ifp = ifp;
828405491d2cSKalle Valo vif->wdev.netdev = ndev;
828505491d2cSKalle Valo ndev->ieee80211_ptr = &vif->wdev;
828605491d2cSKalle Valo SET_NETDEV_DEV(ndev, wiphy_dev(cfg->wiphy));
828705491d2cSKalle Valo
828805491d2cSKalle Valo err = wl_init_priv(cfg);
828905491d2cSKalle Valo if (err) {
829016e64676SRafał Miłecki bphy_err(drvr, "Failed to init iwm_priv (%d)\n", err);
829105491d2cSKalle Valo brcmf_free_vif(vif);
829205491d2cSKalle Valo goto wiphy_out;
829305491d2cSKalle Valo }
829405491d2cSKalle Valo ifp->vif = vif;
829505491d2cSKalle Valo
829605491d2cSKalle Valo /* determine d11 io type before wiphy setup */
829705491d2cSKalle Valo err = brcmf_fil_cmd_int_get(ifp, BRCMF_C_GET_VERSION, &io_type);
829805491d2cSKalle Valo if (err) {
829916e64676SRafał Miłecki bphy_err(drvr, "Failed to get D11 version (%d)\n", err);
830005491d2cSKalle Valo goto priv_out;
830105491d2cSKalle Valo }
830205491d2cSKalle Valo cfg->d11inf.io_type = (u8)io_type;
830305491d2cSKalle Valo brcmu_d11_attach(&cfg->d11inf);
830405491d2cSKalle Valo
8305856d5a01SArend Van Spriel /* regulatory notifer below needs access to cfg so
8306856d5a01SArend Van Spriel * assign it now.
8307856d5a01SArend Van Spriel */
8308856d5a01SArend Van Spriel drvr->config = cfg;
8309856d5a01SArend Van Spriel
831027a8aea1SWinnie Chang err = brcmf_setup_wiphy(wiphy, ifp);
831127a8aea1SWinnie Chang if (err < 0)
831227a8aea1SWinnie Chang goto priv_out;
831327a8aea1SWinnie Chang
831405491d2cSKalle Valo brcmf_dbg(INFO, "Registering custom regulatory\n");
831505491d2cSKalle Valo wiphy->reg_notifier = brcmf_cfg80211_reg_notifier;
831605491d2cSKalle Valo wiphy->regulatory_flags |= REGULATORY_CUSTOM_REG;
831705491d2cSKalle Valo wiphy_apply_custom_regulatory(wiphy, &brcmf_regdom);
831805491d2cSKalle Valo
831905491d2cSKalle Valo /* firmware defaults to 40MHz disabled in 2G band. We signal
832005491d2cSKalle Valo * cfg80211 here that we do and have it decide we can enable
832105491d2cSKalle Valo * it. But first check if device does support 2G operation.
832205491d2cSKalle Valo */
832357fbcce3SJohannes Berg if (wiphy->bands[NL80211_BAND_2GHZ]) {
832457fbcce3SJohannes Berg cap = &wiphy->bands[NL80211_BAND_2GHZ]->ht_cap.cap;
832505491d2cSKalle Valo *cap |= IEEE80211_HT_CAP_SUP_WIDTH_20_40;
832605491d2cSKalle Valo }
8327856d5a01SArend Van Spriel #ifdef CONFIG_PM
8328856d5a01SArend Van Spriel if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_WOWL_GTK))
8329856d5a01SArend Van Spriel ops->set_rekey_data = brcmf_cfg80211_set_rekey_data;
8330856d5a01SArend Van Spriel #endif
83316c04deaeSWright Feng if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_DUMP_OBSS))
83326c04deaeSWright Feng ops->dump_survey = brcmf_cfg80211_dump_survey;
83336c04deaeSWright Feng
833405491d2cSKalle Valo err = wiphy_register(wiphy);
833505491d2cSKalle Valo if (err < 0) {
833616e64676SRafał Miłecki bphy_err(drvr, "Could not register wiphy device (%d)\n", err);
833705491d2cSKalle Valo goto priv_out;
833805491d2cSKalle Valo }
833905491d2cSKalle Valo
8340856d5a01SArend Van Spriel err = brcmf_setup_wiphybands(cfg);
8341ab99063fSRafał Miłecki if (err) {
834216e64676SRafał Miłecki bphy_err(drvr, "Setting wiphy bands failed (%d)\n", err);
8343ab99063fSRafał Miłecki goto wiphy_unreg_out;
8344ab99063fSRafał Miłecki }
8345ab99063fSRafał Miłecki
834605491d2cSKalle Valo /* If cfg80211 didn't disable 40MHz HT CAP in wiphy_register(),
834705491d2cSKalle Valo * setup 40MHz in 2GHz band and enable OBSS scanning.
834805491d2cSKalle Valo */
834905491d2cSKalle Valo if (cap && (*cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40)) {
835005491d2cSKalle Valo err = brcmf_enable_bw40_2g(cfg);
835105491d2cSKalle Valo if (!err)
835205491d2cSKalle Valo err = brcmf_fil_iovar_int_set(ifp, "obss_coex",
835305491d2cSKalle Valo BRCMF_OBSS_COEX_AUTO);
835405491d2cSKalle Valo else
835505491d2cSKalle Valo *cap &= ~IEEE80211_HT_CAP_SUP_WIDTH_20_40;
835605491d2cSKalle Valo }
8357856d5a01SArend Van Spriel
835805491d2cSKalle Valo err = brcmf_fweh_activate_events(ifp);
835905491d2cSKalle Valo if (err) {
836016e64676SRafał Miłecki bphy_err(drvr, "FWEH activation failed (%d)\n", err);
836105491d2cSKalle Valo goto wiphy_unreg_out;
836205491d2cSKalle Valo }
836305491d2cSKalle Valo
836405491d2cSKalle Valo err = brcmf_p2p_attach(cfg, p2pdev_forced);
836505491d2cSKalle Valo if (err) {
836616e64676SRafał Miłecki bphy_err(drvr, "P2P initialisation failed (%d)\n", err);
836705491d2cSKalle Valo goto wiphy_unreg_out;
836805491d2cSKalle Valo }
836905491d2cSKalle Valo err = brcmf_btcoex_attach(cfg);
837005491d2cSKalle Valo if (err) {
837116e64676SRafał Miłecki bphy_err(drvr, "BT-coex initialisation failed (%d)\n", err);
837205491d2cSKalle Valo brcmf_p2p_detach(&cfg->p2p);
837305491d2cSKalle Valo goto wiphy_unreg_out;
837405491d2cSKalle Valo }
8375efc2c1faSArend Van Spriel err = brcmf_pno_attach(cfg);
8376efc2c1faSArend Van Spriel if (err) {
837716e64676SRafał Miłecki bphy_err(drvr, "PNO initialisation failed (%d)\n", err);
8378efc2c1faSArend Van Spriel brcmf_btcoex_detach(cfg);
8379efc2c1faSArend Van Spriel brcmf_p2p_detach(&cfg->p2p);
8380efc2c1faSArend Van Spriel goto wiphy_unreg_out;
8381efc2c1faSArend Van Spriel }
838205491d2cSKalle Valo
8383a7b82d47SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_TDLS)) {
838405491d2cSKalle Valo err = brcmf_fil_iovar_int_set(ifp, "tdls_enable", 1);
838505491d2cSKalle Valo if (err) {
838605491d2cSKalle Valo brcmf_dbg(INFO, "TDLS not enabled (%d)\n", err);
838705491d2cSKalle Valo wiphy->flags &= ~WIPHY_FLAG_SUPPORTS_TDLS;
838805491d2cSKalle Valo } else {
838905491d2cSKalle Valo brcmf_fweh_register(cfg->pub, BRCMF_E_TDLS_PEER_EVENT,
839005491d2cSKalle Valo brcmf_notify_tdls_peer_event);
839105491d2cSKalle Valo }
8392a7b82d47SHante Meuleman }
839305491d2cSKalle Valo
839405491d2cSKalle Valo /* (re-) activate FWEH event handling */
839505491d2cSKalle Valo err = brcmf_fweh_activate_events(ifp);
839605491d2cSKalle Valo if (err) {
839716e64676SRafał Miłecki bphy_err(drvr, "FWEH activation failed (%d)\n", err);
8398cb853da3SArend Van Spriel goto detach;
839905491d2cSKalle Valo }
840005491d2cSKalle Valo
840148ed16e8SHante Meuleman /* Fill in some of the advertised nl80211 supported features */
840248ed16e8SHante Meuleman if (brcmf_feat_is_enabled(ifp, BRCMF_FEAT_SCAN_RANDOM_MAC)) {
840348ed16e8SHante Meuleman wiphy->features |= NL80211_FEATURE_SCHED_SCAN_RANDOM_MAC_ADDR;
840448ed16e8SHante Meuleman #ifdef CONFIG_PM
84056ea09153SFranky Lin if (wiphy->wowlan &&
84066ea09153SFranky Lin wiphy->wowlan->flags & WIPHY_WOWLAN_NET_DETECT)
840748ed16e8SHante Meuleman wiphy->features |= NL80211_FEATURE_ND_RANDOM_MAC_ADDR;
840848ed16e8SHante Meuleman #endif
840948ed16e8SHante Meuleman }
841048ed16e8SHante Meuleman
841105491d2cSKalle Valo return cfg;
841205491d2cSKalle Valo
8413cb853da3SArend Van Spriel detach:
8414efc2c1faSArend Van Spriel brcmf_pno_detach(cfg);
8415cb853da3SArend Van Spriel brcmf_btcoex_detach(cfg);
8416cb853da3SArend Van Spriel brcmf_p2p_detach(&cfg->p2p);
841705491d2cSKalle Valo wiphy_unreg_out:
841805491d2cSKalle Valo wiphy_unregister(cfg->wiphy);
841905491d2cSKalle Valo priv_out:
842005491d2cSKalle Valo wl_deinit_priv(cfg);
842105491d2cSKalle Valo brcmf_free_vif(vif);
842205491d2cSKalle Valo ifp->vif = NULL;
842305491d2cSKalle Valo wiphy_out:
842405491d2cSKalle Valo brcmf_free_wiphy(wiphy);
8425856d5a01SArend Van Spriel kfree(cfg);
842605491d2cSKalle Valo return NULL;
842705491d2cSKalle Valo }
842805491d2cSKalle Valo
brcmf_cfg80211_detach(struct brcmf_cfg80211_info * cfg)842905491d2cSKalle Valo void brcmf_cfg80211_detach(struct brcmf_cfg80211_info *cfg)
843005491d2cSKalle Valo {
843105491d2cSKalle Valo if (!cfg)
843205491d2cSKalle Valo return;
843305491d2cSKalle Valo
8434efc2c1faSArend Van Spriel brcmf_pno_detach(cfg);
843505491d2cSKalle Valo brcmf_btcoex_detach(cfg);
843605491d2cSKalle Valo wiphy_unregister(cfg->wiphy);
843705491d2cSKalle Valo wl_deinit_priv(cfg);
843819079484SZheng Wang cancel_work_sync(&cfg->escan_timeout_work);
843905491d2cSKalle Valo brcmf_free_wiphy(cfg->wiphy);
8440856d5a01SArend Van Spriel kfree(cfg);
844105491d2cSKalle Valo }
8442