xref: /OK3568_Linux_fs/external/rkwifibt/drivers/bcmdhd/wl_cfgvif.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * Wifi Virtual Interface implementaion
3*4882a593Smuzhiyun  *
4*4882a593Smuzhiyun  * Copyright (C) 2020, Broadcom.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  *      Unless you and Broadcom execute a separate written software license
7*4882a593Smuzhiyun  * agreement governing use of this software, this software is licensed to you
8*4882a593Smuzhiyun  * under the terms of the GNU General Public License version 2 (the "GPL"),
9*4882a593Smuzhiyun  * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10*4882a593Smuzhiyun  * following added to such license:
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  *      As a special exception, the copyright holders of this software give you
13*4882a593Smuzhiyun  * permission to link this software with independent modules, and to copy and
14*4882a593Smuzhiyun  * distribute the resulting executable under terms of your choice, provided that
15*4882a593Smuzhiyun  * you also meet, for each linked independent module, the terms and conditions of
16*4882a593Smuzhiyun  * the license of that module.  An independent module is a module which is not
17*4882a593Smuzhiyun  * derived from this software.  The special exception does not apply to any
18*4882a593Smuzhiyun  * modifications of the software.
19*4882a593Smuzhiyun  *
20*4882a593Smuzhiyun  *
21*4882a593Smuzhiyun  * <<Broadcom-WL-IPTag/Dual:>>
22*4882a593Smuzhiyun  */
23*4882a593Smuzhiyun /* */
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #include <typedefs.h>
26*4882a593Smuzhiyun #include <linuxver.h>
27*4882a593Smuzhiyun #include <linux/kernel.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #include <bcmutils.h>
30*4882a593Smuzhiyun #include <bcmstdlib_s.h>
31*4882a593Smuzhiyun #include <bcmwifi_channels.h>
32*4882a593Smuzhiyun #include <bcmendian.h>
33*4882a593Smuzhiyun #include <ethernet.h>
34*4882a593Smuzhiyun #ifdef WL_WPS_SYNC
35*4882a593Smuzhiyun #include <eapol.h>
36*4882a593Smuzhiyun #endif /* WL_WPS_SYNC */
37*4882a593Smuzhiyun #include <802.11.h>
38*4882a593Smuzhiyun #include <bcmiov.h>
39*4882a593Smuzhiyun #include <linux/if_arp.h>
40*4882a593Smuzhiyun #include <asm/uaccess.h>
41*4882a593Smuzhiyun 
42*4882a593Smuzhiyun #include <ethernet.h>
43*4882a593Smuzhiyun #include <linux/kernel.h>
44*4882a593Smuzhiyun #include <linux/kthread.h>
45*4882a593Smuzhiyun #include <linux/netdevice.h>
46*4882a593Smuzhiyun #include <linux/sched.h>
47*4882a593Smuzhiyun #include <linux/etherdevice.h>
48*4882a593Smuzhiyun #include <linux/wireless.h>
49*4882a593Smuzhiyun #include <linux/ieee80211.h>
50*4882a593Smuzhiyun #include <linux/wait.h>
51*4882a593Smuzhiyun #include <net/cfg80211.h>
52*4882a593Smuzhiyun #include <net/rtnetlink.h>
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun #include <wlioctl.h>
55*4882a593Smuzhiyun #include <bcmevent.h>
56*4882a593Smuzhiyun #include <wldev_common.h>
57*4882a593Smuzhiyun #include <wl_cfg80211.h>
58*4882a593Smuzhiyun #include <wl_cfgp2p.h>
59*4882a593Smuzhiyun #include <wl_cfgscan.h>
60*4882a593Smuzhiyun #include <wl_cfgvif.h>
61*4882a593Smuzhiyun #include <bcmdevs.h>
62*4882a593Smuzhiyun #include <bcmdevs_legacy.h>
63*4882a593Smuzhiyun #ifdef WL_FILS
64*4882a593Smuzhiyun #include <fils.h>
65*4882a593Smuzhiyun #include <frag.h>
66*4882a593Smuzhiyun #endif /* WL_FILS */
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun #ifdef OEM_ANDROID
69*4882a593Smuzhiyun #include <wl_android.h>
70*4882a593Smuzhiyun #endif
71*4882a593Smuzhiyun 
72*4882a593Smuzhiyun #if defined(BCMDONGLEHOST)
73*4882a593Smuzhiyun #include <dngl_stats.h>
74*4882a593Smuzhiyun #include <dhd.h>
75*4882a593Smuzhiyun #include <dhd_linux.h>
76*4882a593Smuzhiyun #include <dhd_linux_pktdump.h>
77*4882a593Smuzhiyun #include <dhd_debug.h>
78*4882a593Smuzhiyun #include <dhdioctl.h>
79*4882a593Smuzhiyun #include <wlioctl.h>
80*4882a593Smuzhiyun #include <dhd_cfg80211.h>
81*4882a593Smuzhiyun #include <dhd_bus.h>
82*4882a593Smuzhiyun #include <wl_cfgvendor.h>
83*4882a593Smuzhiyun #endif /* defined(BCMDONGLEHOST) */
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun #ifdef WL_NAN
86*4882a593Smuzhiyun #include <wl_cfgnan.h>
87*4882a593Smuzhiyun #endif /* WL_NAN */
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun #ifdef BCMPCIE
90*4882a593Smuzhiyun #include <dhd_flowring.h>
91*4882a593Smuzhiyun #endif
92*4882a593Smuzhiyun #if defined(BIGDATA_SOFTAP) || defined(DHD_ENABLE_BIGDATA_LOGGING)
93*4882a593Smuzhiyun #include <wl_bigdata.h>
94*4882a593Smuzhiyun #endif /* BIGDATA_SOFTAP || DHD_ENABLE_BIGDATA_LOGGING */
95*4882a593Smuzhiyun #include <dhd_config.h>
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun #define	MAX_VIF_OFFSET	15
98*4882a593Smuzhiyun #define MAX_WAIT_TIME 1500
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun #if !defined(BCMDONGLEHOST)
101*4882a593Smuzhiyun #ifdef ntoh32
102*4882a593Smuzhiyun #undef ntoh32
103*4882a593Smuzhiyun #endif
104*4882a593Smuzhiyun #ifdef ntoh16
105*4882a593Smuzhiyun #undef ntoh16
106*4882a593Smuzhiyun #endif
107*4882a593Smuzhiyun #ifdef htod32
108*4882a593Smuzhiyun #undef htod32
109*4882a593Smuzhiyun #endif
110*4882a593Smuzhiyun #ifdef htod16
111*4882a593Smuzhiyun #undef htod16
112*4882a593Smuzhiyun #endif
113*4882a593Smuzhiyun #define ntoh32(i) (i)
114*4882a593Smuzhiyun #define ntoh16(i) (i)
115*4882a593Smuzhiyun #define htod32(i) (i)
116*4882a593Smuzhiyun #define htod16(i) (i)
117*4882a593Smuzhiyun #define DNGL_FUNC(func, parameters)
118*4882a593Smuzhiyun #else
119*4882a593Smuzhiyun #define DNGL_FUNC(func, parameters) func parameters
120*4882a593Smuzhiyun #define COEX_DHCP
121*4882a593Smuzhiyun 
122*4882a593Smuzhiyun #endif /* defined(BCMDONGLEHOST) */
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun #if defined(STRICT_GCC_WARNINGS) && defined(__GNUC__) && \
125*4882a593Smuzhiyun (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6))
126*4882a593Smuzhiyun _Pragma("GCC diagnostic pop")
127*4882a593Smuzhiyun #endif
128*4882a593Smuzhiyun 
129*4882a593Smuzhiyun /* SoftAP related parameters */
130*4882a593Smuzhiyun #define DEFAULT_2G_SOFTAP_CHANNEL	1
131*4882a593Smuzhiyun #define DEFAULT_2G_SOFTAP_CHANSPEC	0x1006
132*4882a593Smuzhiyun #define DEFAULT_5G_SOFTAP_CHANNEL	149
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun #define MAX_VNDR_OUI_STR_LEN	256u
135*4882a593Smuzhiyun #define VNDR_OUI_STR_LEN	10u
136*4882a593Smuzhiyun #define DOT11_DISCONNECT_RC     2u
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun #if defined(WL_FW_OCE_AP_SELECT)
139*4882a593Smuzhiyun static bool
140*4882a593Smuzhiyun wl_cfgoce_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type);
141*4882a593Smuzhiyun 
142*4882a593Smuzhiyun /* Check whether the given IE looks like WFA OCE IE. */
143*4882a593Smuzhiyun #define wl_cfgoce_is_oce_ie(ie, tlvs, len)      wl_cfgoce_has_ie(ie, tlvs, len, \
144*4882a593Smuzhiyun 	(const uint8 *)WFA_OUI, WFA_OUI_LEN, WFA_OUI_TYPE_MBO_OCE)
145*4882a593Smuzhiyun 
146*4882a593Smuzhiyun /* Is any of the tlvs the expected entry? If
147*4882a593Smuzhiyun  * not update the tlvs buffer pointer/length.
148*4882a593Smuzhiyun  */
149*4882a593Smuzhiyun static bool
wl_cfgoce_has_ie(const u8 * ie,const u8 ** tlvs,u32 * tlvs_len,const u8 * oui,u32 oui_len,u8 type)150*4882a593Smuzhiyun wl_cfgoce_has_ie(const u8 *ie, const u8 **tlvs, u32 *tlvs_len, const u8 *oui, u32 oui_len, u8 type)
151*4882a593Smuzhiyun {
152*4882a593Smuzhiyun 	/* If the contents match the OUI and the type */
153*4882a593Smuzhiyun 	if (ie[TLV_LEN_OFF] >= oui_len + 1 &&
154*4882a593Smuzhiyun 			!bcmp(&ie[TLV_BODY_OFF], oui, oui_len) &&
155*4882a593Smuzhiyun 			type == ie[TLV_BODY_OFF + oui_len]) {
156*4882a593Smuzhiyun 		return TRUE;
157*4882a593Smuzhiyun 	}
158*4882a593Smuzhiyun 
159*4882a593Smuzhiyun 	return FALSE;
160*4882a593Smuzhiyun }
161*4882a593Smuzhiyun #endif /* WL_FW_OCE_AP_SELECT */
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun static bool check_dev_role_integrity(struct bcm_cfg80211 *cfg, u32 dev_role);
164*4882a593Smuzhiyun 
165*4882a593Smuzhiyun #ifdef SUPPORT_AP_BWCTRL
166*4882a593Smuzhiyun static int bw2cap[] = { 0, 0, WLC_BW_CAP_20MHZ, WLC_BW_CAP_40MHZ, WLC_BW_CAP_80MHZ,
167*4882a593Smuzhiyun 	WLC_BW_CAP_160MHZ, WLC_BW_CAP_160MHZ };
168*4882a593Smuzhiyun #endif /* SUPPORT_AP_BWCTRL */
169*4882a593Smuzhiyun 
170*4882a593Smuzhiyun #if !defined(BCMDONGLEHOST)
171*4882a593Smuzhiyun /* Wake lock are used in Android only, which is dongle based as of now */
172*4882a593Smuzhiyun #define DHD_OS_WAKE_LOCK(pub)
173*4882a593Smuzhiyun #define DHD_OS_WAKE_UNLOCK(pub)
174*4882a593Smuzhiyun #define DHD_EVENT_WAKE_LOCK(pub)
175*4882a593Smuzhiyun #define DHD_EVENT_WAKE_UNLOCK(pub)
176*4882a593Smuzhiyun #define DHD_OS_WAKE_LOCK_TIMEOUT(pub)
177*4882a593Smuzhiyun #endif /* defined(BCMDONGLEHOST) */
178*4882a593Smuzhiyun 
179*4882a593Smuzhiyun #define IS_WPA_AKM(akm) ((akm) == RSN_AKM_NONE ||			\
180*4882a593Smuzhiyun 				 (akm) == RSN_AKM_UNSPECIFIED ||	\
181*4882a593Smuzhiyun 				 (akm) == RSN_AKM_PSK)
182*4882a593Smuzhiyun 
183*4882a593Smuzhiyun #ifdef SUPPORT_AP_BWCTRL
184*4882a593Smuzhiyun static void
185*4882a593Smuzhiyun wl_update_apchan_bwcap(struct bcm_cfg80211 *cfg, struct net_device *ndev, chanspec_t chanspec);
186*4882a593Smuzhiyun #endif /* SUPPORT_AP_BWCTRL */
187*4882a593Smuzhiyun 
188*4882a593Smuzhiyun #if ((LINUX_VERSION_CODE >= KERNEL_VERSION (3, 5, 0)) && (LINUX_VERSION_CODE <= (3, 7, 0)))
189*4882a593Smuzhiyun struct chan_info {
190*4882a593Smuzhiyun 	int freq;
191*4882a593Smuzhiyun 	int chan_type;
192*4882a593Smuzhiyun };
193*4882a593Smuzhiyun #endif
194*4882a593Smuzhiyun 
195*4882a593Smuzhiyun #if defined(WL_FW_OCE_AP_SELECT)
wl_cfg80211_is_oce_ap(struct wiphy * wiphy,const u8 * bssid_hint)196*4882a593Smuzhiyun bool wl_cfg80211_is_oce_ap(struct wiphy *wiphy, const u8 *bssid_hint)
197*4882a593Smuzhiyun {
198*4882a593Smuzhiyun 	const u8 *parse = NULL;
199*4882a593Smuzhiyun 	bcm_tlv_t *ie;
200*4882a593Smuzhiyun 	const struct cfg80211_bss_ies *ies;
201*4882a593Smuzhiyun 	u32 len;
202*4882a593Smuzhiyun 	struct cfg80211_bss *bss;
203*4882a593Smuzhiyun 
204*4882a593Smuzhiyun 	bss = CFG80211_GET_BSS(wiphy, NULL, bssid_hint, 0, 0);
205*4882a593Smuzhiyun 	if (!bss) {
206*4882a593Smuzhiyun 		WL_ERR(("Unable to find AP in the cache"));
207*4882a593Smuzhiyun 		return false;
208*4882a593Smuzhiyun 	}
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun 	if (rcu_access_pointer(bss->ies)) {
211*4882a593Smuzhiyun 		ies = rcu_access_pointer(bss->ies);
212*4882a593Smuzhiyun 		parse = ies->data;
213*4882a593Smuzhiyun 		len = ies->len;
214*4882a593Smuzhiyun 	} else {
215*4882a593Smuzhiyun 		WL_ERR(("ies is NULL"));
216*4882a593Smuzhiyun 		return false;
217*4882a593Smuzhiyun 	}
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun 	while ((ie = bcm_parse_tlvs(parse, len, DOT11_MNG_VS_ID))) {
220*4882a593Smuzhiyun 		if (wl_cfgoce_is_oce_ie((const uint8*)ie, (u8 const **)&parse, &len) == TRUE) {
221*4882a593Smuzhiyun 			return true;
222*4882a593Smuzhiyun 		} else {
223*4882a593Smuzhiyun 			ie = bcm_next_tlv((const bcm_tlv_t*) ie, &len);
224*4882a593Smuzhiyun 			if (!ie) {
225*4882a593Smuzhiyun 				return false;
226*4882a593Smuzhiyun 			}
227*4882a593Smuzhiyun 			parse = (uint8 *)ie;
228*4882a593Smuzhiyun 			WL_DBG(("NON OCE IE. next ie ptr:%p", parse));
229*4882a593Smuzhiyun 		}
230*4882a593Smuzhiyun 	}
231*4882a593Smuzhiyun 	WL_DBG(("OCE IE NOT found"));
232*4882a593Smuzhiyun 	return false;
233*4882a593Smuzhiyun }
234*4882a593Smuzhiyun #endif /* WL_FW_OCE_AP_SELECT */
235*4882a593Smuzhiyun 
236*4882a593Smuzhiyun /* Dump the contents of the encoded wps ie buffer and get pbc value */
237*4882a593Smuzhiyun void
wl_validate_wps_ie(const char * wps_ie,s32 wps_ie_len,bool * pbc)238*4882a593Smuzhiyun wl_validate_wps_ie(const char *wps_ie, s32 wps_ie_len, bool *pbc)
239*4882a593Smuzhiyun {
240*4882a593Smuzhiyun 	#define WPS_IE_FIXED_LEN 6
241*4882a593Smuzhiyun 	s16 len;
242*4882a593Smuzhiyun 	const u8 *subel = NULL;
243*4882a593Smuzhiyun 	u16 subelt_id;
244*4882a593Smuzhiyun 	u16 subelt_len;
245*4882a593Smuzhiyun 	u16 val;
246*4882a593Smuzhiyun 	u8 *valptr = (uint8*) &val;
247*4882a593Smuzhiyun 	if (wps_ie == NULL || wps_ie_len < WPS_IE_FIXED_LEN) {
248*4882a593Smuzhiyun 		WL_ERR(("invalid argument : NULL\n"));
249*4882a593Smuzhiyun 		return;
250*4882a593Smuzhiyun 	}
251*4882a593Smuzhiyun 	len = (s16)wps_ie[TLV_LEN_OFF];
252*4882a593Smuzhiyun 
253*4882a593Smuzhiyun 	if (len > wps_ie_len) {
254*4882a593Smuzhiyun 		WL_ERR(("invalid length len %d, wps ie len %d\n", len, wps_ie_len));
255*4882a593Smuzhiyun 		return;
256*4882a593Smuzhiyun 	}
257*4882a593Smuzhiyun 	WL_DBG(("wps_ie len=%d\n", len));
258*4882a593Smuzhiyun 	len -= 4;	/* for the WPS IE's OUI, oui_type fields */
259*4882a593Smuzhiyun 	subel = wps_ie + WPS_IE_FIXED_LEN;
260*4882a593Smuzhiyun 	while (len >= 4) {		/* must have attr id, attr len fields */
261*4882a593Smuzhiyun 		valptr[0] = *subel++;
262*4882a593Smuzhiyun 		valptr[1] = *subel++;
263*4882a593Smuzhiyun 		subelt_id = HTON16(val);
264*4882a593Smuzhiyun 
265*4882a593Smuzhiyun 		valptr[0] = *subel++;
266*4882a593Smuzhiyun 		valptr[1] = *subel++;
267*4882a593Smuzhiyun 		subelt_len = HTON16(val);
268*4882a593Smuzhiyun 
269*4882a593Smuzhiyun 		len -= 4;			/* for the attr id, attr len fields */
270*4882a593Smuzhiyun 		len -= (s16)subelt_len;	/* for the remaining fields in this attribute */
271*4882a593Smuzhiyun 		if (len < 0) {
272*4882a593Smuzhiyun 			break;
273*4882a593Smuzhiyun 		}
274*4882a593Smuzhiyun 		WL_DBG((" subel=%p, subelt_id=0x%x subelt_len=%u\n",
275*4882a593Smuzhiyun 			subel, subelt_id, subelt_len));
276*4882a593Smuzhiyun 
277*4882a593Smuzhiyun 		if (subelt_id == WPS_ID_VERSION) {
278*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_VERSION: %u\n", *subel));
279*4882a593Smuzhiyun 		} else if (subelt_id == WPS_ID_REQ_TYPE) {
280*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_REQ_TYPE: %u\n", *subel));
281*4882a593Smuzhiyun 		} else if (subelt_id == WPS_ID_CONFIG_METHODS) {
282*4882a593Smuzhiyun 			valptr[0] = *subel;
283*4882a593Smuzhiyun 			valptr[1] = *(subel + 1);
284*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_CONFIG_METHODS: %x\n", HTON16(val)));
285*4882a593Smuzhiyun 		} else if (subelt_id == WPS_ID_DEVICE_NAME) {
286*4882a593Smuzhiyun 			char devname[33];
287*4882a593Smuzhiyun 			int namelen = MIN(subelt_len, (sizeof(devname) - 1));
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 			if (namelen) {
290*4882a593Smuzhiyun 				memcpy(devname, subel, namelen);
291*4882a593Smuzhiyun 				devname[namelen] = '\0';
292*4882a593Smuzhiyun 				/* Printing len as rx'ed in the IE */
293*4882a593Smuzhiyun 				WL_DBG(("  attr WPS_ID_DEVICE_NAME: %s (len %u)\n",
294*4882a593Smuzhiyun 					devname, subelt_len));
295*4882a593Smuzhiyun 			}
296*4882a593Smuzhiyun 		} else if (subelt_id == WPS_ID_DEVICE_PWD_ID) {
297*4882a593Smuzhiyun 			valptr[0] = *subel;
298*4882a593Smuzhiyun 			valptr[1] = *(subel + 1);
299*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_DEVICE_PWD_ID: %u\n", HTON16(val)));
300*4882a593Smuzhiyun 			*pbc = (HTON16(val) == DEV_PW_PUSHBUTTON) ? true : false;
301*4882a593Smuzhiyun 		} else if (subelt_id == WPS_ID_PRIM_DEV_TYPE) {
302*4882a593Smuzhiyun 			valptr[0] = *subel;
303*4882a593Smuzhiyun 			valptr[1] = *(subel + 1);
304*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_PRIM_DEV_TYPE: cat=%u \n", HTON16(val)));
305*4882a593Smuzhiyun 			valptr[0] = *(subel + 6);
306*4882a593Smuzhiyun 			valptr[1] = *(subel + 7);
307*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_PRIM_DEV_TYPE: subcat=%u\n", HTON16(val)));
308*4882a593Smuzhiyun 		} else if (subelt_id == WPS_ID_REQ_DEV_TYPE) {
309*4882a593Smuzhiyun 			valptr[0] = *subel;
310*4882a593Smuzhiyun 			valptr[1] = *(subel + 1);
311*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_REQ_DEV_TYPE: cat=%u\n", HTON16(val)));
312*4882a593Smuzhiyun 			valptr[0] = *(subel + 6);
313*4882a593Smuzhiyun 			valptr[1] = *(subel + 7);
314*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_REQ_DEV_TYPE: subcat=%u\n", HTON16(val)));
315*4882a593Smuzhiyun 		} else if (subelt_id == WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS) {
316*4882a593Smuzhiyun 			valptr[0] = *subel;
317*4882a593Smuzhiyun 			valptr[1] = *(subel + 1);
318*4882a593Smuzhiyun 			WL_DBG(("  attr WPS_ID_SELECTED_REGISTRAR_CONFIG_METHODS"
319*4882a593Smuzhiyun 				": cat=%u\n", HTON16(val)));
320*4882a593Smuzhiyun 		} else {
321*4882a593Smuzhiyun 			WL_DBG(("  unknown attr 0x%x\n", subelt_id));
322*4882a593Smuzhiyun 		}
323*4882a593Smuzhiyun 
324*4882a593Smuzhiyun 		subel += subelt_len;
325*4882a593Smuzhiyun 	}
326*4882a593Smuzhiyun }
327*4882a593Smuzhiyun 
328*4882a593Smuzhiyun bool
wl_cfg80211_check_vif_in_use(struct net_device * ndev)329*4882a593Smuzhiyun wl_cfg80211_check_vif_in_use(struct net_device *ndev)
330*4882a593Smuzhiyun {
331*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(ndev);
332*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
333*4882a593Smuzhiyun 	bool nan_enabled = FALSE;
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun #ifdef WL_NAN
336*4882a593Smuzhiyun 	nan_enabled = wl_cfgnan_is_enabled(cfg);
337*4882a593Smuzhiyun #endif /* WL_NAN */
338*4882a593Smuzhiyun 
339*4882a593Smuzhiyun 	if (nan_enabled || (wl_cfgp2p_vif_created(cfg)) ||
340*4882a593Smuzhiyun 		(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) {
341*4882a593Smuzhiyun 		WL_MEM(("%s: Virtual interfaces in use. NAN %d P2P %d softAP %d\n",
342*4882a593Smuzhiyun 			__FUNCTION__, nan_enabled, wl_cfgp2p_vif_created(cfg),
343*4882a593Smuzhiyun 			(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)));
344*4882a593Smuzhiyun 		return TRUE;
345*4882a593Smuzhiyun 	}
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	return FALSE;
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun 
350*4882a593Smuzhiyun #ifdef WL_IFACE_MGMT_CONF
351*4882a593Smuzhiyun #ifdef WL_IFACE_MGMT
352*4882a593Smuzhiyun static s32
wl_cfg80211_is_policy_config_allowed(struct bcm_cfg80211 * cfg)353*4882a593Smuzhiyun wl_cfg80211_is_policy_config_allowed(struct bcm_cfg80211 *cfg)
354*4882a593Smuzhiyun {
355*4882a593Smuzhiyun 	s32 ret = BCME_OK;
356*4882a593Smuzhiyun 	wl_iftype_t active_sec_iface = WL_IFACE_NOT_PRESENT;
357*4882a593Smuzhiyun 	bool p2p_disc_on = false;
358*4882a593Smuzhiyun 	bool sta_assoc_state = false;
359*4882a593Smuzhiyun 	bool nan_init_state = false;
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun 	mutex_lock(&cfg->if_sync);
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	sta_assoc_state = (wl_get_drv_status(cfg, CONNECTED, bcmcfg_to_prmry_ndev(cfg)) ||
364*4882a593Smuzhiyun 		wl_get_drv_status(cfg, CONNECTING, bcmcfg_to_prmry_ndev(cfg)));
365*4882a593Smuzhiyun 	active_sec_iface = wl_cfg80211_get_sec_iface(cfg);
366*4882a593Smuzhiyun 	p2p_disc_on = wl_get_p2p_status(cfg, SCANNING);
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun #ifdef WL_NAN
369*4882a593Smuzhiyun 	if (cfg->nancfg) {
370*4882a593Smuzhiyun 		nan_init_state = cfg->nancfg->nan_init_state;
371*4882a593Smuzhiyun 	}
372*4882a593Smuzhiyun #endif
373*4882a593Smuzhiyun 
374*4882a593Smuzhiyun 	if ((sta_assoc_state == TRUE) || (p2p_disc_on == TRUE) ||
375*4882a593Smuzhiyun 			(nan_init_state == TRUE) ||
376*4882a593Smuzhiyun 			(active_sec_iface != WL_IFACE_NOT_PRESENT)) {
377*4882a593Smuzhiyun 		WL_INFORM_MEM(("Active iface matrix: sta_assoc_state = %d,"
378*4882a593Smuzhiyun 			" p2p_disc = %d, nan_disc = %d, active iface = %s\n",
379*4882a593Smuzhiyun 			sta_assoc_state, p2p_disc_on, nan_init_state,
380*4882a593Smuzhiyun 			wl_iftype_to_str(active_sec_iface)));
381*4882a593Smuzhiyun 		ret = BCME_BUSY;
382*4882a593Smuzhiyun 	}
383*4882a593Smuzhiyun 	mutex_unlock(&cfg->if_sync);
384*4882a593Smuzhiyun 	return ret;
385*4882a593Smuzhiyun }
386*4882a593Smuzhiyun #endif /* WL_IFACE_MGMT */
387*4882a593Smuzhiyun #ifdef WL_NANP2P
388*4882a593Smuzhiyun int
wl_cfg80211_set_iface_conc_disc(struct net_device * ndev,uint8 arg_val)389*4882a593Smuzhiyun wl_cfg80211_set_iface_conc_disc(struct net_device *ndev,
390*4882a593Smuzhiyun 	uint8 arg_val)
391*4882a593Smuzhiyun {
392*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(ndev);
393*4882a593Smuzhiyun 	if (!cfg) {
394*4882a593Smuzhiyun 		WL_ERR(("%s: Cannot find cfg\n", __FUNCTION__));
395*4882a593Smuzhiyun 		return BCME_ERROR;
396*4882a593Smuzhiyun 	}
397*4882a593Smuzhiyun 
398*4882a593Smuzhiyun 	if (wl_cfg80211_is_policy_config_allowed(cfg) != BCME_OK) {
399*4882a593Smuzhiyun 		WL_ERR(("Cant allow iface management modifications\n"));
400*4882a593Smuzhiyun 		return BCME_BUSY;
401*4882a593Smuzhiyun 	}
402*4882a593Smuzhiyun 
403*4882a593Smuzhiyun 	if (arg_val) {
404*4882a593Smuzhiyun 		cfg->conc_disc |= arg_val;
405*4882a593Smuzhiyun 	} else {
406*4882a593Smuzhiyun 		cfg->conc_disc &= ~arg_val;
407*4882a593Smuzhiyun 	}
408*4882a593Smuzhiyun 	return BCME_OK;
409*4882a593Smuzhiyun }
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun uint8
wl_cfg80211_get_iface_conc_disc(struct net_device * ndev)412*4882a593Smuzhiyun wl_cfg80211_get_iface_conc_disc(struct net_device *ndev)
413*4882a593Smuzhiyun {
414*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(ndev);
415*4882a593Smuzhiyun 	if (!cfg) {
416*4882a593Smuzhiyun 		WL_ERR(("%s: Cannot find cfg\n", __FUNCTION__));
417*4882a593Smuzhiyun 		return BCME_ERROR;
418*4882a593Smuzhiyun 	}
419*4882a593Smuzhiyun 	return cfg->conc_disc;
420*4882a593Smuzhiyun }
421*4882a593Smuzhiyun #endif /* WL_NANP2P */
422*4882a593Smuzhiyun #ifdef WL_IFACE_MGMT
423*4882a593Smuzhiyun int
wl_cfg80211_set_iface_policy(struct net_device * ndev,char * arg,int len)424*4882a593Smuzhiyun wl_cfg80211_set_iface_policy(struct net_device *ndev,
425*4882a593Smuzhiyun 	char *arg, int len)
426*4882a593Smuzhiyun {
427*4882a593Smuzhiyun 	int ret = BCME_OK;
428*4882a593Smuzhiyun 	uint8 i = 0;
429*4882a593Smuzhiyun 	iface_mgmt_data_t *iface_data = NULL;
430*4882a593Smuzhiyun 
431*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(ndev);
432*4882a593Smuzhiyun 	if (!cfg) {
433*4882a593Smuzhiyun 		WL_ERR(("%s: Cannot find cfg\n", __FUNCTION__));
434*4882a593Smuzhiyun 		return BCME_ERROR;
435*4882a593Smuzhiyun 	}
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun 	if (wl_cfg80211_is_policy_config_allowed(cfg) != BCME_OK) {
438*4882a593Smuzhiyun 		WL_ERR(("Cant allow iface management modifications\n"));
439*4882a593Smuzhiyun 		return BCME_BUSY;
440*4882a593Smuzhiyun 	}
441*4882a593Smuzhiyun 
442*4882a593Smuzhiyun 	if (!arg || len <= 0 || len > sizeof(iface_mgmt_data_t)) {
443*4882a593Smuzhiyun 		return BCME_BADARG;
444*4882a593Smuzhiyun 	}
445*4882a593Smuzhiyun 
446*4882a593Smuzhiyun 	iface_data = (iface_mgmt_data_t *)arg;
447*4882a593Smuzhiyun 	if (iface_data->policy >= WL_IF_POLICY_INVALID) {
448*4882a593Smuzhiyun 		WL_ERR(("Unexpected value of policy = %d\n",
449*4882a593Smuzhiyun 			iface_data->policy));
450*4882a593Smuzhiyun 		return BCME_BADARG;
451*4882a593Smuzhiyun 	}
452*4882a593Smuzhiyun 
453*4882a593Smuzhiyun 	bzero(&cfg->iface_data, sizeof(iface_mgmt_data_t));
454*4882a593Smuzhiyun 	ret = memcpy_s(&cfg->iface_data, sizeof(iface_mgmt_data_t), arg, len);
455*4882a593Smuzhiyun 	if (ret != BCME_OK) {
456*4882a593Smuzhiyun 		WL_ERR(("Failed to copy iface data, src len = %d\n", len));
457*4882a593Smuzhiyun 		return ret;
458*4882a593Smuzhiyun 	}
459*4882a593Smuzhiyun 
460*4882a593Smuzhiyun 	if (cfg->iface_data.policy == WL_IF_POLICY_ROLE_PRIORITY) {
461*4882a593Smuzhiyun 		for (i = 0; i < WL_IF_TYPE_MAX; i++) {
462*4882a593Smuzhiyun 			WL_DBG(("iface = %s, priority[i] = %d\n",
463*4882a593Smuzhiyun 			wl_iftype_to_str(i), cfg->iface_data.priority[i]));
464*4882a593Smuzhiyun 		}
465*4882a593Smuzhiyun 	}
466*4882a593Smuzhiyun 
467*4882a593Smuzhiyun 	return ret;
468*4882a593Smuzhiyun }
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun uint8
wl_cfg80211_get_iface_policy(struct net_device * ndev)471*4882a593Smuzhiyun wl_cfg80211_get_iface_policy(struct net_device *ndev)
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun {
474*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(ndev);
475*4882a593Smuzhiyun 	if (!cfg) {
476*4882a593Smuzhiyun 		WL_ERR(("%s: Cannot find cfg\n", __FUNCTION__));
477*4882a593Smuzhiyun 		return BCME_ERROR;
478*4882a593Smuzhiyun 	}
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun 	return cfg->iface_data.policy;
481*4882a593Smuzhiyun }
482*4882a593Smuzhiyun #endif /* WL_IFACE_MGMT */
483*4882a593Smuzhiyun #endif /* WL_IFACE_MGMT_CONF */
484*4882a593Smuzhiyun 
485*4882a593Smuzhiyun #ifdef WL_IFACE_MGMT
486*4882a593Smuzhiyun /* Get active secondary data iface type */
487*4882a593Smuzhiyun wl_iftype_t
wl_cfg80211_get_sec_iface(struct bcm_cfg80211 * cfg)488*4882a593Smuzhiyun wl_cfg80211_get_sec_iface(struct bcm_cfg80211 *cfg)
489*4882a593Smuzhiyun {
490*4882a593Smuzhiyun #ifdef WL_STATIC_IF
491*4882a593Smuzhiyun 	struct net_device *static_if_ndev;
492*4882a593Smuzhiyun #else
493*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
494*4882a593Smuzhiyun #endif /* WL_STATIC_IF */
495*4882a593Smuzhiyun 	struct net_device *p2p_ndev = NULL;
496*4882a593Smuzhiyun 
497*4882a593Smuzhiyun 	p2p_ndev = wl_to_p2p_bss_ndev(cfg,
498*4882a593Smuzhiyun 		P2PAPI_BSSCFG_CONNECTION1);
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun #ifdef WL_STATIC_IF
501*4882a593Smuzhiyun 	static_if_ndev = wl_cfg80211_static_if_active(cfg);
502*4882a593Smuzhiyun 	if (static_if_ndev) {
503*4882a593Smuzhiyun 		if (IS_AP_IFACE(static_if_ndev->ieee80211_ptr)) {
504*4882a593Smuzhiyun 			return WL_IF_TYPE_AP;
505*4882a593Smuzhiyun 		}
506*4882a593Smuzhiyun 	}
507*4882a593Smuzhiyun #else
508*4882a593Smuzhiyun 	if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) {
509*4882a593Smuzhiyun 		return WL_IF_TYPE_AP;
510*4882a593Smuzhiyun 	}
511*4882a593Smuzhiyun #endif /* WL_STATIC_IF */
512*4882a593Smuzhiyun 
513*4882a593Smuzhiyun 	if (p2p_ndev && p2p_ndev->ieee80211_ptr) {
514*4882a593Smuzhiyun 		if (p2p_ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
515*4882a593Smuzhiyun 			return WL_IF_TYPE_P2P_GO;
516*4882a593Smuzhiyun 		}
517*4882a593Smuzhiyun 
518*4882a593Smuzhiyun 		/* Set role to GC when cfg80211 layer downgrades P2P
519*4882a593Smuzhiyun 		 * role to station type while bringing down the interface
520*4882a593Smuzhiyun 		 */
521*4882a593Smuzhiyun 		if (p2p_ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION) {
522*4882a593Smuzhiyun 			WL_DBG_MEM(("%s, Change to GC base role\n", __FUNCTION__));
523*4882a593Smuzhiyun 			return WL_IF_TYPE_P2P_GC;
524*4882a593Smuzhiyun 		}
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 		if (p2p_ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_CLIENT) {
527*4882a593Smuzhiyun 			return WL_IF_TYPE_P2P_GC;
528*4882a593Smuzhiyun 		}
529*4882a593Smuzhiyun 	}
530*4882a593Smuzhiyun 
531*4882a593Smuzhiyun #ifdef WL_NAN
532*4882a593Smuzhiyun 	if (wl_cfgnan_is_dp_active(bcmcfg_to_prmry_ndev(cfg))) {
533*4882a593Smuzhiyun 		return WL_IF_TYPE_NAN;
534*4882a593Smuzhiyun 	}
535*4882a593Smuzhiyun #endif /* WL_NAN */
536*4882a593Smuzhiyun 	return WL_IFACE_NOT_PRESENT;
537*4882a593Smuzhiyun }
538*4882a593Smuzhiyun 
539*4882a593Smuzhiyun /*
540*4882a593Smuzhiyun * Handle incoming data interface request based on policy.
541*4882a593Smuzhiyun * If there is any conflicting interface, that will be
542*4882a593Smuzhiyun * deleted.
543*4882a593Smuzhiyun */
544*4882a593Smuzhiyun static s32
wl_cfg80211_data_if_mgmt(struct bcm_cfg80211 * cfg,wl_iftype_t new_wl_iftype)545*4882a593Smuzhiyun wl_cfg80211_data_if_mgmt(struct bcm_cfg80211 *cfg,
546*4882a593Smuzhiyun 	wl_iftype_t new_wl_iftype)
547*4882a593Smuzhiyun {
548*4882a593Smuzhiyun 	s32 ret = BCME_OK;
549*4882a593Smuzhiyun 	bool del_iface = false;
550*4882a593Smuzhiyun 	wl_iftype_t sec_wl_if_type = wl_cfg80211_get_sec_iface(cfg);
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	if (sec_wl_if_type == WL_IF_TYPE_NAN &&
553*4882a593Smuzhiyun 		new_wl_iftype == WL_IF_TYPE_NAN) {
554*4882a593Smuzhiyun 		/* Multi NDP is allowed irrespective of Policy */
555*4882a593Smuzhiyun 		return BCME_OK;
556*4882a593Smuzhiyun 	}
557*4882a593Smuzhiyun 
558*4882a593Smuzhiyun 	if (sec_wl_if_type == WL_IFACE_NOT_PRESENT) {
559*4882a593Smuzhiyun 		/*
560*4882a593Smuzhiyun 		* If there is no active secondary I/F, there
561*4882a593Smuzhiyun 		* is no interface conflict. Do nothing.
562*4882a593Smuzhiyun 		*/
563*4882a593Smuzhiyun 		return BCME_OK;
564*4882a593Smuzhiyun 	}
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	/* Handle secondary data link case */
567*4882a593Smuzhiyun 	switch (cfg->iface_data.policy) {
568*4882a593Smuzhiyun 		case WL_IF_POLICY_CUSTOM:
569*4882a593Smuzhiyun 		case WL_IF_POLICY_DEFAULT: {
570*4882a593Smuzhiyun 			WL_INFORM_MEM(("%s, Delete any existing iface\n", __FUNCTION__));
571*4882a593Smuzhiyun 			del_iface = true;
572*4882a593Smuzhiyun 			break;
573*4882a593Smuzhiyun 		}
574*4882a593Smuzhiyun 		case WL_IF_POLICY_FCFS: {
575*4882a593Smuzhiyun 			WL_INFORM_MEM(("Found active iface = %s, can't support new iface = %s\n",
576*4882a593Smuzhiyun 				wl_iftype_to_str(sec_wl_if_type), wl_iftype_to_str(new_wl_iftype)));
577*4882a593Smuzhiyun 			ret = BCME_ERROR;
578*4882a593Smuzhiyun 			break;
579*4882a593Smuzhiyun 		}
580*4882a593Smuzhiyun 		case WL_IF_POLICY_LP: {
581*4882a593Smuzhiyun 			WL_INFORM_MEM(("Remove active sec data interface, allow incoming iface\n"));
582*4882a593Smuzhiyun 			/* Delete existing data iface and allow incoming sec iface */
583*4882a593Smuzhiyun 			del_iface = true;
584*4882a593Smuzhiyun 			break;
585*4882a593Smuzhiyun 		}
586*4882a593Smuzhiyun 		case WL_IF_POLICY_ROLE_PRIORITY: {
587*4882a593Smuzhiyun 			WL_INFORM_MEM(("Existing iface = %s (%d) and new iface = %s (%d)\n",
588*4882a593Smuzhiyun 				wl_iftype_to_str(sec_wl_if_type),
589*4882a593Smuzhiyun 				cfg->iface_data.priority[sec_wl_if_type],
590*4882a593Smuzhiyun 				wl_iftype_to_str(new_wl_iftype),
591*4882a593Smuzhiyun 				cfg->iface_data.priority[new_wl_iftype]));
592*4882a593Smuzhiyun 			if (cfg->iface_data.priority[new_wl_iftype] >
593*4882a593Smuzhiyun 				cfg->iface_data.priority[sec_wl_if_type]) {
594*4882a593Smuzhiyun 				del_iface = true;
595*4882a593Smuzhiyun 			} else {
596*4882a593Smuzhiyun 				WL_ERR(("Can't support new iface = %s\n",
597*4882a593Smuzhiyun 					wl_iftype_to_str(new_wl_iftype)));
598*4882a593Smuzhiyun 					ret = BCME_ERROR;
599*4882a593Smuzhiyun 			}
600*4882a593Smuzhiyun 			break;
601*4882a593Smuzhiyun 		}
602*4882a593Smuzhiyun 		default: {
603*4882a593Smuzhiyun 			WL_ERR(("Unsupported interface policy = %d\n",
604*4882a593Smuzhiyun 				cfg->iface_data.policy));
605*4882a593Smuzhiyun 			return BCME_ERROR;
606*4882a593Smuzhiyun 		}
607*4882a593Smuzhiyun 	}
608*4882a593Smuzhiyun 	if (del_iface) {
609*4882a593Smuzhiyun 		ret = wl_cfg80211_delete_iface(cfg, sec_wl_if_type);
610*4882a593Smuzhiyun 	}
611*4882a593Smuzhiyun 	return ret;
612*4882a593Smuzhiyun }
613*4882a593Smuzhiyun 
614*4882a593Smuzhiyun /* Handle discovery ifaces based on policy */
615*4882a593Smuzhiyun static s32
wl_cfg80211_disc_if_mgmt(struct bcm_cfg80211 * cfg,wl_iftype_t new_wl_iftype,bool * disable_nan,bool * disable_p2p)616*4882a593Smuzhiyun wl_cfg80211_disc_if_mgmt(struct bcm_cfg80211 *cfg,
617*4882a593Smuzhiyun 	wl_iftype_t new_wl_iftype, bool *disable_nan, bool *disable_p2p)
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun 	s32 ret = BCME_OK;
620*4882a593Smuzhiyun 	wl_iftype_t sec_wl_if_type =
621*4882a593Smuzhiyun 		wl_cfg80211_get_sec_iface(cfg);
622*4882a593Smuzhiyun 	*disable_p2p = false;
623*4882a593Smuzhiyun 	*disable_nan = false;
624*4882a593Smuzhiyun 
625*4882a593Smuzhiyun 	if (sec_wl_if_type == WL_IF_TYPE_NAN &&
626*4882a593Smuzhiyun 			new_wl_iftype == WL_IF_TYPE_NAN) {
627*4882a593Smuzhiyun 		/* Multi NDP is allowed irrespective of Policy */
628*4882a593Smuzhiyun 		return BCME_OK;
629*4882a593Smuzhiyun 	}
630*4882a593Smuzhiyun 
631*4882a593Smuzhiyun 	/*
632*4882a593Smuzhiyun 	* Check for any policy conflicts with active secondary
633*4882a593Smuzhiyun 	* interface for incoming discovery iface
634*4882a593Smuzhiyun 	*/
635*4882a593Smuzhiyun 	if ((sec_wl_if_type != WL_IFACE_NOT_PRESENT) &&
636*4882a593Smuzhiyun 		(is_discovery_iface(new_wl_iftype))) {
637*4882a593Smuzhiyun 		switch (cfg->iface_data.policy) {
638*4882a593Smuzhiyun 			case WL_IF_POLICY_CUSTOM: {
639*4882a593Smuzhiyun 				if (sec_wl_if_type == WL_IF_TYPE_NAN &&
640*4882a593Smuzhiyun 					new_wl_iftype == WL_IF_TYPE_P2P_DISC) {
641*4882a593Smuzhiyun 					WL_INFORM_MEM(("Allow P2P Discovery with active NDP\n"));
642*4882a593Smuzhiyun 					/* No further checks are required. */
643*4882a593Smuzhiyun 					return BCME_OK;
644*4882a593Smuzhiyun 				}
645*4882a593Smuzhiyun 				/*
646*4882a593Smuzhiyun 				* Intentional fall through to default policy
647*4882a593Smuzhiyun 				* as for AP and associated ifaces, both are same
648*4882a593Smuzhiyun 				*/
649*4882a593Smuzhiyun 			}
650*4882a593Smuzhiyun 			case WL_IF_POLICY_DEFAULT: {
651*4882a593Smuzhiyun 				 if (sec_wl_if_type == WL_IF_TYPE_AP) {
652*4882a593Smuzhiyun 					WL_INFORM_MEM(("AP is active, cant support new iface\n"));
653*4882a593Smuzhiyun 					ret = BCME_ERROR;
654*4882a593Smuzhiyun 				} else if (sec_wl_if_type == WL_IF_TYPE_P2P_GC ||
655*4882a593Smuzhiyun 					sec_wl_if_type == WL_IF_TYPE_P2P_GO) {
656*4882a593Smuzhiyun 					if (new_wl_iftype == WL_IF_TYPE_P2P_DISC) {
657*4882a593Smuzhiyun 						/*
658*4882a593Smuzhiyun 						* Associated discovery case,
659*4882a593Smuzhiyun 						* Fall through
660*4882a593Smuzhiyun 						*/
661*4882a593Smuzhiyun 					} else {
662*4882a593Smuzhiyun 						/* Active iface is present, returning error */
663*4882a593Smuzhiyun 						WL_INFORM_MEM(("P2P group is active,"
664*4882a593Smuzhiyun 							" cant support new iface\n"));
665*4882a593Smuzhiyun 						ret = BCME_ERROR;
666*4882a593Smuzhiyun 					}
667*4882a593Smuzhiyun 				} else if (sec_wl_if_type == WL_IF_TYPE_NAN) {
668*4882a593Smuzhiyun 					ret = wl_cfg80211_delete_iface(cfg, sec_wl_if_type);
669*4882a593Smuzhiyun 				}
670*4882a593Smuzhiyun 				break;
671*4882a593Smuzhiyun 			}
672*4882a593Smuzhiyun 			case WL_IF_POLICY_FCFS: {
673*4882a593Smuzhiyun 				WL_INFORM_MEM(("Can't support new iface = %s\n",
674*4882a593Smuzhiyun 						wl_iftype_to_str(new_wl_iftype)));
675*4882a593Smuzhiyun 				ret = BCME_ERROR;
676*4882a593Smuzhiyun 				break;
677*4882a593Smuzhiyun 			}
678*4882a593Smuzhiyun 			case WL_IF_POLICY_LP: {
679*4882a593Smuzhiyun 				/* Delete existing data iface n allow incoming sec iface */
680*4882a593Smuzhiyun 				WL_INFORM_MEM(("Remove active sec data interface = %s\n",
681*4882a593Smuzhiyun 					wl_iftype_to_str(sec_wl_if_type)));
682*4882a593Smuzhiyun 				ret = wl_cfg80211_delete_iface(cfg,
683*4882a593Smuzhiyun 						sec_wl_if_type);
684*4882a593Smuzhiyun 				break;
685*4882a593Smuzhiyun 			}
686*4882a593Smuzhiyun 			case WL_IF_POLICY_ROLE_PRIORITY: {
687*4882a593Smuzhiyun 				WL_INFORM_MEM(("Existing iface = %s (%d) and new iface = %s (%d)\n",
688*4882a593Smuzhiyun 					wl_iftype_to_str(sec_wl_if_type),
689*4882a593Smuzhiyun 					cfg->iface_data.priority[sec_wl_if_type],
690*4882a593Smuzhiyun 					wl_iftype_to_str(new_wl_iftype),
691*4882a593Smuzhiyun 					cfg->iface_data.priority[new_wl_iftype]));
692*4882a593Smuzhiyun 				if (cfg->iface_data.priority[new_wl_iftype] >
693*4882a593Smuzhiyun 					cfg->iface_data.priority[sec_wl_if_type]) {
694*4882a593Smuzhiyun 					WL_INFORM_MEM(("Remove active sec data iface\n"));
695*4882a593Smuzhiyun 					ret = wl_cfg80211_delete_iface(cfg,
696*4882a593Smuzhiyun 						sec_wl_if_type);
697*4882a593Smuzhiyun 				} else {
698*4882a593Smuzhiyun 					WL_ERR(("Can't support new iface = %s"
699*4882a593Smuzhiyun 						" due to low priority\n",
700*4882a593Smuzhiyun 						wl_iftype_to_str(new_wl_iftype)));
701*4882a593Smuzhiyun 						ret = BCME_ERROR;
702*4882a593Smuzhiyun 				}
703*4882a593Smuzhiyun 				break;
704*4882a593Smuzhiyun 			}
705*4882a593Smuzhiyun 			default: {
706*4882a593Smuzhiyun 				WL_ERR(("Unsupported policy\n"));
707*4882a593Smuzhiyun 				return BCME_ERROR;
708*4882a593Smuzhiyun 			}
709*4882a593Smuzhiyun 		}
710*4882a593Smuzhiyun 	} else {
711*4882a593Smuzhiyun 		/*
712*4882a593Smuzhiyun 		* Handle incoming new secondary iface request,
713*4882a593Smuzhiyun 		* irrespective of existing discovery ifaces
714*4882a593Smuzhiyun 		*/
715*4882a593Smuzhiyun 		if ((cfg->iface_data.policy == WL_IF_POLICY_CUSTOM) &&
716*4882a593Smuzhiyun 			(new_wl_iftype == WL_IF_TYPE_NAN)) {
717*4882a593Smuzhiyun 			WL_INFORM_MEM(("Allow NAN Data Path\n"));
718*4882a593Smuzhiyun 			/* No further checks are required. */
719*4882a593Smuzhiyun 			return BCME_OK;
720*4882a593Smuzhiyun 		}
721*4882a593Smuzhiyun 	}
722*4882a593Smuzhiyun 
723*4882a593Smuzhiyun 	/* Check for any conflicting discovery iface */
724*4882a593Smuzhiyun 	switch (new_wl_iftype) {
725*4882a593Smuzhiyun 		case WL_IF_TYPE_P2P_DISC:
726*4882a593Smuzhiyun 		case WL_IF_TYPE_P2P_GO:
727*4882a593Smuzhiyun 		case WL_IF_TYPE_P2P_GC: {
728*4882a593Smuzhiyun 			*disable_nan = true;
729*4882a593Smuzhiyun 			break;
730*4882a593Smuzhiyun 		}
731*4882a593Smuzhiyun 		case WL_IF_TYPE_NAN_NMI:
732*4882a593Smuzhiyun 		case WL_IF_TYPE_NAN: {
733*4882a593Smuzhiyun 			*disable_p2p = true;
734*4882a593Smuzhiyun 			break;
735*4882a593Smuzhiyun 		}
736*4882a593Smuzhiyun 		case WL_IF_TYPE_STA:
737*4882a593Smuzhiyun 		case WL_IF_TYPE_AP: {
738*4882a593Smuzhiyun 			*disable_nan = true;
739*4882a593Smuzhiyun 			*disable_p2p = true;
740*4882a593Smuzhiyun 			break;
741*4882a593Smuzhiyun 		}
742*4882a593Smuzhiyun 		default: {
743*4882a593Smuzhiyun 			WL_ERR(("Unsupported\n"));
744*4882a593Smuzhiyun 			return BCME_ERROR;
745*4882a593Smuzhiyun 		}
746*4882a593Smuzhiyun 	}
747*4882a593Smuzhiyun 	return ret;
748*4882a593Smuzhiyun }
749*4882a593Smuzhiyun 
750*4882a593Smuzhiyun static bool
wl_cfg80211_is_associated_discovery(struct bcm_cfg80211 * cfg,wl_iftype_t new_wl_iftype)751*4882a593Smuzhiyun wl_cfg80211_is_associated_discovery(struct bcm_cfg80211 *cfg,
752*4882a593Smuzhiyun 	wl_iftype_t new_wl_iftype)
753*4882a593Smuzhiyun {
754*4882a593Smuzhiyun 	struct net_device *p2p_ndev = NULL;
755*4882a593Smuzhiyun 	p2p_ndev = wl_to_p2p_bss_ndev(cfg, P2PAPI_BSSCFG_CONNECTION1);
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun 	if (new_wl_iftype == WL_IF_TYPE_P2P_DISC && p2p_ndev &&
758*4882a593Smuzhiyun 		p2p_ndev->ieee80211_ptr &&
759*4882a593Smuzhiyun 		is_p2p_group_iface(p2p_ndev->ieee80211_ptr)) {
760*4882a593Smuzhiyun 			return true;
761*4882a593Smuzhiyun 	}
762*4882a593Smuzhiyun #ifdef WL_NAN
763*4882a593Smuzhiyun 	else if ((new_wl_iftype == WL_IF_TYPE_NAN_NMI) &&
764*4882a593Smuzhiyun 		(wl_cfgnan_is_dp_active(bcmcfg_to_prmry_ndev(cfg)))) {
765*4882a593Smuzhiyun 			return true;
766*4882a593Smuzhiyun 		}
767*4882a593Smuzhiyun #endif /* WL_NAN */
768*4882a593Smuzhiyun 	return false;
769*4882a593Smuzhiyun }
770*4882a593Smuzhiyun 
771*4882a593Smuzhiyun /* Handle incoming discovery iface request */
772*4882a593Smuzhiyun static s32
wl_cfg80211_handle_discovery_config(struct bcm_cfg80211 * cfg,wl_iftype_t new_wl_iftype)773*4882a593Smuzhiyun wl_cfg80211_handle_discovery_config(struct bcm_cfg80211 *cfg,
774*4882a593Smuzhiyun 	wl_iftype_t new_wl_iftype)
775*4882a593Smuzhiyun {
776*4882a593Smuzhiyun 	s32 ret = BCME_OK;
777*4882a593Smuzhiyun 	bool disable_p2p = false;
778*4882a593Smuzhiyun 	bool disable_nan = false;
779*4882a593Smuzhiyun 
780*4882a593Smuzhiyun 	wl_iftype_t active_sec_iface =
781*4882a593Smuzhiyun 		wl_cfg80211_get_sec_iface(cfg);
782*4882a593Smuzhiyun 
783*4882a593Smuzhiyun 	if (is_discovery_iface(new_wl_iftype) &&
784*4882a593Smuzhiyun 		(active_sec_iface != WL_IFACE_NOT_PRESENT)) {
785*4882a593Smuzhiyun 		if (wl_cfg80211_is_associated_discovery(cfg,
786*4882a593Smuzhiyun 			new_wl_iftype) == TRUE) {
787*4882a593Smuzhiyun 			WL_DBG(("Associate iface request is allowed= %s\n",
788*4882a593Smuzhiyun 				wl_iftype_to_str(new_wl_iftype)));
789*4882a593Smuzhiyun 			return ret;
790*4882a593Smuzhiyun 		}
791*4882a593Smuzhiyun 	}
792*4882a593Smuzhiyun 
793*4882a593Smuzhiyun 	ret = wl_cfg80211_disc_if_mgmt(cfg, new_wl_iftype,
794*4882a593Smuzhiyun 			&disable_nan, &disable_p2p);
795*4882a593Smuzhiyun 	if (ret != BCME_OK) {
796*4882a593Smuzhiyun 		WL_ERR(("Failed at disc iface mgmt, ret = %d\n", ret));
797*4882a593Smuzhiyun 		return ret;
798*4882a593Smuzhiyun 	}
799*4882a593Smuzhiyun #ifdef WL_NANP2P
800*4882a593Smuzhiyun 	if (((new_wl_iftype == WL_IF_TYPE_P2P_DISC) && disable_nan) ||
801*4882a593Smuzhiyun 		((new_wl_iftype == WL_IF_TYPE_NAN_NMI) && disable_p2p)) {
802*4882a593Smuzhiyun 		if ((cfg->nan_p2p_supported == TRUE) &&
803*4882a593Smuzhiyun 		(cfg->conc_disc == WL_NANP2P_CONC_SUPPORT)) {
804*4882a593Smuzhiyun 			WL_INFORM_MEM(("P2P + NAN conc is supported\n"));
805*4882a593Smuzhiyun 			disable_p2p = false;
806*4882a593Smuzhiyun 			disable_nan = false;
807*4882a593Smuzhiyun 		}
808*4882a593Smuzhiyun 	}
809*4882a593Smuzhiyun #endif /* WL_NANP2P */
810*4882a593Smuzhiyun 
811*4882a593Smuzhiyun 	if (disable_nan) {
812*4882a593Smuzhiyun #ifdef WL_NAN
813*4882a593Smuzhiyun 		/* Disable nan to avoid conflict with p2p */
814*4882a593Smuzhiyun 		ret = wl_cfgnan_check_nan_disable_pending(cfg, true, true);
815*4882a593Smuzhiyun 		if (ret != BCME_OK) {
816*4882a593Smuzhiyun 			WL_ERR(("failed to disable nan, error[%d]\n", ret));
817*4882a593Smuzhiyun 			return ret;
818*4882a593Smuzhiyun 		}
819*4882a593Smuzhiyun #endif /* WL_NAN */
820*4882a593Smuzhiyun 	}
821*4882a593Smuzhiyun 
822*4882a593Smuzhiyun 	if (disable_p2p) {
823*4882a593Smuzhiyun 		/* Disable p2p discovery */
824*4882a593Smuzhiyun 		ret = wl_cfg80211_deinit_p2p_discovery(cfg);
825*4882a593Smuzhiyun 		if (ret != BCME_OK) {
826*4882a593Smuzhiyun 			/* Should we fail nan enab here */
827*4882a593Smuzhiyun 			WL_ERR(("Failed to disable p2p_disc for allowing nan\n"));
828*4882a593Smuzhiyun 			return ret;
829*4882a593Smuzhiyun 		}
830*4882a593Smuzhiyun 	}
831*4882a593Smuzhiyun 	return ret;
832*4882a593Smuzhiyun }
833*4882a593Smuzhiyun 
834*4882a593Smuzhiyun /*
835*4882a593Smuzhiyun * Check for any conflicting iface before adding iface.
836*4882a593Smuzhiyun * Based on policy, either conflicting iface is removed
837*4882a593Smuzhiyun * or new iface add request is blocked.
838*4882a593Smuzhiyun */
839*4882a593Smuzhiyun s32
wl_cfg80211_handle_if_role_conflict(struct bcm_cfg80211 * cfg,wl_iftype_t new_wl_iftype)840*4882a593Smuzhiyun wl_cfg80211_handle_if_role_conflict(struct bcm_cfg80211 *cfg,
841*4882a593Smuzhiyun 	wl_iftype_t new_wl_iftype)
842*4882a593Smuzhiyun {
843*4882a593Smuzhiyun 	s32 ret = BCME_OK;
844*4882a593Smuzhiyun #ifdef P2P_AP_CONCURRENT
845*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
846*4882a593Smuzhiyun #endif
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun 	WL_INFORM_MEM(("Incoming iface = %s\n", wl_iftype_to_str(new_wl_iftype)));
849*4882a593Smuzhiyun 
850*4882a593Smuzhiyun #ifdef P2P_AP_CONCURRENT
851*4882a593Smuzhiyun 	if (dhd->conf->war & P2P_AP_MAC_CONFLICT) {
852*4882a593Smuzhiyun 		return ret;
853*4882a593Smuzhiyun 	} else
854*4882a593Smuzhiyun #endif
855*4882a593Smuzhiyun #ifdef WL_STATIC_IF
856*4882a593Smuzhiyun 	if (wl_cfg80211_get_sec_iface(cfg) == WL_IF_TYPE_AP &&
857*4882a593Smuzhiyun 			new_wl_iftype == WL_IF_TYPE_AP) {
858*4882a593Smuzhiyun 	} else
859*4882a593Smuzhiyun #endif /* WL_STATIC_IF */
860*4882a593Smuzhiyun 	if (!is_discovery_iface(new_wl_iftype)) {
861*4882a593Smuzhiyun 		/* Incoming data interface request */
862*4882a593Smuzhiyun 		if (wl_cfg80211_get_sec_iface(cfg) != WL_IFACE_NOT_PRESENT) {
863*4882a593Smuzhiyun 			/* active interface present - Apply interface data policy */
864*4882a593Smuzhiyun 			ret = wl_cfg80211_data_if_mgmt(cfg, new_wl_iftype);
865*4882a593Smuzhiyun 			if (ret != BCME_OK) {
866*4882a593Smuzhiyun 				WL_ERR(("if_mgmt fail:%d\n", ret));
867*4882a593Smuzhiyun 				return ret;
868*4882a593Smuzhiyun 			}
869*4882a593Smuzhiyun 		}
870*4882a593Smuzhiyun 	}
871*4882a593Smuzhiyun 	/* Apply discovery config */
872*4882a593Smuzhiyun 	ret = wl_cfg80211_handle_discovery_config(cfg, new_wl_iftype);
873*4882a593Smuzhiyun 	return ret;
874*4882a593Smuzhiyun }
875*4882a593Smuzhiyun #endif /* WL_IFACE_MGMT */
876*4882a593Smuzhiyun 
877*4882a593Smuzhiyun s32
wl_release_vif_macaddr(struct bcm_cfg80211 * cfg,u8 * mac_addr,u16 wl_iftype)878*4882a593Smuzhiyun wl_release_vif_macaddr(struct bcm_cfg80211 *cfg, u8 *mac_addr, u16 wl_iftype)
879*4882a593Smuzhiyun {
880*4882a593Smuzhiyun 	struct net_device *ndev =  bcmcfg_to_prmry_ndev(cfg);
881*4882a593Smuzhiyun 	u16 org_toggle_bytes;
882*4882a593Smuzhiyun 	u16 cur_toggle_bytes;
883*4882a593Smuzhiyun 	u16 toggled_bit;
884*4882a593Smuzhiyun 
885*4882a593Smuzhiyun 	if (!ndev || !mac_addr || ETHER_ISNULLADDR(mac_addr)) {
886*4882a593Smuzhiyun 		return -EINVAL;
887*4882a593Smuzhiyun 	}
888*4882a593Smuzhiyun 	WL_DBG(("%s:Mac addr" MACDBG "\n",
889*4882a593Smuzhiyun 			__FUNCTION__, MAC2STRDBG(mac_addr)));
890*4882a593Smuzhiyun 
891*4882a593Smuzhiyun #if defined(SPECIFIC_MAC_GEN_SCHEME)
892*4882a593Smuzhiyun 	if ((wl_iftype == WL_IF_TYPE_P2P_DISC) || (wl_iftype == WL_IF_TYPE_AP) ||
893*4882a593Smuzhiyun 		(wl_iftype == WL_IF_TYPE_P2P_GO) || (wl_iftype == WL_IF_TYPE_P2P_GC)) {
894*4882a593Smuzhiyun 		/* Avoid invoking release mac addr code for interfaces using
895*4882a593Smuzhiyun 		 * fixed mac addr.
896*4882a593Smuzhiyun 		 */
897*4882a593Smuzhiyun 		return BCME_OK;
898*4882a593Smuzhiyun 	}
899*4882a593Smuzhiyun #else
900*4882a593Smuzhiyun 	if (wl_iftype == WL_IF_TYPE_P2P_DISC) {
901*4882a593Smuzhiyun 		return BCME_OK;
902*4882a593Smuzhiyun 	}
903*4882a593Smuzhiyun #endif /* SPECIFIC_MAC_GEN_SCHEME */
904*4882a593Smuzhiyun 
905*4882a593Smuzhiyun 	/* Fetch last two bytes of mac address */
906*4882a593Smuzhiyun 	org_toggle_bytes = ntoh16(*((u16 *)&ndev->dev_addr[4]));
907*4882a593Smuzhiyun 	cur_toggle_bytes = ntoh16(*((u16 *)&mac_addr[4]));
908*4882a593Smuzhiyun 
909*4882a593Smuzhiyun 	toggled_bit = (org_toggle_bytes ^ cur_toggle_bytes);
910*4882a593Smuzhiyun 	WL_DBG(("org_toggle_bytes:%04X cur_toggle_bytes:%04X\n",
911*4882a593Smuzhiyun 		org_toggle_bytes, cur_toggle_bytes));
912*4882a593Smuzhiyun 	if (toggled_bit & cfg->vif_macaddr_mask) {
913*4882a593Smuzhiyun 		/* This toggled_bit is marked in the used mac addr
914*4882a593Smuzhiyun 		 * mask. Clear it.
915*4882a593Smuzhiyun 		 */
916*4882a593Smuzhiyun 		cfg->vif_macaddr_mask &= ~toggled_bit;
917*4882a593Smuzhiyun 		WL_INFORM(("MAC address - " MACDBG " released. toggled_bit:%04X vif_mask:%04X\n",
918*4882a593Smuzhiyun 			MAC2STRDBG(mac_addr), toggled_bit, cfg->vif_macaddr_mask));
919*4882a593Smuzhiyun 	} else {
920*4882a593Smuzhiyun 		WL_ERR(("MAC address - " MACDBG " not found in the used list."
921*4882a593Smuzhiyun 			" toggled_bit:%04x vif_mask:%04x\n", MAC2STRDBG(mac_addr),
922*4882a593Smuzhiyun 			toggled_bit, cfg->vif_macaddr_mask));
923*4882a593Smuzhiyun 		return -EINVAL;
924*4882a593Smuzhiyun 	}
925*4882a593Smuzhiyun 
926*4882a593Smuzhiyun 	return BCME_OK;
927*4882a593Smuzhiyun }
928*4882a593Smuzhiyun 
929*4882a593Smuzhiyun s32
wl_get_vif_macaddr(struct bcm_cfg80211 * cfg,u16 wl_iftype,u8 * mac_addr)930*4882a593Smuzhiyun wl_get_vif_macaddr(struct bcm_cfg80211 *cfg, u16 wl_iftype, u8 *mac_addr)
931*4882a593Smuzhiyun {
932*4882a593Smuzhiyun 	struct ether_addr *p2p_dev_addr = wl_to_p2p_bss_macaddr(cfg, P2PAPI_BSSCFG_DEVICE);
933*4882a593Smuzhiyun 	struct net_device *ndev =  bcmcfg_to_prmry_ndev(cfg);
934*4882a593Smuzhiyun 	u16 toggle_mask;
935*4882a593Smuzhiyun 	u16 toggle_bit;
936*4882a593Smuzhiyun 	u16 toggle_bytes;
937*4882a593Smuzhiyun 	u16 used;
938*4882a593Smuzhiyun 	u32 offset = 0;
939*4882a593Smuzhiyun 	/* Toggle mask starts from MSB of second last byte */
940*4882a593Smuzhiyun 	u16 mask = 0x8000;
941*4882a593Smuzhiyun 	if (!mac_addr) {
942*4882a593Smuzhiyun 		return -EINVAL;
943*4882a593Smuzhiyun 	}
944*4882a593Smuzhiyun 	if ((wl_iftype == WL_IF_TYPE_P2P_DISC) && p2p_dev_addr &&
945*4882a593Smuzhiyun 		ETHER_IS_LOCALADDR(p2p_dev_addr)) {
946*4882a593Smuzhiyun 		/* If mac address is already generated return the mac */
947*4882a593Smuzhiyun 		(void)memcpy_s(mac_addr, ETH_ALEN, p2p_dev_addr->octet, ETH_ALEN);
948*4882a593Smuzhiyun 		return 0;
949*4882a593Smuzhiyun 	}
950*4882a593Smuzhiyun 	(void)memcpy_s(mac_addr, ETH_ALEN, ndev->perm_addr, ETH_ALEN);
951*4882a593Smuzhiyun /*
952*4882a593Smuzhiyun  * VIF MAC address managment
953*4882a593Smuzhiyun  * P2P Device addres: Primary MAC with locally admin. bit set
954*4882a593Smuzhiyun  * P2P Group address/NAN NMI/Softap/NAN DPI: Primary MAC addr
955*4882a593Smuzhiyun  *    with local admin bit set and one additional bit toggled.
956*4882a593Smuzhiyun  * cfg->vif_macaddr_mask will hold the info regarding the mac address
957*4882a593Smuzhiyun  * released. Ensure to call wl_release_vif_macaddress to free up
958*4882a593Smuzhiyun  * the mac address.
959*4882a593Smuzhiyun  */
960*4882a593Smuzhiyun #if defined(SPECIFIC_MAC_GEN_SCHEME)
961*4882a593Smuzhiyun 	if (wl_iftype == WL_IF_TYPE_P2P_DISC ||	wl_iftype == WL_IF_TYPE_AP) {
962*4882a593Smuzhiyun 		mac_addr[0] |= 0x02;
963*4882a593Smuzhiyun 	} else if ((wl_iftype == WL_IF_TYPE_P2P_GO) || (wl_iftype == WL_IF_TYPE_P2P_GC)) {
964*4882a593Smuzhiyun 		mac_addr[0] |= 0x02;
965*4882a593Smuzhiyun 		mac_addr[4] ^= 0x80;
966*4882a593Smuzhiyun 	}
967*4882a593Smuzhiyun #else
968*4882a593Smuzhiyun 	if (wl_iftype == WL_IF_TYPE_P2P_DISC) {
969*4882a593Smuzhiyun 		mac_addr[0] |= 0x02;
970*4882a593Smuzhiyun 	}
971*4882a593Smuzhiyun #endif /* SPECIFIC_MAC_GEN_SCHEME */
972*4882a593Smuzhiyun 	else {
973*4882a593Smuzhiyun 		/* For locally administered mac addresses, we keep the
974*4882a593Smuzhiyun 		 * OUI part constant and just work on the last two bytes.
975*4882a593Smuzhiyun 		 */
976*4882a593Smuzhiyun 		mac_addr[0] |= 0x02;
977*4882a593Smuzhiyun 		toggle_mask = cfg->vif_macaddr_mask;
978*4882a593Smuzhiyun 		toggle_bytes = ntoh16(*((u16 *)&mac_addr[4]));
979*4882a593Smuzhiyun 		do {
980*4882a593Smuzhiyun 			used = toggle_mask & mask;
981*4882a593Smuzhiyun 			if (!used) {
982*4882a593Smuzhiyun 				/* Use this bit position */
983*4882a593Smuzhiyun 				toggle_bit = mask >> offset;
984*4882a593Smuzhiyun 				toggle_bytes ^= toggle_bit;
985*4882a593Smuzhiyun 				cfg->vif_macaddr_mask |= toggle_bit;
986*4882a593Smuzhiyun 				WL_DBG(("toggle_bit:%04X toggle_bytes:%04X toggle_mask:%04X\n",
987*4882a593Smuzhiyun 					toggle_bit, toggle_bytes, cfg->vif_macaddr_mask));
988*4882a593Smuzhiyun 				/* Macaddress are stored in network order */
989*4882a593Smuzhiyun 				mac_addr[5] = *((u8 *)&toggle_bytes);
990*4882a593Smuzhiyun 				mac_addr[4] = *(((u8 *)&toggle_bytes + 1));
991*4882a593Smuzhiyun 				break;
992*4882a593Smuzhiyun 			}
993*4882a593Smuzhiyun 
994*4882a593Smuzhiyun 			/* Shift by one */
995*4882a593Smuzhiyun 			toggle_mask = toggle_mask << 0x1;
996*4882a593Smuzhiyun 			offset++;
997*4882a593Smuzhiyun 			if (offset > MAX_VIF_OFFSET) {
998*4882a593Smuzhiyun 				/* We have used up all macaddresses. Something wrong! */
999*4882a593Smuzhiyun 				WL_ERR(("Entire range of macaddress used up.\n"));
1000*4882a593Smuzhiyun 				ASSERT(0);
1001*4882a593Smuzhiyun 				break;
1002*4882a593Smuzhiyun 			}
1003*4882a593Smuzhiyun 		} while (true);
1004*4882a593Smuzhiyun 	}
1005*4882a593Smuzhiyun 	WL_INFORM_MEM(("Get virtual I/F mac addr: "MACDBG"\n", MAC2STRDBG(mac_addr)));
1006*4882a593Smuzhiyun 	return 0;
1007*4882a593Smuzhiyun }
1008*4882a593Smuzhiyun 
1009*4882a593Smuzhiyun bcm_struct_cfgdev *
wl_cfg80211_add_virtual_iface(struct wiphy * wiphy,const char * name,unsigned char name_assign_type,enum nl80211_iftype type,u32 * flags,struct vif_params * params)1010*4882a593Smuzhiyun wl_cfg80211_add_virtual_iface(struct wiphy *wiphy,
1011*4882a593Smuzhiyun #if defined(WL_CFG80211_P2P_DEV_IF)
1012*4882a593Smuzhiyun 	const char *name,
1013*4882a593Smuzhiyun #else
1014*4882a593Smuzhiyun 	char *name,
1015*4882a593Smuzhiyun #endif /* WL_CFG80211_P2P_DEV_IF */
1016*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0))
1017*4882a593Smuzhiyun 	unsigned char name_assign_type,
1018*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 1, 0)) */
1019*4882a593Smuzhiyun 	enum nl80211_iftype type,
1020*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
1021*4882a593Smuzhiyun 	u32 *flags,
1022*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) */
1023*4882a593Smuzhiyun 	struct vif_params *params)
1024*4882a593Smuzhiyun {
1025*4882a593Smuzhiyun 	u16 wl_iftype;
1026*4882a593Smuzhiyun 	u16 wl_mode;
1027*4882a593Smuzhiyun 	struct net_device *primary_ndev;
1028*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1029*4882a593Smuzhiyun 	struct wireless_dev *wdev;
1030*4882a593Smuzhiyun 
1031*4882a593Smuzhiyun 	WL_DBG(("Enter iftype: %d\n", type));
1032*4882a593Smuzhiyun 	if (!cfg) {
1033*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
1034*4882a593Smuzhiyun 	}
1035*4882a593Smuzhiyun 
1036*4882a593Smuzhiyun 	/* Use primary I/F for sending cmds down to firmware */
1037*4882a593Smuzhiyun 	primary_ndev = bcmcfg_to_prmry_ndev(cfg);
1038*4882a593Smuzhiyun 	if (unlikely(!wl_get_drv_status(cfg, READY, primary_ndev))) {
1039*4882a593Smuzhiyun 		WL_ERR(("device is not ready\n"));
1040*4882a593Smuzhiyun 		return ERR_PTR(-ENODEV);
1041*4882a593Smuzhiyun 	}
1042*4882a593Smuzhiyun 
1043*4882a593Smuzhiyun 	if (!name) {
1044*4882a593Smuzhiyun 		WL_ERR(("Interface name not provided \n"));
1045*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
1046*4882a593Smuzhiyun 	}
1047*4882a593Smuzhiyun 
1048*4882a593Smuzhiyun 	if (cfg80211_to_wl_iftype(type, &wl_iftype, &wl_mode) < 0) {
1049*4882a593Smuzhiyun 		return ERR_PTR(-EINVAL);
1050*4882a593Smuzhiyun 	}
1051*4882a593Smuzhiyun 
1052*4882a593Smuzhiyun 	wdev = wl_cfg80211_add_if(cfg, primary_ndev, wl_iftype, name, NULL);
1053*4882a593Smuzhiyun 	if (unlikely(!wdev)) {
1054*4882a593Smuzhiyun 		return ERR_PTR(-ENODEV);
1055*4882a593Smuzhiyun 	}
1056*4882a593Smuzhiyun 	return wdev_to_cfgdev(wdev);
1057*4882a593Smuzhiyun }
1058*4882a593Smuzhiyun 
1059*4882a593Smuzhiyun s32
wl_cfg80211_del_virtual_iface(struct wiphy * wiphy,bcm_struct_cfgdev * cfgdev)1060*4882a593Smuzhiyun wl_cfg80211_del_virtual_iface(struct wiphy *wiphy, bcm_struct_cfgdev *cfgdev)
1061*4882a593Smuzhiyun {
1062*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1063*4882a593Smuzhiyun 	struct wireless_dev *wdev = cfgdev_to_wdev(cfgdev);
1064*4882a593Smuzhiyun 	int ret = BCME_OK;
1065*4882a593Smuzhiyun 	u16 wl_iftype;
1066*4882a593Smuzhiyun 	u16 wl_mode;
1067*4882a593Smuzhiyun 	struct net_device *primary_ndev;
1068*4882a593Smuzhiyun 
1069*4882a593Smuzhiyun 	if (!cfg) {
1070*4882a593Smuzhiyun 		return -EINVAL;
1071*4882a593Smuzhiyun 	}
1072*4882a593Smuzhiyun 
1073*4882a593Smuzhiyun 	primary_ndev = bcmcfg_to_prmry_ndev(cfg);
1074*4882a593Smuzhiyun 	wdev = cfgdev_to_wdev(cfgdev);
1075*4882a593Smuzhiyun 	if (!wdev) {
1076*4882a593Smuzhiyun 		WL_ERR(("wdev null"));
1077*4882a593Smuzhiyun 		return -ENODEV;
1078*4882a593Smuzhiyun 	}
1079*4882a593Smuzhiyun 
1080*4882a593Smuzhiyun 	WL_DBG(("Enter  wdev:%p iftype: %d\n", wdev, wdev->iftype));
1081*4882a593Smuzhiyun 	if (cfg80211_to_wl_iftype(wdev->iftype, &wl_iftype, &wl_mode) < 0) {
1082*4882a593Smuzhiyun 		WL_ERR(("Wrong iftype: %d\n", wdev->iftype));
1083*4882a593Smuzhiyun 		return -ENODEV;
1084*4882a593Smuzhiyun 	}
1085*4882a593Smuzhiyun 
1086*4882a593Smuzhiyun 	if ((ret = wl_cfg80211_del_if(cfg, primary_ndev,
1087*4882a593Smuzhiyun 			wdev, NULL)) < 0) {
1088*4882a593Smuzhiyun 		WL_ERR(("IF del failed\n"));
1089*4882a593Smuzhiyun 	}
1090*4882a593Smuzhiyun 
1091*4882a593Smuzhiyun 	return ret;
1092*4882a593Smuzhiyun }
1093*4882a593Smuzhiyun 
1094*4882a593Smuzhiyun static s32
wl_cfg80211_change_p2prole(struct wiphy * wiphy,struct net_device * ndev,enum nl80211_iftype type)1095*4882a593Smuzhiyun wl_cfg80211_change_p2prole(struct wiphy *wiphy, struct net_device *ndev, enum nl80211_iftype type)
1096*4882a593Smuzhiyun {
1097*4882a593Smuzhiyun 	s32 wlif_type;
1098*4882a593Smuzhiyun 	s32 mode = 0;
1099*4882a593Smuzhiyun 	s32 index;
1100*4882a593Smuzhiyun 	s32 err;
1101*4882a593Smuzhiyun 	s32 conn_idx = -1;
1102*4882a593Smuzhiyun 	chanspec_t chspec;
1103*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1104*4882a593Smuzhiyun 	struct ether_addr p2p_dev_addr = {{0, 0, 0, 0, 0, 0}};
1105*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
1106*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
1107*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
1108*4882a593Smuzhiyun 
1109*4882a593Smuzhiyun 	WL_INFORM_MEM(("Enter. current_role:%d new_role:%d \n", ndev->ieee80211_ptr->iftype, type));
1110*4882a593Smuzhiyun 
1111*4882a593Smuzhiyun 	(void)memcpy_s(p2p_dev_addr.octet, ETHER_ADDR_LEN,
1112*4882a593Smuzhiyun 		ndev->dev_addr, ETHER_ADDR_LEN);
1113*4882a593Smuzhiyun 
1114*4882a593Smuzhiyun 	if (!cfg->p2p || !wl_cfgp2p_vif_created(cfg)) {
1115*4882a593Smuzhiyun 		WL_ERR(("P2P not initialized \n"));
1116*4882a593Smuzhiyun 		return -EINVAL;
1117*4882a593Smuzhiyun 	}
1118*4882a593Smuzhiyun 
1119*4882a593Smuzhiyun 	if (!is_p2p_group_iface(ndev->ieee80211_ptr)) {
1120*4882a593Smuzhiyun 		WL_ERR(("Wrong if type \n"));
1121*4882a593Smuzhiyun 		return -EINVAL;
1122*4882a593Smuzhiyun 	}
1123*4882a593Smuzhiyun 
1124*4882a593Smuzhiyun 	/* Abort any on-going scans to avoid race condition issues */
1125*4882a593Smuzhiyun 	wl_cfgscan_cancel_scan(cfg);
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun 	index = wl_get_bssidx_by_wdev(cfg, ndev->ieee80211_ptr);
1128*4882a593Smuzhiyun 	if (index < 0) {
1129*4882a593Smuzhiyun 		WL_ERR(("Find bsscfg index from ndev(%p) failed\n", ndev));
1130*4882a593Smuzhiyun 		return BCME_ERROR;
1131*4882a593Smuzhiyun 	}
1132*4882a593Smuzhiyun 	if (wl_cfgp2p_find_type(cfg, index, &conn_idx) != BCME_OK) {
1133*4882a593Smuzhiyun 		return BCME_ERROR;
1134*4882a593Smuzhiyun 	}
1135*4882a593Smuzhiyun 
1136*4882a593Smuzhiyun 	/* In concurrency case, STA may be already associated in a particular
1137*4882a593Smuzhiyun 	 * channel. so retrieve the current channel of primary interface and
1138*4882a593Smuzhiyun 	 * then start the virtual interface on that.
1139*4882a593Smuzhiyun 	 */
1140*4882a593Smuzhiyun 	chspec = wl_cfg80211_get_shared_freq(wiphy);
1141*4882a593Smuzhiyun 	if (type == NL80211_IFTYPE_P2P_GO) {
1142*4882a593Smuzhiyun 		/* Dual p2p doesn't support multiple P2PGO interfaces,
1143*4882a593Smuzhiyun 		 * p2p_go_count is the counter for GO creation
1144*4882a593Smuzhiyun 		 * requests.
1145*4882a593Smuzhiyun 		 */
1146*4882a593Smuzhiyun 		if ((cfg->p2p->p2p_go_count > 0) && (type == NL80211_IFTYPE_P2P_GO)) {
1147*4882a593Smuzhiyun 			WL_ERR(("FW does not support multiple GO\n"));
1148*4882a593Smuzhiyun 			return BCME_ERROR;
1149*4882a593Smuzhiyun 		}
1150*4882a593Smuzhiyun 		mode = WL_MODE_AP;
1151*4882a593Smuzhiyun 		wlif_type = WL_P2P_IF_GO;
1152*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
1153*4882a593Smuzhiyun 		dhd->op_mode &= ~DHD_FLAG_P2P_GC_MODE;
1154*4882a593Smuzhiyun 		dhd->op_mode |= DHD_FLAG_P2P_GO_MODE;
1155*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
1156*4882a593Smuzhiyun 	} else {
1157*4882a593Smuzhiyun 		wlif_type = WL_P2P_IF_CLIENT;
1158*4882a593Smuzhiyun 		/* for GO */
1159*4882a593Smuzhiyun 		if (wl_get_mode_by_netdev(cfg, ndev) == WL_MODE_AP) {
1160*4882a593Smuzhiyun 			WL_INFORM_MEM(("Downgrading P2P GO to cfg_iftype:%d \n", type));
1161*4882a593Smuzhiyun 			wl_add_remove_eventmsg(ndev, WLC_E_PROBREQ_MSG, false);
1162*4882a593Smuzhiyun 			cfg->p2p->p2p_go_count--;
1163*4882a593Smuzhiyun 			/* disable interface before bsscfg free */
1164*4882a593Smuzhiyun 			err = wl_cfgp2p_ifdisable(cfg, &p2p_dev_addr);
1165*4882a593Smuzhiyun 			/* if fw doesn't support "ifdis",
1166*4882a593Smuzhiyun 			 * do not wait for link down of ap mode
1167*4882a593Smuzhiyun 			 */
1168*4882a593Smuzhiyun 			if (err == 0) {
1169*4882a593Smuzhiyun 				WL_DBG(("Wait for Link Down event for GO !!!\n"));
1170*4882a593Smuzhiyun 				wait_for_completion_timeout(&cfg->iface_disable,
1171*4882a593Smuzhiyun 					msecs_to_jiffies(500));
1172*4882a593Smuzhiyun 			} else if (err != BCME_UNSUPPORTED) {
1173*4882a593Smuzhiyun 				msleep(300);
1174*4882a593Smuzhiyun 			}
1175*4882a593Smuzhiyun 		}
1176*4882a593Smuzhiyun 	}
1177*4882a593Smuzhiyun 
1178*4882a593Smuzhiyun 	wl_set_p2p_status(cfg, IF_CHANGING);
1179*4882a593Smuzhiyun 	wl_clr_p2p_status(cfg, IF_CHANGED);
1180*4882a593Smuzhiyun 	wl_cfgp2p_ifchange(cfg, &p2p_dev_addr,
1181*4882a593Smuzhiyun 		htod32(wlif_type), chspec, conn_idx);
1182*4882a593Smuzhiyun 	wait_event_interruptible_timeout(cfg->netif_change_event,
1183*4882a593Smuzhiyun 		(wl_get_p2p_status(cfg, IF_CHANGED) == true),
1184*4882a593Smuzhiyun 		msecs_to_jiffies(MAX_WAIT_TIME));
1185*4882a593Smuzhiyun 
1186*4882a593Smuzhiyun 	wl_clr_p2p_status(cfg, IF_CHANGING);
1187*4882a593Smuzhiyun 	wl_clr_p2p_status(cfg, IF_CHANGED);
1188*4882a593Smuzhiyun 
1189*4882a593Smuzhiyun 	if (mode == WL_MODE_AP) {
1190*4882a593Smuzhiyun 		wl_set_drv_status(cfg, CONNECTED, ndev);
1191*4882a593Smuzhiyun #ifdef SUPPORT_AP_POWERSAVE
1192*4882a593Smuzhiyun 			dhd_set_ap_powersave(dhd, 0, TRUE);
1193*4882a593Smuzhiyun #endif /* SUPPORT_AP_POWERSAVE */
1194*4882a593Smuzhiyun 	}
1195*4882a593Smuzhiyun 
1196*4882a593Smuzhiyun 	return BCME_OK;
1197*4882a593Smuzhiyun }
1198*4882a593Smuzhiyun 
1199*4882a593Smuzhiyun s32
wl_cfg80211_change_virtual_iface(struct wiphy * wiphy,struct net_device * ndev,enum nl80211_iftype type,u32 * flags,struct vif_params * params)1200*4882a593Smuzhiyun wl_cfg80211_change_virtual_iface(struct wiphy *wiphy, struct net_device *ndev,
1201*4882a593Smuzhiyun 	enum nl80211_iftype type,
1202*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0))
1203*4882a593Smuzhiyun 	u32 *flags,
1204*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(4, 12, 0) */
1205*4882a593Smuzhiyun 	struct vif_params *params)
1206*4882a593Smuzhiyun {
1207*4882a593Smuzhiyun 	s32 infra = 1;
1208*4882a593Smuzhiyun 	s32 err = BCME_OK;
1209*4882a593Smuzhiyun 	u16 wl_iftype;
1210*4882a593Smuzhiyun 	u16 wl_mode;
1211*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1212*4882a593Smuzhiyun 	struct net_info *netinfo = NULL;
1213*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
1214*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
1215*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
1216*4882a593Smuzhiyun 	struct net_device *primary_ndev;
1217*4882a593Smuzhiyun 
1218*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
1219*4882a593Smuzhiyun 	if (!dhd)
1220*4882a593Smuzhiyun 		return -EINVAL;
1221*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
1222*4882a593Smuzhiyun 
1223*4882a593Smuzhiyun 	WL_INFORM_MEM(("[%s] Enter. current cfg_iftype:%d new cfg_iftype:%d \n",
1224*4882a593Smuzhiyun 		ndev->name, ndev->ieee80211_ptr->iftype, type));
1225*4882a593Smuzhiyun 	primary_ndev = bcmcfg_to_prmry_ndev(cfg);
1226*4882a593Smuzhiyun 
1227*4882a593Smuzhiyun 	if (cfg80211_to_wl_iftype(type, &wl_iftype, &wl_mode) < 0) {
1228*4882a593Smuzhiyun 		WL_ERR(("Unknown role \n"));
1229*4882a593Smuzhiyun 		return -EINVAL;
1230*4882a593Smuzhiyun 	}
1231*4882a593Smuzhiyun 
1232*4882a593Smuzhiyun 	mutex_lock(&cfg->if_sync);
1233*4882a593Smuzhiyun 	netinfo = wl_get_netinfo_by_wdev(cfg, ndev->ieee80211_ptr);
1234*4882a593Smuzhiyun 	if (unlikely(!netinfo)) {
1235*4882a593Smuzhiyun #ifdef WL_STATIC_IF
1236*4882a593Smuzhiyun 		if (wl_cfg80211_static_if(cfg, ndev)) {
1237*4882a593Smuzhiyun 			/* Incase of static interfaces, the netinfo will be
1238*4882a593Smuzhiyun 			 * allocated only when FW interface is initialized. So
1239*4882a593Smuzhiyun 			 * store the value and use it during initialization.
1240*4882a593Smuzhiyun 			 */
1241*4882a593Smuzhiyun 			WL_INFORM_MEM(("skip change vif for static if\n"));
1242*4882a593Smuzhiyun 			ndev->ieee80211_ptr->iftype = type;
1243*4882a593Smuzhiyun 			err = BCME_OK;
1244*4882a593Smuzhiyun 		} else
1245*4882a593Smuzhiyun #endif /* WL_STATIC_IF */
1246*4882a593Smuzhiyun 		{
1247*4882a593Smuzhiyun 			WL_ERR(("netinfo not found \n"));
1248*4882a593Smuzhiyun 			err = -ENODEV;
1249*4882a593Smuzhiyun 		}
1250*4882a593Smuzhiyun 		goto fail;
1251*4882a593Smuzhiyun 	}
1252*4882a593Smuzhiyun 
1253*4882a593Smuzhiyun 	if ((primary_ndev == ndev) && !(ndev->flags & IFF_UP)) {
1254*4882a593Smuzhiyun 		/*
1255*4882a593Smuzhiyun 		* If interface is not initialized, store the role and
1256*4882a593Smuzhiyun 		* return. The role will be initilized after interface
1257*4882a593Smuzhiyun 		* up
1258*4882a593Smuzhiyun 		*/
1259*4882a593Smuzhiyun 		WL_INFORM_MEM(("skip change role before dev up\n"));
1260*4882a593Smuzhiyun 		ndev->ieee80211_ptr->iftype = type;
1261*4882a593Smuzhiyun 		err = BCME_OK;
1262*4882a593Smuzhiyun 		goto fail;
1263*4882a593Smuzhiyun 	}
1264*4882a593Smuzhiyun 
1265*4882a593Smuzhiyun 	/* perform pre-if-change tasks */
1266*4882a593Smuzhiyun 	wl_cfg80211_iface_state_ops(ndev->ieee80211_ptr,
1267*4882a593Smuzhiyun 		WL_IF_CHANGE_REQ, wl_iftype, wl_mode);
1268*4882a593Smuzhiyun 
1269*4882a593Smuzhiyun 	switch (type) {
1270*4882a593Smuzhiyun 	case NL80211_IFTYPE_ADHOC:
1271*4882a593Smuzhiyun 		infra = 0;
1272*4882a593Smuzhiyun 		break;
1273*4882a593Smuzhiyun 	case NL80211_IFTYPE_STATION:
1274*4882a593Smuzhiyun 		/* Supplicant sets iftype to STATION while removing p2p GO */
1275*4882a593Smuzhiyun 		if (ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
1276*4882a593Smuzhiyun 			/* Downgrading P2P GO */
1277*4882a593Smuzhiyun 			err = wl_cfg80211_change_p2prole(wiphy, ndev, type);
1278*4882a593Smuzhiyun 			if (unlikely(err)) {
1279*4882a593Smuzhiyun 				WL_ERR(("P2P downgrade failed \n"));
1280*4882a593Smuzhiyun 			}
1281*4882a593Smuzhiyun 		} else if (ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) {
1282*4882a593Smuzhiyun 			/* Downgrade role from AP to STA */
1283*4882a593Smuzhiyun 			if ((err = wl_cfg80211_add_del_bss(cfg, ndev,
1284*4882a593Smuzhiyun 				netinfo->bssidx, wl_iftype, 0, NULL)) < 0) {
1285*4882a593Smuzhiyun 				WL_ERR(("AP-STA Downgrade failed \n"));
1286*4882a593Smuzhiyun 				goto fail;
1287*4882a593Smuzhiyun 			}
1288*4882a593Smuzhiyun 		}
1289*4882a593Smuzhiyun 		break;
1290*4882a593Smuzhiyun 	case NL80211_IFTYPE_AP:
1291*4882a593Smuzhiyun 		/* intentional fall through */
1292*4882a593Smuzhiyun 	case NL80211_IFTYPE_AP_VLAN:
1293*4882a593Smuzhiyun 		{
1294*4882a593Smuzhiyun 			if (!wl_get_drv_status(cfg, AP_CREATED, ndev) &&
1295*4882a593Smuzhiyun 					wl_get_drv_status(cfg, READY, ndev)) {
1296*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && !defined(OEM_ANDROID)
1297*4882a593Smuzhiyun 				dhd->op_mode = DHD_FLAG_HOSTAP_MODE;
1298*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
1299*4882a593Smuzhiyun 				err = wl_cfg80211_set_ap_role(cfg, ndev);
1300*4882a593Smuzhiyun 				if (unlikely(err)) {
1301*4882a593Smuzhiyun 					WL_ERR(("set ap role failed!\n"));
1302*4882a593Smuzhiyun 					goto fail;
1303*4882a593Smuzhiyun 				}
1304*4882a593Smuzhiyun 			} else {
1305*4882a593Smuzhiyun 				WL_INFORM_MEM(("AP_CREATED bit set. Skip role change\n"));
1306*4882a593Smuzhiyun 			}
1307*4882a593Smuzhiyun 			break;
1308*4882a593Smuzhiyun 		}
1309*4882a593Smuzhiyun 	case NL80211_IFTYPE_P2P_GO:
1310*4882a593Smuzhiyun 		/* Intentional fall through */
1311*4882a593Smuzhiyun 	case NL80211_IFTYPE_P2P_CLIENT:
1312*4882a593Smuzhiyun 		infra = 1;
1313*4882a593Smuzhiyun 		err = wl_cfg80211_change_p2prole(wiphy, ndev, type);
1314*4882a593Smuzhiyun 		break;
1315*4882a593Smuzhiyun 	case NL80211_IFTYPE_MONITOR:
1316*4882a593Smuzhiyun 	case NL80211_IFTYPE_WDS:
1317*4882a593Smuzhiyun 	case NL80211_IFTYPE_MESH_POINT:
1318*4882a593Smuzhiyun 		/* Intentional fall through */
1319*4882a593Smuzhiyun 	default:
1320*4882a593Smuzhiyun 		WL_ERR(("Unsupported type:%d \n", type));
1321*4882a593Smuzhiyun 		err = -EINVAL;
1322*4882a593Smuzhiyun 		goto fail;
1323*4882a593Smuzhiyun 	}
1324*4882a593Smuzhiyun 
1325*4882a593Smuzhiyun 	if (wl_get_drv_status(cfg, READY, ndev)) {
1326*4882a593Smuzhiyun 		err = wldev_ioctl_set(ndev, WLC_SET_INFRA, &infra, sizeof(s32));
1327*4882a593Smuzhiyun 		if (err < 0) {
1328*4882a593Smuzhiyun 			WL_ERR(("SET INFRA/IBSS  error %d\n", err));
1329*4882a593Smuzhiyun 			goto fail;
1330*4882a593Smuzhiyun 		}
1331*4882a593Smuzhiyun 	}
1332*4882a593Smuzhiyun 
1333*4882a593Smuzhiyun 	wl_cfg80211_iface_state_ops(primary_ndev->ieee80211_ptr,
1334*4882a593Smuzhiyun 		WL_IF_CHANGE_DONE, wl_iftype, wl_mode);
1335*4882a593Smuzhiyun 
1336*4882a593Smuzhiyun 	/* Update new iftype in relevant structures */
1337*4882a593Smuzhiyun 	if (is_p2p_group_iface(ndev->ieee80211_ptr) && (type == NL80211_IFTYPE_STATION)) {
1338*4882a593Smuzhiyun 		/* For role downgrade cases, we keep interface role as GC */
1339*4882a593Smuzhiyun 		netinfo->iftype = WL_IF_TYPE_P2P_GC;
1340*4882a593Smuzhiyun 		WL_DBG_MEM(("[%s] Set  base role to GC, current role"
1341*4882a593Smuzhiyun 			"ndev->ieee80211_ptr->iftype = %d\n",
1342*4882a593Smuzhiyun 			__FUNCTION__, ndev->ieee80211_ptr->iftype));
1343*4882a593Smuzhiyun 	} else {
1344*4882a593Smuzhiyun 		netinfo->iftype = wl_iftype;
1345*4882a593Smuzhiyun 	}
1346*4882a593Smuzhiyun 
1347*4882a593Smuzhiyun 	ndev->ieee80211_ptr->iftype = type;
1348*4882a593Smuzhiyun 
1349*4882a593Smuzhiyun 	WL_INFORM_MEM(("[%s] cfg_iftype changed to %d\n", ndev->name, type));
1350*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
1351*4882a593Smuzhiyun 	wl_ext_iapsta_update_iftype(ndev, wl_iftype);
1352*4882a593Smuzhiyun #endif
1353*4882a593Smuzhiyun 
1354*4882a593Smuzhiyun fail:
1355*4882a593Smuzhiyun 	if (err) {
1356*4882a593Smuzhiyun 		wl_flush_fw_log_buffer(ndev, FW_LOGSET_MASK_ALL);
1357*4882a593Smuzhiyun 	}
1358*4882a593Smuzhiyun 	mutex_unlock(&cfg->if_sync);
1359*4882a593Smuzhiyun 	return err;
1360*4882a593Smuzhiyun }
1361*4882a593Smuzhiyun 
1362*4882a593Smuzhiyun #ifdef SUPPORT_AP_BWCTRL
1363*4882a593Smuzhiyun static chanspec_t
wl_channel_to_chanspec(struct wiphy * wiphy,struct net_device * dev,u32 channel,u32 bw_cap)1364*4882a593Smuzhiyun wl_channel_to_chanspec(struct wiphy *wiphy, struct net_device *dev, u32 channel, u32 bw_cap)
1365*4882a593Smuzhiyun {
1366*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1367*4882a593Smuzhiyun 	u8 *buf = NULL;
1368*4882a593Smuzhiyun 	wl_uint32_list_t *list;
1369*4882a593Smuzhiyun 	int err = BCME_OK;
1370*4882a593Smuzhiyun 	chanspec_t c = 0, ret_c = 0;
1371*4882a593Smuzhiyun 	int bw = 0, tmp_bw = 0;
1372*4882a593Smuzhiyun 	int i;
1373*4882a593Smuzhiyun 	u32 tmp_c;
1374*4882a593Smuzhiyun 
1375*4882a593Smuzhiyun #define LOCAL_BUF_SIZE	1024
1376*4882a593Smuzhiyun 	buf = (u8 *)MALLOC(cfg->osh, LOCAL_BUF_SIZE);
1377*4882a593Smuzhiyun 	if (!buf) {
1378*4882a593Smuzhiyun 		WL_ERR(("buf memory alloc failed\n"));
1379*4882a593Smuzhiyun 		goto exit;
1380*4882a593Smuzhiyun 	}
1381*4882a593Smuzhiyun 
1382*4882a593Smuzhiyun 	err = wldev_iovar_getbuf_bsscfg(dev, "chanspecs", NULL,
1383*4882a593Smuzhiyun 		0, buf, LOCAL_BUF_SIZE, 0, &cfg->ioctl_buf_sync);
1384*4882a593Smuzhiyun 	if (err != BCME_OK) {
1385*4882a593Smuzhiyun 		WL_ERR(("get chanspecs failed with %d\n", err));
1386*4882a593Smuzhiyun 		goto exit;
1387*4882a593Smuzhiyun 	}
1388*4882a593Smuzhiyun 
1389*4882a593Smuzhiyun 	list = (wl_uint32_list_t *)(void *)buf;
1390*4882a593Smuzhiyun 	for (i = 0; i < dtoh32(list->count); i++) {
1391*4882a593Smuzhiyun 		c = dtoh32(list->element[i]);
1392*4882a593Smuzhiyun 		if (channel <= CH_MAX_2G_CHANNEL) {
1393*4882a593Smuzhiyun 			if (!CHSPEC_IS20(c))
1394*4882a593Smuzhiyun 				continue;
1395*4882a593Smuzhiyun 			if (channel == CHSPEC_CHANNEL(c)) {
1396*4882a593Smuzhiyun 				ret_c = c;
1397*4882a593Smuzhiyun 				bw = 20;
1398*4882a593Smuzhiyun 				goto exit;
1399*4882a593Smuzhiyun 			}
1400*4882a593Smuzhiyun 		}
1401*4882a593Smuzhiyun 		tmp_c = wf_chspec_ctlchan(c);
1402*4882a593Smuzhiyun 		tmp_bw = bw2cap[CHSPEC_BW(c) >> WL_CHANSPEC_BW_SHIFT];
1403*4882a593Smuzhiyun 		if (tmp_c != channel)
1404*4882a593Smuzhiyun 			continue;
1405*4882a593Smuzhiyun 
1406*4882a593Smuzhiyun 		if ((tmp_bw > bw) && (tmp_bw <= bw_cap)) {
1407*4882a593Smuzhiyun 			bw = tmp_bw;
1408*4882a593Smuzhiyun 			ret_c = c;
1409*4882a593Smuzhiyun 			if (bw == bw_cap)
1410*4882a593Smuzhiyun 				goto exit;
1411*4882a593Smuzhiyun 		}
1412*4882a593Smuzhiyun 	}
1413*4882a593Smuzhiyun exit:
1414*4882a593Smuzhiyun 	if (buf) {
1415*4882a593Smuzhiyun 		 MFREE(cfg->osh, buf, LOCAL_BUF_SIZE);
1416*4882a593Smuzhiyun 	}
1417*4882a593Smuzhiyun #undef LOCAL_BUF_SIZE
1418*4882a593Smuzhiyun 	WL_DBG(("return chanspec %x %d\n", ret_c, bw));
1419*4882a593Smuzhiyun 	return ret_c;
1420*4882a593Smuzhiyun }
1421*4882a593Smuzhiyun #endif /* SUPPORT_AP_BWCTRL */
1422*4882a593Smuzhiyun 
1423*4882a593Smuzhiyun void
wl_cfg80211_cleanup_virtual_ifaces(struct bcm_cfg80211 * cfg,bool rtnl_lock_reqd)1424*4882a593Smuzhiyun wl_cfg80211_cleanup_virtual_ifaces(struct bcm_cfg80211 *cfg, bool rtnl_lock_reqd)
1425*4882a593Smuzhiyun {
1426*4882a593Smuzhiyun 	struct net_info *iter, *next;
1427*4882a593Smuzhiyun 	struct net_device *primary_ndev;
1428*4882a593Smuzhiyun 
1429*4882a593Smuzhiyun 	/* Note: This function will clean up only the network interface and host
1430*4882a593Smuzhiyun 	 * data structures. The firmware interface clean up will happen in the
1431*4882a593Smuzhiyun 	 * during chip reset (ifconfig wlan0 down for built-in drivers/rmmod
1432*4882a593Smuzhiyun 	 * context for the module case).
1433*4882a593Smuzhiyun 	 */
1434*4882a593Smuzhiyun 	primary_ndev = bcmcfg_to_prmry_ndev(cfg);
1435*4882a593Smuzhiyun 	WL_DBG(("Enter\n"));
1436*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
1437*4882a593Smuzhiyun 	for_each_ndev(cfg, iter, next) {
1438*4882a593Smuzhiyun 		GCC_DIAGNOSTIC_POP();
1439*4882a593Smuzhiyun 		if (iter->ndev && (iter->ndev != primary_ndev)) {
1440*4882a593Smuzhiyun 			/* Ensure interfaces are down before deleting */
1441*4882a593Smuzhiyun #ifdef WL_STATIC_IF
1442*4882a593Smuzhiyun 			/* Avoiding cleaning static ifaces */
1443*4882a593Smuzhiyun 			if (!wl_cfg80211_static_if(cfg, iter->ndev))
1444*4882a593Smuzhiyun #endif /* WL_STATIC_IF */
1445*4882a593Smuzhiyun 			{
1446*4882a593Smuzhiyun 				dev_close(iter->ndev);
1447*4882a593Smuzhiyun 				WL_DBG(("Cleaning up iface:%s \n", iter->ndev->name));
1448*4882a593Smuzhiyun 				wl_cfg80211_post_ifdel(iter->ndev, rtnl_lock_reqd, 0);
1449*4882a593Smuzhiyun 			}
1450*4882a593Smuzhiyun 		}
1451*4882a593Smuzhiyun 	}
1452*4882a593Smuzhiyun }
1453*4882a593Smuzhiyun 
1454*4882a593Smuzhiyun int
wl_get_bandwidth_cap(struct net_device * ndev,uint32 band,uint32 * bandwidth)1455*4882a593Smuzhiyun wl_get_bandwidth_cap(struct net_device *ndev, uint32 band, uint32 *bandwidth)
1456*4882a593Smuzhiyun {
1457*4882a593Smuzhiyun 	u32 bw = WL_CHANSPEC_BW_20;
1458*4882a593Smuzhiyun 	s32 err = BCME_OK;
1459*4882a593Smuzhiyun 	s32 bw_cap = 0;
1460*4882a593Smuzhiyun 	struct {
1461*4882a593Smuzhiyun 		u32 band;
1462*4882a593Smuzhiyun 		u32 bw_cap;
1463*4882a593Smuzhiyun 	} param = {0, 0};
1464*4882a593Smuzhiyun 	u8 ioctl_buf[WLC_IOCTL_SMLEN];
1465*4882a593Smuzhiyun 
1466*4882a593Smuzhiyun 	if (band == WL_CHANSPEC_BAND_5G) {
1467*4882a593Smuzhiyun 		param.band = WLC_BAND_5G;
1468*4882a593Smuzhiyun 	}
1469*4882a593Smuzhiyun 	else if (band == WL_CHANSPEC_BAND_2G) {
1470*4882a593Smuzhiyun 		param.band = WLC_BAND_2G;
1471*4882a593Smuzhiyun 	}
1472*4882a593Smuzhiyun #ifdef WL_6G_BAND
1473*4882a593Smuzhiyun 	else if (band == WL_CHANSPEC_BAND_6G) {
1474*4882a593Smuzhiyun 		param.band = WLC_BAND_6G;
1475*4882a593Smuzhiyun 	}
1476*4882a593Smuzhiyun #endif
1477*4882a593Smuzhiyun 	if (param.band) {
1478*4882a593Smuzhiyun 		/* bw_cap is newly defined iovar for checking bandwith
1479*4882a593Smuzhiyun 		  * capability of the band in Aardvark_branch_tob
1480*4882a593Smuzhiyun 		  */
1481*4882a593Smuzhiyun 		err = wldev_iovar_getbuf(ndev, "bw_cap", &param, sizeof(param),
1482*4882a593Smuzhiyun 			ioctl_buf, sizeof(ioctl_buf), NULL);
1483*4882a593Smuzhiyun 		if (err) {
1484*4882a593Smuzhiyun 			if (err != BCME_UNSUPPORTED) {
1485*4882a593Smuzhiyun 				WL_ERR(("bw_cap failed, %d\n", err));
1486*4882a593Smuzhiyun 				return err;
1487*4882a593Smuzhiyun 			} else {
1488*4882a593Smuzhiyun 				/* if firmware doesn't support bw_cap iovar,
1489*4882a593Smuzhiyun 				 * we have to use mimo_bw_cap
1490*4882a593Smuzhiyun 				 */
1491*4882a593Smuzhiyun 				err = wldev_iovar_getint(ndev, "mimo_bw_cap", &bw_cap);
1492*4882a593Smuzhiyun 				if (err) {
1493*4882a593Smuzhiyun 					WL_ERR(("error get mimo_bw_cap (%d)\n", err));
1494*4882a593Smuzhiyun 				}
1495*4882a593Smuzhiyun 				if (bw_cap != WLC_N_BW_20ALL) {
1496*4882a593Smuzhiyun 					bw = WL_CHANSPEC_BW_40;
1497*4882a593Smuzhiyun 				}
1498*4882a593Smuzhiyun 			}
1499*4882a593Smuzhiyun 		} else {
1500*4882a593Smuzhiyun 			if (WL_BW_CAP_160MHZ(ioctl_buf[0])) {
1501*4882a593Smuzhiyun 				bw = WL_CHANSPEC_BW_160;
1502*4882a593Smuzhiyun 			} else if (WL_BW_CAP_80MHZ(ioctl_buf[0])) {
1503*4882a593Smuzhiyun 				bw = WL_CHANSPEC_BW_80;
1504*4882a593Smuzhiyun 			} else if (WL_BW_CAP_40MHZ(ioctl_buf[0])) {
1505*4882a593Smuzhiyun 				bw = WL_CHANSPEC_BW_40;
1506*4882a593Smuzhiyun 			} else {
1507*4882a593Smuzhiyun 				bw = WL_CHANSPEC_BW_20;
1508*4882a593Smuzhiyun 			}
1509*4882a593Smuzhiyun 		}
1510*4882a593Smuzhiyun 	} else if (band == WL_CHANSPEC_BAND_2G) {
1511*4882a593Smuzhiyun 		bw = WL_CHANSPEC_BW_20;
1512*4882a593Smuzhiyun 	}
1513*4882a593Smuzhiyun 
1514*4882a593Smuzhiyun 	*bandwidth = bw;
1515*4882a593Smuzhiyun 
1516*4882a593Smuzhiyun 	return err;
1517*4882a593Smuzhiyun }
1518*4882a593Smuzhiyun 
1519*4882a593Smuzhiyun s32
wl_get_nl80211_band(u32 wl_band)1520*4882a593Smuzhiyun wl_get_nl80211_band(u32 wl_band)
1521*4882a593Smuzhiyun {
1522*4882a593Smuzhiyun 	s32 err = BCME_ERROR;
1523*4882a593Smuzhiyun 
1524*4882a593Smuzhiyun 	switch (wl_band) {
1525*4882a593Smuzhiyun 		case WL_CHANSPEC_BAND_2G:
1526*4882a593Smuzhiyun 			return IEEE80211_BAND_2GHZ;
1527*4882a593Smuzhiyun 		case WL_CHANSPEC_BAND_5G:
1528*4882a593Smuzhiyun 			return IEEE80211_BAND_5GHZ;
1529*4882a593Smuzhiyun #ifdef WL_BAND_6G
1530*4882a593Smuzhiyun 		case WL_CHANSPEC_BAND_6G:
1531*4882a593Smuzhiyun 			/* current kernels doesn't support seperate
1532*4882a593Smuzhiyun 			 * band for 6GHz. so till patch is available
1533*4882a593Smuzhiyun 			 * map it under 5GHz
1534*4882a593Smuzhiyun 			 */
1535*4882a593Smuzhiyun 			return IEEE80211_BAND_5GHZ;
1536*4882a593Smuzhiyun #endif /* WL_BAND_6G */
1537*4882a593Smuzhiyun 		default:
1538*4882a593Smuzhiyun 			WL_ERR(("unsupported Band. %d\n", wl_band));
1539*4882a593Smuzhiyun 	}
1540*4882a593Smuzhiyun 
1541*4882a593Smuzhiyun 	return err;
1542*4882a593Smuzhiyun }
1543*4882a593Smuzhiyun 
1544*4882a593Smuzhiyun s32
wl_cfg80211_set_channel(struct wiphy * wiphy,struct net_device * dev,struct ieee80211_channel * chan,enum nl80211_channel_type channel_type)1545*4882a593Smuzhiyun wl_cfg80211_set_channel(struct wiphy *wiphy, struct net_device *dev,
1546*4882a593Smuzhiyun 	struct ieee80211_channel *chan,
1547*4882a593Smuzhiyun 	enum nl80211_channel_type channel_type)
1548*4882a593Smuzhiyun {
1549*4882a593Smuzhiyun 	chanspec_t chspec = INVCHANSPEC;
1550*4882a593Smuzhiyun 	chanspec_t cur_chspec = INVCHANSPEC;
1551*4882a593Smuzhiyun 	u32 bw = WL_CHANSPEC_BW_20;
1552*4882a593Smuzhiyun 	s32 err = BCME_OK;
1553*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
1554*4882a593Smuzhiyun #if defined(CUSTOM_SET_CPUCORE) || defined(APSTA_RESTRICTED_CHANNEL)
1555*4882a593Smuzhiyun 	dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
1556*4882a593Smuzhiyun #endif /* CUSTOM_SET_CPUCORE || APSTA_RESTRICTED_CHANNEL */
1557*4882a593Smuzhiyun 	u16 center_freq = chan->center_freq;
1558*4882a593Smuzhiyun 
1559*4882a593Smuzhiyun 	dev = ndev_to_wlc_ndev(dev, cfg);
1560*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
1561*4882a593Smuzhiyun 	if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP ||
1562*4882a593Smuzhiyun 			dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
1563*4882a593Smuzhiyun 		u16 wl_iftype = 0;
1564*4882a593Smuzhiyun 		u16 wl_mode = 0;
1565*4882a593Smuzhiyun 
1566*4882a593Smuzhiyun 		chspec = wl_freq_to_chanspec(chan->center_freq);
1567*4882a593Smuzhiyun 		if (cfg80211_to_wl_iftype(dev->ieee80211_ptr->iftype,
1568*4882a593Smuzhiyun 				&wl_iftype, &wl_mode) < 0) {
1569*4882a593Smuzhiyun 			WL_ERR(("Unknown interface type:0x%x\n", dev->ieee80211_ptr->iftype));
1570*4882a593Smuzhiyun 			return -EINVAL;
1571*4882a593Smuzhiyun 		}
1572*4882a593Smuzhiyun 		wl_ext_iapsta_update_iftype(dev, wl_iftype);
1573*4882a593Smuzhiyun 		chspec = wl_ext_iapsta_update_channel(dev, chspec);
1574*4882a593Smuzhiyun 		center_freq = wl_channel_to_frequency(wf_chspec_primary20_chan(chspec),
1575*4882a593Smuzhiyun 			CHSPEC_BAND(chspec));
1576*4882a593Smuzhiyun 	} else
1577*4882a593Smuzhiyun #endif
1578*4882a593Smuzhiyun 	chspec = wl_freq_to_chanspec(center_freq);
1579*4882a593Smuzhiyun 
1580*4882a593Smuzhiyun 	WL_MSG(dev->name, "netdev_ifidx(%d) chan_type(%d) target channel(%s-%d %sMHz)\n",
1581*4882a593Smuzhiyun 		dev->ifindex, channel_type, CHSPEC2BANDSTR(chspec),
1582*4882a593Smuzhiyun 		CHSPEC_CHANNEL(chspec), wf_chspec_to_bw_str(chspec));
1583*4882a593Smuzhiyun 
1584*4882a593Smuzhiyun #ifdef WL_P2P_6G
1585*4882a593Smuzhiyun 	if (!(cfg->p2p_6g_enabled)) {
1586*4882a593Smuzhiyun #endif /* WL_P2P_6G */
1587*4882a593Smuzhiyun 		if (IS_P2P_GO(dev->ieee80211_ptr) && (CHSPEC_IS6G(chspec))) {
1588*4882a593Smuzhiyun 			WL_ERR(("P2P GO not allowed on 6G\n"));
1589*4882a593Smuzhiyun 			return -ENOTSUPP;
1590*4882a593Smuzhiyun 		}
1591*4882a593Smuzhiyun #ifdef WL_P2P_6G
1592*4882a593Smuzhiyun 	}
1593*4882a593Smuzhiyun #endif /* WL_P2P_6G */
1594*4882a593Smuzhiyun 
1595*4882a593Smuzhiyun #ifdef NOT_YET
1596*4882a593Smuzhiyun 	switch (channel_type) {
1597*4882a593Smuzhiyun 		case NL80211_CHAN_HT40MINUS:
1598*4882a593Smuzhiyun 			/* secondary channel is below the control channel */
1599*4882a593Smuzhiyun 			chspec = CH40MHZ_CHSPEC(CHSPEC_CHANNEL(chspec), WL_CHANSPEC_CTL_SB_UPPER);
1600*4882a593Smuzhiyun 			break;
1601*4882a593Smuzhiyun 		case NL80211_CHAN_HT40PLUS:
1602*4882a593Smuzhiyun 			/* secondary channel is above the control channel */
1603*4882a593Smuzhiyun 			chspec = CH40MHZ_CHSPEC(CHSPEC_CHANNEL(chspec), WL_CHANSPEC_CTL_SB_LOWER);
1604*4882a593Smuzhiyun 			break;
1605*4882a593Smuzhiyun 		default:
1606*4882a593Smuzhiyun 			chspec = CH20MHZ_CHSPEC(CHSPEC_CHANNEL(chspec));
1607*4882a593Smuzhiyun 
1608*4882a593Smuzhiyun 	}
1609*4882a593Smuzhiyun #endif /* NOT_YET */
1610*4882a593Smuzhiyun 
1611*4882a593Smuzhiyun #if defined(APSTA_RESTRICTED_CHANNEL)
1612*4882a593Smuzhiyun 	/* Some customer platform used limited number of channels
1613*4882a593Smuzhiyun 	 * for SoftAP interface on STA/SoftAP concurrent mode.
1614*4882a593Smuzhiyun 	 * - 2.4GHz Channel: CH1 - CH13
1615*4882a593Smuzhiyun 	 * - 5GHz Channel: CH149 (it depends on the country code)
1616*4882a593Smuzhiyun 	 * If the Android framework sent invaild channel configuration
1617*4882a593Smuzhiyun 	 * to DHD, driver should change the channel which is sutible for
1618*4882a593Smuzhiyun 	 * STA/SoftAP concurrent mode.
1619*4882a593Smuzhiyun 	 * - Set operating channel to CH1 (default 2.4GHz channel for
1620*4882a593Smuzhiyun 	 *   restricted APSTA mode) if STA interface was associated to
1621*4882a593Smuzhiyun 	 *   5GHz APs except for CH149.
1622*4882a593Smuzhiyun 	 * - Otherwise, set the channel to the same channel as existing AP.
1623*4882a593Smuzhiyun 	 */
1624*4882a593Smuzhiyun 	if (wl_get_mode_by_netdev(cfg, dev) == WL_MODE_AP &&
1625*4882a593Smuzhiyun 		DHD_OPMODE_STA_SOFTAP_CONCURR(dhd) &&
1626*4882a593Smuzhiyun 		wl_get_drv_status(cfg, CONNECTED, bcmcfg_to_prmry_ndev(cfg))) {
1627*4882a593Smuzhiyun 		u32 *sta_chanspec = (u32 *)wl_read_prof(cfg,
1628*4882a593Smuzhiyun 			bcmcfg_to_prmry_ndev(cfg), WL_PROF_CHAN);
1629*4882a593Smuzhiyun 		if (chan->band == wl_get_nl80211_band(CHSPEC_BAND(*sta_chanspec))) {
1630*4882a593Smuzhiyun 			/* Do not try SCC in 5GHz if channel is not CH149 */
1631*4882a593Smuzhiyun 			chspec = (
1632*4882a593Smuzhiyun #ifdef WL_6G_BAND
1633*4882a593Smuzhiyun 				CHSPEC_IS6G(*sta_chanspec) ||
1634*4882a593Smuzhiyun #endif /* WL_6G_BAND */
1635*4882a593Smuzhiyun 				(CHSPEC_IS5G(*sta_chanspec) &&
1636*4882a593Smuzhiyun 				wf_chspec_primary20_chan(*sta_chanspec) !=
1637*4882a593Smuzhiyun 				DEFAULT_5G_SOFTAP_CHANNEL)) ?
1638*4882a593Smuzhiyun 				DEFAULT_2G_SOFTAP_CHANSPEC: *sta_chanspec;
1639*4882a593Smuzhiyun 			WL_ERR(("target chanspec will be changed to %x\n", chspec));
1640*4882a593Smuzhiyun 			if (CHSPEC_IS2G(chspec)) {
1641*4882a593Smuzhiyun 				bw = WL_CHANSPEC_BW_20;
1642*4882a593Smuzhiyun 				goto set_channel;
1643*4882a593Smuzhiyun 			}
1644*4882a593Smuzhiyun 		}
1645*4882a593Smuzhiyun 	}
1646*4882a593Smuzhiyun #endif /* APSTA_RESTRICTED_CHANNEL */
1647*4882a593Smuzhiyun 
1648*4882a593Smuzhiyun 	err = wl_get_bandwidth_cap(dev, CHSPEC_BAND(chspec), &bw);
1649*4882a593Smuzhiyun 	if (err < 0) {
1650*4882a593Smuzhiyun 		WL_ERR(("Failed to get bandwidth information, err=%d\n", err));
1651*4882a593Smuzhiyun 		return err;
1652*4882a593Smuzhiyun 	}
1653*4882a593Smuzhiyun 
1654*4882a593Smuzhiyun 	/* In case of 5G downgrade BW to 80MHz as 160MHz channels falls in DFS */
1655*4882a593Smuzhiyun 	if (CHSPEC_IS5G(chspec) && (bw == WL_CHANSPEC_BW_160)) {
1656*4882a593Smuzhiyun 		bw = WL_CHANSPEC_BW_80;
1657*4882a593Smuzhiyun 	}
1658*4882a593Smuzhiyun set_channel:
1659*4882a593Smuzhiyun 	cur_chspec = wf_create_chspec_from_primary(wf_chspec_primary20_chan(chspec),
1660*4882a593Smuzhiyun 		bw, CHSPEC_BAND(chspec));
1661*4882a593Smuzhiyun #ifdef WL_6G_BAND
1662*4882a593Smuzhiyun 	if (cfg->acs_chspec &&
1663*4882a593Smuzhiyun 		CHSPEC_IS6G(cfg->acs_chspec) &&
1664*4882a593Smuzhiyun 		(wf_chspec_ctlchspec(cfg->acs_chspec) == wf_chspec_ctlchspec(cur_chspec))) {
1665*4882a593Smuzhiyun 		WL_DBG(("using acs_chanspec %x\n", cfg->acs_chspec));
1666*4882a593Smuzhiyun 		cur_chspec = cfg->acs_chspec;
1667*4882a593Smuzhiyun 		cfg->acs_chspec = 0;
1668*4882a593Smuzhiyun 	}
1669*4882a593Smuzhiyun #endif /* WL_6G_BAND */
1670*4882a593Smuzhiyun 	if (wf_chspec_valid(cur_chspec)) {
1671*4882a593Smuzhiyun 		/* convert 802.11 ac chanspec to current fw chanspec type */
1672*4882a593Smuzhiyun 		cur_chspec = wl_chspec_host_to_driver(cur_chspec);
1673*4882a593Smuzhiyun 		if (cur_chspec != INVCHANSPEC) {
1674*4882a593Smuzhiyun 			if ((err = wldev_iovar_setint(dev, "chanspec",
1675*4882a593Smuzhiyun 				cur_chspec)) == BCME_BADCHAN) {
1676*4882a593Smuzhiyun 				u32 local_channel = CHSPEC_CHANNEL(chspec);
1677*4882a593Smuzhiyun 				if ((bw == WL_CHANSPEC_BW_80) || (bw == WL_CHANSPEC_BW_160))
1678*4882a593Smuzhiyun 					goto change_bw;
1679*4882a593Smuzhiyun 				err = wldev_ioctl_set(dev, WLC_SET_CHANNEL,
1680*4882a593Smuzhiyun 					&local_channel, sizeof(local_channel));
1681*4882a593Smuzhiyun 				if (err < 0) {
1682*4882a593Smuzhiyun 					WL_ERR(("WLC_SET_CHANNEL error %d"
1683*4882a593Smuzhiyun 					"chip may not be supporting this channel\n", err));
1684*4882a593Smuzhiyun 				}
1685*4882a593Smuzhiyun 			} else if (err) {
1686*4882a593Smuzhiyun 				WL_ERR(("failed to set chanspec error %d\n", err));
1687*4882a593Smuzhiyun 			}
1688*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
1689*4882a593Smuzhiyun #ifdef DISABLE_WL_FRAMEBURST_SOFTAP
1690*4882a593Smuzhiyun 			else {
1691*4882a593Smuzhiyun 				/* Disable Frameburst only for stand-alone 2GHz SoftAP */
1692*4882a593Smuzhiyun 				if (wl_get_mode_by_netdev(cfg, dev) == WL_MODE_AP &&
1693*4882a593Smuzhiyun 					DHD_OPMODE_SUPPORTED(cfg->pub, DHD_FLAG_HOSTAP_MODE) &&
1694*4882a593Smuzhiyun 					(CHSPEC_IS2G(chspec)) &&
1695*4882a593Smuzhiyun 					!wl_get_drv_status(cfg, CONNECTED,
1696*4882a593Smuzhiyun 						bcmcfg_to_prmry_ndev(cfg))) {
1697*4882a593Smuzhiyun 					WL_DBG(("Disabling frameburst on "
1698*4882a593Smuzhiyun 						"stand-alone 2GHz SoftAP\n"));
1699*4882a593Smuzhiyun 					wl_cfg80211_set_frameburst(cfg, FALSE);
1700*4882a593Smuzhiyun 				}
1701*4882a593Smuzhiyun 			}
1702*4882a593Smuzhiyun #endif /* DISABLE_WL_FRAMEBURST_SOFTAP */
1703*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
1704*4882a593Smuzhiyun 		} else {
1705*4882a593Smuzhiyun 			WL_ERR(("failed to convert host chanspec to fw chanspec\n"));
1706*4882a593Smuzhiyun 			err = BCME_ERROR;
1707*4882a593Smuzhiyun 		}
1708*4882a593Smuzhiyun 	} else {
1709*4882a593Smuzhiyun change_bw:
1710*4882a593Smuzhiyun 		if (bw == WL_CHANSPEC_BW_160) {
1711*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_80;
1712*4882a593Smuzhiyun 		} else if (bw == WL_CHANSPEC_BW_80) {
1713*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_40;
1714*4882a593Smuzhiyun 		} else if (bw == WL_CHANSPEC_BW_40) {
1715*4882a593Smuzhiyun 			bw = WL_CHANSPEC_BW_20;
1716*4882a593Smuzhiyun 		} else {
1717*4882a593Smuzhiyun 			bw = 0;
1718*4882a593Smuzhiyun 		}
1719*4882a593Smuzhiyun 		if (bw)
1720*4882a593Smuzhiyun 			goto set_channel;
1721*4882a593Smuzhiyun 		WL_ERR(("Invalid chanspec 0x%x\n", chspec));
1722*4882a593Smuzhiyun 		err = BCME_ERROR;
1723*4882a593Smuzhiyun 	}
1724*4882a593Smuzhiyun #ifdef CUSTOM_SET_CPUCORE
1725*4882a593Smuzhiyun 	if (dhd->op_mode == DHD_FLAG_HOSTAP_MODE) {
1726*4882a593Smuzhiyun 		WL_DBG(("SoftAP mode do not need to set cpucore\n"));
1727*4882a593Smuzhiyun 	} else if (chspec & WL_CHANSPEC_BW_80) {
1728*4882a593Smuzhiyun 		/* SoftAp only mode do not need to set cpucore */
1729*4882a593Smuzhiyun 		if ((dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) &&
1730*4882a593Smuzhiyun 			dev != bcmcfg_to_prmry_ndev(cfg)) {
1731*4882a593Smuzhiyun 			/* Soft AP on virtual Iface (AP+STA case) */
1732*4882a593Smuzhiyun 			dhd->chan_isvht80 |= DHD_FLAG_HOSTAP_MODE;
1733*4882a593Smuzhiyun 			dhd_set_cpucore(dhd, TRUE);
1734*4882a593Smuzhiyun 		} else if (is_p2p_group_iface(dev->ieee80211_ptr)) {
1735*4882a593Smuzhiyun 			/* If P2P IF is vht80 */
1736*4882a593Smuzhiyun 			dhd->chan_isvht80 |= DHD_FLAG_P2P_MODE;
1737*4882a593Smuzhiyun 			dhd_set_cpucore(dhd, TRUE);
1738*4882a593Smuzhiyun 		}
1739*4882a593Smuzhiyun 	}
1740*4882a593Smuzhiyun #endif /* CUSTOM_SET_CPUCORE */
1741*4882a593Smuzhiyun 	if (!err && (wl_get_mode_by_netdev(cfg, dev) == WL_MODE_AP)) {
1742*4882a593Smuzhiyun 		/* Update AP/GO operating chanspec */
1743*4882a593Smuzhiyun 		cfg->ap_oper_channel = wl_freq_to_chanspec(center_freq);
1744*4882a593Smuzhiyun 	}
1745*4882a593Smuzhiyun 	if (err) {
1746*4882a593Smuzhiyun 		wl_flush_fw_log_buffer(bcmcfg_to_prmry_ndev(cfg),
1747*4882a593Smuzhiyun 			FW_LOGSET_MASK_ALL);
1748*4882a593Smuzhiyun 	} else {
1749*4882a593Smuzhiyun 		WL_DBG(("Setting chanspec %x for GO/AP \n", chspec));
1750*4882a593Smuzhiyun 	}
1751*4882a593Smuzhiyun 	return err;
1752*4882a593Smuzhiyun }
1753*4882a593Smuzhiyun 
1754*4882a593Smuzhiyun static s32
wl_validate_opensecurity(struct net_device * dev,s32 bssidx,bool privacy)1755*4882a593Smuzhiyun wl_validate_opensecurity(struct net_device *dev, s32 bssidx, bool privacy)
1756*4882a593Smuzhiyun {
1757*4882a593Smuzhiyun 	s32 err = BCME_OK;
1758*4882a593Smuzhiyun 	u32 wpa_val;
1759*4882a593Smuzhiyun 	s32 wsec = 0;
1760*4882a593Smuzhiyun 
1761*4882a593Smuzhiyun 	/* set auth */
1762*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "auth", 0, bssidx);
1763*4882a593Smuzhiyun 	if (err < 0) {
1764*4882a593Smuzhiyun 		WL_ERR(("auth error %d\n", err));
1765*4882a593Smuzhiyun 		return BCME_ERROR;
1766*4882a593Smuzhiyun 	}
1767*4882a593Smuzhiyun 
1768*4882a593Smuzhiyun 	if (privacy) {
1769*4882a593Smuzhiyun 		/* If privacy bit is set in open mode, then WEP would be enabled */
1770*4882a593Smuzhiyun 		wsec = WEP_ENABLED;
1771*4882a593Smuzhiyun 		WL_DBG(("Setting wsec to %d for WEP \n", wsec));
1772*4882a593Smuzhiyun 	}
1773*4882a593Smuzhiyun 
1774*4882a593Smuzhiyun 	/* set wsec */
1775*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx);
1776*4882a593Smuzhiyun 	if (err < 0) {
1777*4882a593Smuzhiyun 		WL_ERR(("wsec error %d\n", err));
1778*4882a593Smuzhiyun 		return BCME_ERROR;
1779*4882a593Smuzhiyun 	}
1780*4882a593Smuzhiyun 
1781*4882a593Smuzhiyun 	/* set upper-layer auth */
1782*4882a593Smuzhiyun 	if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_ADHOC)
1783*4882a593Smuzhiyun 		wpa_val = WPA_AUTH_NONE;
1784*4882a593Smuzhiyun 	else
1785*4882a593Smuzhiyun 		wpa_val = WPA_AUTH_DISABLED;
1786*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_val, bssidx);
1787*4882a593Smuzhiyun 	if (err < 0) {
1788*4882a593Smuzhiyun 		WL_ERR(("wpa_auth error %d\n", err));
1789*4882a593Smuzhiyun 		return BCME_ERROR;
1790*4882a593Smuzhiyun 	}
1791*4882a593Smuzhiyun 
1792*4882a593Smuzhiyun 	return 0;
1793*4882a593Smuzhiyun }
1794*4882a593Smuzhiyun 
1795*4882a593Smuzhiyun #define MAX_FILS_IND_IE_LEN 1024u
1796*4882a593Smuzhiyun static s32
wl_validate_fils_ind_ie(struct net_device * dev,const bcm_tlv_t * filsindie,s32 bssidx)1797*4882a593Smuzhiyun wl_validate_fils_ind_ie(struct net_device *dev, const bcm_tlv_t *filsindie, s32 bssidx)
1798*4882a593Smuzhiyun {
1799*4882a593Smuzhiyun 	s32 err = BCME_OK;
1800*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = NULL;
1801*4882a593Smuzhiyun 	bcm_iov_buf_t *iov_buf = NULL;
1802*4882a593Smuzhiyun 	bcm_xtlv_t* pxtlv;
1803*4882a593Smuzhiyun 	int iov_buf_size = 0;
1804*4882a593Smuzhiyun 
1805*4882a593Smuzhiyun 	if (!dev || !filsindie) {
1806*4882a593Smuzhiyun 		WL_ERR(("%s: dev/filsidie is null\n", __FUNCTION__));
1807*4882a593Smuzhiyun 		goto exit;
1808*4882a593Smuzhiyun 	}
1809*4882a593Smuzhiyun 
1810*4882a593Smuzhiyun 	cfg = wl_get_cfg(dev);
1811*4882a593Smuzhiyun 	if (!cfg) {
1812*4882a593Smuzhiyun 		WL_ERR(("%s: cfg is null\n", __FUNCTION__));
1813*4882a593Smuzhiyun 		goto exit;
1814*4882a593Smuzhiyun 	}
1815*4882a593Smuzhiyun 
1816*4882a593Smuzhiyun 	iov_buf_size = sizeof(bcm_iov_buf_t) + sizeof(bcm_xtlv_t) + filsindie->len - 1;
1817*4882a593Smuzhiyun 	iov_buf = MALLOCZ(cfg->osh, iov_buf_size);
1818*4882a593Smuzhiyun 	if (!iov_buf) {
1819*4882a593Smuzhiyun 		WL_ERR(("%s: iov_buf alloc failed! %d bytes\n", __FUNCTION__, iov_buf_size));
1820*4882a593Smuzhiyun 		err = BCME_NOMEM;
1821*4882a593Smuzhiyun 		goto exit;
1822*4882a593Smuzhiyun 	}
1823*4882a593Smuzhiyun 	iov_buf->version = WL_FILS_IOV_VERSION;
1824*4882a593Smuzhiyun 	iov_buf->id = WL_FILS_CMD_ADD_IND_IE;
1825*4882a593Smuzhiyun 	iov_buf->len = sizeof(bcm_xtlv_t) + filsindie->len - 1;
1826*4882a593Smuzhiyun 	pxtlv = (bcm_xtlv_t*)&iov_buf->data[0];
1827*4882a593Smuzhiyun 	pxtlv->id = WL_FILS_XTLV_IND_IE;
1828*4882a593Smuzhiyun 	pxtlv->len = filsindie->len;
1829*4882a593Smuzhiyun 	/* memcpy_s return check not required as buffer is allocated based on ie
1830*4882a593Smuzhiyun 	 * len
1831*4882a593Smuzhiyun 	 */
1832*4882a593Smuzhiyun 	(void)memcpy_s(pxtlv->data, filsindie->len, filsindie->data, filsindie->len);
1833*4882a593Smuzhiyun 
1834*4882a593Smuzhiyun 	err = wldev_iovar_setbuf(dev, "fils", iov_buf, iov_buf_size,
1835*4882a593Smuzhiyun 		cfg->ioctl_buf, WLC_IOCTL_SMLEN, &cfg->ioctl_buf_sync);
1836*4882a593Smuzhiyun 	if (unlikely(err)) {
1837*4882a593Smuzhiyun 		WL_ERR(("fils indication ioctl error (%d)\n", err));
1838*4882a593Smuzhiyun 		 goto exit;
1839*4882a593Smuzhiyun 	}
1840*4882a593Smuzhiyun 
1841*4882a593Smuzhiyun exit:
1842*4882a593Smuzhiyun 	if (err < 0) {
1843*4882a593Smuzhiyun 		WL_ERR(("FILS Ind setting error %d\n", err));
1844*4882a593Smuzhiyun 	}
1845*4882a593Smuzhiyun 
1846*4882a593Smuzhiyun 	if (iov_buf) {
1847*4882a593Smuzhiyun 		MFREE(cfg->osh, iov_buf, iov_buf_size);
1848*4882a593Smuzhiyun 	}
1849*4882a593Smuzhiyun 	return err;
1850*4882a593Smuzhiyun }
1851*4882a593Smuzhiyun 
1852*4882a593Smuzhiyun #ifdef MFP
1853*4882a593Smuzhiyun static int
wl_get_mfp_capability(u8 rsn_cap,u32 * wpa_auth,u32 * mfp_val)1854*4882a593Smuzhiyun wl_get_mfp_capability(u8 rsn_cap, u32 *wpa_auth, u32 *mfp_val)
1855*4882a593Smuzhiyun {
1856*4882a593Smuzhiyun 	u32 mfp = 0;
1857*4882a593Smuzhiyun 	if (rsn_cap & RSN_CAP_MFPR) {
1858*4882a593Smuzhiyun 		WL_DBG(("MFP Required \n"));
1859*4882a593Smuzhiyun 		mfp = WL_MFP_REQUIRED;
1860*4882a593Smuzhiyun 		/* Our firmware has requirement that WPA2_AUTH_PSK/WPA2_AUTH_UNSPECIFIED
1861*4882a593Smuzhiyun 		 * be set, if SHA256 OUI is to be included in the rsn ie.
1862*4882a593Smuzhiyun 		 */
1863*4882a593Smuzhiyun 		if (*wpa_auth & WPA2_AUTH_PSK_SHA256) {
1864*4882a593Smuzhiyun 			*wpa_auth |= WPA2_AUTH_PSK;
1865*4882a593Smuzhiyun 		} else if (*wpa_auth & WPA2_AUTH_1X_SHA256) {
1866*4882a593Smuzhiyun 			*wpa_auth |= WPA2_AUTH_UNSPECIFIED;
1867*4882a593Smuzhiyun 		}
1868*4882a593Smuzhiyun 	} else if (rsn_cap & RSN_CAP_MFPC) {
1869*4882a593Smuzhiyun 		WL_DBG(("MFP Capable \n"));
1870*4882a593Smuzhiyun 		mfp = WL_MFP_CAPABLE;
1871*4882a593Smuzhiyun 	}
1872*4882a593Smuzhiyun 
1873*4882a593Smuzhiyun 	/* Validate MFP */
1874*4882a593Smuzhiyun 	if ((*wpa_auth == WPA3_AUTH_SAE_PSK) && (mfp != WL_MFP_REQUIRED)) {
1875*4882a593Smuzhiyun 		WL_ERR(("MFPR should be set for SAE PSK. mfp:%d\n", mfp));
1876*4882a593Smuzhiyun 		return BCME_ERROR;
1877*4882a593Smuzhiyun 	} else if ((*wpa_auth == (WPA3_AUTH_SAE_PSK | WPA2_AUTH_PSK)) &&
1878*4882a593Smuzhiyun 		(mfp != WL_MFP_CAPABLE)) {
1879*4882a593Smuzhiyun 		WL_ERR(("mfp(%d) should be set to capable(%d) for SAE transition mode\n",
1880*4882a593Smuzhiyun 				mfp, WL_MFP_CAPABLE));
1881*4882a593Smuzhiyun 		return BCME_ERROR;
1882*4882a593Smuzhiyun 	}
1883*4882a593Smuzhiyun 
1884*4882a593Smuzhiyun 	*mfp_val = mfp;
1885*4882a593Smuzhiyun 	return BCME_OK;
1886*4882a593Smuzhiyun }
1887*4882a593Smuzhiyun #endif /* MFP */
1888*4882a593Smuzhiyun 
1889*4882a593Smuzhiyun static s32
wl_validate_wpa2ie(struct net_device * dev,const bcm_tlv_t * wpa2ie,s32 bssidx)1890*4882a593Smuzhiyun wl_validate_wpa2ie(struct net_device *dev, const bcm_tlv_t *wpa2ie, s32 bssidx)
1891*4882a593Smuzhiyun {
1892*4882a593Smuzhiyun 	s32 len = 0;
1893*4882a593Smuzhiyun 	s32 err = BCME_OK;
1894*4882a593Smuzhiyun 	u16 auth = 0; /* d11 open authentication */
1895*4882a593Smuzhiyun 	u32 wsec;
1896*4882a593Smuzhiyun 	u32 pval = 0;
1897*4882a593Smuzhiyun 	u32 gval = 0;
1898*4882a593Smuzhiyun 	u32 wpa_auth = 0;
1899*4882a593Smuzhiyun 	const wpa_suite_mcast_t *mcast;
1900*4882a593Smuzhiyun 	const wpa_suite_ucast_t *ucast;
1901*4882a593Smuzhiyun 	const wpa_suite_auth_key_mgmt_t *mgmt;
1902*4882a593Smuzhiyun 	const wpa_pmkid_list_t *pmkid;
1903*4882a593Smuzhiyun 	int cnt = 0;
1904*4882a593Smuzhiyun #ifdef MFP
1905*4882a593Smuzhiyun 	u32 mfp = 0;
1906*4882a593Smuzhiyun #endif /* MFP */
1907*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
1908*4882a593Smuzhiyun 	struct wl_security *sec = wl_read_prof(cfg, dev, WL_PROF_SEC);
1909*4882a593Smuzhiyun 
1910*4882a593Smuzhiyun 	u16 suite_count;
1911*4882a593Smuzhiyun 	u8 rsn_cap[2];
1912*4882a593Smuzhiyun 	u32 wme_bss_disable;
1913*4882a593Smuzhiyun 
1914*4882a593Smuzhiyun 	if (wpa2ie == NULL)
1915*4882a593Smuzhiyun 		goto exit;
1916*4882a593Smuzhiyun 
1917*4882a593Smuzhiyun 	WL_DBG(("Enter \n"));
1918*4882a593Smuzhiyun 	len =  wpa2ie->len - WPA2_VERSION_LEN;
1919*4882a593Smuzhiyun 	/* check the mcast cipher */
1920*4882a593Smuzhiyun 	mcast = (const wpa_suite_mcast_t *)&wpa2ie->data[WPA2_VERSION_LEN];
1921*4882a593Smuzhiyun 	switch (mcast->type) {
1922*4882a593Smuzhiyun 		case WPA_CIPHER_NONE:
1923*4882a593Smuzhiyun 			gval = 0;
1924*4882a593Smuzhiyun 			break;
1925*4882a593Smuzhiyun 		case WPA_CIPHER_WEP_40:
1926*4882a593Smuzhiyun 		case WPA_CIPHER_WEP_104:
1927*4882a593Smuzhiyun 			gval = WEP_ENABLED;
1928*4882a593Smuzhiyun 			break;
1929*4882a593Smuzhiyun 		case WPA_CIPHER_TKIP:
1930*4882a593Smuzhiyun 			gval = TKIP_ENABLED;
1931*4882a593Smuzhiyun 			break;
1932*4882a593Smuzhiyun 		case WPA_CIPHER_AES_CCM:
1933*4882a593Smuzhiyun 			gval = AES_ENABLED;
1934*4882a593Smuzhiyun 			break;
1935*4882a593Smuzhiyun 
1936*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
1937*4882a593Smuzhiyun 		case WAPI_CIPHER_SMS4:
1938*4882a593Smuzhiyun 			gval = SMS4_ENABLED;
1939*4882a593Smuzhiyun 			break;
1940*4882a593Smuzhiyun #endif
1941*4882a593Smuzhiyun 
1942*4882a593Smuzhiyun 		default:
1943*4882a593Smuzhiyun 			WL_ERR(("No Security Info\n"));
1944*4882a593Smuzhiyun 			break;
1945*4882a593Smuzhiyun 	}
1946*4882a593Smuzhiyun 	if ((len -= WPA_SUITE_LEN) <= 0)
1947*4882a593Smuzhiyun 		return BCME_BADLEN;
1948*4882a593Smuzhiyun 
1949*4882a593Smuzhiyun 	/* check the unicast cipher */
1950*4882a593Smuzhiyun 	ucast = (const wpa_suite_ucast_t *)&mcast[1];
1951*4882a593Smuzhiyun 	suite_count = ltoh16_ua(&ucast->count);
1952*4882a593Smuzhiyun 	switch (ucast->list[0].type) {
1953*4882a593Smuzhiyun 		case WPA_CIPHER_NONE:
1954*4882a593Smuzhiyun 			pval = 0;
1955*4882a593Smuzhiyun 			break;
1956*4882a593Smuzhiyun 		case WPA_CIPHER_WEP_40:
1957*4882a593Smuzhiyun 		case WPA_CIPHER_WEP_104:
1958*4882a593Smuzhiyun 			pval = WEP_ENABLED;
1959*4882a593Smuzhiyun 			break;
1960*4882a593Smuzhiyun 		case WPA_CIPHER_TKIP:
1961*4882a593Smuzhiyun 			pval = TKIP_ENABLED;
1962*4882a593Smuzhiyun 			break;
1963*4882a593Smuzhiyun 		case WPA_CIPHER_AES_CCM:
1964*4882a593Smuzhiyun 			pval = AES_ENABLED;
1965*4882a593Smuzhiyun 			break;
1966*4882a593Smuzhiyun 
1967*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
1968*4882a593Smuzhiyun 		case WAPI_CIPHER_SMS4:
1969*4882a593Smuzhiyun 			pval = SMS4_ENABLED;
1970*4882a593Smuzhiyun 			break;
1971*4882a593Smuzhiyun #endif
1972*4882a593Smuzhiyun 
1973*4882a593Smuzhiyun 		default:
1974*4882a593Smuzhiyun 			WL_ERR(("No Security Info\n"));
1975*4882a593Smuzhiyun 	}
1976*4882a593Smuzhiyun 	if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) <= 0)
1977*4882a593Smuzhiyun 		return BCME_BADLEN;
1978*4882a593Smuzhiyun 
1979*4882a593Smuzhiyun 	/* FOR WPS , set SEC_OW_ENABLED */
1980*4882a593Smuzhiyun 	wsec = (pval | gval | SES_OW_ENABLED);
1981*4882a593Smuzhiyun 	/* check the AKM */
1982*4882a593Smuzhiyun 	mgmt = (const wpa_suite_auth_key_mgmt_t *)&ucast->list[suite_count];
1983*4882a593Smuzhiyun 	suite_count = cnt = ltoh16_ua(&mgmt->count);
1984*4882a593Smuzhiyun 	while (cnt--) {
1985*4882a593Smuzhiyun 		if (bcmp(mgmt->list[cnt].oui, WFA_OUI, WFA_OUI_LEN) == 0) {
1986*4882a593Smuzhiyun 			switch (mgmt->list[cnt].type) {
1987*4882a593Smuzhiyun 			case RSN_AKM_DPP:
1988*4882a593Smuzhiyun 				wpa_auth |= WPA3_AUTH_DPP_AKM;
1989*4882a593Smuzhiyun 				break;
1990*4882a593Smuzhiyun 			default:
1991*4882a593Smuzhiyun 				WL_ERR(("No Key Mgmt Info in WFA_OUI\n"));
1992*4882a593Smuzhiyun 			}
1993*4882a593Smuzhiyun 		} else {
1994*4882a593Smuzhiyun 			switch (mgmt->list[cnt].type) {
1995*4882a593Smuzhiyun 			case RSN_AKM_NONE:
1996*4882a593Smuzhiyun 				wpa_auth |= WPA_AUTH_NONE;
1997*4882a593Smuzhiyun 				break;
1998*4882a593Smuzhiyun 			case RSN_AKM_UNSPECIFIED:
1999*4882a593Smuzhiyun 				wpa_auth |= WPA2_AUTH_UNSPECIFIED;
2000*4882a593Smuzhiyun 				break;
2001*4882a593Smuzhiyun 			case RSN_AKM_PSK:
2002*4882a593Smuzhiyun 				wpa_auth |= WPA2_AUTH_PSK;
2003*4882a593Smuzhiyun 				break;
2004*4882a593Smuzhiyun #ifdef MFP
2005*4882a593Smuzhiyun 			case RSN_AKM_MFP_PSK:
2006*4882a593Smuzhiyun 				wpa_auth |= WPA2_AUTH_PSK_SHA256;
2007*4882a593Smuzhiyun 				break;
2008*4882a593Smuzhiyun 			case RSN_AKM_MFP_1X:
2009*4882a593Smuzhiyun 				wpa_auth |= WPA2_AUTH_1X_SHA256;
2010*4882a593Smuzhiyun 				break;
2011*4882a593Smuzhiyun 			case RSN_AKM_FILS_SHA256:
2012*4882a593Smuzhiyun 				wpa_auth |= WPA2_AUTH_FILS_SHA256;
2013*4882a593Smuzhiyun 				break;
2014*4882a593Smuzhiyun 			case RSN_AKM_FILS_SHA384:
2015*4882a593Smuzhiyun 				wpa_auth |= WPA2_AUTH_FILS_SHA384;
2016*4882a593Smuzhiyun 				break;
2017*4882a593Smuzhiyun #if defined(WL_SAE) || defined(WL_CLIENT_SAE)
2018*4882a593Smuzhiyun 			case RSN_AKM_SAE_PSK:
2019*4882a593Smuzhiyun 				wpa_auth |= WPA3_AUTH_SAE_PSK;
2020*4882a593Smuzhiyun 				break;
2021*4882a593Smuzhiyun #endif /* WL_SAE || WL_CLIENT_SAE */
2022*4882a593Smuzhiyun #endif /* MFP */
2023*4882a593Smuzhiyun 			default:
2024*4882a593Smuzhiyun 				WL_ERR(("No Key Mgmt Info\n"));
2025*4882a593Smuzhiyun 			}
2026*4882a593Smuzhiyun 		}
2027*4882a593Smuzhiyun 	}
2028*4882a593Smuzhiyun 	if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) >= RSN_CAP_LEN) {
2029*4882a593Smuzhiyun 		rsn_cap[0] = *(const u8 *)&mgmt->list[suite_count];
2030*4882a593Smuzhiyun 		rsn_cap[1] = *((const u8 *)&mgmt->list[suite_count] + 1);
2031*4882a593Smuzhiyun 
2032*4882a593Smuzhiyun 		if (rsn_cap[0] & (RSN_CAP_16_REPLAY_CNTRS << RSN_CAP_PTK_REPLAY_CNTR_SHIFT)) {
2033*4882a593Smuzhiyun 			wme_bss_disable = 0;
2034*4882a593Smuzhiyun 		} else {
2035*4882a593Smuzhiyun 			wme_bss_disable = 1;
2036*4882a593Smuzhiyun 		}
2037*4882a593Smuzhiyun 
2038*4882a593Smuzhiyun #ifdef MFP
2039*4882a593Smuzhiyun 		if (wl_get_mfp_capability(rsn_cap[0], &wpa_auth, &mfp) != BCME_OK) {
2040*4882a593Smuzhiyun 			WL_ERR(("mfp configuration invalid. rsn_cap:0x%x\n", rsn_cap[0]));
2041*4882a593Smuzhiyun 			return BCME_ERROR;
2042*4882a593Smuzhiyun 		}
2043*4882a593Smuzhiyun #endif /* MFP */
2044*4882a593Smuzhiyun 
2045*4882a593Smuzhiyun 		/* set wme_bss_disable to sync RSN Capabilities */
2046*4882a593Smuzhiyun 		err = wldev_iovar_setint_bsscfg(dev, "wme_bss_disable", wme_bss_disable, bssidx);
2047*4882a593Smuzhiyun 		if (err < 0) {
2048*4882a593Smuzhiyun 			WL_ERR(("wme_bss_disable error %d\n", err));
2049*4882a593Smuzhiyun 			return BCME_ERROR;
2050*4882a593Smuzhiyun 		}
2051*4882a593Smuzhiyun 	} else {
2052*4882a593Smuzhiyun 		WL_DBG(("There is no RSN Capabilities. remained len %d\n", len));
2053*4882a593Smuzhiyun 	}
2054*4882a593Smuzhiyun 
2055*4882a593Smuzhiyun 	len -= RSN_CAP_LEN;
2056*4882a593Smuzhiyun 	if (len >= WPA2_PMKID_COUNT_LEN) {
2057*4882a593Smuzhiyun 		pmkid = (const wpa_pmkid_list_t *)
2058*4882a593Smuzhiyun 		        ((const u8 *)&mgmt->list[suite_count] + RSN_CAP_LEN);
2059*4882a593Smuzhiyun 		cnt = ltoh16_ua(&pmkid->count);
2060*4882a593Smuzhiyun 		if (cnt != 0) {
2061*4882a593Smuzhiyun 			WL_ERR(("AP has non-zero PMKID count. Wrong!\n"));
2062*4882a593Smuzhiyun 			return BCME_ERROR;
2063*4882a593Smuzhiyun 		}
2064*4882a593Smuzhiyun 		/* since PMKID cnt is known to be 0 for AP, */
2065*4882a593Smuzhiyun 		/* so don't bother to send down this info to firmware */
2066*4882a593Smuzhiyun 	}
2067*4882a593Smuzhiyun 
2068*4882a593Smuzhiyun #ifdef MFP
2069*4882a593Smuzhiyun 	len -= WPA2_PMKID_COUNT_LEN;
2070*4882a593Smuzhiyun 	if (len >= WPA_SUITE_LEN) {
2071*4882a593Smuzhiyun 		cfg->bip_pos =
2072*4882a593Smuzhiyun 		        (const u8 *)&mgmt->list[suite_count] + RSN_CAP_LEN + WPA2_PMKID_COUNT_LEN;
2073*4882a593Smuzhiyun 	} else {
2074*4882a593Smuzhiyun 		cfg->bip_pos = NULL;
2075*4882a593Smuzhiyun 	}
2076*4882a593Smuzhiyun #endif
2077*4882a593Smuzhiyun 
2078*4882a593Smuzhiyun 	/* set auth */
2079*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx);
2080*4882a593Smuzhiyun 	if (err < 0) {
2081*4882a593Smuzhiyun 		WL_ERR(("auth error %d\n", err));
2082*4882a593Smuzhiyun 		return BCME_ERROR;
2083*4882a593Smuzhiyun 	}
2084*4882a593Smuzhiyun 
2085*4882a593Smuzhiyun 	/* set wsec */
2086*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx);
2087*4882a593Smuzhiyun 	if (err < 0) {
2088*4882a593Smuzhiyun 		WL_ERR(("wsec error %d\n", err));
2089*4882a593Smuzhiyun 		return BCME_ERROR;
2090*4882a593Smuzhiyun 	}
2091*4882a593Smuzhiyun 
2092*4882a593Smuzhiyun #ifdef MFP
2093*4882a593Smuzhiyun 	cfg->mfp_mode = mfp;
2094*4882a593Smuzhiyun #endif /* MFP */
2095*4882a593Smuzhiyun 
2096*4882a593Smuzhiyun 	/* set upper-layer auth */
2097*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx);
2098*4882a593Smuzhiyun 	if (err < 0) {
2099*4882a593Smuzhiyun 		WL_ERR(("wpa_auth error %d\n", err));
2100*4882a593Smuzhiyun 		return BCME_ERROR;
2101*4882a593Smuzhiyun 	}
2102*4882a593Smuzhiyun 
2103*4882a593Smuzhiyun 	if (sec) {
2104*4882a593Smuzhiyun 		/* store applied sec settings */
2105*4882a593Smuzhiyun 		sec->fw_wpa_auth = wpa_auth;
2106*4882a593Smuzhiyun 		sec->fw_wsec = wsec;
2107*4882a593Smuzhiyun 		sec->fw_auth = auth;
2108*4882a593Smuzhiyun #ifdef MFP
2109*4882a593Smuzhiyun 		sec->fw_mfp = mfp;
2110*4882a593Smuzhiyun #endif /* mfp */
2111*4882a593Smuzhiyun 	}
2112*4882a593Smuzhiyun exit:
2113*4882a593Smuzhiyun 	return 0;
2114*4882a593Smuzhiyun }
2115*4882a593Smuzhiyun 
2116*4882a593Smuzhiyun static s32
wl_validate_wpaie(struct net_device * dev,const wpa_ie_fixed_t * wpaie,s32 bssidx)2117*4882a593Smuzhiyun wl_validate_wpaie(struct net_device *dev, const wpa_ie_fixed_t *wpaie, s32 bssidx)
2118*4882a593Smuzhiyun {
2119*4882a593Smuzhiyun 	const wpa_suite_mcast_t *mcast;
2120*4882a593Smuzhiyun 	const wpa_suite_ucast_t *ucast;
2121*4882a593Smuzhiyun 	const wpa_suite_auth_key_mgmt_t *mgmt;
2122*4882a593Smuzhiyun 	u16 auth = 0; /* d11 open authentication */
2123*4882a593Smuzhiyun 	u16 count;
2124*4882a593Smuzhiyun 	s32 err = BCME_OK;
2125*4882a593Smuzhiyun 	s32 len = 0;
2126*4882a593Smuzhiyun 	u32 i;
2127*4882a593Smuzhiyun 	u32 wsec;
2128*4882a593Smuzhiyun 	u32 pval = 0;
2129*4882a593Smuzhiyun 	u32 gval = 0;
2130*4882a593Smuzhiyun 	u32 wpa_auth = 0;
2131*4882a593Smuzhiyun 	u32 tmp = 0;
2132*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
2133*4882a593Smuzhiyun 	struct wl_security *sec = wl_read_prof(cfg, dev, WL_PROF_SEC);
2134*4882a593Smuzhiyun 
2135*4882a593Smuzhiyun 	if (wpaie == NULL)
2136*4882a593Smuzhiyun 		goto exit;
2137*4882a593Smuzhiyun 	WL_DBG(("Enter \n"));
2138*4882a593Smuzhiyun 	len = wpaie->length;    /* value length */
2139*4882a593Smuzhiyun 	len -= WPA_IE_TAG_FIXED_LEN;
2140*4882a593Smuzhiyun 	/* check for multicast cipher suite */
2141*4882a593Smuzhiyun 	if (len < WPA_SUITE_LEN) {
2142*4882a593Smuzhiyun 		WL_INFORM_MEM(("no multicast cipher suite\n"));
2143*4882a593Smuzhiyun 		goto exit;
2144*4882a593Smuzhiyun 	}
2145*4882a593Smuzhiyun 
2146*4882a593Smuzhiyun 	/* pick up multicast cipher */
2147*4882a593Smuzhiyun 	mcast = (const wpa_suite_mcast_t *)&wpaie[1];
2148*4882a593Smuzhiyun 	len -= WPA_SUITE_LEN;
2149*4882a593Smuzhiyun 	if (!bcmp(mcast->oui, WPA_OUI, WPA_OUI_LEN)) {
2150*4882a593Smuzhiyun 		if (IS_WPA_CIPHER(mcast->type)) {
2151*4882a593Smuzhiyun 			tmp = 0;
2152*4882a593Smuzhiyun 			switch (mcast->type) {
2153*4882a593Smuzhiyun 				case WPA_CIPHER_NONE:
2154*4882a593Smuzhiyun 					tmp = 0;
2155*4882a593Smuzhiyun 					break;
2156*4882a593Smuzhiyun 				case WPA_CIPHER_WEP_40:
2157*4882a593Smuzhiyun 				case WPA_CIPHER_WEP_104:
2158*4882a593Smuzhiyun 					tmp = WEP_ENABLED;
2159*4882a593Smuzhiyun 					break;
2160*4882a593Smuzhiyun 				case WPA_CIPHER_TKIP:
2161*4882a593Smuzhiyun 					tmp = TKIP_ENABLED;
2162*4882a593Smuzhiyun 					break;
2163*4882a593Smuzhiyun 				case WPA_CIPHER_AES_CCM:
2164*4882a593Smuzhiyun 					tmp = AES_ENABLED;
2165*4882a593Smuzhiyun 					break;
2166*4882a593Smuzhiyun 				default:
2167*4882a593Smuzhiyun 					WL_ERR(("No Security Info\n"));
2168*4882a593Smuzhiyun 			}
2169*4882a593Smuzhiyun 			gval |= tmp;
2170*4882a593Smuzhiyun 		}
2171*4882a593Smuzhiyun 	}
2172*4882a593Smuzhiyun 	/* Check for unicast suite(s) */
2173*4882a593Smuzhiyun 	if (len < WPA_IE_SUITE_COUNT_LEN) {
2174*4882a593Smuzhiyun 		WL_INFORM_MEM(("no unicast suite\n"));
2175*4882a593Smuzhiyun 		goto exit;
2176*4882a593Smuzhiyun 	}
2177*4882a593Smuzhiyun 	/* walk thru unicast cipher list and pick up what we recognize */
2178*4882a593Smuzhiyun 	ucast = (const wpa_suite_ucast_t *)&mcast[1];
2179*4882a593Smuzhiyun 	count = ltoh16_ua(&ucast->count);
2180*4882a593Smuzhiyun 	len -= WPA_IE_SUITE_COUNT_LEN;
2181*4882a593Smuzhiyun 	for (i = 0; i < count && len >= WPA_SUITE_LEN;
2182*4882a593Smuzhiyun 		i++, len -= WPA_SUITE_LEN) {
2183*4882a593Smuzhiyun 		if (!bcmp(ucast->list[i].oui, WPA_OUI, WPA_OUI_LEN)) {
2184*4882a593Smuzhiyun 			if (IS_WPA_CIPHER(ucast->list[i].type)) {
2185*4882a593Smuzhiyun 				tmp = 0;
2186*4882a593Smuzhiyun 				switch (ucast->list[i].type) {
2187*4882a593Smuzhiyun 					case WPA_CIPHER_NONE:
2188*4882a593Smuzhiyun 						tmp = 0;
2189*4882a593Smuzhiyun 						break;
2190*4882a593Smuzhiyun 					case WPA_CIPHER_WEP_40:
2191*4882a593Smuzhiyun 					case WPA_CIPHER_WEP_104:
2192*4882a593Smuzhiyun 						tmp = WEP_ENABLED;
2193*4882a593Smuzhiyun 						break;
2194*4882a593Smuzhiyun 					case WPA_CIPHER_TKIP:
2195*4882a593Smuzhiyun 						tmp = TKIP_ENABLED;
2196*4882a593Smuzhiyun 						break;
2197*4882a593Smuzhiyun 					case WPA_CIPHER_AES_CCM:
2198*4882a593Smuzhiyun 						tmp = AES_ENABLED;
2199*4882a593Smuzhiyun 						break;
2200*4882a593Smuzhiyun 					default:
2201*4882a593Smuzhiyun 						WL_ERR(("No Security Info\n"));
2202*4882a593Smuzhiyun 				}
2203*4882a593Smuzhiyun 				pval |= tmp;
2204*4882a593Smuzhiyun 			}
2205*4882a593Smuzhiyun 		}
2206*4882a593Smuzhiyun 	}
2207*4882a593Smuzhiyun 	len -= (count - i) * WPA_SUITE_LEN;
2208*4882a593Smuzhiyun 	/* Check for auth key management suite(s) */
2209*4882a593Smuzhiyun 	if (len < WPA_IE_SUITE_COUNT_LEN) {
2210*4882a593Smuzhiyun 		WL_INFORM_MEM((" no auth key mgmt suite\n"));
2211*4882a593Smuzhiyun 		goto exit;
2212*4882a593Smuzhiyun 	}
2213*4882a593Smuzhiyun 	/* walk thru auth management suite list and pick up what we recognize */
2214*4882a593Smuzhiyun 	mgmt = (const wpa_suite_auth_key_mgmt_t *)&ucast->list[count];
2215*4882a593Smuzhiyun 	count = ltoh16_ua(&mgmt->count);
2216*4882a593Smuzhiyun 	len -= WPA_IE_SUITE_COUNT_LEN;
2217*4882a593Smuzhiyun 	for (i = 0; i < count && len >= WPA_SUITE_LEN;
2218*4882a593Smuzhiyun 		i++, len -= WPA_SUITE_LEN) {
2219*4882a593Smuzhiyun 		if (!bcmp(mgmt->list[i].oui, WPA_OUI, WPA_OUI_LEN)) {
2220*4882a593Smuzhiyun 			if (IS_WPA_AKM(mgmt->list[i].type)) {
2221*4882a593Smuzhiyun 				tmp = 0;
2222*4882a593Smuzhiyun 				switch (mgmt->list[i].type) {
2223*4882a593Smuzhiyun 					case RSN_AKM_NONE:
2224*4882a593Smuzhiyun 						tmp = WPA_AUTH_NONE;
2225*4882a593Smuzhiyun 						break;
2226*4882a593Smuzhiyun 					case RSN_AKM_UNSPECIFIED:
2227*4882a593Smuzhiyun 						tmp = WPA_AUTH_UNSPECIFIED;
2228*4882a593Smuzhiyun 						break;
2229*4882a593Smuzhiyun 					case RSN_AKM_PSK:
2230*4882a593Smuzhiyun 						tmp = WPA_AUTH_PSK;
2231*4882a593Smuzhiyun 						break;
2232*4882a593Smuzhiyun 					default:
2233*4882a593Smuzhiyun 						WL_ERR(("No Key Mgmt Info\n"));
2234*4882a593Smuzhiyun 				}
2235*4882a593Smuzhiyun 				wpa_auth |= tmp;
2236*4882a593Smuzhiyun 			}
2237*4882a593Smuzhiyun 		}
2238*4882a593Smuzhiyun 
2239*4882a593Smuzhiyun 	}
2240*4882a593Smuzhiyun 	/* FOR WPS , set SEC_OW_ENABLED */
2241*4882a593Smuzhiyun 	wsec = (pval | gval | SES_OW_ENABLED);
2242*4882a593Smuzhiyun 	/* set auth */
2243*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx);
2244*4882a593Smuzhiyun 	if (err < 0) {
2245*4882a593Smuzhiyun 		WL_ERR(("auth error %d\n", err));
2246*4882a593Smuzhiyun 		return BCME_ERROR;
2247*4882a593Smuzhiyun 	}
2248*4882a593Smuzhiyun 	/* set wsec */
2249*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx);
2250*4882a593Smuzhiyun 	if (err < 0) {
2251*4882a593Smuzhiyun 		WL_ERR(("wsec error %d\n", err));
2252*4882a593Smuzhiyun 		return BCME_ERROR;
2253*4882a593Smuzhiyun 	}
2254*4882a593Smuzhiyun 	/* set upper-layer auth */
2255*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx);
2256*4882a593Smuzhiyun 	if (err < 0) {
2257*4882a593Smuzhiyun 		WL_ERR(("wpa_auth error %d\n", err));
2258*4882a593Smuzhiyun 		return BCME_ERROR;
2259*4882a593Smuzhiyun 	}
2260*4882a593Smuzhiyun 
2261*4882a593Smuzhiyun 	if (sec) {
2262*4882a593Smuzhiyun 		/* store applied sec settings */
2263*4882a593Smuzhiyun 		sec->fw_wpa_auth = wpa_auth;
2264*4882a593Smuzhiyun 		sec->fw_wsec = wsec;
2265*4882a593Smuzhiyun 		sec->fw_auth = auth;
2266*4882a593Smuzhiyun 	}
2267*4882a593Smuzhiyun 
2268*4882a593Smuzhiyun exit:
2269*4882a593Smuzhiyun 	return 0;
2270*4882a593Smuzhiyun }
2271*4882a593Smuzhiyun 
2272*4882a593Smuzhiyun #if defined(SUPPORT_SOFTAP_WPAWPA2_MIXED)
wl_get_cipher_type(uint8 type)2273*4882a593Smuzhiyun static u32 wl_get_cipher_type(uint8 type)
2274*4882a593Smuzhiyun {
2275*4882a593Smuzhiyun 	u32 ret = 0;
2276*4882a593Smuzhiyun 	switch (type) {
2277*4882a593Smuzhiyun 		case WPA_CIPHER_NONE:
2278*4882a593Smuzhiyun 			ret = 0;
2279*4882a593Smuzhiyun 			break;
2280*4882a593Smuzhiyun 		case WPA_CIPHER_WEP_40:
2281*4882a593Smuzhiyun 		case WPA_CIPHER_WEP_104:
2282*4882a593Smuzhiyun 			ret = WEP_ENABLED;
2283*4882a593Smuzhiyun 			break;
2284*4882a593Smuzhiyun 		case WPA_CIPHER_TKIP:
2285*4882a593Smuzhiyun 			ret = TKIP_ENABLED;
2286*4882a593Smuzhiyun 			break;
2287*4882a593Smuzhiyun 		case WPA_CIPHER_AES_CCM:
2288*4882a593Smuzhiyun 			ret = AES_ENABLED;
2289*4882a593Smuzhiyun 			break;
2290*4882a593Smuzhiyun 
2291*4882a593Smuzhiyun #ifdef BCMWAPI_WPI
2292*4882a593Smuzhiyun 		case WAPI_CIPHER_SMS4:
2293*4882a593Smuzhiyun 			ret = SMS4_ENABLED;
2294*4882a593Smuzhiyun 			break;
2295*4882a593Smuzhiyun #endif
2296*4882a593Smuzhiyun 
2297*4882a593Smuzhiyun 		default:
2298*4882a593Smuzhiyun 			WL_ERR(("No Security Info\n"));
2299*4882a593Smuzhiyun 	}
2300*4882a593Smuzhiyun 	return ret;
2301*4882a593Smuzhiyun }
2302*4882a593Smuzhiyun 
wl_get_suite_auth_key_mgmt_type(uint8 type,const wpa_suite_mcast_t * mcast)2303*4882a593Smuzhiyun static u32 wl_get_suite_auth_key_mgmt_type(uint8 type, const wpa_suite_mcast_t *mcast)
2304*4882a593Smuzhiyun {
2305*4882a593Smuzhiyun 	u32 ret = 0;
2306*4882a593Smuzhiyun 	u32 is_wpa2 = 0;
2307*4882a593Smuzhiyun 
2308*4882a593Smuzhiyun 	if (!bcmp(mcast->oui, WPA2_OUI, WPA2_OUI_LEN)) {
2309*4882a593Smuzhiyun 		is_wpa2 = 1;
2310*4882a593Smuzhiyun 	}
2311*4882a593Smuzhiyun 
2312*4882a593Smuzhiyun 	WL_INFORM_MEM(("%s, type = %d\n", is_wpa2 ? "WPA2":"WPA", type));
2313*4882a593Smuzhiyun 	if (bcmp(mcast->oui, WFA_OUI, WFA_OUI_LEN) == 0) {
2314*4882a593Smuzhiyun 		switch (type) {
2315*4882a593Smuzhiyun 			case RSN_AKM_DPP:
2316*4882a593Smuzhiyun 				ret = WPA3_AUTH_DPP_AKM;
2317*4882a593Smuzhiyun 				break;
2318*4882a593Smuzhiyun 			default:
2319*4882a593Smuzhiyun 				WL_ERR(("No Key Mgmt Info in WFA_OUI\n"));
2320*4882a593Smuzhiyun 		}
2321*4882a593Smuzhiyun 	} else {
2322*4882a593Smuzhiyun 		switch (type) {
2323*4882a593Smuzhiyun 		case RSN_AKM_NONE:
2324*4882a593Smuzhiyun 			/* For WPA and WPA2, AUTH_NONE is common */
2325*4882a593Smuzhiyun 			ret = WPA_AUTH_NONE;
2326*4882a593Smuzhiyun 			break;
2327*4882a593Smuzhiyun 		case RSN_AKM_UNSPECIFIED:
2328*4882a593Smuzhiyun 			if (is_wpa2) {
2329*4882a593Smuzhiyun 				ret = WPA2_AUTH_UNSPECIFIED;
2330*4882a593Smuzhiyun 			} else {
2331*4882a593Smuzhiyun 				ret = WPA_AUTH_UNSPECIFIED;
2332*4882a593Smuzhiyun 			}
2333*4882a593Smuzhiyun 			break;
2334*4882a593Smuzhiyun 		case RSN_AKM_PSK:
2335*4882a593Smuzhiyun 			if (is_wpa2) {
2336*4882a593Smuzhiyun 				ret = WPA2_AUTH_PSK;
2337*4882a593Smuzhiyun 			} else {
2338*4882a593Smuzhiyun 				ret = WPA_AUTH_PSK;
2339*4882a593Smuzhiyun 			}
2340*4882a593Smuzhiyun 			break;
2341*4882a593Smuzhiyun #ifdef WL_SAE
2342*4882a593Smuzhiyun 		case RSN_AKM_SAE_PSK:
2343*4882a593Smuzhiyun 			ret = WPA3_AUTH_SAE_PSK;
2344*4882a593Smuzhiyun 			break;
2345*4882a593Smuzhiyun #endif /* WL_SAE */
2346*4882a593Smuzhiyun 		default:
2347*4882a593Smuzhiyun 			WL_ERR(("No Key Mgmt Info\n"));
2348*4882a593Smuzhiyun 		}
2349*4882a593Smuzhiyun 	}
2350*4882a593Smuzhiyun 	return ret;
2351*4882a593Smuzhiyun }
2352*4882a593Smuzhiyun 
2353*4882a593Smuzhiyun static s32
wl_validate_wpaie_wpa2ie(struct net_device * dev,const wpa_ie_fixed_t * wpaie,const bcm_tlv_t * wpa2ie,s32 bssidx)2354*4882a593Smuzhiyun wl_validate_wpaie_wpa2ie(struct net_device *dev, const wpa_ie_fixed_t *wpaie,
2355*4882a593Smuzhiyun 	const bcm_tlv_t *wpa2ie, s32 bssidx)
2356*4882a593Smuzhiyun {
2357*4882a593Smuzhiyun 	const wpa_suite_mcast_t *mcast;
2358*4882a593Smuzhiyun 	const wpa_suite_ucast_t *ucast;
2359*4882a593Smuzhiyun 	const wpa_suite_auth_key_mgmt_t *mgmt;
2360*4882a593Smuzhiyun 	u16 auth = 0; /* d11 open authentication */
2361*4882a593Smuzhiyun 	u16 count;
2362*4882a593Smuzhiyun 	s32 err = BCME_OK;
2363*4882a593Smuzhiyun 	u32 wme_bss_disable;
2364*4882a593Smuzhiyun 	u16 suite_count;
2365*4882a593Smuzhiyun 	u8 rsn_cap[2];
2366*4882a593Smuzhiyun 	s32 len = 0;
2367*4882a593Smuzhiyun 	u32 i;
2368*4882a593Smuzhiyun 	u32 wsec1, wsec2, wsec;
2369*4882a593Smuzhiyun 	u32 pval = 0;
2370*4882a593Smuzhiyun 	u32 gval = 0;
2371*4882a593Smuzhiyun 	u32 wpa_auth = 0;
2372*4882a593Smuzhiyun 	u32 wpa_auth1 = 0;
2373*4882a593Smuzhiyun 	u32 wpa_auth2 = 0;
2374*4882a593Smuzhiyun #ifdef MFP
2375*4882a593Smuzhiyun 	u32 mfp = 0;
2376*4882a593Smuzhiyun #endif /* MFP */
2377*4882a593Smuzhiyun 
2378*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
2379*4882a593Smuzhiyun 	struct wl_security *sec = wl_read_prof(cfg, dev, WL_PROF_SEC);
2380*4882a593Smuzhiyun 
2381*4882a593Smuzhiyun 	if (wpaie == NULL || wpa2ie == NULL)
2382*4882a593Smuzhiyun 		goto exit;
2383*4882a593Smuzhiyun 
2384*4882a593Smuzhiyun 	WL_DBG(("Enter \n"));
2385*4882a593Smuzhiyun 	len = wpaie->length;    /* value length */
2386*4882a593Smuzhiyun 	len -= WPA_IE_TAG_FIXED_LEN;
2387*4882a593Smuzhiyun 	/* check for multicast cipher suite */
2388*4882a593Smuzhiyun 	if (len < WPA_SUITE_LEN) {
2389*4882a593Smuzhiyun 		WL_INFORM_MEM(("no multicast cipher suite\n"));
2390*4882a593Smuzhiyun 		goto exit;
2391*4882a593Smuzhiyun 	}
2392*4882a593Smuzhiyun 
2393*4882a593Smuzhiyun 	/* pick up multicast cipher */
2394*4882a593Smuzhiyun 	mcast = (const wpa_suite_mcast_t *)&wpaie[1];
2395*4882a593Smuzhiyun 	len -= WPA_SUITE_LEN;
2396*4882a593Smuzhiyun 	if (!bcmp(mcast->oui, WPA_OUI, WPA_OUI_LEN)) {
2397*4882a593Smuzhiyun 		if (IS_WPA_CIPHER(mcast->type)) {
2398*4882a593Smuzhiyun 			gval |= wl_get_cipher_type(mcast->type);
2399*4882a593Smuzhiyun 		}
2400*4882a593Smuzhiyun 	}
2401*4882a593Smuzhiyun 	WL_DBG(("\nwpa ie validate\n"));
2402*4882a593Smuzhiyun 	WL_DBG(("wpa ie mcast cipher = 0x%X\n", gval));
2403*4882a593Smuzhiyun 
2404*4882a593Smuzhiyun 	/* Check for unicast suite(s) */
2405*4882a593Smuzhiyun 	if (len < WPA_IE_SUITE_COUNT_LEN) {
2406*4882a593Smuzhiyun 		WL_INFORM_MEM(("no unicast suite\n"));
2407*4882a593Smuzhiyun 		goto exit;
2408*4882a593Smuzhiyun 	}
2409*4882a593Smuzhiyun 
2410*4882a593Smuzhiyun 	/* walk thru unicast cipher list and pick up what we recognize */
2411*4882a593Smuzhiyun 	ucast = (const wpa_suite_ucast_t *)&mcast[1];
2412*4882a593Smuzhiyun 	count = ltoh16_ua(&ucast->count);
2413*4882a593Smuzhiyun 	len -= WPA_IE_SUITE_COUNT_LEN;
2414*4882a593Smuzhiyun 	for (i = 0; i < count && len >= WPA_SUITE_LEN;
2415*4882a593Smuzhiyun 		i++, len -= WPA_SUITE_LEN) {
2416*4882a593Smuzhiyun 		if (!bcmp(ucast->list[i].oui, WPA_OUI, WPA_OUI_LEN)) {
2417*4882a593Smuzhiyun 			if (IS_WPA_CIPHER(ucast->list[i].type)) {
2418*4882a593Smuzhiyun 				pval |= wl_get_cipher_type(ucast->list[i].type);
2419*4882a593Smuzhiyun 			}
2420*4882a593Smuzhiyun 		}
2421*4882a593Smuzhiyun 	}
2422*4882a593Smuzhiyun 	WL_ERR(("wpa ie ucast count =%d, cipher = 0x%X\n", count, pval));
2423*4882a593Smuzhiyun 
2424*4882a593Smuzhiyun 	/* FOR WPS , set SEC_OW_ENABLED */
2425*4882a593Smuzhiyun 	wsec1 = (pval | gval | SES_OW_ENABLED);
2426*4882a593Smuzhiyun 	WL_ERR(("wpa ie wsec = 0x%X\n", wsec1));
2427*4882a593Smuzhiyun 
2428*4882a593Smuzhiyun 	len -= (count - i) * WPA_SUITE_LEN;
2429*4882a593Smuzhiyun 	/* Check for auth key management suite(s) */
2430*4882a593Smuzhiyun 	if (len < WPA_IE_SUITE_COUNT_LEN) {
2431*4882a593Smuzhiyun 		WL_INFORM_MEM((" no auth key mgmt suite\n"));
2432*4882a593Smuzhiyun 		goto exit;
2433*4882a593Smuzhiyun 	}
2434*4882a593Smuzhiyun 	/* walk thru auth management suite list and pick up what we recognize */
2435*4882a593Smuzhiyun 	mgmt = (const wpa_suite_auth_key_mgmt_t *)&ucast->list[count];
2436*4882a593Smuzhiyun 	count = ltoh16_ua(&mgmt->count);
2437*4882a593Smuzhiyun 	len -= WPA_IE_SUITE_COUNT_LEN;
2438*4882a593Smuzhiyun 	for (i = 0; i < count && len >= WPA_SUITE_LEN;
2439*4882a593Smuzhiyun 		i++, len -= WPA_SUITE_LEN) {
2440*4882a593Smuzhiyun 		if (!bcmp(mgmt->list[i].oui, WPA_OUI, WPA_OUI_LEN)) {
2441*4882a593Smuzhiyun 			if (IS_WPA_AKM(mgmt->list[i].type)) {
2442*4882a593Smuzhiyun 				wpa_auth1 |=
2443*4882a593Smuzhiyun 					wl_get_suite_auth_key_mgmt_type(mgmt->list[i].type, mcast);
2444*4882a593Smuzhiyun 			}
2445*4882a593Smuzhiyun 		}
2446*4882a593Smuzhiyun 
2447*4882a593Smuzhiyun 	}
2448*4882a593Smuzhiyun 	WL_ERR(("wpa ie wpa_suite_auth_key_mgmt count=%d, key_mgmt = 0x%X\n", count, wpa_auth1));
2449*4882a593Smuzhiyun 	WL_ERR(("\nwpa2 ie validate\n"));
2450*4882a593Smuzhiyun 
2451*4882a593Smuzhiyun 	pval = 0;
2452*4882a593Smuzhiyun 	gval = 0;
2453*4882a593Smuzhiyun 	len =  wpa2ie->len;
2454*4882a593Smuzhiyun 	/* check the mcast cipher */
2455*4882a593Smuzhiyun 	mcast = (const wpa_suite_mcast_t *)&wpa2ie->data[WPA2_VERSION_LEN];
2456*4882a593Smuzhiyun 	gval = wl_get_cipher_type(mcast->type);
2457*4882a593Smuzhiyun 
2458*4882a593Smuzhiyun 	WL_ERR(("wpa2 ie mcast cipher = 0x%X\n", gval));
2459*4882a593Smuzhiyun 	if ((len -= WPA_SUITE_LEN) <= 0)
2460*4882a593Smuzhiyun 	{
2461*4882a593Smuzhiyun 		WL_ERR(("P:wpa2 ie len[%d]", len));
2462*4882a593Smuzhiyun 		return BCME_BADLEN;
2463*4882a593Smuzhiyun 	}
2464*4882a593Smuzhiyun 
2465*4882a593Smuzhiyun 	/* check the unicast cipher */
2466*4882a593Smuzhiyun 	ucast = (const wpa_suite_ucast_t *)&mcast[1];
2467*4882a593Smuzhiyun 	suite_count = ltoh16_ua(&ucast->count);
2468*4882a593Smuzhiyun 	WL_ERR((" WPA2 ucast cipher count=%d\n", suite_count));
2469*4882a593Smuzhiyun 	pval |= wl_get_cipher_type(ucast->list[0].type);
2470*4882a593Smuzhiyun 
2471*4882a593Smuzhiyun 	if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) <= 0)
2472*4882a593Smuzhiyun 		return BCME_BADLEN;
2473*4882a593Smuzhiyun 
2474*4882a593Smuzhiyun 	WL_ERR(("wpa2 ie ucast cipher = 0x%X\n", pval));
2475*4882a593Smuzhiyun 
2476*4882a593Smuzhiyun 	/* FOR WPS , set SEC_OW_ENABLED */
2477*4882a593Smuzhiyun 	wsec2 = (pval | gval | SES_OW_ENABLED);
2478*4882a593Smuzhiyun 	WL_ERR(("wpa2 ie wsec = 0x%X\n", wsec2));
2479*4882a593Smuzhiyun 
2480*4882a593Smuzhiyun 	/* check the AKM */
2481*4882a593Smuzhiyun 	mgmt = (const wpa_suite_auth_key_mgmt_t *)&ucast->list[suite_count];
2482*4882a593Smuzhiyun 	suite_count = ltoh16_ua(&mgmt->count);
2483*4882a593Smuzhiyun 	wpa_auth2 = wl_get_suite_auth_key_mgmt_type(mgmt->list[0].type, mcast);
2484*4882a593Smuzhiyun 	WL_ERR(("wpa ie wpa_suite_auth_key_mgmt count=%d, key_mgmt = 0x%X\n", count, wpa_auth2));
2485*4882a593Smuzhiyun 
2486*4882a593Smuzhiyun 	wsec = (wsec1 | wsec2);
2487*4882a593Smuzhiyun 	wpa_auth = (wpa_auth1 | wpa_auth2);
2488*4882a593Smuzhiyun 	WL_ERR(("wpa_wpa2 wsec=0x%X wpa_auth=0x%X\n", wsec, wpa_auth));
2489*4882a593Smuzhiyun 
2490*4882a593Smuzhiyun 	if ((len -= (WPA_IE_SUITE_COUNT_LEN + (WPA_SUITE_LEN * suite_count))) >= RSN_CAP_LEN) {
2491*4882a593Smuzhiyun 		rsn_cap[0] = *(const u8 *)&mgmt->list[suite_count];
2492*4882a593Smuzhiyun 		rsn_cap[1] = *((const u8 *)&mgmt->list[suite_count] + 1);
2493*4882a593Smuzhiyun 		if (rsn_cap[0] & (RSN_CAP_16_REPLAY_CNTRS << RSN_CAP_PTK_REPLAY_CNTR_SHIFT)) {
2494*4882a593Smuzhiyun 			wme_bss_disable = 0;
2495*4882a593Smuzhiyun 		} else {
2496*4882a593Smuzhiyun 			wme_bss_disable = 1;
2497*4882a593Smuzhiyun 		}
2498*4882a593Smuzhiyun 		WL_DBG(("P:rsn_cap[0]=[0x%X]:wme_bss_disabled[%d]\n", rsn_cap[0], wme_bss_disable));
2499*4882a593Smuzhiyun 
2500*4882a593Smuzhiyun #ifdef MFP
2501*4882a593Smuzhiyun 		if (wl_get_mfp_capability(rsn_cap[0], &wpa_auth, &mfp) != BCME_OK) {
2502*4882a593Smuzhiyun 			WL_ERR(("mfp configuration invalid. rsn_cap:0x%x\n", rsn_cap[0]));
2503*4882a593Smuzhiyun 			return BCME_ERROR;
2504*4882a593Smuzhiyun 		}
2505*4882a593Smuzhiyun 		cfg->mfp_mode = mfp;
2506*4882a593Smuzhiyun #endif /* MFP */
2507*4882a593Smuzhiyun 
2508*4882a593Smuzhiyun 		/* set wme_bss_disable to sync RSN Capabilities */
2509*4882a593Smuzhiyun 		err = wldev_iovar_setint_bsscfg(dev, "wme_bss_disable", wme_bss_disable, bssidx);
2510*4882a593Smuzhiyun 		if (err < 0) {
2511*4882a593Smuzhiyun 			WL_ERR(("wme_bss_disable error %d\n", err));
2512*4882a593Smuzhiyun 			return BCME_ERROR;
2513*4882a593Smuzhiyun 		}
2514*4882a593Smuzhiyun 	} else {
2515*4882a593Smuzhiyun 		WL_DBG(("There is no RSN Capabilities. remained len %d\n", len));
2516*4882a593Smuzhiyun 	}
2517*4882a593Smuzhiyun 
2518*4882a593Smuzhiyun 	/* set auth */
2519*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "auth", auth, bssidx);
2520*4882a593Smuzhiyun 	if (err < 0) {
2521*4882a593Smuzhiyun 		WL_ERR(("auth error %d\n", err));
2522*4882a593Smuzhiyun 		return BCME_ERROR;
2523*4882a593Smuzhiyun 	}
2524*4882a593Smuzhiyun 	/* set wsec */
2525*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx);
2526*4882a593Smuzhiyun 	if (err < 0) {
2527*4882a593Smuzhiyun 		WL_ERR(("wsec error %d\n", err));
2528*4882a593Smuzhiyun 		return BCME_ERROR;
2529*4882a593Smuzhiyun 	}
2530*4882a593Smuzhiyun 	/* set upper-layer auth */
2531*4882a593Smuzhiyun 	err = wldev_iovar_setint_bsscfg(dev, "wpa_auth", wpa_auth, bssidx);
2532*4882a593Smuzhiyun 	if (err < 0) {
2533*4882a593Smuzhiyun 		WL_ERR(("wpa_auth error %d\n", err));
2534*4882a593Smuzhiyun 		return BCME_ERROR;
2535*4882a593Smuzhiyun 	}
2536*4882a593Smuzhiyun 
2537*4882a593Smuzhiyun 	if (sec) {
2538*4882a593Smuzhiyun 		sec->fw_wpa_auth = wpa_auth;
2539*4882a593Smuzhiyun 		sec->fw_auth = auth;
2540*4882a593Smuzhiyun 		sec->fw_wsec = wsec;
2541*4882a593Smuzhiyun 	}
2542*4882a593Smuzhiyun 
2543*4882a593Smuzhiyun exit:
2544*4882a593Smuzhiyun 	return 0;
2545*4882a593Smuzhiyun }
2546*4882a593Smuzhiyun #endif /* SUPPORT_SOFTAP_WPAWPA2_MIXED */
2547*4882a593Smuzhiyun 
2548*4882a593Smuzhiyun static s32
wl_cfg80211_bcn_validate_sec(struct net_device * dev,struct parsed_ies * ies,u32 dev_role,s32 bssidx,bool privacy)2549*4882a593Smuzhiyun wl_cfg80211_bcn_validate_sec(
2550*4882a593Smuzhiyun 	struct net_device *dev,
2551*4882a593Smuzhiyun 	struct parsed_ies *ies,
2552*4882a593Smuzhiyun 	u32 dev_role,
2553*4882a593Smuzhiyun 	s32 bssidx,
2554*4882a593Smuzhiyun 	bool privacy)
2555*4882a593Smuzhiyun {
2556*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
2557*4882a593Smuzhiyun 	wl_cfgbss_t *bss = wl_get_cfgbss_by_wdev(cfg, dev->ieee80211_ptr);
2558*4882a593Smuzhiyun 	struct wl_security *sec = wl_read_prof(cfg, dev, WL_PROF_SEC);
2559*4882a593Smuzhiyun 
2560*4882a593Smuzhiyun 	if (!bss) {
2561*4882a593Smuzhiyun 		WL_ERR(("cfgbss is NULL \n"));
2562*4882a593Smuzhiyun 		return BCME_ERROR;
2563*4882a593Smuzhiyun 	}
2564*4882a593Smuzhiyun 
2565*4882a593Smuzhiyun 	if (dev_role == NL80211_IFTYPE_P2P_GO && (ies->wpa2_ie)) {
2566*4882a593Smuzhiyun 		/* For P2P GO, the sec type is WPA2-PSK */
2567*4882a593Smuzhiyun 		WL_DBG(("P2P GO: validating wpa2_ie\n"));
2568*4882a593Smuzhiyun 		if (wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx)  < 0)
2569*4882a593Smuzhiyun 			return BCME_ERROR;
2570*4882a593Smuzhiyun 
2571*4882a593Smuzhiyun 	} else if (dev_role == NL80211_IFTYPE_AP) {
2572*4882a593Smuzhiyun 
2573*4882a593Smuzhiyun 		WL_DBG(("SoftAP: validating security\n"));
2574*4882a593Smuzhiyun 		/* If wpa2_ie or wpa_ie is present validate it */
2575*4882a593Smuzhiyun 
2576*4882a593Smuzhiyun #if defined(SUPPORT_SOFTAP_WPAWPA2_MIXED)
2577*4882a593Smuzhiyun 		if ((ies->wpa_ie != NULL && ies->wpa2_ie != NULL)) {
2578*4882a593Smuzhiyun 			if (wl_validate_wpaie_wpa2ie(dev, ies->wpa_ie, ies->wpa2_ie, bssidx)  < 0) {
2579*4882a593Smuzhiyun 				bss->security_mode = false;
2580*4882a593Smuzhiyun 				return BCME_ERROR;
2581*4882a593Smuzhiyun 			}
2582*4882a593Smuzhiyun 		}
2583*4882a593Smuzhiyun 		else {
2584*4882a593Smuzhiyun #endif /* SUPPORT_SOFTAP_WPAWPA2_MIXED */
2585*4882a593Smuzhiyun 		if ((ies->wpa2_ie || ies->wpa_ie) &&
2586*4882a593Smuzhiyun 			((wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx)  < 0 ||
2587*4882a593Smuzhiyun 			wl_validate_wpaie(dev, ies->wpa_ie, bssidx) < 0))) {
2588*4882a593Smuzhiyun 			bss->security_mode = false;
2589*4882a593Smuzhiyun 			return BCME_ERROR;
2590*4882a593Smuzhiyun 		}
2591*4882a593Smuzhiyun 
2592*4882a593Smuzhiyun 		if (ies->fils_ind_ie &&
2593*4882a593Smuzhiyun 			(wl_validate_fils_ind_ie(dev, ies->fils_ind_ie, bssidx)  < 0)) {
2594*4882a593Smuzhiyun 			bss->security_mode = false;
2595*4882a593Smuzhiyun 			return BCME_ERROR;
2596*4882a593Smuzhiyun 		}
2597*4882a593Smuzhiyun 
2598*4882a593Smuzhiyun 		bss->security_mode = true;
2599*4882a593Smuzhiyun 		if (bss->rsn_ie) {
2600*4882a593Smuzhiyun 			MFREE(cfg->osh, bss->rsn_ie, bss->rsn_ie[1]
2601*4882a593Smuzhiyun 				+ WPA_RSN_IE_TAG_FIXED_LEN);
2602*4882a593Smuzhiyun 			bss->rsn_ie = NULL;
2603*4882a593Smuzhiyun 		}
2604*4882a593Smuzhiyun 		if (bss->wpa_ie) {
2605*4882a593Smuzhiyun 			MFREE(cfg->osh, bss->wpa_ie, bss->wpa_ie[1]
2606*4882a593Smuzhiyun 				+ WPA_RSN_IE_TAG_FIXED_LEN);
2607*4882a593Smuzhiyun 			bss->wpa_ie = NULL;
2608*4882a593Smuzhiyun 		}
2609*4882a593Smuzhiyun 		if (bss->wps_ie) {
2610*4882a593Smuzhiyun 			MFREE(cfg->osh, bss->wps_ie, bss->wps_ie[1] + 2);
2611*4882a593Smuzhiyun 			bss->wps_ie = NULL;
2612*4882a593Smuzhiyun 		}
2613*4882a593Smuzhiyun 		if (bss->fils_ind_ie) {
2614*4882a593Smuzhiyun 			MFREE(cfg->osh, bss->fils_ind_ie, bss->fils_ind_ie[1]
2615*4882a593Smuzhiyun 				+ FILS_INDICATION_IE_TAG_FIXED_LEN);
2616*4882a593Smuzhiyun 			bss->fils_ind_ie = NULL;
2617*4882a593Smuzhiyun 		}
2618*4882a593Smuzhiyun 		if (ies->wpa_ie != NULL) {
2619*4882a593Smuzhiyun 			/* WPAIE */
2620*4882a593Smuzhiyun 			bss->rsn_ie = NULL;
2621*4882a593Smuzhiyun 			bss->wpa_ie = MALLOCZ(cfg->osh,
2622*4882a593Smuzhiyun 					ies->wpa_ie->length
2623*4882a593Smuzhiyun 					+ WPA_RSN_IE_TAG_FIXED_LEN);
2624*4882a593Smuzhiyun 			if (bss->wpa_ie) {
2625*4882a593Smuzhiyun 				memcpy(bss->wpa_ie, ies->wpa_ie,
2626*4882a593Smuzhiyun 					ies->wpa_ie->length
2627*4882a593Smuzhiyun 					+ WPA_RSN_IE_TAG_FIXED_LEN);
2628*4882a593Smuzhiyun 			}
2629*4882a593Smuzhiyun 		} else if (ies->wpa2_ie != NULL) {
2630*4882a593Smuzhiyun 			/* RSNIE */
2631*4882a593Smuzhiyun 			bss->wpa_ie = NULL;
2632*4882a593Smuzhiyun 			bss->rsn_ie = MALLOCZ(cfg->osh,
2633*4882a593Smuzhiyun 					ies->wpa2_ie->len
2634*4882a593Smuzhiyun 					+ WPA_RSN_IE_TAG_FIXED_LEN);
2635*4882a593Smuzhiyun 			if (bss->rsn_ie) {
2636*4882a593Smuzhiyun 				memcpy(bss->rsn_ie, ies->wpa2_ie,
2637*4882a593Smuzhiyun 					ies->wpa2_ie->len
2638*4882a593Smuzhiyun 					+ WPA_RSN_IE_TAG_FIXED_LEN);
2639*4882a593Smuzhiyun 			}
2640*4882a593Smuzhiyun 		}
2641*4882a593Smuzhiyun #ifdef WL_FILS
2642*4882a593Smuzhiyun 		if (ies->fils_ind_ie) {
2643*4882a593Smuzhiyun 			bss->fils_ind_ie = MALLOCZ(cfg->osh,
2644*4882a593Smuzhiyun 					ies->fils_ind_ie->len
2645*4882a593Smuzhiyun 					+ FILS_INDICATION_IE_TAG_FIXED_LEN);
2646*4882a593Smuzhiyun 			if (bss->fils_ind_ie) {
2647*4882a593Smuzhiyun 				memcpy(bss->fils_ind_ie, ies->fils_ind_ie,
2648*4882a593Smuzhiyun 					ies->fils_ind_ie->len
2649*4882a593Smuzhiyun 					+ FILS_INDICATION_IE_TAG_FIXED_LEN);
2650*4882a593Smuzhiyun 			}
2651*4882a593Smuzhiyun 		}
2652*4882a593Smuzhiyun #endif /* WL_FILS */
2653*4882a593Smuzhiyun #if defined(SUPPORT_SOFTAP_WPAWPA2_MIXED)
2654*4882a593Smuzhiyun 		}
2655*4882a593Smuzhiyun #endif /* SUPPORT_SOFTAP_WPAWPA2_MIXED */
2656*4882a593Smuzhiyun 		if (!ies->wpa2_ie && !ies->wpa_ie) {
2657*4882a593Smuzhiyun 			wl_validate_opensecurity(dev, bssidx, privacy);
2658*4882a593Smuzhiyun 			bss->security_mode = false;
2659*4882a593Smuzhiyun 		}
2660*4882a593Smuzhiyun 
2661*4882a593Smuzhiyun 		if (ies->wps_ie) {
2662*4882a593Smuzhiyun 			bss->wps_ie = MALLOCZ(cfg->osh, ies->wps_ie_len);
2663*4882a593Smuzhiyun 			if (bss->wps_ie) {
2664*4882a593Smuzhiyun 				memcpy(bss->wps_ie, ies->wps_ie, ies->wps_ie_len);
2665*4882a593Smuzhiyun 			}
2666*4882a593Smuzhiyun 		}
2667*4882a593Smuzhiyun 	}
2668*4882a593Smuzhiyun 
2669*4882a593Smuzhiyun 	WL_INFORM_MEM(("[%s] wpa_auth:0x%x auth:0x%x wsec:0x%x mfp:0x%x\n",
2670*4882a593Smuzhiyun 		dev->name, sec->fw_wpa_auth, sec->fw_auth, sec->fw_wsec, sec->fw_mfp));
2671*4882a593Smuzhiyun 	return 0;
2672*4882a593Smuzhiyun 
2673*4882a593Smuzhiyun }
2674*4882a593Smuzhiyun 
2675*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \
2676*4882a593Smuzhiyun 	defined(WL_COMPAT_WIRELESS)
wl_cfg80211_bcn_set_params(struct cfg80211_ap_settings * info,struct net_device * dev,u32 dev_role,s32 bssidx)2677*4882a593Smuzhiyun static s32 wl_cfg80211_bcn_set_params(
2678*4882a593Smuzhiyun 	struct cfg80211_ap_settings *info,
2679*4882a593Smuzhiyun 	struct net_device *dev,
2680*4882a593Smuzhiyun 	u32 dev_role, s32 bssidx)
2681*4882a593Smuzhiyun {
2682*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
2683*4882a593Smuzhiyun 	s32 err = BCME_OK;
2684*4882a593Smuzhiyun 
2685*4882a593Smuzhiyun 	WL_DBG(("interval (%d) dtim_period (%d) \n",
2686*4882a593Smuzhiyun 		info->beacon_interval, info->dtim_period));
2687*4882a593Smuzhiyun 
2688*4882a593Smuzhiyun 	if (info->beacon_interval) {
2689*4882a593Smuzhiyun 		if ((err = wldev_ioctl_set(dev, WLC_SET_BCNPRD,
2690*4882a593Smuzhiyun 			&info->beacon_interval, sizeof(s32))) < 0) {
2691*4882a593Smuzhiyun 			WL_ERR(("Beacon Interval Set Error, %d\n", err));
2692*4882a593Smuzhiyun 			return err;
2693*4882a593Smuzhiyun 		}
2694*4882a593Smuzhiyun 	}
2695*4882a593Smuzhiyun 
2696*4882a593Smuzhiyun 	if (info->dtim_period) {
2697*4882a593Smuzhiyun 		if ((err = wldev_ioctl_set(dev, WLC_SET_DTIMPRD,
2698*4882a593Smuzhiyun 			&info->dtim_period, sizeof(s32))) < 0) {
2699*4882a593Smuzhiyun 			WL_ERR(("DTIM Interval Set Error, %d\n", err));
2700*4882a593Smuzhiyun 			return err;
2701*4882a593Smuzhiyun 		}
2702*4882a593Smuzhiyun 	}
2703*4882a593Smuzhiyun 
2704*4882a593Smuzhiyun 	if ((info->ssid) && (info->ssid_len > 0) &&
2705*4882a593Smuzhiyun 		(info->ssid_len <= DOT11_MAX_SSID_LEN)) {
2706*4882a593Smuzhiyun 		WL_DBG(("SSID (%s) len:%zd \n", info->ssid, info->ssid_len));
2707*4882a593Smuzhiyun 		if (dev_role == NL80211_IFTYPE_AP) {
2708*4882a593Smuzhiyun 			/* Store the hostapd SSID */
2709*4882a593Smuzhiyun 			bzero(cfg->hostapd_ssid.SSID, DOT11_MAX_SSID_LEN);
2710*4882a593Smuzhiyun 			memcpy(cfg->hostapd_ssid.SSID, info->ssid, info->ssid_len);
2711*4882a593Smuzhiyun 			cfg->hostapd_ssid.SSID_len = (uint32)info->ssid_len;
2712*4882a593Smuzhiyun 		} else {
2713*4882a593Smuzhiyun 				/* P2P GO */
2714*4882a593Smuzhiyun 			bzero(cfg->p2p->ssid.SSID, DOT11_MAX_SSID_LEN);
2715*4882a593Smuzhiyun 			memcpy(cfg->p2p->ssid.SSID, info->ssid, info->ssid_len);
2716*4882a593Smuzhiyun 			cfg->p2p->ssid.SSID_len = (uint32)info->ssid_len;
2717*4882a593Smuzhiyun 		}
2718*4882a593Smuzhiyun 	}
2719*4882a593Smuzhiyun 
2720*4882a593Smuzhiyun 	return err;
2721*4882a593Smuzhiyun }
2722*4882a593Smuzhiyun #endif /* LINUX_VERSION >= VERSION(3,4,0) || WL_COMPAT_WIRELESS */
2723*4882a593Smuzhiyun 
2724*4882a593Smuzhiyun s32
wl_cfg80211_parse_ies(const u8 * ptr,u32 len,struct parsed_ies * ies)2725*4882a593Smuzhiyun wl_cfg80211_parse_ies(const u8 *ptr, u32 len, struct parsed_ies *ies)
2726*4882a593Smuzhiyun {
2727*4882a593Smuzhiyun 	s32 err = BCME_OK;
2728*4882a593Smuzhiyun 
2729*4882a593Smuzhiyun 	bzero(ies, sizeof(struct parsed_ies));
2730*4882a593Smuzhiyun 
2731*4882a593Smuzhiyun 	/* find the WPSIE */
2732*4882a593Smuzhiyun 	if ((ies->wps_ie = wl_cfgp2p_find_wpsie(ptr, len)) != NULL) {
2733*4882a593Smuzhiyun 		WL_DBG(("WPSIE in beacon \n"));
2734*4882a593Smuzhiyun 		ies->wps_ie_len = ies->wps_ie->length + WPA_RSN_IE_TAG_FIXED_LEN;
2735*4882a593Smuzhiyun 	} else {
2736*4882a593Smuzhiyun 		WL_DBG(("No WPSIE in beacon \n"));
2737*4882a593Smuzhiyun 	}
2738*4882a593Smuzhiyun 
2739*4882a593Smuzhiyun 	/* find the RSN_IE */
2740*4882a593Smuzhiyun 	if ((ies->wpa2_ie = bcm_parse_tlvs(ptr, len,
2741*4882a593Smuzhiyun 		DOT11_MNG_RSN_ID)) != NULL) {
2742*4882a593Smuzhiyun 		WL_DBG((" WPA2 IE found\n"));
2743*4882a593Smuzhiyun 		ies->wpa2_ie_len = ies->wpa2_ie->len;
2744*4882a593Smuzhiyun 	}
2745*4882a593Smuzhiyun 
2746*4882a593Smuzhiyun 	/* find the FILS_IND_IE */
2747*4882a593Smuzhiyun 	if ((ies->fils_ind_ie = bcm_parse_tlvs(ptr, len,
2748*4882a593Smuzhiyun 		DOT11_MNG_FILS_IND_ID)) != NULL) {
2749*4882a593Smuzhiyun 		WL_DBG((" FILS IND IE found\n"));
2750*4882a593Smuzhiyun 		ies->fils_ind_ie_len = ies->fils_ind_ie->len;
2751*4882a593Smuzhiyun 	}
2752*4882a593Smuzhiyun 
2753*4882a593Smuzhiyun 	/* find the WPA_IE */
2754*4882a593Smuzhiyun 	if ((ies->wpa_ie = wl_cfgp2p_find_wpaie(ptr, len)) != NULL) {
2755*4882a593Smuzhiyun 		WL_DBG((" WPA found\n"));
2756*4882a593Smuzhiyun 		ies->wpa_ie_len = ies->wpa_ie->length;
2757*4882a593Smuzhiyun 	}
2758*4882a593Smuzhiyun 
2759*4882a593Smuzhiyun 	return err;
2760*4882a593Smuzhiyun 
2761*4882a593Smuzhiyun }
2762*4882a593Smuzhiyun 
2763*4882a593Smuzhiyun s32
wl_cfg80211_set_ap_role(struct bcm_cfg80211 * cfg,struct net_device * dev)2764*4882a593Smuzhiyun wl_cfg80211_set_ap_role(
2765*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg,
2766*4882a593Smuzhiyun 	struct net_device *dev)
2767*4882a593Smuzhiyun {
2768*4882a593Smuzhiyun 	s32 err = BCME_OK;
2769*4882a593Smuzhiyun 	s32 infra = 1;
2770*4882a593Smuzhiyun 	s32 ap = 0;
2771*4882a593Smuzhiyun 	s32 pm;
2772*4882a593Smuzhiyun 	s32 bssidx;
2773*4882a593Smuzhiyun 	s32 apsta = 0;
2774*4882a593Smuzhiyun 	bool new_chip;
2775*4882a593Smuzhiyun #ifdef WLEASYMESH
2776*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
2777*4882a593Smuzhiyun #endif /* WLEASYMESH */
2778*4882a593Smuzhiyun 
2779*4882a593Smuzhiyun 	new_chip = dhd_conf_new_chip_check(cfg->pub);
2780*4882a593Smuzhiyun 
2781*4882a593Smuzhiyun 	if ((bssidx = wl_get_bssidx_by_wdev(cfg, dev->ieee80211_ptr)) < 0) {
2782*4882a593Smuzhiyun 		WL_ERR(("Find p2p index from wdev(%p) failed\n", dev->ieee80211_ptr));
2783*4882a593Smuzhiyun 		return -EINVAL;
2784*4882a593Smuzhiyun 	}
2785*4882a593Smuzhiyun 
2786*4882a593Smuzhiyun 	WL_INFORM_MEM(("[%s] Bringup SoftAP on bssidx:%d \n", dev->name, bssidx));
2787*4882a593Smuzhiyun 
2788*4882a593Smuzhiyun 	if (bssidx != 0 || new_chip) {
2789*4882a593Smuzhiyun 		if ((err = wl_cfg80211_add_del_bss(cfg, dev, bssidx,
2790*4882a593Smuzhiyun 				WL_IF_TYPE_AP, 0, NULL)) < 0) {
2791*4882a593Smuzhiyun 			WL_ERR(("wl add_del_bss returned error:%d\n", err));
2792*4882a593Smuzhiyun 			return err;
2793*4882a593Smuzhiyun 		}
2794*4882a593Smuzhiyun 	}
2795*4882a593Smuzhiyun 
2796*4882a593Smuzhiyun 	/*
2797*4882a593Smuzhiyun 	 * For older chips, "bss" iovar does not support
2798*4882a593Smuzhiyun 	 * bsscfg role change/upgradation, and still
2799*4882a593Smuzhiyun 	 * return BCME_OK on attempt
2800*4882a593Smuzhiyun 	 * Hence, below traditional way to handle the same
2801*4882a593Smuzhiyun 	 */
2802*4882a593Smuzhiyun 
2803*4882a593Smuzhiyun 	if ((err = wldev_ioctl_get(dev,
2804*4882a593Smuzhiyun 			WLC_GET_AP, &ap, sizeof(s32))) < 0) {
2805*4882a593Smuzhiyun 		WL_ERR(("Getting AP mode failed %d \n", err));
2806*4882a593Smuzhiyun 		return err;
2807*4882a593Smuzhiyun 	}
2808*4882a593Smuzhiyun #ifdef WLEASYMESH
2809*4882a593Smuzhiyun 	else if (dhd->conf->fw_type == FW_TYPE_EZMESH) {
2810*4882a593Smuzhiyun 		WL_MSG(dev->name, "Getting AP mode ok, set map and dwds\n");
2811*4882a593Smuzhiyun 		err = wldev_ioctl_set(dev, WLC_DOWN, &ap, sizeof(s32));
2812*4882a593Smuzhiyun 		if (err < 0) {
2813*4882a593Smuzhiyun 			WL_ERR(("WLC_DOWN error %d\n", err));
2814*4882a593Smuzhiyun 			return err;
2815*4882a593Smuzhiyun 		}
2816*4882a593Smuzhiyun 		//For FrontHaulAP
2817*4882a593Smuzhiyun 		err = wldev_iovar_setint(dev, "map", 2);
2818*4882a593Smuzhiyun 		if (err < 0) {
2819*4882a593Smuzhiyun 			WL_ERR(("wl map 2 error %d\n", err));
2820*4882a593Smuzhiyun 			return err;
2821*4882a593Smuzhiyun 		}
2822*4882a593Smuzhiyun 		err = wldev_iovar_setint(dev, "dwds", 1);
2823*4882a593Smuzhiyun 		if (err < 0) {
2824*4882a593Smuzhiyun 			WL_ERR(("wl dwds 1 error %d\n", err));
2825*4882a593Smuzhiyun 			return err;
2826*4882a593Smuzhiyun 		}
2827*4882a593Smuzhiyun 		WL_MSG(dev->name, "Get AP %d\n", (int)ap);
2828*4882a593Smuzhiyun 	}
2829*4882a593Smuzhiyun #endif /* WLEASYMESH*/
2830*4882a593Smuzhiyun 
2831*4882a593Smuzhiyun 	if (!ap) {
2832*4882a593Smuzhiyun 		/* AP mode switch not supported. Try setting up AP explicitly */
2833*4882a593Smuzhiyun 		err = wldev_iovar_getint(dev, "apsta", (s32 *)&apsta);
2834*4882a593Smuzhiyun 		if (unlikely(err)) {
2835*4882a593Smuzhiyun 			WL_ERR(("Could not get apsta %d\n", err));
2836*4882a593Smuzhiyun 			return err;
2837*4882a593Smuzhiyun 		}
2838*4882a593Smuzhiyun 		if (apsta == 0) {
2839*4882a593Smuzhiyun 			/* If apsta is not set, set it */
2840*4882a593Smuzhiyun 
2841*4882a593Smuzhiyun 			/* Check for any connected interfaces before wl down */
2842*4882a593Smuzhiyun 			if (wl_get_drv_status_all(cfg, CONNECTED) > 0) {
2843*4882a593Smuzhiyun #ifdef WLEASYMESH
2844*4882a593Smuzhiyun 				if (dhd->conf->fw_type == FW_TYPE_EZMESH) {
2845*4882a593Smuzhiyun 					WL_MSG(dev->name, "do wl down\n");
2846*4882a593Smuzhiyun 				} else {
2847*4882a593Smuzhiyun #endif /* WLEASYMESH */
2848*4882a593Smuzhiyun 					WL_ERR(("Concurrent i/f operational. can't do wl down\n"));
2849*4882a593Smuzhiyun 					return BCME_ERROR;
2850*4882a593Smuzhiyun #ifdef WLEASYMESH
2851*4882a593Smuzhiyun 				}
2852*4882a593Smuzhiyun #endif /* WLEASYMESH */
2853*4882a593Smuzhiyun 			}
2854*4882a593Smuzhiyun 			err = wldev_ioctl_set(dev, WLC_DOWN, &ap, sizeof(s32));
2855*4882a593Smuzhiyun 			if (err < 0) {
2856*4882a593Smuzhiyun 				WL_ERR(("WLC_DOWN error %d\n", err));
2857*4882a593Smuzhiyun 				return err;
2858*4882a593Smuzhiyun 			}
2859*4882a593Smuzhiyun #ifdef WLEASYMESH
2860*4882a593Smuzhiyun 			if (dhd->conf->fw_type == FW_TYPE_EZMESH)
2861*4882a593Smuzhiyun 				err = wldev_iovar_setint(dev, "apsta", 1);
2862*4882a593Smuzhiyun 			else
2863*4882a593Smuzhiyun #endif /* WLEASYMESH */
2864*4882a593Smuzhiyun 				err = wldev_iovar_setint(dev, "apsta", 0);
2865*4882a593Smuzhiyun 			if (err < 0) {
2866*4882a593Smuzhiyun 				WL_ERR(("wl apsta 0 error %d\n", err));
2867*4882a593Smuzhiyun 				return err;
2868*4882a593Smuzhiyun 			}
2869*4882a593Smuzhiyun 			ap = 1;
2870*4882a593Smuzhiyun 			if ((err = wldev_ioctl_set(dev,
2871*4882a593Smuzhiyun 					WLC_SET_AP, &ap, sizeof(s32))) < 0) {
2872*4882a593Smuzhiyun 				WL_ERR(("setting AP mode failed %d \n", err));
2873*4882a593Smuzhiyun 				return err;
2874*4882a593Smuzhiyun 			}
2875*4882a593Smuzhiyun #ifdef WLEASYMESH
2876*4882a593Smuzhiyun 			//For FrontHaulAP
2877*4882a593Smuzhiyun 			if (dhd->conf->fw_type == FW_TYPE_EZMESH) {
2878*4882a593Smuzhiyun 				WL_MSG(dev->name, "wl map 2\n");
2879*4882a593Smuzhiyun 				err = wldev_iovar_setint(dev, "map", 2);
2880*4882a593Smuzhiyun 				if (err < 0) {
2881*4882a593Smuzhiyun 					WL_ERR(("wl map 2 error %d\n", err));
2882*4882a593Smuzhiyun 					return err;
2883*4882a593Smuzhiyun 				}
2884*4882a593Smuzhiyun 				err = wldev_iovar_setint(dev, "dwds", 1);
2885*4882a593Smuzhiyun 				if (err < 0) {
2886*4882a593Smuzhiyun 					WL_ERR(("wl dwds 1 error %d\n", err));
2887*4882a593Smuzhiyun 					return err;
2888*4882a593Smuzhiyun 				}
2889*4882a593Smuzhiyun 			}
2890*4882a593Smuzhiyun #endif /* WLEASYMESH */
2891*4882a593Smuzhiyun 		}
2892*4882a593Smuzhiyun 	}
2893*4882a593Smuzhiyun 	else if (bssidx == 0 && !new_chip
2894*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
2895*4882a593Smuzhiyun 			&& !wl_ext_iapsta_other_if_enabled(dev)
2896*4882a593Smuzhiyun #endif
2897*4882a593Smuzhiyun 			) {
2898*4882a593Smuzhiyun 		err = wldev_ioctl_set(dev, WLC_DOWN, &ap, sizeof(s32));
2899*4882a593Smuzhiyun 		if (err < 0) {
2900*4882a593Smuzhiyun 			WL_ERR(("WLC_DOWN error %d\n", err));
2901*4882a593Smuzhiyun 			return err;
2902*4882a593Smuzhiyun 		}
2903*4882a593Smuzhiyun 		err = wldev_iovar_setint(dev, "apsta", 0);
2904*4882a593Smuzhiyun 		if (err < 0) {
2905*4882a593Smuzhiyun 			WL_ERR(("wl apsta 0 error %d\n", err));
2906*4882a593Smuzhiyun 			return err;
2907*4882a593Smuzhiyun 		}
2908*4882a593Smuzhiyun 		ap = 1;
2909*4882a593Smuzhiyun 		if ((err = wldev_ioctl_set(dev, WLC_SET_AP, &ap, sizeof(s32))) < 0) {
2910*4882a593Smuzhiyun 			WL_ERR(("setting AP mode failed %d \n", err));
2911*4882a593Smuzhiyun 			return err;
2912*4882a593Smuzhiyun 		}
2913*4882a593Smuzhiyun 	}
2914*4882a593Smuzhiyun 
2915*4882a593Smuzhiyun 	if (bssidx == 0) {
2916*4882a593Smuzhiyun 		pm = 0;
2917*4882a593Smuzhiyun 		if ((err = wldev_ioctl_set(dev, WLC_SET_PM, &pm, sizeof(pm))) != 0) {
2918*4882a593Smuzhiyun 			WL_ERR(("wl PM 0 returned error:%d\n", err));
2919*4882a593Smuzhiyun 			/* Ignore error, if any */
2920*4882a593Smuzhiyun 			err = BCME_OK;
2921*4882a593Smuzhiyun 		}
2922*4882a593Smuzhiyun 		err = wldev_ioctl_set(dev, WLC_SET_INFRA, &infra, sizeof(s32));
2923*4882a593Smuzhiyun 		if (err < 0) {
2924*4882a593Smuzhiyun 			WL_ERR(("SET INFRA error %d\n", err));
2925*4882a593Smuzhiyun 			return err;
2926*4882a593Smuzhiyun 		}
2927*4882a593Smuzhiyun 	}
2928*4882a593Smuzhiyun 
2929*4882a593Smuzhiyun 	/* On success, mark AP creation in progress. */
2930*4882a593Smuzhiyun 	wl_set_drv_status(cfg, AP_CREATING, dev);
2931*4882a593Smuzhiyun 	return 0;
2932*4882a593Smuzhiyun }
2933*4882a593Smuzhiyun 
2934*4882a593Smuzhiyun void
wl_cfg80211_ap_timeout_work(struct work_struct * work)2935*4882a593Smuzhiyun wl_cfg80211_ap_timeout_work(struct work_struct *work)
2936*4882a593Smuzhiyun {
2937*4882a593Smuzhiyun #if defined (BCMDONGLEHOST)
2938*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = NULL;
2939*4882a593Smuzhiyun 	dhd_pub_t *dhdp = NULL;
2940*4882a593Smuzhiyun 	BCM_SET_CONTAINER_OF(cfg, work, struct bcm_cfg80211, ap_work.work);
2941*4882a593Smuzhiyun 
2942*4882a593Smuzhiyun 	WL_ERR(("** AP LINK UP TIMEOUT **\n"));
2943*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
2944*4882a593Smuzhiyun 	if (dhd_query_bus_erros(dhdp)) {
2945*4882a593Smuzhiyun 		return;
2946*4882a593Smuzhiyun 	}
2947*4882a593Smuzhiyun #ifdef DHD_PCIE_RUNTIMEPM
2948*4882a593Smuzhiyun 	dhdpcie_runtime_bus_wake(dhdp, CAN_SLEEP(), __builtin_return_address(0));
2949*4882a593Smuzhiyun #endif /* DHD_PCIE_RUNTIMEPM */
2950*4882a593Smuzhiyun 	dhdp->iface_op_failed = TRUE;
2951*4882a593Smuzhiyun 
2952*4882a593Smuzhiyun #if defined(DHD_DEBUG) && defined(DHD_FW_COREDUMP)
2953*4882a593Smuzhiyun 	if (dhdp->memdump_enabled) {
2954*4882a593Smuzhiyun 		dhdp->memdump_type = DUMP_TYPE_AP_LINKUP_FAILURE;
2955*4882a593Smuzhiyun 		dhd_bus_mem_dump(dhdp);
2956*4882a593Smuzhiyun 	}
2957*4882a593Smuzhiyun #endif /* DHD_DEBUG && DHD_FW_COREDUMP */
2958*4882a593Smuzhiyun 
2959*4882a593Smuzhiyun #if defined(OEM_ANDROID)
2960*4882a593Smuzhiyun 	WL_ERR(("Notify hang event to upper layer \n"));
2961*4882a593Smuzhiyun 	dhdp->hang_reason = HANG_REASON_IFACE_ADD_FAILURE;
2962*4882a593Smuzhiyun 	net_os_send_hang_message(bcmcfg_to_prmry_ndev(cfg));
2963*4882a593Smuzhiyun #endif /* OEM_ANDROID */
2964*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
2965*4882a593Smuzhiyun }
2966*4882a593Smuzhiyun 
2967*4882a593Smuzhiyun /* In RSDB downgrade cases, the link up event can get delayed upto 7-8 secs */
2968*4882a593Smuzhiyun #define MAX_AP_LINK_WAIT_TIME   10000
2969*4882a593Smuzhiyun static s32
wl_cfg80211_bcn_bringup_ap(struct net_device * dev,struct parsed_ies * ies,u32 dev_role,s32 bssidx)2970*4882a593Smuzhiyun wl_cfg80211_bcn_bringup_ap(
2971*4882a593Smuzhiyun 	struct net_device *dev,
2972*4882a593Smuzhiyun 	struct parsed_ies *ies,
2973*4882a593Smuzhiyun 	u32 dev_role, s32 bssidx)
2974*4882a593Smuzhiyun {
2975*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
2976*4882a593Smuzhiyun 	struct wl_join_params join_params;
2977*4882a593Smuzhiyun 	bool is_bssup = false;
2978*4882a593Smuzhiyun 	s32 infra = 1;
2979*4882a593Smuzhiyun 	s32 join_params_size = 0;
2980*4882a593Smuzhiyun 	s32 ap = 1;
2981*4882a593Smuzhiyun 	s32 wsec;
2982*4882a593Smuzhiyun #ifdef DISABLE_11H_SOFTAP
2983*4882a593Smuzhiyun 	s32 spect = 0;
2984*4882a593Smuzhiyun #endif /* DISABLE_11H_SOFTAP */
2985*4882a593Smuzhiyun #ifdef SOFTAP_UAPSD_OFF
2986*4882a593Smuzhiyun 	uint32 wme_apsd = 0;
2987*4882a593Smuzhiyun #endif /* SOFTAP_UAPSD_OFF */
2988*4882a593Smuzhiyun 	s32 err = BCME_OK;
2989*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
2990*4882a593Smuzhiyun 	s32 is_rsdb_supported = BCME_ERROR;
2991*4882a593Smuzhiyun 	char sec[64];
2992*4882a593Smuzhiyun 
2993*4882a593Smuzhiyun #if defined (BCMDONGLEHOST)
2994*4882a593Smuzhiyun 	is_rsdb_supported = DHD_OPMODE_SUPPORTED(cfg->pub, DHD_FLAG_RSDB_MODE);
2995*4882a593Smuzhiyun 	if (is_rsdb_supported < 0)
2996*4882a593Smuzhiyun 		return (-ENODEV);
2997*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
2998*4882a593Smuzhiyun 
2999*4882a593Smuzhiyun 	WL_DBG(("Enter dev_role:%d bssidx:%d ifname:%s\n", dev_role, bssidx, dev->name));
3000*4882a593Smuzhiyun 
3001*4882a593Smuzhiyun 	/* Common code for SoftAP and P2P GO */
3002*4882a593Smuzhiyun 	wl_clr_drv_status(cfg, AP_CREATED, dev);
3003*4882a593Smuzhiyun 
3004*4882a593Smuzhiyun 	/* Make sure INFRA is set for AP/GO */
3005*4882a593Smuzhiyun 	err = wldev_ioctl_set(dev, WLC_SET_INFRA, &infra, sizeof(s32));
3006*4882a593Smuzhiyun 	if (err < 0) {
3007*4882a593Smuzhiyun 		WL_ERR(("SET INFRA error %d\n", err));
3008*4882a593Smuzhiyun 		goto exit;
3009*4882a593Smuzhiyun 	}
3010*4882a593Smuzhiyun 
3011*4882a593Smuzhiyun 	/* Do abort scan before creating GO */
3012*4882a593Smuzhiyun 	wl_cfgscan_cancel_scan(cfg);
3013*4882a593Smuzhiyun 
3014*4882a593Smuzhiyun 	/* Schedule delayed work to handle link time out. schedule
3015*4882a593Smuzhiyun 	 * before ssid iovar. Sometimes before iovar context should
3016*4882a593Smuzhiyun 	 * resume, the event may come and get processed.
3017*4882a593Smuzhiyun 	 */
3018*4882a593Smuzhiyun 	if (schedule_delayed_work(&cfg->ap_work,
3019*4882a593Smuzhiyun 			msecs_to_jiffies((const unsigned int)MAX_AP_LINK_WAIT_TIME))) {
3020*4882a593Smuzhiyun 		WL_DBG(("ap timeout work scheduled\n"));
3021*4882a593Smuzhiyun 	}
3022*4882a593Smuzhiyun 
3023*4882a593Smuzhiyun 	if (dev_role == NL80211_IFTYPE_P2P_GO) {
3024*4882a593Smuzhiyun 		wl_ext_get_sec(dev, 0, sec, sizeof(sec), TRUE);
3025*4882a593Smuzhiyun 		WL_MSG(dev->name, "Creating GO with sec=%s\n", sec);
3026*4882a593Smuzhiyun 		is_bssup = wl_cfg80211_bss_isup(dev, bssidx);
3027*4882a593Smuzhiyun 		if (!is_bssup && (ies->wpa2_ie != NULL)) {
3028*4882a593Smuzhiyun 
3029*4882a593Smuzhiyun 			err = wldev_iovar_setbuf_bsscfg(dev, "ssid", &cfg->p2p->ssid,
3030*4882a593Smuzhiyun 				sizeof(cfg->p2p->ssid), cfg->ioctl_buf, WLC_IOCTL_MAXLEN,
3031*4882a593Smuzhiyun 				bssidx, &cfg->ioctl_buf_sync);
3032*4882a593Smuzhiyun 			if (err < 0) {
3033*4882a593Smuzhiyun 				WL_ERR(("GO SSID setting error %d\n", err));
3034*4882a593Smuzhiyun 				goto exit;
3035*4882a593Smuzhiyun 			}
3036*4882a593Smuzhiyun 
3037*4882a593Smuzhiyun 			if ((err = wl_cfg80211_bss_up(cfg, dev, bssidx, 1)) < 0) {
3038*4882a593Smuzhiyun 				WL_ERR(("GO Bring up error %d\n", err));
3039*4882a593Smuzhiyun 				goto exit;
3040*4882a593Smuzhiyun 			}
3041*4882a593Smuzhiyun 			wl_clr_drv_status(cfg, AP_CREATING, dev);
3042*4882a593Smuzhiyun 		} else
3043*4882a593Smuzhiyun 			WL_DBG(("Bss is already up\n"));
3044*4882a593Smuzhiyun 	} else if (dev_role == NL80211_IFTYPE_AP) {
3045*4882a593Smuzhiyun 
3046*4882a593Smuzhiyun //		if (!wl_get_drv_status(cfg, AP_CREATING, dev)) {
3047*4882a593Smuzhiyun 			/* Make sure fw is in proper state */
3048*4882a593Smuzhiyun 			err = wl_cfg80211_set_ap_role(cfg, dev);
3049*4882a593Smuzhiyun 			if (unlikely(err)) {
3050*4882a593Smuzhiyun 				WL_ERR(("set ap role failed!\n"));
3051*4882a593Smuzhiyun 				goto exit;
3052*4882a593Smuzhiyun 			}
3053*4882a593Smuzhiyun //		}
3054*4882a593Smuzhiyun 
3055*4882a593Smuzhiyun 		/* Device role SoftAP */
3056*4882a593Smuzhiyun 		WL_DBG(("Creating AP bssidx:%d dev_role:%d\n", bssidx, dev_role));
3057*4882a593Smuzhiyun 		/* Clear the status bit after use */
3058*4882a593Smuzhiyun 		wl_clr_drv_status(cfg, AP_CREATING, dev);
3059*4882a593Smuzhiyun 
3060*4882a593Smuzhiyun #ifdef DISABLE_11H_SOFTAP
3061*4882a593Smuzhiyun 		/* Some old WLAN card (e.g. Intel PRO/Wireless 2200BG)
3062*4882a593Smuzhiyun 		 * does not try to connect SoftAP because they cannot detect
3063*4882a593Smuzhiyun 		 * 11h IEs. For this reason, we disable 11h feature in case
3064*4882a593Smuzhiyun 		 * of SoftAP mode. (Related CSP case number: 661635)
3065*4882a593Smuzhiyun 		 */
3066*4882a593Smuzhiyun 		if (is_rsdb_supported == 0) {
3067*4882a593Smuzhiyun 			err = wldev_ioctl_set(dev, WLC_DOWN, &ap, sizeof(s32));
3068*4882a593Smuzhiyun 			if (err < 0) {
3069*4882a593Smuzhiyun 				WL_ERR(("WLC_DOWN error %d\n", err));
3070*4882a593Smuzhiyun 				goto exit;
3071*4882a593Smuzhiyun 			}
3072*4882a593Smuzhiyun 		}
3073*4882a593Smuzhiyun 		err = wldev_ioctl_set(dev, WLC_SET_SPECT_MANAGMENT,
3074*4882a593Smuzhiyun 			&spect, sizeof(s32));
3075*4882a593Smuzhiyun 		if (err < 0) {
3076*4882a593Smuzhiyun 			WL_ERR(("SET SPECT_MANAGMENT error %d\n", err));
3077*4882a593Smuzhiyun 			goto exit;
3078*4882a593Smuzhiyun 		}
3079*4882a593Smuzhiyun #endif /* DISABLE_11H_SOFTAP */
3080*4882a593Smuzhiyun 
3081*4882a593Smuzhiyun #ifdef WL_DISABLE_HE_SOFTAP
3082*4882a593Smuzhiyun 		err = wl_cfg80211_set_he_mode(dev, cfg, bssidx, WL_HE_FEATURES_HE_AP, FALSE);
3083*4882a593Smuzhiyun 		if (err < 0) {
3084*4882a593Smuzhiyun 			WL_ERR(("failed to set he features, error=%d\n", err));
3085*4882a593Smuzhiyun 		}
3086*4882a593Smuzhiyun #endif /* WL_DISABLE_HE_SOFTAP */
3087*4882a593Smuzhiyun 
3088*4882a593Smuzhiyun #ifdef SOFTAP_UAPSD_OFF
3089*4882a593Smuzhiyun 		err = wldev_iovar_setbuf_bsscfg(dev, "wme_apsd", &wme_apsd, sizeof(wme_apsd),
3090*4882a593Smuzhiyun 			cfg->ioctl_buf, WLC_IOCTL_SMLEN, bssidx, &cfg->ioctl_buf_sync);
3091*4882a593Smuzhiyun 		if (err < 0) {
3092*4882a593Smuzhiyun 			WL_ERR(("failed to disable uapsd, error=%d\n", err));
3093*4882a593Smuzhiyun 		}
3094*4882a593Smuzhiyun #endif /* SOFTAP_UAPSD_OFF */
3095*4882a593Smuzhiyun 
3096*4882a593Smuzhiyun #ifdef WLDWDS
3097*4882a593Smuzhiyun 		err = wldev_iovar_setint(dev, "dwds", 1);
3098*4882a593Smuzhiyun 		if (err < 0) {
3099*4882a593Smuzhiyun 			WL_ERR(("set dwds error %d\n", err));
3100*4882a593Smuzhiyun 			goto exit;
3101*4882a593Smuzhiyun 		}
3102*4882a593Smuzhiyun #endif /* WLDWDS */
3103*4882a593Smuzhiyun 
3104*4882a593Smuzhiyun 		err = wldev_ioctl_set(dev, WLC_UP, &ap, sizeof(s32));
3105*4882a593Smuzhiyun 		if (unlikely(err)) {
3106*4882a593Smuzhiyun 			WL_ERR(("WLC_UP error (%d)\n", err));
3107*4882a593Smuzhiyun 			goto exit;
3108*4882a593Smuzhiyun 		}
3109*4882a593Smuzhiyun 
3110*4882a593Smuzhiyun #ifdef MFP
3111*4882a593Smuzhiyun 		if (cfg->bip_pos) {
3112*4882a593Smuzhiyun 			err = wldev_iovar_setbuf_bsscfg(dev, "bip",
3113*4882a593Smuzhiyun 				(const void *)(cfg->bip_pos), WPA_SUITE_LEN, cfg->ioctl_buf,
3114*4882a593Smuzhiyun 				WLC_IOCTL_SMLEN, bssidx, &cfg->ioctl_buf_sync);
3115*4882a593Smuzhiyun 			if (err < 0) {
3116*4882a593Smuzhiyun 				WL_ERR(("bip set error %d\n", err));
3117*4882a593Smuzhiyun 
3118*4882a593Smuzhiyun #ifdef CUSTOMER_HW6
3119*4882a593Smuzhiyun 				if (wl_customer6_legacy_chip_check(cfg,
3120*4882a593Smuzhiyun 					bcmcfg_to_prmry_ndev(cfg))) {
3121*4882a593Smuzhiyun 					/* Ignore bip error: Some older firmwares doesn't
3122*4882a593Smuzhiyun 					 * support bip iovar/ return BCME_NOTUP while trying
3123*4882a593Smuzhiyun 					 * to set bip from AP bring up context. These firmares
3124*4882a593Smuzhiyun 					 * include bip in RSNIE by default. So its okay to ignore
3125*4882a593Smuzhiyun 					 * the error.
3126*4882a593Smuzhiyun 					 */
3127*4882a593Smuzhiyun 					err = BCME_OK;
3128*4882a593Smuzhiyun 				} else
3129*4882a593Smuzhiyun #endif /* CUSTOMER_HW6 */
3130*4882a593Smuzhiyun 
3131*4882a593Smuzhiyun 				{
3132*4882a593Smuzhiyun 					goto exit;
3133*4882a593Smuzhiyun 				}
3134*4882a593Smuzhiyun 			}
3135*4882a593Smuzhiyun 		}
3136*4882a593Smuzhiyun #endif /* MFP */
3137*4882a593Smuzhiyun 
3138*4882a593Smuzhiyun 		err = wldev_iovar_getint(dev, "wsec", (s32 *)&wsec);
3139*4882a593Smuzhiyun 		if (unlikely(err)) {
3140*4882a593Smuzhiyun 			WL_ERR(("Could not get wsec %d\n", err));
3141*4882a593Smuzhiyun 			goto exit;
3142*4882a593Smuzhiyun 		}
3143*4882a593Smuzhiyun 		if (dhd->conf->chip == BCM43430_CHIP_ID && bssidx > 0 &&
3144*4882a593Smuzhiyun 				(wsec & (TKIP_ENABLED|AES_ENABLED))) {
3145*4882a593Smuzhiyun 			struct net_device *primary_ndev = bcmcfg_to_prmry_ndev(cfg);
3146*4882a593Smuzhiyun 			struct ether_addr bssid;
3147*4882a593Smuzhiyun 			int ret = 0;
3148*4882a593Smuzhiyun 			wsec |= WSEC_SWFLAG; // terence 20180628: fix me, this is a workaround
3149*4882a593Smuzhiyun 			err = wldev_iovar_setint_bsscfg(dev, "wsec", wsec, bssidx);
3150*4882a593Smuzhiyun 			if (err < 0) {
3151*4882a593Smuzhiyun 				WL_ERR(("wsec error %d\n", err));
3152*4882a593Smuzhiyun 				goto exit;
3153*4882a593Smuzhiyun 			}
3154*4882a593Smuzhiyun 			bzero(&bssid, sizeof(bssid));
3155*4882a593Smuzhiyun 			ret = wldev_ioctl_get(primary_ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN);
3156*4882a593Smuzhiyun 			if (ret != BCME_NOTASSOCIATED && memcmp(&ether_null, &bssid, ETHER_ADDR_LEN)) {
3157*4882a593Smuzhiyun 				scb_val_t scbval;
3158*4882a593Smuzhiyun 				bzero(&scbval, sizeof(scb_val_t));
3159*4882a593Smuzhiyun 				scbval.val = WLAN_REASON_DEAUTH_LEAVING;
3160*4882a593Smuzhiyun 				wldev_ioctl_set(primary_ndev, WLC_DISASSOC, &scbval, sizeof(scb_val_t));
3161*4882a593Smuzhiyun 			}
3162*4882a593Smuzhiyun 		}
3163*4882a593Smuzhiyun 		if ((wsec == WEP_ENABLED) && cfg->wep_key.len) {
3164*4882a593Smuzhiyun 			WL_DBG(("Applying buffered WEP KEY \n"));
3165*4882a593Smuzhiyun 			err = wldev_iovar_setbuf_bsscfg(dev, "wsec_key", &cfg->wep_key,
3166*4882a593Smuzhiyun 				sizeof(struct wl_wsec_key), cfg->ioctl_buf,
3167*4882a593Smuzhiyun 				WLC_IOCTL_MAXLEN, bssidx, &cfg->ioctl_buf_sync);
3168*4882a593Smuzhiyun 			/* clear the key after use */
3169*4882a593Smuzhiyun 			bzero(&cfg->wep_key, sizeof(struct wl_wsec_key));
3170*4882a593Smuzhiyun 			if (unlikely(err)) {
3171*4882a593Smuzhiyun 				WL_ERR(("WLC_SET_KEY error (%d)\n", err));
3172*4882a593Smuzhiyun 				goto exit;
3173*4882a593Smuzhiyun 			}
3174*4882a593Smuzhiyun 		}
3175*4882a593Smuzhiyun 
3176*4882a593Smuzhiyun #ifdef MFP
3177*4882a593Smuzhiyun 		if (cfg->mfp_mode) {
3178*4882a593Smuzhiyun 			/* This needs to go after wsec otherwise the wsec command will
3179*4882a593Smuzhiyun 			 * overwrite the values set by MFP
3180*4882a593Smuzhiyun 			 */
3181*4882a593Smuzhiyun 			err = wldev_iovar_setint_bsscfg(dev, "mfp", cfg->mfp_mode, bssidx);
3182*4882a593Smuzhiyun 			if (err < 0) {
3183*4882a593Smuzhiyun 				WL_ERR(("MFP Setting failed. ret = %d \n", err));
3184*4882a593Smuzhiyun 				/* If fw doesn't support mfp, Ignore the error */
3185*4882a593Smuzhiyun 				if (err != BCME_UNSUPPORTED) {
3186*4882a593Smuzhiyun 					goto exit;
3187*4882a593Smuzhiyun 				}
3188*4882a593Smuzhiyun 			}
3189*4882a593Smuzhiyun 		}
3190*4882a593Smuzhiyun #endif /* MFP */
3191*4882a593Smuzhiyun 
3192*4882a593Smuzhiyun 		bzero(&join_params, sizeof(join_params));
3193*4882a593Smuzhiyun 		/* join parameters starts with ssid */
3194*4882a593Smuzhiyun 		join_params_size = sizeof(join_params.ssid);
3195*4882a593Smuzhiyun 		join_params.ssid.SSID_len = MIN(cfg->hostapd_ssid.SSID_len,
3196*4882a593Smuzhiyun 			(uint32)DOT11_MAX_SSID_LEN);
3197*4882a593Smuzhiyun 		memcpy(join_params.ssid.SSID, cfg->hostapd_ssid.SSID,
3198*4882a593Smuzhiyun 			join_params.ssid.SSID_len);
3199*4882a593Smuzhiyun 		join_params.ssid.SSID_len = htod32(join_params.ssid.SSID_len);
3200*4882a593Smuzhiyun 
3201*4882a593Smuzhiyun 		wl_ext_get_sec(dev, 0, sec, sizeof(sec), TRUE);
3202*4882a593Smuzhiyun 		WL_MSG(dev->name, "Creating AP with sec=%s\n", sec);
3203*4882a593Smuzhiyun 		/* create softap */
3204*4882a593Smuzhiyun 		if ((err = wldev_ioctl_set(dev, WLC_SET_SSID, &join_params,
3205*4882a593Smuzhiyun 			join_params_size)) != 0) {
3206*4882a593Smuzhiyun 			WL_ERR(("SoftAP/GO set ssid failed! \n"));
3207*4882a593Smuzhiyun 			goto exit;
3208*4882a593Smuzhiyun 		} else {
3209*4882a593Smuzhiyun 			WL_DBG((" SoftAP SSID \"%s\" \n", join_params.ssid.SSID));
3210*4882a593Smuzhiyun 		}
3211*4882a593Smuzhiyun 
3212*4882a593Smuzhiyun 		if (bssidx != 0) {
3213*4882a593Smuzhiyun 			/* AP on Virtual Interface */
3214*4882a593Smuzhiyun 			if ((err = wl_cfg80211_bss_up(cfg, dev, bssidx, 1)) < 0) {
3215*4882a593Smuzhiyun 				WL_ERR(("AP Bring up error %d\n", err));
3216*4882a593Smuzhiyun 				goto exit;
3217*4882a593Smuzhiyun 			}
3218*4882a593Smuzhiyun 		}
3219*4882a593Smuzhiyun 
3220*4882a593Smuzhiyun 	} else {
3221*4882a593Smuzhiyun 		WL_ERR(("Wrong interface type %d\n", dev_role));
3222*4882a593Smuzhiyun 		goto exit;
3223*4882a593Smuzhiyun 	}
3224*4882a593Smuzhiyun 
3225*4882a593Smuzhiyun 	SUPP_LOG(("AP/GO UP\n"));
3226*4882a593Smuzhiyun 
3227*4882a593Smuzhiyun exit:
3228*4882a593Smuzhiyun 	if (cfg->wep_key.len) {
3229*4882a593Smuzhiyun 		bzero(&cfg->wep_key, sizeof(struct wl_wsec_key));
3230*4882a593Smuzhiyun 	}
3231*4882a593Smuzhiyun 
3232*4882a593Smuzhiyun #ifdef MFP
3233*4882a593Smuzhiyun 	if (cfg->mfp_mode) {
3234*4882a593Smuzhiyun 		cfg->mfp_mode = 0;
3235*4882a593Smuzhiyun 	}
3236*4882a593Smuzhiyun 
3237*4882a593Smuzhiyun 	if (cfg->bip_pos) {
3238*4882a593Smuzhiyun 		cfg->bip_pos = NULL;
3239*4882a593Smuzhiyun 	}
3240*4882a593Smuzhiyun #endif /* MFP */
3241*4882a593Smuzhiyun 
3242*4882a593Smuzhiyun 	if (err) {
3243*4882a593Smuzhiyun 		SUPP_LOG(("AP/GO bring up fail. err:%d\n", err));
3244*4882a593Smuzhiyun 		/* Cancel work if scheduled */
3245*4882a593Smuzhiyun 		if (delayed_work_pending(&cfg->ap_work)) {
3246*4882a593Smuzhiyun 			cancel_delayed_work_sync(&cfg->ap_work);
3247*4882a593Smuzhiyun 			WL_DBG(("cancelled ap_work\n"));
3248*4882a593Smuzhiyun 		}
3249*4882a593Smuzhiyun 	}
3250*4882a593Smuzhiyun 	return err;
3251*4882a593Smuzhiyun }
3252*4882a593Smuzhiyun 
3253*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \
3254*4882a593Smuzhiyun 	defined(WL_COMPAT_WIRELESS)
3255*4882a593Smuzhiyun s32
wl_cfg80211_parse_ap_ies(struct net_device * dev,struct cfg80211_beacon_data * info,struct parsed_ies * ies)3256*4882a593Smuzhiyun wl_cfg80211_parse_ap_ies(
3257*4882a593Smuzhiyun 	struct net_device *dev,
3258*4882a593Smuzhiyun 	struct cfg80211_beacon_data *info,
3259*4882a593Smuzhiyun 	struct parsed_ies *ies)
3260*4882a593Smuzhiyun {
3261*4882a593Smuzhiyun 	struct parsed_ies prb_ies;
3262*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3263*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
3264*4882a593Smuzhiyun 	const u8 *vndr = NULL;
3265*4882a593Smuzhiyun 	u32 vndr_ie_len = 0;
3266*4882a593Smuzhiyun 	s32 err = BCME_OK;
3267*4882a593Smuzhiyun 
3268*4882a593Smuzhiyun 	/* Parse Beacon IEs */
3269*4882a593Smuzhiyun 	if (wl_cfg80211_parse_ies((const u8 *)info->tail,
3270*4882a593Smuzhiyun 		info->tail_len, ies) < 0) {
3271*4882a593Smuzhiyun 		WL_ERR(("Beacon get IEs failed \n"));
3272*4882a593Smuzhiyun 		err = -EINVAL;
3273*4882a593Smuzhiyun 		goto fail;
3274*4882a593Smuzhiyun 	}
3275*4882a593Smuzhiyun 
3276*4882a593Smuzhiyun 	if ((err = wl_cfg80211_config_rsnxe_ie(cfg, dev,
3277*4882a593Smuzhiyun 		(const u8 *)info->tail, info->tail_len)) < 0) {
3278*4882a593Smuzhiyun 		WL_ERR(("Failed to configure rsnxe ie: %d\n", err));
3279*4882a593Smuzhiyun 		err = -EINVAL;
3280*4882a593Smuzhiyun 		goto fail;
3281*4882a593Smuzhiyun 	}
3282*4882a593Smuzhiyun 
3283*4882a593Smuzhiyun 	vndr = (const u8 *)info->proberesp_ies;
3284*4882a593Smuzhiyun 	vndr_ie_len = (uint32)info->proberesp_ies_len;
3285*4882a593Smuzhiyun 
3286*4882a593Smuzhiyun 	if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) {
3287*4882a593Smuzhiyun 		/* SoftAP mode */
3288*4882a593Smuzhiyun 		const struct ieee80211_mgmt *mgmt;
3289*4882a593Smuzhiyun 		mgmt = (const struct ieee80211_mgmt *)info->probe_resp;
3290*4882a593Smuzhiyun 		if (mgmt != NULL) {
3291*4882a593Smuzhiyun 			vndr = (const u8 *)&mgmt->u.probe_resp.variable;
3292*4882a593Smuzhiyun 			vndr_ie_len = (uint32)(info->probe_resp_len -
3293*4882a593Smuzhiyun 				offsetof(const struct ieee80211_mgmt, u.probe_resp.variable));
3294*4882a593Smuzhiyun 		}
3295*4882a593Smuzhiyun 	}
3296*4882a593Smuzhiyun 	/* Parse Probe Response IEs */
3297*4882a593Smuzhiyun 	if (wl_cfg80211_parse_ies((const u8 *)vndr, vndr_ie_len, &prb_ies) < 0) {
3298*4882a593Smuzhiyun 		WL_ERR(("PROBE RESP get IEs failed \n"));
3299*4882a593Smuzhiyun 		err = -EINVAL;
3300*4882a593Smuzhiyun 	}
3301*4882a593Smuzhiyun fail:
3302*4882a593Smuzhiyun 
3303*4882a593Smuzhiyun 	return err;
3304*4882a593Smuzhiyun }
3305*4882a593Smuzhiyun 
3306*4882a593Smuzhiyun s32
wl_cfg80211_set_ies(struct net_device * dev,struct cfg80211_beacon_data * info,s32 bssidx)3307*4882a593Smuzhiyun wl_cfg80211_set_ies(
3308*4882a593Smuzhiyun 	struct net_device *dev,
3309*4882a593Smuzhiyun 	struct cfg80211_beacon_data *info,
3310*4882a593Smuzhiyun 	s32 bssidx)
3311*4882a593Smuzhiyun {
3312*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3313*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
3314*4882a593Smuzhiyun 	const u8 *vndr = NULL;
3315*4882a593Smuzhiyun 	u32 vndr_ie_len = 0;
3316*4882a593Smuzhiyun 	s32 err = BCME_OK;
3317*4882a593Smuzhiyun 
3318*4882a593Smuzhiyun 	/* Set Beacon IEs to FW */
3319*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_mgmt_vndr_ies(cfg, ndev_to_cfgdev(dev), bssidx,
3320*4882a593Smuzhiyun 		VNDR_IE_BEACON_FLAG, (const u8 *)info->tail,
3321*4882a593Smuzhiyun 		info->tail_len)) < 0) {
3322*4882a593Smuzhiyun 		WL_ERR(("Set Beacon IE Failed \n"));
3323*4882a593Smuzhiyun 	} else {
3324*4882a593Smuzhiyun 		WL_DBG(("Applied Vndr IEs for Beacon \n"));
3325*4882a593Smuzhiyun 	}
3326*4882a593Smuzhiyun 
3327*4882a593Smuzhiyun 	vndr = (const u8 *)info->proberesp_ies;
3328*4882a593Smuzhiyun 	vndr_ie_len = (uint32)info->proberesp_ies_len;
3329*4882a593Smuzhiyun 
3330*4882a593Smuzhiyun 	if (dhd->op_mode & DHD_FLAG_HOSTAP_MODE) {
3331*4882a593Smuzhiyun 		/* SoftAP mode */
3332*4882a593Smuzhiyun 		const struct ieee80211_mgmt *mgmt;
3333*4882a593Smuzhiyun 		mgmt = (const struct ieee80211_mgmt *)info->probe_resp;
3334*4882a593Smuzhiyun 		if (mgmt != NULL) {
3335*4882a593Smuzhiyun 			vndr = (const u8 *)&mgmt->u.probe_resp.variable;
3336*4882a593Smuzhiyun 			vndr_ie_len = (uint32)(info->probe_resp_len -
3337*4882a593Smuzhiyun 				offsetof(struct ieee80211_mgmt, u.probe_resp.variable));
3338*4882a593Smuzhiyun 		}
3339*4882a593Smuzhiyun 	}
3340*4882a593Smuzhiyun 
3341*4882a593Smuzhiyun 	/* Set Probe Response IEs to FW */
3342*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_mgmt_vndr_ies(cfg, ndev_to_cfgdev(dev), bssidx,
3343*4882a593Smuzhiyun 		VNDR_IE_PRBRSP_FLAG, vndr, vndr_ie_len)) < 0) {
3344*4882a593Smuzhiyun 		WL_ERR(("Set Probe Resp IE Failed \n"));
3345*4882a593Smuzhiyun 	} else {
3346*4882a593Smuzhiyun 		WL_DBG(("Applied Vndr IEs for Probe Resp \n"));
3347*4882a593Smuzhiyun 	}
3348*4882a593Smuzhiyun 
3349*4882a593Smuzhiyun 	return err;
3350*4882a593Smuzhiyun }
3351*4882a593Smuzhiyun #endif /* LINUX_VERSION >= VERSION(3,4,0) || WL_COMPAT_WIRELESS */
3352*4882a593Smuzhiyun 
wl_cfg80211_hostapd_sec(struct net_device * dev,struct parsed_ies * ies,s32 bssidx)3353*4882a593Smuzhiyun static s32 wl_cfg80211_hostapd_sec(
3354*4882a593Smuzhiyun 	struct net_device *dev,
3355*4882a593Smuzhiyun 	struct parsed_ies *ies,
3356*4882a593Smuzhiyun 	s32 bssidx)
3357*4882a593Smuzhiyun {
3358*4882a593Smuzhiyun 	bool update_bss = 0;
3359*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
3360*4882a593Smuzhiyun 	wl_cfgbss_t *bss = wl_get_cfgbss_by_wdev(cfg, dev->ieee80211_ptr);
3361*4882a593Smuzhiyun 
3362*4882a593Smuzhiyun 	if (!bss) {
3363*4882a593Smuzhiyun 		WL_ERR(("cfgbss is NULL \n"));
3364*4882a593Smuzhiyun 		return -EINVAL;
3365*4882a593Smuzhiyun 	}
3366*4882a593Smuzhiyun 
3367*4882a593Smuzhiyun 	if (ies->wps_ie) {
3368*4882a593Smuzhiyun 		/* Remove after verification.
3369*4882a593Smuzhiyun 		 * Setting IE part moved to set_ies func
3370*4882a593Smuzhiyun 		 */
3371*4882a593Smuzhiyun 		if (bss->wps_ie &&
3372*4882a593Smuzhiyun 			memcmp(bss->wps_ie, ies->wps_ie, ies->wps_ie_len)) {
3373*4882a593Smuzhiyun 			WL_DBG((" WPS IE is changed\n"));
3374*4882a593Smuzhiyun 			MFREE(cfg->osh, bss->wps_ie, bss->wps_ie[1] + 2);
3375*4882a593Smuzhiyun 			bss->wps_ie = MALLOCZ(cfg->osh, ies->wps_ie_len);
3376*4882a593Smuzhiyun 			if (bss->wps_ie) {
3377*4882a593Smuzhiyun 				memcpy(bss->wps_ie, ies->wps_ie, ies->wps_ie_len);
3378*4882a593Smuzhiyun 			}
3379*4882a593Smuzhiyun 		} else if (bss->wps_ie == NULL) {
3380*4882a593Smuzhiyun 			WL_DBG((" WPS IE is added\n"));
3381*4882a593Smuzhiyun 			bss->wps_ie = MALLOCZ(cfg->osh, ies->wps_ie_len);
3382*4882a593Smuzhiyun 			if (bss->wps_ie) {
3383*4882a593Smuzhiyun 				memcpy(bss->wps_ie, ies->wps_ie, ies->wps_ie_len);
3384*4882a593Smuzhiyun 			}
3385*4882a593Smuzhiyun 		}
3386*4882a593Smuzhiyun 
3387*4882a593Smuzhiyun #if defined(SUPPORT_SOFTAP_WPAWPA2_MIXED)
3388*4882a593Smuzhiyun 		if (ies->wpa_ie != NULL && ies->wpa2_ie != NULL) {
3389*4882a593Smuzhiyun 			WL_ERR(("update bss - wpa_ie and  wpa2_ie is not null\n"));
3390*4882a593Smuzhiyun 			if (!bss->security_mode) {
3391*4882a593Smuzhiyun 				/* change from open mode to security mode */
3392*4882a593Smuzhiyun 				update_bss = true;
3393*4882a593Smuzhiyun 				bss->wpa_ie = MALLOCZ(cfg->osh,
3394*4882a593Smuzhiyun 					ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN);
3395*4882a593Smuzhiyun 				if (bss->wpa_ie) {
3396*4882a593Smuzhiyun 					memcpy(bss->wpa_ie, ies->wpa_ie,
3397*4882a593Smuzhiyun 						ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN);
3398*4882a593Smuzhiyun 				}
3399*4882a593Smuzhiyun 				bss->rsn_ie = MALLOCZ(cfg->osh,
3400*4882a593Smuzhiyun 						ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN);
3401*4882a593Smuzhiyun 				if (bss->rsn_ie) {
3402*4882a593Smuzhiyun 					memcpy(bss->rsn_ie, ies->wpa2_ie,
3403*4882a593Smuzhiyun 						ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN);
3404*4882a593Smuzhiyun 				}
3405*4882a593Smuzhiyun 			} else {
3406*4882a593Smuzhiyun 				/* change from (WPA or WPA2 or WPA/WPA2) to WPA/WPA2 mixed mode */
3407*4882a593Smuzhiyun 				if (bss->wpa_ie) {
3408*4882a593Smuzhiyun 					if (memcmp(bss->wpa_ie,
3409*4882a593Smuzhiyun 					ies->wpa_ie, ies->wpa_ie->length +
3410*4882a593Smuzhiyun 					WPA_RSN_IE_TAG_FIXED_LEN)) {
3411*4882a593Smuzhiyun 						MFREE(cfg->osh, bss->wpa_ie,
3412*4882a593Smuzhiyun 							bss->wpa_ie[1] + WPA_RSN_IE_TAG_FIXED_LEN);
3413*4882a593Smuzhiyun 						update_bss = true;
3414*4882a593Smuzhiyun 						bss->wpa_ie = MALLOCZ(cfg->osh,
3415*4882a593Smuzhiyun 							ies->wpa_ie->length
3416*4882a593Smuzhiyun 							+ WPA_RSN_IE_TAG_FIXED_LEN);
3417*4882a593Smuzhiyun 						if (bss->wpa_ie) {
3418*4882a593Smuzhiyun 							memcpy(bss->wpa_ie, ies->wpa_ie,
3419*4882a593Smuzhiyun 								ies->wpa_ie->length
3420*4882a593Smuzhiyun 								+ WPA_RSN_IE_TAG_FIXED_LEN);
3421*4882a593Smuzhiyun 						}
3422*4882a593Smuzhiyun 					}
3423*4882a593Smuzhiyun 				}
3424*4882a593Smuzhiyun 				else {
3425*4882a593Smuzhiyun 					update_bss = true;
3426*4882a593Smuzhiyun 					bss->wpa_ie = MALLOCZ(cfg->osh,
3427*4882a593Smuzhiyun 						ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN);
3428*4882a593Smuzhiyun 					if (bss->wpa_ie) {
3429*4882a593Smuzhiyun 						memcpy(bss->wpa_ie, ies->wpa_ie,
3430*4882a593Smuzhiyun 							ies->wpa_ie->length
3431*4882a593Smuzhiyun 							+ WPA_RSN_IE_TAG_FIXED_LEN);
3432*4882a593Smuzhiyun 					}
3433*4882a593Smuzhiyun 				}
3434*4882a593Smuzhiyun 				if (bss->rsn_ie) {
3435*4882a593Smuzhiyun 					if (memcmp(bss->rsn_ie,
3436*4882a593Smuzhiyun 					ies->wpa2_ie,
3437*4882a593Smuzhiyun 					ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN)) {
3438*4882a593Smuzhiyun 						update_bss = true;
3439*4882a593Smuzhiyun 						MFREE(cfg->osh, bss->rsn_ie,
3440*4882a593Smuzhiyun 							bss->rsn_ie[1] + WPA_RSN_IE_TAG_FIXED_LEN);
3441*4882a593Smuzhiyun 						bss->rsn_ie = MALLOCZ(cfg->osh,
3442*4882a593Smuzhiyun 							ies->wpa2_ie->len
3443*4882a593Smuzhiyun 							+ WPA_RSN_IE_TAG_FIXED_LEN);
3444*4882a593Smuzhiyun 						if (bss->rsn_ie) {
3445*4882a593Smuzhiyun 							memcpy(bss->rsn_ie, ies->wpa2_ie,
3446*4882a593Smuzhiyun 								ies->wpa2_ie->len
3447*4882a593Smuzhiyun 								+ WPA_RSN_IE_TAG_FIXED_LEN);
3448*4882a593Smuzhiyun 						}
3449*4882a593Smuzhiyun 					}
3450*4882a593Smuzhiyun 				}
3451*4882a593Smuzhiyun 				else {
3452*4882a593Smuzhiyun 					update_bss = true;
3453*4882a593Smuzhiyun 					bss->rsn_ie = MALLOCZ(cfg->osh,
3454*4882a593Smuzhiyun 						ies->wpa2_ie->len
3455*4882a593Smuzhiyun 						+ WPA_RSN_IE_TAG_FIXED_LEN);
3456*4882a593Smuzhiyun 					if (bss->rsn_ie) {
3457*4882a593Smuzhiyun 						memcpy(bss->rsn_ie, ies->wpa2_ie,
3458*4882a593Smuzhiyun 							ies->wpa2_ie->len
3459*4882a593Smuzhiyun 							+ WPA_RSN_IE_TAG_FIXED_LEN);
3460*4882a593Smuzhiyun 					}
3461*4882a593Smuzhiyun 				}
3462*4882a593Smuzhiyun 			}
3463*4882a593Smuzhiyun 			WL_ERR(("update_bss=%d\n", update_bss));
3464*4882a593Smuzhiyun 			if (update_bss) {
3465*4882a593Smuzhiyun 				bss->security_mode = true;
3466*4882a593Smuzhiyun 				wl_cfg80211_bss_up(cfg, dev, bssidx, 0);
3467*4882a593Smuzhiyun 				if (wl_validate_wpaie_wpa2ie(dev, ies->wpa_ie,
3468*4882a593Smuzhiyun 					ies->wpa2_ie, bssidx)  < 0) {
3469*4882a593Smuzhiyun 					return BCME_ERROR;
3470*4882a593Smuzhiyun 				}
3471*4882a593Smuzhiyun 				wl_cfg80211_bss_up(cfg, dev, bssidx, 1);
3472*4882a593Smuzhiyun 			}
3473*4882a593Smuzhiyun 
3474*4882a593Smuzhiyun 		}
3475*4882a593Smuzhiyun 		else
3476*4882a593Smuzhiyun #endif /* SUPPORT_SOFTAP_WPAWPA2_MIXED */
3477*4882a593Smuzhiyun 		if ((ies->wpa_ie != NULL || ies->wpa2_ie != NULL)) {
3478*4882a593Smuzhiyun 			if (!bss->security_mode) {
3479*4882a593Smuzhiyun 				/* change from open mode to security mode */
3480*4882a593Smuzhiyun 				update_bss = true;
3481*4882a593Smuzhiyun 				if (ies->wpa_ie != NULL) {
3482*4882a593Smuzhiyun 					bss->wpa_ie = MALLOCZ(cfg->osh,
3483*4882a593Smuzhiyun 						ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN);
3484*4882a593Smuzhiyun 					if (bss->wpa_ie) {
3485*4882a593Smuzhiyun 						memcpy(bss->wpa_ie,
3486*4882a593Smuzhiyun 							ies->wpa_ie,
3487*4882a593Smuzhiyun 							ies->wpa_ie->length
3488*4882a593Smuzhiyun 							+ WPA_RSN_IE_TAG_FIXED_LEN);
3489*4882a593Smuzhiyun 					}
3490*4882a593Smuzhiyun 				} else {
3491*4882a593Smuzhiyun 					bss->rsn_ie = MALLOCZ(cfg->osh,
3492*4882a593Smuzhiyun 						ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN);
3493*4882a593Smuzhiyun 					if (bss->rsn_ie) {
3494*4882a593Smuzhiyun 						memcpy(bss->rsn_ie,
3495*4882a593Smuzhiyun 							ies->wpa2_ie,
3496*4882a593Smuzhiyun 							ies->wpa2_ie->len
3497*4882a593Smuzhiyun 							+ WPA_RSN_IE_TAG_FIXED_LEN);
3498*4882a593Smuzhiyun 					}
3499*4882a593Smuzhiyun 				}
3500*4882a593Smuzhiyun 			} else if (bss->wpa_ie) {
3501*4882a593Smuzhiyun 				/* change from WPA2 mode to WPA mode */
3502*4882a593Smuzhiyun 				if (ies->wpa_ie != NULL) {
3503*4882a593Smuzhiyun 					update_bss = true;
3504*4882a593Smuzhiyun 					MFREE(cfg->osh, bss->rsn_ie,
3505*4882a593Smuzhiyun 						bss->rsn_ie[1] + WPA_RSN_IE_TAG_FIXED_LEN);
3506*4882a593Smuzhiyun 					bss->rsn_ie = NULL;
3507*4882a593Smuzhiyun 					bss->wpa_ie = MALLOCZ(cfg->osh,
3508*4882a593Smuzhiyun 						ies->wpa_ie->length + WPA_RSN_IE_TAG_FIXED_LEN);
3509*4882a593Smuzhiyun 					if (bss->wpa_ie) {
3510*4882a593Smuzhiyun 						memcpy(bss->wpa_ie,
3511*4882a593Smuzhiyun 							ies->wpa_ie,
3512*4882a593Smuzhiyun 							ies->wpa_ie->length
3513*4882a593Smuzhiyun 							+ WPA_RSN_IE_TAG_FIXED_LEN);
3514*4882a593Smuzhiyun 					}
3515*4882a593Smuzhiyun 				} else if (memcmp(bss->rsn_ie,
3516*4882a593Smuzhiyun 					ies->wpa2_ie, ies->wpa2_ie->len
3517*4882a593Smuzhiyun 					+ WPA_RSN_IE_TAG_FIXED_LEN)) {
3518*4882a593Smuzhiyun 					update_bss = true;
3519*4882a593Smuzhiyun 					MFREE(cfg->osh, bss->rsn_ie,
3520*4882a593Smuzhiyun 						bss->rsn_ie[1] + WPA_RSN_IE_TAG_FIXED_LEN);
3521*4882a593Smuzhiyun 					bss->rsn_ie = MALLOCZ(cfg->osh,
3522*4882a593Smuzhiyun 						ies->wpa2_ie->len + WPA_RSN_IE_TAG_FIXED_LEN);
3523*4882a593Smuzhiyun 					if (bss->rsn_ie) {
3524*4882a593Smuzhiyun 						memcpy(bss->rsn_ie,
3525*4882a593Smuzhiyun 							ies->wpa2_ie,
3526*4882a593Smuzhiyun 							ies->wpa2_ie->len
3527*4882a593Smuzhiyun 							+ WPA_RSN_IE_TAG_FIXED_LEN);
3528*4882a593Smuzhiyun 					}
3529*4882a593Smuzhiyun 					bss->wpa_ie = NULL;
3530*4882a593Smuzhiyun 				}
3531*4882a593Smuzhiyun 			}
3532*4882a593Smuzhiyun 			if (update_bss) {
3533*4882a593Smuzhiyun 				bss->security_mode = true;
3534*4882a593Smuzhiyun 				wl_cfg80211_bss_up(cfg, dev, bssidx, 0);
3535*4882a593Smuzhiyun 				if (wl_validate_wpa2ie(dev, ies->wpa2_ie, bssidx)  < 0 ||
3536*4882a593Smuzhiyun 					wl_validate_wpaie(dev, ies->wpa_ie, bssidx) < 0) {
3537*4882a593Smuzhiyun 					return BCME_ERROR;
3538*4882a593Smuzhiyun 				}
3539*4882a593Smuzhiyun 				wl_cfg80211_bss_up(cfg, dev, bssidx, 1);
3540*4882a593Smuzhiyun 			}
3541*4882a593Smuzhiyun 		}
3542*4882a593Smuzhiyun 	} else {
3543*4882a593Smuzhiyun 		WL_ERR(("No WPSIE in beacon \n"));
3544*4882a593Smuzhiyun 	}
3545*4882a593Smuzhiyun 	return 0;
3546*4882a593Smuzhiyun }
3547*4882a593Smuzhiyun 
3548*4882a593Smuzhiyun static s32
wl_cfg80211_set_scb_timings(struct bcm_cfg80211 * cfg,struct net_device * dev)3549*4882a593Smuzhiyun wl_cfg80211_set_scb_timings(
3550*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg,
3551*4882a593Smuzhiyun 	struct net_device *dev)
3552*4882a593Smuzhiyun {
3553*4882a593Smuzhiyun 	int err;
3554*4882a593Smuzhiyun 	u32 ps_pretend;
3555*4882a593Smuzhiyun 	wl_scb_probe_t scb_probe;
3556*4882a593Smuzhiyun 	u32 ps_pretend_retries;
3557*4882a593Smuzhiyun 
3558*4882a593Smuzhiyun 	bzero(&scb_probe, sizeof(wl_scb_probe_t));
3559*4882a593Smuzhiyun 	scb_probe.scb_timeout = WL_SCB_TIMEOUT;
3560*4882a593Smuzhiyun 	scb_probe.scb_activity_time = WL_SCB_ACTIVITY_TIME;
3561*4882a593Smuzhiyun 	scb_probe.scb_max_probe = WL_SCB_MAX_PROBE;
3562*4882a593Smuzhiyun 	err = wldev_iovar_setbuf(dev, "scb_probe", (void *)&scb_probe,
3563*4882a593Smuzhiyun 		sizeof(wl_scb_probe_t), cfg->ioctl_buf, WLC_IOCTL_SMLEN,
3564*4882a593Smuzhiyun 		&cfg->ioctl_buf_sync);
3565*4882a593Smuzhiyun 	if (unlikely(err)) {
3566*4882a593Smuzhiyun 		WL_ERR(("set 'scb_probe' failed, error = %d\n", err));
3567*4882a593Smuzhiyun 		return err;
3568*4882a593Smuzhiyun 	}
3569*4882a593Smuzhiyun 
3570*4882a593Smuzhiyun 	ps_pretend_retries = WL_PSPRETEND_RETRY_LIMIT;
3571*4882a593Smuzhiyun 	err = wldev_iovar_setint(dev, "pspretend_retry_limit", ps_pretend_retries);
3572*4882a593Smuzhiyun 	if (unlikely(err)) {
3573*4882a593Smuzhiyun 		if (err == BCME_UNSUPPORTED) {
3574*4882a593Smuzhiyun 			/* Ignore error if fw doesn't support the iovar */
3575*4882a593Smuzhiyun 			WL_DBG(("set 'pspretend_retry_limit %d' failed, error = %d\n",
3576*4882a593Smuzhiyun 				ps_pretend_retries, err));
3577*4882a593Smuzhiyun 		} else {
3578*4882a593Smuzhiyun 			WL_ERR(("set 'pspretend_retry_limit %d' failed, error = %d\n",
3579*4882a593Smuzhiyun 				ps_pretend_retries, err));
3580*4882a593Smuzhiyun 			return err;
3581*4882a593Smuzhiyun 		}
3582*4882a593Smuzhiyun 	}
3583*4882a593Smuzhiyun 
3584*4882a593Smuzhiyun 	ps_pretend = MAX(WL_SCB_MAX_PROBE / 2, WL_MIN_PSPRETEND_THRESHOLD);
3585*4882a593Smuzhiyun 	err = wldev_iovar_setint(dev, "pspretend_threshold", ps_pretend);
3586*4882a593Smuzhiyun 	if (unlikely(err)) {
3587*4882a593Smuzhiyun 		if (err == BCME_UNSUPPORTED) {
3588*4882a593Smuzhiyun 			/* Ignore error if fw doesn't support the iovar */
3589*4882a593Smuzhiyun 			WL_DBG(("wl pspretend_threshold %d set error %d\n",
3590*4882a593Smuzhiyun 				ps_pretend, err));
3591*4882a593Smuzhiyun 		} else {
3592*4882a593Smuzhiyun 			WL_ERR(("wl pspretend_threshold %d set error %d\n",
3593*4882a593Smuzhiyun 				ps_pretend, err));
3594*4882a593Smuzhiyun 			return err;
3595*4882a593Smuzhiyun 		}
3596*4882a593Smuzhiyun 	}
3597*4882a593Smuzhiyun 
3598*4882a593Smuzhiyun 	return 0;
3599*4882a593Smuzhiyun }
3600*4882a593Smuzhiyun 
3601*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \
3602*4882a593Smuzhiyun 	defined(WL_COMPAT_WIRELESS)
3603*4882a593Smuzhiyun s32
wl_cfg80211_start_ap(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_ap_settings * info)3604*4882a593Smuzhiyun wl_cfg80211_start_ap(
3605*4882a593Smuzhiyun 	struct wiphy *wiphy,
3606*4882a593Smuzhiyun 	struct net_device *dev,
3607*4882a593Smuzhiyun 	struct cfg80211_ap_settings *info)
3608*4882a593Smuzhiyun {
3609*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
3610*4882a593Smuzhiyun 	s32 err = BCME_OK;
3611*4882a593Smuzhiyun 	struct parsed_ies ies;
3612*4882a593Smuzhiyun 	s32 bssidx = 0;
3613*4882a593Smuzhiyun 	u32 dev_role = 0;
3614*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
3615*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
3616*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
3617*4882a593Smuzhiyun 
3618*4882a593Smuzhiyun 	WL_DBG(("Enter \n"));
3619*4882a593Smuzhiyun 
3620*4882a593Smuzhiyun 	if ((bssidx = wl_get_bssidx_by_wdev(cfg, dev->ieee80211_ptr)) < 0) {
3621*4882a593Smuzhiyun 		WL_ERR(("Find p2p index from wdev(%p) failed\n", dev->ieee80211_ptr));
3622*4882a593Smuzhiyun 		return BCME_ERROR;
3623*4882a593Smuzhiyun 	}
3624*4882a593Smuzhiyun 
3625*4882a593Smuzhiyun 	if (p2p_is_on(cfg) && (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO)) {
3626*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_P2P_GO;
3627*4882a593Smuzhiyun 	} else if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) {
3628*4882a593Smuzhiyun 
3629*4882a593Smuzhiyun 		if (!wl_get_drv_status(cfg, AP_CREATING, dev)) {
3630*4882a593Smuzhiyun 			/* Make sure fw is in proper state */
3631*4882a593Smuzhiyun 			err = wl_cfg80211_set_ap_role(cfg, dev);
3632*4882a593Smuzhiyun 			if (unlikely(err)) {
3633*4882a593Smuzhiyun 				WL_ERR(("set ap role failed!\n"));
3634*4882a593Smuzhiyun 				return BCME_ERROR;
3635*4882a593Smuzhiyun 			}
3636*4882a593Smuzhiyun 		}
3637*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_AP;
3638*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
3639*4882a593Smuzhiyun 		dhd->op_mode |= DHD_FLAG_HOSTAP_MODE;
3640*4882a593Smuzhiyun 		err = dhd_ndo_enable(dhd, FALSE);
3641*4882a593Smuzhiyun 		WL_DBG(("Disabling NDO on Hostapd mode %d\n", err));
3642*4882a593Smuzhiyun 		if (err) {
3643*4882a593Smuzhiyun 			WL_ERR(("Disabling NDO Failed %d\n", err));
3644*4882a593Smuzhiyun 		}
3645*4882a593Smuzhiyun 		wl_wlfc_enable(cfg, TRUE);
3646*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
3647*4882a593Smuzhiyun 		wl_ext_iapsta_update_iftype(dev, WL_IF_TYPE_AP);
3648*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
3649*4882a593Smuzhiyun #ifdef PKT_FILTER_SUPPORT
3650*4882a593Smuzhiyun 		/* Disable packet filter */
3651*4882a593Smuzhiyun 		if (dhd->early_suspended) {
3652*4882a593Smuzhiyun 			WL_ERR(("Disable pkt_filter\n"));
3653*4882a593Smuzhiyun 			dhd_enable_packet_filter(0, dhd);
3654*4882a593Smuzhiyun #ifdef APF
3655*4882a593Smuzhiyun 			dhd_dev_apf_disable_filter(dhd_linux_get_primary_netdev(dhd));
3656*4882a593Smuzhiyun #endif /* APF */
3657*4882a593Smuzhiyun 		}
3658*4882a593Smuzhiyun #endif /* PKT_FILTER_SUPPORT */
3659*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
3660*4882a593Smuzhiyun 	} else {
3661*4882a593Smuzhiyun 		/* only AP or GO role need to be handled here. */
3662*4882a593Smuzhiyun 		err = -EINVAL;
3663*4882a593Smuzhiyun 		goto fail;
3664*4882a593Smuzhiyun 	}
3665*4882a593Smuzhiyun 
3666*4882a593Smuzhiyun 	/* disable TDLS */
3667*4882a593Smuzhiyun #ifdef WLTDLS
3668*4882a593Smuzhiyun 	if (bssidx == 0) {
3669*4882a593Smuzhiyun 		/* Disable TDLS for primary Iface. For virtual interface,
3670*4882a593Smuzhiyun 		 * tdls disable will happen from interface create context
3671*4882a593Smuzhiyun 		 */
3672*4882a593Smuzhiyun 		wl_cfg80211_tdls_config(cfg, TDLS_STATE_AP_CREATE, false);
3673*4882a593Smuzhiyun 	}
3674*4882a593Smuzhiyun #endif /*  WLTDLS */
3675*4882a593Smuzhiyun 
3676*4882a593Smuzhiyun 	if (!check_dev_role_integrity(cfg, dev_role)) {
3677*4882a593Smuzhiyun 		err = -EINVAL;
3678*4882a593Smuzhiyun 		goto fail;
3679*4882a593Smuzhiyun 	}
3680*4882a593Smuzhiyun 
3681*4882a593Smuzhiyun /*
3682*4882a593Smuzhiyun  * TODO:
3683*4882a593Smuzhiyun  * Check whether 802.11ac-160MHz bandwidth channel setting has to use the
3684*4882a593Smuzhiyun  *      center frequencies present in 'preset_chandef' instead of using the
3685*4882a593Smuzhiyun  *      hardcoded values in 'wl_cfg80211_set_channel()'.
3686*4882a593Smuzhiyun  */
3687*4882a593Smuzhiyun #if ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 6, 0)) && !defined(WL_COMPAT_WIRELESS))
3688*4882a593Smuzhiyun #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 2) || defined(CFG80211_BKPORT_MLO)
3689*4882a593Smuzhiyun 	if (!dev->ieee80211_ptr->u.ap.preset_chandef.chan)
3690*4882a593Smuzhiyun #else
3691*4882a593Smuzhiyun 	if (!dev->ieee80211_ptr->preset_chandef.chan)
3692*4882a593Smuzhiyun #endif
3693*4882a593Smuzhiyun 	{
3694*4882a593Smuzhiyun 		WL_ERR(("chan is NULL\n"));
3695*4882a593Smuzhiyun 		err = -EINVAL;
3696*4882a593Smuzhiyun 		goto fail;
3697*4882a593Smuzhiyun 	}
3698*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_channel(wiphy, dev,
3699*4882a593Smuzhiyun #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 2) || defined(CFG80211_BKPORT_MLO)
3700*4882a593Smuzhiyun 			dev->ieee80211_ptr->u.ap.preset_chandef.chan,
3701*4882a593Smuzhiyun #else
3702*4882a593Smuzhiyun 			dev->ieee80211_ptr->preset_chandef.chan,
3703*4882a593Smuzhiyun #endif
3704*4882a593Smuzhiyun 			NL80211_CHAN_HT20) < 0)) {
3705*4882a593Smuzhiyun 		WL_ERR(("Set channel failed \n"));
3706*4882a593Smuzhiyun 		goto fail;
3707*4882a593Smuzhiyun 	}
3708*4882a593Smuzhiyun #endif /* ((LINUX_VERSION >= VERSION(3, 6, 0) && !WL_COMPAT_WIRELESS) */
3709*4882a593Smuzhiyun 
3710*4882a593Smuzhiyun 	if ((err = wl_cfg80211_bcn_set_params(info, dev,
3711*4882a593Smuzhiyun 		dev_role, bssidx)) < 0) {
3712*4882a593Smuzhiyun 		WL_ERR(("Beacon params set failed \n"));
3713*4882a593Smuzhiyun 		goto fail;
3714*4882a593Smuzhiyun 	}
3715*4882a593Smuzhiyun 
3716*4882a593Smuzhiyun 	/* Parse IEs */
3717*4882a593Smuzhiyun 	if ((err = wl_cfg80211_parse_ap_ies(dev, &info->beacon, &ies)) < 0) {
3718*4882a593Smuzhiyun 		WL_ERR(("Set IEs failed \n"));
3719*4882a593Smuzhiyun 		goto fail;
3720*4882a593Smuzhiyun 	}
3721*4882a593Smuzhiyun 
3722*4882a593Smuzhiyun 	if ((err = wl_cfg80211_bcn_validate_sec(dev, &ies,
3723*4882a593Smuzhiyun 		dev_role, bssidx, info->privacy)) < 0)
3724*4882a593Smuzhiyun 	{
3725*4882a593Smuzhiyun 		WL_ERR(("Beacon set security failed \n"));
3726*4882a593Smuzhiyun 		goto fail;
3727*4882a593Smuzhiyun 	}
3728*4882a593Smuzhiyun 
3729*4882a593Smuzhiyun 	if ((err = wl_cfg80211_bcn_bringup_ap(dev, &ies,
3730*4882a593Smuzhiyun 		dev_role, bssidx)) < 0) {
3731*4882a593Smuzhiyun 		WL_ERR(("Beacon bring up AP/GO failed \n"));
3732*4882a593Smuzhiyun 		goto fail;
3733*4882a593Smuzhiyun 	}
3734*4882a593Smuzhiyun 
3735*4882a593Smuzhiyun 	/* Set GC/STA SCB expiry timings. */
3736*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_scb_timings(cfg, dev))) {
3737*4882a593Smuzhiyun 		WL_ERR(("scb setting failed \n"));
3738*4882a593Smuzhiyun //		goto fail;
3739*4882a593Smuzhiyun 	}
3740*4882a593Smuzhiyun 
3741*4882a593Smuzhiyun 	wl_set_drv_status(cfg, CONNECTED, dev);
3742*4882a593Smuzhiyun 	WL_DBG(("** AP/GO Created **\n"));
3743*4882a593Smuzhiyun 
3744*4882a593Smuzhiyun #ifdef WL_CFG80211_ACL
3745*4882a593Smuzhiyun 	/* Enfoce Admission Control. */
3746*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_mac_acl(wiphy, dev, info->acl)) < 0) {
3747*4882a593Smuzhiyun 		WL_ERR(("Set ACL failed\n"));
3748*4882a593Smuzhiyun 	}
3749*4882a593Smuzhiyun #endif /* WL_CFG80211_ACL */
3750*4882a593Smuzhiyun 
3751*4882a593Smuzhiyun 	/* Set IEs to FW */
3752*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_ies(dev, &info->beacon, bssidx)) < 0)
3753*4882a593Smuzhiyun 		WL_ERR(("Set IEs failed \n"));
3754*4882a593Smuzhiyun 
3755*4882a593Smuzhiyun #ifdef WLDWDS
3756*4882a593Smuzhiyun 	if (dev->ieee80211_ptr->use_4addr) {
3757*4882a593Smuzhiyun 		if ((err = wl_cfg80211_set_mgmt_vndr_ies(cfg, ndev_to_cfgdev(dev), bssidx,
3758*4882a593Smuzhiyun 				VNDR_IE_ASSOCRSP_FLAG, (const u8 *)info->beacon.assocresp_ies,
3759*4882a593Smuzhiyun 				info->beacon.assocresp_ies_len)) < 0) {
3760*4882a593Smuzhiyun 			WL_ERR(("Set ASSOC RESP IE Failed\n"));
3761*4882a593Smuzhiyun 		}
3762*4882a593Smuzhiyun 	}
3763*4882a593Smuzhiyun #endif /* WLDWDS */
3764*4882a593Smuzhiyun 
3765*4882a593Smuzhiyun 	/* Enable Probe Req filter, WPS-AP certification 4.2.13 */
3766*4882a593Smuzhiyun 	if ((dev_role == NL80211_IFTYPE_AP) && (ies.wps_ie != NULL)) {
3767*4882a593Smuzhiyun 		bool pbc = 0;
3768*4882a593Smuzhiyun 		wl_validate_wps_ie((const char *) ies.wps_ie, ies.wps_ie_len, &pbc);
3769*4882a593Smuzhiyun 		if (pbc) {
3770*4882a593Smuzhiyun 			WL_DBG(("set WLC_E_PROBREQ_MSG\n"));
3771*4882a593Smuzhiyun 			wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, true);
3772*4882a593Smuzhiyun 		}
3773*4882a593Smuzhiyun 	}
3774*4882a593Smuzhiyun 
3775*4882a593Smuzhiyun 	/* Configure hidden SSID */
3776*4882a593Smuzhiyun 	if (info->hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE) {
3777*4882a593Smuzhiyun 		if ((err = wldev_iovar_setint(dev, "closednet", 1)) < 0)
3778*4882a593Smuzhiyun 			WL_ERR(("failed to set hidden : %d\n", err));
3779*4882a593Smuzhiyun 		WL_DBG(("hidden_ssid_enum_val: %d \n", info->hidden_ssid));
3780*4882a593Smuzhiyun 	}
3781*4882a593Smuzhiyun 
3782*4882a593Smuzhiyun #ifdef SUPPORT_AP_RADIO_PWRSAVE
3783*4882a593Smuzhiyun 	if (dev_role == NL80211_IFTYPE_AP) {
3784*4882a593Smuzhiyun 		if (!wl_set_ap_rps(dev, FALSE, dev->name)) {
3785*4882a593Smuzhiyun 			wl_cfg80211_init_ap_rps(cfg);
3786*4882a593Smuzhiyun 		} else {
3787*4882a593Smuzhiyun 			WL_ERR(("Set rpsnoa failed \n"));
3788*4882a593Smuzhiyun 		}
3789*4882a593Smuzhiyun 	}
3790*4882a593Smuzhiyun #endif /* SUPPORT_AP_RADIO_PWRSAVE */
3791*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
3792*4882a593Smuzhiyun 		wl_ext_in4way_sync(dev, 0, WL_EXT_STATUS_AP_ENABLING, NULL);
3793*4882a593Smuzhiyun #endif
3794*4882a593Smuzhiyun fail:
3795*4882a593Smuzhiyun 	if (err) {
3796*4882a593Smuzhiyun 		WL_ERR(("ADD/SET beacon failed\n"));
3797*4882a593Smuzhiyun 		wl_flush_fw_log_buffer(dev, FW_LOGSET_MASK_ALL);
3798*4882a593Smuzhiyun #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 2) || defined(CFG80211_BKPORT_MLO)
3799*4882a593Smuzhiyun 		wl_cfg80211_stop_ap(wiphy, dev, 0);
3800*4882a593Smuzhiyun #else
3801*4882a593Smuzhiyun 		wl_cfg80211_stop_ap(wiphy, dev);
3802*4882a593Smuzhiyun #endif
3803*4882a593Smuzhiyun 		if (dev_role == NL80211_IFTYPE_AP) {
3804*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
3805*4882a593Smuzhiyun 		if (!wl_ext_iapsta_iftype_enabled(dev, WL_IF_TYPE_AP)) {
3806*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
3807*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
3808*4882a593Smuzhiyun 			dhd->op_mode &= ~DHD_FLAG_HOSTAP_MODE;
3809*4882a593Smuzhiyun #ifdef PKT_FILTER_SUPPORT
3810*4882a593Smuzhiyun 			/* Enable packet filter */
3811*4882a593Smuzhiyun 			if (dhd->early_suspended) {
3812*4882a593Smuzhiyun 				WL_ERR(("Enable pkt_filter\n"));
3813*4882a593Smuzhiyun 				dhd_enable_packet_filter(1, dhd);
3814*4882a593Smuzhiyun #ifdef APF
3815*4882a593Smuzhiyun 				dhd_dev_apf_enable_filter(dhd_linux_get_primary_netdev(dhd));
3816*4882a593Smuzhiyun #endif /* APF */
3817*4882a593Smuzhiyun 			}
3818*4882a593Smuzhiyun #endif /* PKT_FILTER_SUPPORT */
3819*4882a593Smuzhiyun #ifdef DISABLE_WL_FRAMEBURST_SOFTAP
3820*4882a593Smuzhiyun 			wl_cfg80211_set_frameburst(cfg, TRUE);
3821*4882a593Smuzhiyun #endif /* DISABLE_WL_FRAMEBURST_SOFTAP */
3822*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
3823*4882a593Smuzhiyun 			wl_wlfc_enable(cfg, FALSE);
3824*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
3825*4882a593Smuzhiyun 		}
3826*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
3827*4882a593Smuzhiyun 		}
3828*4882a593Smuzhiyun #ifdef WLTDLS
3829*4882a593Smuzhiyun 		if (bssidx == 0) {
3830*4882a593Smuzhiyun 			/* Since AP creation failed, re-enable TDLS */
3831*4882a593Smuzhiyun 			wl_cfg80211_tdls_config(cfg, TDLS_STATE_AP_DELETE, false);
3832*4882a593Smuzhiyun 		}
3833*4882a593Smuzhiyun #endif /*  WLTDLS */
3834*4882a593Smuzhiyun 
3835*4882a593Smuzhiyun 	}
3836*4882a593Smuzhiyun 
3837*4882a593Smuzhiyun 	return err;
3838*4882a593Smuzhiyun }
3839*4882a593Smuzhiyun 
3840*4882a593Smuzhiyun s32
wl_cfg80211_stop_ap(struct wiphy * wiphy,struct net_device * dev,unsigned int link_id)3841*4882a593Smuzhiyun wl_cfg80211_stop_ap(
3842*4882a593Smuzhiyun 	struct wiphy *wiphy,
3843*4882a593Smuzhiyun 	struct net_device *dev
3844*4882a593Smuzhiyun #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 2) || defined(CFG80211_BKPORT_MLO)
3845*4882a593Smuzhiyun 	, unsigned int link_id
3846*4882a593Smuzhiyun #endif
3847*4882a593Smuzhiyun 	)
3848*4882a593Smuzhiyun {
3849*4882a593Smuzhiyun 	int err = 0;
3850*4882a593Smuzhiyun 	u32 dev_role = 0;
3851*4882a593Smuzhiyun 	int ap = 0;
3852*4882a593Smuzhiyun 	s32 bssidx = 0;
3853*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
3854*4882a593Smuzhiyun 	s32 is_rsdb_supported = BCME_ERROR;
3855*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
3856*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
3857*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
3858*4882a593Smuzhiyun 
3859*4882a593Smuzhiyun 	WL_DBG(("Enter \n"));
3860*4882a593Smuzhiyun 
3861*4882a593Smuzhiyun 	if (wl_cfg80211_get_bus_state(cfg)) {
3862*4882a593Smuzhiyun 		/* since bus is down, iovar will fail. recovery path will bringup the bus. */
3863*4882a593Smuzhiyun 		WL_ERR(("bus is not ready\n"));
3864*4882a593Smuzhiyun 		return BCME_OK;
3865*4882a593Smuzhiyun 	}
3866*4882a593Smuzhiyun #if defined (BCMDONGLEHOST)
3867*4882a593Smuzhiyun 	is_rsdb_supported = DHD_OPMODE_SUPPORTED(cfg->pub, DHD_FLAG_RSDB_MODE);
3868*4882a593Smuzhiyun 	if (is_rsdb_supported < 0)
3869*4882a593Smuzhiyun 		return (-ENODEV);
3870*4882a593Smuzhiyun #endif
3871*4882a593Smuzhiyun 
3872*4882a593Smuzhiyun 	wl_clr_drv_status(cfg, AP_CREATING, dev);
3873*4882a593Smuzhiyun 	wl_clr_drv_status(cfg, AP_CREATED, dev);
3874*4882a593Smuzhiyun 	cfg->ap_oper_channel = INVCHANSPEC;
3875*4882a593Smuzhiyun 
3876*4882a593Smuzhiyun 	if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) {
3877*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_AP;
3878*4882a593Smuzhiyun 		WL_MSG(dev->name, "stopping AP operation\n");
3879*4882a593Smuzhiyun 	} else if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
3880*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_P2P_GO;
3881*4882a593Smuzhiyun 		WL_MSG(dev->name, "stopping P2P GO operation\n");
3882*4882a593Smuzhiyun 	} else {
3883*4882a593Smuzhiyun 		WL_ERR(("no AP/P2P GO interface is operational.\n"));
3884*4882a593Smuzhiyun 		return -EINVAL;
3885*4882a593Smuzhiyun 	}
3886*4882a593Smuzhiyun 
3887*4882a593Smuzhiyun 	if ((bssidx = wl_get_bssidx_by_wdev(cfg, dev->ieee80211_ptr)) < 0) {
3888*4882a593Smuzhiyun 		WL_ERR(("find p2p index from wdev(%p) failed\n", dev->ieee80211_ptr));
3889*4882a593Smuzhiyun 		return BCME_ERROR;
3890*4882a593Smuzhiyun 	}
3891*4882a593Smuzhiyun 
3892*4882a593Smuzhiyun 	if (!check_dev_role_integrity(cfg, dev_role)) {
3893*4882a593Smuzhiyun 		WL_ERR(("role integrity check failed \n"));
3894*4882a593Smuzhiyun 		err = -EINVAL;
3895*4882a593Smuzhiyun 		goto exit;
3896*4882a593Smuzhiyun 	}
3897*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
3898*4882a593Smuzhiyun 	wl_ext_in4way_sync(dev, 0, WL_EXT_STATUS_AP_DISABLING, NULL);
3899*4882a593Smuzhiyun #endif
3900*4882a593Smuzhiyun 
3901*4882a593Smuzhiyun 	/* Free up resources */
3902*4882a593Smuzhiyun 	wl_cfg80211_cleanup_if(dev);
3903*4882a593Smuzhiyun 
3904*4882a593Smuzhiyun 	/* Clear AP/GO connected status */
3905*4882a593Smuzhiyun 	wl_clr_drv_status(cfg, CONNECTED, dev);
3906*4882a593Smuzhiyun 	if ((err = wl_cfg80211_bss_up(cfg, dev, bssidx, 0)) < 0) {
3907*4882a593Smuzhiyun 		WL_ERR(("bss down error %d\n", err));
3908*4882a593Smuzhiyun 	}
3909*4882a593Smuzhiyun 
3910*4882a593Smuzhiyun 	if (dev_role == NL80211_IFTYPE_AP) {
3911*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
3912*4882a593Smuzhiyun #ifdef DISABLE_WL_FRAMEBURST_SOFTAP
3913*4882a593Smuzhiyun 		wl_cfg80211_set_frameburst(cfg, TRUE);
3914*4882a593Smuzhiyun #endif /* DISABLE_WL_FRAMEBURST_SOFTAP */
3915*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
3916*4882a593Smuzhiyun #ifdef PKT_FILTER_SUPPORT
3917*4882a593Smuzhiyun 		/* Enable packet filter */
3918*4882a593Smuzhiyun 		if (dhd->early_suspended) {
3919*4882a593Smuzhiyun 			WL_ERR(("Enable pkt_filter\n"));
3920*4882a593Smuzhiyun 			dhd_enable_packet_filter(1, dhd);
3921*4882a593Smuzhiyun #ifdef APF
3922*4882a593Smuzhiyun 			dhd_dev_apf_enable_filter(dhd_linux_get_primary_netdev(dhd));
3923*4882a593Smuzhiyun #endif /* APF */
3924*4882a593Smuzhiyun 		}
3925*4882a593Smuzhiyun #endif /* PKT_FILTER_SUPPORT */
3926*4882a593Smuzhiyun 
3927*4882a593Smuzhiyun 		if (is_rsdb_supported == 0) {
3928*4882a593Smuzhiyun 			/* For non-rsdb chips, we use stand alone AP. Do wl down on stop AP */
3929*4882a593Smuzhiyun 			err = wldev_ioctl_set(dev, WLC_UP, &ap, sizeof(s32));
3930*4882a593Smuzhiyun 			if (unlikely(err)) {
3931*4882a593Smuzhiyun 				WL_ERR(("WLC_UP error (%d)\n", err));
3932*4882a593Smuzhiyun 				err = -EINVAL;
3933*4882a593Smuzhiyun 				goto exit;
3934*4882a593Smuzhiyun 			}
3935*4882a593Smuzhiyun 		}
3936*4882a593Smuzhiyun 
3937*4882a593Smuzhiyun #ifdef WL_DISABLE_HE_SOFTAP
3938*4882a593Smuzhiyun 		if (wl_cfg80211_set_he_mode(dev, cfg, bssidx, WL_HE_FEATURES_HE_AP,
3939*4882a593Smuzhiyun 			TRUE) != BCME_OK) {
3940*4882a593Smuzhiyun 			WL_ERR(("failed to set he features\n"));
3941*4882a593Smuzhiyun 		}
3942*4882a593Smuzhiyun #endif /* WL_DISABLE_HE_SOFTAP */
3943*4882a593Smuzhiyun 
3944*4882a593Smuzhiyun 		wl_cfg80211_clear_per_bss_ies(cfg, dev->ieee80211_ptr);
3945*4882a593Smuzhiyun #ifdef SUPPORT_AP_RADIO_PWRSAVE
3946*4882a593Smuzhiyun 		if (!wl_set_ap_rps(dev, FALSE, dev->name)) {
3947*4882a593Smuzhiyun 			wl_cfg80211_init_ap_rps(cfg);
3948*4882a593Smuzhiyun 		} else {
3949*4882a593Smuzhiyun 			WL_ERR(("Set rpsnoa failed \n"));
3950*4882a593Smuzhiyun 		}
3951*4882a593Smuzhiyun #endif /* SUPPORT_AP_RADIO_PWRSAVE */
3952*4882a593Smuzhiyun 	} else {
3953*4882a593Smuzhiyun 		/* Do we need to do something here */
3954*4882a593Smuzhiyun 		WL_DBG(("Stopping P2P GO \n"));
3955*4882a593Smuzhiyun 
3956*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID)
3957*4882a593Smuzhiyun 		DHD_OS_WAKE_LOCK_CTRL_TIMEOUT_ENABLE((dhd_pub_t *)(cfg->pub),
3958*4882a593Smuzhiyun 			DHD_EVENT_TIMEOUT_MS*3);
3959*4882a593Smuzhiyun 		DHD_OS_WAKE_LOCK_TIMEOUT((dhd_pub_t *)(cfg->pub));
3960*4882a593Smuzhiyun #endif
3961*4882a593Smuzhiyun 
3962*4882a593Smuzhiyun 	}
3963*4882a593Smuzhiyun 
3964*4882a593Smuzhiyun 	SUPP_LOG(("AP/GO Link down\n"));
3965*4882a593Smuzhiyun exit:
3966*4882a593Smuzhiyun 	if (err) {
3967*4882a593Smuzhiyun 		/* In case of failure, flush fw logs */
3968*4882a593Smuzhiyun 		wl_flush_fw_log_buffer(dev, FW_LOGSET_MASK_ALL);
3969*4882a593Smuzhiyun 		SUPP_LOG(("AP/GO Link down fail. err:%d\n", err));
3970*4882a593Smuzhiyun 	}
3971*4882a593Smuzhiyun #ifdef WLTDLS
3972*4882a593Smuzhiyun 	if (bssidx == 0) {
3973*4882a593Smuzhiyun 		/* re-enable TDLS if the number of connected interfaces is less than 2 */
3974*4882a593Smuzhiyun 		wl_cfg80211_tdls_config(cfg, TDLS_STATE_AP_DELETE, false);
3975*4882a593Smuzhiyun 	}
3976*4882a593Smuzhiyun #endif /* WLTDLS */
3977*4882a593Smuzhiyun 
3978*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
3979*4882a593Smuzhiyun 	if (dev_role == NL80211_IFTYPE_AP) {
3980*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
3981*4882a593Smuzhiyun 		if (!wl_ext_iapsta_iftype_enabled(dev, WL_IF_TYPE_AP)) {
3982*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
3983*4882a593Smuzhiyun 		/* clear the AP mode */
3984*4882a593Smuzhiyun 		dhd->op_mode &= ~DHD_FLAG_HOSTAP_MODE;
3985*4882a593Smuzhiyun 		wl_wlfc_enable(cfg, FALSE);
3986*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
3987*4882a593Smuzhiyun 		}
3988*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
3989*4882a593Smuzhiyun 	}
3990*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
3991*4882a593Smuzhiyun 	return err;
3992*4882a593Smuzhiyun }
3993*4882a593Smuzhiyun 
3994*4882a593Smuzhiyun s32
wl_cfg80211_change_beacon(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_beacon_data * info)3995*4882a593Smuzhiyun wl_cfg80211_change_beacon(
3996*4882a593Smuzhiyun 	struct wiphy *wiphy,
3997*4882a593Smuzhiyun 	struct net_device *dev,
3998*4882a593Smuzhiyun 	struct cfg80211_beacon_data *info)
3999*4882a593Smuzhiyun {
4000*4882a593Smuzhiyun 	s32 err = BCME_OK;
4001*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
4002*4882a593Smuzhiyun 	struct parsed_ies ies;
4003*4882a593Smuzhiyun 	u32 dev_role = 0;
4004*4882a593Smuzhiyun 	s32 bssidx = 0;
4005*4882a593Smuzhiyun 	bool pbc = 0;
4006*4882a593Smuzhiyun 
4007*4882a593Smuzhiyun 	WL_DBG(("Enter \n"));
4008*4882a593Smuzhiyun 
4009*4882a593Smuzhiyun 	if ((bssidx = wl_get_bssidx_by_wdev(cfg, dev->ieee80211_ptr)) < 0) {
4010*4882a593Smuzhiyun 		WL_ERR(("Find p2p index from wdev(%p) failed\n", dev->ieee80211_ptr));
4011*4882a593Smuzhiyun 		return BCME_ERROR;
4012*4882a593Smuzhiyun 	}
4013*4882a593Smuzhiyun 
4014*4882a593Smuzhiyun 	if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
4015*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_P2P_GO;
4016*4882a593Smuzhiyun 	} else if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) {
4017*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_AP;
4018*4882a593Smuzhiyun 	} else {
4019*4882a593Smuzhiyun 		err = -EINVAL;
4020*4882a593Smuzhiyun 		goto fail;
4021*4882a593Smuzhiyun 	}
4022*4882a593Smuzhiyun 
4023*4882a593Smuzhiyun 	if (!check_dev_role_integrity(cfg, dev_role)) {
4024*4882a593Smuzhiyun 		err = -EINVAL;
4025*4882a593Smuzhiyun 		goto fail;
4026*4882a593Smuzhiyun 	}
4027*4882a593Smuzhiyun 
4028*4882a593Smuzhiyun 	if ((dev_role == NL80211_IFTYPE_P2P_GO) && (cfg->p2p_wdev == NULL)) {
4029*4882a593Smuzhiyun 		WL_ERR(("P2P already down status!\n"));
4030*4882a593Smuzhiyun 		err = BCME_ERROR;
4031*4882a593Smuzhiyun 		goto fail;
4032*4882a593Smuzhiyun 	}
4033*4882a593Smuzhiyun 
4034*4882a593Smuzhiyun 	/* Parse IEs */
4035*4882a593Smuzhiyun 	if ((err = wl_cfg80211_parse_ap_ies(dev, info, &ies)) < 0) {
4036*4882a593Smuzhiyun 		WL_ERR(("Parse IEs failed \n"));
4037*4882a593Smuzhiyun 		goto fail;
4038*4882a593Smuzhiyun 	}
4039*4882a593Smuzhiyun 
4040*4882a593Smuzhiyun 	/* Set IEs to FW */
4041*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_ies(dev, info, bssidx)) < 0) {
4042*4882a593Smuzhiyun 		WL_ERR(("Set IEs failed \n"));
4043*4882a593Smuzhiyun 		goto fail;
4044*4882a593Smuzhiyun 	}
4045*4882a593Smuzhiyun 
4046*4882a593Smuzhiyun 	if (dev_role == NL80211_IFTYPE_AP) {
4047*4882a593Smuzhiyun 		if (wl_cfg80211_hostapd_sec(dev, &ies, bssidx) < 0) {
4048*4882a593Smuzhiyun 			WL_ERR(("Hostapd update sec failed \n"));
4049*4882a593Smuzhiyun 			err = -EINVAL;
4050*4882a593Smuzhiyun 			goto fail;
4051*4882a593Smuzhiyun 		}
4052*4882a593Smuzhiyun 		/* Enable Probe Req filter, WPS-AP certification 4.2.13 */
4053*4882a593Smuzhiyun 		if ((dev_role == NL80211_IFTYPE_AP) && (ies.wps_ie != NULL)) {
4054*4882a593Smuzhiyun 			wl_validate_wps_ie((const char *) ies.wps_ie, ies.wps_ie_len, &pbc);
4055*4882a593Smuzhiyun 			WL_DBG((" WPS AP, wps_ie is exists pbc=%d\n", pbc));
4056*4882a593Smuzhiyun 			if (pbc)
4057*4882a593Smuzhiyun 				wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, true);
4058*4882a593Smuzhiyun 			else
4059*4882a593Smuzhiyun 				wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, false);
4060*4882a593Smuzhiyun 		}
4061*4882a593Smuzhiyun 	}
4062*4882a593Smuzhiyun 
4063*4882a593Smuzhiyun fail:
4064*4882a593Smuzhiyun 	if (err) {
4065*4882a593Smuzhiyun 		wl_flush_fw_log_buffer(dev, FW_LOGSET_MASK_ALL);
4066*4882a593Smuzhiyun 	}
4067*4882a593Smuzhiyun 	return err;
4068*4882a593Smuzhiyun }
4069*4882a593Smuzhiyun #else
4070*4882a593Smuzhiyun s32
wl_cfg80211_add_set_beacon(struct wiphy * wiphy,struct net_device * dev,struct beacon_parameters * info)4071*4882a593Smuzhiyun wl_cfg80211_add_set_beacon(struct wiphy *wiphy, struct net_device *dev,
4072*4882a593Smuzhiyun 	struct beacon_parameters *info)
4073*4882a593Smuzhiyun {
4074*4882a593Smuzhiyun 	s32 err = BCME_OK;
4075*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
4076*4882a593Smuzhiyun 	s32 ie_offset = 0;
4077*4882a593Smuzhiyun 	s32 bssidx = 0;
4078*4882a593Smuzhiyun 	u32 dev_role = NL80211_IFTYPE_AP;
4079*4882a593Smuzhiyun 	struct parsed_ies ies;
4080*4882a593Smuzhiyun 	bcm_tlv_t *ssid_ie;
4081*4882a593Smuzhiyun 	bool pbc = 0;
4082*4882a593Smuzhiyun 	bool privacy;
4083*4882a593Smuzhiyun 	bool is_bss_up = 0;
4084*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
4085*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
4086*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
4087*4882a593Smuzhiyun 
4088*4882a593Smuzhiyun 	WL_DBG(("interval (%d) dtim_period (%d) head_len (%d) tail_len (%d)\n",
4089*4882a593Smuzhiyun 		info->interval, info->dtim_period, info->head_len, info->tail_len));
4090*4882a593Smuzhiyun 
4091*4882a593Smuzhiyun 	if (dev == bcmcfg_to_prmry_ndev(cfg)) {
4092*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_AP;
4093*4882a593Smuzhiyun 	}
4094*4882a593Smuzhiyun #if defined(WL_ENABLE_P2P_IF)
4095*4882a593Smuzhiyun 	else if (dev == cfg->p2p_net) {
4096*4882a593Smuzhiyun 		/* Group Add request on p2p0 */
4097*4882a593Smuzhiyun 		dev = bcmcfg_to_prmry_ndev(cfg);
4098*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_P2P_GO;
4099*4882a593Smuzhiyun 	}
4100*4882a593Smuzhiyun #endif /* WL_ENABLE_P2P_IF */
4101*4882a593Smuzhiyun 
4102*4882a593Smuzhiyun 	if ((bssidx = wl_get_bssidx_by_wdev(cfg, dev->ieee80211_ptr)) < 0) {
4103*4882a593Smuzhiyun 		WL_ERR(("Find p2p index from wdev(%p) failed\n", dev->ieee80211_ptr));
4104*4882a593Smuzhiyun 		return BCME_ERROR;
4105*4882a593Smuzhiyun 	}
4106*4882a593Smuzhiyun 
4107*4882a593Smuzhiyun 	if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_GO) {
4108*4882a593Smuzhiyun 		dev_role = NL80211_IFTYPE_P2P_GO;
4109*4882a593Smuzhiyun 	} else if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) {
4110*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
4111*4882a593Smuzhiyun 		dhd->op_mode |= DHD_FLAG_HOSTAP_MODE;
4112*4882a593Smuzhiyun #endif
4113*4882a593Smuzhiyun 	}
4114*4882a593Smuzhiyun 
4115*4882a593Smuzhiyun 	if (!check_dev_role_integrity(cfg, dev_role)) {
4116*4882a593Smuzhiyun 		err = -ENODEV;
4117*4882a593Smuzhiyun 		goto fail;
4118*4882a593Smuzhiyun 	}
4119*4882a593Smuzhiyun 
4120*4882a593Smuzhiyun 	if ((dev_role == NL80211_IFTYPE_P2P_GO) && (cfg->p2p_wdev == NULL)) {
4121*4882a593Smuzhiyun 		WL_ERR(("P2P already down status!\n"));
4122*4882a593Smuzhiyun 		err = BCME_ERROR;
4123*4882a593Smuzhiyun 		goto fail;
4124*4882a593Smuzhiyun 	}
4125*4882a593Smuzhiyun 
4126*4882a593Smuzhiyun 	ie_offset = DOT11_MGMT_HDR_LEN + DOT11_BCN_PRB_FIXED_LEN;
4127*4882a593Smuzhiyun 	/* find the SSID */
4128*4882a593Smuzhiyun 	if ((ssid_ie = bcm_parse_tlvs((u8 *)&info->head[ie_offset],
4129*4882a593Smuzhiyun 		info->head_len - ie_offset,
4130*4882a593Smuzhiyun 		DOT11_MNG_SSID_ID)) != NULL) {
4131*4882a593Smuzhiyun 		if (dev_role == NL80211_IFTYPE_AP) {
4132*4882a593Smuzhiyun 			/* Store the hostapd SSID */
4133*4882a593Smuzhiyun 			bzero(&cfg->hostapd_ssid.SSID[0], DOT11_MAX_SSID_LEN);
4134*4882a593Smuzhiyun 			cfg->hostapd_ssid.SSID_len = MIN(ssid_ie->len, DOT11_MAX_SSID_LEN);
4135*4882a593Smuzhiyun 			memcpy(&cfg->hostapd_ssid.SSID[0], ssid_ie->data,
4136*4882a593Smuzhiyun 				cfg->hostapd_ssid.SSID_len);
4137*4882a593Smuzhiyun 		} else {
4138*4882a593Smuzhiyun 			/* P2P GO */
4139*4882a593Smuzhiyun 			bzero(&cfg->p2p->ssid.SSID[0], DOT11_MAX_SSID_LEN);
4140*4882a593Smuzhiyun 			cfg->p2p->ssid.SSID_len = MIN(ssid_ie->len, DOT11_MAX_SSID_LEN);
4141*4882a593Smuzhiyun 			memcpy(cfg->p2p->ssid.SSID, ssid_ie->data,
4142*4882a593Smuzhiyun 				cfg->p2p->ssid.SSID_len);
4143*4882a593Smuzhiyun 		}
4144*4882a593Smuzhiyun 	}
4145*4882a593Smuzhiyun 
4146*4882a593Smuzhiyun 	if (wl_cfg80211_parse_ies((u8 *)info->tail,
4147*4882a593Smuzhiyun 		info->tail_len, &ies) < 0) {
4148*4882a593Smuzhiyun 		WL_ERR(("Beacon get IEs failed \n"));
4149*4882a593Smuzhiyun 		err = -EINVAL;
4150*4882a593Smuzhiyun 		goto fail;
4151*4882a593Smuzhiyun 	}
4152*4882a593Smuzhiyun 
4153*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_mgmt_vndr_ies(cfg, ndev_to_cfgdev(dev), bssidx,
4154*4882a593Smuzhiyun 		VNDR_IE_BEACON_FLAG, (u8 *)info->tail,
4155*4882a593Smuzhiyun 		info->tail_len)) < 0) {
4156*4882a593Smuzhiyun 		WL_ERR(("Beacon set IEs failed \n"));
4157*4882a593Smuzhiyun 		goto fail;
4158*4882a593Smuzhiyun 	} else {
4159*4882a593Smuzhiyun 		WL_DBG(("Applied Vndr IEs for Beacon \n"));
4160*4882a593Smuzhiyun 	}
4161*4882a593Smuzhiyun 
4162*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
4163*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_mgmt_vndr_ies(cfg, ndev_to_cfgdev(dev), bssidx,
4164*4882a593Smuzhiyun 		VNDR_IE_PRBRSP_FLAG, (u8 *)info->proberesp_ies,
4165*4882a593Smuzhiyun 		info->proberesp_ies_len)) < 0) {
4166*4882a593Smuzhiyun 		WL_ERR(("ProbeRsp set IEs failed \n"));
4167*4882a593Smuzhiyun 		goto fail;
4168*4882a593Smuzhiyun 	} else {
4169*4882a593Smuzhiyun 		WL_DBG(("Applied Vndr IEs for ProbeRsp \n"));
4170*4882a593Smuzhiyun 	}
4171*4882a593Smuzhiyun #endif
4172*4882a593Smuzhiyun 
4173*4882a593Smuzhiyun 	is_bss_up = wl_cfg80211_bss_isup(dev, bssidx);
4174*4882a593Smuzhiyun 
4175*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
4176*4882a593Smuzhiyun 	privacy = info->privacy;
4177*4882a593Smuzhiyun #else
4178*4882a593Smuzhiyun 	privacy = 0;
4179*4882a593Smuzhiyun #endif
4180*4882a593Smuzhiyun 	if (!is_bss_up &&
4181*4882a593Smuzhiyun 		(wl_cfg80211_bcn_validate_sec(dev, &ies, dev_role, bssidx, privacy) < 0))
4182*4882a593Smuzhiyun 	{
4183*4882a593Smuzhiyun 		WL_ERR(("Beacon set security failed \n"));
4184*4882a593Smuzhiyun 		err = -EINVAL;
4185*4882a593Smuzhiyun 		goto fail;
4186*4882a593Smuzhiyun 	}
4187*4882a593Smuzhiyun 
4188*4882a593Smuzhiyun 	/* Set BI and DTIM period */
4189*4882a593Smuzhiyun 	if (info->interval) {
4190*4882a593Smuzhiyun 		if ((err = wldev_ioctl_set(dev, WLC_SET_BCNPRD,
4191*4882a593Smuzhiyun 			&info->interval, sizeof(s32))) < 0) {
4192*4882a593Smuzhiyun 			WL_ERR(("Beacon Interval Set Error, %d\n", err));
4193*4882a593Smuzhiyun 			return err;
4194*4882a593Smuzhiyun 		}
4195*4882a593Smuzhiyun 	}
4196*4882a593Smuzhiyun 	if (info->dtim_period) {
4197*4882a593Smuzhiyun 		if ((err = wldev_ioctl_set(dev, WLC_SET_DTIMPRD,
4198*4882a593Smuzhiyun 			&info->dtim_period, sizeof(s32))) < 0) {
4199*4882a593Smuzhiyun 			WL_ERR(("DTIM Interval Set Error, %d\n", err));
4200*4882a593Smuzhiyun 			return err;
4201*4882a593Smuzhiyun 		}
4202*4882a593Smuzhiyun 	}
4203*4882a593Smuzhiyun 
4204*4882a593Smuzhiyun 	/* If bss is already up, skip bring up */
4205*4882a593Smuzhiyun 	if (!is_bss_up &&
4206*4882a593Smuzhiyun 		(err = wl_cfg80211_bcn_bringup_ap(dev, &ies, dev_role, bssidx)) < 0)
4207*4882a593Smuzhiyun 	{
4208*4882a593Smuzhiyun 		WL_ERR(("Beacon bring up AP/GO failed \n"));
4209*4882a593Smuzhiyun 		goto fail;
4210*4882a593Smuzhiyun 	}
4211*4882a593Smuzhiyun 
4212*4882a593Smuzhiyun 	/* Set GC/STA SCB expiry timings. */
4213*4882a593Smuzhiyun 	if ((err = wl_cfg80211_set_scb_timings(cfg, dev))) {
4214*4882a593Smuzhiyun 		WL_ERR(("scb setting failed \n"));
4215*4882a593Smuzhiyun 		if (err == BCME_UNSUPPORTED)
4216*4882a593Smuzhiyun 			err = 0;
4217*4882a593Smuzhiyun //		goto fail;
4218*4882a593Smuzhiyun 	}
4219*4882a593Smuzhiyun 
4220*4882a593Smuzhiyun 	if (wl_get_drv_status(cfg, AP_CREATED, dev)) {
4221*4882a593Smuzhiyun 		/* Soft AP already running. Update changed params */
4222*4882a593Smuzhiyun 		if (wl_cfg80211_hostapd_sec(dev, &ies, bssidx) < 0) {
4223*4882a593Smuzhiyun 			WL_ERR(("Hostapd update sec failed \n"));
4224*4882a593Smuzhiyun 			err = -EINVAL;
4225*4882a593Smuzhiyun 			goto fail;
4226*4882a593Smuzhiyun 		}
4227*4882a593Smuzhiyun 	}
4228*4882a593Smuzhiyun 
4229*4882a593Smuzhiyun 	/* Enable Probe Req filter */
4230*4882a593Smuzhiyun 	if (((dev_role == NL80211_IFTYPE_P2P_GO) ||
4231*4882a593Smuzhiyun 		(dev_role == NL80211_IFTYPE_AP)) && (ies.wps_ie != NULL)) {
4232*4882a593Smuzhiyun 		wl_validate_wps_ie((char *) ies.wps_ie, ies.wps_ie_len, &pbc);
4233*4882a593Smuzhiyun 		if (pbc)
4234*4882a593Smuzhiyun 			wl_add_remove_eventmsg(dev, WLC_E_PROBREQ_MSG, true);
4235*4882a593Smuzhiyun 	}
4236*4882a593Smuzhiyun 
4237*4882a593Smuzhiyun 	WL_DBG(("** ADD/SET beacon done **\n"));
4238*4882a593Smuzhiyun 	wl_set_drv_status(cfg, CONNECTED, dev);
4239*4882a593Smuzhiyun 
4240*4882a593Smuzhiyun fail:
4241*4882a593Smuzhiyun 	if (err) {
4242*4882a593Smuzhiyun 		WL_ERR(("ADD/SET beacon failed\n"));
4243*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
4244*4882a593Smuzhiyun 		if (dev_role == NL80211_IFTYPE_AP) {
4245*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
4246*4882a593Smuzhiyun 		if (!wl_ext_iapsta_iftype_enabled(dev, WL_IF_TYPE_AP)) {
4247*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
4248*4882a593Smuzhiyun 			/* clear the AP mode */
4249*4882a593Smuzhiyun 			dhd->op_mode &= ~DHD_FLAG_HOSTAP_MODE;
4250*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
4251*4882a593Smuzhiyun 		}
4252*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
4253*4882a593Smuzhiyun 		}
4254*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
4255*4882a593Smuzhiyun 	}
4256*4882a593Smuzhiyun 	return err;
4257*4882a593Smuzhiyun 
4258*4882a593Smuzhiyun }
4259*4882a593Smuzhiyun 
4260*4882a593Smuzhiyun s32
wl_cfg80211_del_beacon(struct wiphy * wiphy,struct net_device * dev)4261*4882a593Smuzhiyun wl_cfg80211_del_beacon(struct wiphy *wiphy, struct net_device *dev)
4262*4882a593Smuzhiyun {
4263*4882a593Smuzhiyun 	int err = 0;
4264*4882a593Smuzhiyun 	s32 bssidx = 0;
4265*4882a593Smuzhiyun 	int infra = 0;
4266*4882a593Smuzhiyun 	struct wireless_dev *wdev = dev->ieee80211_ptr;
4267*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
4268*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
4269*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
4270*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
4271*4882a593Smuzhiyun 
4272*4882a593Smuzhiyun 	WL_DBG(("Enter. \n"));
4273*4882a593Smuzhiyun 
4274*4882a593Smuzhiyun 	if (!wdev) {
4275*4882a593Smuzhiyun 		WL_ERR(("wdev null \n"));
4276*4882a593Smuzhiyun 		return -EINVAL;
4277*4882a593Smuzhiyun 	}
4278*4882a593Smuzhiyun 
4279*4882a593Smuzhiyun 	if ((wdev->iftype != NL80211_IFTYPE_P2P_GO) && (wdev->iftype != NL80211_IFTYPE_AP)) {
4280*4882a593Smuzhiyun 		WL_ERR(("Unspported iface type iftype:%d \n", wdev->iftype));
4281*4882a593Smuzhiyun 	}
4282*4882a593Smuzhiyun 
4283*4882a593Smuzhiyun 	wl_clr_drv_status(cfg, AP_CREATING, dev);
4284*4882a593Smuzhiyun 	wl_clr_drv_status(cfg, AP_CREATED, dev);
4285*4882a593Smuzhiyun 
4286*4882a593Smuzhiyun 	/* Clear AP/GO connected status */
4287*4882a593Smuzhiyun 	wl_clr_drv_status(cfg, CONNECTED, dev);
4288*4882a593Smuzhiyun 
4289*4882a593Smuzhiyun 	cfg->ap_oper_channel = INVCHANSPEC;
4290*4882a593Smuzhiyun 
4291*4882a593Smuzhiyun 	if ((bssidx = wl_get_bssidx_by_wdev(cfg, dev->ieee80211_ptr)) < 0) {
4292*4882a593Smuzhiyun 		WL_ERR(("find p2p index from wdev(%p) failed\n", dev->ieee80211_ptr));
4293*4882a593Smuzhiyun 		return BCME_ERROR;
4294*4882a593Smuzhiyun 	}
4295*4882a593Smuzhiyun 
4296*4882a593Smuzhiyun 	/* Do bss down */
4297*4882a593Smuzhiyun 	if ((err = wl_cfg80211_bss_up(cfg, dev, bssidx, 0)) < 0) {
4298*4882a593Smuzhiyun 		WL_ERR(("bss down error %d\n", err));
4299*4882a593Smuzhiyun 	}
4300*4882a593Smuzhiyun 
4301*4882a593Smuzhiyun 	/* fall through is intentional */
4302*4882a593Smuzhiyun 	err = wldev_ioctl_set(dev, WLC_SET_INFRA, &infra, sizeof(s32));
4303*4882a593Smuzhiyun 	if (err < 0) {
4304*4882a593Smuzhiyun 		WL_ERR(("SET INFRA error %d\n", err));
4305*4882a593Smuzhiyun 	}
4306*4882a593Smuzhiyun 	 wl_cfg80211_clear_per_bss_ies(cfg, dev->ieee80211_ptr);
4307*4882a593Smuzhiyun 
4308*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
4309*4882a593Smuzhiyun 	if (wdev->iftype == NL80211_IFTYPE_AP) {
4310*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
4311*4882a593Smuzhiyun 		if (!wl_ext_iapsta_iftype_enabled(dev, WL_IF_TYPE_AP)) {
4312*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
4313*4882a593Smuzhiyun 		/* clear the AP mode */
4314*4882a593Smuzhiyun 		dhd->op_mode &= ~DHD_FLAG_HOSTAP_MODE;
4315*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
4316*4882a593Smuzhiyun 		}
4317*4882a593Smuzhiyun #endif /* WL_EXT_IAPSTA */
4318*4882a593Smuzhiyun 	}
4319*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
4320*4882a593Smuzhiyun 
4321*4882a593Smuzhiyun 	return 0;
4322*4882a593Smuzhiyun }
4323*4882a593Smuzhiyun #endif /* LINUX_VERSION < VERSION(3,4,0) || WL_COMPAT_WIRELESS */
4324*4882a593Smuzhiyun 
4325*4882a593Smuzhiyun s32
wl_get_auth_assoc_status(struct bcm_cfg80211 * cfg,struct net_device * ndev,const wl_event_msg_t * e,void * data)4326*4882a593Smuzhiyun wl_get_auth_assoc_status(struct bcm_cfg80211 *cfg, struct net_device *ndev,
4327*4882a593Smuzhiyun 	const wl_event_msg_t *e, void *data)
4328*4882a593Smuzhiyun {
4329*4882a593Smuzhiyun 	u32 reason = ntoh32(e->reason);
4330*4882a593Smuzhiyun 	u32 event = ntoh32(e->event_type);
4331*4882a593Smuzhiyun 	struct wl_security *sec = wl_read_prof(cfg, ndev, WL_PROF_SEC);
4332*4882a593Smuzhiyun 
4333*4882a593Smuzhiyun #if defined(DHD_ENABLE_BIGDATA_LOGGING)
4334*4882a593Smuzhiyun 	(void)memcpy_s(&cfg->event_auth_assoc, sizeof(wl_event_msg_t),
4335*4882a593Smuzhiyun 		e, sizeof(wl_event_msg_t));
4336*4882a593Smuzhiyun 	WL_DBG(("event=%d status %d reason %d \n",
4337*4882a593Smuzhiyun 		ntoh32(cfg->event_auth_assoc.event_type),
4338*4882a593Smuzhiyun 		ntoh32(cfg->event_auth_assoc.status),
4339*4882a593Smuzhiyun 		ntoh32(cfg->event_auth_assoc.reason)));
4340*4882a593Smuzhiyun #endif /* DHD_ENABLE_BIGDATA_LOGGING */
4341*4882a593Smuzhiyun 	if (sec) {
4342*4882a593Smuzhiyun 		switch (event) {
4343*4882a593Smuzhiyun 		case WLC_E_ASSOC:
4344*4882a593Smuzhiyun 		case WLC_E_AUTH:
4345*4882a593Smuzhiyun 		case WLC_E_AUTH_IND:
4346*4882a593Smuzhiyun 			sec->auth_assoc_res_status = reason;
4347*4882a593Smuzhiyun 			break;
4348*4882a593Smuzhiyun 		default:
4349*4882a593Smuzhiyun 			break;
4350*4882a593Smuzhiyun 		}
4351*4882a593Smuzhiyun 	} else {
4352*4882a593Smuzhiyun 		WL_ERR(("sec is NULL\n"));
4353*4882a593Smuzhiyun 	}
4354*4882a593Smuzhiyun 	return 0;
4355*4882a593Smuzhiyun }
4356*4882a593Smuzhiyun 
4357*4882a593Smuzhiyun /* The mainline kernel >= 3.2.0 has support for indicating new/del station
4358*4882a593Smuzhiyun  * to AP/P2P GO via events. If this change is backported to kernel for which
4359*4882a593Smuzhiyun  * this driver is being built, then define WL_CFG80211_STA_EVENT. You
4360*4882a593Smuzhiyun  * should use this new/del sta event mechanism for BRCM supplicant >= 22.
4361*4882a593Smuzhiyun  */
4362*4882a593Smuzhiyun #if !defined(WL_CFG80211_STA_EVENT) && (LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
4363*4882a593Smuzhiyun static s32
wl_notify_connect_status_ap_legacy(struct bcm_cfg80211 * cfg,struct net_device * ndev const wl_event_msg_t * e,void * data)4364*4882a593Smuzhiyun wl_notify_connect_status_ap_legacy(struct bcm_cfg80211 *cfg, struct net_device *ndev
4365*4882a593Smuzhiyun 	const wl_event_msg_t *e, void *data)
4366*4882a593Smuzhiyun {
4367*4882a593Smuzhiyun 	s32 err = 0;
4368*4882a593Smuzhiyun 	u32 event = ntoh32(e->event_type);
4369*4882a593Smuzhiyun 	u32 reason = ntoh32(e->reason);
4370*4882a593Smuzhiyun 	u32 len = ntoh32(e->datalen);
4371*4882a593Smuzhiyun 	u32 status = ntoh32(e->status);
4372*4882a593Smuzhiyun 
4373*4882a593Smuzhiyun 	bool isfree = false;
4374*4882a593Smuzhiyun 	u8 *mgmt_frame;
4375*4882a593Smuzhiyun 	u8 bsscfgidx = e->bsscfgidx;
4376*4882a593Smuzhiyun 	s32 freq;
4377*4882a593Smuzhiyun 	s32 channel;
4378*4882a593Smuzhiyun 	u8 *body = NULL;
4379*4882a593Smuzhiyun 	u16 fc = 0;
4380*4882a593Smuzhiyun 	u32 body_len = 0;
4381*4882a593Smuzhiyun 
4382*4882a593Smuzhiyun 	struct ieee80211_supported_band *band;
4383*4882a593Smuzhiyun 	struct ether_addr da;
4384*4882a593Smuzhiyun 	struct ether_addr bssid;
4385*4882a593Smuzhiyun 	struct wiphy *wiphy = bcmcfg_to_wiphy(cfg);
4386*4882a593Smuzhiyun 	channel_info_t ci;
4387*4882a593Smuzhiyun 	u8 ioctl_buf[WLC_IOCTL_SMLEN];
4388*4882a593Smuzhiyun 
4389*4882a593Smuzhiyun 	WL_DBG(("Enter \n"));
4390*4882a593Smuzhiyun 	if (!len && (event == WLC_E_DEAUTH)) {
4391*4882a593Smuzhiyun 		len = 2; /* reason code field */
4392*4882a593Smuzhiyun 		data = &reason;
4393*4882a593Smuzhiyun 	}
4394*4882a593Smuzhiyun 	if (len) {
4395*4882a593Smuzhiyun 		body = (u8 *)MALLOCZ(cfg->osh, len);
4396*4882a593Smuzhiyun 		if (body == NULL) {
4397*4882a593Smuzhiyun 			WL_ERR(("wl_notify_connect_status: Failed to allocate body\n"));
4398*4882a593Smuzhiyun 			return WL_INVALID;
4399*4882a593Smuzhiyun 		}
4400*4882a593Smuzhiyun 	}
4401*4882a593Smuzhiyun 	bzero(&bssid, ETHER_ADDR_LEN);
4402*4882a593Smuzhiyun 	WL_DBG(("Enter event %d ndev %p\n", event, ndev));
4403*4882a593Smuzhiyun 	if (wl_get_mode_by_netdev(cfg, ndev) == WL_INVALID) {
4404*4882a593Smuzhiyun 		MFREE(cfg->osh, body, len);
4405*4882a593Smuzhiyun 		return WL_INVALID;
4406*4882a593Smuzhiyun 	}
4407*4882a593Smuzhiyun 	if (len)
4408*4882a593Smuzhiyun 		memcpy(body, data, len);
4409*4882a593Smuzhiyun 
4410*4882a593Smuzhiyun 	wldev_iovar_getbuf_bsscfg(ndev, "cur_etheraddr",
4411*4882a593Smuzhiyun 		NULL, 0, ioctl_buf, sizeof(ioctl_buf), bsscfgidx, NULL);
4412*4882a593Smuzhiyun 	memcpy(da.octet, ioctl_buf, ETHER_ADDR_LEN);
4413*4882a593Smuzhiyun 	bzero(&bssid, sizeof(bssid));
4414*4882a593Smuzhiyun 	err = wldev_ioctl_get(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN);
4415*4882a593Smuzhiyun 	switch (event) {
4416*4882a593Smuzhiyun 		case WLC_E_ASSOC_IND:
4417*4882a593Smuzhiyun 			fc = FC_ASSOC_REQ;
4418*4882a593Smuzhiyun 			break;
4419*4882a593Smuzhiyun 		case WLC_E_REASSOC_IND:
4420*4882a593Smuzhiyun 			fc = FC_REASSOC_REQ;
4421*4882a593Smuzhiyun 			break;
4422*4882a593Smuzhiyun 		case WLC_E_DISASSOC_IND:
4423*4882a593Smuzhiyun 			fc = FC_DISASSOC;
4424*4882a593Smuzhiyun 			break;
4425*4882a593Smuzhiyun 		case WLC_E_DEAUTH_IND:
4426*4882a593Smuzhiyun 			fc = FC_DISASSOC;
4427*4882a593Smuzhiyun 			break;
4428*4882a593Smuzhiyun 		case WLC_E_DEAUTH:
4429*4882a593Smuzhiyun 			fc = FC_DISASSOC;
4430*4882a593Smuzhiyun 			break;
4431*4882a593Smuzhiyun 		default:
4432*4882a593Smuzhiyun 			fc = 0;
4433*4882a593Smuzhiyun 			goto exit;
4434*4882a593Smuzhiyun 	}
4435*4882a593Smuzhiyun 	err = wldev_iovar_getint(ndev, "chanspec", (s32 *)&chanspec);
4436*4882a593Smuzhiyun 	if (unlikely(err)) {
4437*4882a593Smuzhiyun 		MFREE(cfg->osh, body, len);
4438*4882a593Smuzhiyun 		WL_ERR(("%s: Could not get chanspec %d\n", __FUNCTION__, err));
4439*4882a593Smuzhiyun 		return err;
4440*4882a593Smuzhiyun 	}
4441*4882a593Smuzhiyun 	chanspec = wl_chspec_driver_to_host(chanspec);
4442*4882a593Smuzhiyun 	freq = wl_channel_to_frequency(wf_chspec_ctlchan(chanspec), CHSPEC_BAND(chanspec));
4443*4882a593Smuzhiyun 	body_len = len;
4444*4882a593Smuzhiyun 	err = wl_frame_get_mgmt(cfg, fc, &da, &e->addr, &bssid,
4445*4882a593Smuzhiyun 		&mgmt_frame, &len, body);
4446*4882a593Smuzhiyun 	if (err < 0)
4447*4882a593Smuzhiyun 		goto exit;
4448*4882a593Smuzhiyun 	isfree = true;
4449*4882a593Smuzhiyun 
4450*4882a593Smuzhiyun 	if ((event == WLC_E_ASSOC_IND && reason == DOT11_SC_SUCCESS) ||
4451*4882a593Smuzhiyun 			(event == WLC_E_DISASSOC_IND) ||
4452*4882a593Smuzhiyun 			((event == WLC_E_DEAUTH_IND) || (event == WLC_E_DEAUTH))) {
4453*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
4454*4882a593Smuzhiyun 		cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, 0);
4455*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
4456*4882a593Smuzhiyun 		cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, 0, GFP_ATOMIC);
4457*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \
4458*4882a593Smuzhiyun 		defined(WL_COMPAT_WIRELESS)
4459*4882a593Smuzhiyun 		cfg80211_rx_mgmt(ndev, freq, 0, mgmt_frame, len, GFP_ATOMIC);
4460*4882a593Smuzhiyun #else
4461*4882a593Smuzhiyun 		cfg80211_rx_mgmt(ndev, freq, mgmt_frame, len, GFP_ATOMIC);
4462*4882a593Smuzhiyun #endif /* LINUX_VERSION >= VERSION(3, 18,0) || WL_COMPAT_WIRELESS */
4463*4882a593Smuzhiyun 	}
4464*4882a593Smuzhiyun 
4465*4882a593Smuzhiyun exit:
4466*4882a593Smuzhiyun 	if (isfree) {
4467*4882a593Smuzhiyun 		MFREE(cfg->osh, mgmt_frame, len);
4468*4882a593Smuzhiyun 	}
4469*4882a593Smuzhiyun 	if (body) {
4470*4882a593Smuzhiyun 		MFREE(cfg->osh, body, body_len);
4471*4882a593Smuzhiyun 	}
4472*4882a593Smuzhiyun 
4473*4882a593Smuzhiyun }
4474*4882a593Smuzhiyun #endif /* WL_CFG80211_STA_EVENT || KERNEL_VER < 3.2 */
4475*4882a593Smuzhiyun 
4476*4882a593Smuzhiyun s32
wl_notify_connect_status_ap(struct bcm_cfg80211 * cfg,struct net_device * ndev,const wl_event_msg_t * e,void * data)4477*4882a593Smuzhiyun wl_notify_connect_status_ap(struct bcm_cfg80211 *cfg, struct net_device *ndev,
4478*4882a593Smuzhiyun 	const wl_event_msg_t *e, void *data)
4479*4882a593Smuzhiyun {
4480*4882a593Smuzhiyun 	s32 err = 0;
4481*4882a593Smuzhiyun 	u32 event = ntoh32(e->event_type);
4482*4882a593Smuzhiyun 	u32 reason = ntoh32(e->reason);
4483*4882a593Smuzhiyun 	u32 len = ntoh32(e->datalen);
4484*4882a593Smuzhiyun 	u32 status = ntoh32(e->status);
4485*4882a593Smuzhiyun #if defined(WL_CFG80211_STA_EVENT) || (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 2, 0))
4486*4882a593Smuzhiyun 	struct station_info sinfo;
4487*4882a593Smuzhiyun #endif /* (LINUX_VERSION >= VERSION(3,2,0)) || !WL_CFG80211_STA_EVENT */
4488*4882a593Smuzhiyun #ifdef BIGDATA_SOFTAP
4489*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
4490*4882a593Smuzhiyun #endif /* BIGDATA_SOFTAP */
4491*4882a593Smuzhiyun 
4492*4882a593Smuzhiyun 	WL_INFORM_MEM(("[%s] Mode AP/GO. Event:%d status:%d reason:%d\n",
4493*4882a593Smuzhiyun 		ndev->name, event, ntoh32(e->status), reason));
4494*4882a593Smuzhiyun 
4495*4882a593Smuzhiyun #ifdef WL_CLIENT_SAE
4496*4882a593Smuzhiyun 	if (event == WLC_E_AUTH && ntoh32(e->auth_type) == DOT11_SAE) {
4497*4882a593Smuzhiyun 		WL_MSG_RLMT(ndev->name, &e->addr, ETHER_ADDR_LEN,
4498*4882a593Smuzhiyun 			"add sta auth event for "MACDBG "\n", MAC2STRDBG(e->addr.octet));
4499*4882a593Smuzhiyun 		err = wl_handle_auth_event(cfg, ndev, e, data);
4500*4882a593Smuzhiyun 		if (err != BCME_OK) {
4501*4882a593Smuzhiyun 			return err;
4502*4882a593Smuzhiyun 		}
4503*4882a593Smuzhiyun 	}
4504*4882a593Smuzhiyun #endif /* WL_CLIENT_SAE */
4505*4882a593Smuzhiyun 
4506*4882a593Smuzhiyun 	if (event == WLC_E_AUTH_IND) {
4507*4882a593Smuzhiyun #ifdef WL_SAE
4508*4882a593Smuzhiyun 		if (ntoh32(e->auth_type) == DOT11_SAE) {
4509*4882a593Smuzhiyun 			wl_bss_handle_sae_auth(cfg, ndev, e, data);
4510*4882a593Smuzhiyun 		}
4511*4882a593Smuzhiyun #endif /* WL_SAE */
4512*4882a593Smuzhiyun 		wl_get_auth_assoc_status(cfg, ndev, e, data);
4513*4882a593Smuzhiyun 		return 0;
4514*4882a593Smuzhiyun 	}
4515*4882a593Smuzhiyun 	/* if link down, bsscfg is disabled. */
4516*4882a593Smuzhiyun 	if (event == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS &&
4517*4882a593Smuzhiyun 		wl_get_p2p_status(cfg, IF_DELETING) && (ndev != bcmcfg_to_prmry_ndev(cfg))) {
4518*4882a593Smuzhiyun 		wl_add_remove_eventmsg(ndev, WLC_E_PROBREQ_MSG, false);
4519*4882a593Smuzhiyun 		WL_MSG(ndev->name, "AP mode link down !! \n");
4520*4882a593Smuzhiyun 		complete(&cfg->iface_disable);
4521*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
4522*4882a593Smuzhiyun 		wl_ext_in4way_sync(ndev, 0, WL_EXT_STATUS_AP_DISABLED, NULL);
4523*4882a593Smuzhiyun #endif
4524*4882a593Smuzhiyun 		return 0;
4525*4882a593Smuzhiyun 	}
4526*4882a593Smuzhiyun 
4527*4882a593Smuzhiyun 	if ((event == WLC_E_LINK) && (status == WLC_E_STATUS_SUCCESS) &&
4528*4882a593Smuzhiyun 		(reason == WLC_E_REASON_INITIAL_ASSOC) &&
4529*4882a593Smuzhiyun 		(wl_get_mode_by_netdev(cfg, ndev) == WL_MODE_AP)) {
4530*4882a593Smuzhiyun 		if (!wl_get_drv_status(cfg, AP_CREATED, ndev)) {
4531*4882a593Smuzhiyun 			char chan_str[64];
4532*4882a593Smuzhiyun 			/* AP/GO brought up successfull in firmware */
4533*4882a593Smuzhiyun 			wl_ext_get_chan_str(ndev, chan_str, sizeof(chan_str));
4534*4882a593Smuzhiyun 			WL_MSG(ndev->name, "AP/GO Link up (%s)\n", chan_str);
4535*4882a593Smuzhiyun 			wl_set_drv_status(cfg, AP_CREATED, ndev);
4536*4882a593Smuzhiyun 			if (delayed_work_pending(&cfg->ap_work)) {
4537*4882a593Smuzhiyun 				cancel_delayed_work_sync(&cfg->ap_work);
4538*4882a593Smuzhiyun 				WL_DBG(("cancelled ap_work\n"));
4539*4882a593Smuzhiyun 			}
4540*4882a593Smuzhiyun #ifdef BIGDATA_SOFTAP
4541*4882a593Smuzhiyun 			wl_ap_stainfo_init(cfg);
4542*4882a593Smuzhiyun #endif /* BIGDATA_SOFTAP */
4543*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
4544*4882a593Smuzhiyun 			wl_ext_in4way_sync(ndev, 0, WL_EXT_STATUS_AP_ENABLED, NULL);
4545*4882a593Smuzhiyun #endif
4546*4882a593Smuzhiyun 			return 0;
4547*4882a593Smuzhiyun 		}
4548*4882a593Smuzhiyun 	}
4549*4882a593Smuzhiyun 
4550*4882a593Smuzhiyun 	if (event == WLC_E_DISASSOC_IND || event == WLC_E_DEAUTH_IND || event == WLC_E_DEAUTH) {
4551*4882a593Smuzhiyun 		WL_MSG_RLMT(ndev->name, &e->addr, ETHER_ADDR_LEN,
4552*4882a593Smuzhiyun 			"event %s(%d) status %d reason %d\n",
4553*4882a593Smuzhiyun 			bcmevent_get_name(event), event, ntoh32(e->status), reason);
4554*4882a593Smuzhiyun 	}
4555*4882a593Smuzhiyun 
4556*4882a593Smuzhiyun #ifdef BIGDATA_SOFTAP
4557*4882a593Smuzhiyun 	if (event == WLC_E_LINK && reason == WLC_E_LINK_BSSCFG_DIS) {
4558*4882a593Smuzhiyun 		WL_ERR(("AP link down - skip get sta data\n"));
4559*4882a593Smuzhiyun 	} else {
4560*4882a593Smuzhiyun 		dhdp = (dhd_pub_t *)(cfg->pub);
4561*4882a593Smuzhiyun 		if (dhdp && dhdp->op_mode & DHD_FLAG_HOSTAP_MODE) {
4562*4882a593Smuzhiyun 			dhd_schedule_gather_ap_stadata(cfg, ndev, e);
4563*4882a593Smuzhiyun 		}
4564*4882a593Smuzhiyun 	}
4565*4882a593Smuzhiyun #endif /* BIGDATA_SOFTAP */
4566*4882a593Smuzhiyun 
4567*4882a593Smuzhiyun #if !defined(WL_CFG80211_STA_EVENT) && !defined(WL_COMPAT_WIRELESS) && \
4568*4882a593Smuzhiyun 	(LINUX_VERSION_CODE < KERNEL_VERSION(3, 2, 0))
4569*4882a593Smuzhiyun 	err = wl_notify_connect_status_ap_legacy(cfg, ndev, e, data);
4570*4882a593Smuzhiyun #else /* LINUX_VERSION < VERSION(3,2,0) && !WL_CFG80211_STA_EVENT && !WL_COMPAT_WIRELESS */
4571*4882a593Smuzhiyun 	memset_s(&sinfo, sizeof(sinfo), 0, sizeof(sinfo));
4572*4882a593Smuzhiyun 	if (((event == WLC_E_ASSOC_IND) || (event == WLC_E_REASSOC_IND)) &&
4573*4882a593Smuzhiyun 		reason == DOT11_SC_SUCCESS) {
4574*4882a593Smuzhiyun 		/* Linux ver >= 4.0 assoc_req_ies_len is used instead of
4575*4882a593Smuzhiyun 		 * STATION_INFO_ASSOC_REQ_IES flag
4576*4882a593Smuzhiyun 		 */
4577*4882a593Smuzhiyun #if (LINUX_VERSION_CODE < KERNEL_VERSION(4, 0, 0))
4578*4882a593Smuzhiyun 		sinfo.filled = STA_INFO_BIT(INFO_ASSOC_REQ_IES);
4579*4882a593Smuzhiyun #endif /*  (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 0, 0)) */
4580*4882a593Smuzhiyun 		if (!data) {
4581*4882a593Smuzhiyun 			WL_ERR(("No IEs present in ASSOC/REASSOC_IND"));
4582*4882a593Smuzhiyun 			return -EINVAL;
4583*4882a593Smuzhiyun 		}
4584*4882a593Smuzhiyun 		sinfo.assoc_req_ies = data;
4585*4882a593Smuzhiyun 		sinfo.assoc_req_ies_len = len;
4586*4882a593Smuzhiyun 		WL_MSG(ndev->name, "new sta event for "MACDBG "\n",
4587*4882a593Smuzhiyun 			MAC2STRDBG(e->addr.octet));
4588*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
4589*4882a593Smuzhiyun 		wl_ext_in4way_sync(ndev, AP_WAIT_STA_RECONNECT,
4590*4882a593Smuzhiyun 			WL_EXT_STATUS_STA_CONNECTED, (void *)&e->addr);
4591*4882a593Smuzhiyun #endif
4592*4882a593Smuzhiyun #ifdef STA_MGMT
4593*4882a593Smuzhiyun 		if (!wl_ext_add_sta_info(ndev, (u8 *)&e->addr)) {
4594*4882a593Smuzhiyun 			return -EINVAL;
4595*4882a593Smuzhiyun 		}
4596*4882a593Smuzhiyun #endif /* STA_MGMT */
4597*4882a593Smuzhiyun 		cfg80211_new_sta(ndev, e->addr.octet, &sinfo, GFP_ATOMIC);
4598*4882a593Smuzhiyun #ifdef WL_WPS_SYNC
4599*4882a593Smuzhiyun 		wl_wps_session_update(ndev, WPS_STATE_LINKUP, e->addr.octet);
4600*4882a593Smuzhiyun #endif /* WL_WPS_SYNC */
4601*4882a593Smuzhiyun 	} else if ((event == WLC_E_DEAUTH_IND) ||
4602*4882a593Smuzhiyun 		((event == WLC_E_DEAUTH) && (reason != DOT11_RC_RESERVED)) ||
4603*4882a593Smuzhiyun 		(event == WLC_E_DISASSOC_IND)) {
4604*4882a593Smuzhiyun 		/*
4605*4882a593Smuzhiyun 		 * WAR: Dongle sends WLC_E_DEAUTH event with DOT11_RC_RESERVED
4606*4882a593Smuzhiyun 		 * to delete flowring in case of PCIE Full dongle.
4607*4882a593Smuzhiyun 		 * By deleting flowring on SoftAP interface we can avoid any issues
4608*4882a593Smuzhiyun 		 * due to stale/bad state of flowring.
4609*4882a593Smuzhiyun 		 * Therefore, we don't need to notify the client dissaociation to Hostapd
4610*4882a593Smuzhiyun 		 * in this case.
4611*4882a593Smuzhiyun 		 * Please refer to the RB:115182 to understand the case more clearly.
4612*4882a593Smuzhiyun 		 */
4613*4882a593Smuzhiyun 		WL_MSG_RLMT(ndev->name, &e->addr, ETHER_ADDR_LEN,
4614*4882a593Smuzhiyun 			"del sta event for "MACDBG "\n", MAC2STRDBG(e->addr.octet));
4615*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
4616*4882a593Smuzhiyun 		wl_ext_in4way_sync(ndev, AP_WAIT_STA_RECONNECT,
4617*4882a593Smuzhiyun 			WL_EXT_STATUS_STA_DISCONNECTED, (void *)&e->addr);
4618*4882a593Smuzhiyun #endif
4619*4882a593Smuzhiyun #ifdef STA_MGMT
4620*4882a593Smuzhiyun 		if (!wl_ext_del_sta_info(ndev, (u8 *)&e->addr)) {
4621*4882a593Smuzhiyun 			return -EINVAL;
4622*4882a593Smuzhiyun 		}
4623*4882a593Smuzhiyun #endif /* STA_MGMT */
4624*4882a593Smuzhiyun 		cfg80211_del_sta(ndev, e->addr.octet, GFP_ATOMIC);
4625*4882a593Smuzhiyun #ifdef WL_WPS_SYNC
4626*4882a593Smuzhiyun 		wl_wps_session_update(ndev, WPS_STATE_LINKDOWN, e->addr.octet);
4627*4882a593Smuzhiyun #endif /* WL_WPS_SYNC */
4628*4882a593Smuzhiyun 	}
4629*4882a593Smuzhiyun 
4630*4882a593Smuzhiyun #endif /* LINUX_VERSION < VERSION(3,2,0) && !WL_CFG80211_STA_EVENT && !WL_COMPAT_WIRELESS */
4631*4882a593Smuzhiyun 	return err;
4632*4882a593Smuzhiyun }
4633*4882a593Smuzhiyun 
4634*4882a593Smuzhiyun s32
wl_frame_get_mgmt(struct bcm_cfg80211 * cfg,u16 fc,const struct ether_addr * da,const struct ether_addr * sa,const struct ether_addr * bssid,u8 ** pheader,u32 * body_len,u8 * pbody)4635*4882a593Smuzhiyun wl_frame_get_mgmt(struct bcm_cfg80211 *cfg, u16 fc,
4636*4882a593Smuzhiyun 	const struct ether_addr *da, const struct ether_addr *sa,
4637*4882a593Smuzhiyun 	const struct ether_addr *bssid, u8 **pheader, u32 *body_len, u8 *pbody)
4638*4882a593Smuzhiyun {
4639*4882a593Smuzhiyun 	struct dot11_management_header *hdr;
4640*4882a593Smuzhiyun 	u32 totlen = 0;
4641*4882a593Smuzhiyun 	s32 err = 0;
4642*4882a593Smuzhiyun 	u8 *offset;
4643*4882a593Smuzhiyun 	u32 prebody_len = *body_len;
4644*4882a593Smuzhiyun 	switch (fc) {
4645*4882a593Smuzhiyun 		case FC_ASSOC_REQ:
4646*4882a593Smuzhiyun 			/* capability , listen interval */
4647*4882a593Smuzhiyun 			totlen = DOT11_ASSOC_REQ_FIXED_LEN;
4648*4882a593Smuzhiyun 			*body_len += DOT11_ASSOC_REQ_FIXED_LEN;
4649*4882a593Smuzhiyun 			break;
4650*4882a593Smuzhiyun 
4651*4882a593Smuzhiyun 		case FC_REASSOC_REQ:
4652*4882a593Smuzhiyun 			/* capability, listen inteval, ap address */
4653*4882a593Smuzhiyun 			totlen = DOT11_REASSOC_REQ_FIXED_LEN;
4654*4882a593Smuzhiyun 			*body_len += DOT11_REASSOC_REQ_FIXED_LEN;
4655*4882a593Smuzhiyun 			break;
4656*4882a593Smuzhiyun 	}
4657*4882a593Smuzhiyun 	totlen += DOT11_MGMT_HDR_LEN + prebody_len;
4658*4882a593Smuzhiyun 	*pheader = (u8 *)MALLOCZ(cfg->osh, totlen);
4659*4882a593Smuzhiyun 	if (*pheader == NULL) {
4660*4882a593Smuzhiyun 		WL_ERR(("memory alloc failed \n"));
4661*4882a593Smuzhiyun 		return -ENOMEM;
4662*4882a593Smuzhiyun 	}
4663*4882a593Smuzhiyun 	hdr = (struct dot11_management_header *) (*pheader);
4664*4882a593Smuzhiyun 	hdr->fc = htol16(fc);
4665*4882a593Smuzhiyun 	hdr->durid = 0;
4666*4882a593Smuzhiyun 	hdr->seq = 0;
4667*4882a593Smuzhiyun 	offset = (u8*)(hdr + 1) + (totlen - DOT11_MGMT_HDR_LEN - prebody_len);
4668*4882a593Smuzhiyun 	bcopy((const char*)da, (u8*)&hdr->da, ETHER_ADDR_LEN);
4669*4882a593Smuzhiyun 	bcopy((const char*)sa, (u8*)&hdr->sa, ETHER_ADDR_LEN);
4670*4882a593Smuzhiyun 	bcopy((const char*)bssid, (u8*)&hdr->bssid, ETHER_ADDR_LEN);
4671*4882a593Smuzhiyun 	if ((pbody != NULL) && prebody_len)
4672*4882a593Smuzhiyun 		bcopy((const char*)pbody, offset, prebody_len);
4673*4882a593Smuzhiyun 	*body_len = totlen;
4674*4882a593Smuzhiyun 	return err;
4675*4882a593Smuzhiyun }
4676*4882a593Smuzhiyun 
4677*4882a593Smuzhiyun #if defined(WLTDLS)
wl_cfg80211_is_tdls_tunneled_frame(void * frame,u32 frame_len)4678*4882a593Smuzhiyun bool wl_cfg80211_is_tdls_tunneled_frame(void *frame, u32 frame_len)
4679*4882a593Smuzhiyun {
4680*4882a593Smuzhiyun 	unsigned char *data;
4681*4882a593Smuzhiyun 
4682*4882a593Smuzhiyun 	if (frame == NULL) {
4683*4882a593Smuzhiyun 		WL_ERR(("Invalid frame \n"));
4684*4882a593Smuzhiyun 		return false;
4685*4882a593Smuzhiyun 	}
4686*4882a593Smuzhiyun 
4687*4882a593Smuzhiyun 	if (frame_len < 5) {
4688*4882a593Smuzhiyun 		WL_ERR(("Invalid frame length [%d] \n", frame_len));
4689*4882a593Smuzhiyun 		return false;
4690*4882a593Smuzhiyun 	}
4691*4882a593Smuzhiyun 
4692*4882a593Smuzhiyun 	data = frame;
4693*4882a593Smuzhiyun 
4694*4882a593Smuzhiyun 	if (!memcmp(data, TDLS_TUNNELED_PRB_REQ, 5) ||
4695*4882a593Smuzhiyun 		!memcmp(data, TDLS_TUNNELED_PRB_RESP, 5)) {
4696*4882a593Smuzhiyun 		WL_DBG(("TDLS Vendor Specific Received type\n"));
4697*4882a593Smuzhiyun 		return true;
4698*4882a593Smuzhiyun 	}
4699*4882a593Smuzhiyun 
4700*4882a593Smuzhiyun 	return false;
4701*4882a593Smuzhiyun }
4702*4882a593Smuzhiyun #endif /* WLTDLS */
4703*4882a593Smuzhiyun 
4704*4882a593Smuzhiyun #ifdef WLTDLS
4705*4882a593Smuzhiyun s32
wl_tdls_event_handler(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,const wl_event_msg_t * e,void * data)4706*4882a593Smuzhiyun wl_tdls_event_handler(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
4707*4882a593Smuzhiyun 	const wl_event_msg_t *e, void *data) {
4708*4882a593Smuzhiyun 
4709*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
4710*4882a593Smuzhiyun 	u32 reason = ntoh32(e->reason);
4711*4882a593Smuzhiyun 	s8 *msg = NULL;
4712*4882a593Smuzhiyun 
4713*4882a593Smuzhiyun 	ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);
4714*4882a593Smuzhiyun 
4715*4882a593Smuzhiyun 	switch (reason) {
4716*4882a593Smuzhiyun 	case WLC_E_TDLS_PEER_DISCOVERED :
4717*4882a593Smuzhiyun 		msg = " TDLS PEER DISCOVERD ";
4718*4882a593Smuzhiyun 		break;
4719*4882a593Smuzhiyun 	case WLC_E_TDLS_PEER_CONNECTED :
4720*4882a593Smuzhiyun 		if (cfg->tdls_mgmt_frame) {
4721*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
4722*4882a593Smuzhiyun 			cfg80211_rx_mgmt(cfgdev, cfg->tdls_mgmt_freq, 0,
4723*4882a593Smuzhiyun 					cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len, 0);
4724*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
4725*4882a593Smuzhiyun 			cfg80211_rx_mgmt(cfgdev, cfg->tdls_mgmt_freq, 0,
4726*4882a593Smuzhiyun 					cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len,	0,
4727*4882a593Smuzhiyun 					GFP_ATOMIC);
4728*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 4, 0)) || \
4729*4882a593Smuzhiyun 			defined(WL_COMPAT_WIRELESS)
4730*4882a593Smuzhiyun 			cfg80211_rx_mgmt(cfgdev, cfg->tdls_mgmt_freq, 0,
4731*4882a593Smuzhiyun 					cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len,
4732*4882a593Smuzhiyun 					GFP_ATOMIC);
4733*4882a593Smuzhiyun #else
4734*4882a593Smuzhiyun 			cfg80211_rx_mgmt(cfgdev, cfg->tdls_mgmt_freq,
4735*4882a593Smuzhiyun 					cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len, GFP_ATOMIC);
4736*4882a593Smuzhiyun 
4737*4882a593Smuzhiyun #endif /* LINUX_VERSION >= VERSION(3, 18,0) || WL_COMPAT_WIRELESS */
4738*4882a593Smuzhiyun 		}
4739*4882a593Smuzhiyun 		msg = " TDLS PEER CONNECTED ";
4740*4882a593Smuzhiyun #ifdef SUPPORT_SET_CAC
4741*4882a593Smuzhiyun 		/* TDLS connect reset CAC */
4742*4882a593Smuzhiyun 		wl_cfg80211_set_cac(cfg, 0);
4743*4882a593Smuzhiyun #endif /* SUPPORT_SET_CAC */
4744*4882a593Smuzhiyun 		break;
4745*4882a593Smuzhiyun 	case WLC_E_TDLS_PEER_DISCONNECTED :
4746*4882a593Smuzhiyun 		if (cfg->tdls_mgmt_frame) {
4747*4882a593Smuzhiyun 			MFREE(cfg->osh, cfg->tdls_mgmt_frame, cfg->tdls_mgmt_frame_len);
4748*4882a593Smuzhiyun 			cfg->tdls_mgmt_frame_len = 0;
4749*4882a593Smuzhiyun 			cfg->tdls_mgmt_freq = 0;
4750*4882a593Smuzhiyun 		}
4751*4882a593Smuzhiyun 		msg = "TDLS PEER DISCONNECTED ";
4752*4882a593Smuzhiyun #ifdef SUPPORT_SET_CAC
4753*4882a593Smuzhiyun 		/* TDLS disconnec, set CAC */
4754*4882a593Smuzhiyun 		wl_cfg80211_set_cac(cfg, 1);
4755*4882a593Smuzhiyun #endif /* SUPPORT_SET_CAC */
4756*4882a593Smuzhiyun 		break;
4757*4882a593Smuzhiyun 	}
4758*4882a593Smuzhiyun 	if (msg) {
4759*4882a593Smuzhiyun 		WL_ERR(("%s: " MACDBG " on %s ndev\n", msg, MAC2STRDBG((const u8*)(&e->addr)),
4760*4882a593Smuzhiyun 			(bcmcfg_to_prmry_ndev(cfg) == ndev) ? "primary" : "secondary"));
4761*4882a593Smuzhiyun 	}
4762*4882a593Smuzhiyun 	return 0;
4763*4882a593Smuzhiyun 
4764*4882a593Smuzhiyun }
4765*4882a593Smuzhiyun 
4766*4882a593Smuzhiyun #if defined(CUSTOMER_HW10)
wl_tdls_enable(struct bcm_cfg80211 * cfg)4767*4882a593Smuzhiyun static void wl_tdls_enable(struct bcm_cfg80211 *cfg)
4768*4882a593Smuzhiyun {
4769*4882a593Smuzhiyun 	int enable = true;
4770*4882a593Smuzhiyun 	int err = 0;
4771*4882a593Smuzhiyun 	struct net_device *primary_dev = bcmcfg_to_prmry_ndev(cfg);
4772*4882a593Smuzhiyun 	dhd_pub_t *dhd =  (dhd_pub_t *)(cfg->pub);
4773*4882a593Smuzhiyun 
4774*4882a593Smuzhiyun /* #define IS_P2P_OPERATING (p2p_is_on(cfg) && cfg->p2p->vif_created ) */
4775*4882a593Smuzhiyun #define IS_P2P_OPERATING (dhd->op_mode & (DHD_FLAG_P2P_GC_MODE | DHD_FLAG_P2P_GO_MODE))
4776*4882a593Smuzhiyun #if !defined(DISABLE_TDLS_IN_P2P)
4777*4882a593Smuzhiyun 	if (cfg->vsdb_mode)
4778*4882a593Smuzhiyun #else
4779*4882a593Smuzhiyun 	if (cfg->vsdb_mode || IS_P2P_OPERATING)
4780*4882a593Smuzhiyun #endif
4781*4882a593Smuzhiyun 	{
4782*4882a593Smuzhiyun 		enable = false;
4783*4882a593Smuzhiyun 	}
4784*4882a593Smuzhiyun 
4785*4882a593Smuzhiyun 	err = wldev_iovar_setint(primary_dev, "tdls_enable", enable);
4786*4882a593Smuzhiyun 	if (err) {
4787*4882a593Smuzhiyun 		WL_ERR(("tdls_enable failed!!: %d\n", enable));
4788*4882a593Smuzhiyun 	}
4789*4882a593Smuzhiyun #undef IS_P2P_OPERATING
4790*4882a593Smuzhiyun }
4791*4882a593Smuzhiyun #endif  /* defined(CUSTOMER_HW10) */
4792*4882a593Smuzhiyun 
4793*4882a593Smuzhiyun #endif  /* WLTDLS */
4794*4882a593Smuzhiyun 
4795*4882a593Smuzhiyun #if (LINUX_VERSION_CODE > KERNEL_VERSION(3, 2, 0)) || \
4796*4882a593Smuzhiyun 	defined(WL_COMPAT_WIRELESS)
4797*4882a593Smuzhiyun s32
4798*4882a593Smuzhiyun #if (defined(CONFIG_ARCH_MSM) && defined(TDLS_MGMT_VERSION2)) || \
4799*4882a593Smuzhiyun 	((LINUX_VERSION_CODE < KERNEL_VERSION(3, 16, 0) && LINUX_VERSION_CODE >= KERNEL_VERSION(3, 15, 0)))
wl_cfg80211_tdls_mgmt(struct wiphy * wiphy,struct net_device * dev,u8 * peer,u8 action_code,u8 dialog_token,u16 status_code,u32 peer_capability,const u8 * buf,size_t len)4800*4882a593Smuzhiyun wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
4801*4882a593Smuzhiyun 	u8 *peer, u8 action_code, u8 dialog_token, u16 status_code,
4802*4882a593Smuzhiyun 	u32 peer_capability, const u8 *buf, size_t len)
4803*4882a593Smuzhiyun #elif ((LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0)) && \
4804*4882a593Smuzhiyun 		(LINUX_VERSION_CODE < KERNEL_VERSION(3, 18, 0)))
4805*4882a593Smuzhiyun wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
4806*4882a593Smuzhiyun 	const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code,
4807*4882a593Smuzhiyun 	u32 peer_capability, const u8 *buf, size_t len)
4808*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 18, 0))
4809*4882a593Smuzhiyun wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
4810*4882a593Smuzhiyun        const u8 *peer, u8 action_code, u8 dialog_token, u16 status_code,
4811*4882a593Smuzhiyun        u32 peer_capability, bool initiator, const u8 *buf, size_t len)
4812*4882a593Smuzhiyun #else /* CONFIG_ARCH_MSM && TDLS_MGMT_VERSION2 */
4813*4882a593Smuzhiyun wl_cfg80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
4814*4882a593Smuzhiyun 	u8 *peer, u8 action_code, u8 dialog_token, u16 status_code,
4815*4882a593Smuzhiyun 	const u8 *buf, size_t len)
4816*4882a593Smuzhiyun #endif /* CONFIG_ARCH_MSM && TDLS_MGMT_VERSION2 */
4817*4882a593Smuzhiyun {
4818*4882a593Smuzhiyun 	s32 ret = 0;
4819*4882a593Smuzhiyun #if defined(BCMDONGLEHOST)
4820*4882a593Smuzhiyun #if defined(TDLS_MSG_ONLY_WFD) && defined(WLTDLS)
4821*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg;
4822*4882a593Smuzhiyun 	tdls_wfd_ie_iovar_t info;
4823*4882a593Smuzhiyun 	bzero(&info, sizeof(info));
4824*4882a593Smuzhiyun 	cfg = wl_get_cfg(dev);
4825*4882a593Smuzhiyun 
4826*4882a593Smuzhiyun #if defined(CONFIG_ARCH_MSM) && defined(TDLS_MGMT_VERSION2)
4827*4882a593Smuzhiyun 	/* Some customer platform back ported this feature from kernel 3.15 to kernel 3.10
4828*4882a593Smuzhiyun 	 * and that cuases build error
4829*4882a593Smuzhiyun 	 */
4830*4882a593Smuzhiyun 	BCM_REFERENCE(peer_capability);
4831*4882a593Smuzhiyun #endif  /* CONFIG_ARCH_MSM && TDLS_MGMT_VERSION2 */
4832*4882a593Smuzhiyun 
4833*4882a593Smuzhiyun 	switch (action_code) {
4834*4882a593Smuzhiyun 		/* We need to set TDLS Wifi Display IE to firmware
4835*4882a593Smuzhiyun 		 * using tdls_wfd_ie iovar
4836*4882a593Smuzhiyun 		 */
4837*4882a593Smuzhiyun 		case WLAN_TDLS_SET_PROBE_WFD_IE:
4838*4882a593Smuzhiyun 			WL_ERR(("wl_cfg80211_tdls_mgmt: WLAN_TDLS_SET_PROBE_WFD_IE\n"));
4839*4882a593Smuzhiyun 			info.mode = TDLS_WFD_PROBE_IE_TX;
4840*4882a593Smuzhiyun 
4841*4882a593Smuzhiyun 			if (len > sizeof(info.data)) {
4842*4882a593Smuzhiyun 				return -EINVAL;
4843*4882a593Smuzhiyun 			}
4844*4882a593Smuzhiyun 			memcpy(&info.data, buf, len);
4845*4882a593Smuzhiyun 			info.length = len;
4846*4882a593Smuzhiyun 			break;
4847*4882a593Smuzhiyun 		case WLAN_TDLS_SET_SETUP_WFD_IE:
4848*4882a593Smuzhiyun 			WL_ERR(("wl_cfg80211_tdls_mgmt: WLAN_TDLS_SET_SETUP_WFD_IE\n"));
4849*4882a593Smuzhiyun 			info.mode = TDLS_WFD_IE_TX;
4850*4882a593Smuzhiyun 
4851*4882a593Smuzhiyun 			if (len > sizeof(info.data)) {
4852*4882a593Smuzhiyun 				return -EINVAL;
4853*4882a593Smuzhiyun 			}
4854*4882a593Smuzhiyun 			memcpy(&info.data, buf, len);
4855*4882a593Smuzhiyun 			info.length = len;
4856*4882a593Smuzhiyun 			break;
4857*4882a593Smuzhiyun 		case WLAN_TDLS_SET_WFD_ENABLED:
4858*4882a593Smuzhiyun 			WL_ERR(("wl_cfg80211_tdls_mgmt: WLAN_TDLS_SET_MODE_WFD_ENABLED\n"));
4859*4882a593Smuzhiyun 			dhd_tdls_set_mode((dhd_pub_t *)(cfg->pub), true);
4860*4882a593Smuzhiyun 			goto out;
4861*4882a593Smuzhiyun 		case WLAN_TDLS_SET_WFD_DISABLED:
4862*4882a593Smuzhiyun 			WL_ERR(("wl_cfg80211_tdls_mgmt: WLAN_TDLS_SET_MODE_WFD_DISABLED\n"));
4863*4882a593Smuzhiyun 			dhd_tdls_set_mode((dhd_pub_t *)(cfg->pub), false);
4864*4882a593Smuzhiyun 			goto out;
4865*4882a593Smuzhiyun 		default:
4866*4882a593Smuzhiyun 			WL_ERR(("Unsupported action code : %d\n", action_code));
4867*4882a593Smuzhiyun 			goto out;
4868*4882a593Smuzhiyun 	}
4869*4882a593Smuzhiyun 	ret = wldev_iovar_setbuf(dev, "tdls_wfd_ie", &info, sizeof(info),
4870*4882a593Smuzhiyun 			cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync);
4871*4882a593Smuzhiyun 
4872*4882a593Smuzhiyun 	if (ret) {
4873*4882a593Smuzhiyun 		WL_ERR(("tdls_wfd_ie error %d\n", ret));
4874*4882a593Smuzhiyun 	}
4875*4882a593Smuzhiyun 
4876*4882a593Smuzhiyun out:
4877*4882a593Smuzhiyun #endif /* TDLS_MSG_ONLY_WFD && WLTDLS */
4878*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
4879*4882a593Smuzhiyun 	return ret;
4880*4882a593Smuzhiyun }
4881*4882a593Smuzhiyun 
4882*4882a593Smuzhiyun s32
4883*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 16, 0))
wl_cfg80211_tdls_oper(struct wiphy * wiphy,struct net_device * dev,const u8 * peer,enum nl80211_tdls_operation oper)4884*4882a593Smuzhiyun wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
4885*4882a593Smuzhiyun 	const u8 *peer, enum nl80211_tdls_operation oper)
4886*4882a593Smuzhiyun #else
4887*4882a593Smuzhiyun wl_cfg80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
4888*4882a593Smuzhiyun 	u8 *peer, enum nl80211_tdls_operation oper)
4889*4882a593Smuzhiyun #endif
4890*4882a593Smuzhiyun {
4891*4882a593Smuzhiyun 	s32 ret = 0;
4892*4882a593Smuzhiyun #ifdef WLTDLS
4893*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
4894*4882a593Smuzhiyun 	tdls_iovar_t info;
4895*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
4896*4882a593Smuzhiyun 	bool tdls_auto_mode = false;
4897*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
4898*4882a593Smuzhiyun 	bzero(&info, sizeof(tdls_iovar_t));
4899*4882a593Smuzhiyun 	if (peer) {
4900*4882a593Smuzhiyun 		memcpy(&info.ea, peer, ETHER_ADDR_LEN);
4901*4882a593Smuzhiyun 	} else {
4902*4882a593Smuzhiyun 		return -1;
4903*4882a593Smuzhiyun 	}
4904*4882a593Smuzhiyun 	switch (oper) {
4905*4882a593Smuzhiyun 	case NL80211_TDLS_DISCOVERY_REQ:
4906*4882a593Smuzhiyun 		/* If the discovery request is broadcast then we need to set
4907*4882a593Smuzhiyun 		 * info.mode to Tunneled Probe Request
4908*4882a593Smuzhiyun 		 */
4909*4882a593Smuzhiyun 		if (memcmp(peer, (const uint8 *)BSSID_BROADCAST, ETHER_ADDR_LEN) == 0) {
4910*4882a593Smuzhiyun 			info.mode = TDLS_MANUAL_EP_WFD_TPQ;
4911*4882a593Smuzhiyun 			WL_ERR(("wl_cfg80211_tdls_oper: TDLS TUNNELED PRBOBE REQUEST\n"));
4912*4882a593Smuzhiyun 		} else {
4913*4882a593Smuzhiyun 			info.mode = TDLS_MANUAL_EP_DISCOVERY;
4914*4882a593Smuzhiyun 		}
4915*4882a593Smuzhiyun 		break;
4916*4882a593Smuzhiyun 	case NL80211_TDLS_SETUP:
4917*4882a593Smuzhiyun 		if (dhdp->tdls_mode == true) {
4918*4882a593Smuzhiyun 			info.mode = TDLS_MANUAL_EP_CREATE;
4919*4882a593Smuzhiyun 			tdls_auto_mode = false;
4920*4882a593Smuzhiyun 			/* Do tear down and create a fresh one */
4921*4882a593Smuzhiyun 			ret = wl_cfg80211_tdls_config(cfg, TDLS_STATE_TEARDOWN, tdls_auto_mode);
4922*4882a593Smuzhiyun 			if (ret < 0) {
4923*4882a593Smuzhiyun 				return ret;
4924*4882a593Smuzhiyun 			}
4925*4882a593Smuzhiyun 		} else {
4926*4882a593Smuzhiyun 			tdls_auto_mode = true;
4927*4882a593Smuzhiyun 		}
4928*4882a593Smuzhiyun 		break;
4929*4882a593Smuzhiyun 	case NL80211_TDLS_TEARDOWN:
4930*4882a593Smuzhiyun 		info.mode = TDLS_MANUAL_EP_DELETE;
4931*4882a593Smuzhiyun 		break;
4932*4882a593Smuzhiyun 	default:
4933*4882a593Smuzhiyun 		WL_ERR(("Unsupported operation : %d\n", oper));
4934*4882a593Smuzhiyun 		goto out;
4935*4882a593Smuzhiyun 	}
4936*4882a593Smuzhiyun 	/* turn on TDLS */
4937*4882a593Smuzhiyun 	ret = wl_cfg80211_tdls_config(cfg, TDLS_STATE_SETUP, tdls_auto_mode);
4938*4882a593Smuzhiyun 	if (ret < 0) {
4939*4882a593Smuzhiyun 		return ret;
4940*4882a593Smuzhiyun 	}
4941*4882a593Smuzhiyun 	if (info.mode) {
4942*4882a593Smuzhiyun 		ret = wldev_iovar_setbuf(dev, "tdls_endpoint", &info, sizeof(info),
4943*4882a593Smuzhiyun 			cfg->ioctl_buf, WLC_IOCTL_MAXLEN, &cfg->ioctl_buf_sync);
4944*4882a593Smuzhiyun 		if (ret) {
4945*4882a593Smuzhiyun 			WL_ERR(("tdls_endpoint error %d\n", ret));
4946*4882a593Smuzhiyun 		}
4947*4882a593Smuzhiyun 	}
4948*4882a593Smuzhiyun out:
4949*4882a593Smuzhiyun 	/* use linux generic error code instead of firmware error code */
4950*4882a593Smuzhiyun 	if (ret) {
4951*4882a593Smuzhiyun 		wl_flush_fw_log_buffer(dev, FW_LOGSET_MASK_ALL);
4952*4882a593Smuzhiyun 		return -ENOTSUPP;
4953*4882a593Smuzhiyun 	}
4954*4882a593Smuzhiyun #endif /* WLTDLS */
4955*4882a593Smuzhiyun 	return ret;
4956*4882a593Smuzhiyun }
4957*4882a593Smuzhiyun #endif /* LINUX_VERSION > VERSION(3,2,0) || WL_COMPAT_WIRELESS */
4958*4882a593Smuzhiyun 
check_dev_role_integrity(struct bcm_cfg80211 * cfg,u32 dev_role)4959*4882a593Smuzhiyun static bool check_dev_role_integrity(struct bcm_cfg80211 *cfg, u32 dev_role)
4960*4882a593Smuzhiyun {
4961*4882a593Smuzhiyun #if defined(BCMDONGLEHOST)
4962*4882a593Smuzhiyun 	dhd_pub_t *dhd = (dhd_pub_t *)(cfg->pub);
4963*4882a593Smuzhiyun 	if (((dev_role == NL80211_IFTYPE_AP) &&
4964*4882a593Smuzhiyun 		!(dhd->op_mode & DHD_FLAG_HOSTAP_MODE)) ||
4965*4882a593Smuzhiyun 		((dev_role == NL80211_IFTYPE_P2P_GO) &&
4966*4882a593Smuzhiyun 		!(dhd->op_mode & DHD_FLAG_P2P_GO_MODE)))
4967*4882a593Smuzhiyun 	{
4968*4882a593Smuzhiyun 		WL_ERR(("device role select failed role:%d op_mode:%d \n", dev_role, dhd->op_mode));
4969*4882a593Smuzhiyun 		return false;
4970*4882a593Smuzhiyun 	}
4971*4882a593Smuzhiyun #endif /* defined(BCMDONGLEHOST) */
4972*4882a593Smuzhiyun 	return true;
4973*4882a593Smuzhiyun }
4974*4882a593Smuzhiyun 
4975*4882a593Smuzhiyun s32
wl_cfg80211_dfs_ap_move(struct net_device * ndev,char * data,char * command,int total_len)4976*4882a593Smuzhiyun wl_cfg80211_dfs_ap_move(struct net_device *ndev, char *data, char *command, int total_len)
4977*4882a593Smuzhiyun {
4978*4882a593Smuzhiyun 	char ioctl_buf[WLC_IOCTL_SMLEN];
4979*4882a593Smuzhiyun 	int err = 0;
4980*4882a593Smuzhiyun 	uint32 val = 0;
4981*4882a593Smuzhiyun 	chanspec_t chanspec = 0;
4982*4882a593Smuzhiyun 	int abort;
4983*4882a593Smuzhiyun 	int bytes_written = 0;
4984*4882a593Smuzhiyun 	struct wl_dfs_ap_move_status_v2 *status;
4985*4882a593Smuzhiyun 	char chanbuf[CHANSPEC_STR_LEN];
4986*4882a593Smuzhiyun 	const char *dfs_state_str[DFS_SCAN_S_MAX] = {
4987*4882a593Smuzhiyun 		"Radar Free On Channel",
4988*4882a593Smuzhiyun 		"Radar Found On Channel",
4989*4882a593Smuzhiyun 		"Radar Scan In Progress",
4990*4882a593Smuzhiyun 		"Radar Scan Aborted",
4991*4882a593Smuzhiyun 		"RSDB Mode switch in Progress For Scan"
4992*4882a593Smuzhiyun 	};
4993*4882a593Smuzhiyun 	if (ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP) {
4994*4882a593Smuzhiyun 		bytes_written = snprintf(command, total_len, "AP is not up\n");
4995*4882a593Smuzhiyun 		return bytes_written;
4996*4882a593Smuzhiyun 	}
4997*4882a593Smuzhiyun 	if (!*data) {
4998*4882a593Smuzhiyun 		if ((err = wldev_iovar_getbuf(ndev, "dfs_ap_move", NULL, 0,
4999*4882a593Smuzhiyun 				ioctl_buf, sizeof(ioctl_buf), NULL))) {
5000*4882a593Smuzhiyun 			WL_ERR(("setting dfs_ap_move failed with err=%d \n", err));
5001*4882a593Smuzhiyun 			return err;
5002*4882a593Smuzhiyun 		}
5003*4882a593Smuzhiyun 		status = (struct wl_dfs_ap_move_status_v2 *)ioctl_buf;
5004*4882a593Smuzhiyun 
5005*4882a593Smuzhiyun 		if (status->version != WL_DFS_AP_MOVE_VERSION) {
5006*4882a593Smuzhiyun 			err = BCME_UNSUPPORTED;
5007*4882a593Smuzhiyun 			WL_ERR(("err=%d version=%d\n", err, status->version));
5008*4882a593Smuzhiyun 			return err;
5009*4882a593Smuzhiyun 		}
5010*4882a593Smuzhiyun 
5011*4882a593Smuzhiyun 		if (status->move_status != (int8) DFS_SCAN_S_IDLE) {
5012*4882a593Smuzhiyun 			chanspec = wl_chspec_driver_to_host(status->chanspec);
5013*4882a593Smuzhiyun 			if (chanspec != 0 && chanspec != INVCHANSPEC) {
5014*4882a593Smuzhiyun 				wf_chspec_ntoa(chanspec, chanbuf);
5015*4882a593Smuzhiyun 				bytes_written = snprintf(command, total_len,
5016*4882a593Smuzhiyun 					"AP Target Chanspec %s (0x%x)\n", chanbuf, chanspec);
5017*4882a593Smuzhiyun 			}
5018*4882a593Smuzhiyun 			bytes_written += snprintf(command + bytes_written,
5019*4882a593Smuzhiyun 					total_len - bytes_written,
5020*4882a593Smuzhiyun 					"%s\n", dfs_state_str[status->move_status]);
5021*4882a593Smuzhiyun 			return bytes_written;
5022*4882a593Smuzhiyun 		} else {
5023*4882a593Smuzhiyun 			bytes_written = snprintf(command, total_len, "dfs AP move in IDLE state\n");
5024*4882a593Smuzhiyun 			return bytes_written;
5025*4882a593Smuzhiyun 		}
5026*4882a593Smuzhiyun 	}
5027*4882a593Smuzhiyun 
5028*4882a593Smuzhiyun 	abort = bcm_atoi(data);
5029*4882a593Smuzhiyun 	if (abort == -1) {
5030*4882a593Smuzhiyun 		if ((err = wldev_iovar_setbuf(ndev, "dfs_ap_move", &abort,
5031*4882a593Smuzhiyun 				sizeof(int), ioctl_buf, sizeof(ioctl_buf), NULL)) < 0) {
5032*4882a593Smuzhiyun 			WL_ERR(("seting dfs_ap_move failed with err %d\n", err));
5033*4882a593Smuzhiyun 			return err;
5034*4882a593Smuzhiyun 		}
5035*4882a593Smuzhiyun 	} else {
5036*4882a593Smuzhiyun 		chanspec = wf_chspec_aton(data);
5037*4882a593Smuzhiyun 		if (chanspec != 0) {
5038*4882a593Smuzhiyun 			val = wl_chspec_host_to_driver(chanspec);
5039*4882a593Smuzhiyun 			if (val != INVCHANSPEC) {
5040*4882a593Smuzhiyun 				if ((err = wldev_iovar_setbuf(ndev, "dfs_ap_move", &val,
5041*4882a593Smuzhiyun 					sizeof(int), ioctl_buf, sizeof(ioctl_buf), NULL)) < 0) {
5042*4882a593Smuzhiyun 					WL_ERR(("seting dfs_ap_move failed with err %d\n", err));
5043*4882a593Smuzhiyun 					return err;
5044*4882a593Smuzhiyun 				}
5045*4882a593Smuzhiyun 				WL_DBG((" set dfs_ap_move successfull"));
5046*4882a593Smuzhiyun 			} else {
5047*4882a593Smuzhiyun 				err = BCME_USAGE_ERROR;
5048*4882a593Smuzhiyun 			}
5049*4882a593Smuzhiyun 		}
5050*4882a593Smuzhiyun 	}
5051*4882a593Smuzhiyun 	return err;
5052*4882a593Smuzhiyun }
5053*4882a593Smuzhiyun 
5054*4882a593Smuzhiyun #ifdef WL_CFG80211_ACL
5055*4882a593Smuzhiyun static int
wl_cfg80211_set_mac_acl(struct wiphy * wiphy,struct net_device * cfgdev,const struct cfg80211_acl_data * acl)5056*4882a593Smuzhiyun wl_cfg80211_set_mac_acl(struct wiphy *wiphy, struct net_device *cfgdev,
5057*4882a593Smuzhiyun 	const struct cfg80211_acl_data *acl)
5058*4882a593Smuzhiyun {
5059*4882a593Smuzhiyun 	int i;
5060*4882a593Smuzhiyun 	int ret = 0;
5061*4882a593Smuzhiyun 	int macnum = 0;
5062*4882a593Smuzhiyun 	int macmode = MACLIST_MODE_DISABLED;
5063*4882a593Smuzhiyun 	struct maclist *list;
5064*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(cfgdev);
5065*4882a593Smuzhiyun 
5066*4882a593Smuzhiyun 	/* get the MAC filter mode */
5067*4882a593Smuzhiyun 	if (acl && acl->acl_policy == NL80211_ACL_POLICY_DENY_UNLESS_LISTED) {
5068*4882a593Smuzhiyun 		macmode = MACLIST_MODE_ALLOW;
5069*4882a593Smuzhiyun 	} else if (acl && acl->acl_policy == NL80211_ACL_POLICY_ACCEPT_UNLESS_LISTED &&
5070*4882a593Smuzhiyun 	acl->n_acl_entries) {
5071*4882a593Smuzhiyun 		macmode = MACLIST_MODE_DENY;
5072*4882a593Smuzhiyun 	}
5073*4882a593Smuzhiyun 
5074*4882a593Smuzhiyun 	/* if acl == NULL, macmode is still disabled.. */
5075*4882a593Smuzhiyun 	if (macmode == MACLIST_MODE_DISABLED) {
5076*4882a593Smuzhiyun 		if ((ret = wl_android_set_ap_mac_list(cfgdev, macmode, NULL)) != 0)
5077*4882a593Smuzhiyun 			WL_ERR(("wl_cfg80211_set_mac_acl: Setting MAC list"
5078*4882a593Smuzhiyun 				" failed error=%d\n", ret));
5079*4882a593Smuzhiyun 
5080*4882a593Smuzhiyun 		return ret;
5081*4882a593Smuzhiyun 	}
5082*4882a593Smuzhiyun 
5083*4882a593Smuzhiyun 	macnum = acl->n_acl_entries;
5084*4882a593Smuzhiyun 	if (macnum < 0 || macnum > MAX_NUM_MAC_FILT) {
5085*4882a593Smuzhiyun 		WL_ERR(("wl_cfg80211_set_mac_acl: invalid number of MAC address entries %d\n",
5086*4882a593Smuzhiyun 			macnum));
5087*4882a593Smuzhiyun 		return -1;
5088*4882a593Smuzhiyun 	}
5089*4882a593Smuzhiyun 
5090*4882a593Smuzhiyun 	/* allocate memory for the MAC list */
5091*4882a593Smuzhiyun 	list = (struct maclist *)MALLOC(cfg->osh, sizeof(int) +
5092*4882a593Smuzhiyun 		sizeof(struct ether_addr) * macnum);
5093*4882a593Smuzhiyun 	if (!list) {
5094*4882a593Smuzhiyun 		WL_ERR(("wl_cfg80211_set_mac_acl: failed to allocate memory\n"));
5095*4882a593Smuzhiyun 		return -1;
5096*4882a593Smuzhiyun 	}
5097*4882a593Smuzhiyun 
5098*4882a593Smuzhiyun 	/* prepare the MAC list */
5099*4882a593Smuzhiyun 	list->count = htod32(macnum);
5100*4882a593Smuzhiyun 	for (i = 0; i < macnum; i++) {
5101*4882a593Smuzhiyun 		memcpy(&list->ea[i], &acl->mac_addrs[i], ETHER_ADDR_LEN);
5102*4882a593Smuzhiyun 	}
5103*4882a593Smuzhiyun 	/* set the list */
5104*4882a593Smuzhiyun 	if ((ret = wl_android_set_ap_mac_list(cfgdev, macmode, list)) != 0)
5105*4882a593Smuzhiyun 		WL_ERR(("wl_cfg80211_set_mac_acl: Setting MAC list failed error=%d\n", ret));
5106*4882a593Smuzhiyun 
5107*4882a593Smuzhiyun 	MFREE(cfg->osh, list, sizeof(int) +
5108*4882a593Smuzhiyun 		sizeof(struct ether_addr) * macnum);
5109*4882a593Smuzhiyun 
5110*4882a593Smuzhiyun 	return ret;
5111*4882a593Smuzhiyun }
5112*4882a593Smuzhiyun #endif /* WL_CFG80211_ACL */
5113*4882a593Smuzhiyun 
wl_chspec_chandef(chanspec_t chanspec,struct cfg80211_chan_def * chandef,struct wiphy * wiphy)5114*4882a593Smuzhiyun int wl_chspec_chandef(chanspec_t chanspec,
5115*4882a593Smuzhiyun 	struct cfg80211_chan_def *chandef, struct wiphy *wiphy)
5116*4882a593Smuzhiyun {
5117*4882a593Smuzhiyun 	uint16 freq = 0;
5118*4882a593Smuzhiyun 	struct ieee80211_channel *chan;
5119*4882a593Smuzhiyun 
5120*4882a593Smuzhiyun 	if (!chandef) {
5121*4882a593Smuzhiyun 		return -1;
5122*4882a593Smuzhiyun 	} else {
5123*4882a593Smuzhiyun 		memset(chandef, 0, sizeof(*chandef));
5124*4882a593Smuzhiyun 	}
5125*4882a593Smuzhiyun 
5126*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 8, 0))
5127*4882a593Smuzhiyun 	chandef->center_freq1 =  wl_channel_to_frequency(CHSPEC_CHANNEL(chanspec), CHSPEC_BAND(chanspec));
5128*4882a593Smuzhiyun 	freq = wl_channel_to_frequency(wf_chspec_primary20_chan(chanspec), CHSPEC_BAND(chanspec));
5129*4882a593Smuzhiyun 	chandef->chan = ieee80211_get_channel(wiphy, freq);
5130*4882a593Smuzhiyun 	chandef->center_freq2 = 0;
5131*4882a593Smuzhiyun 
5132*4882a593Smuzhiyun 	switch (CHSPEC_BW(chanspec)) {
5133*4882a593Smuzhiyun 		case WL_CHANSPEC_BW_20:
5134*4882a593Smuzhiyun 			chandef->width = NL80211_CHAN_WIDTH_20;
5135*4882a593Smuzhiyun 			break;
5136*4882a593Smuzhiyun 
5137*4882a593Smuzhiyun 		case WL_CHANSPEC_BW_40:
5138*4882a593Smuzhiyun 			chandef->width = NL80211_CHAN_WIDTH_40;
5139*4882a593Smuzhiyun 			break;
5140*4882a593Smuzhiyun 
5141*4882a593Smuzhiyun 		case WL_CHANSPEC_BW_80:
5142*4882a593Smuzhiyun 			chandef->width = NL80211_CHAN_WIDTH_80;
5143*4882a593Smuzhiyun 			break;
5144*4882a593Smuzhiyun 
5145*4882a593Smuzhiyun 		case WL_CHANSPEC_BW_8080:
5146*4882a593Smuzhiyun 		{
5147*4882a593Smuzhiyun 			/* XXX Left as is but need proper calculation for center_freq2 is used */
5148*4882a593Smuzhiyun 			int chan_type = 0;
5149*4882a593Smuzhiyun 			int channel = 0;
5150*4882a593Smuzhiyun 			uint16 sb = CHSPEC_CTL_SB(chanspec);
5151*4882a593Smuzhiyun 
5152*4882a593Smuzhiyun 			if (sb == WL_CHANSPEC_CTL_SB_LL) {
5153*4882a593Smuzhiyun 				channel -= (CH_10MHZ_APART + CH_20MHZ_APART);
5154*4882a593Smuzhiyun 			} else if (sb == WL_CHANSPEC_CTL_SB_LU) {
5155*4882a593Smuzhiyun 				channel -= CH_10MHZ_APART;
5156*4882a593Smuzhiyun 			} else if (sb == WL_CHANSPEC_CTL_SB_UL) {
5157*4882a593Smuzhiyun 				channel += CH_10MHZ_APART;
5158*4882a593Smuzhiyun 			} else {
5159*4882a593Smuzhiyun 				/* WL_CHANSPEC_CTL_SB_UU */
5160*4882a593Smuzhiyun 				channel += (CH_10MHZ_APART + CH_20MHZ_APART);
5161*4882a593Smuzhiyun 			}
5162*4882a593Smuzhiyun 
5163*4882a593Smuzhiyun 			if (sb == WL_CHANSPEC_CTL_SB_LL || sb == WL_CHANSPEC_CTL_SB_LU)
5164*4882a593Smuzhiyun 				chan_type = NL80211_CHAN_HT40MINUS;
5165*4882a593Smuzhiyun 			else if (sb == WL_CHANSPEC_CTL_SB_UL || sb == WL_CHANSPEC_CTL_SB_UU)
5166*4882a593Smuzhiyun 				chan_type = NL80211_CHAN_HT40PLUS;
5167*4882a593Smuzhiyun 			freq = wl_channel_to_frequency(channel, CHSPEC_BAND(chanspec));
5168*4882a593Smuzhiyun 			chan = ieee80211_get_channel(wiphy, freq);
5169*4882a593Smuzhiyun 			cfg80211_chandef_create(chandef, chan, chan_type);
5170*4882a593Smuzhiyun 			return 0;
5171*4882a593Smuzhiyun 			break;
5172*4882a593Smuzhiyun 		}
5173*4882a593Smuzhiyun 
5174*4882a593Smuzhiyun 		case WL_CHANSPEC_BW_160:
5175*4882a593Smuzhiyun 			chandef->width = NL80211_CHAN_WIDTH_160;
5176*4882a593Smuzhiyun 			break;
5177*4882a593Smuzhiyun 		default:
5178*4882a593Smuzhiyun 			chandef->width = NL80211_CHAN_WIDTH_20;
5179*4882a593Smuzhiyun 			break;
5180*4882a593Smuzhiyun 	}
5181*4882a593Smuzhiyun 
5182*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 5, 0) && \
5183*4882a593Smuzhiyun       (LINUX_VERSION_CODE <= KERNEL_VERSION (3, 7, 0)))
5184*4882a593Smuzhiyun 
5185*4882a593Smuzhiyun 	int chan_type = 0;
5186*4882a593Smuzhiyun 	int channel = 0;
5187*4882a593Smuzhiyun 	channel = CHSPEC_CHANNEL(chanspec);
5188*4882a593Smuzhiyun 	switch (CHSPEC_BW(chanspec)) {
5189*4882a593Smuzhiyun 		case WL_CHANSPEC_BW_20:
5190*4882a593Smuzhiyun 			chan_type = NL80211_CHAN_HT20;
5191*4882a593Smuzhiyun 			break;
5192*4882a593Smuzhiyun 		case WL_CHANSPEC_BW_40:
5193*4882a593Smuzhiyun 			if (CHSPEC_SB_UPPER(chanspec)) {
5194*4882a593Smuzhiyun 				channel += CH_10MHZ_APART;
5195*4882a593Smuzhiyun 			} else {
5196*4882a593Smuzhiyun 				channel -= CH_10MHZ_APART;
5197*4882a593Smuzhiyun 			}
5198*4882a593Smuzhiyun 			chan_type = NL80211_CHAN_HT40PLUS;
5199*4882a593Smuzhiyun 			break;
5200*4882a593Smuzhiyun 
5201*4882a593Smuzhiyun 		default:
5202*4882a593Smuzhiyun 			chan_type = NL80211_CHAN_HT20;
5203*4882a593Smuzhiyun 			break;
5204*4882a593Smuzhiyun 	}
5205*4882a593Smuzhiyun 
5206*4882a593Smuzhiyun 	freq = wl_channel_to_frequency(channel, CHSPEC_BAND(chanspec));
5207*4882a593Smuzhiyun 	chan = ieee80211_get_channel(wiphy, freq);
5208*4882a593Smuzhiyun 	WL_DBG(("channel:%d freq:%d chan_type: %d chan_ptr:%p \n",
5209*4882a593Smuzhiyun 		channel, freq, chan_type, chan));
5210*4882a593Smuzhiyun 	if (unlikely(!chan)) {
5211*4882a593Smuzhiyun 		/* fw and cfg80211 channel lists are not in sync */
5212*4882a593Smuzhiyun 		WL_ERR(("Couldn't find matching channel in wiphy channel list \n"));
5213*4882a593Smuzhiyun 		ASSERT(0);
5214*4882a593Smuzhiyun 		return -EINVAL;
5215*4882a593Smuzhiyun 	}
5216*4882a593Smuzhiyun 
5217*4882a593Smuzhiyun 	chandef->freq = freq;
5218*4882a593Smuzhiyun 	chandef->chan_type = chan_type;
5219*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 8, 0)) */
5220*4882a593Smuzhiyun 
5221*4882a593Smuzhiyun 	return 0;
5222*4882a593Smuzhiyun }
5223*4882a593Smuzhiyun 
5224*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
5225*4882a593Smuzhiyun void
wl_cfg80211_ch_switch_notify(struct net_device * dev,uint16 chanspec,struct wiphy * wiphy)5226*4882a593Smuzhiyun wl_cfg80211_ch_switch_notify(struct net_device *dev, uint16 chanspec, struct wiphy *wiphy)
5227*4882a593Smuzhiyun {
5228*4882a593Smuzhiyun 	u32 freq;
5229*4882a593Smuzhiyun 	struct cfg80211_chan_def chandef;
5230*4882a593Smuzhiyun 
5231*4882a593Smuzhiyun 	if (!wiphy) {
5232*4882a593Smuzhiyun 		WL_ERR(("wiphy is null\n"));
5233*4882a593Smuzhiyun 		return;
5234*4882a593Smuzhiyun 	}
5235*4882a593Smuzhiyun #if (LINUX_VERSION_CODE <= KERNEL_VERSION (3, 18, 0))
5236*4882a593Smuzhiyun 	/* Channel switch support is only for AP/GO/ADHOC/MESH */
5237*4882a593Smuzhiyun 	if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION ||
5238*4882a593Smuzhiyun 		dev->ieee80211_ptr->iftype == NL80211_IFTYPE_P2P_CLIENT) {
5239*4882a593Smuzhiyun 		WL_ERR(("No channel switch notify support for STA/GC\n"));
5240*4882a593Smuzhiyun 		return;
5241*4882a593Smuzhiyun 	}
5242*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE <= KERNEL_VERSION (3, 18, 0)) */
5243*4882a593Smuzhiyun 
5244*4882a593Smuzhiyun 	if (wl_chspec_chandef(chanspec, &chandef, wiphy)) {
5245*4882a593Smuzhiyun 		WL_ERR(("chspec_chandef failed\n"));
5246*4882a593Smuzhiyun 		return;
5247*4882a593Smuzhiyun 	}
5248*4882a593Smuzhiyun 
5249*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 8, 0))
5250*4882a593Smuzhiyun 	freq = chandef.chan ? chandef.chan->center_freq : chandef.center_freq1;
5251*4882a593Smuzhiyun #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 2) || defined(CFG80211_BKPORT_MLO)
5252*4882a593Smuzhiyun 	cfg80211_ch_switch_notify(dev, &chandef, 0);
5253*4882a593Smuzhiyun #else
5254*4882a593Smuzhiyun 	cfg80211_ch_switch_notify(dev, &chandef);
5255*4882a593Smuzhiyun #endif
5256*4882a593Smuzhiyun #elif (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 5, 0) && (LINUX_VERSION_CODE <= (3, 7, 0)))
5257*4882a593Smuzhiyun 	freq = chandef.freq;
5258*4882a593Smuzhiyun 	cfg80211_ch_switch_notify(dev, freq, chandef.chan_type);
5259*4882a593Smuzhiyun #endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION (3, 8, 0)) */
5260*4882a593Smuzhiyun 
5261*4882a593Smuzhiyun 	WL_MSG(dev->name, "Channel switch notification for freq: %d chanspec: 0x%x\n",
5262*4882a593Smuzhiyun 		freq, chanspec);
5263*4882a593Smuzhiyun #ifdef WL_EXT_IAPSTA
5264*4882a593Smuzhiyun 	wl_ext_fw_reinit_incsa(dev);
5265*4882a593Smuzhiyun #endif
5266*4882a593Smuzhiyun 	return;
5267*4882a593Smuzhiyun }
5268*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE >= (3, 5, 0) */
5269*4882a593Smuzhiyun 
5270*4882a593Smuzhiyun static void
wl_ap_channel_ind(struct bcm_cfg80211 * cfg,struct net_device * ndev,chanspec_t chanspec)5271*4882a593Smuzhiyun wl_ap_channel_ind(struct bcm_cfg80211 *cfg,
5272*4882a593Smuzhiyun 	struct net_device *ndev,
5273*4882a593Smuzhiyun 	chanspec_t chanspec)
5274*4882a593Smuzhiyun {
5275*4882a593Smuzhiyun 	u32 channel = LCHSPEC_CHANNEL(chanspec);
5276*4882a593Smuzhiyun 
5277*4882a593Smuzhiyun 	WL_INFORM_MEM(("(%s) AP channel:%d chspec:0x%x \n",
5278*4882a593Smuzhiyun 		ndev->name, channel, chanspec));
5279*4882a593Smuzhiyun 
5280*4882a593Smuzhiyun #ifdef SUPPORT_AP_BWCTRL
5281*4882a593Smuzhiyun 	wl_update_apchan_bwcap(cfg, ndev, chanspec);
5282*4882a593Smuzhiyun #endif /* SUPPORT_AP_BWCTRL */
5283*4882a593Smuzhiyun 
5284*4882a593Smuzhiyun 	if (!(cfg->ap_oper_channel == INVCHANSPEC) && (cfg->ap_oper_channel != chanspec)) {
5285*4882a593Smuzhiyun 		/*
5286*4882a593Smuzhiyun 		 * If cached channel is different from the channel indicated
5287*4882a593Smuzhiyun 		 * by the event, notify user space about the channel switch.
5288*4882a593Smuzhiyun 		 */
5289*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
5290*4882a593Smuzhiyun 		wl_cfg80211_ch_switch_notify(ndev, chanspec, bcmcfg_to_wiphy(cfg));
5291*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE >= (3, 5, 0) */
5292*4882a593Smuzhiyun 		cfg->ap_oper_channel = chanspec;
5293*4882a593Smuzhiyun 	}
5294*4882a593Smuzhiyun }
5295*4882a593Smuzhiyun 
5296*4882a593Smuzhiyun s32
wl_ap_start_ind(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,const wl_event_msg_t * e,void * data)5297*4882a593Smuzhiyun wl_ap_start_ind(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
5298*4882a593Smuzhiyun const wl_event_msg_t *e, void *data)
5299*4882a593Smuzhiyun {
5300*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
5301*4882a593Smuzhiyun 	chanspec_t chanspec;
5302*4882a593Smuzhiyun 
5303*4882a593Smuzhiyun 	WL_DBG(("Enter\n"));
5304*4882a593Smuzhiyun 	if (unlikely(e->status)) {
5305*4882a593Smuzhiyun 		WL_ERR(("status:0x%x \n", e->status));
5306*4882a593Smuzhiyun 		return -1;
5307*4882a593Smuzhiyun 	}
5308*4882a593Smuzhiyun 
5309*4882a593Smuzhiyun 	if (!data) {
5310*4882a593Smuzhiyun 		return -EINVAL;
5311*4882a593Smuzhiyun 	}
5312*4882a593Smuzhiyun 
5313*4882a593Smuzhiyun 	if (likely(cfgdev)) {
5314*4882a593Smuzhiyun 		ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);
5315*4882a593Smuzhiyun 		chanspec = *((chanspec_t *)data);
5316*4882a593Smuzhiyun 
5317*4882a593Smuzhiyun 		if (wl_get_mode_by_netdev(cfg, ndev) == WL_MODE_AP) {
5318*4882a593Smuzhiyun 			/* For AP/GO role */
5319*4882a593Smuzhiyun 			wl_ap_channel_ind(cfg, ndev, chanspec);
5320*4882a593Smuzhiyun 		}
5321*4882a593Smuzhiyun 	}
5322*4882a593Smuzhiyun 
5323*4882a593Smuzhiyun 	return 0;
5324*4882a593Smuzhiyun }
5325*4882a593Smuzhiyun 
5326*4882a593Smuzhiyun s32
wl_csa_complete_ind(struct bcm_cfg80211 * cfg,bcm_struct_cfgdev * cfgdev,const wl_event_msg_t * e,void * data)5327*4882a593Smuzhiyun wl_csa_complete_ind(struct bcm_cfg80211 *cfg, bcm_struct_cfgdev *cfgdev,
5328*4882a593Smuzhiyun const wl_event_msg_t *e, void *data)
5329*4882a593Smuzhiyun {
5330*4882a593Smuzhiyun 	int error = 0;
5331*4882a593Smuzhiyun 	u32 chanspec = 0;
5332*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
5333*4882a593Smuzhiyun 	struct ether_addr bssid;
5334*4882a593Smuzhiyun 
5335*4882a593Smuzhiyun 	WL_DBG(("Enter\n"));
5336*4882a593Smuzhiyun 	if (unlikely(e->status)) {
5337*4882a593Smuzhiyun 		WL_ERR(("status:0x%x \n", e->status));
5338*4882a593Smuzhiyun 		return -1;
5339*4882a593Smuzhiyun 	}
5340*4882a593Smuzhiyun 
5341*4882a593Smuzhiyun 	if (likely(cfgdev)) {
5342*4882a593Smuzhiyun 		ndev = cfgdev_to_wlc_ndev(cfgdev, cfg);
5343*4882a593Smuzhiyun 		/* Get association state if not AP and then query chanspec */
5344*4882a593Smuzhiyun 		if (!((wl_get_mode_by_netdev(cfg, ndev)) == WL_MODE_AP)) {
5345*4882a593Smuzhiyun 			error = wldev_ioctl_get(ndev, WLC_GET_BSSID, &bssid, ETHER_ADDR_LEN);
5346*4882a593Smuzhiyun 			if (error) {
5347*4882a593Smuzhiyun 				WL_ERR(("CSA on %s. Not associated. error=%d\n",
5348*4882a593Smuzhiyun 					ndev->name, error));
5349*4882a593Smuzhiyun 				return BCME_ERROR;
5350*4882a593Smuzhiyun 			}
5351*4882a593Smuzhiyun 		}
5352*4882a593Smuzhiyun 
5353*4882a593Smuzhiyun 		error = wldev_iovar_getint(ndev, "chanspec", &chanspec);
5354*4882a593Smuzhiyun 		if (unlikely(error)) {
5355*4882a593Smuzhiyun 			WL_ERR(("Get chanspec error: %d \n", error));
5356*4882a593Smuzhiyun 			return -1;
5357*4882a593Smuzhiyun 		}
5358*4882a593Smuzhiyun 
5359*4882a593Smuzhiyun 		WL_INFORM_MEM(("[%s] CSA ind. ch:0x%x\n", ndev->name, chanspec));
5360*4882a593Smuzhiyun 		if (wl_get_mode_by_netdev(cfg, ndev) == WL_MODE_AP) {
5361*4882a593Smuzhiyun 			/* For AP/GO role */
5362*4882a593Smuzhiyun 			wl_ap_channel_ind(cfg, ndev, chanspec);
5363*4882a593Smuzhiyun 		} else {
5364*4882a593Smuzhiyun 			/* STA/GC roles */
5365*4882a593Smuzhiyun 			if (!wl_get_drv_status(cfg, CONNECTED, ndev)) {
5366*4882a593Smuzhiyun 				WL_ERR(("CSA on %s. Not associated.\n", ndev->name));
5367*4882a593Smuzhiyun 				return BCME_ERROR;
5368*4882a593Smuzhiyun 			}
5369*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0))
5370*4882a593Smuzhiyun 			wl_cfg80211_ch_switch_notify(ndev, chanspec, bcmcfg_to_wiphy(cfg));
5371*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE >= (3, 5, 0) */
5372*4882a593Smuzhiyun 		}
5373*4882a593Smuzhiyun 
5374*4882a593Smuzhiyun 	}
5375*4882a593Smuzhiyun 
5376*4882a593Smuzhiyun 	return 0;
5377*4882a593Smuzhiyun }
5378*4882a593Smuzhiyun 
5379*4882a593Smuzhiyun #ifdef WLTDLS
5380*4882a593Smuzhiyun s32
wl_cfg80211_tdls_config(struct bcm_cfg80211 * cfg,enum wl_tdls_config state,bool auto_mode)5381*4882a593Smuzhiyun wl_cfg80211_tdls_config(struct bcm_cfg80211 *cfg, enum wl_tdls_config state, bool auto_mode)
5382*4882a593Smuzhiyun {
5383*4882a593Smuzhiyun 	struct net_device *ndev = bcmcfg_to_prmry_ndev(cfg);
5384*4882a593Smuzhiyun 	int err = 0;
5385*4882a593Smuzhiyun 	struct net_info *iter, *next;
5386*4882a593Smuzhiyun 	int update_reqd = 0;
5387*4882a593Smuzhiyun 	int enable = 0;
5388*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
5389*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
5390*4882a593Smuzhiyun 
5391*4882a593Smuzhiyun 	/*
5392*4882a593Smuzhiyun 	 * TDLS need to be enabled only if we have a single STA/GC
5393*4882a593Smuzhiyun 	 * connection.
5394*4882a593Smuzhiyun 	 */
5395*4882a593Smuzhiyun 
5396*4882a593Smuzhiyun 	WL_DBG(("Enter state:%d\n", state));
5397*4882a593Smuzhiyun 	if (!cfg->tdls_supported) {
5398*4882a593Smuzhiyun 		/* FW doesn't support tdls. Do nothing */
5399*4882a593Smuzhiyun 		return -ENODEV;
5400*4882a593Smuzhiyun 	}
5401*4882a593Smuzhiyun 
5402*4882a593Smuzhiyun 	/* Protect tdls config session */
5403*4882a593Smuzhiyun 	mutex_lock(&cfg->tdls_sync);
5404*4882a593Smuzhiyun 
5405*4882a593Smuzhiyun 	if (state == TDLS_STATE_TEARDOWN) {
5406*4882a593Smuzhiyun 		/* Host initiated TDLS tear down */
5407*4882a593Smuzhiyun 		err = dhd_tdls_enable(ndev, false, auto_mode, NULL);
5408*4882a593Smuzhiyun 		goto exit;
5409*4882a593Smuzhiyun 	} else if ((state == TDLS_STATE_AP_CREATE) ||
5410*4882a593Smuzhiyun 		(state == TDLS_STATE_NMI_CREATE)) {
5411*4882a593Smuzhiyun 		/* We don't support tdls while AP/GO/NAN is operational */
5412*4882a593Smuzhiyun 		update_reqd = true;
5413*4882a593Smuzhiyun 		enable = false;
5414*4882a593Smuzhiyun 	} else if ((state == TDLS_STATE_CONNECT) || (state == TDLS_STATE_IF_CREATE)) {
5415*4882a593Smuzhiyun 		if (wl_get_drv_status_all(cfg,
5416*4882a593Smuzhiyun 			CONNECTED) >= TDLS_MAX_IFACE_FOR_ENABLE) {
5417*4882a593Smuzhiyun 			/* For STA/GC connect command request, disable
5418*4882a593Smuzhiyun 			 * tdls if we have any concurrent interfaces
5419*4882a593Smuzhiyun 			 * operational.
5420*4882a593Smuzhiyun 			 */
5421*4882a593Smuzhiyun 			WL_DBG(("Interface limit restriction. disable tdls.\n"));
5422*4882a593Smuzhiyun 			update_reqd = true;
5423*4882a593Smuzhiyun 			enable = false;
5424*4882a593Smuzhiyun 		}
5425*4882a593Smuzhiyun 	} else if ((state == TDLS_STATE_DISCONNECT) ||
5426*4882a593Smuzhiyun 		(state == TDLS_STATE_AP_DELETE) ||
5427*4882a593Smuzhiyun 		(state == TDLS_STATE_SETUP) ||
5428*4882a593Smuzhiyun 		(state == TDLS_STATE_IF_DELETE)) {
5429*4882a593Smuzhiyun 		/* Enable back the tdls connection only if we have less than
5430*4882a593Smuzhiyun 		 * or equal to a single STA/GC connection.
5431*4882a593Smuzhiyun 		 */
5432*4882a593Smuzhiyun 		if (wl_get_drv_status_all(cfg,
5433*4882a593Smuzhiyun 			CONNECTED) == 0) {
5434*4882a593Smuzhiyun 			/* If there are no interfaces connected, enable tdls */
5435*4882a593Smuzhiyun 			update_reqd = true;
5436*4882a593Smuzhiyun 			enable = true;
5437*4882a593Smuzhiyun 		} else if (wl_get_drv_status_all(cfg,
5438*4882a593Smuzhiyun 			CONNECTED) == TDLS_MAX_IFACE_FOR_ENABLE) {
5439*4882a593Smuzhiyun 			/* We have one interface in CONNECTED state.
5440*4882a593Smuzhiyun 			 * Verify whether its a STA interface before
5441*4882a593Smuzhiyun 			 * we enable back tdls.
5442*4882a593Smuzhiyun 			 */
5443*4882a593Smuzhiyun 			GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
5444*4882a593Smuzhiyun 			for_each_ndev(cfg, iter, next) {
5445*4882a593Smuzhiyun 				GCC_DIAGNOSTIC_POP();
5446*4882a593Smuzhiyun 				if ((iter->ndev) && (wl_get_drv_status(cfg, CONNECTED, ndev)) &&
5447*4882a593Smuzhiyun 					(ndev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)) {
5448*4882a593Smuzhiyun 					WL_DBG(("Non STA iface operational. cfg_iftype:%d"
5449*4882a593Smuzhiyun 						" Can't enable tdls.\n",
5450*4882a593Smuzhiyun 						ndev->ieee80211_ptr->iftype));
5451*4882a593Smuzhiyun 					err = -ENOTSUPP;
5452*4882a593Smuzhiyun 					goto exit;
5453*4882a593Smuzhiyun 				}
5454*4882a593Smuzhiyun 			}
5455*4882a593Smuzhiyun 			/* No AP/GO found. Enable back tdls */
5456*4882a593Smuzhiyun 			update_reqd = true;
5457*4882a593Smuzhiyun 			enable = true;
5458*4882a593Smuzhiyun 		} else {
5459*4882a593Smuzhiyun 			WL_DBG(("Concurrent connection mode. Can't enable tdls. \n"));
5460*4882a593Smuzhiyun 			err = -ENOTSUPP;
5461*4882a593Smuzhiyun 			goto exit;
5462*4882a593Smuzhiyun 		}
5463*4882a593Smuzhiyun 	} else {
5464*4882a593Smuzhiyun 		WL_ERR(("Unknown tdls state:%d \n", state));
5465*4882a593Smuzhiyun 		err = -EINVAL;
5466*4882a593Smuzhiyun 		goto exit;
5467*4882a593Smuzhiyun 	}
5468*4882a593Smuzhiyun 
5469*4882a593Smuzhiyun 	if (update_reqd == true) {
5470*4882a593Smuzhiyun 		if (dhdp->tdls_enable == enable) {
5471*4882a593Smuzhiyun 			WL_DBG(("No change in tdls state. Do nothing."
5472*4882a593Smuzhiyun 				" tdls_enable:%d\n", enable));
5473*4882a593Smuzhiyun 			goto exit;
5474*4882a593Smuzhiyun 		}
5475*4882a593Smuzhiyun 		err = wldev_iovar_setint(ndev, "tdls_enable", enable);
5476*4882a593Smuzhiyun 		if (unlikely(err)) {
5477*4882a593Smuzhiyun 			WL_ERR(("tdls_enable setting failed. err:%d\n", err));
5478*4882a593Smuzhiyun 			goto exit;
5479*4882a593Smuzhiyun 		} else {
5480*4882a593Smuzhiyun 			WL_INFORM_MEM(("tdls_enable %d state:%d\n", enable, state));
5481*4882a593Smuzhiyun 			/* Update the dhd state variable to be in sync */
5482*4882a593Smuzhiyun 			dhdp->tdls_enable = enable;
5483*4882a593Smuzhiyun 			if (state == TDLS_STATE_SETUP) {
5484*4882a593Smuzhiyun 				/* For host initiated setup, apply TDLS params
5485*4882a593Smuzhiyun 				 * Don't propagate errors up for param config
5486*4882a593Smuzhiyun 				 * failures
5487*4882a593Smuzhiyun 				 */
5488*4882a593Smuzhiyun 				dhd_tdls_enable(ndev, true, auto_mode, NULL);
5489*4882a593Smuzhiyun 
5490*4882a593Smuzhiyun 			}
5491*4882a593Smuzhiyun 		}
5492*4882a593Smuzhiyun 	} else {
5493*4882a593Smuzhiyun 		WL_DBG(("Skip tdls config. state:%d update_reqd:%d "
5494*4882a593Smuzhiyun 			"current_status:%d \n",
5495*4882a593Smuzhiyun 			state, update_reqd, dhdp->tdls_enable));
5496*4882a593Smuzhiyun 	}
5497*4882a593Smuzhiyun 
5498*4882a593Smuzhiyun exit:
5499*4882a593Smuzhiyun 	if (err) {
5500*4882a593Smuzhiyun 		wl_flush_fw_log_buffer(ndev, FW_LOGSET_MASK_ALL);
5501*4882a593Smuzhiyun 	}
5502*4882a593Smuzhiyun 	mutex_unlock(&cfg->tdls_sync);
5503*4882a593Smuzhiyun 	return err;
5504*4882a593Smuzhiyun }
5505*4882a593Smuzhiyun #endif /* WLTDLS */
5506*4882a593Smuzhiyun 
wl_get_ap_netdev(struct bcm_cfg80211 * cfg,char * ifname)5507*4882a593Smuzhiyun struct net_device* wl_get_ap_netdev(struct bcm_cfg80211 *cfg, char *ifname)
5508*4882a593Smuzhiyun {
5509*4882a593Smuzhiyun 	struct net_info *iter, *next;
5510*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
5511*4882a593Smuzhiyun 
5512*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
5513*4882a593Smuzhiyun 	for_each_ndev(cfg, iter, next) {
5514*4882a593Smuzhiyun 		GCC_DIAGNOSTIC_POP();
5515*4882a593Smuzhiyun 		if (iter->ndev) {
5516*4882a593Smuzhiyun 			if (strncmp(iter->ndev->name, ifname, IFNAMSIZ) == 0) {
5517*4882a593Smuzhiyun 				if (iter->ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) {
5518*4882a593Smuzhiyun 					ndev = iter->ndev;
5519*4882a593Smuzhiyun 					break;
5520*4882a593Smuzhiyun 				}
5521*4882a593Smuzhiyun 			}
5522*4882a593Smuzhiyun 		}
5523*4882a593Smuzhiyun 	}
5524*4882a593Smuzhiyun 
5525*4882a593Smuzhiyun 	return ndev;
5526*4882a593Smuzhiyun }
5527*4882a593Smuzhiyun 
5528*4882a593Smuzhiyun #ifdef SUPPORT_AP_HIGHER_BEACONRATE
5529*4882a593Smuzhiyun #define WLC_RATE_FLAG	0x80
5530*4882a593Smuzhiyun #define RATE_MASK		0x7f
5531*4882a593Smuzhiyun 
wl_set_ap_beacon_rate(struct net_device * dev,int val,char * ifname)5532*4882a593Smuzhiyun int wl_set_ap_beacon_rate(struct net_device *dev, int val, char *ifname)
5533*4882a593Smuzhiyun {
5534*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
5535*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
5536*4882a593Smuzhiyun 	wl_rateset_args_t rs;
5537*4882a593Smuzhiyun 	int error = BCME_ERROR, i;
5538*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
5539*4882a593Smuzhiyun 
5540*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
5541*4882a593Smuzhiyun 
5542*4882a593Smuzhiyun 	if (dhdp && !(dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) {
5543*4882a593Smuzhiyun 		WL_ERR(("Not Hostapd mode\n"));
5544*4882a593Smuzhiyun 		return BCME_NOTAP;
5545*4882a593Smuzhiyun 	}
5546*4882a593Smuzhiyun 
5547*4882a593Smuzhiyun 	ndev = wl_get_ap_netdev(cfg, ifname);
5548*4882a593Smuzhiyun 
5549*4882a593Smuzhiyun 	if (ndev == NULL) {
5550*4882a593Smuzhiyun 		WL_ERR(("No softAP interface named %s\n", ifname));
5551*4882a593Smuzhiyun 		return BCME_NOTAP;
5552*4882a593Smuzhiyun 	}
5553*4882a593Smuzhiyun 
5554*4882a593Smuzhiyun 	bzero(&rs, sizeof(wl_rateset_args_t));
5555*4882a593Smuzhiyun 	error = wldev_iovar_getbuf(ndev, "rateset", NULL, 0,
5556*4882a593Smuzhiyun 		&rs, sizeof(wl_rateset_args_t), NULL);
5557*4882a593Smuzhiyun 	if (error < 0) {
5558*4882a593Smuzhiyun 		WL_ERR(("get rateset failed = %d\n", error));
5559*4882a593Smuzhiyun 		return error;
5560*4882a593Smuzhiyun 	}
5561*4882a593Smuzhiyun 
5562*4882a593Smuzhiyun 	if (rs.count < 1) {
5563*4882a593Smuzhiyun 		WL_ERR(("Failed to get rate count\n"));
5564*4882a593Smuzhiyun 		return BCME_ERROR;
5565*4882a593Smuzhiyun 	}
5566*4882a593Smuzhiyun 
5567*4882a593Smuzhiyun 	/* Host delivers target rate in the unit of 500kbps */
5568*4882a593Smuzhiyun 	/* To make it to 1mbps unit, atof should be implemented for 5.5mbps basic rate */
5569*4882a593Smuzhiyun 	for (i = 0; i < rs.count && i < WL_NUMRATES; i++)
5570*4882a593Smuzhiyun 		if (rs.rates[i] & WLC_RATE_FLAG)
5571*4882a593Smuzhiyun 			if ((rs.rates[i] & RATE_MASK) == val)
5572*4882a593Smuzhiyun 				break;
5573*4882a593Smuzhiyun 
5574*4882a593Smuzhiyun 	/* Valid rate has been delivered as an argument */
5575*4882a593Smuzhiyun 	if (i < rs.count && i < WL_NUMRATES) {
5576*4882a593Smuzhiyun 		error = wldev_iovar_setint(ndev, "force_bcn_rspec", val);
5577*4882a593Smuzhiyun 		if (error < 0) {
5578*4882a593Smuzhiyun 			WL_ERR(("set beacon rate failed = %d\n", error));
5579*4882a593Smuzhiyun 			return BCME_ERROR;
5580*4882a593Smuzhiyun 		}
5581*4882a593Smuzhiyun 	} else {
5582*4882a593Smuzhiyun 		WL_ERR(("Rate is invalid"));
5583*4882a593Smuzhiyun 		return BCME_BADARG;
5584*4882a593Smuzhiyun 	}
5585*4882a593Smuzhiyun 
5586*4882a593Smuzhiyun 	return BCME_OK;
5587*4882a593Smuzhiyun }
5588*4882a593Smuzhiyun 
5589*4882a593Smuzhiyun int
wl_get_ap_basic_rate(struct net_device * dev,char * command,char * ifname,int total_len)5590*4882a593Smuzhiyun wl_get_ap_basic_rate(struct net_device *dev, char* command, char *ifname, int total_len)
5591*4882a593Smuzhiyun {
5592*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
5593*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
5594*4882a593Smuzhiyun 	wl_rateset_args_t rs;
5595*4882a593Smuzhiyun 	int error = BCME_ERROR;
5596*4882a593Smuzhiyun 	int i, bytes_written = 0;
5597*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
5598*4882a593Smuzhiyun 
5599*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
5600*4882a593Smuzhiyun 
5601*4882a593Smuzhiyun 	if (!(dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) {
5602*4882a593Smuzhiyun 		WL_ERR(("Not Hostapd mode\n"));
5603*4882a593Smuzhiyun 		return BCME_NOTAP;
5604*4882a593Smuzhiyun 	}
5605*4882a593Smuzhiyun 
5606*4882a593Smuzhiyun 	ndev = wl_get_ap_netdev(cfg, ifname);
5607*4882a593Smuzhiyun 
5608*4882a593Smuzhiyun 	if (ndev == NULL) {
5609*4882a593Smuzhiyun 		WL_ERR(("No softAP interface named %s\n", ifname));
5610*4882a593Smuzhiyun 		return BCME_NOTAP;
5611*4882a593Smuzhiyun 	}
5612*4882a593Smuzhiyun 
5613*4882a593Smuzhiyun 	bzero(&rs, sizeof(wl_rateset_args_t));
5614*4882a593Smuzhiyun 	error = wldev_iovar_getbuf(ndev, "rateset", NULL, 0,
5615*4882a593Smuzhiyun 		&rs, sizeof(wl_rateset_args_t), NULL);
5616*4882a593Smuzhiyun 	if (error < 0) {
5617*4882a593Smuzhiyun 		WL_ERR(("get rateset failed = %d\n", error));
5618*4882a593Smuzhiyun 		return error;
5619*4882a593Smuzhiyun 	}
5620*4882a593Smuzhiyun 
5621*4882a593Smuzhiyun 	if (rs.count < 1) {
5622*4882a593Smuzhiyun 		WL_ERR(("Failed to get rate count\n"));
5623*4882a593Smuzhiyun 		return BCME_ERROR;
5624*4882a593Smuzhiyun 	}
5625*4882a593Smuzhiyun 
5626*4882a593Smuzhiyun 	/* Delivers basic rate in the unit of 500kbps to host */
5627*4882a593Smuzhiyun 	for (i = 0; i < rs.count && i < WL_NUMRATES; i++)
5628*4882a593Smuzhiyun 		if (rs.rates[i] & WLC_RATE_FLAG)
5629*4882a593Smuzhiyun 			bytes_written += snprintf(command + bytes_written, total_len,
5630*4882a593Smuzhiyun 							"%d ", rs.rates[i] & RATE_MASK);
5631*4882a593Smuzhiyun 
5632*4882a593Smuzhiyun 	/* Remove last space in the command buffer */
5633*4882a593Smuzhiyun 	if (bytes_written && (bytes_written < total_len)) {
5634*4882a593Smuzhiyun 		command[bytes_written - 1] = '\0';
5635*4882a593Smuzhiyun 		bytes_written--;
5636*4882a593Smuzhiyun 	}
5637*4882a593Smuzhiyun 
5638*4882a593Smuzhiyun 	return bytes_written;
5639*4882a593Smuzhiyun 
5640*4882a593Smuzhiyun }
5641*4882a593Smuzhiyun #endif /* SUPPORT_AP_HIGHER_BEACONRATE */
5642*4882a593Smuzhiyun 
5643*4882a593Smuzhiyun #ifdef SUPPORT_AP_RADIO_PWRSAVE
5644*4882a593Smuzhiyun #define MSEC_PER_MIN	(60000L)
5645*4882a593Smuzhiyun 
5646*4882a593Smuzhiyun static int
_wl_update_ap_rps_params(struct net_device * dev)5647*4882a593Smuzhiyun _wl_update_ap_rps_params(struct net_device *dev)
5648*4882a593Smuzhiyun {
5649*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = NULL;
5650*4882a593Smuzhiyun 	rpsnoa_iovar_params_t iovar;
5651*4882a593Smuzhiyun 	u8 smbuf[WLC_IOCTL_SMLEN];
5652*4882a593Smuzhiyun 
5653*4882a593Smuzhiyun 	if (!dev)
5654*4882a593Smuzhiyun 		return BCME_BADARG;
5655*4882a593Smuzhiyun 
5656*4882a593Smuzhiyun 	cfg = wl_get_cfg(dev);
5657*4882a593Smuzhiyun 
5658*4882a593Smuzhiyun 	bzero(&iovar, sizeof(iovar));
5659*4882a593Smuzhiyun 	bzero(smbuf, sizeof(smbuf));
5660*4882a593Smuzhiyun 
5661*4882a593Smuzhiyun 	iovar.hdr.ver = RADIO_PWRSAVE_VERSION;
5662*4882a593Smuzhiyun 	iovar.hdr.subcmd = WL_RPSNOA_CMD_PARAMS;
5663*4882a593Smuzhiyun 	iovar.hdr.len = sizeof(iovar);
5664*4882a593Smuzhiyun 	iovar.param->band = WLC_BAND_ALL;
5665*4882a593Smuzhiyun 	iovar.param->level = cfg->ap_rps_info.level;
5666*4882a593Smuzhiyun 	iovar.param->stas_assoc_check = cfg->ap_rps_info.sta_assoc_check;
5667*4882a593Smuzhiyun 	iovar.param->pps = cfg->ap_rps_info.pps;
5668*4882a593Smuzhiyun 	iovar.param->quiet_time = cfg->ap_rps_info.quiet_time;
5669*4882a593Smuzhiyun 
5670*4882a593Smuzhiyun 	if (wldev_iovar_setbuf(dev, "rpsnoa", &iovar, sizeof(iovar),
5671*4882a593Smuzhiyun 		smbuf, sizeof(smbuf), NULL)) {
5672*4882a593Smuzhiyun 		WL_ERR(("Failed to set rpsnoa params"));
5673*4882a593Smuzhiyun 		return BCME_ERROR;
5674*4882a593Smuzhiyun 	}
5675*4882a593Smuzhiyun 
5676*4882a593Smuzhiyun 	return BCME_OK;
5677*4882a593Smuzhiyun }
5678*4882a593Smuzhiyun 
5679*4882a593Smuzhiyun int
wl_get_ap_rps(struct net_device * dev,char * command,char * ifname,int total_len)5680*4882a593Smuzhiyun wl_get_ap_rps(struct net_device *dev, char* command, char *ifname, int total_len)
5681*4882a593Smuzhiyun {
5682*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
5683*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
5684*4882a593Smuzhiyun 	int error = BCME_ERROR;
5685*4882a593Smuzhiyun 	int bytes_written = 0;
5686*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
5687*4882a593Smuzhiyun 	rpsnoa_iovar_status_t iovar;
5688*4882a593Smuzhiyun 	u8 smbuf[WLC_IOCTL_SMLEN];
5689*4882a593Smuzhiyun 	u32 chanspec = 0;
5690*4882a593Smuzhiyun 	u8 idx = 0;
5691*4882a593Smuzhiyun 	u16 state;
5692*4882a593Smuzhiyun 	u32 sleep;
5693*4882a593Smuzhiyun 	u32 time_since_enable;
5694*4882a593Smuzhiyun 
5695*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
5696*4882a593Smuzhiyun 
5697*4882a593Smuzhiyun 	if (!dhdp) {
5698*4882a593Smuzhiyun 		error = BCME_NOTUP;
5699*4882a593Smuzhiyun 		goto fail;
5700*4882a593Smuzhiyun 	}
5701*4882a593Smuzhiyun 
5702*4882a593Smuzhiyun 	if (!(dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) {
5703*4882a593Smuzhiyun 		WL_ERR(("Not Hostapd mode\n"));
5704*4882a593Smuzhiyun 		error = BCME_NOTAP;
5705*4882a593Smuzhiyun 		goto fail;
5706*4882a593Smuzhiyun 	}
5707*4882a593Smuzhiyun 
5708*4882a593Smuzhiyun 	ndev = wl_get_ap_netdev(cfg, ifname);
5709*4882a593Smuzhiyun 
5710*4882a593Smuzhiyun 	if (ndev == NULL) {
5711*4882a593Smuzhiyun 		WL_ERR(("No softAP interface named %s\n", ifname));
5712*4882a593Smuzhiyun 		error = BCME_NOTAP;
5713*4882a593Smuzhiyun 		goto fail;
5714*4882a593Smuzhiyun 	}
5715*4882a593Smuzhiyun 
5716*4882a593Smuzhiyun 	bzero(&iovar, sizeof(iovar));
5717*4882a593Smuzhiyun 	bzero(smbuf, sizeof(smbuf));
5718*4882a593Smuzhiyun 
5719*4882a593Smuzhiyun 	iovar.hdr.ver = RADIO_PWRSAVE_VERSION;
5720*4882a593Smuzhiyun 	iovar.hdr.subcmd = WL_RPSNOA_CMD_STATUS;
5721*4882a593Smuzhiyun 	iovar.hdr.len = sizeof(iovar);
5722*4882a593Smuzhiyun 	iovar.stats->band = WLC_BAND_ALL;
5723*4882a593Smuzhiyun 
5724*4882a593Smuzhiyun 	error = wldev_iovar_getbuf(ndev, "rpsnoa", &iovar, sizeof(iovar),
5725*4882a593Smuzhiyun 		smbuf, sizeof(smbuf), NULL);
5726*4882a593Smuzhiyun 	if (error < 0) {
5727*4882a593Smuzhiyun 		WL_ERR(("get ap radio pwrsave failed = %d\n", error));
5728*4882a593Smuzhiyun 		goto fail;
5729*4882a593Smuzhiyun 	}
5730*4882a593Smuzhiyun 
5731*4882a593Smuzhiyun 	/* RSDB event doesn't seem to be handled correctly.
5732*4882a593Smuzhiyun 	 * So check chanspec of AP directly from the firmware
5733*4882a593Smuzhiyun 	 */
5734*4882a593Smuzhiyun 	error = wldev_iovar_getint(ndev, "chanspec", (s32 *)&chanspec);
5735*4882a593Smuzhiyun 	if (error < 0) {
5736*4882a593Smuzhiyun 		WL_ERR(("get chanspec from AP failed = %d\n", error));
5737*4882a593Smuzhiyun 		goto fail;
5738*4882a593Smuzhiyun 	}
5739*4882a593Smuzhiyun 
5740*4882a593Smuzhiyun 	chanspec = wl_chspec_driver_to_host(chanspec);
5741*4882a593Smuzhiyun 	if (CHSPEC_IS2G(chanspec))
5742*4882a593Smuzhiyun 		idx = 0;
5743*4882a593Smuzhiyun 	else if (
5744*4882a593Smuzhiyun #ifdef WL_6G_BAND
5745*4882a593Smuzhiyun 		CHSPEC_IS6G(chanspec) ||
5746*4882a593Smuzhiyun #endif /* WL_6G_BAND */
5747*4882a593Smuzhiyun 		CHSPEC_IS5G(chanspec))
5748*4882a593Smuzhiyun 		idx = 1;
5749*4882a593Smuzhiyun 	else {
5750*4882a593Smuzhiyun 		error = BCME_BADCHAN;
5751*4882a593Smuzhiyun 		goto fail;
5752*4882a593Smuzhiyun 	}
5753*4882a593Smuzhiyun 
5754*4882a593Smuzhiyun 	state = ((rpsnoa_iovar_status_t *)smbuf)->stats[idx].state;
5755*4882a593Smuzhiyun 	sleep = ((rpsnoa_iovar_status_t *)smbuf)->stats[idx].sleep_dur;
5756*4882a593Smuzhiyun 	time_since_enable = ((rpsnoa_iovar_status_t *)smbuf)->stats[idx].sleep_avail_dur;
5757*4882a593Smuzhiyun 
5758*4882a593Smuzhiyun 	/* Conver ms to minute, round down only */
5759*4882a593Smuzhiyun 	sleep = DIV_U64_BY_U32(sleep, MSEC_PER_MIN);
5760*4882a593Smuzhiyun 	time_since_enable = DIV_U64_BY_U32(time_since_enable, MSEC_PER_MIN);
5761*4882a593Smuzhiyun 
5762*4882a593Smuzhiyun 	bytes_written += snprintf(command + bytes_written, total_len,
5763*4882a593Smuzhiyun 		"state=%d sleep=%d time_since_enable=%d", state, sleep, time_since_enable);
5764*4882a593Smuzhiyun 	error = bytes_written;
5765*4882a593Smuzhiyun 
5766*4882a593Smuzhiyun fail:
5767*4882a593Smuzhiyun 	return error;
5768*4882a593Smuzhiyun }
5769*4882a593Smuzhiyun 
5770*4882a593Smuzhiyun int
wl_set_ap_rps(struct net_device * dev,bool enable,char * ifname)5771*4882a593Smuzhiyun wl_set_ap_rps(struct net_device *dev, bool enable, char *ifname)
5772*4882a593Smuzhiyun {
5773*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
5774*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
5775*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
5776*4882a593Smuzhiyun 	rpsnoa_iovar_t iovar;
5777*4882a593Smuzhiyun 	u8 smbuf[WLC_IOCTL_SMLEN];
5778*4882a593Smuzhiyun 	int ret = BCME_OK;
5779*4882a593Smuzhiyun 
5780*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
5781*4882a593Smuzhiyun 
5782*4882a593Smuzhiyun 	if (!dhdp) {
5783*4882a593Smuzhiyun 		ret = BCME_NOTUP;
5784*4882a593Smuzhiyun 		goto exit;
5785*4882a593Smuzhiyun 	}
5786*4882a593Smuzhiyun 
5787*4882a593Smuzhiyun 	if (!(dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) {
5788*4882a593Smuzhiyun 		WL_ERR(("Not Hostapd mode\n"));
5789*4882a593Smuzhiyun 		ret = BCME_NOTAP;
5790*4882a593Smuzhiyun 		goto exit;
5791*4882a593Smuzhiyun 	}
5792*4882a593Smuzhiyun 
5793*4882a593Smuzhiyun 	ndev = wl_get_ap_netdev(cfg, ifname);
5794*4882a593Smuzhiyun 
5795*4882a593Smuzhiyun 	if (ndev == NULL) {
5796*4882a593Smuzhiyun 		WL_ERR(("No softAP interface named %s\n", ifname));
5797*4882a593Smuzhiyun 		ret = BCME_NOTAP;
5798*4882a593Smuzhiyun 		goto exit;
5799*4882a593Smuzhiyun 	}
5800*4882a593Smuzhiyun 
5801*4882a593Smuzhiyun 	if (cfg->ap_rps_info.enable != enable) {
5802*4882a593Smuzhiyun 		cfg->ap_rps_info.enable = enable;
5803*4882a593Smuzhiyun 		if (enable) {
5804*4882a593Smuzhiyun 			ret = _wl_update_ap_rps_params(ndev);
5805*4882a593Smuzhiyun 			if (ret) {
5806*4882a593Smuzhiyun 				WL_ERR(("Filed to update rpsnoa params\n"));
5807*4882a593Smuzhiyun 				goto exit;
5808*4882a593Smuzhiyun 			}
5809*4882a593Smuzhiyun 		}
5810*4882a593Smuzhiyun 		bzero(&iovar, sizeof(iovar));
5811*4882a593Smuzhiyun 		bzero(smbuf, sizeof(smbuf));
5812*4882a593Smuzhiyun 
5813*4882a593Smuzhiyun 		iovar.hdr.ver = RADIO_PWRSAVE_VERSION;
5814*4882a593Smuzhiyun 		iovar.hdr.subcmd = WL_RPSNOA_CMD_ENABLE;
5815*4882a593Smuzhiyun 		iovar.hdr.len = sizeof(iovar);
5816*4882a593Smuzhiyun 		iovar.data->band = WLC_BAND_ALL;
5817*4882a593Smuzhiyun 		iovar.data->value = (int16)enable;
5818*4882a593Smuzhiyun 
5819*4882a593Smuzhiyun 		ret = wldev_iovar_setbuf(ndev, "rpsnoa", &iovar, sizeof(iovar),
5820*4882a593Smuzhiyun 			smbuf, sizeof(smbuf), NULL);
5821*4882a593Smuzhiyun 		if (ret) {
5822*4882a593Smuzhiyun 			WL_ERR(("Failed to enable AP radio power save"));
5823*4882a593Smuzhiyun 			goto exit;
5824*4882a593Smuzhiyun 		}
5825*4882a593Smuzhiyun 		cfg->ap_rps_info.enable = enable;
5826*4882a593Smuzhiyun 	}
5827*4882a593Smuzhiyun exit:
5828*4882a593Smuzhiyun 	return ret;
5829*4882a593Smuzhiyun }
5830*4882a593Smuzhiyun 
5831*4882a593Smuzhiyun int
wl_update_ap_rps_params(struct net_device * dev,ap_rps_info_t * rps,char * ifname)5832*4882a593Smuzhiyun wl_update_ap_rps_params(struct net_device *dev, ap_rps_info_t* rps, char *ifname)
5833*4882a593Smuzhiyun {
5834*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
5835*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
5836*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
5837*4882a593Smuzhiyun 
5838*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
5839*4882a593Smuzhiyun 
5840*4882a593Smuzhiyun 	if (!dhdp)
5841*4882a593Smuzhiyun 		return BCME_NOTUP;
5842*4882a593Smuzhiyun 
5843*4882a593Smuzhiyun 	if (!(dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) {
5844*4882a593Smuzhiyun 		WL_ERR(("Not Hostapd mode\n"));
5845*4882a593Smuzhiyun 		return BCME_NOTAP;
5846*4882a593Smuzhiyun 	}
5847*4882a593Smuzhiyun 
5848*4882a593Smuzhiyun 	ndev = wl_get_ap_netdev(cfg, ifname);
5849*4882a593Smuzhiyun 
5850*4882a593Smuzhiyun 	if (ndev == NULL) {
5851*4882a593Smuzhiyun 		WL_ERR(("No softAP interface named %s\n", ifname));
5852*4882a593Smuzhiyun 		return BCME_NOTAP;
5853*4882a593Smuzhiyun 	}
5854*4882a593Smuzhiyun 
5855*4882a593Smuzhiyun 	if (!rps)
5856*4882a593Smuzhiyun 		return BCME_BADARG;
5857*4882a593Smuzhiyun 
5858*4882a593Smuzhiyun 	if (rps->pps < RADIO_PWRSAVE_PPS_MIN)
5859*4882a593Smuzhiyun 		return BCME_BADARG;
5860*4882a593Smuzhiyun 
5861*4882a593Smuzhiyun 	if (rps->level < RADIO_PWRSAVE_LEVEL_MIN ||
5862*4882a593Smuzhiyun 		rps->level > RADIO_PWRSAVE_LEVEL_MAX)
5863*4882a593Smuzhiyun 		return BCME_BADARG;
5864*4882a593Smuzhiyun 
5865*4882a593Smuzhiyun 	if (rps->quiet_time < RADIO_PWRSAVE_QUIETTIME_MIN)
5866*4882a593Smuzhiyun 		return BCME_BADARG;
5867*4882a593Smuzhiyun 
5868*4882a593Smuzhiyun 	if (rps->sta_assoc_check > RADIO_PWRSAVE_ASSOCCHECK_MAX ||
5869*4882a593Smuzhiyun 		rps->sta_assoc_check < RADIO_PWRSAVE_ASSOCCHECK_MIN)
5870*4882a593Smuzhiyun 		return BCME_BADARG;
5871*4882a593Smuzhiyun 
5872*4882a593Smuzhiyun 	cfg->ap_rps_info.pps = rps->pps;
5873*4882a593Smuzhiyun 	cfg->ap_rps_info.level = rps->level;
5874*4882a593Smuzhiyun 	cfg->ap_rps_info.quiet_time = rps->quiet_time;
5875*4882a593Smuzhiyun 	cfg->ap_rps_info.sta_assoc_check = rps->sta_assoc_check;
5876*4882a593Smuzhiyun 
5877*4882a593Smuzhiyun 	if (cfg->ap_rps_info.enable) {
5878*4882a593Smuzhiyun 		if (_wl_update_ap_rps_params(ndev)) {
5879*4882a593Smuzhiyun 			WL_ERR(("Failed to update rpsnoa params"));
5880*4882a593Smuzhiyun 			return BCME_ERROR;
5881*4882a593Smuzhiyun 		}
5882*4882a593Smuzhiyun 	}
5883*4882a593Smuzhiyun 
5884*4882a593Smuzhiyun 	return BCME_OK;
5885*4882a593Smuzhiyun }
5886*4882a593Smuzhiyun 
5887*4882a593Smuzhiyun void
wl_cfg80211_init_ap_rps(struct bcm_cfg80211 * cfg)5888*4882a593Smuzhiyun wl_cfg80211_init_ap_rps(struct bcm_cfg80211 *cfg)
5889*4882a593Smuzhiyun {
5890*4882a593Smuzhiyun 	cfg->ap_rps_info.enable = FALSE;
5891*4882a593Smuzhiyun 	cfg->ap_rps_info.sta_assoc_check = RADIO_PWRSAVE_STAS_ASSOC_CHECK;
5892*4882a593Smuzhiyun 	cfg->ap_rps_info.pps = RADIO_PWRSAVE_PPS;
5893*4882a593Smuzhiyun 	cfg->ap_rps_info.quiet_time = RADIO_PWRSAVE_QUIET_TIME;
5894*4882a593Smuzhiyun 	cfg->ap_rps_info.level = RADIO_PWRSAVE_LEVEL;
5895*4882a593Smuzhiyun }
5896*4882a593Smuzhiyun #endif /* SUPPORT_AP_RADIO_PWRSAVE */
5897*4882a593Smuzhiyun 
5898*4882a593Smuzhiyun int
wl_cfg80211_iface_count(struct net_device * dev)5899*4882a593Smuzhiyun wl_cfg80211_iface_count(struct net_device *dev)
5900*4882a593Smuzhiyun {
5901*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
5902*4882a593Smuzhiyun 	struct net_info *iter, *next;
5903*4882a593Smuzhiyun 	int iface_count = 0;
5904*4882a593Smuzhiyun 
5905*4882a593Smuzhiyun 	/* Return the count of network interfaces (skip netless p2p discovery
5906*4882a593Smuzhiyun 	 * interface)
5907*4882a593Smuzhiyun 	 */
5908*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
5909*4882a593Smuzhiyun 	for_each_ndev(cfg, iter, next) {
5910*4882a593Smuzhiyun 		GCC_DIAGNOSTIC_POP();
5911*4882a593Smuzhiyun 		if (iter->ndev) {
5912*4882a593Smuzhiyun 			iface_count++;
5913*4882a593Smuzhiyun 		}
5914*4882a593Smuzhiyun 	}
5915*4882a593Smuzhiyun 	return iface_count;
5916*4882a593Smuzhiyun }
5917*4882a593Smuzhiyun 
5918*4882a593Smuzhiyun typedef struct {
5919*4882a593Smuzhiyun 	uint16 id;
5920*4882a593Smuzhiyun 	uint16 len;
5921*4882a593Smuzhiyun 	uint32 val;
5922*4882a593Smuzhiyun } he_xtlv_v32;
5923*4882a593Smuzhiyun 
5924*4882a593Smuzhiyun static bool
wl_he_get_uint_cb(void * ctx,uint16 * id,uint16 * len)5925*4882a593Smuzhiyun wl_he_get_uint_cb(void *ctx, uint16 *id, uint16 *len)
5926*4882a593Smuzhiyun {
5927*4882a593Smuzhiyun 	he_xtlv_v32 *v32 = ctx;
5928*4882a593Smuzhiyun 
5929*4882a593Smuzhiyun 	*id = v32->id;
5930*4882a593Smuzhiyun 	*len = v32->len;
5931*4882a593Smuzhiyun 
5932*4882a593Smuzhiyun 	return FALSE;
5933*4882a593Smuzhiyun }
5934*4882a593Smuzhiyun 
5935*4882a593Smuzhiyun 	static void
wl_he_pack_uint_cb(void * ctx,uint16 id,uint16 len,uint8 * buf)5936*4882a593Smuzhiyun wl_he_pack_uint_cb(void *ctx, uint16 id, uint16 len, uint8 *buf)
5937*4882a593Smuzhiyun {
5938*4882a593Smuzhiyun 	he_xtlv_v32 *v32 = ctx;
5939*4882a593Smuzhiyun 
5940*4882a593Smuzhiyun 	BCM_REFERENCE(id);
5941*4882a593Smuzhiyun 	BCM_REFERENCE(len);
5942*4882a593Smuzhiyun 
5943*4882a593Smuzhiyun 	v32->val = htod32(v32->val);
5944*4882a593Smuzhiyun 
5945*4882a593Smuzhiyun 	switch (v32->len) {
5946*4882a593Smuzhiyun 		case sizeof(uint8):
5947*4882a593Smuzhiyun 			*buf = (uint8)v32->val;
5948*4882a593Smuzhiyun 			break;
5949*4882a593Smuzhiyun 		case sizeof(uint16):
5950*4882a593Smuzhiyun 			store16_ua(buf, (uint16)v32->val);
5951*4882a593Smuzhiyun 			break;
5952*4882a593Smuzhiyun 		case sizeof(uint32):
5953*4882a593Smuzhiyun 			store32_ua(buf, v32->val);
5954*4882a593Smuzhiyun 			break;
5955*4882a593Smuzhiyun 		default:
5956*4882a593Smuzhiyun 			/* ASSERT(0); */
5957*4882a593Smuzhiyun 			break;
5958*4882a593Smuzhiyun 	}
5959*4882a593Smuzhiyun }
5960*4882a593Smuzhiyun 
wl_cfg80211_set_he_mode(struct net_device * dev,struct bcm_cfg80211 * cfg,s32 bssidx,u32 he_flag,bool set)5961*4882a593Smuzhiyun int wl_cfg80211_set_he_mode(struct net_device *dev, struct bcm_cfg80211 *cfg,
5962*4882a593Smuzhiyun 		s32 bssidx, u32 he_flag, bool set)
5963*4882a593Smuzhiyun {
5964*4882a593Smuzhiyun 	bcm_xtlv_t read_he_xtlv;
5965*4882a593Smuzhiyun 	uint8 se_he_xtlv[32];
5966*4882a593Smuzhiyun 	int se_he_xtlv_len = sizeof(se_he_xtlv);
5967*4882a593Smuzhiyun 	he_xtlv_v32 v32;
5968*4882a593Smuzhiyun 	u32 he_feature = 0;
5969*4882a593Smuzhiyun 	s32 err = 0;
5970*4882a593Smuzhiyun 
5971*4882a593Smuzhiyun 	read_he_xtlv.id = WL_HE_CMD_FEATURES;
5972*4882a593Smuzhiyun 	read_he_xtlv.len = 0;
5973*4882a593Smuzhiyun 	err = wldev_iovar_getbuf_bsscfg(dev, "he", &read_he_xtlv, sizeof(read_he_xtlv),
5974*4882a593Smuzhiyun 			cfg->ioctl_buf, WLC_IOCTL_SMLEN, bssidx, NULL);
5975*4882a593Smuzhiyun 	if (err < 0) {
5976*4882a593Smuzhiyun 		WL_ERR(("HE get failed. error=%d\n", err));
5977*4882a593Smuzhiyun 		return err;
5978*4882a593Smuzhiyun 	} else {
5979*4882a593Smuzhiyun 		he_feature =  *(int*)cfg->ioctl_buf;
5980*4882a593Smuzhiyun 		he_feature = dtoh32(he_feature);
5981*4882a593Smuzhiyun 	}
5982*4882a593Smuzhiyun 
5983*4882a593Smuzhiyun 	v32.id = WL_HE_CMD_FEATURES;
5984*4882a593Smuzhiyun 	v32.len = sizeof(s32);
5985*4882a593Smuzhiyun 
5986*4882a593Smuzhiyun 	if (set) {
5987*4882a593Smuzhiyun 		v32.val = (he_feature | he_flag);
5988*4882a593Smuzhiyun 	} else {
5989*4882a593Smuzhiyun 		v32.val = (he_feature & ~he_flag);
5990*4882a593Smuzhiyun 	}
5991*4882a593Smuzhiyun 
5992*4882a593Smuzhiyun 	err = bcm_pack_xtlv_buf((void *)&v32, se_he_xtlv, sizeof(se_he_xtlv),
5993*4882a593Smuzhiyun 			BCM_XTLV_OPTION_ALIGN32, wl_he_get_uint_cb, wl_he_pack_uint_cb,
5994*4882a593Smuzhiyun 			&se_he_xtlv_len);
5995*4882a593Smuzhiyun 	if (err != BCME_OK) {
5996*4882a593Smuzhiyun 		WL_ERR(("failed to pack he settvl=%d\n", err));
5997*4882a593Smuzhiyun 	}
5998*4882a593Smuzhiyun 
5999*4882a593Smuzhiyun 	err = wldev_iovar_setbuf_bsscfg(dev, "he", &se_he_xtlv, sizeof(se_he_xtlv),
6000*4882a593Smuzhiyun 			cfg->ioctl_buf, WLC_IOCTL_SMLEN, bssidx, &cfg->ioctl_buf_sync);
6001*4882a593Smuzhiyun 	if (err < 0) {
6002*4882a593Smuzhiyun 		WL_ERR(("failed to set he features, error=%d\n", err));
6003*4882a593Smuzhiyun 	}
6004*4882a593Smuzhiyun 	WL_INFORM(("Set HE[%d] done\n", set));
6005*4882a593Smuzhiyun 
6006*4882a593Smuzhiyun 	return err;
6007*4882a593Smuzhiyun }
6008*4882a593Smuzhiyun 
6009*4882a593Smuzhiyun #if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0))
6010*4882a593Smuzhiyun int
wl_cfg80211_channel_switch(struct wiphy * wiphy,struct net_device * dev,struct cfg80211_csa_settings * params)6011*4882a593Smuzhiyun wl_cfg80211_channel_switch(struct wiphy *wiphy, struct net_device *dev,
6012*4882a593Smuzhiyun 	struct cfg80211_csa_settings *params)
6013*4882a593Smuzhiyun {
6014*4882a593Smuzhiyun 	s32 err = BCME_OK;
6015*4882a593Smuzhiyun 	u32 bw = WL_CHANSPEC_BW_20;
6016*4882a593Smuzhiyun 	chanspec_t chspec = 0;
6017*4882a593Smuzhiyun 	wl_chan_switch_t csa_arg;
6018*4882a593Smuzhiyun 	struct cfg80211_chan_def *chandef = &params->chandef;
6019*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wiphy_priv(wiphy);
6020*4882a593Smuzhiyun 	struct net_device *primary_dev = bcmcfg_to_prmry_ndev(cfg);
6021*4882a593Smuzhiyun 
6022*4882a593Smuzhiyun 	dev = ndev_to_wlc_ndev(dev, cfg);
6023*4882a593Smuzhiyun 	chspec = wl_freq_to_chanspec(chandef->chan->center_freq);
6024*4882a593Smuzhiyun 
6025*4882a593Smuzhiyun 	WL_ERR(("netdev_ifidx(%d), target channel(%d) target bandwidth(%d),"
6026*4882a593Smuzhiyun 		" mode(%d), count(%d)\n", dev->ifindex, CHSPEC_CHANNEL(chspec), chandef->width,
6027*4882a593Smuzhiyun 		params->block_tx, params->count));
6028*4882a593Smuzhiyun 
6029*4882a593Smuzhiyun 	if (wl_get_mode_by_netdev(cfg, dev) != WL_MODE_AP) {
6030*4882a593Smuzhiyun 		WL_ERR(("Channel Switch doesn't support on "
6031*4882a593Smuzhiyun 			"the non-SoftAP mode\n"));
6032*4882a593Smuzhiyun 		return -EINVAL;
6033*4882a593Smuzhiyun 	}
6034*4882a593Smuzhiyun 
6035*4882a593Smuzhiyun 	/* Check if STA is trying to associate with an AP */
6036*4882a593Smuzhiyun 	if (wl_get_drv_status(cfg, CONNECTING, primary_dev)) {
6037*4882a593Smuzhiyun 		WL_ERR(("Connecting is in progress\n"));
6038*4882a593Smuzhiyun 		return BCME_BUSY;
6039*4882a593Smuzhiyun 	}
6040*4882a593Smuzhiyun 
6041*4882a593Smuzhiyun 	if (chspec == cfg->ap_oper_channel) {
6042*4882a593Smuzhiyun 		WL_ERR(("Channel %d is same as current operating channel,"
6043*4882a593Smuzhiyun 			" so skip\n", CHSPEC_CHANNEL(chspec)));
6044*4882a593Smuzhiyun 		return BCME_OK;
6045*4882a593Smuzhiyun 	}
6046*4882a593Smuzhiyun 
6047*4882a593Smuzhiyun 	if (
6048*4882a593Smuzhiyun #ifdef WL_6G_BAND
6049*4882a593Smuzhiyun 		CHSPEC_IS6G(chspec) ||
6050*4882a593Smuzhiyun #endif
6051*4882a593Smuzhiyun 		CHSPEC_IS5G(chspec)) {
6052*4882a593Smuzhiyun #ifdef APSTA_RESTRICTED_CHANNEL
6053*4882a593Smuzhiyun 		if (CHSPEC_CHANNEL(chspec) != DEFAULT_5G_SOFTAP_CHANNEL) {
6054*4882a593Smuzhiyun 			WL_ERR(("Invalid 5G Channel, chan=%d\n", CHSPEC_CHANNEL(chspec)));
6055*4882a593Smuzhiyun 			return -EINVAL;
6056*4882a593Smuzhiyun 		}
6057*4882a593Smuzhiyun #endif /* APSTA_RESTRICTED_CHANNEL */
6058*4882a593Smuzhiyun 		err = wl_get_bandwidth_cap(primary_dev, CHSPEC_BAND(chspec), &bw);
6059*4882a593Smuzhiyun 		if (err < 0) {
6060*4882a593Smuzhiyun 			WL_ERR(("Failed to get bandwidth information,"
6061*4882a593Smuzhiyun 				" err=%d\n", err));
6062*4882a593Smuzhiyun 			return err;
6063*4882a593Smuzhiyun 		}
6064*4882a593Smuzhiyun 	} else if (CHSPEC_IS2G(chspec)) {
6065*4882a593Smuzhiyun #ifdef BCMDONGLEHOST
6066*4882a593Smuzhiyun #ifdef APSTA_RESTRICTED_CHANNEL
6067*4882a593Smuzhiyun 		dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
6068*4882a593Smuzhiyun 		chanspec_t *sta_chanspec = (chanspec_t *)wl_read_prof(cfg,
6069*4882a593Smuzhiyun 			primary_dev, WL_PROF_CHAN);
6070*4882a593Smuzhiyun 
6071*4882a593Smuzhiyun 		/* In 2GHz STA/SoftAP concurrent mode, the operating channel
6072*4882a593Smuzhiyun 		 * of STA and SoftAP should be confgiured to the same 2GHz
6073*4882a593Smuzhiyun 		 * channel. Otherwise, it is an invalid configuration.
6074*4882a593Smuzhiyun 		 */
6075*4882a593Smuzhiyun 		if (DHD_OPMODE_STA_SOFTAP_CONCURR(dhdp) &&
6076*4882a593Smuzhiyun 			wl_get_drv_status(cfg, CONNECTED, primary_dev) &&
6077*4882a593Smuzhiyun 			sta_chanspec && (CHSPEC_CHANNEL(*sta_chanspec) != CHSPEC_CHANNEL(chspec))) {
6078*4882a593Smuzhiyun 			WL_ERR(("Invalid 2G Channel in case of STA/SoftAP"
6079*4882a593Smuzhiyun 				" concurrent mode, sta_chan=%d, chan=%d\n",
6080*4882a593Smuzhiyun 				CHSPEC_CHANNEL(*sta_chanspec), CHSPEC_CHANNEL(chspec)));
6081*4882a593Smuzhiyun 			return -EINVAL;
6082*4882a593Smuzhiyun 		}
6083*4882a593Smuzhiyun #endif /* APSTA_RESTRICTED_CHANNEL */
6084*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
6085*4882a593Smuzhiyun 		bw = WL_CHANSPEC_BW_20;
6086*4882a593Smuzhiyun 	} else {
6087*4882a593Smuzhiyun 		WL_ERR(("invalid band (%d)\n", CHSPEC_BAND(chspec)));
6088*4882a593Smuzhiyun 		return -EINVAL;
6089*4882a593Smuzhiyun 	}
6090*4882a593Smuzhiyun 
6091*4882a593Smuzhiyun #ifdef WL_6G_BAND
6092*4882a593Smuzhiyun 	/* Avoid in case of 6G as for each center frequency bw is unique and is
6093*4882a593Smuzhiyun 	* detected based on centre frequency.
6094*4882a593Smuzhiyun 	*/
6095*4882a593Smuzhiyun 	if (!CHSPEC_IS6G(chspec))
6096*4882a593Smuzhiyun #endif /* WL_6G_BAND */
6097*4882a593Smuzhiyun 	{
6098*4882a593Smuzhiyun 		chspec = wf_channel2chspec(CHSPEC_CHANNEL(chspec), bw);
6099*4882a593Smuzhiyun 	}
6100*4882a593Smuzhiyun 	if (!wf_chspec_valid(chspec)) {
6101*4882a593Smuzhiyun 		WL_ERR(("Invalid chanspec 0x%x\n", chspec));
6102*4882a593Smuzhiyun 		return -EINVAL;
6103*4882a593Smuzhiyun 	}
6104*4882a593Smuzhiyun 
6105*4882a593Smuzhiyun 	/* Send CSA to associated STAs */
6106*4882a593Smuzhiyun 	memset(&csa_arg, 0, sizeof(wl_chan_switch_t));
6107*4882a593Smuzhiyun 	csa_arg.mode = params->block_tx;
6108*4882a593Smuzhiyun 	csa_arg.count = params->count;
6109*4882a593Smuzhiyun 	csa_arg.chspec = chspec;
6110*4882a593Smuzhiyun 	csa_arg.frame_type = CSA_BROADCAST_ACTION_FRAME;
6111*4882a593Smuzhiyun 	csa_arg.reg = 0;
6112*4882a593Smuzhiyun 
6113*4882a593Smuzhiyun 	err = wldev_iovar_setbuf(dev, "csa", &csa_arg, sizeof(wl_chan_switch_t),
6114*4882a593Smuzhiyun 		cfg->ioctl_buf, WLC_IOCTL_SMLEN, &cfg->ioctl_buf_sync);
6115*4882a593Smuzhiyun 	if (err < 0) {
6116*4882a593Smuzhiyun 		WL_ERR(("Failed to switch channel, err=%d\n", err));
6117*4882a593Smuzhiyun 	}
6118*4882a593Smuzhiyun 
6119*4882a593Smuzhiyun 	return err;
6120*4882a593Smuzhiyun }
6121*4882a593Smuzhiyun #endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(3, 12, 0) */
6122*4882a593Smuzhiyun 
6123*4882a593Smuzhiyun #ifdef SUPPORT_AP_SUSPEND
6124*4882a593Smuzhiyun void
wl_set_ap_suspend_error_handler(struct net_device * ndev,bool suspend)6125*4882a593Smuzhiyun wl_set_ap_suspend_error_handler(struct net_device *ndev, bool suspend)
6126*4882a593Smuzhiyun {
6127*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(ndev);
6128*4882a593Smuzhiyun 	dhd_pub_t *dhdp = (dhd_pub_t *)(cfg->pub);
6129*4882a593Smuzhiyun 
6130*4882a593Smuzhiyun 	if (wl_get_drv_status(cfg, READY, ndev)) {
6131*4882a593Smuzhiyun #if defined(BCMDONGLEHOST)
6132*4882a593Smuzhiyun 		/* IF dongle is down due to previous hang or other conditions, sending
6133*4882a593Smuzhiyun 		* one more hang notification is not needed.
6134*4882a593Smuzhiyun 		*/
6135*4882a593Smuzhiyun 		if (dhd_query_bus_erros(dhdp)) {
6136*4882a593Smuzhiyun 			return;
6137*4882a593Smuzhiyun 		}
6138*4882a593Smuzhiyun 		dhdp->iface_op_failed = TRUE;
6139*4882a593Smuzhiyun #if defined(DHD_FW_COREDUMP)
6140*4882a593Smuzhiyun 		if (dhdp->memdump_enabled) {
6141*4882a593Smuzhiyun 			dhdp->memdump_type = DUMP_TYPE_IFACE_OP_FAILURE;
6142*4882a593Smuzhiyun 			dhd_bus_mem_dump(dhdp);
6143*4882a593Smuzhiyun 		}
6144*4882a593Smuzhiyun #endif /* DHD_FW_COREDUMP */
6145*4882a593Smuzhiyun #endif /* BCMDONGLEHOST */
6146*4882a593Smuzhiyun 
6147*4882a593Smuzhiyun #if defined(BCMDONGLEHOST) && defined(OEM_ANDROID)
6148*4882a593Smuzhiyun 		WL_ERR(("Notify hang event to upper layer \n"));
6149*4882a593Smuzhiyun 		dhdp->hang_reason = suspend ?
6150*4882a593Smuzhiyun 			HANG_REASON_BSS_DOWN_FAILURE : HANG_REASON_BSS_UP_FAILURE;
6151*4882a593Smuzhiyun 		net_os_send_hang_message(ndev);
6152*4882a593Smuzhiyun #endif /* BCMDONGLEHOST && OEM_ANDROID */
6153*4882a593Smuzhiyun 
6154*4882a593Smuzhiyun 	}
6155*4882a593Smuzhiyun }
6156*4882a593Smuzhiyun 
6157*4882a593Smuzhiyun #define MAX_AP_RESUME_TIME   5000
6158*4882a593Smuzhiyun int
wl_set_ap_suspend(struct net_device * dev,bool suspend,char * ifname)6159*4882a593Smuzhiyun wl_set_ap_suspend(struct net_device *dev, bool suspend, char *ifname)
6160*4882a593Smuzhiyun {
6161*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
6162*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
6163*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
6164*4882a593Smuzhiyun 	int ret = BCME_OK;
6165*4882a593Smuzhiyun 	bool is_bssup = FALSE;
6166*4882a593Smuzhiyun 	int bssidx;
6167*4882a593Smuzhiyun 	unsigned long start_j;
6168*4882a593Smuzhiyun 	int time_to_sleep = MAX_AP_RESUME_TIME;
6169*4882a593Smuzhiyun 
6170*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
6171*4882a593Smuzhiyun 
6172*4882a593Smuzhiyun 	if (!dhdp) {
6173*4882a593Smuzhiyun 		return BCME_NOTUP;
6174*4882a593Smuzhiyun 	}
6175*4882a593Smuzhiyun 
6176*4882a593Smuzhiyun 	if (!(dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) {
6177*4882a593Smuzhiyun 		WL_ERR(("Not Hostapd mode\n"));
6178*4882a593Smuzhiyun 		return BCME_NOTAP;
6179*4882a593Smuzhiyun 	}
6180*4882a593Smuzhiyun 
6181*4882a593Smuzhiyun 	ndev = wl_get_ap_netdev(cfg, ifname);
6182*4882a593Smuzhiyun 
6183*4882a593Smuzhiyun 	if (ndev == NULL) {
6184*4882a593Smuzhiyun 		WL_ERR(("No softAP interface named %s\n", ifname));
6185*4882a593Smuzhiyun 		return BCME_NOTAP;
6186*4882a593Smuzhiyun 	}
6187*4882a593Smuzhiyun 
6188*4882a593Smuzhiyun 	if ((bssidx = wl_get_bssidx_by_wdev(cfg, ndev->ieee80211_ptr)) < 0) {
6189*4882a593Smuzhiyun 		WL_ERR(("Find p2p index from wdev(%p) failed\n", ndev->ieee80211_ptr));
6190*4882a593Smuzhiyun 		return BCME_NOTFOUND;
6191*4882a593Smuzhiyun 	}
6192*4882a593Smuzhiyun 
6193*4882a593Smuzhiyun 	is_bssup = wl_cfg80211_bss_isup(ndev, bssidx);
6194*4882a593Smuzhiyun 	if (is_bssup && suspend) {
6195*4882a593Smuzhiyun 		wl_clr_drv_status(cfg, AP_CREATED, ndev);
6196*4882a593Smuzhiyun 		wl_clr_drv_status(cfg, CONNECTED, ndev);
6197*4882a593Smuzhiyun 
6198*4882a593Smuzhiyun 		if ((ret = wl_cfg80211_bss_up(cfg, ndev, bssidx, 0)) < 0) {
6199*4882a593Smuzhiyun 			WL_ERR(("AP suspend error %d, suspend %d\n", ret, suspend));
6200*4882a593Smuzhiyun 			ret = BCME_NOTDOWN;
6201*4882a593Smuzhiyun 			goto exit;
6202*4882a593Smuzhiyun 		}
6203*4882a593Smuzhiyun 	} else if (!is_bssup && !suspend) {
6204*4882a593Smuzhiyun 		/* Abort scan before starting AP again */
6205*4882a593Smuzhiyun 		wl_cfgscan_cancel_scan(cfg);
6206*4882a593Smuzhiyun 
6207*4882a593Smuzhiyun 		if ((ret = wl_cfg80211_bss_up(cfg, ndev, bssidx, 1)) < 0) {
6208*4882a593Smuzhiyun 			WL_ERR(("AP resume error %d, suspend %d\n", ret, suspend));
6209*4882a593Smuzhiyun 			ret = BCME_NOTUP;
6210*4882a593Smuzhiyun 			goto exit;
6211*4882a593Smuzhiyun 		}
6212*4882a593Smuzhiyun 
6213*4882a593Smuzhiyun 		while (TRUE) {
6214*4882a593Smuzhiyun 			start_j = get_jiffies_64();
6215*4882a593Smuzhiyun 			/* Wait for Linkup event to mark successful AP bring up */
6216*4882a593Smuzhiyun 			ret = wait_event_interruptible_timeout(cfg->netif_change_event,
6217*4882a593Smuzhiyun 				wl_get_drv_status(cfg, AP_CREATED, ndev),
6218*4882a593Smuzhiyun 				msecs_to_jiffies(time_to_sleep));
6219*4882a593Smuzhiyun 			if (ret == -ERESTARTSYS) {
6220*4882a593Smuzhiyun 				WL_ERR(("waitqueue was interrupted by a signal\n"));
6221*4882a593Smuzhiyun 				time_to_sleep -= jiffies_to_msecs(get_jiffies_64() - start_j);
6222*4882a593Smuzhiyun 				if (time_to_sleep <= 0) {
6223*4882a593Smuzhiyun 					WL_ERR(("time to sleep hits 0\n"));
6224*4882a593Smuzhiyun 					ret = BCME_NOTUP;
6225*4882a593Smuzhiyun 					goto exit;
6226*4882a593Smuzhiyun 				}
6227*4882a593Smuzhiyun 			} else if (ret == 0 || !wl_get_drv_status(cfg, AP_CREATED, ndev)) {
6228*4882a593Smuzhiyun 				WL_ERR(("AP resume failed!\n"));
6229*4882a593Smuzhiyun 				ret = BCME_NOTUP;
6230*4882a593Smuzhiyun 				goto exit;
6231*4882a593Smuzhiyun 			} else {
6232*4882a593Smuzhiyun 				wl_set_drv_status(cfg, CONNECTED, ndev);
6233*4882a593Smuzhiyun 				wl_clr_drv_status(cfg, AP_CREATING, ndev);
6234*4882a593Smuzhiyun 				ret = BCME_OK;
6235*4882a593Smuzhiyun 				break;
6236*4882a593Smuzhiyun 			}
6237*4882a593Smuzhiyun 		}
6238*4882a593Smuzhiyun 	} else {
6239*4882a593Smuzhiyun 		/* bssup + resume or bssdown + suspend,
6240*4882a593Smuzhiyun 		 * So, returns OK
6241*4882a593Smuzhiyun 		 */
6242*4882a593Smuzhiyun 		ret = BCME_OK;
6243*4882a593Smuzhiyun 	}
6244*4882a593Smuzhiyun exit:
6245*4882a593Smuzhiyun 	if (ret != BCME_OK)
6246*4882a593Smuzhiyun 		wl_set_ap_suspend_error_handler(bcmcfg_to_prmry_ndev(cfg), suspend);
6247*4882a593Smuzhiyun 
6248*4882a593Smuzhiyun 	return ret;
6249*4882a593Smuzhiyun }
6250*4882a593Smuzhiyun #endif /* SUPPORT_AP_SUSPEND */
6251*4882a593Smuzhiyun 
6252*4882a593Smuzhiyun #ifdef SUPPORT_SOFTAP_ELNA_BYPASS
wl_set_softap_elna_bypass(struct net_device * dev,char * ifname,int enable)6253*4882a593Smuzhiyun int wl_set_softap_elna_bypass(struct net_device *dev, char *ifname, int enable)
6254*4882a593Smuzhiyun {
6255*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
6256*4882a593Smuzhiyun 	struct net_device *ifdev = NULL;
6257*4882a593Smuzhiyun 	char iobuf[WLC_IOCTL_SMLEN];
6258*4882a593Smuzhiyun 	int err = BCME_OK;
6259*4882a593Smuzhiyun 	int iftype = 0;
6260*4882a593Smuzhiyun 
6261*4882a593Smuzhiyun 	memset(iobuf, 0, WLC_IOCTL_SMLEN);
6262*4882a593Smuzhiyun 
6263*4882a593Smuzhiyun 	/* Check the interface type */
6264*4882a593Smuzhiyun 	ifdev = wl_get_netdev_by_name(cfg, ifname);
6265*4882a593Smuzhiyun 	if (ifdev == NULL) {
6266*4882a593Smuzhiyun 		WL_ERR(("%s: Could not find net_device for ifname:%s\n", __FUNCTION__, ifname));
6267*4882a593Smuzhiyun 		err = BCME_BADARG;
6268*4882a593Smuzhiyun 		goto fail;
6269*4882a593Smuzhiyun 	}
6270*4882a593Smuzhiyun 
6271*4882a593Smuzhiyun 	iftype = ifdev->ieee80211_ptr->iftype;
6272*4882a593Smuzhiyun 	if (iftype == NL80211_IFTYPE_AP) {
6273*4882a593Smuzhiyun 		err = wldev_iovar_setint(ifdev, "softap_elnabypass", enable);
6274*4882a593Smuzhiyun 		if (unlikely(err)) {
6275*4882a593Smuzhiyun 			WL_ERR(("%s: Failed to set softap_elnabypass, err=%d\n",
6276*4882a593Smuzhiyun 				__FUNCTION__, err));
6277*4882a593Smuzhiyun 		}
6278*4882a593Smuzhiyun 	} else {
6279*4882a593Smuzhiyun 		WL_ERR(("%s: softap_elnabypass should control in SoftAP mode only\n",
6280*4882a593Smuzhiyun 			__FUNCTION__));
6281*4882a593Smuzhiyun 		err = BCME_BADARG;
6282*4882a593Smuzhiyun 	}
6283*4882a593Smuzhiyun fail:
6284*4882a593Smuzhiyun 	return err;
6285*4882a593Smuzhiyun }
wl_get_softap_elna_bypass(struct net_device * dev,char * ifname,void * param)6286*4882a593Smuzhiyun int wl_get_softap_elna_bypass(struct net_device *dev, char *ifname, void *param)
6287*4882a593Smuzhiyun {
6288*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
6289*4882a593Smuzhiyun 	int *enable = (int*)param;
6290*4882a593Smuzhiyun 	struct net_device *ifdev = NULL;
6291*4882a593Smuzhiyun 	char iobuf[WLC_IOCTL_SMLEN];
6292*4882a593Smuzhiyun 	int err = BCME_OK;
6293*4882a593Smuzhiyun 	int iftype = 0;
6294*4882a593Smuzhiyun 
6295*4882a593Smuzhiyun 	memset(iobuf, 0, WLC_IOCTL_SMLEN);
6296*4882a593Smuzhiyun 
6297*4882a593Smuzhiyun 	/* Check the interface type */
6298*4882a593Smuzhiyun 	ifdev = wl_get_netdev_by_name(cfg, ifname);
6299*4882a593Smuzhiyun 	if (ifdev == NULL) {
6300*4882a593Smuzhiyun 		WL_ERR(("%s: Could not find net_device for ifname:%s\n", __FUNCTION__, ifname));
6301*4882a593Smuzhiyun 		err = BCME_BADARG;
6302*4882a593Smuzhiyun 		goto fail;
6303*4882a593Smuzhiyun 	}
6304*4882a593Smuzhiyun 
6305*4882a593Smuzhiyun 	iftype = ifdev->ieee80211_ptr->iftype;
6306*4882a593Smuzhiyun 	if (iftype == NL80211_IFTYPE_AP) {
6307*4882a593Smuzhiyun 		err = wldev_iovar_getint(ifdev, "softap_elnabypass", enable);
6308*4882a593Smuzhiyun 		if (unlikely(err)) {
6309*4882a593Smuzhiyun 			WL_ERR(("%s: Failed to get softap_elnabypass, err=%d\n",
6310*4882a593Smuzhiyun 				__FUNCTION__, err));
6311*4882a593Smuzhiyun 		}
6312*4882a593Smuzhiyun 	} else {
6313*4882a593Smuzhiyun 		WL_ERR(("%s: softap_elnabypass should control in SoftAP mode only\n",
6314*4882a593Smuzhiyun 			__FUNCTION__));
6315*4882a593Smuzhiyun 		err = BCME_BADARG;
6316*4882a593Smuzhiyun 	}
6317*4882a593Smuzhiyun fail:
6318*4882a593Smuzhiyun 	return err;
6319*4882a593Smuzhiyun 
6320*4882a593Smuzhiyun }
6321*4882a593Smuzhiyun #endif /* SUPPORT_SOFTAP_ELNA_BYPASS */
6322*4882a593Smuzhiyun 
6323*4882a593Smuzhiyun #ifdef SUPPORT_AP_BWCTRL
6324*4882a593Smuzhiyun #define OPER_MODE_ENABLE	(1 << 8)
6325*4882a593Smuzhiyun static int op2bw[] = {20, 40, 80, 160};
6326*4882a593Smuzhiyun 
6327*4882a593Smuzhiyun static int
wl_get_ap_he_mode(struct net_device * ndev,struct bcm_cfg80211 * cfg,bool * he)6328*4882a593Smuzhiyun wl_get_ap_he_mode(struct net_device *ndev, struct bcm_cfg80211 *cfg, bool *he)
6329*4882a593Smuzhiyun {
6330*4882a593Smuzhiyun 	bcm_xtlv_t read_he_xtlv;
6331*4882a593Smuzhiyun 	int ret = 0;
6332*4882a593Smuzhiyun 	u8  he_enab = 0;
6333*4882a593Smuzhiyun 	u32 he_feature = 0;
6334*4882a593Smuzhiyun 	*he = FALSE;
6335*4882a593Smuzhiyun 
6336*4882a593Smuzhiyun 	/* Check he enab first */
6337*4882a593Smuzhiyun 	read_he_xtlv.id = WL_HE_CMD_ENAB;
6338*4882a593Smuzhiyun 	read_he_xtlv.len = 0;
6339*4882a593Smuzhiyun 
6340*4882a593Smuzhiyun 	ret = wldev_iovar_getbuf(ndev, "he", &read_he_xtlv, sizeof(read_he_xtlv),
6341*4882a593Smuzhiyun 			cfg->ioctl_buf, WLC_IOCTL_SMLEN, NULL);
6342*4882a593Smuzhiyun 	if (ret < 0) {
6343*4882a593Smuzhiyun 		if (ret == BCME_UNSUPPORTED) {
6344*4882a593Smuzhiyun 			/* HE not supported */
6345*4882a593Smuzhiyun 			ret = BCME_OK;
6346*4882a593Smuzhiyun 		} else {
6347*4882a593Smuzhiyun 			WL_ERR(("HE ENAB get failed. ret=%d\n", ret));
6348*4882a593Smuzhiyun 		}
6349*4882a593Smuzhiyun 		goto exit;
6350*4882a593Smuzhiyun 	} else {
6351*4882a593Smuzhiyun 		he_enab =  *(u8*)cfg->ioctl_buf;
6352*4882a593Smuzhiyun 	}
6353*4882a593Smuzhiyun 
6354*4882a593Smuzhiyun 	if (!he_enab) {
6355*4882a593Smuzhiyun 		goto exit;
6356*4882a593Smuzhiyun 	}
6357*4882a593Smuzhiyun 
6358*4882a593Smuzhiyun 	/* Then check BIT3 of he features */
6359*4882a593Smuzhiyun 	read_he_xtlv.id = WL_HE_CMD_FEATURES;
6360*4882a593Smuzhiyun 	read_he_xtlv.len = 0;
6361*4882a593Smuzhiyun 
6362*4882a593Smuzhiyun 	ret = wldev_iovar_getbuf(ndev, "he", &read_he_xtlv, sizeof(read_he_xtlv),
6363*4882a593Smuzhiyun 			cfg->ioctl_buf, WLC_IOCTL_SMLEN, NULL);
6364*4882a593Smuzhiyun 	if (ret < 0) {
6365*4882a593Smuzhiyun 		WL_ERR(("HE FEATURE get failed. error=%d\n", ret));
6366*4882a593Smuzhiyun 		goto exit;
6367*4882a593Smuzhiyun 	} else {
6368*4882a593Smuzhiyun 		he_feature =  *(int*)cfg->ioctl_buf;
6369*4882a593Smuzhiyun 		he_feature = dtoh32(he_feature);
6370*4882a593Smuzhiyun 	}
6371*4882a593Smuzhiyun 
6372*4882a593Smuzhiyun 	if (he_feature & WL_HE_FEATURES_HE_AP) {
6373*4882a593Smuzhiyun 		WL_DBG(("HE is enabled in AP\n"));
6374*4882a593Smuzhiyun 		*he = TRUE;
6375*4882a593Smuzhiyun 	}
6376*4882a593Smuzhiyun exit:
6377*4882a593Smuzhiyun 	return ret;
6378*4882a593Smuzhiyun }
6379*4882a593Smuzhiyun 
6380*4882a593Smuzhiyun static void
wl_update_apchan_bwcap(struct bcm_cfg80211 * cfg,struct net_device * ndev,chanspec_t chanspec)6381*4882a593Smuzhiyun wl_update_apchan_bwcap(struct bcm_cfg80211 *cfg, struct net_device *ndev, chanspec_t chanspec)
6382*4882a593Smuzhiyun {
6383*4882a593Smuzhiyun 	struct net_device *dev = bcmcfg_to_prmry_ndev(cfg);
6384*4882a593Smuzhiyun 	struct wireless_dev *wdev = ndev_to_wdev(dev);
6385*4882a593Smuzhiyun 	struct wiphy *wiphy = wdev->wiphy;
6386*4882a593Smuzhiyun 	int ret = BCME_OK;
6387*4882a593Smuzhiyun 	u32 bw_cap;
6388*4882a593Smuzhiyun 	u32 ctl_chan;
6389*4882a593Smuzhiyun 	chanspec_t chanbw = WL_CHANSPEC_BW_20;
6390*4882a593Smuzhiyun 
6391*4882a593Smuzhiyun 	/* Update channel in profile */
6392*4882a593Smuzhiyun 	ctl_chan = wf_chspec_ctlchan(chanspec);
6393*4882a593Smuzhiyun 	wl_update_prof(cfg, ndev, NULL, &chanspec, WL_PROF_CHAN);
6394*4882a593Smuzhiyun 
6395*4882a593Smuzhiyun 	/* BW cap is only updated in 5GHz */
6396*4882a593Smuzhiyun 	if (ctl_chan <= CH_MAX_2G_CHANNEL)
6397*4882a593Smuzhiyun 		return;
6398*4882a593Smuzhiyun 
6399*4882a593Smuzhiyun 	/* Get WL BW CAP */
6400*4882a593Smuzhiyun 	ret = wl_get_bandwidth_cap(bcmcfg_to_prmry_ndev(cfg),
6401*4882a593Smuzhiyun 		CHSPEC_BAND(chanspec), &bw_cap);
6402*4882a593Smuzhiyun 	if (ret < 0) {
6403*4882a593Smuzhiyun 		WL_ERR(("get bw_cap failed = %d\n", ret));
6404*4882a593Smuzhiyun 		goto exit;
6405*4882a593Smuzhiyun 	}
6406*4882a593Smuzhiyun 
6407*4882a593Smuzhiyun 	chanbw = CHSPEC_BW(wl_channel_to_chanspec(wiphy,
6408*4882a593Smuzhiyun 		ndev, wf_chspec_ctlchan(chanspec), bw_cap));
6409*4882a593Smuzhiyun 
6410*4882a593Smuzhiyun exit:
6411*4882a593Smuzhiyun 	cfg->bw_cap_5g = bw2cap[chanbw >> WL_CHANSPEC_BW_SHIFT];
6412*4882a593Smuzhiyun 	WL_INFORM_MEM(("supported bw cap is:0x%x\n", cfg->bw_cap_5g));
6413*4882a593Smuzhiyun 
6414*4882a593Smuzhiyun }
6415*4882a593Smuzhiyun 
6416*4882a593Smuzhiyun int
wl_rxchain_to_opmode_nss(int rxchain)6417*4882a593Smuzhiyun wl_rxchain_to_opmode_nss(int rxchain)
6418*4882a593Smuzhiyun {
6419*4882a593Smuzhiyun 	/*
6420*4882a593Smuzhiyun 	 * Nss 1 -> 0, Nss 2 -> 1
6421*4882a593Smuzhiyun 	 * This is from operating mode field
6422*4882a593Smuzhiyun 	 * in 8.4.1.50 of 802.11ac-2013
6423*4882a593Smuzhiyun 	 */
6424*4882a593Smuzhiyun 	/* TODO : Nss 3 ? */
6425*4882a593Smuzhiyun 	if (rxchain == 3)
6426*4882a593Smuzhiyun 		return (1 << 4);
6427*4882a593Smuzhiyun 	else
6428*4882a593Smuzhiyun 		return 0;
6429*4882a593Smuzhiyun }
6430*4882a593Smuzhiyun 
6431*4882a593Smuzhiyun int
wl_update_opmode(struct net_device * ndev,u32 bw)6432*4882a593Smuzhiyun wl_update_opmode(struct net_device *ndev, u32 bw)
6433*4882a593Smuzhiyun {
6434*4882a593Smuzhiyun 	int ret = BCME_OK;
6435*4882a593Smuzhiyun 	int oper_mode;
6436*4882a593Smuzhiyun 	int rxchain;
6437*4882a593Smuzhiyun 
6438*4882a593Smuzhiyun 	ret = wldev_iovar_getint(ndev, "rxchain", (s32 *)&rxchain);
6439*4882a593Smuzhiyun 	if (ret < 0) {
6440*4882a593Smuzhiyun 		WL_ERR(("get rxchain failed = %d\n", ret));
6441*4882a593Smuzhiyun 		goto exit;
6442*4882a593Smuzhiyun 	}
6443*4882a593Smuzhiyun 
6444*4882a593Smuzhiyun 	oper_mode = bw;
6445*4882a593Smuzhiyun 	oper_mode |= wl_rxchain_to_opmode_nss(rxchain);
6446*4882a593Smuzhiyun 	/* Enable flag */
6447*4882a593Smuzhiyun 	oper_mode |= OPER_MODE_ENABLE;
6448*4882a593Smuzhiyun 
6449*4882a593Smuzhiyun 	ret = wldev_iovar_setint(ndev, "oper_mode", oper_mode);
6450*4882a593Smuzhiyun 	if (ret < 0) {
6451*4882a593Smuzhiyun 		WL_ERR(("set oper_mode failed = %d\n", ret));
6452*4882a593Smuzhiyun 		goto exit;
6453*4882a593Smuzhiyun 	}
6454*4882a593Smuzhiyun 
6455*4882a593Smuzhiyun exit:
6456*4882a593Smuzhiyun 	return ret;
6457*4882a593Smuzhiyun }
6458*4882a593Smuzhiyun 
6459*4882a593Smuzhiyun int
wl_set_ap_bw(struct net_device * dev,u32 bw,char * ifname)6460*4882a593Smuzhiyun wl_set_ap_bw(struct net_device *dev, u32 bw, char *ifname)
6461*4882a593Smuzhiyun {
6462*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
6463*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
6464*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
6465*4882a593Smuzhiyun 	int ret = BCME_OK;
6466*4882a593Smuzhiyun 	chanspec_t *chanspec;
6467*4882a593Smuzhiyun 	bool he;
6468*4882a593Smuzhiyun 
6469*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
6470*4882a593Smuzhiyun 
6471*4882a593Smuzhiyun 	if (!dhdp) {
6472*4882a593Smuzhiyun 		return BCME_NOTUP;
6473*4882a593Smuzhiyun 	}
6474*4882a593Smuzhiyun 
6475*4882a593Smuzhiyun 	if (!(dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) {
6476*4882a593Smuzhiyun 		WL_ERR(("Not Hostapd mode\n"));
6477*4882a593Smuzhiyun 		return BCME_NOTAP;
6478*4882a593Smuzhiyun 	}
6479*4882a593Smuzhiyun 
6480*4882a593Smuzhiyun 	ndev = wl_get_ap_netdev(cfg, ifname);
6481*4882a593Smuzhiyun 
6482*4882a593Smuzhiyun 	if (ndev == NULL) {
6483*4882a593Smuzhiyun 		WL_ERR(("No softAP interface named %s\n", ifname));
6484*4882a593Smuzhiyun 		return BCME_NOTAP;
6485*4882a593Smuzhiyun 	}
6486*4882a593Smuzhiyun 
6487*4882a593Smuzhiyun 	if (bw > DOT11_OPER_MODE_160MHZ) {
6488*4882a593Smuzhiyun 		WL_ERR(("BW is too big %d\n", bw));
6489*4882a593Smuzhiyun 		return BCME_BADARG;
6490*4882a593Smuzhiyun 	}
6491*4882a593Smuzhiyun 
6492*4882a593Smuzhiyun 	chanspec = (chanspec_t *)wl_read_prof(cfg, ndev, WL_PROF_CHAN);
6493*4882a593Smuzhiyun 	if (CHSPEC_IS2G(*chanspec)) {
6494*4882a593Smuzhiyun 		WL_ERR(("current chanspec is %d, not supported\n", *chanspec));
6495*4882a593Smuzhiyun 		ret = BCME_BADCHAN;
6496*4882a593Smuzhiyun 		goto exit;
6497*4882a593Smuzhiyun 	}
6498*4882a593Smuzhiyun 
6499*4882a593Smuzhiyun 	if ((DHD_OPMODE_STA_SOFTAP_CONCURR(dhdp) &&
6500*4882a593Smuzhiyun 		wl_get_drv_status(cfg, CONNECTED, bcmcfg_to_prmry_ndev(cfg))) ||
6501*4882a593Smuzhiyun 		wl_cfgnan_is_enabled(cfg)) {
6502*4882a593Smuzhiyun 		WL_ERR(("BW control in concurrent mode is not supported\n"));
6503*4882a593Smuzhiyun 		return BCME_BUSY;
6504*4882a593Smuzhiyun 	}
6505*4882a593Smuzhiyun 
6506*4882a593Smuzhiyun 	/* When SCAN is on going either in STA or in AP, return BUSY */
6507*4882a593Smuzhiyun 	if (wl_get_drv_status_all(cfg, SCANNING)) {
6508*4882a593Smuzhiyun 		WL_ERR(("STA is SCANNING, not support BW control\n"));
6509*4882a593Smuzhiyun 		return BCME_BUSY;
6510*4882a593Smuzhiyun 	}
6511*4882a593Smuzhiyun 
6512*4882a593Smuzhiyun 	/* When SCANABORT is on going either in STA or in AP, return BUSY */
6513*4882a593Smuzhiyun 	if (wl_get_drv_status_all(cfg, SCAN_ABORTING)) {
6514*4882a593Smuzhiyun 		WL_ERR(("STA is SCAN_ABORTING, not support BW control\n"));
6515*4882a593Smuzhiyun 		return BCME_BUSY;
6516*4882a593Smuzhiyun 	}
6517*4882a593Smuzhiyun 
6518*4882a593Smuzhiyun 	/* When CONNECTION is on going in STA, return BUSY */
6519*4882a593Smuzhiyun 	if (wl_get_drv_status(cfg, CONNECTING, bcmcfg_to_prmry_ndev(cfg))) {
6520*4882a593Smuzhiyun 		WL_ERR(("STA is CONNECTING, not support BW control\n"));
6521*4882a593Smuzhiyun 		return BCME_BUSY;
6522*4882a593Smuzhiyun 	}
6523*4882a593Smuzhiyun 
6524*4882a593Smuzhiyun 	/* BW control in AX mode needs more verification */
6525*4882a593Smuzhiyun 	ret = wl_get_ap_he_mode(ndev, cfg, &he);
6526*4882a593Smuzhiyun 	if (ret == BCME_OK && he) {
6527*4882a593Smuzhiyun 		WL_ERR(("BW control in HE mode is not supported\n"));
6528*4882a593Smuzhiyun 		return BCME_UNSUPPORTED;
6529*4882a593Smuzhiyun 	}
6530*4882a593Smuzhiyun 	if (ret < 0) {
6531*4882a593Smuzhiyun 		WL_ERR(("Check AX mode is failed\n"));
6532*4882a593Smuzhiyun 		goto exit;
6533*4882a593Smuzhiyun 	}
6534*4882a593Smuzhiyun 
6535*4882a593Smuzhiyun 	if ((!WL_BW_CAP_160MHZ(cfg->bw_cap_5g) && (bw == DOT11_OPER_MODE_160MHZ)) ||
6536*4882a593Smuzhiyun 		(!WL_BW_CAP_80MHZ(cfg->bw_cap_5g) && (bw >= DOT11_OPER_MODE_80MHZ)) ||
6537*4882a593Smuzhiyun 		(!WL_BW_CAP_40MHZ(cfg->bw_cap_5g) && (bw >= DOT11_OPER_MODE_40MHZ)) ||
6538*4882a593Smuzhiyun 		(!WL_BW_CAP_20MHZ(cfg->bw_cap_5g))) {
6539*4882a593Smuzhiyun 		WL_ERR(("bw_cap %x does not support bw = %d\n", cfg->bw_cap_5g, bw));
6540*4882a593Smuzhiyun 		ret = BCME_BADARG;
6541*4882a593Smuzhiyun 		goto exit;
6542*4882a593Smuzhiyun 	}
6543*4882a593Smuzhiyun 
6544*4882a593Smuzhiyun 	WL_DBG(("Updating AP BW to %d\n", op2bw[bw]));
6545*4882a593Smuzhiyun 
6546*4882a593Smuzhiyun 	ret = wl_update_opmode(ndev, bw);
6547*4882a593Smuzhiyun 	if (ret < 0) {
6548*4882a593Smuzhiyun 		WL_ERR(("opmode set failed = %d\n", ret));
6549*4882a593Smuzhiyun 		goto exit;
6550*4882a593Smuzhiyun 	}
6551*4882a593Smuzhiyun 
6552*4882a593Smuzhiyun exit:
6553*4882a593Smuzhiyun 	return ret;
6554*4882a593Smuzhiyun }
6555*4882a593Smuzhiyun 
6556*4882a593Smuzhiyun int
wl_get_ap_bw(struct net_device * dev,char * command,char * ifname,int total_len)6557*4882a593Smuzhiyun wl_get_ap_bw(struct net_device *dev, char* command, char *ifname, int total_len)
6558*4882a593Smuzhiyun {
6559*4882a593Smuzhiyun 	struct bcm_cfg80211 *cfg = wl_get_cfg(dev);
6560*4882a593Smuzhiyun 	dhd_pub_t *dhdp;
6561*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
6562*4882a593Smuzhiyun 	int ret = BCME_OK;
6563*4882a593Smuzhiyun 	u32 chanspec = 0;
6564*4882a593Smuzhiyun 	u32 bw = DOT11_OPER_MODE_20MHZ;
6565*4882a593Smuzhiyun 	int bytes_written = 0;
6566*4882a593Smuzhiyun 
6567*4882a593Smuzhiyun 	dhdp = (dhd_pub_t *)(cfg->pub);
6568*4882a593Smuzhiyun 
6569*4882a593Smuzhiyun 	if (!dhdp) {
6570*4882a593Smuzhiyun 		return BCME_NOTUP;
6571*4882a593Smuzhiyun 	}
6572*4882a593Smuzhiyun 
6573*4882a593Smuzhiyun 	if (!(dhdp->op_mode & DHD_FLAG_HOSTAP_MODE)) {
6574*4882a593Smuzhiyun 		WL_ERR(("Not Hostapd mode\n"));
6575*4882a593Smuzhiyun 		return BCME_NOTAP;
6576*4882a593Smuzhiyun 	}
6577*4882a593Smuzhiyun 
6578*4882a593Smuzhiyun 	ndev = wl_get_ap_netdev(cfg, ifname);
6579*4882a593Smuzhiyun 
6580*4882a593Smuzhiyun 	if (ndev == NULL) {
6581*4882a593Smuzhiyun 		WL_ERR(("No softAP interface named %s\n", ifname));
6582*4882a593Smuzhiyun 		return BCME_NOTAP;
6583*4882a593Smuzhiyun 	}
6584*4882a593Smuzhiyun 
6585*4882a593Smuzhiyun 	ret = wldev_iovar_getint(ndev, "chanspec", (s32 *)&chanspec);
6586*4882a593Smuzhiyun 	if (ret < 0) {
6587*4882a593Smuzhiyun 		WL_ERR(("get chanspec from AP failed = %d\n", ret));
6588*4882a593Smuzhiyun 		goto exit;
6589*4882a593Smuzhiyun 	}
6590*4882a593Smuzhiyun 
6591*4882a593Smuzhiyun 	chanspec = wl_chspec_driver_to_host(chanspec);
6592*4882a593Smuzhiyun 
6593*4882a593Smuzhiyun 	if (CHSPEC_IS20(chanspec)) {
6594*4882a593Smuzhiyun 		bw = DOT11_OPER_MODE_20MHZ;
6595*4882a593Smuzhiyun 	} else if (CHSPEC_IS40(chanspec)) {
6596*4882a593Smuzhiyun 		bw = DOT11_OPER_MODE_40MHZ;
6597*4882a593Smuzhiyun 	} else if (CHSPEC_IS80(chanspec)) {
6598*4882a593Smuzhiyun 		bw = DOT11_OPER_MODE_80MHZ;
6599*4882a593Smuzhiyun 	} else if (CHSPEC_IS_BW_160_WIDE(chanspec)) {
6600*4882a593Smuzhiyun 		bw = DOT11_OPER_MODE_160MHZ;
6601*4882a593Smuzhiyun 	} else {
6602*4882a593Smuzhiyun 		WL_ERR(("chanspec error %x\n", chanspec));
6603*4882a593Smuzhiyun 		ret = BCME_BADCHAN;
6604*4882a593Smuzhiyun 		goto exit;
6605*4882a593Smuzhiyun 	}
6606*4882a593Smuzhiyun 
6607*4882a593Smuzhiyun 	bytes_written += snprintf(command + bytes_written, total_len,
6608*4882a593Smuzhiyun 		"bw=%d", bw);
6609*4882a593Smuzhiyun 	ret = bytes_written;
6610*4882a593Smuzhiyun exit:
6611*4882a593Smuzhiyun 	return ret;
6612*4882a593Smuzhiyun }
6613*4882a593Smuzhiyun 
6614*4882a593Smuzhiyun void
wl_restore_ap_bw(struct bcm_cfg80211 * cfg)6615*4882a593Smuzhiyun wl_restore_ap_bw(struct bcm_cfg80211 *cfg)
6616*4882a593Smuzhiyun {
6617*4882a593Smuzhiyun 	int ret = BCME_OK;
6618*4882a593Smuzhiyun 	u32 bw;
6619*4882a593Smuzhiyun 	bool he = FALSE;
6620*4882a593Smuzhiyun 	struct net_info *iter, *next;
6621*4882a593Smuzhiyun 	struct net_device *ndev = NULL;
6622*4882a593Smuzhiyun 	chanspec_t *chanspec;
6623*4882a593Smuzhiyun 
6624*4882a593Smuzhiyun 	if (!cfg) {
6625*4882a593Smuzhiyun 		return;
6626*4882a593Smuzhiyun 	}
6627*4882a593Smuzhiyun 
6628*4882a593Smuzhiyun 	GCC_DIAGNOSTIC_PUSH_SUPPRESS_CAST();
6629*4882a593Smuzhiyun 	for_each_ndev(cfg, iter, next) {
6630*4882a593Smuzhiyun 		GCC_DIAGNOSTIC_POP();
6631*4882a593Smuzhiyun 		if (iter->ndev) {
6632*4882a593Smuzhiyun 			if (iter->ndev->ieee80211_ptr->iftype == NL80211_IFTYPE_AP) {
6633*4882a593Smuzhiyun 				chanspec = (chanspec_t *)wl_read_prof(cfg, iter->ndev,
6634*4882a593Smuzhiyun 					WL_PROF_CHAN);
6635*4882a593Smuzhiyun 				if (CHSPEC_IS2G(*chanspec)) {
6636*4882a593Smuzhiyun 					ndev = iter->ndev;
6637*4882a593Smuzhiyun 					break;
6638*4882a593Smuzhiyun 				}
6639*4882a593Smuzhiyun 			}
6640*4882a593Smuzhiyun 		}
6641*4882a593Smuzhiyun 	}
6642*4882a593Smuzhiyun 
6643*4882a593Smuzhiyun 	if (!ndev) {
6644*4882a593Smuzhiyun 		return;
6645*4882a593Smuzhiyun 	}
6646*4882a593Smuzhiyun 
6647*4882a593Smuzhiyun 	/* BW control in AX mode not allowed */
6648*4882a593Smuzhiyun 	ret = wl_get_ap_he_mode(bcmcfg_to_prmry_ndev(cfg), cfg, &he);
6649*4882a593Smuzhiyun 	if (ret == BCME_OK && he) {
6650*4882a593Smuzhiyun 		return;
6651*4882a593Smuzhiyun 	}
6652*4882a593Smuzhiyun 	if (ret < 0) {
6653*4882a593Smuzhiyun 		WL_ERR(("Check AX mode is failed\n"));
6654*4882a593Smuzhiyun 		return;
6655*4882a593Smuzhiyun 	}
6656*4882a593Smuzhiyun 
6657*4882a593Smuzhiyun 	if (WL_BW_CAP_160MHZ(cfg->bw_cap_5g)) {
6658*4882a593Smuzhiyun 		bw = DOT11_OPER_MODE_160MHZ;
6659*4882a593Smuzhiyun 	} else if (WL_BW_CAP_80MHZ(cfg->bw_cap_5g)) {
6660*4882a593Smuzhiyun 		bw = DOT11_OPER_MODE_80MHZ;
6661*4882a593Smuzhiyun 	} else if (WL_BW_CAP_40MHZ(cfg->bw_cap_5g)) {
6662*4882a593Smuzhiyun 		bw = DOT11_OPER_MODE_40MHZ;
6663*4882a593Smuzhiyun 	} else {
6664*4882a593Smuzhiyun 		return;
6665*4882a593Smuzhiyun 	}
6666*4882a593Smuzhiyun 
6667*4882a593Smuzhiyun 	WL_DBG(("Restoring AP BW to %d\n", op2bw[bw]));
6668*4882a593Smuzhiyun 
6669*4882a593Smuzhiyun 	ret = wl_update_opmode(ndev, bw);
6670*4882a593Smuzhiyun 	if (ret < 0) {
6671*4882a593Smuzhiyun 		WL_ERR(("bw restore failed = %d\n", ret));
6672*4882a593Smuzhiyun 		return;
6673*4882a593Smuzhiyun 	}
6674*4882a593Smuzhiyun }
6675*4882a593Smuzhiyun #endif /* SUPPORT_AP_BWCTRL */
6676